From eafd73ba3be3df9a67f53cc5659ba928e6db883a Mon Sep 17 00:00:00 2001 From: Sergiu Deitsch Date: Mon, 8 Jan 2024 10:54:02 +0100 Subject: [PATCH] feat(dbghelp): show source line number if present --- bazel/glog.bzl | 4 +++ src/symbolize.cc | 75 +++++++++++++++++++++++++++------------ src/symbolize.h | 26 +++++++++++++- src/symbolize_unittest.cc | 11 +++--- 4 files changed, 88 insertions(+), 28 deletions(-) diff --git a/bazel/glog.bzl b/bazel/glog.bzl index dca92953d..30dafa472 100644 --- a/bazel/glog.bzl +++ b/bazel/glog.bzl @@ -210,6 +210,10 @@ def glog_library(with_gflags = 1, **kwargs): "@bazel_tools//src/conditions:windows": [":strip_include_prefix_hack"], "//conditions:default": [], }), + linkopts = select({ + "@bazel_tools//src/conditions:windows": ["dbghelp.lib"], + "//conditions:default": [], + }), **kwargs ) diff --git a/src/symbolize.cc b/src/symbolize.cc index 8b9c2085b..63d8406a8 100644 --- a/src/symbolize.cc +++ b/src/symbolize.cc @@ -743,8 +743,8 @@ static void SafeAppendHexNumber(uint64_t value, char* dest, size_t dest_size) { // and "out" is used as its output. // To keep stack consumption low, we would like this function to not // get inlined. -static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, - size_t out_size) { +static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle( + void* pc, char* out, size_t out_size, SymbolizeOptions /*options*/) { auto pc0 = reinterpret_cast(pc); uint64_t start_address = 0; uint64_t base_address = 0; @@ -831,8 +831,8 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, namespace google { -static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, - size_t out_size) { +static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle( + void* pc, char* out, size_t out_size, SymbolizeOptions /*options*/) { Dl_info info; if (dladdr(pc, &info)) { if (info.dli_sname) { @@ -854,24 +854,19 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, # include # include -# ifdef _MSC_VER -# pragma comment(lib, "dbghelp") -# endif - namespace google { -class SymInitializer { +class SymInitializer final { public: HANDLE process; bool ready; - SymInitializer() : process(nullptr), ready(false) { + SymInitializer() : process(GetCurrentProcess()), ready(false) { // Initialize the symbol handler. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms680344(v=vs.85).aspx - process = GetCurrentProcess(); // Defer symbol loading. // We do not request undecorated symbols with SYMOPT_UNDNAME // because the mangling library calls UnDecorateSymbolName. - SymSetOptions(SYMOPT_DEFERRED_LOADS); + SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); if (SymInitialize(process, nullptr, true)) { ready = true; } @@ -881,13 +876,15 @@ class SymInitializer { // We do not need to close `HANDLE process` because it's a "pseudo handle." } - private: - SymInitializer(const SymInitializer&); - SymInitializer& operator=(const SymInitializer&); + SymInitializer(const SymInitializer&) = delete; + SymInitializer& operator=(const SymInitializer&) = delete; + SymInitializer(SymInitializer&&) = delete; + SymInitializer& operator=(SymInitializer&&) = delete; }; static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, - size_t out_size) { + size_t out_size, + SymbolizeOptions options) { const static SymInitializer symInitializer; if (!symInitializer.ready) { return false; @@ -902,12 +899,43 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, // This could break if a symbol has Unicode in it. BOOL ret = SymFromAddr(symInitializer.process, reinterpret_cast(pc), 0, symbol); - if (ret == 1 && static_cast(symbol->NameLen) < out_size) { - // `NameLen` does not include the null terminating character. - strncpy(out, symbol->Name, static_cast(symbol->NameLen) + 1); - out[static_cast(symbol->NameLen)] = '\0'; + std::size_t namelen = static_cast(symbol->NameLen); + if (ret && namelen < out_size) { + std::strncpy(out, symbol->Name, namelen); + out[namelen] = '\0'; + + DWORD displacement; + IMAGEHLP_LINE64 line{sizeof(IMAGEHLP_LINE64)}; + + BOOL found = FALSE; + + if ((options & SymbolizeOptions::kNoLineNumbers) != + SymbolizeOptions::kNoLineNumbers) { + found = SymGetLineFromAddr64(symInitializer.process, + reinterpret_cast(pc), &displacement, + &line); + } + // Symbolization succeeded. Now we try to demangle the symbol. DemangleInplace(out, out_size); + out_size -= std::strlen(out); + + if (found) { + std::size_t fnlen = std::strlen(line.FileName); + // Determine the number of digits (base 10) necessary to represent the + // line number + std::size_t digits = 1; // At least one digit required + for (DWORD value = line.LineNumber; (value /= 10) != 0; ++digits) { + } + constexpr std::size_t extralen = 4; // space + parens () + : + const std::size_t suffixlen = fnlen + extralen + fnlen + digits; + + if (suffixlen < out_size) { + out_size -= std::snprintf(out + namelen, out_size, " (%s:%u)", + line.FileName, line.LineNumber); + } + } + return true; } return false; @@ -921,8 +949,8 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out, namespace google { -bool Symbolize(void* pc, char* out, size_t out_size) { - return SymbolizeAndDemangle(pc, out, out_size); +bool Symbolize(void* pc, char* out, size_t out_size, SymbolizeOptions options) { + return SymbolizeAndDemangle(pc, out, out_size, options); } } // namespace google @@ -936,7 +964,8 @@ bool Symbolize(void* pc, char* out, size_t out_size) { namespace google { // TODO: Support other environments. -bool Symbolize(void* /*pc*/, char* /*out*/, size_t /*out_size*/) { +bool Symbolize(void* /*pc*/, char* /*out*/, size_t /*out_size*/, + SymbolizeOptions /*options*/) { assert(0); return false; } diff --git a/src/symbolize.h b/src/symbolize.h index 4d4cad21b..a93584f3b 100644 --- a/src/symbolize.h +++ b/src/symbolize.h @@ -54,6 +54,8 @@ #ifndef BASE_SYMBOLIZE_H_ #define BASE_SYMBOLIZE_H_ +#include + #include "config.h" #include "glog/logging.h" #include "utilities.h" @@ -142,11 +144,33 @@ void InstallSymbolizeOpenObjectFileCallback( namespace google { +enum class SymbolizeOptions { + // No additional options. + kNone = 0, + // Do not display source and line numbers in the symbolized output. + kNoLineNumbers = 1 +}; + +constexpr SymbolizeOptions operator&(SymbolizeOptions lhs, + SymbolizeOptions rhs) noexcept { + return static_cast( + static_cast>(lhs) & + static_cast>(rhs)); +} + +constexpr SymbolizeOptions operator|(SymbolizeOptions lhs, + SymbolizeOptions rhs) noexcept { + return static_cast( + static_cast>(lhs) | + static_cast>(rhs)); +} + // Symbolizes a program counter. On success, returns true and write the // symbol name to "out". The symbol name is demangled if possible // (supports symbols generated by GCC 3.x or newer). Otherwise, // returns false. -GLOG_EXPORT bool Symbolize(void* pc, char* out, size_t out_size); +GLOG_EXPORT bool Symbolize(void* pc, char* out, size_t out_size, + SymbolizeOptions options = SymbolizeOptions::kNone); } // namespace google diff --git a/src/symbolize_unittest.cc b/src/symbolize_unittest.cc index 2fc1277e6..30945c141 100644 --- a/src/symbolize_unittest.cc +++ b/src/symbolize_unittest.cc @@ -62,9 +62,10 @@ using namespace google; # if defined(__ELF__) || defined(GLOG_OS_WINDOWS) || defined(GLOG_OS_CYGWIN) // A wrapper function for Symbolize() to make the unit test simple. -static const char* TrySymbolize(void* pc) { +static const char* TrySymbolize(void* pc, google::SymbolizeOptions options = + google::SymbolizeOptions::kNone) { static char symbol[4096]; - if (Symbolize(pc, symbol, sizeof(symbol))) { + if (Symbolize(pc, symbol, sizeof(symbol), options)) { return symbol; } else { return nullptr; @@ -394,7 +395,8 @@ static void ATTRIBUTE_NOINLINE TestWithPCInsideInlineFunction() { static void ATTRIBUTE_NOINLINE TestWithReturnAddress() { # if defined(HAVE_ATTRIBUTE_NOINLINE) void* return_address = __builtin_return_address(0); - const char* symbol = TrySymbolize(return_address); + const char* symbol = + TrySymbolize(return_address, google::SymbolizeOptions::kNoLineNumbers); # if !defined(_MSC_VER) || !defined(NDEBUG) CHECK(symbol != nullptr); @@ -439,7 +441,8 @@ __declspec(noinline) void TestWithReturnAddress() { _ReturnAddress() # endif ; - const char* symbol = TrySymbolize(return_address); + const char* symbol = + TrySymbolize(return_address, google::SymbolizeOptions::kNoLineNumbers); # if !defined(_MSC_VER) || !defined(NDEBUG) CHECK(symbol != nullptr); CHECK_STREQ(symbol, "main");