#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: db_trace.c,v 1.63 2023/09/26 14:33:55 tsutsui Exp $");
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <machine/db_machdep.h>
#include <ddb/db_interface.h>
#include <ddb/db_output.h>
#include <ddb/db_access.h>
#include <ddb/db_sym.h>
#include <ddb/db_variables.h>
static int db_var_short(const struct db_variable *, db_expr_t *, int);
const struct db_variable db_regs[] = {
{ "d0", (long *)&ddb_regs.tf_regs[0], FCN_NULL, NULL },
{ "d1", (long *)&ddb_regs.tf_regs[1], FCN_NULL, NULL },
{ "d2", (long *)&ddb_regs.tf_regs[2], FCN_NULL, NULL },
{ "d3", (long *)&ddb_regs.tf_regs[3], FCN_NULL, NULL },
{ "d4", (long *)&ddb_regs.tf_regs[4], FCN_NULL, NULL },
{ "d5", (long *)&ddb_regs.tf_regs[5], FCN_NULL, NULL },
{ "d6", (long *)&ddb_regs.tf_regs[6], FCN_NULL, NULL },
{ "d7", (long *)&ddb_regs.tf_regs[7], FCN_NULL, NULL },
{ "a0", (long *)&ddb_regs.tf_regs[8+0], FCN_NULL, NULL },
{ "a1", (long *)&ddb_regs.tf_regs[8+1], FCN_NULL, NULL },
{ "a2", (long *)&ddb_regs.tf_regs[8+2], FCN_NULL, NULL },
{ "a3", (long *)&ddb_regs.tf_regs[8+3], FCN_NULL, NULL },
{ "a4", (long *)&ddb_regs.tf_regs[8+4], FCN_NULL, NULL },
{ "a5", (long *)&ddb_regs.tf_regs[8+5], FCN_NULL, NULL },
{ "a6", (long *)&ddb_regs.tf_regs[8+6], FCN_NULL, NULL },
{ "sp", (long *)&ddb_regs.tf_regs[8+7], FCN_NULL, NULL },
{ "pc", (long *)&ddb_regs.tf_pc, FCN_NULL, NULL },
{ "sr", (long *)&ddb_regs.tf_sr, db_var_short, NULL }
};
const struct db_variable * const db_eregs =
db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
static int
db_var_short(const struct db_variable *varp, db_expr_t *valp, int op)
{
if (op == DB_VAR_GET)
*valp = (db_expr_t)*((short*)varp->valuep);
else
*((short*)varp->valuep) = (short) *valp;
return 0;
}
#define MAXINT 0x7fffffff
#define INKERNEL(va,pcb) (((u_int)(va) > (u_int)(pcb)) && \
((u_int)(va) < ((u_int)(pcb) + USPACE)))
#define get(addr, space) \
(db_get_value((db_addr_t)(addr), sizeof(int), false))
#define get16(addr, space) \
(db_get_value((db_addr_t)(addr), sizeof(u_short), false))
#define NREGISTERS 16
struct stackpos {
int k_pc;
int k_fp;
int k_nargs;
int k_entry;
int k_caller;
int k_flags;
int k_regloc[NREGISTERS];
};
static void findentry(struct stackpos *, void (*)(const char *, ...));
#ifdef _KERNEL
static void findregs(struct stackpos *, db_addr_t);
static int nextframe(struct stackpos *, struct pcb *, int,
void (*)(const char *, ...));
#endif
static void stacktop(db_regs_t *, struct stackpos *,
void (*)(const char *, ...));
#define FR_SAVFP 0
#define FR_SAVPC 4
static void
stacktop(db_regs_t *regs, struct stackpos *sp, void (*pr)(const char *, ...))
{
int i;
for (i = 0; i < (8+6); i++) {
sp->k_regloc[i] = (int) ®s->tf_regs[i];
}
sp->k_fp = get(®s->tf_regs[8+6], 0);
sp->k_pc = get(®s->tf_pc, 0);
sp->k_flags = 0;
findentry(sp, pr);
}
#define HIWORD 0xffff0000
#define LOWORD 0x0000ffff
#define LINKLA6 0x480e0000
#define LINKWA6 0x4e560000
#define ADDLSP 0xdffc0000
#define ADDWSP 0xdefc0000
#define LEASP 0x4fef0000
#define TSTBSP 0x4a2f0000
#define INSMSK 0xfff80000
#define MOVLSP 0x2e800000
#define MOVLD0 0x20000000
#define MOVLA0 0x20400000
#define MVLMSK 0xf1ff0000
#define MOVEML 0x48d70000
#define JSR 0x4eb80000
#define JSRPC 0x4eba0000
#define LONGBIT 0x00010000
#define BSR 0x61000000
#define BSRL 0x61ff0000
#define BYTE3 0x0000ff00
#define LOBYTE 0x000000ff
#define ADQMSK 0xf1ff0000
#define ADDQSP 0x508f0000
#define ADDQWSP 0x504f0000
#if 0
static struct nlist * trampsym = 0;
static struct nlist * funcsym = 0;
#endif
#ifdef _KERNEL
static int
nextframe(struct stackpos *sp, struct pcb *pcb, int kerneltrace,
void (*pr)(const char *, ...))
{
int i;
db_addr_t addr;
db_addr_t calladdr;
db_addr_t oldfp = sp->k_fp;
calladdr = sp->k_caller;
addr = sp->k_entry;
if (addr == MAXINT) {
for (i = 0; i < NREGISTERS; i++)
sp->k_regloc[i] = -1;
} else
findregs(sp, addr);
sp->k_pc = calladdr;
sp->k_fp = get(sp->k_fp + FR_SAVFP, DSP);
sp->k_flags = 0;
if (kerneltrace && !INKERNEL(sp->k_fp, pcb)) {
sp->k_nargs = 0;
sp->k_entry = MAXINT;
} else
findentry(sp, pr);
if (sp->k_fp == 0 || oldfp == (db_addr_t)sp->k_fp)
return 0;
return sp->k_fp;
}
#endif
static void
findentry(struct stackpos *sp, void (*pr)(const char *, ...))
{
int instruc;
int val;
db_addr_t addr, nextword;
addr = get(sp->k_fp + FR_SAVPC, DSP);
if (addr == 0) {
sp->k_entry = MAXINT;
sp->k_nargs = 0;
return;
}
instruc = get(addr - 6, ISP);
nextword = get(addr - 4, ISP);
if ((instruc & HIWORD) == (JSR | LONGBIT)) {
sp->k_caller = addr - 6;
sp->k_entry = nextword;
} else if ((instruc & HIWORD) == BSRL) {
sp->k_caller = addr - 6;
sp->k_entry = nextword + (addr - 4);
} else {
instruc = nextword;
if ((instruc & HIWORD) == JSR) {
sp->k_caller = addr - 4;
sp->k_entry = instruc & LOWORD;
} else if ((instruc & HIWORD) == BSR) {
sp->k_caller = addr - 4;
sp->k_entry = (addr - 2) + (short)(instruc & LOWORD);
} else if ((instruc & HIWORD) == JSRPC) {
sp->k_caller = addr - 4;
sp->k_entry = (addr - 2) + (instruc & LOWORD);
} else {
if ((instruc & BYTE3) == (BSR >> 16)) {
sp->k_caller = addr - 2;
sp->k_entry = addr + (char)(instruc & LOBYTE);
} else {
sp->k_caller = addr - 2;
sp->k_entry = MAXINT;
}
}
}
instruc = get(addr, ISP);
if ((instruc & MVLMSK) == MOVLA0 ||
(instruc & MVLMSK) == MOVLD0)
instruc = get(addr += 2, ISP);
if ((instruc & ADQMSK) == ADDQSP ||
(instruc & ADQMSK) == ADDQWSP) {
val = 0;
do {
int n;
n = (instruc >> (16+9)) & 07;
if (n == 0)
n = 8;
val += n;
instruc = get(addr += 2, ISP);
} while ((instruc & ADQMSK) == ADDQSP ||
(instruc & ADQMSK) == ADDQWSP);
} else if ((instruc & HIWORD) == ADDLSP)
val = get(addr + 2, ISP);
else if ((instruc & HIWORD) == ADDWSP ||
(instruc & HIWORD) == LEASP)
val = instruc & LOWORD;
else
val = 20;
sp->k_nargs = val / 4;
}
#ifdef _KERNEL
static void
findregs(struct stackpos *sp, db_addr_t addr)
{
long instruc, val, i;
int regp;
regp = 0;
instruc = get(addr, ISP);
if ((instruc & HIWORD) == LINKLA6) {
instruc = get(addr + 2, ISP);
addr += 6;
regp = sp->k_fp + instruc;
} else if ((instruc & HIWORD) == LINKWA6) {
addr += 4;
if ((instruc &= LOWORD) == 0) {
instruc = get(addr, ISP);
if ((instruc & HIWORD) == ADDLSP) {
instruc = get(addr + 2, ISP);
addr += 6;
}
} else {
instruc <<= 16;
instruc >>= 16;
}
regp = sp->k_fp + instruc;
}
instruc = get(addr, ISP);
if ((instruc & HIWORD) == TSTBSP)
addr += 4;
instruc = get(addr, ISP);
if ((instruc & INSMSK) == MOVLSP) {
i = (instruc >> 16) & 07;
sp->k_regloc[i] = regp;
} else if ((instruc & HIWORD) == MOVEML) {
val = instruc & LOWORD;
i = 0;
while (val) {
if (val & 1) {
sp->k_regloc[i] = regp;
regp += sizeof(int);
}
val >>= 1;
i++;
}
}
}
#endif
void
db_stack_trace_print(db_expr_t addr, bool have_addr, db_expr_t count,
const char *modif, void (*pr)(const char *, ...))
{
int i, nargs;
long val;
db_addr_t regp;
const char * name;
struct stackpos pos;
struct pcb *pcb;
struct lwp *l;
#ifdef _KERNEL
bool kernel_only = true;
#endif
bool trace_thread = false;
bool lwpaddr = false;
int fault_pc = 0;
{
const char *cp = modif;
char c;
while ((c = *cp++) != 0) {
if (c == 'a') {
lwpaddr = true;
trace_thread = true;
} else if (c == 't')
trace_thread = true;
#ifdef _KERNEL
else if (c == 'u')
kernel_only = false;
#endif
}
}
#ifdef _KERNEL
l = curlwp;
#endif
if (!have_addr)
stacktop(&ddb_regs, &pos, pr);
else {
if (trace_thread) {
struct proc *p;
if (lwpaddr) {
l = (struct lwp *)addr;
p = l->l_proc;
(*pr)("trace: pid %d ", p->p_pid);
} else {
(*pr)("trace: pid %d ", (int)addr);
#ifdef _KERNEL
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);
pcb = lwp_getpcb(l);
pos.k_fp = pcb->pcb_regs[PCB_REGS_FP];
pos.k_pc = get(pcb->pcb_regs[PCB_REGS_SP], DSP);
(*pr)("at %p\n", (void *)pos.k_fp);
} else {
pos.k_fp = addr;
pos.k_pc = MAXINT;
}
pos.k_flags = 0;
pos.k_nargs = 0;
pos.k_entry = MAXINT;
for (i = 0; i < NREGISTERS; i++)
pos.k_regloc[i] = 0;
findentry(&pos, pr);
}
while (count) {
count--;
if (pos.k_pc == MAXINT) {
name = "?";
pos.k_pc = 0;
val = MAXINT;
} else {
db_find_sym_and_offset(pos.k_pc, &name, &val);
if (name == 0) {
name = "?";
val = MAXINT;
}
}
if (strcmp(___STRING(_C_LABEL(trap)), name) == 0) {
int tfp;
tfp = pos.k_fp + FR_SAVFP + 4 + (5 * 4);
regp = tfp + offsetof(struct frame, f_sr);
if (!USERMODE(get16(regp, DSP))) {
regp = tfp + offsetof(struct frame, f_pc);
fault_pc = get(regp, DSP);
}
} else if (fault_pc) {
if (strcmp("faultstkadj", name) == 0) {
db_find_sym_and_offset(fault_pc, &name, &val);
if (name == 0) {
name = "?";
val = MAXINT;
}
}
fault_pc = 0;
}
(*pr)("%s", name);
if (pos.k_entry != MAXINT && name) {
const char *entry_name;
long e_val;
db_find_sym_and_offset(pos.k_entry, &entry_name,
&e_val);
if (entry_name != 0 && entry_name != name &&
e_val != val) {
(*pr)("(?)\n%s", entry_name);
}
}
(*pr)("(");
regp = pos.k_fp + FR_SAVFP + 4;
if ((nargs = pos.k_nargs)) {
while (nargs--) {
(*pr)("%lx", get(regp += 4, DSP));
if (nargs)
(*pr)(",");
}
}
if (val == MAXINT)
(*pr)(") at %x\n", pos.k_pc);
else
(*pr)(") + %lx\n", val);
#ifdef _KERNEL
pcb = lwp_getpcb(l);
if (kernel_only && !INKERNEL(pos.k_fp, pcb))
break;
if (nextframe(&pos, pcb, kernel_only, pr) == 0)
break;
#endif
}
}