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