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