2023-01-19 16:34:03 +00:00
|
|
|
#include <climits>
|
2023-01-20 14:41:06 +00:00
|
|
|
#include <cstring>
|
2023-01-19 16:34:03 +00:00
|
|
|
#include <optional>
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
|
|
#include "logcat_entry.h"
|
|
|
|
#include "pcre2_wrapper.h"
|
|
|
|
|
2023-01-20 14:41:06 +00:00
|
|
|
static const Pcre2Regex LogcatEntryRegex("^\\s*(\\d+)(?:\\.\\d+)?(?:\\s+([\\w\\d._-]+))?\\s+(\\d+)\\s+(\\d+)\\s+([A-Z])\\s+(.+?)\\s*:\\s(.*)$");
|
|
|
|
static const Pcre2Regex BufferRegex("^--------- (?:beginning of|switch to) (\\w+)$");
|
2023-01-19 16:34:03 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2023-01-20 14:41:06 +00:00
|
|
|
|
|
|
|
std::optional<Buffer> try_parse_buffer(char* buf, size_t length) {
|
|
|
|
regmatch_t matches[2];
|
|
|
|
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("Buffer line too long for int");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!BufferRegex.match(buf, sizeof(matches) / sizeof(regmatch_t), matches, REG_STARTEND)) {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* buffer_str = &buf[matches[1].rm_so];
|
2023-01-23 14:29:37 +00:00
|
|
|
if (!strncmp(buffer_str, "main", 4)) return Buffer::Main;
|
|
|
|
if (!strncmp(buffer_str, "system", 6)) return Buffer::System;
|
|
|
|
if (!strncmp(buffer_str, "radio", 5)) return Buffer::Radio;
|
|
|
|
if (!strncmp(buffer_str, "events", 6)) return Buffer::Events;
|
|
|
|
if (!strncmp(buffer_str, "crash", 5)) return Buffer::Crash;
|
2023-01-20 14:41:06 +00:00
|
|
|
return Buffer::Unknown;
|
|
|
|
}
|