From c0bfe87342989335383bf3c179524bd797ca07ef Mon Sep 17 00:00:00 2001 From: Thierry Treyer Date: Thu, 13 Apr 2023 10:58:35 -0700 Subject: [PATCH] Faster locateSymbol by re-using dwfl across calls --- src/Common.h | 11 +++ src/SymbolService.cpp | 167 +++++++++++++++++++----------------------- src/SymbolService.h | 13 +++- 3 files changed, 99 insertions(+), 92 deletions(-) diff --git a/src/Common.h b/src/Common.h index 05893f7..c402592 100644 --- a/src/Common.h +++ b/src/Common.h @@ -105,3 +105,14 @@ struct TypeHierarchy { std::set thriftIssetStructTypes; std::map> descendantClasses; }; + +// Helper for std::variant and std::visit +// https://en.cppreference.com/w/cpp/utility/variant/visit +template +struct visitor : Ts... { + using Ts::operator()...; +}; + +// Type deduction for the helper above +template +visitor(Ts...) -> visitor; diff --git a/src/SymbolService.cpp b/src/SymbolService.cpp index bd6e1c3..6927220 100644 --- a/src/SymbolService.cpp +++ b/src/SymbolService.cpp @@ -85,16 +85,28 @@ static bool isExecutableAddr( return it != end(exeAddrs) && addr >= it->first; } -SymbolService::SymbolService(std::variant newTarget) { - target = std::move(newTarget); +SymbolService::SymbolService(pid_t pid) : target{pid} { + // Update target processes memory map + LoadExecutableAddressRange(pid, executableAddrs); + if (!loadModules()) { + throw std::runtime_error("Failed to load modules for process " + + std::to_string(pid)); + } +} - if (target.index() == 0) { - // Update target processes memory map - LoadExecutableAddressRange(std::get(target), executableAddrs); +SymbolService::SymbolService(fs::path executablePath) + : target{std::move(executablePath)} { + if (!loadModules()) { + throw std::runtime_error("Failed to load modules for executable " + + executablePath.string()); } } SymbolService::~SymbolService() { + if (dwfl != nullptr) { + dwfl_end(dwfl); + } + if (prog != nullptr) { drgn_program_destroy(prog); } @@ -200,14 +212,42 @@ static int moduleCallback(Dwfl_Module* mod, return DWARF_CB_OK; } -/** - * Resolve a symbol to its location in the target ELF binary. - * - * @param[in] symName - symbol to resolve - * @return - A std::optional with the symbol's information - */ -std::optional SymbolService::locateSymbol( - const std::string& symName, bool demangle) { +/* Load modules from a live process */ +bool SymbolService::loadModulesFromPid(pid_t target) { + if (int err = dwfl_linux_proc_report(dwfl, target)) { + LOG(ERROR) << "dwfl_linux_proc_report: " << dwfl_errmsg(err); + return false; + } + + return true; +} + +/* Load modules from an ELF binary */ +bool SymbolService::loadModulesFromPath(const fs::path& target) { + auto* mod = dwfl_report_offline(dwfl, target.c_str(), target.c_str(), -1); + if (mod == nullptr) { + LOG(ERROR) << "dwfl_report_offline: " << dwfl_errmsg(dwfl_errno()); + return false; + } + + Dwarf_Addr start = 0; + Dwarf_Addr end = 0; + if (dwfl_module_info(mod, nullptr, &start, &end, nullptr, nullptr, nullptr, + nullptr) == nullptr) { + LOG(ERROR) << "dwfl_module_info: " << dwfl_errmsg(dwfl_errno()); + return false; + } + + VLOG(1) << "Module info for " << target << ": start= " << std::hex << start + << ", end=" << end; + + // Add module's boundary to executableAddrs + executableAddrs = {{start, end}}; + + return true; +} + +bool SymbolService::loadModules() { static char* debuginfo_path; static const Dwfl_Callbacks proc_callbacks{ .find_elf = dwfl_linux_proc_find_elf, @@ -216,55 +256,42 @@ std::optional SymbolService::locateSymbol( .debuginfo_path = &debuginfo_path, }; - Dwfl* dwfl = dwfl_begin(&proc_callbacks); + dwfl = dwfl_begin(&proc_callbacks); if (dwfl == nullptr) { LOG(ERROR) << "dwfl_begin: " << dwfl_errmsg(dwfl_errno()); - return std::nullopt; + return false; } - BOOST_SCOPE_EXIT_ALL(&) { - dwfl_end(dwfl); - }; - switch (target.index()) { - case 0: { - auto pid = std::get(target); - if (int err = dwfl_linux_proc_report(dwfl, pid)) { - LOG(ERROR) << "dwfl_linux_proc_report: " << dwfl_errmsg(err); - return std::nullopt; - } - break; - } - case 1: { - const auto& exe = std::get(target); - Dwfl_Module* mod = - dwfl_report_offline(dwfl, exe.c_str(), exe.c_str(), -1); - if (mod == nullptr) { - LOG(ERROR) << "dwfl_report_offline: " << dwfl_errmsg(dwfl_errno()); - return std::nullopt; - } + dwfl_report_begin(dwfl); - Dwarf_Addr start = 0; - Dwarf_Addr end = 0; - if (dwfl_module_info(mod, nullptr, &start, &end, nullptr, nullptr, - nullptr, nullptr) == nullptr) { - LOG(ERROR) << "dwfl_module_info: " << dwfl_errmsg(dwfl_errno()); - return std::nullopt; - } + bool ok = std::visit( + visitor{[this](pid_t target) { return loadModulesFromPid(target); }, + [this](const fs::path& target) { + return loadModulesFromPath(target); + }}, + target); - VLOG(1) << "Module info for " << exe << ": start= " << std::hex << start - << ", end=" << end; - - // Add module's boundary to executableAddrs - executableAddrs = {{start, end}}; - - break; - } + if (!ok) { + // The loadModules* function above already logged the error message + return false; } if (dwfl_report_end(dwfl, nullptr, nullptr) != 0) { LOG(ERROR) << "dwfl_report_end: " << dwfl_errmsg(-1); + return false; } + return true; +} + +/** + * Resolve a symbol to its location in the target ELF binary. + * + * @param[in] symName - symbol to resolve + * @return - A std::optional with the symbol's information + */ +std::optional SymbolService::locateSymbol( + const std::string& symName, bool demangle) { ModParams m = {.symName = symName, .sym = {}, .value = 0, @@ -331,46 +358,6 @@ static int buildIDCallback(Dwfl_Module* mod, } std::optional SymbolService::locateBuildID() { - static char* debuginfoPath; - static const Dwfl_Callbacks procCallbacks = { - .find_elf = dwfl_linux_proc_find_elf, - .find_debuginfo = dwfl_standard_find_debuginfo, - .section_address = dwfl_offline_section_address, - .debuginfo_path = &debuginfoPath, - }; - - Dwfl* dwfl = dwfl_begin(&procCallbacks); - if (dwfl == nullptr) { - LOG(ERROR) << "dwfl_begin: " << dwfl_errmsg(dwfl_errno()); - return std::nullopt; - } - - BOOST_SCOPE_EXIT_ALL(&) { - dwfl_end(dwfl); - }; - - switch (target.index()) { - case 0: { - auto pid = std::get(target); - if (auto err = dwfl_linux_proc_report(dwfl, pid)) { - LOG(ERROR) << "dwfl_linux_proc_report: " << dwfl_errmsg(err); - } - break; - } - case 1: { - const auto& exe = std::get(target); - if (dwfl_report_offline(dwfl, exe.c_str(), exe.c_str(), -1) == nullptr) { - LOG(ERROR) << "dwfl_report_offline: " << dwfl_errmsg(dwfl_errno()); - return std::nullopt; - } - break; - } - } - - if (dwfl_report_end(dwfl, nullptr, nullptr) != 0) { - LOG(ERROR) << "dwfl_report_end: " << dwfl_errmsg(-1); - } - std::optional buildID; dwfl_getmodules(dwfl, buildIDCallback, (void*)&buildID, 0); diff --git a/src/SymbolService.h b/src/SymbolService.h index 621eb99..80f7afb 100644 --- a/src/SymbolService.h +++ b/src/SymbolService.h @@ -28,6 +28,7 @@ namespace fs = std::filesystem; +struct Dwfl; struct drgn_program; struct irequest; @@ -38,7 +39,10 @@ struct SymbolInfo { class SymbolService { public: - SymbolService(std::variant); + SymbolService(pid_t); + SymbolService(fs::path); + SymbolService(const SymbolService&) = delete; + SymbolService& operator=(const SymbolService&) = delete; ~SymbolService(); struct drgn_program* getDrgnProgram(); @@ -60,9 +64,14 @@ class SymbolService { } private: - std::variant target{0}; + std::variant target; + struct Dwfl* dwfl{nullptr}; struct drgn_program* prog{nullptr}; + bool loadModules(); + bool loadModulesFromPid(pid_t); + bool loadModulesFromPath(const fs::path&); + std::vector> executableAddrs{}; bool hardDisableDrgn = false; };