#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/stacktrace.h>
#include <sys/user.h>
#include <arm64/armreg.h>
#include <machine/db_machdep.h>
#include <ddb/db_access.h>
#include <ddb/db_interface.h>
#include <ddb/db_variables.h>
#include <ddb/db_sym.h>
#include <ddb/db_output.h>
db_regs_t ddb_regs;
#define INKERNEL(va) (((vaddr_t)(va)) & (1ULL << 63))
void
db_stack_trace_print(db_expr_t addr, int have_addr, db_expr_t count,
char *modif, int (*pr)(const char *, ...))
{
struct switchframe sf;
vaddr_t frame, lastframe, lr, lastlr;
char c, *cp = modif;
db_expr_t offset;
Elf_Sym * sym;
const char *name;
int kernel_only = 1;
int trace_thread = 0;
struct proc *p;
while ((c = *cp++) != 0) {
if (c == 'u')
kernel_only = 0;
if (c == 't')
trace_thread = 1;
}
if (trace_thread) {
p = tfind((pid_t)addr);
if (p == NULL) {
(*pr)("not found\n");
return;
}
}
if (!have_addr) {
frame = ddb_regs.tf_x[29];
lr = ddb_regs.tf_elr;
} else if (trace_thread) {
db_read_bytes(p->p_addr->u_pcb.pcb_sp, sizeof(sf), &sf);
frame = sf.sf_x29;
lr = sf.sf_lr;
} else {
frame = (vaddr_t)db_get_value(addr, 8, 0);
lr = (vaddr_t)db_get_value(addr + 8, 8, 0);
}
while (count-- && frame != 0) {
lastlr = lr;
lr = (vaddr_t)db_get_value(frame + 8, 8, 0);
if (INKERNEL(frame)) {
sym = db_search_symbol(lastlr, DB_STGY_ANY, &offset);
db_symbol_values(sym, &name, NULL);
} else {
sym = NULL;
name = NULL;
}
if (name == NULL || strcmp(name, "end") == 0) {
(*pr)("%llx at 0x%lx", lastlr, lr - 4);
} else {
(*pr)("%s() at ", name);
db_printsym(lr - 4, DB_STGY_PROC, pr);
}
(*pr)("\n");
if (name != NULL) {
if ((strcmp (name, "handle_el0_irq") == 0) ||
(strcmp (name, "handle_el1h_irq") == 0) ||
(strcmp (name, "handle_el0_fiq") == 0) ||
(strcmp (name, "handle_el1h_fiq") == 0)) {
(*pr)("--- interrupt ---\n");
} else if (
(strcmp (name, "handle_el0_sync") == 0) ||
(strcmp (name, "handle_el1h_sync") == 0)) {
(*pr)("--- trap ---\n");
}
}
lastframe = frame;
frame = (vaddr_t)db_get_value(frame, 8, 0);
if (frame == 0) {
break;
}
if (INKERNEL(frame)) {
if (frame <= lastframe) {
(*pr)("Bad frame pointer: 0x%lx\n", frame);
break;
}
} else if (INKERNEL(lastframe)) {
if (kernel_only) {
(*pr)("end of kernel\n");
break;
}
} else {
if (frame <= lastframe) {
(*pr)("Bad user frame pointer: 0x%lx\n",
frame);
break;
}
}
--count;
}
}
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);
KASSERT(INKERNEL(frame));
limit = (struct callframe *)STACKALIGN(p->p_addr + USPACE -
sizeof(struct trapframe) - 0x10);
while (st->st_count < STACKTRACE_MAX) {
if (skip == 0)
st->st_pc[st->st_count++] = frame->f_lr;
else
skip--;
lastframe = frame;
frame = frame->f_frame;
if (frame <= lastframe)
break;
if (frame >= limit)
break;
if (!INKERNEL(frame->f_lr))
break;
}
}
void
stacktrace_save_utrace(struct stacktrace *st)
{
st->st_count = 0;
}