Parse logcat entries
This commit is contained in:
parent
6c339fa799
commit
b6ad9c5d90
6
Makefile
6
Makefile
|
@ -44,14 +44,14 @@ LIBS =
|
||||||
##---------------------------------------------------------------------
|
##---------------------------------------------------------------------
|
||||||
|
|
||||||
ifeq ($(UNAME_S), Linux) #LINUX
|
ifeq ($(UNAME_S), Linux) #LINUX
|
||||||
LIBS += $(LINUX_GL_LIBS) -ldl `sdl2-config --libs` -lfontconfig
|
LIBS += $(LINUX_GL_LIBS) -ldl `sdl2-config --libs` -lfontconfig -lpcre2-8 -lpcre2-posix
|
||||||
|
|
||||||
CXXFLAGS += `sdl2-config --cflags` -DUSE_FONTCONFIG
|
CXXFLAGS += `sdl2-config --cflags` -DUSE_FONTCONFIG
|
||||||
CFLAGS = $(CXXFLAGS)
|
CFLAGS = $(CXXFLAGS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(UNAME_S), Darwin) #APPLE
|
ifeq ($(UNAME_S), Darwin) #APPLE
|
||||||
LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs`
|
LIBS += -framework OpenGL -framework Cocoa -framework IOKit -framework CoreVideo `sdl2-config --libs` -lpcre2-8 -lpcre2-posix
|
||||||
LIBS += -L/usr/local/lib -L/opt/local/lib
|
LIBS += -L/usr/local/lib -L/opt/local/lib
|
||||||
|
|
||||||
CXXFLAGS += `sdl2-config --cflags`
|
CXXFLAGS += `sdl2-config --cflags`
|
||||||
|
@ -60,7 +60,7 @@ ifeq ($(UNAME_S), Darwin) #APPLE
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(OS), Windows_NT)
|
ifeq ($(OS), Windows_NT)
|
||||||
LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2`
|
LIBS += -lgdi32 -lopengl32 -limm32 `pkg-config --static --libs sdl2` -lpcre2-8 -lpcre2-posix
|
||||||
|
|
||||||
CXXFLAGS += `pkg-config --cflags sdl2`
|
CXXFLAGS += `pkg-config --cflags sdl2`
|
||||||
CFLAGS = $(CXXFLAGS)
|
CFLAGS = $(CXXFLAGS)
|
||||||
|
|
|
@ -23,6 +23,19 @@ static inline void check_for_logcat_items(LogcatThread& logcat_thread) {
|
||||||
log_entries += '\n';
|
log_entries += '\n';
|
||||||
}
|
}
|
||||||
log_entries += std::move(std::get<std::string>(*logcat_thread_item));
|
log_entries += std::move(std::get<std::string>(*logcat_thread_item));
|
||||||
|
} else if (std::holds_alternative<LogcatEntry>(*logcat_thread_item)) {
|
||||||
|
LogcatEntry logcat_entry = std::move(std::get<LogcatEntry>(*logcat_thread_item));
|
||||||
|
log("Received new logcat entry");
|
||||||
|
log(std::string(" - Buffer: ") + buffer_to(logcat_entry.buffer));
|
||||||
|
log(std::string(" - Time: ") + std::to_string(logcat_entry.time));
|
||||||
|
if (logcat_entry.user) {
|
||||||
|
log(std::string(" - User: ") + *logcat_entry.user);
|
||||||
|
}
|
||||||
|
log(std::string(" - PID: ") + std::to_string(logcat_entry.pid));
|
||||||
|
log(std::string(" - TID: ") + std::to_string(logcat_entry.tid));
|
||||||
|
log(std::string(" - Priority: ") + priority_to(logcat_entry.priority));
|
||||||
|
log(std::string(" - Tag: ") + logcat_entry.tag);
|
||||||
|
log(std::string(" - Message: ") + logcat_entry.message);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("Cannot handle all possible logcat thread item variants");
|
throw std::runtime_error("Cannot handle all possible logcat thread item variants");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include <climits>
|
||||||
|
#include <optional>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "logcat_entry.h"
|
||||||
|
#include "pcre2_wrapper.h"
|
||||||
|
|
||||||
|
const Pcre2Regex LogcatEntryRegex("^\\s*(\\d+)(?:\\.\\d+)?(?:\\s+([\\w\\d._-]+))?\\s+(\\d+)\\s+(\\d+)\\s+([A-Z])\\s+(.+?)\\s*:\\s(.*)$");
|
||||||
|
|
||||||
|
Priority priority_from(char c) {
|
||||||
|
switch (c) {
|
||||||
|
case 'V': return Priority::Verbose;
|
||||||
|
case 'D': return Priority::Debug;
|
||||||
|
case 'I': return Priority::Info;
|
||||||
|
case 'W': return Priority::Warn;
|
||||||
|
case 'E': return Priority::Error;
|
||||||
|
case 'F': return Priority::Fatal;
|
||||||
|
default: return Priority::Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* priority_to(Priority priority) {
|
||||||
|
switch (priority) {
|
||||||
|
case Priority::Verbose: return "Verbose";
|
||||||
|
case Priority::Debug: return "Debug";
|
||||||
|
case Priority::Info: return "Info";
|
||||||
|
case Priority::Warn: return "Warning";
|
||||||
|
case Priority::Error: return "Error";
|
||||||
|
case Priority::Fatal: return "Fatal";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* buffer_to(Buffer buffer) {
|
||||||
|
switch (buffer) {
|
||||||
|
case Buffer::Main: return "Main";
|
||||||
|
case Buffer::System: return "System";
|
||||||
|
case Buffer::Radio: return "Radio";
|
||||||
|
case Buffer::Events: return "Events";
|
||||||
|
case Buffer::Crash: return "Crash";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline long to_long(const char* str, const char* expected_end) {
|
||||||
|
char* endptr;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
long res = strtol(str, &endptr, 10);
|
||||||
|
if (endptr != expected_end) {
|
||||||
|
throw std::invalid_argument(std::string(str) + " has trailing text");
|
||||||
|
} else if (res == LONG_MAX && errno == ERANGE) {
|
||||||
|
throw std::overflow_error(std::string(str) + " is too big");
|
||||||
|
} else if (res == LONG_MIN && errno == ERANGE) {
|
||||||
|
throw std::underflow_error(std::string(str) + " is too small");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long long to_unsigned_long_long(const char* str, const char* expected_end) {
|
||||||
|
char* endptr;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
unsigned long long res = strtoull(str, &endptr, 10);
|
||||||
|
if (endptr != expected_end) {
|
||||||
|
throw std::invalid_argument(std::string(str) + " has trailing text");
|
||||||
|
} else if (res == ULLONG_MAX && errno == ERANGE) {
|
||||||
|
throw std::overflow_error(std::string(str) + " is too big");
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<LogcatEntry> try_parse_logcat_entry(char* buf, size_t length, Buffer buffer) {
|
||||||
|
regmatch_t matches[8];
|
||||||
|
matches[0].rm_so = 0;
|
||||||
|
matches[0].rm_eo = static_cast<regoff_t>(length);
|
||||||
|
if (static_cast<size_t>(matches[0].rm_eo) != length) {
|
||||||
|
throw std::range_error("Logcat entry line too long for int");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LogcatEntryRegex.match(buf, sizeof(matches) / sizeof(regmatch_t), matches, REG_STARTEND)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> user;
|
||||||
|
if (matches[2].rm_so > -1 && matches[2].rm_eo > -1) {
|
||||||
|
user = std::string(&buf[matches[2].rm_so], static_cast<size_t>(matches[2].rm_eo - matches[2].rm_so));
|
||||||
|
}
|
||||||
|
// if pcre2 gives us negative offsets then i'll die
|
||||||
|
LogcatEntry logcat_entry = {
|
||||||
|
.buffer = buffer,
|
||||||
|
.time = to_long(&buf[matches[1].rm_so], &buf[matches[1].rm_eo]),
|
||||||
|
.user = std::move(user),
|
||||||
|
.pid = to_unsigned_long_long(&buf[matches[3].rm_so], &buf[matches[3].rm_eo]),
|
||||||
|
.tid = to_unsigned_long_long(&buf[matches[4].rm_so], &buf[matches[4].rm_eo]),
|
||||||
|
.priority = priority_from(buf[matches[5].rm_so]),
|
||||||
|
.tag = std::string(&buf[matches[6].rm_so], static_cast<size_t>(matches[6].rm_eo - matches[6].rm_so)),
|
||||||
|
.message = std::string(&buf[matches[7].rm_so], static_cast<size_t>(matches[7].rm_eo - matches[7].rm_so)),
|
||||||
|
};
|
||||||
|
return std::move(logcat_entry);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
enum class Buffer {
|
||||||
|
Unknown,
|
||||||
|
Main,
|
||||||
|
System,
|
||||||
|
Radio,
|
||||||
|
Events,
|
||||||
|
Crash,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Priority {
|
||||||
|
Unknown,
|
||||||
|
Verbose,
|
||||||
|
Debug,
|
||||||
|
Info,
|
||||||
|
Warn,
|
||||||
|
Error,
|
||||||
|
Fatal,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LogcatEntry {
|
||||||
|
Buffer buffer;
|
||||||
|
time_t time;
|
||||||
|
std::optional<std::string> user;
|
||||||
|
size_t pid;
|
||||||
|
size_t tid;
|
||||||
|
Priority priority;
|
||||||
|
std::string tag;
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
Priority priority_from(char c);
|
||||||
|
const char* priority_to(Priority priority);
|
||||||
|
const char* buffer_to(Buffer buffer);
|
||||||
|
std::optional<LogcatEntry> try_parse_logcat_entry(char* buf, size_t length, Buffer buffer);
|
|
@ -7,6 +7,7 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "logcat_thread.h"
|
#include "logcat_thread.h"
|
||||||
|
#include "logcat_entry.h"
|
||||||
|
|
||||||
#define EPOLL_MAX_EVENTS 10
|
#define EPOLL_MAX_EVENTS 10
|
||||||
|
|
||||||
|
@ -151,16 +152,29 @@ void LogcatThread::join() {
|
||||||
this->_thread.join();
|
this->_thread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogcatThread::_handle_line(char* buf, size_t used, bool is_stdout) {
|
void LogcatThread::_handle_line(char* buf, size_t length, bool is_stdout) {
|
||||||
if (!is_stdout) {
|
if (!is_stdout) {
|
||||||
std::string log_entry = format_log(std::string("logcat stderr: ") + std::string(buf, used));
|
std::string log_entry = format_log(std::string("Received from logcat stderr: ") + std::string(buf, length));
|
||||||
printf("%s\n", log_entry.c_str());
|
printf("%s\n", log_entry.c_str());
|
||||||
this->atomic_ring_buffer.put_and_increment_write(std::move(log_entry));
|
this->atomic_ring_buffer.put_and_increment_write(std::move(log_entry));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO actually parse
|
std::optional<LogcatEntry> logcat_entry;
|
||||||
std::string log_entry = format_log(std::string("logcat stdout: ") + std::string(buf, used));
|
try {
|
||||||
|
logcat_entry = try_parse_logcat_entry(buf, length, Buffer::Unknown);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::string log_entry = format_log(std::string("Failed to parse logcat entry: ") + e.what());
|
||||||
|
printf("%s\n", log_entry.c_str());
|
||||||
|
this->atomic_ring_buffer.put_and_increment_write(std::move(log_entry));
|
||||||
|
}
|
||||||
|
if (logcat_entry) {
|
||||||
|
this->atomic_ring_buffer.put_and_increment_write(std::move(*logcat_entry));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO handle buffers
|
||||||
|
|
||||||
|
std::string log_entry = format_log(std::string("Cannot parse logcat stdout: ") + std::string(buf, length));
|
||||||
printf("%s\n", log_entry.c_str());
|
printf("%s\n", log_entry.c_str());
|
||||||
this->atomic_ring_buffer.put_and_increment_write(std::move(log_entry));
|
this->atomic_ring_buffer.put_and_increment_write(std::move(log_entry));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include "arb.h"
|
#include "arb.h"
|
||||||
|
#include "logcat_entry.h"
|
||||||
|
|
||||||
typedef std::variant<std::string> LogcatThreadItem;
|
typedef std::variant<std::string, LogcatEntry> LogcatThreadItem;
|
||||||
#define NEWLINE_BUF_SIZE 512 * 1024
|
#define NEWLINE_BUF_SIZE 512 * 1024
|
||||||
|
|
||||||
class LogcatThread {
|
class LogcatThread {
|
||||||
|
@ -25,7 +26,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _handle_line(char* buf, size_t used, bool is_stdout);
|
void _handle_line(char* buf, size_t length, bool is_stdout);
|
||||||
void _run(std::stop_token stoken);
|
void _run(std::stop_token stoken);
|
||||||
|
|
||||||
int _epoll_fd = -1;
|
int _epoll_fd = -1;
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include "pcre2_wrapper.h"
|
||||||
|
|
||||||
|
Pcre2Exception::Pcre2Exception(int error_code, const regex_t* regex) {
|
||||||
|
pcre2_regerror(error_code, regex, this->_error_message, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Pcre2Exception::what() const noexcept {
|
||||||
|
return this->_error_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pcre2Regex::Pcre2Regex(const char* pattern, int cflags) {
|
||||||
|
int res = pcre2_regcomp(&this->_regex, pattern, cflags);
|
||||||
|
if (res) {
|
||||||
|
throw Pcre2Exception(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pcre2Regex::~Pcre2Regex() {
|
||||||
|
pcre2_regfree(&this->_regex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Pcre2Regex::match(const char* str, size_t nmatch, regmatch_t pmatch[], int eflags) const {
|
||||||
|
int res = pcre2_regexec(&this->_regex, str, nmatch, pmatch, eflags);
|
||||||
|
if (res == REG_NOMATCH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (res) {
|
||||||
|
throw Pcre2Exception(res, &this->_regex);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <pcre2posix.h>
|
||||||
|
|
||||||
|
class Pcre2Exception : public std::exception {
|
||||||
|
public:
|
||||||
|
Pcre2Exception(int error_code, const regex_t* regex = nullptr);
|
||||||
|
const char* what() const noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
char _error_message[1024];
|
||||||
|
};
|
||||||
|
|
||||||
|
class Pcre2Regex {
|
||||||
|
public:
|
||||||
|
// https://stackoverflow.com/a/2173764
|
||||||
|
Pcre2Regex(const Pcre2Regex&) = delete;
|
||||||
|
Pcre2Regex& operator=(const Pcre2Regex&) = delete;
|
||||||
|
|
||||||
|
Pcre2Regex(const char* pattern, int cflags = 0);
|
||||||
|
~Pcre2Regex();
|
||||||
|
bool match(const char* str, size_t nmatch, regmatch_t pmatch[], int eflags = 0) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
regex_t _regex;
|
||||||
|
};
|
Loading…
Reference in New Issue