From 79d849a3a0462ab0a33cbf208e27e28d05eab213 Mon Sep 17 00:00:00 2001 From: Viktor Malik Date: Fri, 3 Mar 2023 08:28:54 +0100 Subject: [PATCH 1/2] Parse kernel configuration In future, it may (and will) be useful to have access to the running kernel configuration, e.g. to add config-specific compilation options to ClangParser. This adds and fills a new map BPFtrace::kconfig that maps config options to their values. Both the option name and the value are strings. The configuration is parsed from one of two sources: - /boot/config-$(uname -r) - /proc/config.gz For testing purposes, the config filename may be passed through the BPFTRACE_KCONFIG_TEST env variable. --- src/bpftrace.h | 1 + src/utils.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ src/utils.h | 12 ++++++++++++ tests/utils.cpp | 19 +++++++++++++++++++ 4 files changed, 82 insertions(+) diff --git a/src/bpftrace.h b/src/bpftrace.h index a6a8c00b..94587bff 100644 --- a/src/bpftrace.h +++ b/src/bpftrace.h @@ -168,6 +168,7 @@ public: std::map helper_use_loc_; // mapping traceable functions to modules (or "vmlinux") that they appear in FuncsModulesMap traceable_funcs_; + KConfig kconfig; std::vector> attached_probes_; std::map> pcap_writers; diff --git a/src/utils.cpp b/src/utils.cpp index 2d9c6695..54c8f054 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -178,6 +179,55 @@ StdioSilencer::~StdioSilencer() } } +KConfig::KConfig() +{ + std::vector config_locs; + + // Try to get the config from BPFTRACE_KCONFIG_TEST env + // If not set, use the set of default locations + const char *path_env = std::getenv("BPFTRACE_KCONFIG_TEST"); + if (path_env) + config_locs = { std::string(path_env) }; + else + { + struct utsname utsname; + if (uname(&utsname) < 0) + return; + config_locs = { + "/boot/config-" + std::string(utsname.release), + "/proc/config.gz", + }; + } + + for (auto &path : config_locs) + { + // gzopen/gzgets handle both uncompressed and compressed files + gzFile file = gzopen(path.c_str(), "r"); + if (!file) + continue; + + char buf[4096]; + while (gzgets(file, buf, sizeof(buf))) + { + std::string option(buf); + if (option.find("CONFIG_") == 0) + { + // trim trailing '\n' + if (option[option.length() - 1] == '\n') + option = option.substr(0, option.length() - 1); + + auto split = option.find("="); + if (split == std::string::npos) + continue; + + config.emplace(option.substr(0, split), option.substr(split + 1)); + } + } + gzclose(file); + } +} + + bool get_uint64_env_var(const std::string &str, uint64_t &dest) { if (const char* env_p = std::getenv(str.c_str())) diff --git a/src/utils.h b/src/utils.h index dccc4504..a76aa161 100644 --- a/src/utils.h +++ b/src/utils.h @@ -130,6 +130,18 @@ struct DeprecatedName typedef std::unordered_map> FuncsModulesMap; +struct KConfig +{ + KConfig(); + bool has_value(const std::string &name, const std::string &value) const + { + auto c = config.find(name); + return c != config.end() && c->second == value; + } + + std::unordered_map config; +}; + static std::vector DEPRECATED_LIST = { }; diff --git a/tests/utils.cpp b/tests/utils.cpp index 9ca4ace5..8470745b 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -222,6 +222,25 @@ TEST(utils, get_cgroup_path_in_hierarchy) } } +TEST(utils, parse_kconfig) +{ + char path[] = "/tmp/configXXXXXX"; + int fd = mkstemp(path); + const std::string config = "# Intro comment\n" + "CONFIG_YES=y\n" + "CONFIG_MOD=m\n" + "CONFIG_VAL=42\n" + "# CONFIG_NO is not set"; + EXPECT_EQ(write(fd, config.c_str(), config.length()), config.length()); + setenv("BPFTRACE_KCONFIG_TEST", path, true); + + KConfig kconfig; + ASSERT_TRUE(kconfig.has_value("CONFIG_YES", "y")); + ASSERT_TRUE(kconfig.has_value("CONFIG_MOD", "m")); + ASSERT_TRUE(kconfig.has_value("CONFIG_VAL", "42")); + ASSERT_EQ(kconfig.config.find("CONFIG_NO"), kconfig.config.end()); +} + } // namespace utils } // namespace test } // namespace bpftrace -- 2.39.2