1
0
Files
2022-09-29 17:59:04 +03:00

4395 lines
109 KiB
C

#ident "$Header: /proj/irix6.5.7m/isms/irix/cmd/icrash_old/lib/libutil/RCS/trace.c,v 1.1 1999/05/25 19:19:20 tjm Exp $"
#include <stdio.h>
#include <sys/types.h>
#include <sys/pcb.h>
#include "icrash.h"
#include "trace.h"
#include "extern.h"
/*
* alloc_sframe() -- Allocate a stack frame record
*/
sframe_t *
alloc_sframe(trace_t *trace)
{
sframe_t *f;
if (!(f = (sframe_t *) alloc_block(sizeof (sframe_t), B_TEMP))) {
return((sframe_t *)NULL);
}
f->level = trace->nframes + 1;
return(f);
}
/*
* free_sframes() -- Free all stack frames allocated to a trace record.
*/
void
free_sframes(trace_t *t)
{
sframe_t *sf;
t->nframes = 0;
sf = t->frame;
while(t->frame) {
sf = (sframe_t *)kl_dequeue((element_t **)&t->frame);
if (sf->srcfile) {
free_block((k_ptr_t)sf->srcfile);
}
free_block((k_ptr_t)sf);
}
}
/*
* alloc_trace_rec() -- Allocate stack trace header
*/
trace_t *
alloc_trace_rec()
{
trace_t *t;
t = (trace_t *)alloc_block(sizeof(trace_t), B_TEMP);
return(t);
}
/*
* free_trace_rec() -- Free memory associated with stack trace header
*/
void
free_trace_rec(trace_t *t)
{
int i;
if (t->ktp) {
free_block(t->ktp);
}
if (t->ktp2) {
free_block(t->ktp2);
}
if (t->exp) {
free_block(t->exp);
}
if (t->pdap) {
free_block(t->pdap);
}
for (i = 0; i < STACK_SEGMENTS; i++) {
if (t->stack[i].ptr) {
free_block((k_ptr_t)t->stack[i].ptr);
}
}
free_sframes(t);
free_block((k_ptr_t)t);
}
/*
* clean_trace_rec() -- Clean up stack trace record without releasing
* any of the allocated memory (except sframes).
*/
void
clean_trace_rec(trace_t *t)
{
int i;
t->flags = 0;
t->kthread = 0;
if (t->ktp) {
free_block(t->ktp);
t->ktp = 0;
}
t->kthread2 = 0;
if (t->ktp2) {
free_block(t->ktp2);
t->ktp2 = 0;
}
t->exception = 0;
if (t->exp) {
free_block(t->exp);
t->exp = 0;
}
t->u_eframe = 0;
if (t->pdap) {
free_block(t->pdap);
t->pdap = (k_ptr_t)NULL;
}
t->stackcnt = 0;
t->intstack = 0;
t->bootstack = 0;
for (i = 0; i < STACK_SEGMENTS; i++) {
if (t->stack[i].ptr) {
t->stack[i].type = 0;
t->stack[i].size = 0;
t->stack[i].addr = (kaddr_t)NULL;
free_block((k_ptr_t)t->stack[i].ptr);
t->stack[i].ptr = (uint *)NULL;
}
}
t->nframes = 0;
free_sframes(t);
}
/*
* setup_trace_rec()
*
* Set up a trace record and initialize all the relevant fields
* depending on the value of the flag paramater.
*
* 1 == KTHREAD_FLAG
*
* This option is used to setup a trace record for any kind of a
* kthread. The kthread structure contains information on what type
* of thread environment it supports. (XXX: For now, only PROC and
* KTHREAD environments are supported). It is assumed that the
* kthread pointer actually points to a data buffer containing the
* full type specific structure (e.g., proc struct). Such a pointer
* is returned from the kl_get_kthread() routine. (XXX: For now, the
* kthread struct is always part of the proc structure). A check is
* made to see if the process is running on a cpu. If it is, we need
* to get the pad_s struct for the cpu. The kthread stack (kernel
* stack), plus the intstack and bootstack if the process is running
* on a cpu, are then loaded into buffers and placed in the trace
* record.
*
* 2 == KTHREAD2_FLAG
*
* This is a special option. It is for that rare case when we have to
* have more than one kthread mapped into the trace_rec. This option
* will only work when the trace_rec passed in has already been set up.
*
* 3 == CPU_FLAG
*
* The ptr paramater points to a data block containing a pda_s
* struct. Get the intstack and bootstack, load them into buffers,
* and link them into the trace record. Check and see if a
* process/kthread is running on this cpu. If one is, load the
* kthread into the trace record.
*
* 4 == RAW_FLAG
*
* The addr paramater contains the virtual address for the start of
* a stack. There is no way to tell if this address is a
* kernel/kthread stack or if it is from one of the cpus (intstack
* or bootstack). We will load it anyway and assume that the size of
* the stack is one page. (XXX: We might like to cycle through the
* cpus looking to see if the stack address is a bootstack or
* intstack. Also, we could walk through the process/kthreads and
* see if there is a mapping for it).
*
* If an error occurs, the global structure error gets loaded with
* the appropriate error related information and '1' is returned. For
* all flag values except RAW_FLAG, we need to map ptr into the
* appropriate trace rec field even when we fail. That will ensure
* that the data block will be freed up by free_trace_rec() or
* clean_trace_rec().
*
*/
int
setup_trace_rec(kaddr_t addr, kstruct_t *s, int flag, trace_t *trace)
{
int type, size, stkflg = -1;
char stat;
cpuid_t cpuid;
kaddr_t kthread, upage, curkthread, saved_kthread;
kl_reset_error();
if (flag == KTHREAD_FLAG) {
trace->ktp = s->ptr;
trace->kthread = s->addr;
/* Make sure the kthread is valid
*/
#ifdef ANON_ITHREADS
if (!IS_UTHREAD(K, trace->ktp) && !IS_XTHREAD(K, trace->ktp) &&
!IS_ITHREAD(K, trace->ktp) && !IS_STHREAD(K, trace->ktp)) {
#else
if (!IS_UTHREAD(K, trace->ktp) && !IS_XTHREAD(K, trace->ktp) &&
!IS_STHREAD(K, trace->ktp)) {
#endif
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "setup_trace_rec: kthread at 0x%llx "
"is not valid!\n", trace->kthread);
}
KL_SET_ERROR_NVAL(KLE_BAD_KTHREAD, trace->kthread, 2);
return(1);
}
/* If the kthread is KT_UTHREAD type, then get the exception
* structure as well (that's where the u_eframe is stored).
*/
if (IS_UTHREAD(K, trace->ktp)) {
trace->exception =
kl_kaddr(K, trace->ktp, "uthread_s", "ut_exception");
trace->exp = alloc_block(EXCEPTION_SIZE(K), B_TEMP);
kl_get_block(K, trace->exception, EXCEPTION_SIZE(K),
trace->exp, "exception");
if (KL_ERROR) {
KL_SET_ERROR_NVAL(KLE_BAD_KTHREAD, trace->kthread, 2);
return(1);
}
trace->u_eframe =
trace->exception + FIELD("exception", "u_eframe");
}
/* If the kthread is running on a cpu, get the pda_s struct,
* along with the address of the intstack and bootstack.
*/
if ((cpuid = kl_uint(K, trace->ktp,
"kthread", "k_sonproc", 0)) != -1) {
trace->pdap = alloc_block(PDA_S_SIZE(K), B_TEMP);
kl_get_pda_s(K, (kaddr_t)cpuid, trace->pdap);
if (KL_ERROR) {
return(1);
}
trace->intstack = kl_kaddr(K, trace->pdap, "pda_s", "p_intstack");
trace->bootstack =
kl_kaddr(K, trace->pdap, "pda_s", "p_bootstack");
}
}
else if (flag == KTHREAD2_FLAG) {
#ifdef ANON_ITHREADS
/* Before we do anyting else, we have to make sure that the
* the thread already mapped in is of type ITHREAD (XXX or
* XTHREAD?)
*/
if (!IS_ITHREAD(K, trace->ktp)) {
KL_SET_ERROR_NVAL(KLE_BAD_KTHREAD, s->addr, 2);
return(1);
}
/* XXX -- Not sure about the following code if there aren't any
* ithreads. Have to look into this...
*/
#endif
trace->ktp2 = s->ptr;
trace->kthread2 = s->addr;
/* Make sure the kthread is valid
*/
#ifdef ANON_ITHREADS
if (!IS_UTHREAD(K, trace->ktp2) && !IS_XTHREAD(K, trace->ktp2) &&
!IS_ITHREAD(K, trace->ktp2) && !IS_STHREAD(K, trace->ktp2)) {
#else
if (!IS_UTHREAD(K, trace->ktp2) && !IS_XTHREAD(K, trace->ktp2) &&
!IS_STHREAD(K, trace->ktp2)) {
#endif
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "setup_trace_rec: kthread at 0x%llx "
"is not valid!\n", trace->kthread2);
}
KL_SET_ERROR_NVAL(KLE_BAD_KTHREAD, trace->kthread2, 2);
return(1);
}
/* If the kthread is KT_UTHREAD type, then get the exception
* structure as well (that's where the u_eframe is stored).
* There shouldn't already be one there...
*/
if (IS_UTHREAD(K, trace->ktp2)) {
trace->exception =
kl_kaddr(K, trace->ktp2, "uthread_s", "ut_exception");
trace->exp = alloc_block(EXCEPTION_SIZE(K), B_TEMP);
kl_get_block(K, trace->exception, EXCEPTION_SIZE(K),
trace->exp, "exception");
if (KL_ERROR) {
KL_SET_ERROR_NVAL(KLE_BAD_KTHREAD, trace->kthread2, 2);
return(1);
}
trace->u_eframe =
trace->exception + FIELD("exception", "u_eframe");
}
/* The kthread SHOULDN't be running on a cpu. If it, then
* return with an error.
*/
if ((cpuid = kl_uint(K, trace->ktp2,
"kthread", "k_sonproc", 0)) != -1) {
KL_SET_ERROR_NVAL(KLE_BAD_KTHREAD, trace->kthread2, 2);
return(1);
}
}
else if (flag == CPU_FLAG) {
trace->pdap = s->ptr;
trace->intstack = kl_kaddr(K, trace->pdap, "pda_s", "p_intstack");
trace->bootstack = kl_kaddr(K, trace->pdap, "pda_s", "p_bootstack");
stkflg = kl_uint(K, trace->pdap, "pda_s", "p_kstackflag", 0);
/* Check to see if there is a kthread running on this CPU.
*/
cpuid = kl_uint(K, trace->pdap, "pda_s", "p_cpuid", 0);
curkthread = kl_kaddr(K, trace->pdap, "pda_s", "p_curkthread");
if (curkthread) {
trace->ktp = kl_get_kthread(K, curkthread, 0);
trace->kthread = curkthread;
if (KL_ERROR) {
/* It's possible that with an NMI dump that the
* kthread pointer in the pda_s struct is bad. So,
* we will set a flag in the trace_rec indicating
* this and forge ahead. It's the only way to
* get a stack trace on the INTSTACK or BOOTSTACK.
*/
if (IS_NMI(K)) {
trace->flags |= TF_BAD_KTHREAD;
if (trace->ktp) {
free_block(trace->ktp);
}
trace->ktp = 0;
trace->kthread = 0;
}
else {
return(1);
}
}
else {
/* If the kthread is KT_UTHREAD type, then get the exception
* structure as well (that's where the u_eframe is stored).
*/
if (IS_UTHREAD(K, trace->ktp)) {
trace->exception = kl_kaddr(K, trace->ktp,
"uthread_s", "ut_exception");
trace->exp = alloc_block(EXCEPTION_SIZE(K), B_TEMP);
kl_get_block(K, trace->exception, EXCEPTION_SIZE(K),
trace->exp, "exception");
if (KL_ERROR) {
KL_SET_ERROR_NVAL(KLE_BAD_KTHREAD, trace->kthread, 2);
return(1);
}
trace->u_eframe = trace->exception +
FIELD("exception", "u_eframe");
}
}
}
}
else if (flag == RAW_FLAG) {
/* Currently, we don't support trace_rec setups for
* non kthread/proc/pda stacks. For now, just return
* an error ...
*/
KL_SET_ERROR(KLE_NOT_IMPLEMENTED);
return(1);
}
else {
KL_SET_ERROR(KLE_UNKNOWN_ERROR);
return(1);
}
/* Now to add all valid stacks... Note that in the case of KTHRAD2_FLAG,
* we only add that stack.
*/
/* First, check to see if this is a CPU trace
*/
if (trace->pdap && !(trace->flags & TF_TRACEREC_VALID)) {
/* Add the bootstack
*/
size = stack_size(BOOTSTACK, trace);
if (add_stack(trace->bootstack, BOOTSTACK, size, trace) == -1) {
return(1);
}
/* Add the intstack
*/
size = stack_size(INTSTACK, trace);
if (add_stack(trace->intstack, INTSTACK, size, trace) == -1) {
return(1);
}
}
/* Add the kthread stack if there is one
*/
if (trace->ktp && !(trace->flags & TF_TRACEREC_VALID)) {
/* Get the stack address from the kthread
*/
addr = kthread_stack(trace, &size);
if (KL_ERROR) {
return(1);
}
/* Determine what type of stack this is
*/
if (IS_UTHREAD(K, trace->ktp)) {
if (addr == K_KERNELSTACK(K)) {
type = KERNELSTACK;
}
else if (addr == K_KEXTSTACK(K)) {
type = KEXTSTACK;
}
}
else if (IS_XTHREAD(K, trace->ktp)) {
type = XTHREADSTACK;
}
#ifdef ANON_ITHREADS
else if (IS_ITHREAD(K, trace->ktp)) {
type = ITHREADSTACK;
}
#endif
else if (IS_STHREAD(K, trace->ktp)) {
type = STHREADSTACK;
}
/* We have to set defkthread equal to kthread so that address
* translation will be done properly.
*/
saved_kthread = K_DEFKTHREAD(K);
kl_set_defkthread(K, trace->kthread);
/* Add the stack to the trace record.
*/
if (add_stack(addr, type, size, trace) == -1) {
return(1);
}
/* Now set defkthread back
*/
kl_set_defkthread(K, saved_kthread);
}
/* Add the second kthread stack
*/
if (flag == KTHREAD2_FLAG) {
/* Get the stack address from the kthread
*/
addr = kl_kthread_stack(K, trace->kthread2, &size);
if (KL_ERROR) {
return(1);
}
/* Determine what type of kthread stack this is
*/
if (IS_UTHREAD(K, trace->ktp2)) {
if (addr == K_KERNELSTACK(K)) {
type = KERNELSTACK;
}
else if (addr == K_KEXTSTACK(K)) {
type = KEXTSTACK;
}
}
else if (IS_XTHREAD(K, trace->ktp2)) {
type = XTHREADSTACK;
}
#ifdef ANON_ITHREADS
else if (IS_ITHREAD(K, trace->ktp2)) {
type = ITHREADSTACK;
}
#endif
else if (IS_STHREAD(K, trace->ktp2)) {
type = STHREADSTACK;
}
/* We have to set defkthread equal to kthread2 so that address
* translation will be done properly.
*/
saved_kthread = K_DEFKTHREAD(K);
kl_set_defkthread(K, trace->kthread2);
/* Add the stack to the trace record.
*/
if (add_stack(addr, type, size, trace) == -1) {
return(1);
}
/* Now set defkthread back
*/
kl_set_defkthread(K, saved_kthread);
trace->flags |= TF_KTHREAD2_VALID;
return(0);
}
trace->flags = TF_TRACEREC_VALID;
return(0);
}
/*
* setup_kthread_trace_rec()
*/
int
setup_kthread_trace_rec(kaddr_t value, int mode, trace_t *trace)
{
k_ptr_t ktp;
kstruct_t *ksp;
kaddr_t kthread;
kl_reset_error();
if (mode != 2) {
KL_SET_ERROR_NVAL(KLE_BAD_KTHREAD, value, mode);
return(1);
}
kthread = value;
ktp = kl_get_kthread(K, kthread, 0);
if (KL_ERROR) {
return(1);
}
ksp = (kstruct_t *)alloc_block(sizeof(kstruct_t), B_TEMP);
ksp->addr = kthread;
ksp->ptr = ktp;
setup_trace_rec((kaddr_t)0, ksp, KTHREAD_FLAG, trace);
free_block((k_ptr_t)ksp);
if (KL_ERROR) {
return(1);
}
else {
return(0);
}
}
/*
* setup_cpu_trace_rec()
*
* Value can be a cpuid or it can be a pointer to a pda_s strudt.
* The kl_get_pda_s() routine determins which type of value it is.
*/
int
setup_cpu_trace_rec(kaddr_t value, trace_t *trace)
{
k_ptr_t pdap;
cpuid_t cpuid;
kstruct_t *ksp;
kaddr_t pda;
kl_reset_error();
pdap = alloc_block(PDA_S_SIZE(K), B_TEMP);
ksp = (kstruct_t *)alloc_block(sizeof(kstruct_t), B_TEMP);
kl_get_pda_s(K, value, pdap);
if (KL_ERROR) {
free_block(pdap);
free_block((k_ptr_t)ksp);
return(1);
}
cpuid = kl_uint(K, pdap, "pda_s", "p_cpuid", 0);
ksp->addr = kl_pda_s_addr(K, cpuid);
ksp->ptr = pdap;
setup_trace_rec((kaddr_t)0, ksp, CPU_FLAG, trace);
free_block((k_ptr_t)ksp);
if (KL_ERROR) {
return(1);
}
else {
return(0);
}
}
/*
* stack_index()
*
* Check to see if we have a stack record allocated that contains
* addr. Return the stack record index if we do. Return -1 if addr
* is not a valid stack address. This routine assumes that the trace
* record has been fully initialized before entering.
*/
int
stack_index(kaddr_t addr, trace_t *trace)
{
int i;
kaddr_t saddr;
if (DEBUG(DC_TRACE, 1) || DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "stack_index: addr=0x%llx, trace=0x%x\n",
addr, trace);
}
kl_reset_error();
kl_is_valid_kaddr(K, addr, trace->ktp, WORD_ALIGN_FLAG);
if (KL_ERROR) {
if (trace->flags & TF_KTHREAD2_VALID) {
kl_reset_error();
kl_is_valid_kaddr(K, addr, trace->ktp2, WORD_ALIGN_FLAG);
}
if (KL_ERROR) {
KL_ERROR |= KLE_BAD_SP;
return(-1);
}
}
/* Cycle through all the stacks in trace record and see if addr
* falls inside one of them.
*/
for (i = 0; i < trace->stackcnt; i++) {
if ((addr >= trace->stack[i].addr) &&
(addr < trace->stack[i].addr + trace->stack[i].size)) {
return(i);
}
}
KL_SET_ERROR_NVAL(KLE_BAD_SP, addr, 2);
return (-1);
}
/*
* add_stack()
*
* Allocate space for a new stack and load it into the next
* available slot in the trace record stack array. Return the stack
* record index upon success. Return -1 if an error occurs.
*/
int
add_stack(kaddr_t addr, int type, int size, trace_t *trace)
{
int curstack;
k_ptr_t ptr;
if (DEBUG(DC_TRACE, 1) || DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "add_stack: addr=0x%llx, type=%d, "
"size=%d, trace=0x%x\n", addr, type, size, trace);
}
kl_reset_error();
/* Allocate space for the stack.
*/
ptr = alloc_block(size, B_TEMP);
/* Get the contents of stack and load it into the newly allocated
* buffer
*/
kl_readmem(K, addr, 1, ptr, 0, size, "stack");
if (KL_ERROR) {
if (KL_ERROR == KLE_INVALID_KERNELSTACK) {
k_uint_t e = KL_ERROR;
/* XXX - This doesn't work anymore (kthread != proc)
*/
if (kl_uint(K, trace->ktp2, "proc", "p_stat", 0) == 3) {
KL_SET_ERROR_NVAL(e|KLE_DEFUNCT_PROCESS, addr, 2);
}
else {
KL_SET_ERROR_NVAL(e, addr, 2);
}
}
else {
KL_SET_ERROR_NVAL(KL_ERROR|KLE_BAD_SADDR, addr, 2);
}
free_block(ptr);
return(-1);
}
/* Bump the stack counter
*/
curstack = trace->stackcnt;
trace->stackcnt++;
/* Fill in the stack record detail.
*/
trace->stack[curstack].ptr = ptr;
trace->stack[curstack].size = size;
trace->stack[curstack].type = type;
trace->stack[curstack].addr = addr;
return (curstack);
}
/*
* stack_type()
*
* Given the virtual stack address addr, check to see which stack it
* is from. A stack type will be returned ONLY IF a proper virtual
* to physical mapping is possible (e.g., the kthread must be of
* type KT_UTHREAD if the stack type is KERNELSTACK). Note that it is
* expected that all relevent fields in the trace struct (kthread,
* pdap, etc.) will be filled in upon entry to this routine.
* Consequently, if the stack isn't already loaded in, we have
* to return an error.
*/
int
stack_type(kaddr_t addr, trace_t *trace)
{
int i, type = 0;
kaddr_t kstack;
kl_reset_error();
/* First, cycle through any stacks that have already been
* allocated to see if there is a match.
*/
for (i = 0; i < trace->stackcnt; i++) {
if ((addr >= trace->stack[i].addr) &&
(addr < trace->stack[i].addr + trace->stack[i].size)) {
return(trace->stack[i].type);
}
else if (trace->stack[i].addr == K_KERNELSTACK(K)) {
if ((addr >= K_KEXTSTACK(K)) && (addr < K_KERNELSTACK(K))) {
/* There isn't any page mapped for the kextstack page.
* just return the kernelstack as the type.
*/
return(trace->stack[i].type);
}
}
}
KL_SET_ERROR_NVAL(E_STACK_NOT_FOUND, addr, 2);
return(0);
}
/*
* stack_size()
*
* Return the size of a stack based on its type. Use type to locate
* the proper stack. The trace record should be fully set up before
* calling this routine. If no stack could be found, then return a
* size of zero.
*/
int
stack_size(int type, trace_t *trace)
{
int i, size = 0;
kaddr_t saddr;
kl_reset_error();
/* First, cycle through all stacks contained in the trace
* record and see if the one we are looking for is currently
* mapped. We can't use this approach if there is more than
* one kthread mapped in.
*/
if (!(trace->flags & TF_KTHREAD2_VALID)) {
for (i = 0; i < trace->stackcnt; i++) {
if (type == trace->stack[i].type) {
return(trace->stack[i].size);
}
}
}
/* If we reach this point, most likely it was from a call in the
* setup_trace_rec() routine. Use a basic set of rules to determine
* the size of the stack.
*/
switch (type) {
case KERNELSTACK :
size = K_PAGESZ(K);
break;
case KEXTSTACK :
size = 2 * K_PAGESZ(K);
break;
#ifdef ANON_ATHREADS
case ITHREADSTACK :
#endif
case XTHREADSTACK :
case STHREADSTACK :
/* XXX -- we need to handle the two kthread case!
*/
if (trace->ktp) {
size = KL_UINT(K, trace->ktp, "kthread", "k_stacksize");
}
break;
case INTSTACK :
if (trace->pdap) {
kaddr_t lastframe;
lastframe = kl_kaddr(K, trace->pdap, "pda_s", "p_intlastframe");
size = (lastframe + EFRAME_S_SIZE(K)) - trace->intstack;
}
break;
case BOOTSTACK :
if (trace->pdap) {
size = K_PAGESZ(K) - (trace->bootstack & (K_PAGESZ(K) - 1));
}
break;
case RAW_STACK :
size = K_PAGESZ(K);
break;
default:
size = 0;
break;
}
return(size);
}
/*
* stack_addr()
*
* Given a virtual stack address (addr), check to see which stack it
* is from. Return the start address of that stack. Note that a
* stack address will be returned ONLY IF a proper virtual to
* physical mapping is possible. Also note that it is assumed that
* all relevent fields in the trace struct (kthread, pdap, etc.) are
* filled in appropriately.
*/
kaddr_t
stack_addr(kaddr_t addr, trace_t *trace)
{
int type, size;
kaddr_t saddr;
kl_reset_error();
type = stack_type(addr, trace);
if (KL_ERROR) {
return(0);
}
/* Return the stack address based on the stack type.
*/
switch (type) {
#ifdef ANON_ITHREADS
case ITHREADSTACK :
#endif
case XTHREADSTACK :
case STHREADSTACK :
saddr = kl_kthread_stack(K, trace->kthread, &size);
if (trace->flags & TF_KTHREAD2_VALID) {
/* We have to make sure this is the right stack address;
*/
if ((addr > saddr) && (addr <= (saddr + size))) {
break;
}
saddr = kl_kthread_stack(K, trace->kthread2, &size);
}
break;
case KERNELSTACK:
saddr = K_KERNELSTACK(K);
break;
case KEXTSTACK :
saddr = K_KEXTSTACK(K);
break;
case BOOTSTACK :
saddr = trace->bootstack;
break;
case INTSTACK :
saddr = trace->intstack;
break;
case UNKNOWN_STACK :
saddr = addr & (1 - (K_PAGESZ(K) - 1));
break;
default :
saddr = 0;
break;
}
if (saddr == 0) {
KL_SET_ERROR_NVAL(E_STACK_NOT_FOUND|KLE_BAD_SP, saddr, 2);
}
if (PTRSZ64(K)) {
return (saddr);
}
else {
return (saddr & 0xffffffff);
}
}
/*
* kthread_stack()
*
* This routine returns the address of the kthread stack (which
* can be a process kernelstack or it can be a stack for an
* sthread). If a pointer to a size variable is passed in,
* the size of the stack is returned as well. Special
* considerations are taken when the kernel is configured to
* support stack extension pages (for processes only). Since the
* extended page address is always mapped in (along with a
* double page stack size), we have to determine if the
* extension page has actually been allocated before we return
* it (XXX Need to check on this). Otherwise, the call to kl_virtop()
* will fail. If it is allocated, then we return the stack
* extension address. Otherwise, we return the kernelstack (and
* reduce the size by half).
*/
kaddr_t
kthread_stack(trace_t *trace, int *size)
{
kaddr_t saddr;
if (DEBUG(DC_TRACE, 1) || DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "kthread_stack: trace=0x%x, size=0x%x\n",
trace, size);
}
kl_reset_error();
if (!trace->ktp) {
KL_SET_ERROR(E_NO_KTHREAD);
return(0);
}
saddr = kl_kaddr(K, trace->ktp, "kthread", "k_stack");
/* If stack extensions are enabled (usually only with 32-bit
* kernels) we have to check and see if an extension page is
* mapped in for the current process. If a page is mapped in,
* then use it, otherwise use the regular stack page.
*/
if (K_EXTUSIZE(K)) {
if (saddr == K_KEXTSTACK(K)) {
if (!IS_UTHREAD(K, trace->ktp)) {
if (IS_XTHREAD(K, trace->ktp)) {
KL_SET_ERROR(KLE_IS_XTHREAD);
}
#ifdef ANON_ITHREADS
else if (IS_ITHREAD(K, trace->ktp)) {
KL_SET_ERROR(KLE_IS_ITHREAD);
}
#endif
else if (IS_STHREAD(K, trace->ktp)) {
KL_SET_ERROR(KLE_IS_STHREAD);
}
return(0);
}
kl_virtop(K, saddr, trace->ktp);
if (KL_ERROR) {
saddr = K_KERNELSTACK(K);
if (size) {
*size = stack_size(KERNELSTACK, trace);
}
}
else if (size) {
*size = stack_size(KEXTSTACK, trace);
}
return(saddr);
}
}
/* We either have the kernelstack (on a system that does not
* support extension stack pages) -- or an sthread stack. Just
* get the size from the kthread struct.
*/
if (size) {
*size = KL_UINT(K, trace->ktp, "kthread", "k_stacksize");
}
return(saddr);
}
/*
* is_kthreadstack()
*/
int
is_kthreadstack(kaddr_t addr, trace_t *trace)
{
int size;
kaddr_t ktstack;
kl_reset_error();
/* Just in case...
*/
if (!trace->ktp) {
KL_SET_ERROR(E_NO_KTHREAD);
return(0);
}
ktstack = kl_kaddr(K, trace->ktp, "kthread", "k_stack");
size = KL_UINT(K, trace->ktp, "kthread", "k_stacksize");
if ((addr >= ktstack) && (addr < (ktstack + size))) {
return(1);
}
else {
KL_SET_ERROR_NVAL(KLE_BAD_KERNELSTACK, addr, 2);
return(0);
}
}
/*
* _get_kthread_stack()
*
* A wrapper for kthread_stack() that allows routines outside the
* trace module to get kthread stack addresses (they don't need to
* know about trace structs).
*/
kaddr_t
_get_kthread_stack(kaddr_t kthread, int *size)
{
kaddr_t addr;
k_ptr_t ktp;
kstruct_t *ksp;
trace_t *trace;
kl_reset_error();
if (*size) {
*size = 0;
}
trace = alloc_trace_rec();
ksp = (kstruct_t*)alloc_block(sizeof(kstruct_t), B_TEMP);
ktp = kl_get_kthread(K, kthread, 0);
if (KL_ERROR) {
free_block((k_ptr_t)ksp);
free_trace_rec(trace);
return((kaddr_t)NULL);
}
ksp->addr = kthread;
ksp->ptr = ktp;
setup_trace_rec((kaddr_t)0, ksp, KTHREAD_FLAG, trace);
free_block((k_ptr_t)ksp);
if (KL_ERROR) {
free_trace_rec(trace);
return((kaddr_t)NULL);
}
addr = kthread_stack(trace, size);
free_trace_rec(trace);
return(addr);
}
/*
* get_frame_size()
*
* Search the size of func (or first 40 instructions) looking for
* the instruction that decrements the stack pointer. The amount of
* the decrement is the size of stack frame. If this is a LEAF
* function, there will be no stack frame size (return 0).
*
* Note that it is possible for part of a function to use the
* exception frame/SP from the calling function and part that
* allocates space in the stack. We have to be careful not to
* go beyond addr when determining this. To search the entire
* function for frame_size regardless of PC, flag must be non-zero.
*/
short
get_frame_size(kaddr_t addr, int flag)
{
int i, off, size;
uint instr;
kaddr_t func_addr;
func_addr = get_funcaddr(addr);
if (KL_ERROR) {
return(-1);
}
size = get_funcsize(func_addr);
if (KL_ERROR) {
/* This should never really happen, but just in case...
*/
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "get_frame_size: func_addr=0x%llx, size=%d\n",
func_addr, size);
}
size = 40;
}
else {
/* Convert size to number of instructions. If flag is non-zero,
* use size returned by get_funcsize() regardless of PC value.
* Otherwise, adjust size to go only upto and including PC.
*/
if (flag == 0) {
size = (addr - func_addr);
}
size = size / 4;
}
for(i = 0; i < size; i++) {
kl_get_block(K, (func_addr + (i * 4)), 4, &instr, "instr");
if (PTRSZ64(K)) {
int op;
op =(instr & 0xfc000000) >> 26;
if (op) {
if ((op == 25) && (((instr & 0x3e00000) >> 21) == 29) &&
(((instr & 0x1f0000) >> 16) == 29)) {
off = (short)(instr & 0xffff);
return(abs(off));
}
}
else {
op = instr & 0x3f;
if (((op == 45) || (op == 47)) &&
(((instr & 0x3e00000) >> 21) == 29) &&
(((instr & 0xf800) >> 11) == 29)) {
uint last_instr;
int rt;
/* We have to back up one instruction and get the
* imediate value that was loaded into rt ($at).
* That's should be our offset.
*/
if (i) {
rt = (instr & 0x1f0000) >> 16;
kl_get_block(K, (func_addr + ((i - 1) * 4)), 4,
&last_instr, "instr");
if (((last_instr & 0x1f0000) >> 16) == rt) {
off = (short)(last_instr & 0xffff);
return(abs(off));
}
}
}
else if ((op == 47) && (((instr & 0x3e00000) >> 21) == 29) &&
(((instr & 0xf800) >> 11) == 29)) {
uint last_instr;
int rt;
/* We have to back up one instruction and get the
* imediate value that was loaded into rt ($at).
* That's should be our offset.
*/
if (i) {
rt = (instr & 0x1f0000) >> 16;
kl_get_block(K, func_addr + ((i - 1) * 4), 4,
&last_instr, "instr");
if (((last_instr & 0x1f0000) >> 16) == rt) {
off = (short)(last_instr & 0xffff);
return(abs(off));
}
}
}
}
}
else {
if ((instr & 0xffff0000) == 0x27bd0000) {
off = (short)(instr & 0xffff);
return(abs(off));
}
}
}
return(0);
}
/*
* get_ra_offset()
*
* Walk through the instructions in a function looking for the
* instruction that stores the return address. If the instruction is
* a store word (sw), return the ra offset. if the instruction is a
* store double (sd), return the ra_offset + 4 (even though icrash
* looks at 64 bit kernels, it lives in a 32 bit world).
*
* Note that it is possible for part of a function to use the
* exception frame/SP from the calling function and part that
* allocates space in the stack. We have to be careful not to
* go beyond addr when determining this. To search the entire
* function for ra_offset regardless of PC, flag must be non-zero.
*/
int
get_ra_offset(kaddr_t addr, int flag)
{
int i, size;
uint instr;
kaddr_t func_addr;
kl_reset_error();
func_addr = get_funcaddr(addr);
if (KL_ERROR) {
KL_SET_ERROR_NVAL(E_NO_RA, addr, 2);
return(-1);
}
size = get_funcsize(addr);
if (KL_ERROR) {
/* This should never really happen, but just in case...
*/
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "get_ra_offset: func_addr = 0x%llx, size=%d\n",
func_addr, size);
}
size = 100;
}
else {
/* Convert size to number of instructions. If flag is non-zero,
* use size returned by get_funcsize() regardless of PC value.
* Otherwise, adjust size to go only upto and including PC.
*/
if (flag == 0) {
size = (addr - func_addr);
}
size = size / 4;
}
for(i = 0; i < size; i++) {
kl_get_block(K, (func_addr + (i * 4)), 4, &instr, "instr");
if (PTRSZ64(K)) {
if ((instr & 0xffff0000) == 0xffbf0000) {
return(instr & 0xffff);
}
}
else {
if ((instr & 0xffff0000) == 0xafbf0000) {
return(instr & 0xffff);
}
else if ((instr & 0xffff0000) == 0xffbf0000) {
return((instr & 0xffff) + 4);
}
}
}
KL_SET_ERROR_NVAL(KLE_BAD_FUNCADDR, addr, 2);
return(-1);
}
/*
* find_functions()
*
* Search through a block of code looking for instructions that
* decrement the stack pointer. Between an instruction that
* decrements a stack frame pointer and the next such function,
* we will consider a "function." We have to do this because
* many loadable module functions do not contain a symbol table
* entry.
*/
function_rec_t *
find_functions(kaddr_t start_addr, int bytes)
{
int i, frame_size, off, size;
uint instr;
kaddr_t addr;
function_rec_t *func_list, *frp;
size = (bytes / 4) + 1;
addr = start_addr;
frp = func_list = (function_rec_t*)NULL;
for(i = 0; i < size; i++) {
kl_get_block(K, (addr + (i * 4)), 4, &instr, "instr");
if (PTRSZ64(K)) {
int op;
if (op = (instr & 0xfc000000) >> 26) {
off = 0;
if ((op == 25) && (((instr & 0x3e00000) >> 21) == 29) &&
(((instr & 0x1f0000) >> 16) == 29)) {
off = (short)(instr & 0xffff);
}
/* Check to see if the offset is less than zero. If
* it is then it means that we are at the start of
* a new function (or block of text?) If the offset
* is greater than zero, it may mean that we have
* reached the end of the function. We need to check
* and see if we are looking at the last instruction
* in the sequence.
*/
if (off < 0) {
if (frp) {
/* Finish off the current function
*/
frp->func_size = (addr + (i * 4)) - frp->lowpc;
frp->next = (function_rec_t*)
alloc_block(sizeof(function_rec_t), B_TEMP);
frp = frp->next;
}
else {
func_list = (function_rec_t*)
alloc_block(sizeof(function_rec_t), B_TEMP);
frp = func_list;
}
frp->lowpc = addr + (i * 4);
frp->frame_size = abs(off);
}
else if (off > 0) {
if (i == (size - 1)) {
/* We are at the end of the instruction
* block. We can figure that we are at
* the end of the current function.
*
* XXX - not sure how to handle the case
* where the incrementing of the stack pointer
* occurs before the last instruction in
* the block.
*/
}
}
}
}
else {
if ((instr & 0xffff0000) == 0x27bd0000) {
off = (short)(instr & 0xffff);
}
}
}
return(func_list);
}
/*
* is_exception()
*/
int
is_exception(char *funcname)
{
if (!strncmp(funcname, "VEC", 3) ||
!strcmp(funcname, "systrap") ||
!strcmp(funcname, "tfp_special_int") ||
!strcmp(funcname, "exception_ip12") ||
!strncmp(funcname, "elocore_exl", 11)) {
return(1);
}
return(0);
}
#define RETURN_TRACE(trace, flags) \
if (flags & (C_ALL|C_FULL)) { \
return (trace->nframes); \
} \
else { \
return(0); \
}
/*
* find_trace()
*
* Given a starting pc (spc), starting stack pointer (ssp), and
* stack page address, check to see if a valid trace is possible. A
* trace is considered valid if no errors are encountered (bad PC,
* bad SP, etc.) Certain errors are tolorated however. For example,
* if the current stack frame is an exception frame (e.g., VEC_*),
* go ahead and return success -- even if PC and SP obtained from
* the exception frame are bad (a partial trace is better than no
* trace)..
*
* Return zero if no valid trace was found. Otherwise, return the
* number of frames found. If the C_ALL flag is set, return the
* number of good frames that were found.
*
* Parameters:
*
* spc starting program counter
* ssp starting stack pointer
* check_pc if non-NULL, check to see if check_pc/check_sp
* check_sp are a sub-trace of trace beginning with spc/ssp
* trace structure containing all trace related info (frames,
* pages, page/frame counts, etc.
* flags
*/
int
find_trace(kaddr_t spc, kaddr_t ssp, kaddr_t check_pc, kaddr_t check_sp,
trace_t *trace, int flags)
{
int i, j, line_no, frame_size, ra_offset;
int curstkidx, exception = 0;
kaddr_t s, sp, pc, ra, saddr, upage, func_addr, curkthread;
uint *sbp, *asp;
char *srcfile, *funcname;
sframe_t *curframe;
if (DEBUG(DC_TRACE, 1) || DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "find_trace: spc=0x%llx, ssp=0x%llx\n"
" check_pc=0x%llx, check_sp=0x%llx\n",
spc, ssp, check_pc, check_sp);
fprintf(KL_ERRORFP, " trace=0x%x, flags=0x%x\n",
trace, flags);
}
pc = spc;
sp = ssp;
curstkidx = stack_index(sp, trace);
if (KL_ERROR) {
return(0);
}
sbp = trace->stack[curstkidx].ptr;
saddr = trace->stack[curstkidx].addr;
/* Build the trace
*/
while(pc) {
/* Make sure that we have the correct stack. It's possible that
* an exception frame in one stack contains an sp from another
* stack.
*/
if (trace->stack[curstkidx].addr != saddr) {
sbp = trace->stack[curstkidx].ptr;
saddr = trace->stack[curstkidx].addr;
asp = (uint*)((uint)sbp + ((sp - trace->stack[curstkidx].addr)));
}
/* If the current stack is the bootstack and this is the
* last frame, then just return (it's possible for the pc
* to be non-null).
*/
if ((saddr == trace->bootstack) &&
(sp == kl_kaddr(K, trace->pdap, "pda_s", "p_bootlastframe"))) {
return(trace->nframes);
}
/* Allocate space for a stack frame rec
*/
curframe = alloc_sframe(trace);
/* Get function start address (and make sure pc is valid text).
*/
func_addr = get_funcaddr(pc);
if (KL_ERROR || (pc & 3)) {
/* We have to reset the global error structure or we
* wont be able to print out any trace fragments.
*/
kl_reset_error();
curframe->error = KLE_BAD_PC;
UPDATE_FRAME(0, pc, 0, 0, 0, 0, 0, 0);
RETURN_TRACE(trace, flags);
}
/* Check to see if check_pc/check_sp points to a sub-trace
* of spc/ssp. If it does then don't return a trace (unless C_ALL).
* Make sure we free the curframe block since we wont be linking
* it in to the trace rec.
*/
if (check_pc && ((pc == check_pc) && (sp == check_sp))) {
free_block((k_ptr_t)curframe);
RETURN_TRACE(trace, flags);
}
/* Determine source filename, funcname and line number,
* stackframe size and RA offset.
*/
funcname = get_funcname(pc);
if (!KL_ERROR) {
srcfile = get_srcfile(pc);
line_no = get_lineno(pc);
if (frame_size = get_frame_size(pc, 0)) {
/* Get the offset in the stack frame to the return address.
* If there isn't one, most likely it is because the function
* does not make a call to any other function (the RA doesn't
* have to be saved). There's not much we can do, so just
* return an error (this should only happen on the first
* level of a trace).
*/
if ((ra_offset = get_ra_offset(pc, 0)) == -1) {
free_block((k_ptr_t)curframe);
RETURN_TRACE(trace, flags);
}
}
if (DEBUG(DC_TRACE, 2)) {
int funcsize;
funcsize = get_funcsize(func_addr);
fprintf(KL_ERRORFP, "find_trace: funcname=%s, line_no=%d, "
"funcsize=%d\n", funcname, line_no, funcsize);
fprintf(KL_ERRORFP, " frame_size=%d, ra_offset=%d\n",
frame_size, ra_offset);
}
/* Determine the start address of the stack which SP is from
*/
s = stack_addr(sp, trace);
if (KL_ERROR) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "\nfind_trace: SP 0x%llx is not "
"valid\n", sp);
}
return(0);
}
/* Now check to make sure it is part of the current stack
* segment.
*/
if (s == trace->stack[curstkidx].addr) {
asp = (uint*)((uint)sbp +
((sp - trace->stack[curstkidx].addr)));
}
else {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "\nfind_trace: SP 0x%llx is not from "
"stack 0x%llx\n", sp, trace->stack[curstkidx].addr);
}
KL_SET_ERROR_NVAL(KLE_BAD_SP, s, 2);
return(0);
}
if (DEBUG(DC_TRACE, 2)) {
fprintf(KL_ERRORFP, "->find_trace: %s: pc=0x%llx, sp=0x%llx, "
"asp=0x%x (0x%llx)\n",
funcname, pc, sp, asp, *(kaddr_t*)asp);
}
/* Check to see if the current stack function is an
* exception. If it is, make an attempt to locate the
* exception frame (on the current stack, a new stack,
* or in the ublock.
*/
if (exception = is_exception(funcname)) {
UPDATE_FRAME(funcname, pc, ra, sp, asp,
srcfile, line_no, frame_size);
/* Try and locate an exception frame. If the eframe
* is on another stack, the new stack page will be
* mapped into trace_rec and curframe will be adjusted.
*/
if (find_eframe(trace, flags)) {
return(trace->nframes);
}
/* Check to see if the exception frame is in the ublock.
* If it is, just return (we're at the end of the trace).
*/
if (curframe->ep == trace->u_eframe) {
return(trace->nframes);
}
/* Set the actual stack pointer (asp), real stack pointer
* current stack page, etc from curframe (in case any of
* them have changed).
*/
curstkidx = curframe->ptr;
asp = curframe->eframe;
ssp = curframe->ep;
sbp = trace->stack[curstkidx].ptr;
saddr = trace->stack[curstkidx].addr;
/* Get the PC, RA, and SP from the exception frame.
*/
pc = *(kaddr_t*)((uint)asp + (FIELD("eframe_s", "ef_epc")));
ra = *(kaddr_t*)((uint)asp + (FIELD("eframe_s", "ef_ra")));
sp = *(kaddr_t*)((uint)asp + (FIELD("eframe_s", "ef_sp")));
if (PTRSZ32(K)) {
pc &= 0xffffffff;
ra &= 0xffffffff;
sp &= 0xffffffff;
}
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "->find_trace: asp=0x%x, ssp=0x%llx, "
"curstkidx=%d\n", asp, ssp, curstkidx);
fprintf(KL_ERRORFP, " pc=0x%llx, ra=0x%llx, "
"sp=0x%llx\n", pc, ra, sp);
}
/* make sure sp is within current stack page
*/
if ((sp < ssp) || (sp >= (trace->stack[curstkidx].addr +
trace->stack[curstkidx].size))) {
/* Make sure the stack pointer is from some other
* stack already in the trace record (e.g., the
* bootstack). If it is, then continue on (spage,
* ssp, etc. will be adjusted at the top of the
* loop).
*/
if ((curstkidx = stack_index(sp, trace)) == -1) {
curframe = alloc_sframe(trace);
curframe->error = KLE_BAD_SP;
UPDATE_FRAME(0, 0, 0, sp, 0, 0, 0, 0);
/* Return success even though we have a bad SP. It's
* possible that defkthread was not set properly and
* this is really a good trace.
*/
return(trace->nframes);
}
}
/* make sure pc is valid text
*/
if (!IS_TEXT(pc) || (pc & 3)) {
pc = ra - 8;
/* make sure the new PC is valid
*/
if (!IS_TEXT(pc) || (pc & 3)) {
curframe = alloc_sframe(trace);
curframe->error = KLE_BAD_PC;
UPDATE_FRAME(0, pc, 0, 0, 0, 0, 0, 0);
/* Return success even though we have a bad PC.
* It's possible that defkthread was not set
* properly and this is really a good trace.
*/
return(trace->nframes);
}
}
else {
/* If current function doesn't have a stack frame,
* or if the RA is not saved in the exception frame,
* capture an sframe_rec for the function and then
* go on, using the RA to generate the next PC.
*/
frame_size = get_frame_size(pc, 0);
ra_offset = get_ra_offset(pc, 0);
if (!frame_size || (ra_offset == -1)) {
/* Determine the funcname, source file and line
* number for pc
*/
funcname = get_funcname(pc);
srcfile = get_srcfile(pc);
line_no = get_lineno(pc);
if (DEBUG(DC_TRACE, 2)) {
fprintf(KL_ERRORFP, "find_trace: sp=0x%llx, "
"pc=0x%llx, ra=0x%llx, frame_size=%d\n",
sp, pc, ra, frame_size);
}
curframe = alloc_sframe(trace);
UPDATE_FRAME(funcname, pc, ra, sp, asp,
srcfile, line_no, 0);
pc = ra - 8;
sp = sp + frame_size;
}
}
exception = 0;
}
else {
if (frame_size) {
/* Get the RA from the frame
*/
ra = kl_kaddr_val(K, (k_ptr_t)((uint)asp + ra_offset));
UPDATE_FRAME(funcname, pc, ra, sp, asp,
srcfile, line_no, frame_size);
if (ra) {
sp += frame_size;
pc = ra - 8;
}
else {
pc = 0;
}
}
else {
ra = 0;
UPDATE_FRAME(funcname, pc, ra, sp, asp,
srcfile, line_no, frame_size);
pc = 0;
}
}
}
else {
curframe->error = KLE_BAD_PC;
UPDATE_FRAME(0, pc, 0, 0, 0, 0, 0, 0);
RETURN_TRACE(trace, flags);
}
}
return(trace->nframes);
}
/*
* find_trace2() -- Try and find a valid trace using pc, ra, sp, and saddr
*
* This routine is particularily usefule when pc is from a LEAF
* function. In such cases, special handling is necessary to
* generate a comlete backtrace. This routine is primarily for
* genereting a backtrace from an exception frame or from nmi
* register dumps. This routine does some testing of pc and then
* hands the values off to find_trace(), which does the real work.
* Unlike with find_trace(), find_trace2() returns zero (0) on
* success and one (1) if an error occurs.
*/
int
find_trace2(kaddr_t pc, kaddr_t ra, kaddr_t sp, kaddr_t saddr,
trace_t *trace, int flags)
{
int type, line_no, curstkidx = -1, frame_size = 0, ra_offset = 0;
char *srcfile, *funcname;
uint *asp;
sframe_t *curframe;
if (DEBUG(DC_TRACE, 1) || DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "find_trace2: pc=0x%llx, ra=0x%llx, sp=0x%llx, "
"saddr=0x%llx\n", pc, ra, sp, saddr);
fprintf(KL_ERRORFP, " trace=0x%x, flags=0x%x\n",
trace, flags);
}
/* Make sure that sp and spage is valid and that SP is from that stack.
*/
if (!(type = stack_type(saddr, trace))) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "find_trace2: 0x%llx is not a valid stack.",
saddr);
}
return(1);
}
if (type != stack_type(sp, trace)) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "find_trace2: sp 0x%llx is not from stack "
"0x%llx\n", sp, saddr);
}
KL_SET_ERROR_NVAL(KLE_BAD_SP, sp, 2);
return(1);
}
curstkidx = stack_index(saddr, trace);
if (KL_ERROR) {
return(1);
}
/* Check to see if PC is valid text and from a LEAF function or
* there is no saved RA in the stack frame (which would be the case
* if the function makes no calls to other functions). If it is,
* capture information about the stack frame and then use RA to
* compute the new PC.
*/
if (frame_size = get_frame_size(pc, 0)) {
ra_offset = get_ra_offset(pc, 0);
asp = (uint*)((uint)trace->stack[curstkidx].ptr +
(sp - trace->stack[curstkidx].addr));
}
else {
asp = (uint *)NULL;
}
if (IS_TEXT(pc) && (!frame_size || (ra_offset == -1))) {
/* Determine the funcname, source file and line number for PC
*/
funcname = get_funcname(pc);
srcfile = get_srcfile(pc);
line_no = get_lineno(pc);
/* Capture stackframe info for PC then use RA to compute new PC
*/
curframe = alloc_sframe(trace);
UPDATE_FRAME(funcname, pc, ra, sp, asp, srcfile, line_no, frame_size);
pc = ra - 8;
if (frame_size) {
sp += frame_size;
}
}
else if (!IS_TEXT(pc) || (pc & 3)) {
kaddr_t newpc;
/* If the PC is bad, try using the RA to compute a PC. If both
* PC and RA are invalid then just return.
*/
newpc = ra - 8;
if (!IS_TEXT(newpc) || (newpc & 3) || get_frame_size(newpc, 0)) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "Could not dump trace. Both PC and RA are "
"invalid! PC=0x%llx, RA=0x%llx\n", pc, ra);
}
KL_SET_ERROR_NVAL(KLE_BAD_PC, newpc, 2);
return(1);
}
pc = newpc;
}
if (find_trace(pc, sp, (kaddr_t)0, (kaddr_t)0, trace, (flags | C_ALL))) {
return(0);
}
return(1);
}
/*
* is_eframe()
*
* Return 1 if eframe appears to be valid. Return 0 if it is not valid.
*/
int
is_eframe(kaddr_t ef, trace_t *trace)
{
k_ptr_t efp;
kaddr_t pc, ra, sp, saddr;
/* Get the address of the stack the ef is from
*/
saddr = stack_addr(ef, trace);
if (KL_ERROR) {
return(0);
}
efp = alloc_block(EFRAME_S_SIZE(K), B_TEMP);
kl_get_block(K, ef, EFRAME_S_SIZE(K), efp, "eframe_s");
if (KL_ERROR) {
KL_ERROR |= KLE_BAD_EFRAME_S;
return(0);
}
/* Get the PC, RA and SP from the exception frame.
*/
sp = *(kaddr_t*)((uint)efp + (FIELD("eframe_s", "ef_sp")));
pc = *(kaddr_t*)((uint)efp + (FIELD("eframe_s", "ef_epc")));
ra = *(kaddr_t*)((uint)efp + (FIELD("eframe_s", "ef_ra")));
if (PTRSZ32(K)) {
pc &= 0xffffffff;
ra &= 0xffffffff;
sp &= 0xffffffff;
}
free_block(efp);
/* Check to see if the SP, PC, or RA are NULL. If any one of them
* is, then it most likely means that efp is not a valid eframe
* pointer.
*/
if (!pc || !ra || !sp) {
return(0);
}
if (stack_addr(sp, trace) != saddr) {
return(0);
}
return(1);
}
/*
* find_eframe()
*
* locate the pointer to the eframe for current frame level. Check
* and see if the current stack pointer points to an exception frame
* (which would be the a0 value saved by the next function). If it
* does, do the following:
*
* o Check to see if the eframe pointer is the current SP. If it is,
* update the current sframe rec and return.
*
* o Otherwise, if the current stack is the interrupt stack, check
* to see if the eframe pointer is from the kernel stack. If it
* is, update the eframe record and load the kernel stack page.
* Then return.
*
* o If the eframe pointer is located in the ublock, update the
* eframe rec and return.
*
* If the current stack pointer does NOT point to an exception frame
* It is NULL or is not a valid kernel address, or does not belong
* in the current or interrupt stacks or is not in the ublock) do a
* brute force in the subsequent stack frames -- looking for a
* pointer that will work.
*/
int
find_eframe(trace_t *trace, int flags)
{
int i, size, intstksz, found, ktstkidx = -1;
int eflag = -1, curstkflg = -1, curstack = -1;
uint *sbp, *asp, *s;
kaddr_t ep = 0, sp, upage, saddr, lastframe, ktstack;
sframe_t *curframe;
/* Initialize some variables
*/
curframe = trace->frame->prev;
asp = curframe->asp;
curstack = curframe->ptr;
saddr = trace->stack[curstack].addr;
/* Determine which stack we currently are on (KERNELSTACK/KEXTSTACK,
* INTSTACK, STHREADSTACK, XTHREADSTACK, or UNKNOWN_STACK). The only
* time the current stack should be UNKNOWN_STACK is when an strace
* is done an address that is NOT an interrupt or bootstack for a
* CPU (for example when someone converts the kernelstack address
* into a K0 address).
*/
if (!(curstkflg = stack_type(saddr, trace))) {
return(1);
}
switch (curstkflg) {
case KEXTSTACK :
case KERNELSTACK :
lastframe = K_KERNELSTACK(K) + (K_PAGESZ(K) - 144);
break;
case INTSTACK :
lastframe = kl_kaddr(K, trace->pdap, "pda_s", "p_intlastframe");
break;
case BOOTSTACK :
lastframe = kl_kaddr(K, trace->pdap, "pda_s", "p_bootlastframe");
break;
#ifdef ANON_ITHREADS
case ITHREADSTACK :
lastframe = trace->stack[curstack].addr +
trace->stack[curstack].size - 352;
break;
#endif
case XTHREADSTACK :
case STHREADSTACK :
lastframe = 0;
break;
case RAW_STACK :
KL_SET_ERROR(KLE_NOT_IMPLEMENTED);
return(1);
}
#ifdef ANON_ITHREADS
/* If the current thread is an ithread, check to see if we are
* at the lastframe in the stack. If we are, then we should
* just return no eframe found (with no error condition set).
* The exception to this is when the ithread's k_pinned field
* points to a kthread with a valid eframe pointer in k_eframe.
* In that case only we need to make the jump to the "pinned"
* kthread and continue the backtrace.
*/
if (curstkflg == ITHREADSTACK) {
if (trace->frame->prev->sp == lastframe) {
kaddr_t pinned_kthread;
if (trace->flags & TF_KTHREAD2_VALID) {
/* XXX - we have to deal with the possibility of multiple
* nested ithreads! For now, we'll only walk one extra
* stack level...
*/
return(1);
}
pinned_kthread = kl_kaddr(K, trace->ktp, "kthread", "k_pinned");
if (pinned_kthread) {
int stktyp;
k_ptr_t pktp;
kstruct_t *ksp;
pktp = kl_get_kthread(K, pinned_kthread, 0);
if (KL_ERROR) {
return(1);
}
if (ep = kl_kaddr(K, pktp, "kthread", "k_eframe")) {
/* It gets tricky here. We have to add a new stack
* to the trace_rec. It's possible that this would
* cause us to have more than one ITHREADSTACK mapped
* in! We have to be careful that we don't try and
* grab the wrong one later on...
*/
ksp = (kstruct_t*)alloc_block(sizeof(kstruct_t), B_TEMP);
ksp->addr = pinned_kthread;
ksp->ptr = pktp;
setup_trace_rec((kaddr_t)0, ksp, KTHREAD2_FLAG, trace);
if (KL_ERROR) {
return(1);
}
free_block((k_ptr_t)ksp);
/* Set the ep address and the eframe pointer in the
* current sframe record.
*/
curstack = stack_index(ep, trace);
/* We have to make sure that ep is actually from one
* of the stacks we have mapped into the trace_rec.
* It's possible that ep is actually the same as
* u_eframe. In other words, the uthread was
* interrupted while executing application code.
* Or, if some other error occurred, we can't go
* on from here anyway, so...
*/
if (curstack == -1) {
/* Check to see if ep and trace->u_eframe are
* the same.
*/
if (ep == trace->u_eframe) {
/* Make sure the kthread is of type KT_UTHREAD
*/
if (!IS_UTHREAD(K, trace->ktp2)) {
KL_SET_ERROR(KLE_KTHREAD_TYPE(K, trace->ktp2));
curframe->error =
KLE_KTHREAD_TYPE(K, trace->ktp2);
return(1);
}
/* Point eframe to u_eframe in exception struct
*/
curframe->ep = trace->u_eframe;
curframe->eframe = (k_ptr_t)((uint)trace->exp +
FIELD("exception", "u_eframe"));
curframe->flag = TF_KTHREAD2_VALID;
curframe->ra = 0;
return(0);
}
else {
KL_SET_ERROR_NVAL(KLE_BAD_SP, ep, 2);
return(1);
}
}
saddr = trace->stack[curstack].addr;
curframe->eframe =
(uint*)((uint)trace->stack[curstack].ptr +
(ep - saddr));
curframe->ep = ep;
curframe->ptr = curstack;
curframe->flag = TF_KTHREAD2_VALID;
return(0);
}
else {
/* Like above, we have to return 1 to terminate the
* backtrace (there is no error).
*/
return(1);
}
}
else {
/* Note that we have to return 1 to terminate the backtrace.
* However, we don't set any error message (there isn't any
* error) so the trace will look just fine.
*/
return(1);
}
}
}
else if (curstkflg == INTSTACK) {
#else
if (curstkflg == INTSTACK) {
#endif
/* If we are at the end of the INTSTACK, then we have to
* see if we should switch to some other stack and continue
* our backtrace. It depends on weather or not a kthread was
* running on this CPU (we ARE on the INTSTACK).
*/
if (trace->frame->prev->sp == lastframe) {
if (trace->kthread) {
/* We need to see if there is an eframe pointer in the
* running thread's kthread struct. If there is, then
* use that as the efrme pointer (we will have to
* switch stacks too).
*/
if (ep = kl_kaddr(K, trace->ktp, "kthread", "k_eframe")) {
/* Set the ep address and the eframe pointer in the
* current sframe record.
*/
curstack = stack_index(ep, trace);
saddr = trace->stack[curstack].addr;
curframe->eframe =
(uint*)((uint)trace->stack[curstack].ptr +
(ep - saddr));
curframe->ep = ep;
curframe->ptr = curstack;
return(0);
}
else {
/* If this is a uthread, point to u_eframe in
* exception struct.
*/
if (curframe->ep = trace->u_eframe) {
curframe->eframe = (k_ptr_t)((uint)trace->exp +
FIELD("exception", "u_eframe"));
curframe->ra = 0;
return(0);
}
else {
/* Return 1 to terminate the backtrace (there
* wasn't any error).
*/
return(1);
}
}
}
else {
/* Like above, we have to return 1 to terminate the
* backtrace (there is no error).
*/
return(1);
}
}
}
/* If we don't know what the current stack page is, we really can't
* determine where the eframe pointer is. We need to mark curframe
* with an error and return.
*/
if (curstkflg == UNKNOWN_STACK) {
KL_SET_ERROR(E_NO_EFRAME);
return(1);
}
/* First we need to check to see if the current stack pointer
* points to the exception frame.
*/
sp = kl_reg(K, asp, "eframe_s", "ef_sp");
if (PTRSZ32(K)) {
sp = (sp & 0xffffffff);
}
if (sp == (kaddr_t)(curframe->sp + EFRAME_S_SIZE(K))) {
curframe->eframe = (k_ptr_t)asp;
curframe->ep = curframe->sp;
return(0);
}
/* Check to see if the current stack pointer points to the pointer
* to the exception frame. It's necessary to perform some sanity
* checking as it's entirly possible for ep to be non-zero yet not
* a valid eframe pointer.
*/
ep = kl_kaddr_val(K, asp);
if ((ep && (ep > (K_KUBASE(K) + K_KUSIZE(K)))) ||
!strcmp(curframe->funcname, "systrap") ||
!strcmp(curframe->funcname, "tfp_special_int")) {
if (ep == curframe->sp) {
/* The current stack pointer is a pointer to itself. Normally,
* this means the current SP is the start of an exception frame.
* Double check to make sure this is really the case.
*/
sp = kl_reg(K, asp, "eframe_s", "ef_sp");
if (PTRSZ32(K)) {
sp = (sp & 0xffffffff);
}
if (sp == (kaddr_t)(curframe->sp + EFRAME_S_SIZE(K))) {
curframe->eframe = (k_ptr_t)asp;
return(0);
}
else {
ep = 0;
}
}
else if ((ep < K_KERNELSTACK(K)) &&
(ep >= (K_KERNELSTACK(K) - K_PAGESZ(K)))) {
/* eframe pointer is is in kernelstack
*/
if ((curstkflg == INTSTACK) && (curframe->sp == lastframe)) {
eflag = KERNELSTACK;
}
else {
ep = 0;
}
}
else if ((ep == trace->u_eframe) ||
!strcmp(curframe->funcname, "systrap") ||
!strcmp(curframe->funcname, "tfp_special_int")) {
/* The eframe is in the ublock
*/
if (curstkflg == BOOTSTACK) {
ep = 0;
}
else if (curframe->sp != lastframe) {
ep = 0;
}
else {
eflag = U_EFRAME;
if (!ep) {
ep = trace->u_eframe;
}
}
}
else if (trace->pdap) {
if ((ep >= trace->intstack) &&
(ep < (trace->intstack + K_PAGESZ(K)))) {
eflag = INTSTACK;
}
else if ((ep >= trace->bootstack) &&
(ep < (trace->bootstack + K_PAGESZ(K)))) {
eflag = BOOTSTACK;
}
else {
ep = 0;
}
}
else {
/* This isn't an exception frame, set ep equal to zero to
* force a brute force search.
*/
ep = 0;
}
}
else {
ep = 0;
}
/* If the exception frame pointer was not found, use a brute force
* approach to look for one in the next stack frame. Perform some
* sanity checks on any potentially valid addresses found to ensure
* that an exception frame is actually there.
*/
if (!ep) {
s = curframe->prev->asp;
size = curframe->prev->frame_size / K_NBPW(K);
for (i = 0; i < size; i++) {
ep = kl_kaddr_val(K, s);
if (ep == trace->u_eframe) {
/* The address is for the eframe in the ublock
*/
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "find_eframe: EP 0x%llx is in the "
"ublock\n", ep);
}
if (curstkflg == BOOTSTACK) {
ep = 0;
}
else if (curframe->sp != lastframe) {
ep = 0;
}
else {
eflag = U_EFRAME;
break;
}
}
else if (trace->ktp &&
(IS_UTHREAD(K, trace->ktp) && IS_KERNELSTACK(K, ep))) {
kaddr_t kstkpg;
/* The address is from the kernel stack
*/
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "find_eframe: EP 0x%llx is from the"
"kernelstack\n", ep);
}
/* Get the index for the kernelstack page for the current
* process (if there is one -- defkthread if there isn't).
*/
kstkpg = stack_index(K_KERNELSTACK(K), trace);
if (KL_ERROR) {
curframe->error = KLE_BAD_SADDR;
return(1);
}
if (curstkflg == BOOTSTACK) {
ep = 0;
}
else if (curstkflg == INTSTACK) {
if (curframe->sp != lastframe) {
ep = 0;
}
}
else if (curstkflg == KERNELSTACK) {
k_ptr_t eptr;
if (curframe->sp == lastframe) {
ep = 0;
}
else {
eptr = (k_ptr_t*)((uint)trace->stack[kstkpg].ptr +
(ep - K_KERNELSTACK(K)));
if (kl_reg(K, eptr, "eframe_s", "ef_sp") !=
(kaddr_t)(ep + EFRAME_S_SIZE(K))) {
ep = 0;
}
}
}
if (ep) {
is_eframe(ep, trace);
if (KL_ERROR) {
curframe->error = KLE_BAD_EFRAME_S;
}
curstack = kstkpg;
eflag = KERNELSTACK;
break;
}
}
else if (trace->ktp && !IS_UTHREAD(K, trace->ktp)) {
/* This is not a process (e.g., no u_area). Check to see
* if ep is from the kthread stack (NOT the kernelstack).
* First, check to see if the kthread stack is mapped in
* the trace struct.
*/
if (ktstkidx == -1) {
for (i = 0; i < trace->stackcnt; i++) {
#ifdef ANON_ITHREADS
if ((trace->stack[i].type == ITHREADSTACK) ||
(trace->stack[i].type == XTHREADSTACK) ||
(trace->stack[i].type == STHREADSTACK)) {
#else
if ((trace->stack[i].type == XTHREADSTACK) ||
(trace->stack[i].type == STHREADSTACK)) {
#endif
ktstkidx = i;
ktstack = trace->stack[i].addr;
break;
}
}
if (i == trace->stackcnt) {
/* We didn't find it, return an error
*/
KL_SET_ERROR(KLE_BAD_KERNELSTACK);
return(1);
}
}
/* Now, check to see if ep is on the kthread stack
*/
if ((ep >= ktstack) &&
(ep < (ktstack + trace->stack[ktstkidx].size))) {
if (curstkflg == BOOTSTACK) {
ep = 0;
}
else if (curstkflg == INTSTACK) {
if (curframe->sp != lastframe) {
ep = 0;
}
}
}
else {
ep = 0;
}
if (ep) {
curstack = ktstkidx;
if (IS_XTHREAD(K, trace->ktp)) {
eflag = XTHREADSTACK;
}
#ifdef ANON_ITHREADS
else if (IS_ITHREAD(K, trace->ktp)) {
eflag = ITHREADSTACK;
}
#endif
else if (IS_STHREAD(K, trace->ktp)) {
eflag = STHREADSTACK;
}
break;
}
}
else if (trace->pdap) {
if ((ep >= trace->intstack) &&
(ep < (trace->intstack + stack_size(INTSTACK, trace)))) {
/* Address is from the CPU intstack
*/
if (curstkflg == INTSTACK) {
eflag = INTSTACK;
break;
}
else {
ep = 0;
}
}
else if ((ep >= trace->bootstack) &&
(ep < (trace->bootstack + stack_size(BOOTSTACK, trace)))) {
/* If address is from the CPU bootstack
*/
if (curstkflg == INTSTACK) {
eflag = BOOTSTACK;
break;
}
else {
ep = 0;
}
}
else {
ep = 0;
}
}
else if ((ep >= trace->stack[curstack].addr) &&
(ep < (trace->stack[curstack].addr +
trace->stack[curstack].size))) {
/* The address is from the current stack page
*/
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "find_eframe: EP 0x%llx is from the "
"current stack page (0x%llx)\n",
ep, trace->stack[curstack].addr);
}
eflag = UNKNOWN_STACK;
break;
}
s += (K_NBPW(K) / 4);
}
if (!ep) {
/* If we didn't find a potential eframe address, just
* assume that it's in the current stack and forge ahead.
* Before we do that though, we should check to make sure
* that asp is eight-byte aligned (register values are
* all eight bytes in length -- this prevents a SIGBUS
* later on).
*/
if ((uint)asp % 8) {
curframe->error = KLE_BAD_EFRAME_S;
return(1);
}
curframe->eframe = (k_ptr_t)asp;
curframe->ep = curframe->sp;
return(0);
}
}
switch (eflag) {
case U_EFRAME: /* eframe in ublock */
/* Make sure the kthread is of type KT_UTHREAD
*/
if (!IS_UTHREAD(K, trace->ktp)) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "find_eframe: No process set!\n");
}
KL_SET_ERROR(KLE_KTHREAD_TYPE(K, trace->ktp));
curframe->error = KLE_KTHREAD_TYPE(K, trace->ktp);
return(1);
}
/* Point eframe to u_eframe in exception struct
*/
curframe->ep = trace->u_eframe;
curframe->eframe = (k_ptr_t)((uint)trace->exp +
FIELD("exception", "u_eframe"));
curframe->ra = 0;
return(0);
case KERNELSTACK: /* eframe is on kernelstack */
curstack = stack_index(K_KERNELSTACK(K), trace);
if (KL_ERROR) {
curframe->error = KLE_BAD_SADDR;
return(1);
}
break;
case INTSTACK: /* eframe is on interrupt stack */
curstack = stack_index(trace->intstack, trace);
if (KL_ERROR) {
curframe->error = KLE_BAD_SADDR;
return(1);
}
break;
case BOOTSTACK: /* eframe is on bootstack */
curstack = stack_index(trace->bootstack, trace);
if (KL_ERROR) {
curframe->error = KLE_BAD_SADDR;
return(1);
}
break;
case XTHREADSTACK: /* kernel xthread stack */
#ifdef ANON_ITHREADS
case ITHREADSTACK: /* kernel ithread stack */
#endif
case STHREADSTACK: /* kernel sthread stack */
break;
default: /* Unknown error */
KL_SET_ERROR(KLE_UNKNOWN_ERROR);
curframe->error = KLE_UNKNOWN_ERROR;
return(1);
}
/* Set the ep address and the eframe pointer in the current
* sframe record.
*/
saddr = trace->stack[curstack].addr;
curframe->eframe = (uint*)((uint)trace->stack[curstack].ptr + (ep - saddr));
curframe->ep = ep;
curframe->ptr = curstack;
return(0);
}
#ifdef INCLUDE_REGINFO
/*
* check_instr()
*/
int
check_instr(uint instr, sframe_t *fp)
{
int i, rd, rt, rs, sa, base, offset;
if (PTRSZ64(K)) {
switch ((instr & 0xfc000000) >> 26) {
case 0x23:
case 0x37:
/* lw rt, offset(base)
*
* We have to check and see if this instruction modifys
* any of the A-regs or S-regs before their value has
* been saved.
*/
rt = ((instr & 0x1f0000) >> 16);
if ((IS_AREG(rt) || IS_SREG(rt)) &&
(fp->regs[rt].state == REGVAL_UNKNOWN)) {
fp->regs[rt].state = REGVAL_BAD;
}
break;
case 0x3f:
/* sd rt, offset(base)
*/
rt = ((instr & 0x1f0000) >> 16);
base = ((instr & 0x3e00000) >> 21);
offset = (instr & 0xffff);
if ((base == 29) &&
(IS_SREG(rt) || IS_AREG(rt) || (rt == 31)) &&
fp->regs[rt].state == REGVAL_UNKNOWN) {
fp->regs[rt].state = REGVAL_VALID;
kl_get_block(K, (fp->sp + offset), 8,
&fp->regs[rt].value, "reg_value");
/* If this was an A-reg that was saved, check the
* other A-regs to see if one of them had been stuffed
* into this register (just like an S-reg).
*/
if (IS_AREG(rt)) {
for (i = 4; i < 12; i++) {
if (i == rt) {
continue;
}
if ((fp->regs[i].state == REGVAL_SAVEREG) &&
(fp->regs[i].savereg == rt)) {
fp->regs[i].state = REGVAL_VALID;
fp->regs[i].value = fp->regs[rt].value;
break;
}
}
}
}
break;
case 0x0: /* SPECIAL */
/* We now have to determine which special instruction
* this is.
*/
switch (instr & 0x3f) {
case 0x0:
/* sll rd, rt, sa (sll rd, rt, 0)
*
* Save a place holder for these values in the
* current frame. The REAL value will be determined
* later on.
*/
rd = ((instr & 0xf800) >> 11);
rt = ((instr & 0x1f0000) >> 16);
sa = ((instr & 0x7c0) >> 6);
if (!sa && IS_AREG(rt) &&
(fp->regs[rt].state == REGVAL_UNKNOWN)) {
fp->regs[rt].state = REGVAL_SAVEREG;
fp->regs[rt].savereg = rd;
}
else if ((IS_AREG(rd) || IS_SREG(rd)) &&
(fp->regs[rd].state == REGVAL_UNKNOWN)) {
fp->regs[rd].state = REGVAL_BAD;
}
break;
case 0x25:
/* move rd, rs (or rd, rs, 0)
*
* Save a place holder for these values in the
* current frame. The REAL value may be determined
* later on.
*/
rs = ((instr & 0x3e00000) >> 21);
rd = ((instr & 0xf800) >> 11);
if (IS_AREG(rs) &&
(fp->regs[rs].state == REGVAL_UNKNOWN)) {
fp->regs[rs].state = REGVAL_SAVEREG;
fp->regs[rs].savereg = rd;
}
else if ((IS_AREG(rd) || IS_SREG(rd)) &&
(fp->regs[rd].state == REGVAL_UNKNOWN)) {
fp->regs[rd].state = REGVAL_BAD;
}
break;
default:
break;
}
break;
default:
break;
}
}
else {
/* Need to test this out on 32-bit systems
*/
return(-1);
}
return(0);
}
/*
* get_eframe_regs()
*/
void
get_eframe_regs(reg_rec_t *regs, k_ptr_t eframe)
{
/* Save the A-regs
*/
regs[4].state = REGVAL_VALID;
regs[4].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_a0")));
regs[5].state = REGVAL_VALID;
regs[5].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_a1")));
regs[6].state = REGVAL_VALID;
regs[6].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_a2")));
regs[7].state = REGVAL_VALID;
regs[7].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_a3")));
regs[8].state = REGVAL_VALID;
regs[8].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_a4")));
regs[9].state = REGVAL_VALID;
regs[9].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_a5")));
regs[10].state = REGVAL_VALID;
regs[10].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_a6")));
regs[11].state = REGVAL_VALID;
regs[11].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_a7")));
/* Save the S-regs
*/
regs[16].state = REGVAL_VALID;
regs[16].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_s0")));
regs[17].state = REGVAL_VALID;
regs[17].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_s1")));
regs[18].state = REGVAL_VALID;
regs[18].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_s2")));
regs[19].state = REGVAL_VALID;
regs[19].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_s3")));
regs[20].state = REGVAL_VALID;
regs[20].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_s4")));
regs[21].state = REGVAL_VALID;
regs[21].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_s5")));
regs[22].state = REGVAL_VALID;
regs[22].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_s6")));
regs[23].state = REGVAL_VALID;
regs[23].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_s7")));
regs[30].state = REGVAL_VALID;
regs[30].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_fp")));
/* Save the RA
*/
regs[31].state = REGVAL_VALID;
regs[31].value = *(kaddr_t*)((uint)eframe + (FIELD("eframe_s", "ef_ra")));
}
/*
* get_machregs()
*/
void
get_machregs(reg_rec_t *regs, uint64 *machregsp)
{
regs[16].value = ((uint64*)machregsp)[0]; /* S0 */
regs[16].state = REGVAL_VALID;
regs[17].value = ((uint64*)machregsp)[1]; /* S1 */
regs[17].state = REGVAL_VALID;
regs[18].value = ((uint64*)machregsp)[1]; /* S2 */
regs[18].state = REGVAL_VALID;
regs[19].value = ((uint64*)machregsp)[3]; /* S3 */
regs[19].state = REGVAL_VALID;
regs[20].value = ((uint64*)machregsp)[4]; /* S4 */
regs[20].state = REGVAL_VALID;
regs[21].value = ((uint64*)machregsp)[5]; /* S5 */
regs[21].state = REGVAL_VALID;
regs[22].value = ((uint64*)machregsp)[6]; /* S6 */
regs[22].state = REGVAL_VALID;
regs[23].value = ((uint64*)machregsp)[7]; /* S7 */
regs[23].state = REGVAL_VALID;
}
/*
* get_reg_values()
*
* Walk through the stack, frame-by-frame, and determine as many
* of the saved register values as possible.
*/
int
get_reg_values(trace_t *trace)
{
int i, size, func_size, areg, sreg;
uint instr;
sframe_t *fp, *tfp1, *tfp2;
k_ptr_t machregsp = 0;
kaddr_t func_addr, value;
kl_reset_error();
/* First thing we have to do is get the starting register values
* if at all possible. If the backtrace begins with an eframe,
* then it's possible to get all register values. Otherwise
* we can only get the values for the S-regs (saved in the
* k_regs field of the kthread, the pda_s struct, or dumpregs).
*/
if (trace->pdap) {
/* This backtrace originates on a CPU. We need to check and
* see if this is the panicking CPU. If it is, then get the
* S-reg values from dumpregs. Otherwise, get them from the
* pda_s struct.
*/
if (kl_uint(K, trace->pdap, "pda_s", "p_panicregs_valid", 0)) {
machregsp = (uint64*)((uint)trace->pdap +
FIELD("pda_s", "p_panicregs_tbl"));
get_machregs(trace->regs, machregsp);
}
else {
get_machregs(trace->regs, (uint64*)K_DUMPREGS(K));
}
}
else if (trace->ktp) {
uint64 *eframe;
if (kl_kaddr(K, trace->ktp, "kthread", "k_eframe")) {
eframe = (uint64*)((uint)trace->ktp + FIELD("kthread", "k_regs"));
get_eframe_regs(trace->regs, eframe);
}
else {
get_machregs(trace->regs, (uint64*)trace->ktp);
}
}
/* Get the last (newest) frame on the stack. For the first pass,
* we are going to walk back up the stack.
*/
fp = trace->frame;
do {
if (fp->eframe) {
get_eframe_regs(fp->regs, fp->eframe);
fp = fp->next;
continue;
}
func_addr = get_funcaddr(fp->pc);
if (KL_ERROR) {
KL_SET_ERROR_NVAL(E_NO_RA, fp->pc, 2);
return(-1);
}
/* Set funcsize so that we only walk the code upto the PC
* for this frame.
*/
func_size = get_funcsize(fp->pc);
if (KL_ERROR) {
/* This should never really happen, but just in case...
*/
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "set_regvalues: func_addr = 0x%llx, "
"size=%d\n", func_addr, size);
}
size = 20;
}
else {
/* Convert size to number of instructions (upto pc + 1 to
* allow for jump delay slot).
*/
size = ((fp->pc - func_addr) / 4) + 1;
if (size > func_size) {
size = func_size / 4;
}
}
for(i = 0; i < size; i++) {
kl_get_block(K, (func_addr + (i * 4)), 4, &instr, "instr");
check_instr(instr, fp);
}
fp = fp->next;
} while (fp != trace->frame);
/* We now have to fill in the S-reg values from the regs array
* in trace for the first (newest) frame in the backtrace. This
* must be done before we begin pass 2 below.
*/
fp = trace->frame;
for (i = 0; i < 32; i++) {
if (!IS_AREG(i) && !IS_SREG(i)) {
continue;
}
if ((fp->regs[i].state == REGVAL_UNKNOWN) &&
(trace->regs[i].state == REGVAL_VALID)) {
fp->regs[i].state = REGVAL_VALID;
fp->regs[i].value = trace->regs[i].value;
}
}
/* Pass 2. Go from the oldest frame to the newest. Look for A-regs
* that have been saved in S-regs. When one is found, walk ahead
* and look for where the S-reg might have been stuffed in a stack
* frame. If we find one, drag the saved value back to the current
* frame to fill in the A-reg value (fill in all S-values between
* there and here has well.
*/
fp = trace->frame->prev;
do {
for (i = 0; i < 32; i++) {
if (fp->regs[i].state == REGVAL_SAVEREG) {
tfp1 = fp->prev;
areg = i;
sreg = fp->regs[i].savereg;
while (tfp1 != trace->frame->prev) {
if (tfp1->regs[sreg].state == REGVAL_VALID) {
value = tfp1->regs[sreg].value;
tfp2 = tfp1->next;
while (tfp2 != fp) {
tfp2->regs[sreg].state = REGVAL_VALID;
tfp2->regs[sreg].value = value;
tfp2 = tfp2->next;
}
fp->regs[areg].state = REGVAL_VALID;
fp->regs[areg].value = value;
break;
}
tfp1 = tfp1->prev;
}
}
}
fp = fp->prev;
} while (fp != trace->frame);
#ifdef NOT
/* Now for the last pass. Walk from the bottom (newest) frame in the
* stack to the top of the stack. Fill in all the S-regs and A-regs
* that haven't been identified so far -- if possible.
*
* XXX - Still working on this...
*/
fp = trace->frame->prev;
do {
for (i = 0; i < 32; i++) {
if (IS_AREG(i) || IS_SREG(i)) {
if (fp->regs[i].state != REGVAL_VALID) {
tfp1 = fp->prev;
areg = i;
sreg = fp->regs[i].savereg;
while (tfp1 != trace->frame) {
if (tfp1->regs[sreg].state == REGVAL_VALID) {
value = tfp1->regs[sreg].value;
tfp2 = tfp1->next;
while (tfp2 != fp) {
tfp2->regs[sreg].state = REGVAL_VALID;
tfp2->regs[sreg].value = value;
tfp2 = tfp2->next;
}
fp->regs[areg].state = REGVAL_VALID;
fp->regs[areg].value = value;
break;
}
tfp1 = tfp1->prev;
}
}
}
}
fp = fp->prev;
} while (fp != trace->frame);
#endif
return(0);
}
/*
* print_reg_values()
*/
void
print_reg_values(sframe_t *fp, FILE *ofp)
{
int i, count;
fprintf(ofp, "\n");
for (i = 0; i < 32; i++) {
switch(fp->regs[i].state) {
case REGVAL_VALID:
fprintf(ofp, "r%d=0x%llx ", i, fp->regs[i].value);
if (fp->regs[i].savereg) {
fprintf(ofp, "(r%d-->r%d)\n", i, fp->regs[i].savereg);
}
else {
fprintf(ofp, "\n");
}
break;
case REGVAL_SAVEREG:
fprintf(ofp, "r%d-->r%d\n", i, fp->regs[i].savereg);
break;
case REGVAL_BAD:
fprintf(ofp, "r%d is BAD!\n", i);
break;
#ifdef NOT
case REGVAL_UNKNOWN:
fprintf(ofp, "r%d is UNKNOWN!\n", i);
break;
#endif
}
count++;
}
fprintf(ofp, "\n");
}
#endif /* INCLUDE_REGINFO */
/*
* cpu_trace()
*/
int
cpu_trace(int cpuid, int flags, FILE *ofp)
{
trace_t *trace;
kstruct_t *ksp;
k_ptr_t pdap;
pdap = alloc_block(PDA_S_SIZE(K), B_TEMP);
kl_get_pda_s(K, (kaddr_t)cpuid, pdap);
if (KL_ERROR) {
free_block(pdap);
return(1);
}
trace = alloc_trace_rec();
ksp = (kstruct_t*)alloc_block(sizeof(kstruct_t), B_TEMP);
ksp->addr = kl_pda_s_addr(K, cpuid);
ksp->ptr = pdap;
setup_trace_rec((kaddr_t)0, ksp, CPU_FLAG, trace);
free_block((k_ptr_t)ksp);
if (KL_ERROR) {
if (!(flags & C_SUPRSERR)) {
print_trace_error(trace, ofp);
}
free_trace_rec(trace);
return(1);
}
if (!ACTIVE(K)) {
/* Even if there was an error, we want to print out the
* portion of the trace we were able to get.
*/
if (get_cpu_trace(cpuid, trace) && !trace->nframes) {
if (!(flags & C_SUPRSERR)) {
print_trace_error(trace, ofp);
}
free_trace_rec(trace);
return(1);
}
print_trace(trace, flags, ofp);
}
else if (!(flags & C_SUPRSERR)) {
fprintf(ofp, "Process is currently running on CPU %d.\n\n", cpuid);
}
free_trace_rec(trace);
return(0);
}
/*
* get_cpu_trace()
*/
int
get_cpu_trace(int cpuid, trace_t *trace)
{
int size, stkflg, panicregs_valid = 0;
kaddr_t curkthread, epc, pc, ra, sp, saddr, saved_kthread = 0;
k_ptr_t machregsp;
kl_reset_error();
/* Make sure this is a core dump. We can't do a CPU trace on
* a live system.
*/
if (ACTIVE(K)) {
KL_SET_ERROR(KLE_ACTIVE);
return(1);
}
/* Based on the current status of the CPU, determine what the
* address of the current stack is.
*/
stkflg = kl_uint(K, trace->pdap, "pda_s", "p_kstackflag", 0);
if (stkflg == KERNELSTACK) {
saddr = kthread_stack(trace, (int *)NULL);
}
else if (stkflg == INTSTACK) {
saddr = trace->intstack;
}
else if (stkflg == BOOTSTACK) {
saddr = trace->bootstack;
}
/* Make sure that defkthread is set to the kthread running on
* cpuid (if there is one).
*/
if (K_DEFKTHREAD(K) != trace->kthread) {
saved_kthread = K_DEFKTHREAD(K);
kl_set_defkthread(K, trace->kthread);
}
/* Check to see if the dump was initiated by an NMI...If it was,
* use kl_NMI_saveregs to get the starting epc, pc, sp, and ra.
*/
if (IS_NMI(K)) {
if (kl_NMI_saveregs(K, cpuid, &epc, &pc, &sp, &ra) == -1) {
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
KL_SET_ERROR(E_NO_TRACE);
return(1);
}
/* Make sure that saddr is set correctly. It's possible that
* there is a bad kthread running on the CPU (because this is
* an NMI dump). Regardless of the current setting of saddr,
* set it again based on the contents of sp.
*/
saddr = stack_addr(sp, trace);
if (!find_trace2(epc, ra, sp, saddr, trace, C_ALL)) {
/* We found a trace
*/
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
return(0);
}
/* No trace was found.
*/
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
KL_SET_ERROR(E_NO_TRACE);
return(1);
}
/* Check to see if p_panicregs_valid in the pda_s struct is non-NULL.
* If it is, use the values stored in p_panicregs_tbl to generate the
* trace. Otherwise, use the values stored in dumpregs. In both cases,
* PC is element 10, SP is element 8.
*/
if (kl_uint(K, trace->pdap, "pda_s", "p_panicregs_valid", 0)) {
machregsp = (k_ptr_t)((uint)trace->pdap +
FIELD("pda_s", "p_panicregs_tbl"));
panicregs_valid = 1;
}
else {
machregsp = K_DUMPREGS(K);
}
pc = ((kaddr_t*)machregsp)[10];
sp = ((kaddr_t*)machregsp)[8];
if (PTRSZ32(K)) {
pc &= 0xffffffff;
sp &= 0xffffffff;
}
/* If sp is from the kernel/kthread stack, make sure that defkthread is
* set. Otherwise, make sure that sp comes from an intstack or bootstack
* AND make sure it is from the one pointed to by trace->pdap. In certain
* rare situations, it's possible that the p_panicregs_tbl will not be
* valid for more than one CPU in a dump. If we don't check, we could use
* dumpregs for the wrong CPU.
*/
if (IS_KERNELSTACK(K, sp) || is_kthreadstack(sp, trace)) {
if (!K_DEFKTHREAD(K)) {
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
KL_SET_ERROR_NVAL(KLE_BAD_SP, sp, 2);
return(1);
}
}
else if (!trace->pdap) {
/* This shouldn'n happen, but just in case...
*/
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
KL_SET_ERROR(E_NO_PDA);
return(1);
}
else {
saddr = 0;
if ((sp >= trace->intstack) &&
(sp < trace->intstack + stack_size(INTSTACK, trace))) {
saddr = trace->intstack;
}
else if ((sp >= trace->bootstack) &&
(sp < trace->bootstack + stack_size(BOOTSTACK, trace))) {
saddr = trace->bootstack;
}
if (!saddr) {
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
if (panicregs_valid) {
KL_SET_ERROR_NVAL(KLE_BAD_SADDR, sp, 2);
}
else {
KL_SET_ERROR(E_NO_TRACE);
}
return(1);
}
}
/* Just in case, make sure the PC is valid.
*/
if (!IS_TEXT(pc) || (pc & 3)) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "PC is invalid (pc=0x%llx)\n", pc);
}
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
KL_SET_ERROR_NVAL(KLE_BAD_PC, pc, 2);
return(1);
}
/* Find a stack trace
*/
trace->nframes = find_trace(pc, sp, (kaddr_t)0, (kaddr_t)0, trace, C_ALL);
if (trace->nframes == 0) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "get_cpu_trace: Not a valid trace! "
"(pc=0x%llx, sp=0x%llx, saddr=0x%llx\n", pc, sp, saddr);
}
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
KL_SET_ERROR(E_NO_TRACE);
return(1);
}
if (saved_kthread) {
kl_set_defkthread(K, saved_kthread);
}
return(0);
}
#define TRACE_TABLE_SIZE 100
#define NEXT_WORD(wordp, sp) \
BUMP_WORDP(wordp); \
if (PTRSZ64(K)) { \
sp += 8; \
} \
else { \
sp += 4; \
}
struct t {
kaddr_t pc;
kaddr_t sp;
};
/*
* print_valid_traces()
*
* Parameters:
*
* saddr Address of the stack we are searching in
* stack Structure containing all stack related info and buffers
* level Minimum number of frames for valid stack
* flags
* ofp
*
*/
int
print_valid_traces(kaddr_t saddr,
trace_t *trace, int level, int flags, FILE *ofp)
{
int i = 0, j;
int stkidx, frame_size, ra_offset, valid_trace, sframe_count;
kaddr_t sp, rsp, pc, jpc;
uint instr, *wordp;
char *funcname;
struct t *trace_table;
sframe_t *sframe_hold;
if (DEBUG(DC_TRACE, 1) || DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "print_valid_traces: trace=0x%x, level=%d\n",
trace, level);
}
/* Set the kernel stack pointer (sp) and word pointer (wordp) at
* the start of the stack page -- unless stack page and ublock are
* in the same page. Then set sp and saddr just beyond the
* ublock.
*/
stkidx = stack_index(saddr, trace);
if (KL_ERROR) {
return(1);
}
trace_table =
(struct t*)alloc_block(TRACE_TABLE_SIZE * sizeof(struct t), B_TEMP);
wordp = trace->stack[stkidx].ptr;
sp = saddr;
while(wordp < trace->stack[stkidx].ptr + (trace->stack[stkidx].size/4)) {
if (pc = kl_kaddr_val(K, wordp)) {
/* determine the real PC (subtract 8 from the potential RA).
*/
pc -= 8;
/* check to see if the pc is a valid code address
*/
if (!IS_TEXT(pc) || (pc & 3)) {
NEXT_WORD(wordp, sp);
continue;
}
/* get the instruction pc points to
*/
kl_get_block(K, pc, 4, &instr, "instr");
/* Go to the next word in the stack if instr isn't a
* jump or jump and link
*/
if (!((instr >> 26) == 2) && !((instr >> 26) == 3)) {
NEXT_WORD(wordp, sp);
continue;
}
/* Determine the jump pc address
*/
jpc = ((instr & 0x3ffffff) << 2) | (pc >> 28 << 28);
/* check to see if jpc is a valid code address
*/
if (!IS_TEXT(jpc) || (jpc & 3)) {
NEXT_WORD(wordp, sp);
continue;
}
/* Determine size of the jpc stack frame
*/
if (frame_size = get_frame_size(jpc, 1)) {
/* Determine offset to ra in stack frame
*/
ra_offset = get_ra_offset(jpc, 1);
if (DEBUG(DC_TRACE, 2)) {
fprintf(KL_ERRORFP, "print_valid_traces: ra_offset=%d\n",
ra_offset);
}
}
else {
/* jpc doesn't appear to be a LEAF function (it doesn't
* have a stack frame). We'll make a try to see if it
* jumps to another function (e.g., psema --> _psema).
* If it does, we'll use the jumpped to function instead
* to compute the real SP.
*/
funcname = get_funcname(jpc);
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "print_valid_traces: name=%s "
"does not have a stack frame!\n", funcname);
}
/* get the instruction jpc points to
*/
kl_get_block(K, jpc, 4, &instr, "instr");
/* if instr isn't a jump or jump and link use the
* current sp as the real sp.
*/
if (!((instr >> 26) == 2) && !((instr >> 26) == 3)) {
ra_offset = 0;
goto TRY_ANYWAY;
}
/* Disassemble the new jpc instruciton
*/
jpc = ((instr & 0x3ffffff) << 2) | (pc >> 28 << 28);
/* Check to see if jpc is a valid code address.
*/
if (!IS_TEXT(jpc) || (jpc & 3)) {
ra_offset = 0;
goto TRY_ANYWAY;
}
/* Determine size of the new jpc stack frame
*/
frame_size = get_frame_size(jpc, 1);
if (frame_size) {
/* Determine offset to ra in stack frame
*/
ra_offset = get_ra_offset(jpc, 1);
}
else {
ra_offset = 0;
goto TRY_ANYWAY;
}
}
TRY_ANYWAY:
/* Determine the real stack pointer for pc
*/
if (ra_offset == -1) {
sp = sp + frame_size;
}
else {
rsp = sp - ra_offset + frame_size;
}
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP,
"print_valid_traces: sp=0x%llx, rsp=0x%llx\n", sp, rsp);
fprintf(KL_ERRORFP,
" pc=0x%llx (%s), jpc=0x%llx (%s)\n",
pc, get_funcname(pc), jpc, get_funcname(jpc));
fprintf(KL_ERRORFP,
" frame_size=%d, ra_offset=%d\n",
frame_size, ra_offset);
}
if (find_trace(pc, rsp, (kaddr_t)0, (kaddr_t)0,
trace, flags) >= level) {
/* Check to see if the current trace is a sub-trace
* of one we've already found.
*/
j = 0;
valid_trace = 1;
/* Hold the valid frame records while we check to see
* if this is a sub-trace.
*/
sframe_hold = trace->frame;
sframe_count = trace->nframes;
trace->frame = (sframe_t*)NULL;
trace->nframes = 0;
while (trace_table[j].pc) {
if (!find_trace(trace_table[j].pc, trace_table[j].sp,
pc, rsp, trace, flags)) {
free_sframes(trace);
valid_trace = 0;
break;
}
free_sframes(trace);
j++;
}
trace->frame = sframe_hold;
trace->nframes = sframe_count;
/* print the trace if it is valid and unique -- or if
* the C_ALL flag was set.
*/
if (valid_trace) {
fprintf(ofp, "\n");
fprintf(ofp, "PC=0x%llx, SP=0x%llx\n", pc, rsp);
fprintf(ofp, "========================================="
"=======================\n");
print_trace(trace, flags, ofp);
fprintf(ofp, "========================================="
"=======================\n");
trace_table[i].pc = pc;
trace_table[i].sp = rsp;
if (++i == TRACE_TABLE_SIZE) {
fprintf(KL_ERRORFP, "There are too many stack traces "
"for trace_table (max = 100)!\n");
goto ALL_DONE;
}
}
}
NEXT_WORD(wordp, sp);
free_sframes(trace);
}
else {
if (DEBUG(DC_TRACE, 2)) {
fprintf(KL_ERRORFP, "--print_valid_traces: wordp=0x%x, "
"*wordp=0x%llx, sp=0x%llx\n",
wordp, kl_kaddr_val(K, wordp), sp);
}
NEXT_WORD(wordp, sp);
}
}
ALL_DONE:
/* Make sure and clear any error that might be set (after all
* we are likely to generate some with random tries at stack
* traces).
*/
kl_reset_error();
free_block((k_ptr_t)trace_table);
return(0);
}
/*
* do_list()
*
* Output a list of all valid code addresses contained in a stack
* along with their function name, line number, and source file name.
*/
int
do_list(kaddr_t saddr, trace_t *trace, FILE *ofp)
{
int stkidx, ifd, line_no;
char *srcfile, *funcname;
uint *wordp;
kaddr_t addr;
if (DEBUG(DC_TRACE, 1) || DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "do_list: trace=0x%x\n", trace);
}
stkidx = stack_index(saddr, trace);
if (KL_ERROR) {
return(1);
}
wordp = trace->stack[stkidx].ptr;
while(wordp < (trace->stack[stkidx].ptr + trace->stack[stkidx].size/4)) {
if (addr = kl_kaddr_val(K, wordp)) {
/* check to see if this is a valid code address
*/
if (funcname = get_funcname(addr)) {
/* Determine the source file name and line number
*/
srcfile = get_srcfile(addr);
line_no = get_lineno(addr);
fprintf(ofp, "0x%llx -- 0x%llx\n (%s:%d \"%s\")\n",
(trace->stack[stkidx].addr + ((uint)wordp -
(uint)trace->stack[stkidx].ptr)),
addr, funcname, line_no, srcfile);
/* Make sure and free the memory block that holds
* the source filename.
*/
if (srcfile) {
free_block((k_ptr_t)srcfile);
}
}
BUMP_WORDP(wordp);
}
else {
BUMP_WORDP(wordp);
}
}
return(0);
}
/*
* eframe_trace()
*/
int
eframe_trace(kaddr_t ef, trace_t *trace, int flags, FILE *ofp)
{
kaddr_t addr, pc, ra, sp, saddr, defk_saved = 0;
kstruct_t *ksp;
k_ptr_t efp, ktp;
efp = alloc_block(EFRAME_S_SIZE(K), B_TEMP);
if (trace->kthread != K_DEFKTHREAD(K)) {
defk_saved = K_DEFKTHREAD(K);
kl_set_defkthread(K, trace->kthread);
}
kl_get_struct(K, ef, EFRAME_S_SIZE(K), efp, "eframe_s");
if (KL_ERROR) {
KL_ERROR |= KLE_BAD_EFRAME_S;
print_trace_error(trace, ofp);
free_block(efp);
if (defk_saved) {
kl_set_defkthread(K, defk_saved);
}
return(1);
}
/* Print out the eframe regardless of weather or not we can
* find a valid trace.
*/
if (!(flags & C_INDENT) && !(trace->flags & TF_SUPPRESS_HEADER)) {
trace_banner(ofp, SMAJOR);
}
if (!(trace->flags & TF_SUPPRESS_HEADER)) {
fprintf(ofp, "EXCEPTION FRAME FOR 0x%llx:\n\n", ef);
}
print_eframe_s((kaddr_t)NULL, efp, flags, ofp);
/* Get the PC, RA and SP from the exception frame.
*/
sp = *(kaddr_t*)((uint)efp + (FIELD("eframe_s", "ef_sp")));
pc = *(kaddr_t*)((uint)efp + (FIELD("eframe_s", "ef_epc")));
ra = *(kaddr_t*)((uint)efp + (FIELD("eframe_s", "ef_ra")));
if (PTRSZ32(K)) {
pc &= 0xffffffff;
ra &= 0xffffffff;
sp &= 0xffffffff;
}
if (DEBUG(DC_TRACE, 2)) {
fprintf(KL_ERRORFP, "eframe_trace: pc=0x%llx, ra=0x%llx, sp=0x%llx\n",
pc, ra, sp);
}
ksp = (kstruct_t*)alloc_block(sizeof(kstruct_t), B_TEMP);
/* If the trace record has not been setup already, we need to do
* that here.
*/
if (!(trace->flags & TF_TRACEREC_VALID)) {
if (IS_KERNELSTACK(K, sp)) {
if (K_DEFKTHREAD(K)) {
ktp = kl_get_kthread(K, K_DEFKTHREAD(K), 0);
if (!KL_ERROR) {
if (IS_UTHREAD(K, ktp)) {
ksp->addr = K_DEFKTHREAD(K);
ksp->ptr = ktp;
setup_trace_rec((kaddr_t)0, ksp, KTHREAD_FLAG, trace);
free_block((k_ptr_t)ksp);
}
else {
KL_SET_ERROR_NVAL(KLE_KTHREAD_TYPE(K, ktp),
K_DEFKTHREAD(K), 2);
free_block(ktp);
}
}
}
else {
KL_SET_ERROR(KLE_NO_DEFKTHREAD);
}
}
else if (K_DEFKTHREAD(K)) {
ktp = kl_get_kthread(K, K_DEFKTHREAD(K), 0);
if (!KL_ERROR) {
ksp->addr = K_DEFKTHREAD(K);
ksp->ptr = ktp;
setup_trace_rec((kaddr_t)0, ksp, KTHREAD_FLAG, trace);
free_block((k_ptr_t)ksp);
if (KL_ERROR) {
KL_SET_ERROR_NVAL(KLE_KTHREAD_TYPE(K, ktp),
K_DEFKTHREAD(K), 2);
free_block(ktp);
}
}
}
else {
k_ptr_t pdap;
cpuid_t cpuid;
/* Check to see which cpu intstack/bootstack this sp might
* be from.
*/
pdap = kl_sp_to_pdap(K, sp, (k_ptr_t)NULL);
if (pdap) {
cpuid = kl_uint(K, pdap, "pda_s", "p_cpuid", 0);
ksp->addr = kl_pda_s_addr(K, cpuid);
ksp->ptr = pdap;
setup_trace_rec((kaddr_t)0, ksp, CPU_FLAG, trace);
free_block((k_ptr_t)ksp);
}
else if (!KL_ERROR) {
KL_SET_ERROR_NVAL(KLE_BAD_SP, sp, 2);
}
}
}
if (!KL_ERROR) {
/* Before we try and find a trace, check and see if the eframe
* pointer is the same as u_eframe in the trace_rec. If it is,
* it means the uthread took an interrupt while executing user code.
* consequently there wont be any traces to find. In that case, just
* return...
*/
if (ef == trace->u_eframe) {
if (!(flags & C_INDENT) && !(trace->flags & TF_SUPPRESS_HEADER)) {
trace_banner(ofp, SMAJOR);
}
free_block(efp);
if (defk_saved) {
kl_set_defkthread(K, defk_saved);
}
return(0);
}
saddr = stack_addr(sp, trace);
if (!KL_ERROR) {
find_trace2(pc, ra, sp, saddr, trace, (flags | C_ALL));
}
}
if (KL_ERROR && !trace->nframes) {
print_trace_error(trace, ofp);
}
else {
print_trace(trace, flags, ofp);
}
if (!(flags & C_INDENT) && !(trace->flags & TF_SUPPRESS_HEADER)) {
trace_banner(ofp, SMAJOR);
}
free_block(efp);
if (defk_saved) {
kl_set_defkthread(K, defk_saved);
}
return(0);
}
/*
* proc_trace()
*/
int
proc_trace(trace_t *trace, int flags, FILE *ofp)
{
int i, stkflg = -1, stacksize;
pid_t p_pid;
cpuid_t cpuid;
char p_stat;
k_ptr_t pdap, kt_name;
kaddr_t p, pda;
kaddr_t pc, sp, saddr, upage, curkthread;
if (DEBUG(DC_TRACE, 1) || DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "proc_trace: trace=0x%x, flags=%d\n", trace, flags);
}
p_pid = KL_INT(K, trace->ktp, "proc", "p_pid");
if (IS_UTHREAD(K, trace->ktp)) {
fprintf (ofp, "STACK TRACE FOR UTHREAD 0x%llx ", trace->kthread);
fprintf (ofp, "(%s, PID=%d):\n\n",
kl_kthread_name(K, trace->ktp), p_pid);
}
else {
if (kt_name = kl_kthread_name(K, trace->ktp)) {
fprintf (ofp, "STACK TRACE FOR KTHREAD 0x%llx ", trace->kthread);
fprintf (ofp, "(%s, PID=%d):\n\n", kt_name, p_pid);
free_block(kt_name);
}
else {
fprintf (ofp, "(PID=%d):\n\n", p_pid);
}
}
/* Determine if kthread is currently running on a CPU (by
* checking p_sonproc). If it isn't, then it's fairly straight
* forward to get a stack trace (just grab the PC and SP from
* the pcb_args in the ublock and use the process kernel stack
* page). If it IS, then get a cpu trace -- IF this is a core
* dump (you can't get a cpu trace on a live system).
*/
cpuid = kl_uint(K, trace->ktp, "kthread", "k_sonproc", 0);
if (cpuid != -1) {
/* Process is running on a CPU
*/
if (!ACTIVE(K)) {
if (get_cpu_trace(cpuid, trace)) {
return(1);
}
print_trace(trace, flags, ofp);
}
else {
fprintf(ofp, "Process is currently running on CPU %d.\n\n", cpuid);
}
return(0);
}
else {
saddr = kthread_stack(trace, &stacksize);
pc = ((kaddr_t*)trace->ktp)[10];
sp = ((kaddr_t*)trace->ktp)[8];
if (PTRSZ32(K)) {
pc &= 0xffffffff;
sp &= 0xffffffff;
}
}
/* Just in case, make sure the PC is valid code and that SP is from
* saddr.
*/
if (!IS_TEXT(pc) || (pc & 3)) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "PC is invalid (pc=0x%llx\n", pc);
}
KL_SET_ERROR_NVAL(KLE_BAD_PC, pc, 2);
return(1);
}
if (((sp < saddr) || (sp >= (saddr + stacksize)))) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "SP is invalid (sp=0x%llx)\n", sp);
}
KL_SET_ERROR_NVAL(KLE_BAD_SP, sp, 2);
return(1);
}
/* Go get a stack trace...even one that ends with an error.
*/
trace->nframes = find_trace(pc, sp, (kaddr_t)0,
(kaddr_t)0, trace, (flags | C_ALL));
if (trace->nframes == 0) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "proc_trace: Not a valid trace! "
"(pc=0x%llx, sp=0x%llx, saddr=0x%llx\n",
pc, sp, saddr);
}
if (!KL_ERROR) {
KL_SET_ERROR(E_NO_TRACE);
}
return(1);
}
print_trace(trace, flags, ofp);
return(0);
}
/*
* kthread_trace()
*/
int
kthread_trace(trace_t *trace, int flags, FILE *ofp)
{
int i, stacksize;
pid_t p_pid;
k_uint_t k_id;
cpuid_t cpuid;
char p_stat;
k_ptr_t kt_name;
kaddr_t p, pc, sp, saddr, upage, curkthread, eframe;
kl_reset_error();
if (DEBUG(DC_FUNCTRACE, 3)) {
fprintf(KL_ERRORFP, "kthread_trace: trace=0x%x, flags=%d\n",
trace, flags);
}
/* Determine what type of kthread this is and print the appropriate
* trace header.
*/
if (IS_UTHREAD(K, trace->ktp)) {
p_pid = kl_uthread_to_pid(K, trace->ktp);
fprintf (ofp, "STACK TRACE FOR UTHREAD 0x%llx ", trace->kthread);
fprintf (ofp, "(%s, PID=%d):\n\n",
kl_kthread_name(K, trace->ktp), p_pid);
}
else if (IS_STHREAD(K, trace->ktp)) {
fprintf (ofp, "STACK TRACE FOR STHREAD 0x%llx ", trace->kthread);
fprintf (ofp, "(%s):\n\n", kl_kthread_name(K, trace->ktp));
}
else if (IS_XTHREAD(K, trace->ktp)) {
fprintf (ofp, "STACK TRACE FOR XTHREAD 0x%llx ", trace->kthread);
fprintf (ofp, "(%s):\n\n", kl_kthread_name(K, trace->ktp));
}
#ifdef ANON_ITHREADS
else if (IS_ITHREAD(K, trace->ktp)) {
fprintf (ofp, "STACK TRACE FOR ITHREAD 0x%llx ", trace->kthread);
fprintf (ofp, "(%s):\n\n", kl_kthread_name(K, trace->ktp));
}
#endif
/* Determine if the kthread is currently running on a CPU (by
* checking k_sonproc). If it isn't, then it's fairly straight
* forward to get a stack trace (just check for an eframe pointer
* in the kthread struct or grab the PC and SP from the k_regs[]).
* If it the kthread was running on a CPU, then get a cpu trace.
* Note that it isn't possible to get a cpu trace on a live system.
*/
cpuid = kl_uint(K, trace->ktp, "kthread", "k_sonproc", 0);
if (cpuid != -1) {
/* kthread is running on a CPU. If it's not running, then
* issue an error message to that effect and return.
*/
if (trace->kthread == kl_get_curkthread(K, cpuid)) {
if (!ACTIVE(K)) {
get_cpu_trace(cpuid, trace);
/* If there was an error, go ahead and print out the part
* of the trace we were able to get (if any).
*/
if (KL_ERROR && !trace->nframes) {
return(1);
}
print_trace(trace, flags, ofp);
}
else {
fprintf(ofp, "Kthread is currently running on "
"CPU %d.\n\n", cpuid);
}
return(0);
}
#ifdef ANON_ITHREADS
if (IS_ITHREAD(K, trace->ktp)) {
fprintf(ofp, "ithread 0x%llx is currently not active\n\n",
trace->kthread);
}
#endif
}
saddr = kthread_stack(trace, &stacksize);
/* We have to check and see if there is an eframe pointer in
* the kthread struct. If there is, then we can use eframe_trace()
* to generate the stack trace).
*/
if (eframe = kl_kaddr(K, trace->ktp, "kthread", "k_eframe")) {
trace->flags |= TF_SUPPRESS_HEADER;
return(eframe_trace(eframe, trace, flags, ofp));
}
#ifdef ANON_ITHREADS
if (IS_ITHREAD(K, trace->ktp)) {
/* XXX - Need to determine if the ithread is active. If it's
* inactive, we shouldn't have even have gotten this far...
* We'll just bail for now.
*/
KL_SET_ERROR(E_NO_TRACE);
return(1);
}
else if (IS_XTHREAD(K, trace->ktp)) {
#else
if (IS_XTHREAD(K, trace->ktp)) {
#endif
/* Determine if the xthread is using its stack. If it's not
* then return with an error.
*/
if (KL_UINT(K, trace->ktp, "kthread", "k_flags") & 0x400) {
KL_SET_ERROR(E_NO_TRACE);
return(1);
}
}
pc = ((kaddr_t*)trace->ktp)[10];
sp = ((kaddr_t*)trace->ktp)[8];
if (PTRSZ32(K)) {
pc &= 0xffffffff;
sp &= 0xffffffff;
}
/* Just in case, make sure the PC is valid code and that SP is from
* saddr.
*/
if (!IS_TEXT(pc) || (pc & 3)) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "PC is invalid (pc=0x%llx\n", pc);
}
KL_SET_ERROR_NVAL(KLE_BAD_PC, pc, 2);
return(1);
}
if (((sp < saddr) || (sp >= (saddr + stacksize)))) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "SP is invalid (sp=0x%llx)\n", sp);
}
KL_SET_ERROR_NVAL(KLE_BAD_SP, sp, 2);
return(1);
}
/* Go get a stack trace...even one that ends with an error.
*/
trace->nframes = find_trace(pc, sp, (kaddr_t)0,
(kaddr_t)0, trace, (flags | C_ALL));
if (trace->nframes == 0) {
if (DEBUG(DC_TRACE, 1)) {
fprintf(KL_ERRORFP, "kthread_trace: Not a valid trace! "
"(pc=0x%llx, sp=0x%llx, saddr=0x%llx\n",
pc, sp, saddr);
}
if (!KL_ERROR) {
KL_SET_ERROR(E_NO_TRACE);
}
return(1);
}
print_trace(trace, flags, ofp);
return(0);
}
/*
* print_trace()
*/
int
print_trace(trace_t *trace, int flags, FILE *ofp)
{
int i, j;
#ifdef INCLUDE_REGINFO
int args;
#endif
sframe_t *sf;
/* If there is no trace, make sure error is set to something and
* return (the print_trace_error() routine will print out an error
* message).
*/
if (!(sf = trace->frame)) {
if (!KL_ERROR) {
KL_SET_ERROR(E_NO_TRACE);
}
return(1);
}
#ifdef INCLUDE_REGINFO
if (trace->flags & TF_GET_REGVALS) {
get_reg_values(trace);
}
#endif
for (i = 0; i < trace->nframes; i++, sf = sf->next) {
/* If the sframe_rec contains frame information, print out the
* stack frame -- EVEN when there has been an error.
*/
if (sf->funcname) {
fprintf(ofp, "%2d %s[", sf->level, sf->funcname);
if (sf->srcfile) {
fprintf(ofp, "%s: %d, 0x%llx]\n",
sf->srcfile, sf->line_no, sf->pc);
}
else {
fprintf(ofp, "???: %d, 0x%llx]\n", sf->line_no, sf->pc);
}
/* Check to see if this stack frame is really in a stack from
* another kthread (e.g., trace->kthread2). If it is, we need
* to put a message in the middle of the backtrace to indicate
* this.
*/
if (sf->flag & TF_KTHREAD2_VALID) {
int p_pid;
if (IS_UTHREAD(K, trace->ktp2)) {
p_pid = kl_uthread_to_pid(K, trace->ktp2);
fprintf (ofp, "\nSTACK TRACE CONTINUES ON UTHREAD "
"0x%llx ", trace->kthread2);
fprintf (ofp, "(%s, PID=%d):\n\n",
kl_kthread_name(K, trace->ktp2), p_pid);
}
else if (IS_STHREAD(K, trace->ktp2)) {
fprintf (ofp, "\nSTACK TRACE CONTINUES ON STHREAD "
"0x%llx ", trace->kthread2);
fprintf (ofp, "(%s):\n\n",
kl_kthread_name(K, trace->ktp2));
}
else if (IS_XTHREAD(K, trace->ktp2)) {
fprintf (ofp, "\nSTACK TRACE CONTINUES ON XTHREAD "
"0x%llx ", trace->kthread2);
fprintf (ofp, "(%s):\n\n",
kl_kthread_name(K, trace->ktp2));
}
#if ANON_ITHREADS
else if (IS_ITHREAD(K, trace->ktp2)) {
fprintf (ofp, "\nSTACK TRACE CONTINUES ON ITHREAD "
"0x%llx ", trace->kthread2);
fprintf (ofp, "(%s):\n\n",
kl_kthread_name(K, trace->ktp2));
}
#endif
}
#ifdef INCLUDE_REGINFO
args = 0;
for (j = 4; j < 12; j++) {
if (sf->regs[j].state == REGVAL_VALID) {
if (args == 0) {
fprintf(ofp, " (0x%llx", sf->regs[j].value);
}
else {
fprintf(ofp, ",0x%llx", sf->regs[j].value);
}
args++;
}
else if (sf->regs[j].state == REGVAL_UNKNOWN) {
continue;
}
else {
break;
}
}
if (args) {
fprintf(ofp, ")\n");
}
#endif
if (sf->eframe) {
print_eframe_s((kaddr_t)NULL, sf->eframe, flags, ofp);
/* If the eframe points to the ublock, just print the
* eframe and return.
*/
if (sf->ep == trace->u_eframe) {
return(0);
}
}
#ifdef INCLUDE_REGINFO
if (trace->flags & TF_GET_REGVALS) {
print_reg_values(sf, ofp);
}
#endif /* INCLUDE_REGINFO */
if (flags & C_FULL) {
fprintf(ofp, "\n RA=0x%llx, SP=0x%llx, FRAME SIZE=%d\n",
sf->ra, sf->sp, sf->frame_size);
fprintf(ofp, "\n");
if (sf->frame_size) {
dump_stack_frame(trace, sf, ofp);
}
}
}
/* If no error occurred, just return. Otherwise, print an error
* message.
*/
if (sf->error == KLE_BAD_PC) {
fprintf(ofp, "Trace stops because bad PC 0x%llx found\n", sf->pc);
return(1);
}
else if (sf->error == KLE_BAD_SP) {
fprintf(ofp, "Trace stops because bad SP 0x%llx found\n", sf->sp);
return(1);
}
else if (sf->error == KLE_BAD_EP) {
fprintf(ofp, "Trace stops because eframe could not be found\n");
return(1);
}
else if (sf->error == KLE_BAD_PROC) {
fprintf(ofp, "Trace stops because proc was invalid or not "
"specified\n");
return(1);
}
else if (sf->error == KLE_UNKNOWN_ERROR) {
fprintf(ofp, "Trace stops because of an unknown error\n");
return(1);
}
}
return(0);
}
/*
* dump_stack_frame()
*/
void
dump_stack_frame(trace_t *trace, sframe_t *curframe, FILE *ofp)
{
int i, first_time = 1;
kaddr_t sp;
uint *asp;
sp = curframe->sp;
asp = curframe->asp;
for (i = 0; i < curframe->frame_size / K_NBPW(K); i++) {
if (!(i % ((K_NBPW(K) == 8) ? 2 : 4))) {
if (first_time) {
first_time = 0;
if (PTRSZ64(K)) {
fprintf(ofp, " %llx: %016llx ",
sp, kl_kaddr_val(K, asp));
asp += 2;
}
else {
fprintf(ofp, " %llx: %08x ", sp, *asp++);
}
}
else {
fprintf(ofp, "\n %llx: ", sp);
if (PTRSZ64(K)) {
fprintf(ofp, "%016llx ", kl_kaddr_val(K, asp));
asp += 2;
}
else {
fprintf(ofp, "%08x ", *asp++);
}
}
sp += 16;
}
else {
if (PTRSZ64(K)) {
fprintf(ofp, "%016llx ", kl_kaddr_val(K, asp));
asp += 2;
}
else {
fprintf(ofp, "%08x ", *asp++);
}
}
}
if (curframe->frame_size) {
fprintf(ofp, "\n\n");
}
}
/*
* print_trace_error()
*/
void
print_trace_error(trace_t *trace, FILE *ofp)
{
if (DEBUG(DC_GLOBAL, 1)) {
fprintf(ofp, "Could not find trace...\n");
}
kl_print_error(K);
}
/*
* print_cpu_status()
*
* Print out information about a specific CPU. This routine is
* called from the report module. It is in the trace module because
* it is so closely tied to the trace data structures and function
* calls.
*/
int
print_cpu_status(int cpuid, FILE *ofp)
{
int i;
int stkidx = -1, stkflg = -1, ktp_alloced = 0;
k_ptr_t up, pdap, ktp;
kstruct_t *ksp;
trace_t *trace;
sframe_t *frame;
pdap = alloc_block(PDA_S_SIZE(K), B_TEMP);
kl_get_pda_s(K, (kaddr_t)cpuid, pdap);
if (KL_ERROR) {
free_block(pdap);
return(1);
}
trace = alloc_trace_rec();
ksp = (kstruct_t*)alloc_block(sizeof(kstruct_t), B_TEMP);
ksp->addr = kl_pda_s_addr(K, cpuid);
ksp->ptr = pdap;
setup_trace_rec((kaddr_t)0, ksp, CPU_FLAG, trace);
free_block((k_ptr_t)ksp);
if (KL_ERROR) {
fprintf(ofp, " CPU %d ERROR: ", cpuid);
print_trace_error(trace, ofp);
free_trace_rec(trace);
return(1);
}
/* Get a stack trace for cpuid
*/
if (!get_cpu_trace(cpuid, trace) && !KL_ERROR) {
/* Point at the first (the most recent) stack level
*/
if (frame = trace->frame) {
if (strcmp(frame->funcname, "panicspin") == 0) {
/* This CPU should be on the interrupt stack -- spinning
* waiting for the dump to complete. It most likely was not
* servicing an interrupt when the panic occured. We need
* to backtrace to the VEC_intr() to see which stack was
* active when the interrupt occurred.
*/
for (;;) {
frame = frame->next;
if (strcmp(frame->funcname, "VEC_int") == 0) {
if (frame->next == trace->frame) {
/* The VEC_int frame is the last frame in the
* trace. If there is a kthread running on this
* CPU, we have to determine what type. If it's
* a uthread, it means that the interrupt
* occurred while the application was in USER
* mode. If it was any other type of thread,
* key the stack type to it. If no kthread was
* running, it means we interrupted out of an
* idle state.
*/
if (trace->kthread) {
if (IS_UTHREAD(K, trace->ktp)) {
stkflg = 0;
}
else if (IS_XTHREAD(K, trace->ktp)) {
stkflg = XTHREADSTACK;
}
#ifdef ANON_ITHREADS
else if (IS_ITHREAD(K, trace->ktp)) {
stkflg = ITHREADSTACK;
}
#endif
else if (IS_STHREAD(K, trace->ktp)) {
stkflg = STHREADSTACK;
}
}
else {
stkflg = BOOTSTACK;
}
}
else {
stkidx = frame->next->ptr;
}
break;
}
if (frame->next == trace->frame) {
break;
}
}
}
else {
/* Assume this is the panicing CPU or it's an NMI dump. In
* either case, the first (most recent) frame should be in
* the stack that was active when the dump occurred.
*/
int s;
kaddr_t pda;
pdap = alloc_block(PDA_S_SIZE(K), B_TEMP);
kl_get_pda_s(K, (kaddr_t)cpuid, pdap);
stkidx = frame->ptr;
s = kl_uint(K, pdap, "pda_s", "p_kstackflag", 0);
if (trace->stack[stkidx].type != s) {
stkflg = s;
stkidx = -1;
}
free_block(pdap);
}
}
if (stkidx != -1) {
stkflg = trace->stack[stkidx].type;
}
}
if (KL_ERROR || ((stkidx == -1) && (stkflg == -1))) {
/* For some reason (an error) we were not able to get a valid
* stack trace. So, we'll just have to make a best guess based
* on the stack flag in the CPU's pda_s struct.
*/
kaddr_t pda, curkthread;
pdap = alloc_block(PDA_S_SIZE(K), B_TEMP);
kl_get_pda_s(K, (kaddr_t)cpuid, pdap);
stkflg = kl_uint(K, pdap, "pda_s", "p_kstackflag", 0);
if (curkthread = kl_kaddr(K, pdap, "pda_s", "p_curkthread")) {
if (ktp = kl_get_kthread(K, curkthread, 0)) {
ktp_alloced++;
}
}
}
else {
ktp = trace->ktp;
}
if (stkflg == KERNELSTACK) {
/* HACK! With certain NMI dumps, it's possible for
* the kthread running on a cpu to be bad (all NULLs
* or not even in the dump). We can't use the below
* macros or it will cause a SEGV. Just print an error
* message instead.
*/
if (!ktp) {
fprintf(KL_ERRORFP, " CPU %d ERROR: kthread (0x%llx) on CPU %d "
"is bad!\n",
cpuid,
kl_kaddr(K, pdap, "pda_s", "p_curkthread"), cpuid);
if (trace) {
free_trace_rec(trace);
}
return(0);
}
if (IS_XTHREAD(K, ktp)) {
stkflg = XTHREADSTACK;
}
#ifdef ANON_ITHREADS
else if (IS_ITHREAD(K, ktp)) {
stkflg = ITHREADSTACK;
}
#endif
else if (IS_STHREAD(K, ktp)) {
stkflg = STHREADSTACK;
}
}
fprintf(ofp, " ");
switch (stkflg) {
case USERSTACK:
fprintf(ofp,
"CPU %d was in user mode running the command "
"'%s'\n", cpuid, kl_kthread_name(K, ktp));
break;
case KERNELSTACK:
fprintf(ofp,
"CPU %d was in kernel mode running the command "
"'%s'\n", cpuid, kl_kthread_name(K, ktp));
break;
case INTSTACK:
fprintf(ofp, "CPU %d was servicing an interrupt\n", cpuid);
break;
case BOOTSTACK:
fprintf(ofp, "CPU %d was idle\n", cpuid);
break;
case STHREADSTACK: {
k_ptr_t kt_name;
fprintf(ofp, "CPU %d was in kernel mode running an "
"sthread ", cpuid);
if (kt_name = kl_kthread_name(K, ktp)) {
fprintf(ofp, "named '%s'\n", kt_name);
}
else {
fprintf(ofp, "\n");
}
break;
}
#ifdef ANON_ITHREADS
case ITHREADSTACK: {
k_ptr_t kt_name;
fprintf(ofp, "CPU %d was in kernel mode running an "
"ithread ", cpuid);
if (kt_name = kl_kthread_name(K, ktp)) {
fprintf(ofp, "named '%s'\n", kt_name);
}
else {
fprintf(ofp, "\n");
}
break;
}
#endif
case XTHREADSTACK: {
k_ptr_t kt_name;
fprintf(ofp, "CPU %d was in kernel mode running an "
"xthread ", cpuid);
if (kt_name = kl_kthread_name(K, ktp)) {
fprintf(ofp, "named '%s'\n", kt_name);
}
else {
fprintf(ofp, "\n");
}
break;
}
default :
fprintf(ofp, "CPU %d -- invalid stack!\n", cpuid);
}
/* Free buffers...
*/
if (ktp_alloced) {
free_block(ktp);
}
if (trace) {
free_trace_rec(trace);
}
return(0);
}