• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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