#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.39 2023/11/21 22:19:12 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <machine/alpha.h>
#include <machine/db_machdep.h>
#include <machine/alpha_instruction.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>
struct prologue_info {
int pi_reg_offset[32];
uint32_t pi_regmask;
int pi_frame_size;
};
static void
decode_prologue(db_addr_t callpc, db_addr_t func,
struct prologue_info *pi, void (*pr)(const char *, ...))
{
long signed_immediate;
alpha_instruction ins;
db_addr_t pc;
pi->pi_regmask = 0;
pi->pi_frame_size = 0;
#define CHECK_FRAMESIZE \
do { \
if (pi->pi_frame_size != 0) { \
(*pr)("frame size botch: adjust register offsets?\n"); \
} \
} while (0)
for (pc = func; pc < callpc; pc += sizeof(alpha_instruction)) {
db_read_bytes(pc, sizeof(ins.bits), (char *)&ins.bits);
if (ins.mem_format.opcode == op_lda &&
ins.mem_format.ra == 30 &&
ins.mem_format.rb == 30) {
signed_immediate = (long)ins.mem_format.displacement;
if (signed_immediate <= 0) {
CHECK_FRAMESIZE;
pi->pi_frame_size += -signed_immediate;
}
} else if (ins.operate_lit_format.opcode == op_arit &&
ins.operate_lit_format.function == op_subq &&
ins.operate_lit_format.ra == 30 &&
ins.operate_lit_format.rc == 30) {
CHECK_FRAMESIZE;
pi->pi_frame_size += ins.operate_lit_format.literal;
} else if (ins.mem_format.opcode == op_stq &&
ins.mem_format.rb == 30 &&
ins.mem_format.ra != 31) {
signed_immediate = (long)ins.mem_format.displacement;
pi->pi_regmask |= 1 << ins.mem_format.ra;
pi->pi_reg_offset[ins.mem_format.ra] = signed_immediate;
}
}
}
static void
decode_syscall(int number, void (*pr)(const char *, ...))
{
(*pr)(" (%d)", number);
}
void
db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif, void (*pr)(const char *, ...))
{
db_stack_trace_print_ra(0, false, addr, have_addr,
count, modif, pr);
}
void
db_stack_trace_print_ra(db_expr_t ra, bool have_ra,
db_expr_t addr, bool have_addr,
db_expr_t count,
const char *modif, void (*pr)(const char *, ...))
{
db_addr_t callpc, frame, symval;
struct prologue_info pi;
db_expr_t diff;
db_sym_t sym;
u_long tfps;
const char *symname;
struct pcb *pcbp;
const char *cp = modif;
struct trapframe *tf;
bool ra_from_tf;
u_long last_ipl = ~0L;
char c;
bool trace_thread = false;
bool lwpaddr = false;
while ((c = *cp++) != 0) {
trace_thread |= c == 't';
trace_thread |= c == 'a';
lwpaddr |= c == 'a';
}
if (!have_addr) {
addr = DDB_REGS->tf_regs[FRAME_SP] - FRAME_SIZE * 8;
tf = (struct trapframe *)addr;
callpc = db_alpha_tf_reg(tf, FRAME_PC);
frame = (db_addr_t)tf + FRAME_SIZE * 8;
ra_from_tf = true;
} else {
#ifdef _KERNEL
struct proc *p = NULL;
struct lwp *l = NULL;
#else
struct proc pstore, *p = &pstore;
struct lwp lstore, *l = &lstore;
#endif
if (trace_thread) {
if (lwpaddr) {
#ifdef _KERNEL
l = (struct lwp *)addr;
p = l->l_proc;
#else
db_read_bytes(addr, sizeof(*l), (char *)l);
db_read_bytes((db_addr_t)l->l_proc,
sizeof(*p), (char *)p);
#endif
(*pr)("trace: pid %d ", p->p_pid);
} else {
#ifdef _KERNEL
(*pr)("trace: pid %d ", (int)addr);
p = proc_find_raw(addr);
if (p == NULL) {
(*pr)("not found\n");
return;
}
l = LIST_FIRST(&p->p_lwps);
KASSERT(l != NULL);
#else
(*pr)("no proc_find_raw() in crash\n");
return;
#endif
}
(*pr)("lid %d ", l->l_lid);
pcbp = lwp_getpcb(l);
addr = db_alpha_read_saved_reg(&pcbp->pcb_hw.apcb_ksp);
callpc = db_alpha_read_saved_reg(&pcbp->pcb_context[7]);
(*pr)("at 0x%lx\n", addr);
} else if (have_ra) {
callpc = ra;
(*pr)("at 0x%lx pc 0x%lx\n", addr, callpc);
} else {
(*pr)("alpha trace requires known PC =eject=\n");
return;
}
frame = addr;
tf = NULL;
ra_from_tf = false;
}
while (count--) {
sym = db_search_symbol(callpc, DB_STGY_ANY, &diff);
if (sym == DB_SYM_NULL)
break;
db_symbol_values(sym, &symname, (db_expr_t *)&symval);
if (callpc < symval) {
(*pr)("symbol botch: callpc 0x%lx < "
"func 0x%lx (%s)\n", callpc, symval, symname);
return;
}
if (db_alpha_sym_is_backstop(symval)) {
(*pr)("--- kernel thread backstop ---\n");
break;
}
(*pr)("%s() at ", symname);
db_printsym(callpc, DB_STGY_PROC, pr);
(*pr)("\n");
if (db_alpha_sym_is_trap(symval)) {
tf = (struct trapframe *)frame;
(*pr)("--- %s", db_alpha_trapsym_description(symval));
tfps = db_alpha_tf_reg(tf, FRAME_PS);
if (db_alpha_sym_is_syscall(symval)) {
decode_syscall(db_alpha_tf_reg(tf, FRAME_V0),
pr);
}
if ((tfps & ALPHA_PSL_IPL_MASK) != last_ipl) {
last_ipl = tfps & ALPHA_PSL_IPL_MASK;
if (! db_alpha_sym_is_syscall(symval)) {
(*pr)(" (from ipl %ld)", last_ipl);
}
}
(*pr)(" ---\n");
if (tfps & ALPHA_PSL_USERMODE) {
(*pr)("--- user mode ---\n");
break;
}
callpc = db_alpha_tf_reg(tf, FRAME_PC);
frame = (db_addr_t)tf + FRAME_SIZE * 8;
ra_from_tf = true;
continue;
}
decode_prologue(callpc, symval, &pi, pr);
if ((pi.pi_regmask & (1 << 26)) == 0) {
if (ra_from_tf) {
callpc = db_alpha_tf_reg(tf, FRAME_RA);
} else {
(*pr)("--- root of call graph ---\n");
break;
}
} else {
unsigned long reg;
db_read_bytes(frame + pi.pi_reg_offset[26],
sizeof(reg), (char *)®);
callpc = reg;
}
frame += pi.pi_frame_size;
ra_from_tf = false;
}
}