node/src/debug_utils.cc
Joyee Cheung 30950864d3
src: print more information in C++ assertions
This patch:

- Introduce an internal GetCurrentStackTrace() utility to get the
  current JavaScript stack trace with best effort.
- Indent the assertion message so that is separated from the native
  stack trace for redability
- Print the JS stack trace when it's available

Previoiusly the abort message looks like this:

```
out/Release/node[24458]: ../../src/node_file.cc:2008:void node::fs::Ope
n(const FunctionCallbackInfo<v8::Value> &): Assertion `(argc) >= (3)' f
ailed.
 1: 0x1043fb9a4 node::Abort() [node]
 2: 0x1043fb6e4 node::PrintCaughtException(v8::Isolate*, v8::Local<v8::
Context>, v8::TryCatch const&) [node]
 3: 0x104407708 node::fs::Open(v8::FunctionCallbackInfo<v8::Value> cons
t&) [node]
 4: 0x104611e74 v8::internal::MaybeHandle<v8::internal::Object> v8::int
ernal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::
Isolate*, v8::internal::Handle<v8::internal::HeapObject
>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::intern
al::Handle<v8::internal::Object>, unsigned long*, int) [node
]
 5: 0x1046116c8 v8::internal::Builtin_HandleApiCall(int, unsigned long*
, v8::internal::Isolate*) [node]
 6: 0x104e9cb24 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [node]
 7: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node]
 8: 0x104e1250c Builtins_JSEntryTrampoline [node]
 9: 0x104e121f4 Builtins_JSEntry [node]
10: 0x1046ed54c v8::internal::(anonymous namespace)::Invoke(v8::interna
l::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&)
[node]
11: 0x1046edb60 v8::internal::Execution::CallScript(v8::internal::Isola
te*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Hand
le<v8::internal::Object>, v8::internal::Handle<v8::in
ternal::Object>) [node]
12: 0x1045a9fa0 v8::Script::Run(v8::Local<v8::Context>, v8::Local<v8::D
ata>) [node]
13: 0x1043efb68 node::contextify::ContextifyScript::EvalMachine(v8::Loc
al<v8::Context>, node::Environment*, long long, bool, bool, bool, v8::M
icrotaskQueue*, v8::FunctionCallbackInfo<v8::Value> const&) [node
]
14: 0x1043ef3e0 node::contextify::ContextifyScript::RunInContext(v8::Fu
nctionCallbackInfo<v8::Value> const&) [node]
15: 0x104611e74 v8::internal::MaybeHandle<v8::internal::Object> v8::int
ernal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::
Isolate*, v8::internal::Handle<v8::internal::HeapObject>
, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::interna
l::Handle<v8::internal::Object>, unsigned long*, int) [node
]
16: 0x1046116c8 v8::internal::Builtin_HandleApiCall(int, unsigned long*
, v8::internal::Isolate*) [node]
17: 0x104e9cb24 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [node]
18: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node]
19: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node]
20: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node]
21: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node]
22: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node]
23: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node]
24: 0x104e143e4 Builtins_InterpreterEntryTrampoline [node]
25: 0x104e1250c Builtins_JSEntryTrampoline [node]
26: 0x104e121f4 Builtins_JSEntry [node]
27: 0x1046ed54c v8::internal::(anonymous namespace)::Invoke(v8::interna
l::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&)
[node]
28: 0x1046ecdc8 v8::internal::Execution::Call(v8::internal::Isolate*, v
8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::int
ernal::Object>, int, v8::internal::Handle<v
8::internal::Object>*) [node]
29: 0x1045be23c v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8
::Value>, int, v8::Local<v8::Value>*) [node]
30: 0x1043df704 node::builtins::BuiltinLoader::CompileAndCall(v8::Local
<v8::Context>, char const*, node::Realm*) [node]
31: 0x10446f2d4 node::Realm::ExecuteBootstrapper(char const*) [node]
32: 0x1043c3378 node::StartExecution(node::Environment*, std::__1::func
tion<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const&
)>) [node]
33: 0x10432dc28 node::LoadEnvironment(node::Environment*, std::__1::fun
ction<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const
&)>) [node]
34: 0x10443d1f4 node::NodeMainInstance::Run(node::ExitCode*, node::Envi
ronment*) [node]
35: 0x10443cfd0 node::NodeMainInstance::Run() [node]
36: 0x1043c5d18 node::Start(int, char**) [node]
37: 0x19a027f28 start [/usr/lib/dyld]
[1]    24458 abort      out/Release/node -p "process.binding('fs').open
()"
```

Now it looks like this:

```
  #  out/Release/node[24856]: void node::fs::Open(const FunctionCallbac
kInfo<v8::Value> &) at ../../src/node_file.cc:2008
  #  Assertion failed: (argc) >= (3)

