Skip to content

Commit 789dcec

Browse files
committed
test crash handler
1 parent d0d0f2e commit 789dcec

8 files changed

Lines changed: 288 additions & 43 deletions

File tree

.github/workflows/tests.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ jobs:
5151
cd build/result/${{ matrix.build-config }}
5252
./cage-test-core
5353
54+
- name: Crash handler
55+
shell: bash
56+
run: |
57+
cd build/result/${{ matrix.build-config }}
58+
./cage-test-crash write-to-null || true
59+
echo "log:"
60+
cat cage-test-crash.log
61+
5462
- name: Assets
5563
shell: bash
5664
run: |
@@ -127,6 +135,14 @@ jobs:
127135
cd build/result/${{ matrix.build-config }}
128136
./cage-test-core
129137
138+
- name: Crash handler
139+
shell: bash
140+
run: |
141+
cd build/result/${{ matrix.build-config }}
142+
./cage-test-crash write-to-null || true
143+
echo "log:"
144+
cat cage-test-crash.log
145+
130146
- name: Assets
131147
run: |
132148
cd build/result/${{ matrix.build-config }}
@@ -180,6 +196,14 @@ jobs:
180196
cd build/result/${{ matrix.build-config }}
181197
./cage-test-core
182198
199+
- name: Crash handler
200+
shell: bash
201+
run: |
202+
cd build/result/${{ matrix.build-config }}
203+
./cage-test-crash write-to-null || true
204+
echo "log:"
205+
cat cage-test-crash.log
206+
183207
- name: Assets
184208
run: |
185209
cd build/result/${{ matrix.build-config }}

sources/CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,13 @@ cage_ide_category(cage-test-entities cage/tests)
173173
cage_ide_sort_files(cage-test-entities)
174174
cage_ide_working_dir_in_place(cage-test-entities)
175175

176+
file(GLOB_RECURSE cage-test-crash-sources "test-crash/*")
177+
add_executable(cage-test-crash ${cage-test-crash-sources})
178+
target_link_libraries(cage-test-crash cage-core)
179+
cage_ide_category(cage-test-crash cage/tests)
180+
cage_ide_sort_files(cage-test-crash)
181+
cage_ide_working_dir_in_place(cage-test-crash)
182+
176183
########
177184
# TOOLS
178185
########

sources/libcore/errors/crashHandlerMac.cpp

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#ifdef CAGE_SYSTEM_MAC
22

3+
#include <dlfcn.h>
34
#include <mach/exception_types.h>
45
#include <mach/mach.h>
56
#include <mach/task.h>
@@ -48,7 +49,13 @@ namespace cage
4849

4950
void writeThreadInfo(mach_port_t thread)
5051
{
51-
pthread_t pt = pthread_from_mach_thread_np(thread);
52+
const pthread_t pt = pthread_from_mach_thread_np(thread);
53+
if (!pt)
54+
{
55+
privat::crashHandlerSafeWrite("thread id: unknown\n");
56+
return;
57+
}
58+
5259
uint64 tid = 0;
5360
pthread_threadid_np(pt, &tid);
5461
privat::crashHandlerSafeWrite(Stringizer() + "thread id: " + tid + "\n");
@@ -63,16 +70,17 @@ namespace cage
6370
}
6471
}
6572

