src,win: informative stack traces

Refresh `Win32SymbolDebuggingContext::LookupSymbol` to use more APIs

PR-URL: https://github.com/nodejs/node/pull/23822
Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-symbol-information-by-address
Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-undecorated-symbol-names
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
Refael Ackermann 2018-10-22 15:07:00 -04:00
parent f90cf19fdd
commit 247b513059
2 changed files with 124 additions and 31 deletions

View File

@ -100,35 +100,104 @@ class Win32SymbolDebuggingContext final : public NativeSymbolDebuggingContext {
USE(SymInitialize(current_process_, nullptr, true)); USE(SymInitialize(current_process_, nullptr, true));
} }
~Win32SymbolDebuggingContext() { ~Win32SymbolDebuggingContext() override {
USE(SymCleanup(current_process_)); USE(SymCleanup(current_process_));
} }
SymbolInfo LookupSymbol(void* address) override { using NameAndDisplacement = std::pair<std::string, DWORD64>;
// Ref: https://msdn.microsoft.com/en-en/library/windows/desktop/ms680578(v=vs.85).aspx NameAndDisplacement WrappedSymFromAddr(DWORD64 dwAddress) const {
char info_buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; // Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-symbol-information-by-address
SYMBOL_INFO* info = reinterpret_cast<SYMBOL_INFO*>(info_buf); // Patches:
char demangled[MAX_SYM_NAME]; // Use `fprintf(stderr, ` instead of `printf`
// `sym.filename = pSymbol->Name` on success
// `current_process_` instead of `hProcess.
DWORD64 dwDisplacement = 0;
// Patch: made into arg - DWORD64 dwAddress = SOME_ADDRESS;
info->MaxNameLen = MAX_SYM_NAME; char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
info->SizeOfStruct = sizeof(SYMBOL_INFO); const auto pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
SymbolInfo ret; pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
const bool have_info = SymFromAddr(current_process_, pSymbol->MaxNameLen = MAX_SYM_NAME;
reinterpret_cast<DWORD64>(address),
nullptr, if (SymFromAddr(current_process_, dwAddress, &dwDisplacement, pSymbol)) {
info); // SymFromAddr returned success
if (have_info && strlen(info->Name) == 0) { return NameAndDisplacement(pSymbol->Name, dwDisplacement);
if (UnDecorateSymbolName(info->Name, } else {
demangled, // SymFromAddr failed
sizeof(demangled), const DWORD error = GetLastError(); // "eat" the error anyway
UNDNAME_COMPLETE)) { #ifdef DEBUG
ret.name = demangled; fprintf(stderr, "SymFromAddr returned error : %lu\n", error);
} else { #endif
ret.name = info->Name;
}
} }
// End MSDN code
return NameAndDisplacement();
}
SymbolInfo WrappedGetLine(DWORD64 dwAddress) const {
SymbolInfo sym{};
// Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-symbol-information-by-address
// Patches:
// Use `fprintf(stderr, ` instead of `printf`.
// Assign values to `sym` on success.
// `current_process_` instead of `hProcess.
// Patch: made into arg - DWORD64 dwAddress;
DWORD dwDisplacement;
IMAGEHLP_LINE64 line;
SymSetOptions(SYMOPT_LOAD_LINES);
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
// Patch: made into arg - dwAddress = 0x1000000;
if (SymGetLineFromAddr64(current_process_, dwAddress,
&dwDisplacement, &line)) {
// SymGetLineFromAddr64 returned success
sym.filename = line.FileName;
sym.line = line.LineNumber;
} else {
// SymGetLineFromAddr64 failed
const DWORD error = GetLastError(); // "eat" the error anyway
#ifdef DEBUG
fprintf(stderr, "SymGetLineFromAddr64 returned error : %lu\n", error);
#endif
}
// End MSDN code
return sym;
}
// Fills the SymbolInfo::name of the io/out argument `sym`
std::string WrappedUnDecorateSymbolName(const char* name) const {
// Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-undecorated-symbol-names
// Patches:
// Use `fprintf(stderr, ` instead of `printf`.
// return `szUndName` instead of `printf` on success
char szUndName[MAX_SYM_NAME];
if (UnDecorateSymbolName(name, szUndName, sizeof(szUndName),
UNDNAME_COMPLETE)) {
// UnDecorateSymbolName returned success
return szUndName;
} else {
// UnDecorateSymbolName failed
const DWORD error = GetLastError(); // "eat" the error anyway
#ifdef DEBUG
fprintf(stderr, "UnDecorateSymbolName returned error %lu\n", error);
#endif
}
return nullptr;
}
SymbolInfo LookupSymbol(void* address) override {
const DWORD64 dw_address = reinterpret_cast<DWORD64>(address);
SymbolInfo ret = WrappedGetLine(dw_address);
std::tie(ret.name, ret.dis) = WrappedSymFromAddr(dw_address);
if (!ret.name.empty()) {
ret.name = WrappedUnDecorateSymbolName(ret.name.c_str());
}
return ret; return ret;
} }
@ -145,6 +214,13 @@ class Win32SymbolDebuggingContext final : public NativeSymbolDebuggingContext {
return CaptureStackBackTrace(0, count, frames, nullptr); return CaptureStackBackTrace(0, count, frames, nullptr);
} }
Win32SymbolDebuggingContext(const Win32SymbolDebuggingContext&) = delete;
Win32SymbolDebuggingContext(Win32SymbolDebuggingContext&&) = delete;
Win32SymbolDebuggingContext operator=(const Win32SymbolDebuggingContext&)
= delete;
Win32SymbolDebuggingContext operator=(Win32SymbolDebuggingContext&&)
= delete;
private: private:
HANDLE current_process_; HANDLE current_process_;
}; };
@ -158,13 +234,18 @@ NativeSymbolDebuggingContext::New() {
#endif // __POSIX__ #endif // __POSIX__
std::string NativeSymbolDebuggingContext::SymbolInfo::Display() const { std::string NativeSymbolDebuggingContext::SymbolInfo::Display() const {
std::string ret = name; std::ostringstream oss;
if (!filename.empty()) { oss << name;
ret += " ["; if (dis != 0) {
ret += filename; oss << "+" << dis;
ret += ']';
} }
return ret; if (!filename.empty()) {
oss << " [" << filename << ']';
}
if (line != 0) {
oss << ":L" << line;
}
return oss.str();
} }
void DumpBacktrace(FILE* fp) { void DumpBacktrace(FILE* fp) {
@ -173,8 +254,8 @@ void DumpBacktrace(FILE* fp) {
const int size = sym_ctx->GetStackTrace(frames, arraysize(frames)); const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
for (int i = 1; i < size; i += 1) { for (int i = 1; i < size; i += 1) {
void* frame = frames[i]; void* frame = frames[i];
fprintf(fp, "%2d: %p %s\n", NativeSymbolDebuggingContext::SymbolInfo s = sym_ctx->LookupSymbol(frame);
i, frame, sym_ctx->LookupSymbol(frame).Display().c_str()); fprintf(fp, "%2d: %p %s\n", i, frame, s.Display().c_str());
} }
} }

View File

@ -6,6 +6,7 @@
#include "async_wrap.h" #include "async_wrap.h"
#include "env.h" #include "env.h"
#include <string> #include <string>
#include <sstream>
// Use FORCE_INLINE on functions that have a debug-category-enabled check first // Use FORCE_INLINE on functions that have a debug-category-enabled check first
// and then ideally only a single function call following it, to maintain // and then ideally only a single function call following it, to maintain
@ -93,14 +94,25 @@ class NativeSymbolDebuggingContext {
public: public:
std::string name; std::string name;
std::string filename; std::string filename;
size_t line = 0;
size_t dis = 0;
std::string Display() const; std::string Display() const;
}; };
NativeSymbolDebuggingContext() = default;
virtual ~NativeSymbolDebuggingContext() {} virtual ~NativeSymbolDebuggingContext() {}
virtual SymbolInfo LookupSymbol(void* address) { return { "", "" }; }
virtual SymbolInfo LookupSymbol(void* address) { return {}; }
virtual bool IsMapped(void* address) { return false; } virtual bool IsMapped(void* address) { return false; }
virtual int GetStackTrace(void** frames, int count) { return 0; } virtual int GetStackTrace(void** frames, int count) { return 0; }
NativeSymbolDebuggingContext(const NativeSymbolDebuggingContext&) = delete;
NativeSymbolDebuggingContext(NativeSymbolDebuggingContext&&) = delete;
NativeSymbolDebuggingContext operator=(NativeSymbolDebuggingContext&)
= delete;
NativeSymbolDebuggingContext operator=(NativeSymbolDebuggingContext&&)
= delete;
}; };
// Variant of `uv_loop_close` that tries to be as helpful as possible // Variant of `uv_loop_close` that tries to be as helpful as possible