1 | // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors |
2 | // Licensed under the MIT License: |
3 | // |
4 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | // of this software and associated documentation files (the "Software"), to deal |
6 | // in the Software without restriction, including without limitation the rights |
7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
8 | // copies of the Software, and to permit persons to whom the Software is |
9 | // furnished to do so, subject to the following conditions: |
10 | // |
11 | // The above copyright notice and this permission notice shall be included in |
12 | // all copies or substantial portions of the Software. |
13 | // |
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
20 | // THE SOFTWARE. |
21 | |
22 | #ifndef _GNU_SOURCE |
23 | #define _GNU_SOURCE |
24 | #endif |
25 | |
26 | #include "exception.h" |
27 | #include "string.h" |
28 | #include "debug.h" |
29 | #include "threadlocal.h" |
30 | #include "miniposix.h" |
31 | #include "function.h" |
32 | #include <stdlib.h> |
33 | #include <exception> |
34 | #include <new> |
35 | #include <signal.h> |
36 | #include <stdint.h> |
37 | #ifndef _WIN32 |
38 | #include <sys/mman.h> |
39 | #endif |
40 | #include "io.h" |
41 | |
42 | #if !KJ_NO_RTTI |
43 | #include <typeinfo> |
44 | #endif |
45 | #if __GNUC__ |
46 | #include <cxxabi.h> |
47 | #endif |
48 | |
49 | #if (__linux__ && __GLIBC__ && !__UCLIBC__) || __APPLE__ |
50 | #define KJ_HAS_BACKTRACE 1 |
51 | #include <execinfo.h> |
52 | #endif |
53 | |
54 | #if _WIN32 |
55 | #define WIN32_LEAN_AND_MEAN |
56 | #include <windows.h> |
57 | #include "windows-sanity.h" |
58 | #include <dbghelp.h> |
59 | #endif |
60 | |
61 | #if (__linux__ || __APPLE__) |
62 | #include <stdio.h> |
63 | #include <pthread.h> |
64 | #endif |
65 | |
66 | #if KJ_HAS_LIBDL |
67 | #include "dlfcn.h" |
68 | #endif |
69 | |
70 | namespace kj { |
71 | |
72 | StringPtr KJ_STRINGIFY(LogSeverity severity) { |
73 | static const char* SEVERITY_STRINGS[] = { |
74 | "info" , |
75 | "warning" , |
76 | "error" , |
77 | "fatal" , |
78 | "debug" |
79 | }; |
80 | |
81 | return SEVERITY_STRINGS[static_cast<uint>(severity)]; |
82 | } |
83 | |
84 | #if _WIN32 && _M_X64 |
85 | // Currently the Win32 stack-trace code only supports x86_64. We could easily extend it to support |
86 | // i386 as well but it requires some code changes around how we read the context to start the |
87 | // trace. |
88 | |
89 | namespace { |
90 | |
91 | struct Dbghelp { |
92 | // Load dbghelp.dll dynamically since we don't really need it, it's just for debugging. |
93 | |
94 | HINSTANCE lib; |
95 | |
96 | BOOL (WINAPI *symInitialize)(HANDLE hProcess,PCSTR UserSearchPath,BOOL fInvadeProcess); |
97 | BOOL (WINAPI *stackWalk64)( |
98 | DWORD MachineType,HANDLE hProcess,HANDLE hThread, |
99 | LPSTACKFRAME64 StackFrame,PVOID ContextRecord, |
100 | PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, |
101 | PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, |
102 | PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, |
103 | PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); |
104 | PVOID (WINAPI *symFunctionTableAccess64)(HANDLE hProcess,DWORD64 AddrBase); |
105 | DWORD64 (WINAPI *symGetModuleBase64)(HANDLE hProcess,DWORD64 qwAddr); |
106 | BOOL (WINAPI *symGetLineFromAddr64)( |
107 | HANDLE hProcess,DWORD64 qwAddr,PDWORD pdwDisplacement,PIMAGEHLP_LINE64 Line64); |
108 | |
109 | Dbghelp() |
110 | : lib(LoadLibraryA("dbghelp.dll" )), |
111 | symInitialize(lib == nullptr ? nullptr : |
112 | reinterpret_cast<decltype(symInitialize)>( |
113 | GetProcAddress(lib, "SymInitialize" ))), |
114 | stackWalk64(symInitialize == nullptr ? nullptr : |
115 | reinterpret_cast<decltype(stackWalk64)>( |
116 | GetProcAddress(lib, "StackWalk64" ))), |
117 | symFunctionTableAccess64(symInitialize == nullptr ? nullptr : |
118 | reinterpret_cast<decltype(symFunctionTableAccess64)>( |
119 | GetProcAddress(lib, "SymFunctionTableAccess64" ))), |
120 | symGetModuleBase64(symInitialize == nullptr ? nullptr : |
121 | reinterpret_cast<decltype(symGetModuleBase64)>( |
122 | GetProcAddress(lib, "SymGetModuleBase64" ))), |
123 | symGetLineFromAddr64(symInitialize == nullptr ? nullptr : |
124 | reinterpret_cast<decltype(symGetLineFromAddr64)>( |
125 | GetProcAddress(lib, "SymGetLineFromAddr64" ))) { |
126 | if (symInitialize != nullptr) { |
127 | symInitialize(GetCurrentProcess(), NULL, TRUE); |
128 | } |
129 | } |
130 | }; |
131 | |
132 | const Dbghelp& getDbghelp() { |
133 | static Dbghelp dbghelp; |
134 | return dbghelp; |
135 | } |
136 | |
137 | ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount, |
138 | HANDLE thread, CONTEXT& context) { |
139 | const Dbghelp& dbghelp = getDbghelp(); |
140 | if (dbghelp.stackWalk64 == nullptr || |
141 | dbghelp.symFunctionTableAccess64 == nullptr || |
142 | dbghelp.symGetModuleBase64 == nullptr) { |
143 | return nullptr; |
144 | } |
145 | |
146 | STACKFRAME64 frame; |
147 | memset(&frame, 0, sizeof(frame)); |
148 | |
149 | frame.AddrPC.Offset = context.Rip; |
150 | frame.AddrPC.Mode = AddrModeFlat; |
151 | frame.AddrStack.Offset = context.Rsp; |
152 | frame.AddrStack.Mode = AddrModeFlat; |
153 | frame.AddrFrame.Offset = context.Rbp; |
154 | frame.AddrFrame.Mode = AddrModeFlat; |
155 | |
156 | HANDLE process = GetCurrentProcess(); |
157 | |
158 | uint count = 0; |
159 | for (; count < space.size(); count++) { |
160 | if (!dbghelp.stackWalk64(IMAGE_FILE_MACHINE_AMD64, process, thread, |
161 | &frame, &context, NULL, dbghelp.symFunctionTableAccess64, |
162 | dbghelp.symGetModuleBase64, NULL)){ |
163 | break; |
164 | } |
165 | |
166 | // Subtract 1 from each address so that we identify the calling instructions, rather than the |
167 | // return addresses (which are typically the instruction after the call). |
168 | space[count] = reinterpret_cast<void*>(frame.AddrPC.Offset - 1); |
169 | } |
170 | |
171 | return space.slice(kj::min(ignoreCount, count), count); |
172 | } |
173 | |
174 | } // namespace |
175 | #endif |
176 | |
177 | ArrayPtr<void* const> getStackTrace(ArrayPtr<void*> space, uint ignoreCount) { |
178 | if (getExceptionCallback().stackTraceMode() == ExceptionCallback::StackTraceMode::NONE) { |
179 | return nullptr; |
180 | } |
181 | |
182 | #if _WIN32 && _M_X64 |
183 | CONTEXT context; |
184 | RtlCaptureContext(&context); |
185 | return getStackTrace(space, ignoreCount, GetCurrentThread(), context); |
186 | #elif KJ_HAS_BACKTRACE |
187 | size_t size = backtrace(space.begin(), space.size()); |
188 | for (auto& addr: space.slice(0, size)) { |
189 | // The addresses produced by backtrace() are return addresses, which means they point to the |
190 | // instruction immediately after the call. Invoking addr2line on these can be confusing because |
191 | // it often points to the next line. If the next instruction is inlined from another function, |
192 | // the trace can be extra-confusing, since now it claims to be in a function that was not |
193 | // actually on the call stack. If we subtract 1 from each address, though, we get a much more |
194 | // reasonable trace. This may cause the addresses to be invalid instruction pointers if the |
195 | // instructions were multi-byte, but it appears addr2line is able to cope with this. |
196 | addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) - 1); |
197 | } |
198 | return space.slice(kj::min(ignoreCount + 1, size), size); |
199 | #else |
200 | return nullptr; |
201 | #endif |
202 | } |
203 | |
204 | String stringifyStackTrace(ArrayPtr<void* const> trace) { |
205 | if (trace.size() == 0) return nullptr; |
206 | if (getExceptionCallback().stackTraceMode() != ExceptionCallback::StackTraceMode::FULL) { |
207 | return nullptr; |
208 | } |
209 | |
210 | #if _WIN32 && _M_X64 && _MSC_VER |
211 | |
212 | // Try to get file/line using SymGetLineFromAddr64(). We don't bother if we aren't on MSVC since |
213 | // this requires MSVC debug info. |
214 | // |
215 | // TODO(someday): We could perhaps shell out to addr2line on MinGW. |
216 | |
217 | const Dbghelp& dbghelp = getDbghelp(); |
218 | if (dbghelp.symGetLineFromAddr64 == nullptr) return nullptr; |
219 | |
220 | HANDLE process = GetCurrentProcess(); |
221 | |
222 | KJ_STACK_ARRAY(String, lines, trace.size(), 32, 32); |
223 | |
224 | for (auto i: kj::indices(trace)) { |
225 | IMAGEHLP_LINE64 lineInfo; |
226 | memset(&lineInfo, 0, sizeof(lineInfo)); |
227 | lineInfo.SizeOfStruct = sizeof(lineInfo); |
228 | if (dbghelp.symGetLineFromAddr64(process, reinterpret_cast<DWORD64>(trace[i]), NULL, &lineInfo)) { |
229 | lines[i] = kj::str('\n', lineInfo.FileName, ':', lineInfo.LineNumber); |
230 | } |
231 | } |
232 | |
233 | return strArray(lines, "" ); |
234 | |
235 | #elif (__linux__ || __APPLE__) && !__ANDROID__ |
236 | // We want to generate a human-readable stack trace. |
237 | |
238 | // TODO(someday): It would be really great if we could avoid farming out to another process |
239 | // and do this all in-process, but that may involve onerous requirements like large library |
240 | // dependencies or using -rdynamic. |
241 | |
242 | // The environment manipulation is not thread-safe, so lock a mutex. This could still be |
243 | // problematic if another thread is manipulating the environment in unrelated code, but there's |
244 | // not much we can do about that. This is debug-only anyway and only an issue when LD_PRELOAD |
245 | // is in use. |
246 | static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; |
247 | pthread_mutex_lock(&mutex); |
248 | KJ_DEFER(pthread_mutex_unlock(&mutex)); |
249 | |
250 | // Don't heapcheck / intercept syscalls. |
251 | const char* preload = getenv("LD_PRELOAD" ); |
252 | String oldPreload; |
253 | if (preload != nullptr) { |
254 | oldPreload = heapString(preload); |
255 | unsetenv("LD_PRELOAD" ); |
256 | } |
257 | KJ_DEFER(if (oldPreload != nullptr) { setenv("LD_PRELOAD" , oldPreload.cStr(), true); }); |
258 | |
259 | String lines[32]; |
260 | FILE* p = nullptr; |
261 | auto strTrace = strArray(trace, " " ); |
262 | |
263 | #if __linux__ |
264 | if (access("/proc/self/exe" , R_OK) < 0) { |
265 | // Apparently /proc is not available? |
266 | return nullptr; |
267 | } |
268 | |
269 | // Obtain symbolic stack trace using addr2line. |
270 | // TODO(cleanup): Use fork() and exec() or maybe our own Subprocess API (once it exists), to |
271 | // avoid depending on a shell. |
272 | p = popen(str("addr2line -e /proc/" , getpid(), "/exe " , strTrace).cStr(), "r" ); |
273 | #elif __APPLE__ |
274 | // The Mac OS X equivalent of addr2line is atos. |
275 | // (Internally, it uses the private CoreSymbolication.framework library.) |
276 | p = popen(str("xcrun atos -p " , getpid(), ' ', strTrace).cStr(), "r" ); |
277 | #endif |
278 | |
279 | if (p == nullptr) { |
280 | return nullptr; |
281 | } |
282 | |
283 | char line[512]; |
284 | size_t i = 0; |
285 | while (i < kj::size(lines) && fgets(line, sizeof(line), p) != nullptr) { |
286 | // Don't include exception-handling infrastructure or promise infrastructure in stack trace. |
287 | // addr2line output matches file names; atos output matches symbol names. |
288 | if (strstr(line, "kj/common.c++" ) != nullptr || |
289 | strstr(line, "kj/exception." ) != nullptr || |
290 | strstr(line, "kj/debug." ) != nullptr || |
291 | strstr(line, "kj/async." ) != nullptr || |
292 | strstr(line, "kj/async-prelude.h" ) != nullptr || |
293 | strstr(line, "kj/async-inl.h" ) != nullptr || |
294 | strstr(line, "kj::Exception" ) != nullptr || |
295 | strstr(line, "kj::_::Debug" ) != nullptr) { |
296 | continue; |
297 | } |
298 | |
299 | size_t len = strlen(line); |
300 | if (len > 0 && line[len-1] == '\n') line[len-1] = '\0'; |
301 | lines[i++] = str("\n " , trimSourceFilename(line), ": returning here" ); |
302 | } |
303 | |
304 | // Skip remaining input. |
305 | while (fgets(line, sizeof(line), p) != nullptr) {} |
306 | |
307 | pclose(p); |
308 | |
309 | return strArray(arrayPtr(lines, i), "" ); |
310 | |
311 | #else |
312 | return nullptr; |
313 | #endif |
314 | } |
315 | |
316 | String stringifyStackTraceAddresses(ArrayPtr<void* const> trace) { |
317 | #if KJ_HAS_LIBDL |
318 | return strArray(KJ_MAP(addr, trace) { |
319 | Dl_info info; |
320 | // Shared libraries are mapped near the end of the address space while the executable is mapped |
321 | // near the beginning. We want to print addresses in the executable as raw addresses, not |
322 | // offsets, since that's what addr2line expects for executables. For shared libraries it |
323 | // expects offsets. In any case, most frames are likely to be in the main executable so it |
324 | // makes the output cleaner if we don't repeatedly write its name. |
325 | if (reinterpret_cast<uintptr_t>(addr) >= 0x400000000000ull && dladdr(addr, &info)) { |
326 | uintptr_t offset = reinterpret_cast<uintptr_t>(addr) - |
327 | reinterpret_cast<uintptr_t>(info.dli_fbase); |
328 | return kj::str(info.dli_fname, '@', reinterpret_cast<void*>(offset)); |
329 | } else { |
330 | return kj::str(addr); |
331 | } |
332 | }, " " ); |
333 | #else |
334 | // TODO(someday): Support other platforms. |
335 | return kj::strArray(trace, " " ); |
336 | #endif |
337 | } |
338 | |
339 | StringPtr stringifyStackTraceAddresses(ArrayPtr<void* const> trace, ArrayPtr<char> scratch) { |
340 | // Version which writes into a pre-allocated buffer. This is safe for signal handlers to the |
341 | // extent that dladdr() is safe. |
342 | // |
343 | // TODO(cleanup): We should improve the KJ stringification framework so that there's a way to |
344 | // write this string directly into a larger message buffer with strPreallocated(). |
345 | |
346 | #if KJ_HAS_LIBDL |
347 | char* ptr = scratch.begin(); |
348 | char* limit = scratch.end() - 1; |
349 | |
350 | for (auto addr: trace) { |
351 | Dl_info info; |
352 | // Shared libraries are mapped near the end of the address space while the executable is mapped |
353 | // near the beginning. We want to print addresses in the executable as raw addresses, not |
354 | // offsets, since that's what addr2line expects for executables. For shared libraries it |
355 | // expects offsets. In any case, most frames are likely to be in the main executable so it |
356 | // makes the output cleaner if we don't repeatedly write its name. |
357 | if (reinterpret_cast<uintptr_t>(addr) >= 0x400000000000ull && dladdr(addr, &info)) { |
358 | uintptr_t offset = reinterpret_cast<uintptr_t>(addr) - |
359 | reinterpret_cast<uintptr_t>(info.dli_fbase); |
360 | ptr = _::fillLimited(ptr, limit, kj::StringPtr(info.dli_fname), "@0x"_kj , hex(offset)); |
361 | } else { |
362 | ptr = _::fillLimited(ptr, limit, toCharSequence(addr)); |
363 | } |
364 | |
365 | ptr = _::fillLimited(ptr, limit, " "_kj ); |
366 | } |
367 | *ptr = '\0'; |
368 | return StringPtr(scratch.begin(), ptr); |
369 | #else |
370 | // TODO(someday): Support other platforms. |
371 | return kj::strPreallocated(scratch, kj::delimited(trace, " " )); |
372 | #endif |
373 | } |
374 | |
375 | String getStackTrace() { |
376 | void* space[32]; |
377 | auto trace = getStackTrace(space, 2); |
378 | return kj::str(stringifyStackTraceAddresses(trace), stringifyStackTrace(trace)); |
379 | } |
380 | |
381 | #if _WIN32 && _M_X64 |
382 | namespace { |
383 | |
384 | DWORD mainThreadId = 0; |
385 | |
386 | BOOL WINAPI breakHandler(DWORD type) { |
387 | switch (type) { |
388 | case CTRL_C_EVENT: |
389 | case CTRL_BREAK_EVENT: { |
390 | HANDLE thread = OpenThread(THREAD_ALL_ACCESS, FALSE, mainThreadId); |
391 | if (thread != NULL) { |
392 | if (SuspendThread(thread) != (DWORD)-1) { |
393 | CONTEXT context; |
394 | memset(&context, 0, sizeof(context)); |
395 | context.ContextFlags = CONTEXT_FULL; |
396 | if (GetThreadContext(thread, &context)) { |
397 | void* traceSpace[32]; |
398 | auto trace = getStackTrace(traceSpace, 0, thread, context); |
399 | ResumeThread(thread); |
400 | auto message = kj::str("*** Received CTRL+C. stack: " , |
401 | stringifyStackTraceAddresses(trace), |
402 | stringifyStackTrace(trace), '\n'); |
403 | FdOutputStream(STDERR_FILENO).write(message.begin(), message.size()); |
404 | } else { |
405 | ResumeThread(thread); |
406 | } |
407 | } |
408 | CloseHandle(thread); |
409 | } |
410 | break; |
411 | } |
412 | default: |
413 | break; |
414 | } |
415 | |
416 | return FALSE; // still crash |
417 | } |
418 | |
419 | kj::StringPtr exceptionDescription(DWORD code) { |
420 | switch (code) { |
421 | case EXCEPTION_ACCESS_VIOLATION: return "access violation" ; |
422 | case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "array bounds exceeded" ; |
423 | case EXCEPTION_BREAKPOINT: return "breakpoint" ; |
424 | case EXCEPTION_DATATYPE_MISALIGNMENT: return "datatype misalignment" ; |
425 | case EXCEPTION_FLT_DENORMAL_OPERAND: return "denormal floating point operand" ; |
426 | case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "floating point division by zero" ; |
427 | case EXCEPTION_FLT_INEXACT_RESULT: return "inexact floating point result" ; |
428 | case EXCEPTION_FLT_INVALID_OPERATION: return "invalid floating point operation" ; |
429 | case EXCEPTION_FLT_OVERFLOW: return "floating point overflow" ; |
430 | case EXCEPTION_FLT_STACK_CHECK: return "floating point stack overflow" ; |
431 | case EXCEPTION_FLT_UNDERFLOW: return "floating point underflow" ; |
432 | case EXCEPTION_ILLEGAL_INSTRUCTION: return "illegal instruction" ; |
433 | case EXCEPTION_IN_PAGE_ERROR: return "page error" ; |
434 | case EXCEPTION_INT_DIVIDE_BY_ZERO: return "integer divided by zero" ; |
435 | case EXCEPTION_INT_OVERFLOW: return "integer overflow" ; |
436 | case EXCEPTION_INVALID_DISPOSITION: return "invalid disposition" ; |
437 | case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "noncontinuable exception" ; |
438 | case EXCEPTION_PRIV_INSTRUCTION: return "privileged instruction" ; |
439 | case EXCEPTION_SINGLE_STEP: return "single step" ; |
440 | case EXCEPTION_STACK_OVERFLOW: return "stack overflow" ; |
441 | default: return "(unknown exception code)" ; |
442 | } |
443 | } |
444 | |
445 | LONG WINAPI sehHandler(EXCEPTION_POINTERS* info) { |
446 | void* traceSpace[32]; |
447 | auto trace = getStackTrace(traceSpace, 0, GetCurrentThread(), *info->ContextRecord); |
448 | auto message = kj::str("*** Received structured exception #0x" , |
449 | hex(info->ExceptionRecord->ExceptionCode), ": " , |
450 | exceptionDescription(info->ExceptionRecord->ExceptionCode), |
451 | "; stack: " , |
452 | stringifyStackTraceAddresses(trace), |
453 | stringifyStackTrace(trace), '\n'); |
454 | FdOutputStream(STDERR_FILENO).write(message.begin(), message.size()); |
455 | return EXCEPTION_EXECUTE_HANDLER; // still crash |
456 | } |
457 | |
458 | } // namespace |
459 | |
460 | void printStackTraceOnCrash() { |
461 | mainThreadId = GetCurrentThreadId(); |
462 | KJ_WIN32(SetConsoleCtrlHandler(breakHandler, TRUE)); |
463 | SetUnhandledExceptionFilter(&sehHandler); |
464 | } |
465 | |
466 | #elif KJ_HAS_BACKTRACE |
467 | namespace { |
468 | |
469 | void crashHandler(int signo, siginfo_t* info, void* context) { |
470 | void* traceSpace[32]; |
471 | |
472 | // ignoreCount = 2 to ignore crashHandler() and signal trampoline. |
473 | auto trace = getStackTrace(traceSpace, 2); |
474 | |
475 | auto message = kj::str("*** Received signal #" , signo, ": " , strsignal(signo), |
476 | "\nstack: " , stringifyStackTraceAddresses(trace), |
477 | stringifyStackTrace(trace), '\n'); |
478 | |
479 | FdOutputStream(STDERR_FILENO).write(message.begin(), message.size()); |
480 | _exit(1); |
481 | } |
482 | |
483 | } // namespace |
484 | |
485 | void printStackTraceOnCrash() { |
486 | // Set up alternate signal stack so that stack overflows can be handled. |
487 | stack_t stack; |
488 | memset(&stack, 0, sizeof(stack)); |
489 | |
490 | #ifndef MAP_ANONYMOUS |
491 | #define MAP_ANONYMOUS MAP_ANON |
492 | #endif |
493 | #ifndef MAP_GROWSDOWN |
494 | #define MAP_GROWSDOWN 0 |
495 | #endif |
496 | |
497 | stack.ss_size = 65536; |
498 | // Note: ss_sp is char* on FreeBSD, void* on Linux and OSX. |
499 | stack.ss_sp = reinterpret_cast<char*>(mmap( |
500 | nullptr, stack.ss_size, PROT_READ | PROT_WRITE, |
501 | MAP_ANONYMOUS | MAP_PRIVATE | MAP_GROWSDOWN, -1, 0)); |
502 | KJ_SYSCALL(sigaltstack(&stack, nullptr)); |
503 | |
504 | // Catch all relevant signals. |
505 | struct sigaction action; |
506 | memset(&action, 0, sizeof(action)); |
507 | |
508 | action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER | SA_RESETHAND; |
509 | action.sa_sigaction = &crashHandler; |
510 | |
511 | // Dump stack on common "crash" signals. |
512 | KJ_SYSCALL(sigaction(SIGSEGV, &action, nullptr)); |
513 | KJ_SYSCALL(sigaction(SIGBUS, &action, nullptr)); |
514 | KJ_SYSCALL(sigaction(SIGFPE, &action, nullptr)); |
515 | KJ_SYSCALL(sigaction(SIGABRT, &action, nullptr)); |
516 | KJ_SYSCALL(sigaction(SIGILL, &action, nullptr)); |
517 | |
518 | // Dump stack on unimplemented syscalls -- useful in seccomp sandboxes. |
519 | KJ_SYSCALL(sigaction(SIGSYS, &action, nullptr)); |
520 | |
521 | #ifdef KJ_DEBUG |
522 | // Dump stack on keyboard interrupt -- useful for infinite loops. Only in debug mode, though, |
523 | // because stack traces on ctrl+c can be obnoxious for, say, command-line tools. |
524 | KJ_SYSCALL(sigaction(SIGINT, &action, nullptr)); |
525 | #endif |
526 | } |
527 | #else |
528 | void printStackTraceOnCrash() { |
529 | } |
530 | #endif |
531 | |
532 | kj::StringPtr trimSourceFilename(kj::StringPtr filename) { |
533 | // Removes noisy prefixes from source code file name. |
534 | // |
535 | // The goal here is to produce the "canonical" filename given the filename returned by e.g. |
536 | // addr2line. addr2line gives us the full path of the file as passed on the compiler |
537 | // command-line, which in turn is affected by build system and by whether and where we're |
538 | // performing an out-of-tree build. |
539 | // |
540 | // To deal with all this, we look for directory names in the path which we recognize to be |
541 | // locations that represent roots of the source tree. We strip said root and everything before |
542 | // it. |
543 | // |
544 | // On Windows, we often get filenames containing backslashes. Since we aren't allowed to allocate |
545 | // a new string here, we can't do much about this, so our returned "canonical" name will |
546 | // unfortunately end up with backslashes. |
547 | |
548 | static constexpr const char* ROOTS[] = { |
549 | "ekam-provider/canonical/" , // Ekam source file. |
550 | "ekam-provider/c++header/" , // Ekam include file. |
551 | "src/" , // Non-Ekam source root. |
552 | "tmp/" , // Non-Ekam generated code. |
553 | #if _WIN32 |
554 | "src\\" , // Win32 source root. |
555 | "tmp\\" , // Win32 generated code. |
556 | #endif |
557 | }; |
558 | |
559 | retry: |
560 | for (size_t i: kj::indices(filename)) { |
561 | if (i == 0 || filename[i-1] == '/' |
562 | #if _WIN32 |
563 | || filename[i-1] == '\\' |
564 | #endif |
565 | ) { |
566 | // We're at the start of a directory name. Check for valid prefixes. |
567 | for (kj::StringPtr root: ROOTS) { |
568 | if (filename.slice(i).startsWith(root)) { |
569 | filename = filename.slice(i + root.size()); |
570 | |
571 | // We should keep searching to find the last instance of a root name. `i` is no longer |
572 | // a valid index for `filename` so start the loop over. |
573 | goto retry; |
574 | } |
575 | } |
576 | } |
577 | } |
578 | |
579 | return filename; |
580 | } |
581 | |
582 | StringPtr KJ_STRINGIFY(Exception::Type type) { |
583 | static const char* TYPE_STRINGS[] = { |
584 | "failed" , |
585 | "overloaded" , |
586 | "disconnected" , |
587 | "unimplemented" |
588 | }; |
589 | |
590 | return TYPE_STRINGS[static_cast<uint>(type)]; |
591 | } |
592 | |
593 | String KJ_STRINGIFY(const Exception& e) { |
594 | uint contextDepth = 0; |
595 | |
596 | Maybe<const Exception::Context&> contextPtr = e.getContext(); |
597 | for (;;) { |
598 | KJ_IF_MAYBE(c, contextPtr) { |
599 | ++contextDepth; |
600 | contextPtr = c->next; |
601 | } else { |
602 | break; |
603 | } |
604 | } |
605 | |
606 | Array<String> contextText = heapArray<String>(contextDepth); |
607 | |
608 | contextDepth = 0; |
609 | contextPtr = e.getContext(); |
610 | for (;;) { |
611 | KJ_IF_MAYBE(c, contextPtr) { |
612 | contextText[contextDepth++] = |
613 | str(c->file, ":" , c->line, ": context: " , c->description, "\n" ); |
614 | contextPtr = c->next; |
615 | } else { |
616 | break; |
617 | } |
618 | } |
619 | |
620 | return str(strArray(contextText, "" ), |
621 | e.getFile(), ":" , e.getLine(), ": " , e.getType(), |
622 | e.getDescription() == nullptr ? "" : ": " , e.getDescription(), |
623 | e.getStackTrace().size() > 0 ? "\nstack: " : "" , |
624 | stringifyStackTraceAddresses(e.getStackTrace()), |
625 | stringifyStackTrace(e.getStackTrace())); |
626 | } |
627 | |
628 | Exception::Exception(Type type, const char* file, int line, String description) noexcept |
629 | : file(trimSourceFilename(file).cStr()), line(line), type(type), description(mv(description)), |
630 | traceCount(0) {} |
631 | |
632 | Exception::Exception(Type type, String file, int line, String description) noexcept |
633 | : ownFile(kj::mv(file)), file(trimSourceFilename(ownFile).cStr()), line(line), type(type), |
634 | description(mv(description)), traceCount(0) {} |
635 | |
636 | Exception::Exception(const Exception& other) noexcept |
637 | : file(other.file), line(other.line), type(other.type), |
638 | description(heapString(other.description)), traceCount(other.traceCount) { |
639 | if (file == other.ownFile.cStr()) { |
640 | ownFile = heapString(other.ownFile); |
641 | file = ownFile.cStr(); |
642 | } |
643 | |
644 | memcpy(trace, other.trace, sizeof(trace[0]) * traceCount); |
645 | |
646 | KJ_IF_MAYBE(c, other.context) { |
647 | context = heap(**c); |
648 | } |
649 | } |
650 | |
651 | Exception::~Exception() noexcept {} |
652 | |
653 | Exception::Context::Context(const Context& other) noexcept |
654 | : file(other.file), line(other.line), description(str(other.description)) { |
655 | KJ_IF_MAYBE(n, other.next) { |
656 | next = heap(**n); |
657 | } |
658 | } |
659 | |
660 | void Exception::wrapContext(const char* file, int line, String&& description) { |
661 | context = heap<Context>(file, line, mv(description), mv(context)); |
662 | } |
663 | |
664 | void Exception::extendTrace(uint ignoreCount) { |
665 | KJ_STACK_ARRAY(void*, newTraceSpace, kj::size(trace) + ignoreCount + 1, |
666 | sizeof(trace)/sizeof(trace[0]) + 8, 128); |
667 | |
668 | auto newTrace = kj::getStackTrace(newTraceSpace, ignoreCount + 1); |
669 | if (newTrace.size() > ignoreCount + 2) { |
670 | // Remove suffix that won't fit into our static-sized trace. |
671 | newTrace = newTrace.slice(0, kj::min(kj::size(trace) - traceCount, newTrace.size())); |
672 | |
673 | // Copy the rest into our trace. |
674 | memcpy(trace + traceCount, newTrace.begin(), newTrace.asBytes().size()); |
675 | traceCount += newTrace.size(); |
676 | } |
677 | } |
678 | |
679 | void Exception::truncateCommonTrace() { |
680 | if (traceCount > 0) { |
681 | // Create a "reference" stack trace that is a little bit deeper than the one in the exception. |
682 | void* refTraceSpace[sizeof(this->trace) / sizeof(this->trace[0]) + 4]; |
683 | auto refTrace = kj::getStackTrace(refTraceSpace, 0); |
684 | |
685 | // We expect that the deepest frame in the exception's stack trace should be somewhere in our |
686 | // own trace, since our own trace has a deeper limit. Search for it. |
687 | for (uint i = refTrace.size(); i > 0; i--) { |
688 | if (refTrace[i-1] == trace[traceCount-1]) { |
689 | // See how many frames match. |
690 | for (uint j = 0; j < i; j++) { |
691 | if (j >= traceCount) { |
692 | // We matched the whole trace, apparently? |
693 | traceCount = 0; |
694 | return; |
695 | } else if (refTrace[i-j-1] != trace[traceCount-j-1]) { |
696 | // Found mismatching entry. |
697 | |
698 | // If we matched more than half of the reference trace, guess that this is in fact |
699 | // the prefix we're looking for. |
700 | if (j > refTrace.size() / 2) { |
701 | // Delete the matching suffix. Also delete one non-matched entry on the assumption |
702 | // that both traces contain that stack frame but are simply at different points in |
703 | // the function. |
704 | traceCount -= j + 1; |
705 | return; |
706 | } |
707 | } |
708 | } |
709 | } |
710 | } |
711 | |
712 | // No match. Ignore. |
713 | } |
714 | } |
715 | |
716 | void Exception::addTrace(void* ptr) { |
717 | if (traceCount < kj::size(trace)) { |
718 | trace[traceCount++] = ptr; |
719 | } |
720 | } |
721 | |
722 | class ExceptionImpl: public Exception, public std::exception { |
723 | public: |
724 | inline ExceptionImpl(Exception&& other): Exception(mv(other)) {} |
725 | ExceptionImpl(const ExceptionImpl& other): Exception(other) { |
726 | // No need to copy whatBuffer since it's just to hold the return value of what(). |
727 | } |
728 | |
729 | const char* what() const noexcept override; |
730 | |
731 | private: |
732 | mutable String whatBuffer; |
733 | }; |
734 | |
735 | const char* ExceptionImpl::what() const noexcept { |
736 | whatBuffer = str(*this); |
737 | return whatBuffer.begin(); |
738 | } |
739 | |
740 | // ======================================================================================= |
741 | |
742 | namespace { |
743 | |
744 | KJ_THREADLOCAL_PTR(ExceptionCallback) threadLocalCallback = nullptr; |
745 | |
746 | } // namespace |
747 | |
748 | ExceptionCallback::ExceptionCallback(): next(getExceptionCallback()) { |
749 | char stackVar; |
750 | ptrdiff_t offset = reinterpret_cast<char*>(this) - &stackVar; |
751 | KJ_ASSERT(offset < 65536 && offset > -65536, |
752 | "ExceptionCallback must be allocated on the stack." ); |
753 | |
754 | threadLocalCallback = this; |
755 | } |
756 | |
757 | ExceptionCallback::ExceptionCallback(ExceptionCallback& next): next(next) {} |
758 | |
759 | ExceptionCallback::~ExceptionCallback() noexcept(false) { |
760 | if (&next != this) { |
761 | threadLocalCallback = &next; |
762 | } |
763 | } |
764 | |
765 | void ExceptionCallback::onRecoverableException(Exception&& exception) { |
766 | next.onRecoverableException(mv(exception)); |
767 | } |
768 | |
769 | void ExceptionCallback::onFatalException(Exception&& exception) { |
770 | next.onFatalException(mv(exception)); |
771 | } |
772 | |
773 | void ExceptionCallback::logMessage( |
774 | LogSeverity severity, const char* file, int line, int contextDepth, String&& text) { |
775 | next.logMessage(severity, file, line, contextDepth, mv(text)); |
776 | } |
777 | |
778 | ExceptionCallback::StackTraceMode ExceptionCallback::stackTraceMode() { |
779 | return next.stackTraceMode(); |
780 | } |
781 | |
782 | Function<void(Function<void()>)> ExceptionCallback::getThreadInitializer() { |
783 | return next.getThreadInitializer(); |
784 | } |
785 | |
786 | class ExceptionCallback::RootExceptionCallback: public ExceptionCallback { |
787 | public: |
788 | RootExceptionCallback(): ExceptionCallback(*this) {} |
789 | |
790 | void onRecoverableException(Exception&& exception) override { |
791 | #if KJ_NO_EXCEPTIONS |
792 | logException(LogSeverity::ERROR, mv(exception)); |
793 | #else |
794 | if (std::uncaught_exception()) { |
795 | // Bad time to throw an exception. Just log instead. |
796 | // |
797 | // TODO(someday): We should really compare uncaughtExceptionCount() against the count at |
798 | // the innermost runCatchingExceptions() frame in this thread to tell if exceptions are |
799 | // being caught correctly. |
800 | logException(LogSeverity::ERROR, mv(exception)); |
801 | } else { |
802 | throw ExceptionImpl(mv(exception)); |
803 | } |
804 | #endif |
805 | } |
806 | |
807 | void onFatalException(Exception&& exception) override { |
808 | #if KJ_NO_EXCEPTIONS |
809 | logException(LogSeverity::FATAL, mv(exception)); |
810 | #else |
811 | throw ExceptionImpl(mv(exception)); |
812 | #endif |
813 | } |
814 | |
815 | void logMessage(LogSeverity severity, const char* file, int line, int contextDepth, |
816 | String&& text) override { |
817 | text = str(kj::repeat('_', contextDepth), file, ":" , line, ": " , severity, ": " , |
818 | mv(text), '\n'); |
819 | |
820 | StringPtr textPtr = text; |
821 | |
822 | while (textPtr != nullptr) { |
823 | miniposix::ssize_t n = miniposix::write(STDERR_FILENO, textPtr.begin(), textPtr.size()); |
824 | if (n <= 0) { |
825 | // stderr is broken. Give up. |
826 | return; |
827 | } |
828 | textPtr = textPtr.slice(n); |
829 | } |
830 | } |
831 | |
832 | StackTraceMode stackTraceMode() override { |
833 | #ifdef KJ_DEBUG |
834 | return StackTraceMode::FULL; |
835 | #else |
836 | return StackTraceMode::ADDRESS_ONLY; |
837 | #endif |
838 | } |
839 | |
840 | Function<void(Function<void()>)> getThreadInitializer() override { |
841 | return [](Function<void()> func) { |
842 | // No initialization needed since RootExceptionCallback is automatically the root callback |
843 | // for new threads. |
844 | func(); |
845 | }; |
846 | } |
847 | |
848 | private: |
849 | void logException(LogSeverity severity, Exception&& e) { |
850 | // We intentionally go back to the top exception callback on the stack because we don't want to |
851 | // bypass whatever log processing is in effect. |
852 | // |
853 | // We intentionally don't log the context since it should get re-added by the exception callback |
854 | // anyway. |
855 | getExceptionCallback().logMessage(severity, e.getFile(), e.getLine(), 0, str( |
856 | e.getType(), e.getDescription() == nullptr ? "" : ": " , e.getDescription(), |
857 | e.getStackTrace().size() > 0 ? "\nstack: " : "" , |
858 | stringifyStackTraceAddresses(e.getStackTrace()), |
859 | stringifyStackTrace(e.getStackTrace()), "\n" )); |
860 | } |
861 | }; |
862 | |
863 | ExceptionCallback& getExceptionCallback() { |
864 | static ExceptionCallback::RootExceptionCallback defaultCallback; |
865 | ExceptionCallback* scoped = threadLocalCallback; |
866 | return scoped != nullptr ? *scoped : defaultCallback; |
867 | } |
868 | |
869 | void throwFatalException(kj::Exception&& exception, uint ignoreCount) { |
870 | exception.extendTrace(ignoreCount + 1); |
871 | getExceptionCallback().onFatalException(kj::mv(exception)); |
872 | abort(); |
873 | } |
874 | |
875 | void throwRecoverableException(kj::Exception&& exception, uint ignoreCount) { |
876 | exception.extendTrace(ignoreCount + 1); |
877 | getExceptionCallback().onRecoverableException(kj::mv(exception)); |
878 | } |
879 | |
880 | // ======================================================================================= |
881 | |
882 | namespace _ { // private |
883 | |
884 | #if __cplusplus >= 201703L |
885 | |
886 | uint uncaughtExceptionCount() { |
887 | return std::uncaught_exceptions(); |
888 | } |
889 | |
890 | #elif __GNUC__ |
891 | |
892 | // Horrible -- but working -- hack: We can dig into __cxa_get_globals() in order to extract the |
893 | // count of uncaught exceptions. This function is part of the C++ ABI implementation used on Linux, |
894 | // OSX, and probably other platforms that use GCC. Unfortunately, __cxa_get_globals() is only |
895 | // actually defined in cxxabi.h on some platforms (e.g. Linux, but not OSX), and even where it is |
896 | // defined, it returns an incomplete type. Here we use the same hack used by Evgeny Panasyuk: |
897 | // https://github.com/panaseleus/stack_unwinding/blob/master/boost/exception/uncaught_exception_count.hpp |
898 | // |
899 | // Notice that a similar hack is possible on MSVC -- if its C++11 support ever gets to the point of |
900 | // supporting KJ in the first place. |
901 | // |
902 | // It appears likely that a future version of the C++ standard may include an |
903 | // uncaught_exception_count() function in the standard library, or an equivalent language feature. |
904 | // Some discussion: |
905 | // https://groups.google.com/a/isocpp.org/d/msg/std-proposals/HglEslyZFYs/kKdu5jJw5AgJ |
906 | |
907 | struct FakeEhGlobals { |
908 | // Fake |
909 | |
910 | void* caughtExceptions; |
911 | uint uncaughtExceptions; |
912 | }; |
913 | |
914 | // LLVM's libstdc++ doesn't declare __cxa_get_globals in its cxxabi.h. GNU does. Because it is |
915 | // extern "C", the compiler wills get upset if we re-declare it even in a different namespace. |
916 | #if _LIBCPPABI_VERSION |
917 | extern "C" void* __cxa_get_globals(); |
918 | #else |
919 | using abi::__cxa_get_globals; |
920 | #endif |
921 | |
922 | uint uncaughtExceptionCount() { |
923 | return reinterpret_cast<FakeEhGlobals*>(__cxa_get_globals())->uncaughtExceptions; |
924 | } |
925 | |
926 | #elif _MSC_VER |
927 | |
928 | #if _MSC_VER >= 1900 |
929 | // MSVC14 has a refactored CRT which now provides a direct accessor for this value. |
930 | // See https://svn.boost.org/trac/boost/ticket/10158 for a brief discussion. |
931 | extern "C" int *__cdecl __processing_throw(); |
932 | |
933 | uint uncaughtExceptionCount() { |
934 | return static_cast<uint>(*__processing_throw()); |
935 | } |
936 | |
937 | #elif _MSC_VER >= 1400 |
938 | // The below was copied from: |
939 | // https://github.com/panaseleus/stack_unwinding/blob/master/boost/exception/uncaught_exception_count.hpp |
940 | |
941 | extern "C" char *__cdecl _getptd(); |
942 | |
943 | uint uncaughtExceptionCount() { |
944 | return *reinterpret_cast<uint*>(_getptd() + (sizeof(void*) == 8 ? 0x100 : 0x90)); |
945 | } |
946 | #else |
947 | uint uncaughtExceptionCount() { |
948 | // Since the above doesn't work, fall back to uncaught_exception(). This will produce incorrect |
949 | // results in very obscure cases that Cap'n Proto doesn't really rely on anyway. |
950 | return std::uncaught_exception(); |
951 | } |
952 | #endif |
953 | |
954 | #else |
955 | #error "This needs to be ported to your compiler / C++ ABI." |
956 | #endif |
957 | |
958 | } // namespace _ (private) |
959 | |
960 | UnwindDetector::UnwindDetector(): uncaughtCount(_::uncaughtExceptionCount()) {} |
961 | |
962 | bool UnwindDetector::isUnwinding() const { |
963 | return _::uncaughtExceptionCount() > uncaughtCount; |
964 | } |
965 | |
966 | void UnwindDetector::catchExceptionsAsSecondaryFaults(_::Runnable& runnable) const { |
967 | // TODO(someday): Attach the secondary exception to whatever primary exception is causing |
968 | // the unwind. For now we just drop it on the floor as this is probably fine most of the |
969 | // time. |
970 | runCatchingExceptions(runnable); |
971 | } |
972 | |
973 | #if __GNUC__ && !KJ_NO_RTTI |
974 | static kj::String demangleTypeName(const char* name) { |
975 | if (name == nullptr) return kj::heapString("(nil)" ); |
976 | |
977 | int status; |
978 | char* buf = abi::__cxa_demangle(name, nullptr, nullptr, &status); |
979 | kj::String result = kj::heapString(buf == nullptr ? name : buf); |
980 | free(buf); |
981 | return kj::mv(result); |
982 | } |
983 | |
984 | kj::String getCaughtExceptionType() { |
985 | return demangleTypeName(abi::__cxa_current_exception_type()->name()); |
986 | } |
987 | #else |
988 | kj::String getCaughtExceptionType() { |
989 | return kj::heapString("(unknown)" ); |
990 | } |
991 | #endif |
992 | |
993 | namespace _ { // private |
994 | |
995 | class RecoverableExceptionCatcher: public ExceptionCallback { |
996 | // Catches a recoverable exception without using try/catch. Used when compiled with |
997 | // -fno-exceptions. |
998 | |
999 | public: |
1000 | virtual ~RecoverableExceptionCatcher() noexcept(false) {} |
1001 | |
1002 | void onRecoverableException(Exception&& exception) override { |
1003 | if (caught == nullptr) { |
1004 | caught = mv(exception); |
1005 | } else { |
1006 | // TODO(someday): Consider it a secondary fault? |
1007 | } |
1008 | } |
1009 | |
1010 | Maybe<Exception> caught; |
1011 | }; |
1012 | |
1013 | Maybe<Exception> runCatchingExceptions(Runnable& runnable) noexcept { |
1014 | #if KJ_NO_EXCEPTIONS |
1015 | RecoverableExceptionCatcher catcher; |
1016 | runnable.run(); |
1017 | KJ_IF_MAYBE(e, catcher.caught) { |
1018 | e->truncateCommonTrace(); |
1019 | } |
1020 | return mv(catcher.caught); |
1021 | #else |
1022 | try { |
1023 | runnable.run(); |
1024 | return nullptr; |
1025 | } catch (Exception& e) { |
1026 | e.truncateCommonTrace(); |
1027 | return kj::mv(e); |
1028 | } catch (std::bad_alloc& e) { |
1029 | return Exception(Exception::Type::OVERLOADED, |
1030 | "(unknown)" , -1, str("std::bad_alloc: " , e.what())); |
1031 | } catch (std::exception& e) { |
1032 | return Exception(Exception::Type::FAILED, |
1033 | "(unknown)" , -1, str("std::exception: " , e.what())); |
1034 | } catch (...) { |
1035 | #if __GNUC__ && !KJ_NO_RTTI |
1036 | return Exception(Exception::Type::FAILED, "(unknown)" , -1, str( |
1037 | "unknown non-KJ exception of type: " , getCaughtExceptionType())); |
1038 | #else |
1039 | return Exception(Exception::Type::FAILED, "(unknown)" , -1, str("unknown non-KJ exception" )); |
1040 | #endif |
1041 | } |
1042 | #endif |
1043 | } |
1044 | |
1045 | } // namespace _ (private) |
1046 | |
1047 | } // namespace kj |
1048 | |