66-
void writeRegisters(mach_port_t thread)
73+
std::pair<uint64, uint64> writeRegisters(mach_port_t thread)
6774
{
6875
#if defined(__x86_64__)
6976
x86_thread_state64_t state;
7077
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
7178
if (thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&state, &count) == KERN_SUCCESS)
7279
{
73-
privat::crashHandlerSafeWrite(Stringizer() + "pc: " + (void *)state.__rip + "\n");
74-
privat::crashHandlerSafeWrite(Stringizer() + "sp: " + (void *)state.__rsp + "\n");
75-
privat::crashHandlerSafeWrite(Stringizer() + "bp: " + (void *)state.__rbp + "\n");
80+
privat::crashHandlerSafeWrite(Stringizer() + "rip: " + (void *)state.__rip + "\n");
81+
privat::crashHandlerSafeWrite(Stringizer() + "rsp: " + (void *)state.__rsp + "\n");
82+
privat::crashHandlerSafeWrite(Stringizer() + "rbp: " + (void *)state.__rbp + "\n");
83+
return { (uint64)state.__rbp, (uint64)state.__rip };
7684
}
7785
#elif defined(__arm64__)
7886
arm_thread_state64_t state;
@@ -82,10 +90,53 @@ namespace cage
8290
privat::crashHandlerSafeWrite(Stringizer() + "pc: " + (void *)state.__pc + "\n");
8391
privat::crashHandlerSafeWrite(Stringizer() + "sp: " + (void *)state.__sp + "\n");
8492
privat::crashHandlerSafeWrite(Stringizer() + "fp: " + (void *)state.__fp + "\n");
93+
return { (uint64)state.__fp, (uint64)state.__pc };
8594
}
8695
#else
8796
#error "unknwon cpu architecture"
8897
#endif
98+
return {};
99+
}
100+
101+
void printStack(uint64 fp, uint64 pc, mach_port_t thread)
102+
{
103+
privat::crashHandlerSafeWrite("stack trace:\n");
104+
105+
const auto &printFrame = [](uint64 addr)
106+
{
107+
Dl_info info = {};
108+
if (dladdr((void *)addr, &info) && info.dli_sname)
109+
privat::crashHandlerSafeWrite(Stringizer() + info.dli_sname + " + " + (uint64)((char *)addr - (char *)info.dli_saddr) + "\n");
110+
else
111+
privat::crashHandlerSafeWrite(Stringizer() + (void *)addr + "\n");
112+
};
113+
114+
if (pc)
115+
printFrame(pc);
116+
117+
const pthread_t pt = pthread_from_mach_thread_np(thread);
118+
if (!pt)
119+
{
120+
privat::crashHandlerSafeWrite("unknown thread id\n");
121+
return;
122+
}
123+
124+
const uint64 top = (uint64)pthread_get_stackaddr_np(pt);
125+
const uint64 bottom = top - (uint64)pthread_get_stacksize_np(pt);
126+
127+
for (uint32 i = 0; i < 100; i++)
128+
{
129+
if (fp < bottom || fp + 2 * sizeof(uint64) > top)
130+
break;
131+
if (fp % alignof(void *) != 0)
132+
break;
133+
const uint64_t *frame = (uint64_t *)fp;
134+
if (frame[1] == 0)
135+
break;
136+
printFrame(frame[1]);
137+
fp = frame[0];
138+
}
139+
privat::crashHandlerSafeWrite("\n");
89140
}
90141
}
91142
}
@@ -95,13 +146,16 @@ extern "C"
95146
kern_return_t catch_mach_exception_raise(mach_port_t exception_port, mach_port_t thread, mach_port_t task, exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count)
96147
{
97148
using namespace cage;
149+
thread_suspend(thread);
98150
privat::crashHandlerSafeWrite(Stringizer() + "mach exception: " + excToStr(exception) + " (" + exception + ")\n");
99151
if (code_count > 0)
100152
privat::crashHandlerSafeWrite(Stringizer() + "code[0]: " + code[0] + "\n");
101153
if (code_count > 1)
102154
privat::crashHandlerSafeWrite(Stringizer() + "code[1]: " + code[1] + "\n");
103155
writeThreadInfo(thread);
104-
writeRegisters(thread);
156+
const auto regs = writeRegisters(thread);
157+
printStack(regs.first, reg.second, thread);
158+
thread_resume(thread);
105159
// let the system continue propagating the exception
106160
return KERN_FAILURE;
107161
}

sources/libcore/errors/crashHandlerSignals.cpp

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,29 @@ namespace cage
3434
}
3535
}
3636

