panic: print backtrace on panic

Panics currently lack backtraces, which are a very useful thing to
have. Make the profiling backtrace function generic and print backtraces
using it.
This commit is contained in:
Matheus
2026-10-08 22:14:46 -03:00
parent 9b8c81a2cf
commit 91ec85d8ca
4 changed files with 99 additions and 24 deletions
+12 -24
View File
@@ -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();
+53
View File
@@ -0,0 +1,53 @@
#include <arch/backtrace.h>
#include <stdint.h>
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;
}
}
@@ -0,0 +1,11 @@
#ifndef _ARCH_BACKTRACE_H
#define _ARCH_BACKTRACE_H
#include <stdbool.h>
#include <arch/context.h>
typedef bool (*backtrace_fn_t)(void *ctx, void *ip);
void arch_get_backtrace(context_t *cpu_context, backtrace_fn_t fn, void *context);
#endif
+23
View File
@@ -3,6 +3,18 @@
#include <arch/smp.h>
#include <printf.h>
#include <kernel/interrupt.h>
#include <arch/backtrace.h>
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 (;;);
}