1 #include "debug_utils-inl.h" // NOLINT(build/include)
2 #include "env-inl.h"
3 #include "node_internals.h"
4 #include "util.h"
5
6 #ifdef __POSIX__
7 #if defined(__linux__)
8 #include <features.h>
9 #endif
10
11 #ifdef __ANDROID__
12 #include <android/log.h>
13 #endif
14
15 #if defined(__linux__) && !defined(__GLIBC__) || \
16 defined(__UCLIBC__) || \
17 defined(_AIX)
18 #define HAVE_EXECINFO_H 0
19 #else
20 #define HAVE_EXECINFO_H 1
21 #endif
22
23 #if HAVE_EXECINFO_H
24 #include <cxxabi.h>
25 #include <dlfcn.h>
26 #include <execinfo.h>
27 #include <unistd.h>
28 #include <sys/mman.h>
29 #include <cstdio>
30 #endif
31
32 #endif // __POSIX__
33
34 #if defined(__linux__) || defined(__sun) || \
35 defined(__FreeBSD__) || defined(__OpenBSD__) || \
36 defined(__DragonFly__)
37 #include <link.h>
38 #endif
39
40 #ifdef __APPLE__
41 #include <mach-o/dyld.h> // _dyld_get_image_name()
42 #endif // __APPLE__
43
44 #ifdef _AIX
45 #include <sys/ldr.h> // ld_info structure
46 #endif // _AIX
47
48 #ifdef _WIN32
49 #include <Lm.h>
50 #include <Windows.h>
51 #include <dbghelp.h>
52 #include <process.h>
53 #include <psapi.h>
54 #include <tchar.h>
55 #endif // _WIN32
56
57 namespace node {
58 namespace per_process {
59 EnabledDebugList enabled_debug_list;
60 }
61
Parse(std::shared_ptr<KVStore> env_vars,v8::Isolate * isolate)62 void EnabledDebugList::Parse(std::shared_ptr<KVStore> env_vars,
63 v8::Isolate* isolate) {
64 std::string cats;
65 credentials::SafeGetenv("NODE_DEBUG_NATIVE", &cats, env_vars, isolate);
66 Parse(cats);
67 }
68
Parse(const std::string & cats)69 void EnabledDebugList::Parse(const std::string& cats) {
70 std::string debug_categories = cats;
71 while (!debug_categories.empty()) {
72 std::string::size_type comma_pos = debug_categories.find(',');
73 std::string wanted = ToLower(debug_categories.substr(0, comma_pos));
74
75 #define V(name) \
76 { \
77 static const std::string available_category = ToLower(#name); \
78 if (available_category.find(wanted) != std::string::npos) \
79 set_enabled(DebugCategory::name); \
80 }
81
82 DEBUG_CATEGORY_NAMES(V)
83 #undef V
84
85 if (comma_pos == std::string::npos) break;
86 // Use everything after the `,` as the list for the next iteration.
87 debug_categories = debug_categories.substr(comma_pos + 1);
88 }
89 }
90
91 #ifdef __POSIX__
92 #if HAVE_EXECINFO_H
93 class PosixSymbolDebuggingContext final : public NativeSymbolDebuggingContext {
94 public:
PosixSymbolDebuggingContext()95 PosixSymbolDebuggingContext() : pagesize_(getpagesize()) { }
96
LookupSymbol(void * address)97 SymbolInfo LookupSymbol(void* address) override {
98 Dl_info info;
99 const bool have_info = dladdr(address, &info);
100 SymbolInfo ret;
101 if (!have_info)
102 return ret;
103
104 if (info.dli_sname != nullptr) {
105 if (char* demangled =
106 abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr)) {
107 ret.name = demangled;
108 free(demangled);
109 } else {
110 ret.name = info.dli_sname;
111 }
112 }
113
114 if (info.dli_fname != nullptr) {
115 ret.filename = info.dli_fname;
116 }
117
118 return ret;
119 }
120
IsMapped(void * address)121 bool IsMapped(void* address) override {
122 void* page_aligned = reinterpret_cast<void*>(
123 reinterpret_cast<uintptr_t>(address) & ~(pagesize_ - 1));
124 return msync(page_aligned, pagesize_, MS_ASYNC) == 0;
125 }
126
GetStackTrace(void ** frames,int count)127 int GetStackTrace(void** frames, int count) override {
128 return backtrace(frames, count);
129 }
130
131 private:
132 uintptr_t pagesize_;
133 };
134
135 std::unique_ptr<NativeSymbolDebuggingContext>
New()136 NativeSymbolDebuggingContext::New() {
137 return std::make_unique<PosixSymbolDebuggingContext>();
138 }
139
140 #else // HAVE_EXECINFO_H
141
142 std::unique_ptr<NativeSymbolDebuggingContext>
New()143 NativeSymbolDebuggingContext::New() {
144 return std::make_unique<NativeSymbolDebuggingContext>();
145 }
146
147 #endif // HAVE_EXECINFO_H
148
149 #else // __POSIX__
150
151 class Win32SymbolDebuggingContext final : public NativeSymbolDebuggingContext {
152 public:
Win32SymbolDebuggingContext()153 Win32SymbolDebuggingContext() {
154 current_process_ = GetCurrentProcess();
155 USE(SymInitialize(current_process_, nullptr, true));
156 }
157
~Win32SymbolDebuggingContext()158 ~Win32SymbolDebuggingContext() override {
159 USE(SymCleanup(current_process_));
160 }
161
162 using NameAndDisplacement = std::pair<std::string, DWORD64>;
WrappedSymFromAddr(DWORD64 dwAddress) const163 NameAndDisplacement WrappedSymFromAddr(DWORD64 dwAddress) const {
164 // Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-symbol-information-by-address
165 // Patches:
166 // Use `fprintf(stderr, ` instead of `printf`
167 // `sym.filename = pSymbol->Name` on success
168 // `current_process_` instead of `hProcess.
169 DWORD64 dwDisplacement = 0;
170 // Patch: made into arg - DWORD64 dwAddress = SOME_ADDRESS;
171
172 char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
173 const auto pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
174
175 pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
176 pSymbol->MaxNameLen = MAX_SYM_NAME;
177
178 if (SymFromAddr(current_process_, dwAddress, &dwDisplacement, pSymbol)) {
179 // SymFromAddr returned success
180 return NameAndDisplacement(pSymbol->Name, dwDisplacement);
181 } else {
182 // SymFromAddr failed
183 const DWORD error = GetLastError(); // "eat" the error anyway
184 #ifdef DEBUG
185 fprintf(stderr, "SymFromAddr returned error : %lu\n", error);
186 #endif
187 }
188 // End MSDN code
189
190 return NameAndDisplacement();
191 }
192
WrappedGetLine(DWORD64 dwAddress) const193 SymbolInfo WrappedGetLine(DWORD64 dwAddress) const {
194 SymbolInfo sym{};
195
196 // Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-symbol-information-by-address
197 // Patches:
198 // Use `fprintf(stderr, ` instead of `printf`.
199 // Assign values to `sym` on success.
200 // `current_process_` instead of `hProcess.
201
202 // Patch: made into arg - DWORD64 dwAddress;
203 DWORD dwDisplacement;
204 IMAGEHLP_LINE64 line;
205
206 SymSetOptions(SYMOPT_LOAD_LINES);
207
208 line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
209 // Patch: made into arg - dwAddress = 0x1000000;
210
211 if (SymGetLineFromAddr64(current_process_, dwAddress,
212 &dwDisplacement, &line)) {
213 // SymGetLineFromAddr64 returned success
214 sym.filename = line.FileName;
215 sym.line = line.LineNumber;
216 } else {
217 // SymGetLineFromAddr64 failed
218 const DWORD error = GetLastError(); // "eat" the error anyway
219 #ifdef DEBUG
220 fprintf(stderr, "SymGetLineFromAddr64 returned error : %lu\n", error);
221 #endif
222 }
223 // End MSDN code
224
225 return sym;
226 }
227
228 // Fills the SymbolInfo::name of the io/out argument `sym`
WrappedUnDecorateSymbolName(const char * name) const229 std::string WrappedUnDecorateSymbolName(const char* name) const {
230 // Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-undecorated-symbol-names
231 // Patches:
232 // Use `fprintf(stderr, ` instead of `printf`.
233 // return `szUndName` instead of `printf` on success
234 char szUndName[MAX_SYM_NAME];
235 if (UnDecorateSymbolName(name, szUndName, sizeof(szUndName),
236 UNDNAME_COMPLETE)) {
237 // UnDecorateSymbolName returned success
238 return szUndName;
239 } else {
240 // UnDecorateSymbolName failed
241 const DWORD error = GetLastError(); // "eat" the error anyway
242 #ifdef DEBUG
243 fprintf(stderr, "UnDecorateSymbolName returned error %lu\n", error);
244 #endif
245 }
246 return nullptr;
247 }
248
LookupSymbol(void * address)249 SymbolInfo LookupSymbol(void* address) override {
250 const DWORD64 dw_address = reinterpret_cast<DWORD64>(address);
251 SymbolInfo ret = WrappedGetLine(dw_address);
252 std::tie(ret.name, ret.dis) = WrappedSymFromAddr(dw_address);
253 if (!ret.name.empty()) {
254 ret.name = WrappedUnDecorateSymbolName(ret.name.c_str());
255 }
256 return ret;
257 }
258
IsMapped(void * address)259 bool IsMapped(void* address) override {
260 MEMORY_BASIC_INFORMATION info;
261
262 if (VirtualQuery(address, &info, sizeof(info)) != sizeof(info))
263 return false;
264
265 return info.State == MEM_COMMIT && info.Protect != 0;
266 }
267
GetStackTrace(void ** frames,int count)268 int GetStackTrace(void** frames, int count) override {
269 return CaptureStackBackTrace(0, count, frames, nullptr);
270 }
271
272 Win32SymbolDebuggingContext(const Win32SymbolDebuggingContext&) = delete;
273 Win32SymbolDebuggingContext(Win32SymbolDebuggingContext&&) = delete;
274 Win32SymbolDebuggingContext operator=(const Win32SymbolDebuggingContext&)
275 = delete;
276 Win32SymbolDebuggingContext operator=(Win32SymbolDebuggingContext&&)
277 = delete;
278
279 private:
280 HANDLE current_process_;
281 };
282
283 std::unique_ptr<NativeSymbolDebuggingContext>
New()284 NativeSymbolDebuggingContext::New() {
285 return std::unique_ptr<NativeSymbolDebuggingContext>(
286 new Win32SymbolDebuggingContext());
287 }
288
289 #endif // __POSIX__
290
Display() const291 std::string NativeSymbolDebuggingContext::SymbolInfo::Display() const {
292 std::ostringstream oss;
293 oss << name;
294 if (dis != 0) {
295 oss << "+" << dis;
296 }
297 if (!filename.empty()) {
298 oss << " [" << filename << ']';
299 }
300 if (line != 0) {
301 oss << ":L" << line;
302 }
303 return oss.str();
304 }
305
DumpBacktrace(FILE * fp)306 void DumpBacktrace(FILE* fp) {
307 auto sym_ctx = NativeSymbolDebuggingContext::New();
308 void* frames[256];
309 const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
310 for (int i = 1; i < size; i += 1) {
311 void* frame = frames[i];
312 NativeSymbolDebuggingContext::SymbolInfo s = sym_ctx->LookupSymbol(frame);
313 fprintf(fp, "%2d: %p %s\n", i, frame, s.Display().c_str());
314 }
315 }
316
CheckedUvLoopClose(uv_loop_t * loop)317 void CheckedUvLoopClose(uv_loop_t* loop) {
318 if (uv_loop_close(loop) == 0) return;
319
320 PrintLibuvHandleInformation(loop, stderr);
321
322 fflush(stderr);
323 // Finally, abort.
324 UNREACHABLE("uv_loop_close() while having open handles");
325 }
326
PrintLibuvHandleInformation(uv_loop_t * loop,FILE * stream)327 void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream) {
328 struct Info {
329 std::unique_ptr<NativeSymbolDebuggingContext> ctx;
330 FILE* stream;
331 size_t num_handles;
332 };
333
334 Info info { NativeSymbolDebuggingContext::New(), stream, 0 };
335
336 fprintf(stream, "uv loop at [%p] has open handles:\n", loop);
337
338 uv_walk(loop, [](uv_handle_t* handle, void* arg) {
339 Info* info = static_cast<Info*>(arg);
340 NativeSymbolDebuggingContext* sym_ctx = info->ctx.get();
341 FILE* stream = info->stream;
342 info->num_handles++;
343
344 fprintf(stream, "[%p] %s%s\n", handle, uv_handle_type_name(handle->type),
345 uv_is_active(handle) ? " (active)" : "");
346
347 void* close_cb = reinterpret_cast<void*>(handle->close_cb);
348 fprintf(stream, "\tClose callback: %p %s\n",
349 close_cb, sym_ctx->LookupSymbol(close_cb).Display().c_str());
350
351 fprintf(stream, "\tData: %p %s\n",
352 handle->data, sym_ctx->LookupSymbol(handle->data).Display().c_str());
353
354 // We are also interested in the first field of what `handle->data`
355 // points to, because for C++ code that is usually the virtual table pointer
356 // and gives us information about the exact kind of object we're looking at.
357 void* first_field = nullptr;
358 // `handle->data` might be any value, including `nullptr`, or something
359 // cast from a completely different type; therefore, check that it’s
360 // dereferenceable first.
361 if (sym_ctx->IsMapped(handle->data))
362 first_field = *reinterpret_cast<void**>(handle->data);
363
364 if (first_field != nullptr) {
365 fprintf(stream, "\t(First field): %p %s\n",
366 first_field, sym_ctx->LookupSymbol(first_field).Display().c_str());
367 }
368 }, &info);
369
370 fprintf(stream, "uv loop at [%p] has %zu open handles in total\n",
371 loop, info.num_handles);
372 }
373
GetLoadedLibraries()374 std::vector<std::string> NativeSymbolDebuggingContext::GetLoadedLibraries() {
375 std::vector<std::string> list;
376 #if defined(__linux__) || defined(__FreeBSD__) || \
377 defined(__OpenBSD__) || defined(__DragonFly__)
378 dl_iterate_phdr(
379 [](struct dl_phdr_info* info, size_t size, void* data) {
380 auto list = static_cast<std::vector<std::string>*>(data);
381 if (*info->dlpi_name != '\0') {
382 list->emplace_back(info->dlpi_name);
383 }
384 return 0;
385 },
386 &list);
387 #elif __APPLE__
388 uint32_t i = 0;
389 for (const char* name = _dyld_get_image_name(i); name != nullptr;
390 name = _dyld_get_image_name(++i)) {
391 list.emplace_back(name);
392 }
393
394 #elif _AIX
395 // We can't tell in advance how large the buffer needs to be.
396 // Retry until we reach too large a size (1Mb).
397 const unsigned int kBufferGrowStep = 4096;
398 MallocedBuffer<char> buffer(kBufferGrowStep);
399 int rc = -1;
400 do {
401 rc = loadquery(L_GETINFO, buffer.data, buffer.size);
402 if (rc == 0) break;
403 buffer = MallocedBuffer<char>(buffer.size + kBufferGrowStep);
404 } while (buffer.size < 1024 * 1024);
405
406 if (rc == 0) {
407 char* buf = buffer.data;
408 ld_info* cur_info = nullptr;
409 do {
410 std::ostringstream str;
411 cur_info = reinterpret_cast<ld_info*>(buf);
412 char* member_name = cur_info->ldinfo_filename +
413 strlen(cur_info->ldinfo_filename) + 1;
414 if (*member_name != '\0') {
415 str << cur_info->ldinfo_filename << "(" << member_name << ")";
416 list.emplace_back(str.str());
417 str.str("");
418 } else {
419 list.emplace_back(cur_info->ldinfo_filename);
420 }
421 buf += cur_info->ldinfo_next;
422 } while (cur_info->ldinfo_next != 0);
423 }
424 #elif __sun
425 Link_map* p;
426
427 if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &p) != -1) {
428 for (Link_map* l = p; l != nullptr; l = l->l_next) {
429 list.emplace_back(l->l_name);
430 }
431 }
432
433 #elif _WIN32
434 // Windows implementation - get a handle to the process.
435 HANDLE process_handle = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
436 FALSE, GetCurrentProcessId());
437 if (process_handle == nullptr) {
438 // Cannot proceed, return an empty list.
439 return list;
440 }
441 // Get a list of all the modules in this process
442 DWORD size_1 = 0;
443 DWORD size_2 = 0;
444 // First call to get the size of module array needed
445 if (EnumProcessModules(process_handle, nullptr, 0, &size_1)) {
446 MallocedBuffer<HMODULE> modules(size_1);
447
448 // Second call to populate the module array
449 if (EnumProcessModules(process_handle, modules.data, size_1, &size_2)) {
450 for (DWORD i = 0;
451 i < (size_1 / sizeof(HMODULE)) && i < (size_2 / sizeof(HMODULE));
452 i++) {
453 WCHAR module_name[MAX_PATH];
454 // Obtain and report the full pathname for each module
455 if (GetModuleFileNameExW(process_handle,
456 modules.data[i],
457 module_name,
458 arraysize(module_name) / sizeof(WCHAR))) {
459 DWORD size = WideCharToMultiByte(
460 CP_UTF8, 0, module_name, -1, nullptr, 0, nullptr, nullptr);
461 char* str = new char[size];
462 WideCharToMultiByte(
463 CP_UTF8, 0, module_name, -1, str, size, nullptr, nullptr);
464 list.emplace_back(str);
465 }
466 }
467 }
468 }
469
470 // Release the handle to the process.
471 CloseHandle(process_handle);
472 #endif
473 return list;
474 }
475
FWrite(FILE * file,const std::string & str)476 void FWrite(FILE* file, const std::string& str) {
477 auto simple_fwrite = [&]() {
478 // The return value is ignored because there's no good way to handle it.
479 fwrite(str.data(), str.size(), 1, file);
480 };
481
482 if (file != stderr && file != stdout) {
483 simple_fwrite();
484 return;
485 }
486 #ifdef _WIN32
487 HANDLE handle =
488 GetStdHandle(file == stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
489
490 // Check if stderr is something other than a tty/console
491 if (handle == INVALID_HANDLE_VALUE || handle == nullptr ||
492 uv_guess_handle(_fileno(file)) != UV_TTY) {
493 simple_fwrite();
494 return;
495 }
496
497 // Get required wide buffer size
498 int n = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0);
499
500 std::vector<wchar_t> wbuf(n);
501 MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), wbuf.data(), n);
502
503 WriteConsoleW(handle, wbuf.data(), n, nullptr, nullptr);
504 return;
505 #elif defined(__ANDROID__)
506 if (file == stderr) {
507 __android_log_print(ANDROID_LOG_ERROR, "nodejs", "%s", str.data());
508 return;
509 }
510 #endif
511 simple_fwrite();
512 }
513
514 } // namespace node
515
__DumpBacktrace(FILE * fp)516 extern "C" void __DumpBacktrace(FILE* fp) {
517 node::DumpBacktrace(fp);
518 }
519