37+
void crashHandlerPrintThread()
38+
{
39+
privat::crashHandlerSafeWrite(Stringizer() + "in thread: " + currentThreadName() + "\n");
40+
}
41+
42+
void crashHandlerPrintStack()
43+
{
44+
// backtrace (best-effort; not strictly async-signal-safe)
45+
static constexpr int MaxFrames = 256;
46+
void *frames[MaxFrames];
47+
int n = backtrace(frames, MaxFrames);
48+
if (n <= 0)
49+
{
50+
crashHandlerSafeWrite("stack trace: <empty>\n");
51+
return;
52+
}
53+
crashHandlerSafeWrite(Stringizer() + "stack trace:\n");
54+
backtrace_symbols_fd(frames, n, crashHandlerLogFileFd);
55+
if (crashHandlerLogFileFd != STDERR_FILENO)
56+
backtrace_symbols_fd(frames, n, STDERR_FILENO);
57+
crashHandlerSafeWrite("\n");
58+
}
59+
3760
void crashHandlerThreadInit()
3861
{
3962
// install alternative stack memory for handling signals
@@ -80,6 +103,7 @@ namespace cage
80103
return &prevHandlers[sig];
81104
return nullptr;
82105
}
106+
83107
const char *sigToStr(int sig)
84108
{
85109
switch (sig)
@@ -189,19 +213,6 @@ namespace cage
189213
}
190214
}
191215

192-
void printStackTrace()
193-
{
194-
// backtrace (best-effort; not strictly async-signal-safe)
195-
static constexpr int MaxFrames = 256;
196-
void *frames[MaxFrames];
197-
int n = backtrace(frames, MaxFrames);
198-
privat::crashHandlerSafeWrite(Stringizer() + "stack trace:\n");
199-
backtrace_symbols_fd(frames, n, privat::crashHandlerLogFileFd);
200-
if (privat::crashHandlerLogFileFd != STDERR_FILENO)
201-
backtrace_symbols_fd(frames, n, STDERR_FILENO);
202-
privat::crashHandlerSafeWrite("\n");
203-
}
204-
205216
void performPreviousHandler(int sig, siginfo_t *si, void *uctx)
206217
{
207218
if (struct sigaction *prev = prevHandler(sig))
@@ -230,8 +241,8 @@ namespace cage
230241
privat::crashHandlerSafeWrite(Stringizer() + "signal handler: " + sigToStr(sig) + " (" + sig + ")\n");
231242
if (si)
232243
privat::crashHandlerSafeWrite(Stringizer() + "fault addr: " + (uintptr_t)si->si_addr + ", code: " + si->si_code + "\n");
233-
privat::crashHandlerSafeWrite(Stringizer() + "in thread: " + currentThreadName() + "\n");
234-
printStackTrace();
244+
privat::crashHandlerPrintThread();
245+
privat::crashHandlerPrintStack();
235246
performPreviousHandler(sig, si, uctx);
236247
}
237248

sources/libcore/errors/crashHandlerWindows.cpp

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,30 @@
1717

1818
namespace cage
1919
{
20+
namespace
21+
{
22+
void printStackTrace(PEXCEPTION_POINTERS ex);
23+
}
24+
2025
namespace privat
2126
{
27+
void crashHandlerPrintThread()
28+
{
29+
CAGE_LOG(SeverityEnum::Info, "crash-handler", Stringizer() + "in thread: " + currentThreadName());
30+
}
31+
32+
void crashHandlerPrintStack()
33+
{
34+
CONTEXT ctx = {};
35+
RtlCaptureContext(&ctx); // manual context capture
36+
EXCEPTION_RECORD rec = {};
37+
rec.ExceptionCode = 0xDEAD; // custom marker
38+
EXCEPTION_POINTERS ptrs = {};
39+
ptrs.ContextRecord = &ctx;
40+
ptrs.ExceptionRecord = &rec;
41+
printStackTrace(&ptrs);
42+
}
43+
2244
void crashHandlerThreadInit()
2345
{
2446
// reserve safe memory on the stack for handling EXCEPTION_STACK_OVERFLOW
@@ -222,10 +244,11 @@ namespace cage
222244
str + symbol->Name;
223245
}
224246
CAGE_LOG_CONTINUE(SeverityEnum::Info, "crash-handler", str.value.empty() ? String("unknown-frame") : str);
225-
if (++iteration == 50)
247+
if (++iteration >= 100)
226248
break;
227249
}
228250
SymCleanup(process);
251+
CAGE_LOG_CONTINUE(SeverityEnum::Info, "crash-handler", "");
229252
}
230253
else
231254
{
@@ -259,7 +282,7 @@ namespace cage
259282
for (uint32 i = 0; i < ex->ExceptionRecord->NumberParameters; i++)
260283
CAGE_LOG(SeverityEnum::Info, "crash-handler", Stringizer() + "parameter[" + i + "]: " + ex->ExceptionRecord->ExceptionInformation);
261284
}
262-
CAGE_LOG(SeverityEnum::Info, "crash-handler", Stringizer() + "in thread: " + currentThreadName());
285+
privat::crashHandlerPrintThread();
263286
printStackTrace(ex);
264287
}
265288