----- Native stack trace -----

 1: 0x1001efe64 node::Abort() [node]
 2: 0x1001efba4 node::PrintCaughtException(v8::Isolate*, v8::Local<v8::
Context>, v8::TryCatch const&) [node]
 3: 0x1001fb868 node::fs::Open(v8::FunctionCallbackInfo<v8::Value> cons
t&) [node]
 4: 0x100405fd4 v8::internal::MaybeHandle<v8::internal::Object> v8::int
ernal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::
Isolate*, v8::internal::Handle<v8::internal::HeapObject
>, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::intern
al::Handle<v8::internal::Object>, unsigned long*, int) [node
]
 5: 0x100405828 v8::internal::Builtin_HandleApiCall(int, unsigned long*
, v8::internal::Isolate*) [node]
 6: 0x100c90b24 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [node]
 7: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node]
 8: 0x100c0650c Builtins_JSEntryTrampoline [node]
 9: 0x100c061f4 Builtins_JSEntry [node]
10: 0x1004e16ac v8::internal::(anonymous namespace)::Invoke(v8::interna
l::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&)
[node]
11: 0x1004e1cc0 v8::internal::Execution::CallScript(v8::internal::Isola
te*, v8::internal::Handle<v8::internal::JSFunction>, v8::internal::Hand
le<v8::internal::Object>, v8::internal::Handle<v8::in
ternal::Object>) [node]
12: 0x10039e100 v8::Script::Run(v8::Local<v8::Context>, v8::Local<v8::D
ata>) [node]
13: 0x1001e4028 node::contextify::ContextifyScript::EvalMachine(v8::Loc
al<v8::Context>, node::Environment*, long long, bool, bool, bool, v8::M
icrotaskQueue*, v8::FunctionCallbackInfo<v8::Value> const&) [node
]
14: 0x1001e38a0 node::contextify::ContextifyScript::RunInContext(v8::Fu
nctionCallbackInfo<v8::Value> const&) [node]
15: 0x100405fd4 v8::internal::MaybeHandle<v8::internal::Object> v8::int
ernal::(anonymous namespace)::HandleApiCallHelper<false>(v8::internal::
Isolate*, v8::internal::Handle<v8::internal::HeapObject>
, v8::internal::Handle<v8::internal::FunctionTemplateInfo>, v8::interna
l::Handle<v8::internal::Object>, unsigned long*, int) [node
]
16: 0x100405828 v8::internal::Builtin_HandleApiCall(int, unsigned long*
, v8::internal::Isolate*) [node]
17: 0x100c90b24 Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit [node]
18: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node]
19: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node]
20: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node]
21: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node]
22: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node]
23: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node]
24: 0x100c083e4 Builtins_InterpreterEntryTrampoline [node]
25: 0x100c0650c Builtins_JSEntryTrampoline [node]
26: 0x100c061f4 Builtins_JSEntry [node]
27: 0x1004e16ac v8::internal::(anonymous namespace)::Invoke(v8::interna
l::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&)
[node]
28: 0x1004e0f28 v8::internal::Execution::Call(v8::internal::Isolate*, v
8::internal::Handle<v8::internal::Object>, v8::internal::Handle<v8::int
ernal::Object>, int, v8::internal::Handle<v
8::internal::Object>*) [node]
29: 0x1003b239c v8::Function::Call(v8::Local<v8::Context>, v8::Local<v8
::Value>, int, v8::Local<v8::Value>*) [node]
30: 0x1001d3bc4 node::builtins::BuiltinLoader::CompileAndCall(v8::Local
<v8::Context>, char const*, node::Realm*) [node]
31: 0x100263434 node::Realm::ExecuteBootstrapper(char const*) [node]
32: 0x1001b7838 node::StartExecution(node::Environment*, std::__1::func
tion<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const&
)>) [node]
33: 0x100121c28 node::LoadEnvironment(node::Environment*, std::__1::fun
ction<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const
&)>) [node]
34: 0x100231354 node::NodeMainInstance::Run(node::ExitCode*, node::Envi
ronment*) [node]
35: 0x100231130 node::NodeMainInstance::Run() [node]
36: 0x1001ba1d8 node::Start(int, char**) [node]
37: 0x19a027f28 start [/usr/lib/dyld]

----- JavaScript stack trace -----

1: [eval]:1:23
2: runScriptInThisContext (node:internal/vm:144:10)
3: node:internal/process/execution:109:14
4: [eval]-wrapper:6:24
5: runScript (node:internal/process/execution:92:62)
6: evalScript (node:internal/process/execution:123:10)
7: node:internal/main/eval_string:51:3

