Parse logcat entries

This commit is contained in:
blankie 2023-01-19 23:34:03 +07:00
parent 6c339fa799
commit b6ad9c5d90
Signed by: blankie
GPG Key ID: CC15FC822C7F61F5
8 changed files with 235 additions and 9 deletions

View File

@ -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)

View File

@ -23,6 +23,19 @@ static inline void check_for_logcat_items(LogcatThread& logcat_thread) {
log_entries += '\n';
}
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 {
throw std::runtime_error("Cannot handle all possible logcat thread item variants");
}

101
logcat_entry.cpp Normal file
View File

@ -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);
}

39
logcat_entry.h Normal file
View File

@ -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);

View File

@ -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<LogcatEntry> 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));
}

View File

@ -3,8 +3,9 @@
#include <thread>
#include <variant>
#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
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;

31
pcre2_wrapper.cpp Normal file
View File

@ -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;
}

27
pcre2_wrapper.h Normal file
View File

@ -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;
};