Skip to content

Commit

Permalink
feat(dbghelp): show source line number if present (#1051)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiud authored Jan 8, 2024
1 parent c469cc2 commit 9377254
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 29 deletions.
4 changes: 4 additions & 0 deletions bazel/glog.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
78 changes: 54 additions & 24 deletions src/symbolize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
# include GLOG_BUILD_CONFIG_INCLUDE
#endif // GLOG_BUILD_CONFIG_INCLUDE

#include "symbolize.h"

#include "utilities.h"

#if defined(HAVE_SYMBOLIZE)
Expand All @@ -62,7 +64,6 @@
# include <limits>

# include "demangle.h"
# include "symbolize.h"

// We don't use assert() since it's not guaranteed to be
// async-signal-safe. Instead we define a minimal assertion
Expand Down Expand Up @@ -743,8 +744,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<uintptr_t>(pc);
uint64_t start_address = 0;
uint64_t base_address = 0;
Expand Down Expand Up @@ -831,8 +832,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) {
Expand All @@ -854,24 +855,19 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
# include <dbghelp.h>
# include <windows.h>

# 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;
}
Expand All @@ -881,13 +877,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;
Expand All @@ -902,12 +900,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<DWORD64>(pc),
0, symbol);
if (ret == 1 && static_cast<ssize_t>(symbol->NameLen) < out_size) {
// `NameLen` does not include the null terminating character.
strncpy(out, symbol->Name, static_cast<size_t>(symbol->NameLen) + 1);
out[static_cast<size_t>(symbol->NameLen)] = '\0';
std::size_t namelen = static_cast<size_t>(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<DWORD64>(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;
Expand All @@ -921,8 +950,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
Expand All @@ -936,7 +965,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;
}
Expand Down
26 changes: 25 additions & 1 deletion src/symbolize.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
#ifndef BASE_SYMBOLIZE_H_
#define BASE_SYMBOLIZE_H_

#include <type_traits>

#include "config.h"
#include "glog/logging.h"
#include "utilities.h"
Expand Down Expand Up @@ -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<SymbolizeOptions>(
static_cast<std::underlying_type_t<SymbolizeOptions>>(lhs) &
static_cast<std::underlying_type_t<SymbolizeOptions>>(rhs));
}

constexpr SymbolizeOptions operator|(SymbolizeOptions lhs,
SymbolizeOptions rhs) noexcept {
return static_cast<SymbolizeOptions>(
static_cast<std::underlying_type_t<SymbolizeOptions>>(lhs) |
static_cast<std::underlying_type_t<SymbolizeOptions>>(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

Expand Down
11 changes: 7 additions & 4 deletions src/symbolize_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand Down

0 comments on commit 9377254

Please sign in to comment.