From b6ad9c5d9017bfe4ee4e69b1bf176546ce91dd34 Mon Sep 17 00:00:00 2001 From: blankie Date: Thu, 19 Jan 2023 23:34:03 +0700 Subject: [PATCH] Parse logcat entries --- Makefile | 6 +-- event_loop.cpp | 13 ++++++ logcat_entry.cpp | 101 ++++++++++++++++++++++++++++++++++++++++++++++ logcat_entry.h | 39 ++++++++++++++++++ logcat_thread.cpp | 22 ++++++++-- logcat_thread.h | 5 ++- pcre2_wrapper.cpp | 31 ++++++++++++++ pcre2_wrapper.h | 27 +++++++++++++ 8 files changed, 235 insertions(+), 9 deletions(-) create mode 100644 logcat_entry.cpp create mode 100644 logcat_entry.h create mode 100644 pcre2_wrapper.cpp create mode 100644 pcre2_wrapper.h diff --git a/Makefile b/Makefile index 97184aa..80a57a0 100644 --- a/Makefile +++ b/Makefile @@ -44,14 +44,14 @@ LIBS = ##--------------------------------------------------------------------- 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 CFLAGS = $(CXXFLAGS) endif 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 CXXFLAGS += `sdl2-config --cflags` @@ -60,7 +60,7 @@ ifeq ($(UNAME_S), Darwin) #APPLE endif 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` CFLAGS = $(CXXFLAGS) diff --git a/event_loop.cpp b/event_loop.cpp index 1c65b92..3d82e94 100644 --- a/event_loop.cpp +++ b/event_loop.cpp @@ -23,6 +23,19 @@ static inline void check_for_logcat_items(LogcatThread& logcat_thread) { log_entries += '\n'; } log_entries += std::move(std::get(*logcat_thread_item)); + } else if (std::holds_alternative(*logcat_thread_item)) { + LogcatEntry logcat_entry = std::move(std::get(*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 { throw std::runtime_error("Cannot handle all possible logcat thread item variants"); } diff --git a/logcat_entry.cpp b/logcat_entry.cpp new file mode 100644 index 0000000..969f160 --- /dev/null +++ b/logcat_entry.cpp @@ -0,0 +1,101 @@ +#include +#include +#include + +#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 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(length); + if (static_cast(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 user; + if (matches[2].rm_so > -1 && matches[2].rm_eo > -1) { + user = std::string(&buf[matches[2].rm_so], static_cast(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(matches[6].rm_eo - matches[6].rm_so)), + .message = std::string(&buf[matches[7].rm_so], static_cast(matches[7].rm_eo - matches[7].rm_so)), + }; + return std::move(logcat_entry); +} diff --git a/logcat_entry.h b/logcat_entry.h new file mode 100644 index 0000000..2ac9afb --- /dev/null +++ b/logcat_entry.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +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 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 try_parse_logcat_entry(char* buf, size_t length, Buffer buffer); diff --git a/logcat_thread.cpp b/logcat_thread.cpp index 9af038d..1954283 100644 --- a/logcat_thread.cpp +++ b/logcat_thread.cpp @@ -7,6 +7,7 @@ #include "log.h" #include "misc.h" #include "logcat_thread.h" +#include "logcat_entry.h" #define EPOLL_MAX_EVENTS 10 @@ -151,16 +152,29 @@ void LogcatThread::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) { - 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()); this->atomic_ring_buffer.put_and_increment_write(std::move(log_entry)); return; } - // TODO actually parse - std::string log_entry = format_log(std::string("logcat stdout: ") + std::string(buf, used)); + std::optional logcat_entry; + 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()); this->atomic_ring_buffer.put_and_increment_write(std::move(log_entry)); } diff --git a/logcat_thread.h b/logcat_thread.h index 4854177..69dcfbb 100644 --- a/logcat_thread.h +++ b/logcat_thread.h @@ -3,8 +3,9 @@ #include #include #include "arb.h" +#include "logcat_entry.h" -typedef std::variant LogcatThreadItem; +typedef std::variant LogcatThreadItem; #define NEWLINE_BUF_SIZE 512 * 1024 class LogcatThread { @@ -25,7 +26,7 @@ public: #endif 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); int _epoll_fd = -1; diff --git a/pcre2_wrapper.cpp b/pcre2_wrapper.cpp new file mode 100644 index 0000000..21e6017 --- /dev/null +++ b/pcre2_wrapper.cpp @@ -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; +} diff --git a/pcre2_wrapper.h b/pcre2_wrapper.h new file mode 100644 index 0000000..f2b045b --- /dev/null +++ b/pcre2_wrapper.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include + +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; +};