Files
hardware_samsung-extra_inte…/debug-tools/bootlogger/KernelConfig.cpp
royna2544 915f4c8ad8 debug-tools: bootlogger: Use fork for logcat
- Use android logging system
2025-04-27 13:02:02 +09:00

153 lines
3.8 KiB
C++

#include <fmt/core.h>
#include <string_view>
#include <sys/stat.h>
#include <zlib.h>
#include <array>
#include <cstdio>
#include <regex>
#include <sstream>
#include <string>
#include "LoggerInternal.h"
static constexpr std::string_view kProcConfigGz = "/proc/config.gz";
static int ReadConfigGz(std::string &out) {
std::array<char, BUF_SIZE> buf{};
size_t len = 0;
gzFile f = gzopen(kProcConfigGz.data(), "rb");
if (f == nullptr) {
PLOG(ERROR) << "gzopen failed";
return -errno;
}
while ((len = gzread(f, buf.data(), buf.size())) != 0U) {
out.append(buf.data(), len);
}
if (len < 0) {
int errnum = 0;
const char *errmsg = gzerror(f, &errnum);
LOG(ERROR) << "Could not read " << kProcConfigGz << ": " << errmsg;
return (errnum == Z_ERRNO ? -errno : errnum);
}
gzclose(f);
return 0;
}
static bool parseOneConfigLine(const std::string &line,
KernelConfigType &outvec) {
static const std::regex kDisabledConfig(R"(^#\sCONFIG_\w+ is not set$)");
static const std::regex kEnabledConfig(R"(^CONFIG_\w+=(y|m|(")?(.+)?(")?)$)");
static const auto flags = std::regex_constants::format_sed;
std::string config;
bool ret = false;
ConfigValue value = ConfigValue::UNKNOWN;
ret = std::regex_match(line, kEnabledConfig, flags);
if (ret) {
char c = line[line.find('=') + 1];
switch (c) {
case 'y':
value = ConfigValue::BUILT_IN;
break;
case 'm':
value = ConfigValue::MODULE;
break;
case '"':
value = ConfigValue::STRING;
break;
case '-': // Minus
case '0' ... '9':
value = ConfigValue::INT;
break;
default:
LOG(WARNING) << "Unknown config value: " << c;
return ret;
};
} else {
ret = std::regex_match(line, kDisabledConfig, flags);
if (ret) {
value = ConfigValue::UNSET;
} else {
// Is it a comment or newline?
// Assume OK first
ret = true;
if (!line.empty()) {
ret = line.front() == '#';
if (!ret) {
LOG(WARNING) << "Unparsable line: " << line;
}
}
return ret;
}
}
// Trim out CONFIG_* part
switch (value) {
case BUILT_IN:
case MODULE:
case STRING:
case INT:
// CONFIG_AAA=y
// = symbol being the delimiter
config = line.substr(0, line.find_first_of('='));
break;
case UNSET:
// # CONFIG_AAA is not set
// Space after AAA being the delimiter.
// '# ', size 2
config = line.substr(2);
config = config.substr(0, config.find_first_of(' '));
break;
case UNKNOWN:
break;
}
outvec.emplace(config, value);
return ret;
}
int ReadKernelConfig(KernelConfigType &out) {
struct stat statbuf {};
std::string buf;
std::string line;
std::stringstream ss;
int rc = 0;
int lines = 0;
// Determine config.gz size
rc = stat(kProcConfigGz.data(), &statbuf);
if (rc < 0) {
PLOG(ERROR) << "stat " << kProcConfigGz << " failed";
return -errno;
}
// Linux uses gzip -9 ratio to compress, which has average ratio of 21%
// Reserve string buffer size to avoid realloc's
buf.reserve(statbuf.st_size * 5);
rc = ReadConfigGz(buf);
if (rc < 0) {
return rc;
}
// Clear if there was anything
out.clear();
// Determine map size by newlines
for (const char c : buf) {
if (c == '\n') {
lines++;
}
}
// Avoid unnessary reallocs (Kernel configurations are a lot)
out.reserve(lines);
// Parse line by line
ss = std::stringstream(buf);
while (std::getline(ss, line)) {
// Returns true (1) on success, so invert it to
// make use of bitwise OR
rc |= static_cast<int>(!parseOneConfigLine(line, out));
}
// If any of them returned false, rc would be 1
if (rc != 0) {
LOG(ERROR) << "Error(s) were found parsing " << kProcConfigGz;
}
return rc;
}