+trace_sym_lookup(ptr_t addr)
+{
+ int c = trace_ctx.ksym_table->ksym_count;
+ struct ksym_entry* ksent = trace_ctx.ksym_table->syms;
+
+ int i = c - 1, j = 0, m = 0;
+
+ if (addr > ksent[i].pc || addr < ksent[j].pc || addr < KERNEL_EXEC) {
+ return NULL;
+ }
+
+ while (i - j != 1) {
+ m = (i + j) / 2;
+ if (ksent[m].pc > addr) {
+ i = m;
+ } else if (ksent[m].pc < addr) {
+ j = m;
+ } else {
+ break;
+ }
+ }
+
+ struct ksym_entry* result = &ksent[MIN(i, j)];
+ if (result->pc > addr) {
+ return NULL;
+ }
+
+ return result;
+}
+
+static char*
+ksym_getstr(struct ksym_entry* sym)
+{
+ if (!sym) {
+ return "???";
+ }
+
+ return (char*)((ptr_t)trace_ctx.ksym_table +
+ trace_ctx.ksym_table->ksym_label_off + sym->label_off);
+}
+
+int
+trace_walkback(struct trace_record* tb_buffer,
+ ptr_t fp,
+ int limit,
+ ptr_t* last_fp)
+{
+ ptr_t* frame = (ptr_t*)fp;
+ struct ksym_entry* current = NULL;
+ int i = 0;
+
+ while (frame && i < limit) {
+ ptr_t pc = *(frame + 1);
+
+ current = trace_sym_lookup(pc);
+ tb_buffer[i] = (struct trace_record){ .pc = current ? current->pc : pc,
+ .symbol = ksym_getstr(current) };
+
+ frame = (ptr_t*)*frame;
+ i++;
+ }
+
+ if (last_fp) {
+ *last_fp = (ptr_t)frame;
+ }
+
+ return i;
+}
+
+void
+trace_printstack_of(ptr_t fp)