#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.6 2022/12/24 14:14:52 uwe Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <uvm/uvm_prot.h>
#include <uvm/uvm_pmap.h>
#include <machine/frame.h>
#include <machine/trap.h>
#include <machine/intrdefs.h>
#include <machine/pmap.h>
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_access.h>
#include <ddb/db_variables.h>
#include <ddb/db_output.h>
#include <ddb/db_interface.h>
#include <ddb/db_user.h>
#include <ddb/db_proc.h>
#include <ddb/db_command.h>
#include <x86/db_machdep.h>
int
db_x86_regop(const struct db_variable *vp, db_expr_t *val, int opcode)
{
db_expr_t *regaddr =
(db_expr_t *)(((uint8_t *)DDB_REGS) + ((size_t)vp->valuep));
switch (opcode) {
case DB_VAR_GET:
*val = *regaddr;
break;
case DB_VAR_SET:
*regaddr = *val;
break;
default:
db_printf("db_x86_regop: unknown op %d", opcode);
db_error(NULL);
}
return 0;
}
#if 0
db_addr_t db_trap_symbol_value = 0;
db_addr_t db_syscall_symbol_value = 0;
db_addr_t db_kdintr_symbol_value = 0;
bool db_trace_symbols_found = false;
void db_find_trace_symbols(void);
void
db_find_trace_symbols(void)
{
db_expr_t value;
if (db_value_of_name("_trap", &value))
db_trap_symbol_value = (db_addr_t) value;
if (db_value_of_name("_kdintr", &value))
db_kdintr_symbol_value = (db_addr_t) value;
if (db_value_of_name("_syscall", &value))
db_syscall_symbol_value = (db_addr_t) value;
db_trace_symbols_found = true;
}
#endif
#define set_frame_callpc() do { \
frame = (long *)ddb_regs.tf_bp; \
callpc = (db_addr_t)ddb_regs.tf_ip; \
} while (0)
void
db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif, void (*pr)(const char *, ...))
{
long *frame, *lastframe;
long *retaddr, *arg0;
long *argp;
db_addr_t callpc;
int is_trap;
bool kernel_only = true;
bool trace_thread = false;
bool lwpaddr = false;
#if 0
if (!db_trace_symbols_found)
db_find_trace_symbols();
#endif
{
const char *cp = modif;
char c;
while ((c = *cp++) != 0) {
if (c == 'a') {
lwpaddr = true;
trace_thread = true;
}
if (c == 't')
trace_thread = true;
if (c == 'u')
kernel_only = false;
}
}
if (have_addr && trace_thread) {
struct pcb *pcb;
proc_t p;
lwp_t l;
if (lwpaddr) {
db_read_bytes(addr, sizeof(l),
(char *)&l);
db_read_bytes((db_addr_t)l.l_proc,
sizeof(p), (char *)&p);
(*pr)("trace: pid %d ", p.p_pid);
} else {
proc_t *pp;
(*pr)("trace: pid %d ", (int)addr);
if ((pp = db_proc_find((pid_t)addr)) == 0) {
(*pr)("not found\n");
return;
}
db_read_bytes((db_addr_t)pp, sizeof(p), (char *)&p);
addr = (db_addr_t)p.p_lwps.lh_first;
db_read_bytes(addr, sizeof(l), (char *)&l);
}
(*pr)("lid %d ", l.l_lid);
pcb = lwp_getpcb(&l);
#ifdef _KERNEL
if (l.l_proc == curproc && (lwp_t *)addr == curlwp)
set_frame_callpc();
else
#endif
{
db_read_bytes((db_addr_t)&pcb->pcb_bp,
sizeof(frame), (char *)&frame);
db_read_bytes((db_addr_t)(frame + 1),
sizeof(callpc), (char *)&callpc);
db_read_bytes((db_addr_t)frame,
sizeof(frame), (char *)&frame);
}
(*pr)("at %p\n", frame);
} else if (have_addr) {
frame = (long *)addr;
db_read_bytes((db_addr_t)(frame + 1),
sizeof(callpc), (char *)&callpc);
db_read_bytes((db_addr_t)frame,
sizeof(frame), (char *)&frame);
} else {
set_frame_callpc();
}
retaddr = frame + 1;
arg0 = frame + 2;
lastframe = NULL;
while (count && frame != 0) {
int narg;
const char *name;
db_expr_t offset;
db_sym_t sym;
char *argnames[MAXNARG], **argnp = NULL;
db_addr_t lastcallpc;
name = "?";
is_trap = NONE;
offset = 0;
sym = db_frame_info(frame, callpc, &name, &offset, &is_trap,
&narg);
if (lastframe == NULL && sym == DB_SYM_NULL && callpc != 0) {
u_long instr = db_get_value(callpc, 4, false);
offset = 1;
if (
#ifdef __x86_64__
(instr == 0xe5894855 ||
(instr & 0x00ffffff) == 0x0048e589
)
#else
((instr & 0x00ffffff) == 0x00e58955 ||
(instr & 0x0000ffff) == 0x0000e589
)
#endif
)
{
offset = 0;
}
}
if (is_trap == NONE) {
if (db_sym_numargs(sym, &narg, argnames))
argnp = argnames;
else
narg = db_numargs(frame);
}
(*pr)("%s(", name);
if (lastframe == NULL && offset == 0 && !have_addr) {
argp = (long *)&((struct x86_frame *)
(ddb_regs.tf_sp-sizeof(long)))->f_arg0;
} else {
argp = frame + 2;
}
while (narg) {
if (argnp)
(*pr)("%s=", *argnp++);
(*pr)("%lx", db_get_value((long)argp, sizeof(long),
false));
argp++;
if (--narg != 0)
(*pr)(",");
}
(*pr)(") at ");
db_printsym(callpc, DB_STGY_PROC, pr);
(*pr)("\n");
if (lastframe == NULL && offset == 0 && !have_addr) {
struct x86_frame *fp = (void *)
(ddb_regs.tf_sp-sizeof(long));
lastframe = (long *)fp;
callpc = (db_addr_t)
db_get_value((db_addr_t)&fp->f_retaddr,
sizeof(long), false);
continue;
}
lastframe = frame;
lastcallpc = callpc;
if (!db_nextframe(&frame, &retaddr, &arg0, &callpc,
frame + 2, is_trap, pr))
break;
if (INKERNEL((long)frame)) {
#ifdef __i386__
if (!db_intrstack_p(frame) &&
db_intrstack_p(lastframe)) {
(*pr)("--- switch to interrupt stack ---\n");
} else
#endif
if (frame < lastframe ||
(frame == lastframe && callpc == lastcallpc)) {
(*pr)("Bad frame pointer: %p\n", frame);
break;
}
} else if (INKERNEL((long)lastframe)) {
if (kernel_only)
break;
} else {
if (frame <= lastframe) {
(*pr)("Bad user frame pointer: %p\n", frame);
break;
}
}
--count;
}
if (count && is_trap != NONE) {
db_printsym(callpc, DB_STGY_XTRN, pr);
(*pr)(":\n");
}
}