diff --git a/kernel-src/arch/x86-64/arch_prof.c b/kernel-src/arch/x86-64/arch_prof.c index de599efe..d7fa3718 100644 --- a/kernel-src/arch/x86-64/arch_prof.c +++ b/kernel-src/arch/x86-64/arch_prof.c @@ -68,39 +68,27 @@ static void init_intel_pmc(void) { arch_apic_init_perf(); } -static uint8_t get_backtrace(context_t *context, uintptr_t *data) { - if (IS_USER_ADDRESS(context->rip)) - return 0; +static bool backtrace_fn(void *context, void *ip) { + uint8_t *data = context; - data[0] = context->rip; + if (data[0] == 255) + return false; - uint64_t *rbp = (uint64_t *)context->rbp; - uint8_t done = 1; + uintptr_t *trace = (uintptr_t *)&data[1]; + trace[data[0]] = (uintptr_t)ip; + data[0] += 1; - for (;;) { - if (IS_USER_ADDRESS(rbp) || done == 255) - break; - - uint64_t rip = *(rbp + 1); - if (IS_USER_ADDRESS(rip)) - break; - - data[done++] = rip - 1; - rbp = (uint64_t *)*rbp; - } - - return done; + return true; } static bool intel_irq(context_t *context) { if ((rdmsr(MSR_PERF_GLOBAL_STATUS) & INTEL_TSC_INDEX_OFFSET) == 0) return false; - uint8_t data[1 + sizeof(uintptr_t) * 256]; - - uint8_t size = get_backtrace(context, (uintptr_t *)(data + 1)); - *data = size; - profiling_insert(size, data); + uint8_t data[1 + sizeof(uintptr_t) * 255]; + data[0] = 0; + arch_get_backtrace(context, backtrace_fn, data); + profiling_insert(data[0], data); arch_apic_init_perf(); // SDM LVT figure says a PMC irq masks itself, so unmask it reset_intel_pmc(); diff --git a/kernel-src/arch/x86-64/backtrace.c b/kernel-src/arch/x86-64/backtrace.c new file mode 100644 index 00000000..ede0911a --- /dev/null +++ b/kernel-src/arch/x86-64/backtrace.c @@ -0,0 +1,53 @@ +#include +#include + +static bool valid_rip(void *rip) { + if ((uint64_t)rip < (uint64_t)0xffffffff80000000) + return false; + + return true; +} + +// TODO 5-level paging support? +static bool valid_frame(void *frame, void *old_frame) { + if ((uint64_t)frame < (uint64_t)0xffff800000000000) + return false; + + if ((uint64_t)frame >= (uint64_t)0xffffffff80000000) + return false; + + if ((uint64_t)old_frame >= (uint64_t)frame) + return false; + + return true; +} + +void arch_get_backtrace(context_t *cpu_context, backtrace_fn_t fn, void *context) { + if (!valid_rip((void *)cpu_context->rip)) + return; + + if (!fn(context, (void *)cpu_context->rip)) + return; + + size_t loops = 0; + uint64_t *rbp = (uint64_t *)cpu_context->rbp; + uint64_t *old_frame = NULL; + for (;;) { + if (!valid_frame(rbp, old_frame)) + return; + + void *rip = (void *)*(rbp + 1); + if (!valid_rip(rip)) + return; + + if (!fn(context, rip)) + return; + + old_frame = rbp; + rbp = (uint64_t *)*rbp; + + if (++loops == 256) + return; + } +} + diff --git a/kernel-src/include/x86-64/arch/backtrace.h b/kernel-src/include/x86-64/arch/backtrace.h new file mode 100644 index 00000000..6ae87e9a --- /dev/null +++ b/kernel-src/include/x86-64/arch/backtrace.h @@ -0,0 +1,11 @@ +#ifndef _ARCH_BACKTRACE_H +#define _ARCH_BACKTRACE_H + +#include +#include + +typedef bool (*backtrace_fn_t)(void *ctx, void *ip); + +void arch_get_backtrace(context_t *cpu_context, backtrace_fn_t fn, void *context); + +#endif diff --git a/kernel-src/lib/panic.c b/kernel-src/lib/panic.c index 13030993..1d56ec40 100644 --- a/kernel-src/lib/panic.c +++ b/kernel-src/lib/panic.c @@ -3,6 +3,18 @@ #include #include #include +#include + +static bool paniced = false; + +static bool step_backtrace(void *, void *ip) { + printf("%p\n", ip); + return true; +} + +static void print_backtrace(context_t *context, void *) { + arch_get_backtrace(context, step_backtrace, NULL); +} __attribute__((noreturn)) void _panic(char *msg, context_t *ctx) { interrupt_set(false); @@ -17,5 +29,16 @@ __attribute__((noreturn)) void _panic(char *msg, context_t *ctx) { if (ctx) PRINT_CTX(ctx); + if (!paniced) { + paniced = true; + printf("backtrace:\n"); + if (ctx == NULL) + arch_context_saveandcall(print_backtrace, NULL, NULL); + else + print_backtrace(ctx, NULL); + } else { + printf("!! DOUBLE PANIC !!\n"); + } + for (;;); }