#include <sys/param.h>
#include <sys/proc.h>
#include <sys/stacktrace.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <machine/db_machdep.h>
#include <ddb/db_access.h>
#include <ddb/db_interface.h>
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>
db_regs_t ddb_regs;
struct db_variable db_regs[] = {
{ "r0", (long *)&ddb_regs.fixreg[0], FCN_NULL },
{ "r1", (long *)&ddb_regs.fixreg[1], FCN_NULL },
{ "r2", (long *)&ddb_regs.fixreg[2], FCN_NULL },
{ "r3", (long *)&ddb_regs.fixreg[3], FCN_NULL },
{ "r4", (long *)&ddb_regs.fixreg[4], FCN_NULL },
{ "r5", (long *)&ddb_regs.fixreg[5], FCN_NULL },
{ "r6", (long *)&ddb_regs.fixreg[6], FCN_NULL },
{ "r7", (long *)&ddb_regs.fixreg[7], FCN_NULL },
{ "r8", (long *)&ddb_regs.fixreg[8], FCN_NULL },
{ "r9", (long *)&ddb_regs.fixreg[9], FCN_NULL },
{ "r10", (long *)&ddb_regs.fixreg[10], FCN_NULL },
{ "r11", (long *)&ddb_regs.fixreg[11], FCN_NULL },
{ "r12", (long *)&ddb_regs.fixreg[12], FCN_NULL },
{ "r13", (long *)&ddb_regs.fixreg[13], FCN_NULL },
{ "r14", (long *)&ddb_regs.fixreg[14], FCN_NULL },
{ "r15", (long *)&ddb_regs.fixreg[15], FCN_NULL },
{ "r16", (long *)&ddb_regs.fixreg[16], FCN_NULL },
{ "r17", (long *)&ddb_regs.fixreg[17], FCN_NULL },
{ "r18", (long *)&ddb_regs.fixreg[18], FCN_NULL },
{ "r19", (long *)&ddb_regs.fixreg[19], FCN_NULL },
{ "r20", (long *)&ddb_regs.fixreg[20], FCN_NULL },
{ "r21", (long *)&ddb_regs.fixreg[21], FCN_NULL },
{ "r22", (long *)&ddb_regs.fixreg[22], FCN_NULL },
{ "r23", (long *)&ddb_regs.fixreg[23], FCN_NULL },
{ "r24", (long *)&ddb_regs.fixreg[24], FCN_NULL },
{ "r25", (long *)&ddb_regs.fixreg[25], FCN_NULL },
{ "r26", (long *)&ddb_regs.fixreg[26], FCN_NULL },
{ "r27", (long *)&ddb_regs.fixreg[27], FCN_NULL },
{ "r28", (long *)&ddb_regs.fixreg[28], FCN_NULL },
{ "r29", (long *)&ddb_regs.fixreg[29], FCN_NULL },
{ "r30", (long *)&ddb_regs.fixreg[30], FCN_NULL },
{ "r31", (long *)&ddb_regs.fixreg[31], FCN_NULL },
{ "lr", (long *)&ddb_regs.lr, FCN_NULL },
{ "cr", (long *)&ddb_regs.cr, FCN_NULL },
{ "xer", (long *)&ddb_regs.xer, FCN_NULL },
{ "ctr", (long *)&ddb_regs.ctr, FCN_NULL },
{ "iar", (long *)&ddb_regs.srr0, FCN_NULL },
{ "msr", (long *)&ddb_regs.srr1, FCN_NULL },
{ "dar", (long *)&ddb_regs.dar, FCN_NULL },
{ "dsisr", (long *)&ddb_regs.dsisr, FCN_NULL },
};
struct db_variable *db_eregs = db_regs + nitems(db_regs);
extern vaddr_t trapexit;
#define inst_establish_frame(ins) ((ins & 0xffff0003) == 0xf8210001)
void
db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
char *modif, int (*pr)(const char *, ...))
{
vaddr_t callpc, lr, sp, lastsp;
db_expr_t offset;
const char *name;
char c, *cp = modif;
Elf_Sym *sym;
int has_frame, trace_proc = 0;
int end_trace = 0;
while ((c = *cp++) != 0) {
if (c == 't')
trace_proc = 1;
}
if (!have_addr) {
sp = ddb_regs.fixreg[1];
callpc = ddb_regs.srr0;
has_frame = 0;
} else {
if (trace_proc) {
(*pr)("trace/t not yet implemented!\n");
return;
} else
sp = addr;
db_read_bytes(sp, sizeof(vaddr_t), (char *)&sp);
db_read_bytes(sp + 16, sizeof(vaddr_t), (char *)&lr);
callpc = lr - 4;
has_frame = 1;
}
while (count && sp != 0) {
sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
db_symbol_values(sym, &name, NULL);
if (!has_frame && sym) {
vaddr_t iaddr, limit;
uint32_t ins;
iaddr = sym->st_value;
limit = MIN(iaddr + 0x100, callpc);
for (; iaddr < limit; iaddr += 4) {
db_read_bytes(iaddr, sizeof(ins),
(char *)&ins);
if (inst_establish_frame(ins)) {
has_frame = 1;
break;
}
}
}
if (name == NULL || strcmp(name, "end") == 0) {
(*pr)("at 0x%lx", callpc);
} else {
db_printsym(callpc, DB_STGY_PROC, pr);
}
(*pr)("\n");
lastsp = sp;
if (lr == (vaddr_t)&trapexit) {
struct trapframe *frame =
(struct trapframe *)(sp + 32);
if ((frame->srr1 & PSL_PR) && frame->exc == EXC_SC) {
(*pr)("--- syscall (number %ld) ---\n",
frame->fixreg[0]);
} else {
(*pr)("--- trap (type 0x%x) ---\n",
frame->exc);
}
if (frame->srr1 & PSL_PR)
end_trace = 1;
sp = frame->fixreg[1];
lr = frame->srr0 + 4;
} else if (!has_frame) {
lr = ddb_regs.lr;
has_frame = 1;
} else {
db_read_bytes(sp, sizeof(vaddr_t), (char *)&sp);
if (sp == 0)
break;
if (sp <= lastsp) {
(*pr)("Bad frame pointer: 0x%lx\n", sp);
break;
}
db_read_bytes(sp + 16, sizeof(vaddr_t), (char *)&lr);
}
callpc = lr - 4;
if (end_trace) {
(*pr)("End of kernel: 0x%lx lr 0x%lx\n", sp, callpc);
break;
}
--count;
}
}
extern char _start[], _etext[];
void
stacktrace_save_at(struct stacktrace *st, unsigned int skip)
{
struct callframe *frame, *lastframe, *limit;
struct proc *p = curproc;
st->st_count = 0;
if (p == NULL)
return;
frame = __builtin_frame_address(0);
limit = (struct callframe *)(p->p_addr + USPACE - FRAMELEN);
while (st->st_count < STACKTRACE_MAX) {
if (skip == 0)
st->st_pc[st->st_count++] = frame->cf_lr;
else
skip--;
lastframe = frame;
frame = (struct callframe *)frame->cf_sp;
if (frame <= lastframe)
break;
if (frame >= limit)
break;
if (frame->cf_lr < (vaddr_t)_start ||
frame->cf_lr >= (vaddr_t)_etext)
break;
}
}
void
stacktrace_save_utrace(struct stacktrace *st)
{
st->st_count = 0;
}