mirror of
https://github.com/zebrajr/node.git
synced 2025-12-06 12:20:27 +01:00
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>
540 lines
16 KiB
C++
540 lines
16 KiB
C++
#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 it’s
|
||
// 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);
|
||
}
|