Subject: Changes to pmax/trap.c that add interrupt counters, better stack traceback
To: None <port-pmax@NetBSD.ORG>
From: Jonathan Stone <jonathan@DSG.Stanford.EDU>
List: port-pmax
Date: 01/28/1995 13:50:26
The following patch includes some changes to trap.c I've been meaning
to get merged for some time.
The patch changes the preprocessor macros that enable debugging of
Ultrix system-call emulation, since sometimes I need to debug the
Ultrix emulation syscall vector itself, without debugging native
syscalls or Ultrix emulation per se.
The patch adds a redundant sanity check that the new-style argument
counting mechanism is right for Ultrix syscalls -- that the new-style
(sy_argsize /sizeof(int)) gives the same result as the old-style
sy_nargs.. There's no real reason to keep this.
The patch adds interrupt counters for 5k/200s. Modifying other interrupt
handlers to count interrupts is simple enough. I think using an enum
to index the array in locore.S is better than the current scheme, but
I don't understand entirely how the XINE interrupts work, so I haven't
tried to generate a complete set of interrupts. If someone wants
to modify the 3min, xine, and 3max+ handlers accordingly, that'd be
great. I can guess at the appropriate code, but I can't test it.
The patch adds some really hairy-looking stack traceback code, which
I've used to do tracebacks from interrupt handlers. This may or may
not be redundant if there's a kernel debugger. I find that, when
debugging device drivers, getting a stack is often all I want.
There's some extra hairiness to allow an "addlog()" rather than a
"printf()", since at the time I did this, I really wanted samples of
stack tracebacks from within an interrupt handler. I think that
functionality may be useful again at some point.
--Jonathan
*** trap.c.DIST Wed Jan 18 04:02:09 1995
--- trap.c.PUBLIC Sat Jan 28 13:35:11 1995
***************
*** 82,87 ****
--- 82,90 ----
#include <le.h>
#include <dc.h>
+ #include <sys/cdefs.h>
+ #include <sys/syslog.h>
+
struct proc *machFPCurProcPtr; /* pointer to last proc to use FP */
extern void MachKernGenException();
***************
*** 91,96 ****
--- 94,100 ----
extern void MachTLBModException();
extern void MachTLBMissException();
extern unsigned MachEmulateBranch();
+ extern void MachUTLBMiss();
void (*machExceptionTable[])() = {
/*
***************
*** 162,167 ****
--- 166,175 ----
u_int ra;
u_int code;
} trapdebug[TRAPSIZE], *trp = trapdebug;
+
+ extern void idle(), cpu_switch(), splx(), MachEmptyWriteBuffer();
+ extern void stacktrace();
+ extern void logstacktrace();
#endif
static void pmax_errintr();
***************
*** 178,183 ****
--- 186,192 ----
#endif
int (*pmax_hardware_intr)() = (int (*)())0;
extern volatile struct chiptime *Mach_clock_addr;
+ extern unsigned long intrcnt[];
#ifdef COMPAT_ULTRIX
extern struct sysent ultrix_sysent[];
***************
*** 185,191 ****
extern char *ultrix_syscallnames[];
#endif
! #if defined(COMPAT_ULTRIX) && defined(SYSCALL_DEBUG)
int ultrix_scdebug = 1;
void
--- 194,205 ----
extern char *ultrix_syscallnames[];
#endif
! #if (defined(COMPAT_ULTRIX) && defined(SYSCALL_DEBUG))
! #define DEBUG_ULTRIX_SYSCALL 1
! #endif
!
!
! #if defined(DEBUG_ULTRIX_SYSCALL)
int ultrix_scdebug = 1;
void
***************
*** 195,203 ****
{
int i;
! if (!ultrix_scdebug)
return;
!
printf("proc %d: ultrix syscall ", p->p_pid);
if (code < 0 || code >= nultrix_sysent) {
printf("OUT OF RANGE (%d)", code);
--- 209,222 ----
{
int i;
! if (0 < code && code <= nultrix_sysent &&
! ultrix_sysent[code].sy_call == nosys)
! printf("Ultrix call %d mapped to bad syscall\n", code);
! if (!ultrix_scdebug) {
! if (code < 0 || code >= nultrix_sysent)
! code = 0;
return;
! }
printf("proc %d: ultrix syscall ", p->p_pid);
if (code < 0 || code >= nultrix_sysent) {
printf("OUT OF RANGE (%d)", code);
***************
*** 493,499 ****
if (i) {
locr0[V0] = i;
locr0[A3] = 1;
! #if defined(SYSCALL_DEBUG) && defined(COMPAT_ULTRIX)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_call(p,
code, callp->sy_narg,
--- 512,518 ----
if (i) {
locr0[V0] = i;
locr0[A3] = 1;
! #if defined(DEBUG_ULTRIX_SYSCALL)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_call(p,
code, callp->sy_narg,
***************
*** 538,544 ****
if (i) {
locr0[V0] = i;
locr0[A3] = 1;
! #if defined(SYSCALL_DEBUG) && defined(COMPAT_ULTRIX)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_call(p,
code, callp->sy_narg,
--- 557,563 ----
if (i) {
locr0[V0] = i;
locr0[A3] = 1;
! #if defined(DEBUG_ULTRIX_SYSCALL)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_call(p,
code, callp->sy_narg,
***************
*** 567,573 ****
callp = &systab[SYS_syscall]; /* (illegal) */
else
callp = &systab[code];
! i = callp->sy_narg;
args.i[0] = locr0[A0];
args.i[1] = locr0[A1];
args.i[2] = locr0[A2];
--- 586,596 ----
callp = &systab[SYS_syscall]; /* (illegal) */
else
callp = &systab[code];
! /* i = callp->sy_narg*/
! i = callp->sy_argsize / sizeof(int);
! if (i != callp->sy_narg)
! printf("syscall %d: args %d or %d?\n",
! code,i, callp->sy_narg);
args.i[0] = locr0[A0];
args.i[1] = locr0[A1];
args.i[2] = locr0[A2];
***************
*** 580,586 ****
if (i) {
locr0[V0] = i;
locr0[A3] = 1;
! #if defined(SYSCALL_DEBUG) && defined(COMPAT_ULTRIX)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_call(p,
code, callp->sy_narg,
--- 603,609 ----
if (i) {
locr0[V0] = i;
locr0[A3] = 1;
! #if defined(DEBUG_ULTRIX_SYSCALL)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_call(p,
code, callp->sy_narg,
***************
*** 601,607 ****
}
}
}
! #if defined(SYSCALL_DEBUG) && defined(COMPAT_ULTRIX)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_call(p, code, callp->sy_narg, args.i);
#endif
--- 624,630 ----
}
}
}
! #if defined(DEBUG_ULTRIX_SYSCALL)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_call(p, code, callp->sy_narg, args.i);
#endif
***************
*** 661,667 ****
locr0[A3] = 1;
}
done:
! #if defined(COMPAT_ULTRIX) && defined(SYSCALL_DEBUG)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_ret(p, code, i, rval[0]);
#endif
--- 684,690 ----
locr0[A3] = 1;
}
done:
! #if defined(DEBUG_ULTRIX_SYSCALL)
if (p->p_emul == EMUL_ULTRIX)
ultrix_scdebug_ret(p, code, i, rval[0]);
#endif
***************
*** 873,878 ****
--- 896,902 ----
if (pmax_hardware_intr)
splx((*pmax_hardware_intr)(mask, pc, statusReg, causeReg));
if (mask & MACH_INT_MASK_5) {
+ intrcnt[7]++;
if (!USERMODE(statusReg)) {
#ifdef DEBUG
trapDump("fpintr");
***************
*** 885,898 ****
}
if (mask & MACH_SOFT_INT_MASK_0) {
clearsoftclock();
cnt.v_soft++;
softclock();
}
/* process network interrupt if we trapped or will very soon */
if ((mask & MACH_SOFT_INT_MASK_1) ||
netisr && (statusReg & MACH_SOFT_INT_MASK_1)) {
- clearsoftnet();
cnt.v_soft++;
#ifdef INET
if (netisr & (1 << NETISR_ARP)) {
netisr &= ~(1 << NETISR_ARP);
--- 909,925 ----
}
if (mask & MACH_SOFT_INT_MASK_0) {
clearsoftclock();
+ intrcnt[0]++;
cnt.v_soft++;
softclock();
}
+
/* process network interrupt if we trapped or will very soon */
if ((mask & MACH_SOFT_INT_MASK_1) ||
netisr && (statusReg & MACH_SOFT_INT_MASK_1)) {
cnt.v_soft++;
+ intrcnt[1]++;
+ clearsoftnet();
#ifdef INET
if (netisr & (1 << NETISR_ARP)) {
netisr &= ~(1 << NETISR_ARP);
***************
*** 933,942 ****
--- 960,971 ----
/* handle clock interrupts ASAP */
if (mask & MACH_INT_MASK_3) {
+ intrcnt[6]++;
temp = c->regc; /* XXX clear interrupt bits */
cf.pc = pc;
cf.sr = statusReg;
hardclock(&cf);
+
/* keep clock interrupts enabled */
causeReg &= ~MACH_INT_MASK_3;
}
***************
*** 943,961 ****
/* Re-enable clock interrupts */
splx(MACH_INT_MASK_3 | MACH_SR_INT_ENA_CUR);
#if NSII > 0
! if (mask & MACH_INT_MASK_0)
siiintr(0);
#endif
#if NLE > 0
! if (mask & MACH_INT_MASK_1)
leintr(0);
#endif
#if NDC > 0
! if (mask & MACH_INT_MASK_2)
dcintr(0);
#endif
! if (mask & MACH_INT_MASK_4)
pmax_errintr();
return ((statusReg & ~causeReg & MACH_HARD_INT_MASK) |
MACH_SR_INT_ENA_CUR);
}
--- 972,998 ----
/* Re-enable clock interrupts */
splx(MACH_INT_MASK_3 | MACH_SR_INT_ENA_CUR);
#if NSII > 0
! if (mask & MACH_INT_MASK_0) {
! intrcnt[2]++;
siiintr(0);
+ }
#endif
#if NLE > 0
! if (mask & MACH_INT_MASK_1) {
! intrcnt[3]++;
leintr(0);
+ }
#endif
#if NDC > 0
! if (mask & MACH_INT_MASK_2) {
! intrcnt[4];
dcintr(0);
+ }
#endif
! if (mask & MACH_INT_MASK_4) {
! intrcnt[5];
pmax_errintr();
+ }
return ((statusReg & ~causeReg & MACH_HARD_INT_MASK) |
MACH_SR_INT_ENA_CUR);
}
***************
*** 987,992 ****
--- 1024,1030 ----
warned = 0;
printf("WARNING: power supply is OK again\n");
}
+ intrcnt[6]++;
temp = c->regc; /* XXX clear interrupt bits */
cf.pc = pc;
***************
*** 999,1009 ****
/* Re-enable clock interrupts */
splx(MACH_INT_MASK_1 | MACH_SR_INT_ENA_CUR);
if (mask & MACH_INT_MASK_0) {
csr = *(unsigned *)MACH_PHYS_TO_UNCACHED(KN02_SYS_CSR);
m = csr & (csr >> KN02_CSR_IOINTEN_SHIFT) & KN02_CSR_IOINT;
#if 0
! *(unsigned *)MACHPHYS_TO_UNCACHED(KN02_SYS_CSR) =
(csr & ~(KN02_CSR_WRESERVED | 0xFF)) |
(m << KN02_CSR_IOINTEN_SHIFT);
#endif
--- 1037,1048 ----
/* Re-enable clock interrupts */
splx(MACH_INT_MASK_1 | MACH_SR_INT_ENA_CUR);
if (mask & MACH_INT_MASK_0) {
+ static int intr_map[8] = { 8, 9, 10, 11, 12, 4, 3, 2 };
csr = *(unsigned *)MACH_PHYS_TO_UNCACHED(KN02_SYS_CSR);
m = csr & (csr >> KN02_CSR_IOINTEN_SHIFT) & KN02_CSR_IOINT;
#if 0
! *(unsigned *)MACH_PHYS_TO_UNCACHED(KN02_SYS_CSR) =
(csr & ~(KN02_CSR_WRESERVED | 0xFF)) |
(m << KN02_CSR_IOINTEN_SHIFT);
#endif
***************
*** 1010,1015 ****
--- 1049,1055 ----
for (i = 0; m; i++, m >>= 1) {
if (!(m & 1))
continue;
+ intrcnt[intr_map[i]]++;
if (tc_slot_info[i].intr)
(*tc_slot_info[i].intr)(tc_slot_info[i].unit);
else
***************
*** 1020,1027 ****
csr & ~(KN02_CSR_WRESERVED | 0xFF);
#endif
}
! if (mask & MACH_INT_MASK_3)
kn02_errintr();
return ((statusReg & ~causeReg & MACH_HARD_INT_MASK) |
MACH_SR_INT_ENA_CUR);
}
--- 1060,1074 ----
csr & ~(KN02_CSR_WRESERVED | 0xFF);
#endif
}
! if (mask & MACH_INT_MASK_3) {
! intrcnt[5]++;
kn02_errintr();
+ }
+
+ /* Reset the interrupts we just handled. We don't handle software
+ * interrupts here so don't clear them from the function result, which
+ * will be written into the interrupt-enable status register.
+ */
return ((statusReg & ~causeReg & MACH_HARD_INT_MASK) |
MACH_SR_INT_ENA_CUR);
}
***************
*** 1715,1726 ****
#define MIPS_JR_RA 0x03e00008 /* instruction code for jr ra */
/*
* Print a stack backtrace.
*/
void
! stacktrace(a0, a1, a2, a3)
int a0, a1, a2, a3;
{
unsigned pc, sp, fp, ra, va, subr;
unsigned instr, mask;
--- 1762,1790 ----
#define MIPS_JR_RA 0x03e00008 /* instruction code for jr ra */
+ /* forward */
+ char *fn_name(unsigned addr);
+ void stacktrace_subr __P((int, int, int, int, void (*)(const char*, ...)));
+
/*
* Print a stack backtrace.
*/
+ void stacktrace(a0, a1, a2, a3)
+ int a0, a1, a2, a3;
+ {
+ stacktrace_subr(a0, a1, a2, a3, printf);
+ }
+
+ void logstacktrace(a0, a1, a2, a3)
+ int a0, a1, a2, a3;
+ {
+ stacktrace_subr(a0, a1, a2, a3, addlog);
+ }
+
void
! stacktrace_subr(a0, a1, a2, a3, printfn)
int a0, a1, a2, a3;
+ void (*printfn) __P((const char*, ...));
{
unsigned pc, sp, fp, ra, va, subr;
unsigned instr, mask;
***************
*** 1729,1734 ****
--- 1793,1799 ----
int regs[3];
extern setsoftclock();
extern char start[], edata[];
+ unsigned int frames = 0;
cpu_getregs(regs);
***************
*** 1738,1770 ****
ra = 0;
fp = regs[2];
loop:
! /* check for current PC in the kernel interrupt handler code */
if (pc >= (unsigned)MachKernIntr && pc < (unsigned)MachUserIntr) {
/* NOTE: the offsets depend on the code in locore.s */
! printf("interrupt\n");
a0 = kdbpeek(sp + 36);
a1 = kdbpeek(sp + 40);
a2 = kdbpeek(sp + 44);
a3 = kdbpeek(sp + 48);
! pc = kdbpeek(sp + 20);
! ra = kdbpeek(sp + 92);
! sp = kdbpeek(sp + 100);
! fp = kdbpeek(sp + 104);
}
! /* check for current PC in the exception handler code */
! if (pc >= 0x80000000 && pc < (unsigned)setsoftclock) {
ra = 0;
! subr = 0;
goto done;
}
/* check for bad PC */
if (pc & 3 || pc < 0x80000000 || pc >= (unsigned)edata) {
! printf("PC 0x%x: not in kernel\n", pc);
ra = 0;
- subr = 0;
goto done;
}
--- 1803,1882 ----
ra = 0;
fp = regs[2];
+ /* Jump here when done with a frame, to start a new one */
loop:
! ra = 0;
!
! /* Jump here after a nonstandard (interrupt handler) frame */
! specialframe:
! stksize = 0;
! subr = 0;
! if (frames++ > 100) {
! (*printfn)("\nstackframe count exceeded\n");
! return; /*XXX*/
! }
!
! /* check for bad SP: could foul up next frame */
! if (sp & 3 || sp < 0x80000000) {
! (*printfn)("SP 0x%x: not in kernel\n", sp);
! ra = 0;
! subr = 0;
! goto done;
! }
!
! /* Backtraces should contine through interrupts from kernel mode */
if (pc >= (unsigned)MachKernIntr && pc < (unsigned)MachUserIntr) {
/* NOTE: the offsets depend on the code in locore.s */
! (*printfn)("MachKernIntr+%x: (%x, %x ,%x) -------\n",
! pc-(unsigned)MachKernIntr, a0, a1, a2);
a0 = kdbpeek(sp + 36);
a1 = kdbpeek(sp + 40);
a2 = kdbpeek(sp + 44);
a3 = kdbpeek(sp + 48);
!
! pc = kdbpeek(sp + 20); /* exc_pc - pc at time of exception */
! ra = kdbpeek(sp + 92); /* ra at time of exception */
! sp = sp + 108;
! goto specialframe;
}
!
! # define Between(x, y, z) \
! ( ((x) <= (y)) && ((y) < (z)) )
! # define pcBetween(a,b) \
! Between((unsigned)a, pc, (unsigned)b)
!
! /*
! * Check for current PC in exception handler code that don't
! * have a preceding "j ra" at the tail of the preceding function.
! * Depends on relative ordering of functions in locore.
! */
! if (pcBetween(MachKernGenException, MachUserGenException))
! subr = (unsigned) MachKernGenException;
! else if (pcBetween(MachUserGenException,MachKernIntr))
! subr = (unsigned) MachUserGenException;
! else if (pcBetween(MachKernIntr, MachUserIntr))
! subr = (unsigned) MachKernIntr;
! else if (pcBetween(MachUserIntr, MachTLBMissException))
! subr = (unsigned) MachUserIntr;
! else if (pcBetween(splx, MachEmptyWriteBuffer))
! subr = (unsigned) splx;
! else if (pcBetween(cpu_switch, fuword))
! subr = (unsigned) cpu_switch;
! else if (pcBetween(idle, cpu_switch)) {
! subr = (unsigned) idle;
ra = 0;
! goto done;
! }
! else if (pc >= (unsigned)MachUTLBMiss && pc < (unsigned)setsoftclock) {
! (*printfn)("<<locore>>");
goto done;
}
/* check for bad PC */
if (pc & 3 || pc < 0x80000000 || pc >= (unsigned)edata) {
! (*printfn)("PC 0x%x: not in kernel\n", pc);
ra = 0;
goto done;
}
***************
*** 1772,1791 ****
* Find the beginning of the current subroutine by scanning backwards
* from the current PC for the end of the previous subroutine.
*/
! va = pc - sizeof(int);
! while ((instr = kdbpeek(va)) != MIPS_JR_RA)
va -= sizeof(int);
! va += 2 * sizeof(int); /* skip back over branch & delay slot */
! /* skip over nulls which might separate .o files */
! while ((instr = kdbpeek(va)) == 0)
! va += sizeof(int);
! subr = va;
/* scan forwards to find stack size and any saved registers */
stksize = 0;
more = 3;
mask = 0;
! for (; more; va += sizeof(int), more = (more == 3) ? 3 : more - 1) {
/* stop if hit our current position */
if (va >= pc)
break;
--- 1884,1911 ----
* Find the beginning of the current subroutine by scanning backwards
* from the current PC for the end of the previous subroutine.
*/
! if (!subr) {
! va = pc - sizeof(int);
! while ((instr = kdbpeek(va)) != MIPS_JR_RA)
va -= sizeof(int);
! va += 2 * sizeof(int); /* skip back over branch & delay slot */
! /* skip over nulls which might separate .o files */
! while ((instr = kdbpeek(va)) == 0)
! va += sizeof(int);
! subr = va;
! }
+ /*
+ * Jump here for locore entry pointsn for which the preceding
+ * function doesn't end in "j ra"
+ */
+ stackscan:
/* scan forwards to find stack size and any saved registers */
stksize = 0;
more = 3;
mask = 0;
! for (va = subr; more; va += sizeof(int),
! more = (more == 3) ? 3 : more - 1) {
/* stop if hit our current position */
if (va >= pc)
break;
***************
*** 1833,1839 ****
/* only restore the first one */
if (mask & (1 << i.IType.rt))
break;
! mask |= 1 << i.IType.rt;
switch (i.IType.rt) {
case 4: /* a0 */
a0 = kdbpeek(sp + (short)i.IType.imm);
--- 1953,1959 ----
/* only restore the first one */
if (mask & (1 << i.IType.rt))
break;
! mask |= (1 << i.IType.rt);
switch (i.IType.rt) {
case 4: /* a0 */
a0 = kdbpeek(sp + (short)i.IType.imm);
***************
*** 1865,1886 ****
/* look for stack pointer adjustment */
if (i.IType.rs != 29 || i.IType.rt != 29)
break;
! stksize = (short)i.IType.imm;
}
}
done:
! printf("%x+%x (%x,%x,%x,%x) ra %x sz %d\n",
! subr, pc - subr, a0, a1, a2, a3, ra, stksize);
if (ra) {
if (pc == ra && stksize == 0)
! printf("stacktrace: loop!\n");
else {
pc = ra;
! sp -= stksize;
goto loop;
}
}
}
#endif /* DEBUG */
--- 1985,2050 ----
/* look for stack pointer adjustment */
if (i.IType.rs != 29 || i.IType.rt != 29)
break;
! stksize = - ((short)i.IType.imm);
}
}
done:
! (*printfn)("%s+%x (%x,%x,%x,%x) ra %x sz %d\n",
! fn_name(subr), pc - subr, a0, a1, a2, a3, ra, stksize);
if (ra) {
if (pc == ra && stksize == 0)
! (*printfn)("stacktrace: loop!\n");
else {
pc = ra;
! sp += stksize;
! ra = 0;
goto loop;
}
+ } else {
+ if (curproc)
+ (*printfn)("User-level: pid %d\n", curproc->p_pid);
+ else
+ (*printfn)("User-level: curproc NULL\n");
}
}
+
+ /*
+ * Functions ``special'' enough to print by name
+ */
+ #ifdef __STDC__
+ #define Name(_fn) { (void*)_fn, # _fn }
+ #else
+ #define Name(_fn) { _fn, "_fn"}
+ #endif
+ static struct { void *addr; char *name;} names[] = {
+ Name(interrupt),
+ Name(trap),
+ Name(MachKernGenException),
+ Name(MachUserGenException),
+ Name(MachKernIntr),
+ Name(MachUserIntr),
+ Name(splx),
+ Name(idle),
+ Name(cpu_switch),
+ {0, 0}
+ };
+
+ /*
+ * Map a function address to a string name, if known; or a hex string.
+ */
+ char *
+ fn_name(unsigned addr)
+ {
+ static char buf[17];
+ int i = 0;
+
+ for (i = 0; names[i].name; i++)
+ if (names[i].addr == (void*)addr)
+ return (names[i].name);
+ sprintf(buf, "%x", addr);
+ return (buf);
+ }
+
#endif /* DEBUG */