[1]    24856 abort      out/Release/node -p "process.binding('fs').open
()"
```

PR-URL: https://github.com/nodejs/node/pull/50242
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
2023-11-01 16:20:18 +00:00

540 lines
16 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-inl.h" // NOLINT(build/include)
#include "env-inl.h"
#include "node_internals.h"
#include "util.h"
#ifdef __POSIX__
#if defined(__linux__)
#include <features.h>
#endif
#ifdef __ANDROID__
#include <android/log.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__) || \
defined(__DragonFly__)
#include <link.h>
#endif
#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 {
namespace per_process {
EnabledDebugList enabled_debug_list;
}
using v8::Local;
using v8::StackTrace;
void EnabledDebugList::Parse(std::shared_ptr<KVStore> env_vars,
v8::Isolate* isolate) {
std::string cats;
credentials::SafeGetenv("NODE_DEBUG_NATIVE", &cats, env_vars, isolate);
Parse(cats);
}
void EnabledDebugList::Parse(const std::string& cats) {
std::string debug_categories = cats;
while (!debug_categories.empty()) {
std::string::size_type comma_pos = debug_categories.find(',');
std::string wanted = ToLower(debug_categories.substr(0, comma_pos));
#define V(name) \
{ \
static const std::string available_category = ToLower(#name); \
if (available_category.find(wanted) != std::string::npos) \
set_enabled(DebugCategory::name); \
}
DEBUG_CATEGORY_NAMES(V)
#undef V
if (comma_pos == std::string::npos) break;
// Use everything after the `,` as the list for the next iteration.
debug_categories = debug_categories.substr(comma_pos + 1);
}
}
#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::make_unique<PosixSymbolDebuggingContext>();
}
#else // HAVE_EXECINFO_H
std::unique_ptr<NativeSymbolDebuggingContext>
NativeSymbolDebuggingContext::New() {
return std::make_unique<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 DumpNativeBacktrace(FILE* fp) {
fprintf(fp, "----- Native stack trace -----\n\n");
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 DumpJavaScriptBacktrace(FILE* fp) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
if (isolate == nullptr) {
return;
}
Local<StackTrace> stack;
if (!GetCurrentStackTrace(isolate).ToLocal(&stack)) {
return;
}
fprintf(fp, "\n----- JavaScript stack trace -----\n\n");
PrintStackTrace(isolate, stack, StackTracePrefix::kNumber);
fprintf(fp, "\n");
}
void CheckedUvLoopClose(uv_loop_t* loop) {
if (uv_loop_close(loop) == 0) return;
PrintLibuvHandleInformation(loop, stderr);
fflush(stderr);
// Finally, abort.
UNREACHABLE("uv_loop_close() while having open handles");
}
void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream) {
struct Info {
std::unique_ptr<NativeSymbolDebuggingContext> ctx;
FILE* stream;
size_t num_handles;
};
Info info { NativeSymbolDebuggingContext::New(), stream, 0 };
fprintf(stream, "uv loop at [%p] has open handles:\n", loop);
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;
info->num_handles++;
fprintf(stream, "[%p] %s%s\n", handle, uv_handle_type_name(handle->type),
uv_is_active(handle) ? " (active)" : "");
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 its
// dereferenceable 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);
fprintf(stream, "uv loop at [%p] has %zu open handles in total\n",
loop, info.num_handles);
}
std::vector<std::string> NativeSymbolDebuggingContext::GetLoadedLibraries() {
std::vector<std::string> list;
#if defined(__linux__) || defined(__FreeBSD__) || \
defined(__OpenBSD__) || defined(__DragonFly__)
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->emplace_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.emplace_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.emplace_back(str.str());
str.str("");
} else {
list.emplace_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.emplace_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.emplace_back(str);
}
}
}
}
// Release the handle to the process.
CloseHandle(process_handle);
#endif
return list;
}
void FWrite(FILE* file, const std::string& str) {
auto simple_fwrite = [&]() {
// The return value is ignored because there's no good way to handle it.
fwrite(str.data(), str.size(), 1, file);
};
if (file != stderr && file != stdout) {
simple_fwrite();
return;
}
#ifdef _WIN32
HANDLE handle =
GetStdHandle(file == stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
// Check if stderr is something other than a tty/console
if (handle == INVALID_HANDLE_VALUE || handle == nullptr ||
uv_guess_handle(_fileno(file)) != UV_TTY) {
simple_fwrite();
return;
}
// Get required wide buffer size
int n = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0);
std::vector<wchar_t> wbuf(n);
MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), wbuf.data(), n);
WriteConsoleW(handle, wbuf.data(), n, nullptr, nullptr);
return;
#elif defined(__ANDROID__)
if (file == stderr) {
__android_log_print(ANDROID_LOG_ERROR, "nodejs", "%s", str.data());
return;
}
#endif
simple_fwrite();
}
} // namespace node
extern "C" void __DumpBacktrace(FILE* fp) {
node::DumpNativeBacktrace(fp);
node::DumpJavaScriptBacktrace(fp);
}