Files
node/src/debug_utils.cc
T
David Carlier 3a9cb5ccb0 src: fix OpenBSD build
PR-URL: https://github.com/nodejs/node/pull/28384
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Rich Trott <rtrott@gmail.com>
2019-07-29 23:27:12 -07:00

441 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "debug_utils.h"
#include "env-inl.h"
#include "util-inl.h"
#ifdef __POSIX__
#if defined(__linux__)
#include <features.h>
#endif
#if defined(__linux__) && !defined(__GLIBC__) || \
defined(__UCLIBC__) || \
defined(_AIX)
#define HAVE_EXECINFO_H 0
#else
#define HAVE_EXECINFO_H 1
#endif
#if HAVE_EXECINFO_H
#include <cxxabi.h>
#include <dlfcn.h>
#include <execinfo.h>
#include <unistd.h>
#include <sys/mman.h>
#include <cstdio>
#endif
#endif // __POSIX__
#if defined(__linux__) || defined(__sun) || \
defined(__FreeBSD__) || defined(__OpenBSD__)
#include <link.h>
#endif // (__linux__) || defined(__sun) ||
// (__FreeBSD__) || defined(__OpenBSD__)
#ifdef __APPLE__
#include <mach-o/dyld.h> // _dyld_get_image_name()
#endif // __APPLE__
#ifdef _AIX
#include <sys/ldr.h> // ld_info structure
#endif // _AIX
#ifdef _WIN32
#include <Lm.h>
#include <Windows.h>
#include <dbghelp.h>
#include <process.h>
#include <psapi.h>
#include <tchar.h>
#endif // _WIN32
namespace node {
#ifdef __POSIX__
#if HAVE_EXECINFO_H
class PosixSymbolDebuggingContext final : public NativeSymbolDebuggingContext {
public:
PosixSymbolDebuggingContext() : pagesize_(getpagesize()) { }
SymbolInfo LookupSymbol(void* address) override {
Dl_info info;
const bool have_info = dladdr(address, &info);
SymbolInfo ret;
if (!have_info)
return ret;
if (info.dli_sname != nullptr) {
if (char* demangled =
abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr)) {
ret.name = demangled;
free(demangled);
} else {
ret.name = info.dli_sname;
}
}
if (info.dli_fname != nullptr) {
ret.filename = info.dli_fname;
}
return ret;
}
bool IsMapped(void* address) override {
void* page_aligned = reinterpret_cast<void*>(
reinterpret_cast<uintptr_t>(address) & ~(pagesize_ - 1));
return msync(page_aligned, pagesize_, MS_ASYNC) == 0;
}
int GetStackTrace(void** frames, int count) override {
return backtrace(frames, count);
}
private:
uintptr_t pagesize_;
};
std::unique_ptr<NativeSymbolDebuggingContext>
NativeSymbolDebuggingContext::New() {
return std::unique_ptr<NativeSymbolDebuggingContext>(
new PosixSymbolDebuggingContext());
}
#else // HAVE_EXECINFO_H
std::unique_ptr<NativeSymbolDebuggingContext>
NativeSymbolDebuggingContext::New() {
return std::unique_ptr<NativeSymbolDebuggingContext>(
new NativeSymbolDebuggingContext());
}
#endif // HAVE_EXECINFO_H
#else // __POSIX__
class Win32SymbolDebuggingContext final : public NativeSymbolDebuggingContext {
public:
Win32SymbolDebuggingContext() {
current_process_ = GetCurrentProcess();
USE(SymInitialize(current_process_, nullptr, true));
}
~Win32SymbolDebuggingContext() override {
USE(SymCleanup(current_process_));
}
using NameAndDisplacement = std::pair<std::string, DWORD64>;
NameAndDisplacement WrappedSymFromAddr(DWORD64 dwAddress) const {
// Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-symbol-information-by-address
// Patches:
// 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;
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
const auto pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
if (SymFromAddr(current_process_, dwAddress, &dwDisplacement, pSymbol)) {
// SymFromAddr returned success
return NameAndDisplacement(pSymbol->Name, dwDisplacement);
} else {
// SymFromAddr failed
const DWORD error = GetLastError(); // "eat" the error anyway
#ifdef DEBUG
fprintf(stderr, "SymFromAddr returned error : %lu\n", error);
#endif
}
// 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;
}
bool IsMapped(void* address) override {
MEMORY_BASIC_INFORMATION info;
if (VirtualQuery(address, &info, sizeof(info)) != sizeof(info))
return false;
return info.State == MEM_COMMIT && info.Protect != 0;
}
int GetStackTrace(void** frames, int count) override {
return CaptureStackBackTrace(0, count, frames, nullptr);
}
Win32SymbolDebuggingContext(const Win32SymbolDebuggingContext&) = delete;
Win32SymbolDebuggingContext(Win32SymbolDebuggingContext&&) = delete;
Win32SymbolDebuggingContext operator=(const Win32SymbolDebuggingContext&)
= delete;
Win32SymbolDebuggingContext operator=(Win32SymbolDebuggingContext&&)
= delete;
private:
HANDLE current_process_;
};
std::unique_ptr<NativeSymbolDebuggingContext>
NativeSymbolDebuggingContext::New() {
return std::unique_ptr<NativeSymbolDebuggingContext>(
new Win32SymbolDebuggingContext());
}
#endif // __POSIX__
std::string NativeSymbolDebuggingContext::SymbolInfo::Display() const {
std::ostringstream oss;
oss << name;
if (dis != 0) {
oss << "+" << dis;
}
if (!filename.empty()) {
oss << " [" << filename << ']';
}
if (line != 0) {
oss << ":L" << line;
}
return oss.str();
}
void DumpBacktrace(FILE* fp) {
auto sym_ctx = NativeSymbolDebuggingContext::New();
void* frames[256];
const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
for (int i = 1; i < size; i += 1) {
void* frame = frames[i];
NativeSymbolDebuggingContext::SymbolInfo s = sym_ctx->LookupSymbol(frame);
fprintf(fp, "%2d: %p %s\n", i, frame, s.Display().c_str());
}
}
void CheckedUvLoopClose(uv_loop_t* loop) {
if (uv_loop_close(loop) == 0) return;
PrintLibuvHandleInformation(loop, stderr);
fflush(stderr);
// Finally, abort.
CHECK(0 && "uv_loop_close() while having open handles");
}
void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream) {
struct Info {
std::unique_ptr<NativeSymbolDebuggingContext> ctx;
FILE* stream;
};
Info info { NativeSymbolDebuggingContext::New(), stream };
fprintf(stream, "uv loop at [%p] has %d active handles\n",
loop, loop->active_handles);
uv_walk(loop, [](uv_handle_t* handle, void* arg) {
Info* info = static_cast<Info*>(arg);
NativeSymbolDebuggingContext* sym_ctx = info->ctx.get();
FILE* stream = info->stream;
fprintf(stream, "[%p] %s\n", handle, uv_handle_type_name(handle->type));
void* close_cb = reinterpret_cast<void*>(handle->close_cb);
fprintf(stream, "\tClose callback: %p %s\n",
close_cb, sym_ctx->LookupSymbol(close_cb).Display().c_str());
fprintf(stream, "\tData: %p %s\n",
handle->data, sym_ctx->LookupSymbol(handle->data).Display().c_str());
// We are also interested in the first field of what `handle->data`
// points to, because for C++ code that is usually the virtual table pointer
// and gives us information about the exact kind of object we're looking at.
void* first_field = nullptr;
// `handle->data` might be any value, including `nullptr`, or something
// cast from a completely different type; therefore, check that it’s
// dereferencable first.
if (sym_ctx->IsMapped(handle->data))
first_field = *reinterpret_cast<void**>(handle->data);
if (first_field != nullptr) {
fprintf(stream, "\t(First field): %p %s\n",
first_field, sym_ctx->LookupSymbol(first_field).Display().c_str());
}
}, &info);
}
std::vector<std::string> NativeSymbolDebuggingContext::GetLoadedLibraries() {
std::vector<std::string> list;
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__)
dl_iterate_phdr(
[](struct dl_phdr_info* info, size_t size, void* data) {
auto list = static_cast<std::vector<std::string>*>(data);
if (*info->dlpi_name != '\0') {
list->push_back(info->dlpi_name);
}
return 0;
},
&list);
#elif __APPLE__
uint32_t i = 0;
for (const char* name = _dyld_get_image_name(i); name != nullptr;
name = _dyld_get_image_name(++i)) {
list.push_back(name);
}
#elif _AIX
// We can't tell in advance how large the buffer needs to be.
// Retry until we reach too large a size (1Mb).
const unsigned int kBufferGrowStep = 4096;
MallocedBuffer<char> buffer(kBufferGrowStep);
int rc = -1;
do {
rc = loadquery(L_GETINFO, buffer.data, buffer.size);
if (rc == 0) break;
buffer = MallocedBuffer<char>(buffer.size + kBufferGrowStep);
} while (buffer.size < 1024 * 1024);
if (rc == 0) {
char* buf = buffer.data;
ld_info* cur_info = nullptr;
do {
std::ostringstream str;
cur_info = reinterpret_cast<ld_info*>(buf);
char* member_name = cur_info->ldinfo_filename +
strlen(cur_info->ldinfo_filename) + 1;
if (*member_name != '\0') {
str << cur_info->ldinfo_filename << "(" << member_name << ")";
list.push_back(str.str());
str.str("");
} else {
list.push_back(cur_info->ldinfo_filename);
}
buf += cur_info->ldinfo_next;
} while (cur_info->ldinfo_next != 0);
}
#elif __sun
Link_map* p;
if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &p) != -1) {
for (Link_map* l = p; l != nullptr; l = l->l_next) {
list.push_back(l->l_name);
}
}
#elif _WIN32
// Windows implementation - get a handle to the process.
HANDLE process_handle = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
FALSE, GetCurrentProcessId());
if (process_handle == nullptr) {
// Cannot proceed, return an empty list.
return list;
}
// Get a list of all the modules in this process
DWORD size_1 = 0;
DWORD size_2 = 0;
// First call to get the size of module array needed
if (EnumProcessModules(process_handle, nullptr, 0, &size_1)) {
MallocedBuffer<HMODULE> modules(size_1);
// Second call to populate the module array
if (EnumProcessModules(process_handle, modules.data, size_1, &size_2)) {
for (DWORD i = 0;
i < (size_1 / sizeof(HMODULE)) && i < (size_2 / sizeof(HMODULE));
i++) {
WCHAR module_name[MAX_PATH];
// Obtain and report the full pathname for each module
if (GetModuleFileNameExW(process_handle,
modules.data[i],
module_name,
arraysize(module_name) / sizeof(WCHAR))) {
DWORD size = WideCharToMultiByte(
CP_UTF8, 0, module_name, -1, nullptr, 0, nullptr, nullptr);
char* str = new char[size];
WideCharToMultiByte(
CP_UTF8, 0, module_name, -1, str, size, nullptr, nullptr);
list.push_back(str);
}
}
}
}
// Release the handle to the process.
CloseHandle(process_handle);
#endif
return list;
}
} // namespace node
extern "C" void __DumpBacktrace(FILE* fp) {
node::DumpBacktrace(fp);
}