@@ -276,7 +299,10 @@ namespace cage
276299
{
277300
commonHandler(ex);
278301
if (previousExceptionFilter)
302+
{
303+
CAGE_LOG(SeverityEnum::Info, "crash-handler", "calling previous handler");
279304
return previousExceptionFilter(ex);
305+
}
280306
return EXCEPTION_CONTINUE_SEARCH;
281307
}
282308

@@ -301,37 +327,31 @@ namespace cage
301327
return data;
302328
}
303329

304-
_invalid_parameter_handler previousInvalidParameter = nullptr;
330+
void commonHandler()
331+
{
332+
privat::crashHandlerPrintThread();
333+
privat::crashHandlerPrintStack();
334+
CAGE_LOG(SeverityEnum::Info, "crash-handler", "calling abort");
335+
std::abort();
336+
}
305337

306338
void invalidParameterHandler(const wchar_t *expression, const wchar_t *function, const wchar_t *file, unsigned int line, uintptr_t pReserved)
307339
{
308340
CAGE_LOG(SeverityEnum::Error, "crash-handler", "crash handler: invalid parameter");
309341
CAGE_LOG(SeverityEnum::Info, "crash-handler", Stringizer() + "expression: " + narrow(expression) + ", function: " + narrow(function));
310-
CAGE_LOG(SeverityEnum::Info, "crash-handler", Stringizer() + "in thread: " + currentThreadName());
311-
detail::debugBreakpoint();
312-
if (previousInvalidParameter)
313-
previousInvalidParameter(expression, function, file, line, pReserved);
314-
std::terminate();
342+
commonHandler();
315343
}
316344

317-
_purecall_handler previousPurecall = nullptr;
318-
319345
void purecallHandler()
320346
{
321347
CAGE_LOG(SeverityEnum::Error, "crash-handler", "crash handler: purecall");
322-
CAGE_LOG(SeverityEnum::Info, "crash-handler", Stringizer() + "in thread: " + currentThreadName());
323-
detail::debugBreakpoint();
324-
if (previousPurecall)
325-
previousPurecall();
326-
std::terminate();
348+
commonHandler();
327349
}
328350

329351
void sigAbrtHandler(int)
330352
{
331353
CAGE_LOG(SeverityEnum::Error, "crash-handler", "crash handler: abort signal");
332-
CAGE_LOG(SeverityEnum::Info, "crash-handler", Stringizer() + "in thread: " + currentThreadName());
333-
detail::debugBreakpoint();
334-
std::terminate();
354+
commonHandler();
335355
}
336356

337357
struct SetupHandlersWindows
@@ -347,8 +367,8 @@ namespace cage
347367
previousExceptionFilter = SetUnhandledExceptionFilter(&unhandledHandler);
348368
if (!SetConsoleCtrlHandler(&consoleHandler, TRUE))
349369
CAGE_THROW_ERROR(SystemError, "SetConsoleCtrlHandler", GetLastError());
350-
previousInvalidParameter = _set_invalid_parameter_handler(&invalidParameterHandler);
351-
previousPurecall = _set_purecall_handler(&purecallHandler);
370+
_set_invalid_parameter_handler(&invalidParameterHandler);
371+
_set_purecall_handler(&purecallHandler);
352372
signal(SIGABRT, &sigAbrtHandler);
353373
}
354374
} setupHandlersWindows;

0 commit comments

Comments
 (0)