Logo Search packages:      
Sourcecode: lcrash version File versions  Download package

trace_i386.c

/*
 * $Id: trace_i386.c,v 1.1 2004/12/21 23:26:17 tjm Exp $
 *
 * This file is part of lcrash, an analysis tool for Linux memory dumps.
 *
 * Created by Silicon Graphics, Inc.
 * Contributions by IBM, and others
 *
 * Copyright (C) 1999 - 2002 Silicon Graphics, Inc. All rights reserved.
 * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version. See the file COPYING for more
 * information.
 */

#include <lcrash.h>
#include <strings.h>

/*
 * function declarations
 */
static kaddr_t get_call_pc_i386(kaddr_t);
static int  get_jmp_instr(kaddr_t, kaddr_t, kaddr_t*, char*, char**);
static int  is_push(unsigned int);
static int  is_pop(unsigned int);
static int  get_framesize(kaddr_t);
static int  dumptask_trace_i386(kaddr_t, int, FILE*);
static int  eframe_type(struct kl_pt_regs_i386*);
static void print_eframe(FILE*, struct kl_pt_regs_i386*);
static void get_pt_regs_i386(struct kl_pt_regs_i386*, void*);


/*
 * function definitions
 */

/*
 * get_call_pc_i386()
 */
kaddr_t
get_call_pc_i386(kaddr_t ra)
{
      kaddr_t addr = 0;
      instr_rec_i386_t *irp;

      if (!(irp = get_instr_stream_i386(ra, 1, 0))) {
            return((kaddr_t)NULL);
      }
      if (!irp->prev) {
            free_instr_stream_i386(irp);
            return((kaddr_t)NULL);
      }
      if ((irp->prev->opcode == 0x00e8) || (irp->prev->opcode == 0xff02)) {
            addr = irp->prev->addr;
      }
      free_instr_stream_i386(irp);
      return(addr);
}

/*
 * get_jmp_instr()
 */
int
get_jmp_instr(
kaddr_t addr, 
kaddr_t isp, 
kaddr_t *caddr, 
char *fname, 
char **cfname)
{
      kaddr_t a;
      int offset;
      instr_rec_i386_t *irp;

      if (!(irp = get_instr_stream_i386(addr, 1, 0))) {
            return(1);
      }
      if (!irp->prev) {
            free_instr_stream_i386(irp);
            return(1);
      }
      irp = irp->prev;
      if (!(irp->opcode == 0x00e8) && !(irp->opcode == 0xff02)) {
            free_instr_stream_i386(irp);
            return(1);
      }

      /* Check for the easiest case first...
       */
      if (irp->opcode == 0xe8) {
            a = irp->operand[0].op_addr;
            if ((*cfname = kl_funcname(a))) {
                  *caddr = a;
            }
      } else if (irp->opcode == 0xff02) {
            switch (irp->modrm) {
                  case 0x14:
                        if (irp->sib == 0x85) {
                              a = KL_VREAD_PTR(addr - 4);
                              if (KL_ERROR) {
                                    free_instr_stream_i386(irp);
                                    return(1);
                              }
                              if (strstr(fname, "system_call")) {
                                    GET_BLOCK(isp + 28, 4, &offset);
                                    a += (offset * 4);
                                    a = KL_VREAD_PTR(a);
                                    if ((*cfname = 
                                          kl_funcname(a))) {
                                          *caddr = a;
                                    }
                              }
                        }
                        break;

                  case 0xc2: /* EAX */
                  case 0xca: /* ECX */
                  case 0xd2: /* EDX */
                  case 0xda: /* EBX */
                  case 0xea: /* EBP */
                  case 0xf2: /* ESI */
                  case 0xfa: /* EDI */
                        break;
            } 
      }
      free_instr_stream_i386(irp);
      return(0);
}

/* 
 * is_push()
 */
int
is_push(unsigned int opcode)
{
      switch(opcode) {
            case 0x0006:
            case 0x000e:
            case 0x0016:
            case 0x001e:
            case 0x0050:
            case 0x0051:
            case 0x0052:
            case 0x0053:
            case 0x0054:
            case 0x0055:
            case 0x0056:
            case 0x0057:
            case 0x0068:
            case 0x006a:
            case 0x009c:
            case 0x0fa0:
            case 0x0fa8:
            case 0xff06:
                  return(1);
            case 0x0060:
                  return(2);
      }
      return(0);
}

/* 
 * is_pop()
 */
int
is_pop(unsigned int opcode)
{
      switch(opcode) {
            case 0x0007:
            case 0x0017:
            case 0x001f:
            case 0x0058:
            case 0x0059:
            case 0x005a:
            case 0x005b:
            case 0x005c:
            case 0x005d:
            case 0x005e:
            case 0x005f:
            case 0x008f:
            case 0x009d:
            case 0x0fa1:
            case 0x0fa9:
                  return(1);
            case 0x0061:
                  return(2);
      }
      return(0);
}

/*
#define FRMSIZE_DBG 1
#define FRMSIZE2_DBG 1
*/

/*
 * get_framesize()
 */
static int
get_framesize(kaddr_t pc)
{
      int size, ret, frmsize = 0;
      kaddr_t addr;
      instr_rec_i386_t irp;
        syment_t *sp;

      if (!(sp = kl_lkup_symaddr(pc))) {
            return(-1);
      }
#ifdef FRMSIZE_DBG
      fprintf(stderr, "get_framesize(): pc=0x%x (0x%x:%s)\n", 
            pc, sp->s_addr, sp->s_name);
#endif
      addr = sp->s_addr;
      while (addr <= pc) {
            memset(&irp, 0, sizeof(irp));
            irp.aflag = 1;
            irp.dflag = 1;
            if (!(size = get_instr_info_i386(addr, &irp))) {
                  fprintf(stderr, "ZERO SIZE!!\n");
                  return(-1);
            }
            if (size != irp.size) {
                  fprintf(stderr, "SIZE DOES NOT MATCH!!\n");
            }
            if (irp.opcode == 0x8300) {
                  /* e.g., addl   $0x8,%esp */ 
                  if (irp.operand[0].op_reg == R_eSP) {
                        frmsize -= irp.operand[1].op_addr;
#ifdef FRMSIZE_DBG
                        fprintf(stderr, "    addl  --> 0x%x: -%d\n", 
                              addr, irp.operand[1].op_addr);
#endif
                  }
            } else if ((irp.opcode == 0x8305) || (irp.opcode == 0x8105)) {
                  /* e.g., subl   $0x40,%esp */
                  if (irp.operand[0].op_reg == R_eSP) {
                        frmsize += irp.operand[1].op_addr;
#ifdef FRMSIZE_DBG
                        fprintf(stderr, "    subl  --> 0x%x: +%d\n", 
                              addr, irp.operand[1].op_addr);
#endif
                  }
            } else if ((ret = is_push(irp.opcode))) {
                  if (ret == 2) {
                        frmsize += (8 * 4);
#ifdef FRMSIZE_DBG
                        fprintf(stderr, "   pusha  --> 0x%x: +%d\n",
                              addr, (8 * 4));
#endif
                  } else {
                        frmsize += 4; 
#ifdef FRMSIZE_DBG
                        fprintf(stderr, "   pushl  --> 0x%x: +%d\n" ,
                              addr, 4);
#endif
                  }
            } else if ((ret = is_pop(irp.opcode))) {
                  if (ret == 2) {
                        frmsize -= (8 * 4);
#ifdef FRMSIZE_DBG
                        fprintf(stderr, "    popa  --> 0x%x: -%d\n", 
                              addr, (8 * 4));
#endif
                  } else {
                        frmsize -= 4;
#ifdef FRMSIZE_DBG
                        fprintf(stderr, "    popl  --> 0x%x: -%d\n", 
                              addr, 4);
#endif
                  }
#ifdef FRMSIZE2_DBG
            } else {
                  fprintf(stderr, "              0x%x: opcode=0x%x\n", 
                        addr, irp.opcode);
#endif
            }
            addr += size;
      }
      return(frmsize);
}

/*
 * print_pc_i386()
 */
void
print_pc_i386(kaddr_t addr, FILE *ofp)
{
      int offset = 0;
      syment_t *sp;

      if ((sp = kl_lkup_symaddr(addr))) {
            offset = addr - sp->s_addr;
      }

      /* Print out address
       */
      fprintf(ofp, "0x%"FMT64"x", addr);

      /* Print out symbol name
       */
      if (sp) {
            if (offset) {
                  fprintf(ofp, " <%s+%d>", sp->s_name, offset);
            } else {
                  fprintf(ofp, " <%s>", sp->s_name);
            }
      }
}

/* 
 * setup_trace_rec_i386()
 */
int
setup_trace_rec_i386(kaddr_t saddr, kaddr_t task, int flag, trace_t *trace)
{
      int aflag = K_TEMP;
      kl_reset_error();

      if (flag & C_PERM) {
            aflag = K_PERM;
      }
      if (task) {
            trace->tsp = kl_alloc_block(TASK_STRUCT_SZ, aflag);
            if (kl_get_task_struct(task, 2, trace->tsp)) {
                  kl_free_block(trace->tsp);
                  trace->tsp = NULL;
                  return(1);
            }
      }
      trace->stack[0].type = S_KERNELSTACK;
      trace->stack[0].size = STACK_SIZE;

      /* Get the base address of the stack
       */
      trace->stack[0].addr = saddr - trace->stack[0].size;
      trace->stack[0].ptr = kl_alloc_block(STACK_SIZE, aflag);
      if (KL_ERROR) {
            clean_trace_rec(trace);
            return(1);
      }
      GET_BLOCK(trace->stack[0].addr, STACK_SIZE, trace->stack[0].ptr);
      if (KL_ERROR) {
            clean_trace_rec(trace);
            return(1);
      }
      return(0);
}

/*
 * Read pt_regs from buf into our kl_pt_regs_i386 struct
 */
static void
get_pt_regs_i386(struct kl_pt_regs_i386 *regs, void *buf)
{
      uint32_t *tmp = buf;

      regs->ebx = KL_GET_UINT32(tmp);
      regs->ecx = KL_GET_UINT32(++tmp);
      regs->edx = KL_GET_UINT32(++tmp);
      regs->esi = KL_GET_UINT32(++tmp);
      regs->edi = KL_GET_UINT32(++tmp);
      regs->ebp = KL_GET_UINT32(++tmp);
      regs->eax = KL_GET_UINT32(++tmp);
      regs->xds = KL_GET_UINT32(++tmp);
      regs->xes = KL_GET_UINT32(++tmp);
      regs->orig_eax = KL_GET_UINT32(++tmp);
      regs->eip = KL_GET_UINT32(++tmp);
      regs->xcs = KL_GET_UINT32(++tmp);
      regs->eflags = KL_GET_UINT32(++tmp);
      regs->esp = KL_GET_UINT32(++tmp);
      regs->xss = KL_GET_UINT32(++tmp);
}

#define _KERNEL_EFRAME_I386      0
#define _USER_EFRAME_I386        1
#define _KERNEL_EFRAME_SZ_I386   13 /* no ss and esp */
#define _USER_EFRAME_SZ_I386     15
#define _KERNEL_CS_I386          0x10
#define _KERNEL_DS_I386          0x18
#define _USER_CS_I386            0x23
#define _USER_DS_I386            0x2B
/* 
 * Check if the exception frame is of kernel or user type 
 * Is checking only DS and CS values sufficient ?
 */
static int
eframe_type(struct kl_pt_regs_i386 *regs)
{
      if (((regs->xcs & 0xffff) == _KERNEL_CS_I386) && 
                  ((regs->xds & 0xffff) == _KERNEL_DS_I386))
            return _KERNEL_EFRAME_I386;
      else if (((regs->xcs & 0xffff) == _USER_CS_I386) && 
                  ((regs->xds & 0xffff) == _USER_DS_I386))
            return _USER_EFRAME_I386;
      return -1;
}

static void
print_eframe(FILE *ofp, struct kl_pt_regs_i386 *regs)
{
      int type = eframe_type(regs);

      fprintf(ofp, "   ebx: %08x ecx: %08x edx: %08x    esi: %08x\n",
            regs->ebx, regs->ecx, regs->edx, regs->esi);
      fprintf(ofp, "   edi: %08x ebp: %08x eax: %08x     ds: %04x\n",
            regs->edi, regs->ebp, regs->eax, (regs->xds & 0xffff));
      fprintf(ofp, "    es: %04x     eip: %08x  cs: %04x "
            "    eflags: %08x\n", (regs->xes & 0xffff), regs->eip,
            (regs->xcs & 0xffff), regs->eflags);      

      if (type == _USER_EFRAME_I386){
            fprintf(ofp, "   esp: %08x  ss: %04x\n",
                  regs->esp, regs->xss);
      }
}

static syment_t *
kernel_text_address(kaddr_t pc) {
        syment_t *sp = NULL;
      sp = kl_lkup_symaddr_text(pc);
      kl_reset_error();
      return sp;
}

/*
 * check_valid_return()
 */ 
static int check_valid_return(kaddr_t pc, kaddr_t currpc)
{
      instr_rec_i386_t *irp;
      int ret = 1;      /* assume invalid return address */
      kaddr_t startaddr, calladdr;
      unsigned long off8, off32;

      if (!(irp = get_instr_stream_i386(pc, 1, 0)))
            return 1;

      if (!irp->prev) {
            free_instr_stream_i386(irp);
            return 1;
      }
      startaddr = kl_funcaddr(currpc);
      
      switch (irp->prev->opcode) {
            case 0xe8:  /* call %off32 */
            case 0xe9:  /* jmp %off32 */
                  off32 = KL_VREAD_UINT32((irp->addr)-4);
                  calladdr = pc + (signed int)off32;
                  if (calladdr == startaddr) 
                        ret = 0;
                  break;
            case 0xeb:  /* jmp %off8 */
                  off8 = KL_VREAD_INT8((irp->addr)-1);
                  calladdr = pc + (signed int)off8;
                  if (calladdr == startaddr)
                        ret = 0;
                  break;
            case 0xff02:      /* call with reg specified targets */
                  if ((irp->prev->modrm & 0x38) == 0x10) 
                        ret = 0;
                  break;
            default:
                  ret = 1;
                  break;
      }
      free_instr_stream_i386(irp);
      return ret;
}

/*
 * find_trace_i386()
 *
 *   Given a starting pc (start_cp), starting stack pointer (start_sp), 
 *   and stack 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 passed in, then
 *   return a trace even if it is a subtrace of a trace that was
 *   previously found.
 *
 *   Parameters:
 *
 *   start_pc       starting program counter
 *   start_sp       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_i386(
      kaddr_t start_pc, 
      kaddr_t start_sp, 
      kaddr_t check_pc, 
      kaddr_t check_sp,
      trace_t *trace, 
      int flags)
{
      kaddr_t sp, pc, currpc;
      sframe_t *curframe;
      int curstkidx = 0;
      syment_t *endmap;
      syment_t *sym;

      endmap = kl_lkup_symname("_end");

      if ((sym = kernel_text_address(start_pc)) != NULL) {
            curframe = alloc_sframe(trace, flags);
            UPDATE_FRAME_I386(sym->s_name, start_pc, 0, start_sp, 0, 0,
                        0, 0, 0, 0);
      }

      pc = start_pc;
      sp = start_sp;
      currpc = start_pc;
      while(((sp + sizeof(void *) -1) & (STACK_SIZE-sizeof(void*)))) {
            pc = KL_VREAD_PTR(sp);
            if ((sym = kernel_text_address(pc))) {
                    if (!(check_valid_return(pc, currpc))) {
                        curframe = alloc_sframe(trace, flags);
                        UPDATE_FRAME_I386(sym->s_name, pc, 0, sp, 
                                    0, 0, 0, 0, 0, 0);
                        if (check_pc && ((pc == check_pc) 
                                    && (sp == check_sp))) {
                              kl_free_block((void *)curframe);
                              if (flags & C_ALL) {
                                    return(trace->nframes);
                              } else {
                                    return(0);
                              }
                        }
                        currpc = pc;
                  }
            }
            sp++;
      }
      return(trace->nframes);
}

/*
 * print_trace_i386()
 */
void
print_trace_i386(trace_t *trace, int flags, FILE *ofp)
{
      int offset;
      sframe_t *frmp;

      if ((frmp = trace->frame)) {
            fprintf(ofp, "Sl      SP      Function [IP]\n");
            do {
                  fprintf(ofp, "%2d [0x%"FMT64"x] %s", 
                        frmp->level, frmp->sp, frmp->funcname);
                  offset = pc_offset(frmp->pc);
                  if (offset > 0) {
                        fprintf(ofp, "+%d", offset);
                  } else if (offset < 0) {
                        fprintf(ofp, "+<ERROR>");
                  }
                  fprintf(ofp, " [0x%"FMT64"x]\n", frmp->pc);
                  if (frmp->flag & EX_FRAME_I386){
                        struct kl_pt_regs_i386 regs;
                        get_pt_regs_i386(&regs, (void*) frmp->asp);
                        print_eframe(ofp, &regs);
                  }
                  if (flags & C_FULL) {
                        fprintf(ofp, "\n");
                        fprintf(ofp, "   RA=0x%08"FMT64"x, SP=0x%08"
                              FMT64"x, FP=0x%08"FMT64
                              "x, SIZE=%d\n\n",
                              frmp->ra, frmp->sp, 
                              frmp->fp, frmp->frame_size);
#ifdef FRMSIZE_DBG
                        fprintf(ofp, "\n  FRAMESIZE=%d\n\n",
                              get_framesize(frmp->pc));
#endif
                        dump_stack_frame(trace, frmp, ofp);
                  }
                  if (frmp->error) {
                        fprintf(ofp, "TRACE ERROR 0x%"FMT64"x\n", 
                              frmp->error);
                  }
                  frmp = frmp->next;
            } while (frmp != trace->frame);
      }
}

/*
 * task_trace_i386()
 */
int
task_trace_i386(kaddr_t task, int flags, FILE *ofp)
{
      void *tsp;
      kaddr_t saddr, eip, esp;
      trace_t *trace;

        if (!(tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP))) {
            return(1);
      }
      if (kl_get_task_struct(task, 2, tsp)) {
            kl_free_block(tsp);
            return(1);
      }
      trace = (trace_t *)alloc_trace_rec(C_TEMP);
      if (!trace) {
            fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
            return(1);
      } else {
            saddr = KL_KERNELSTACK_UINT64(task);
            if (kl_smp_dumptask_i386(task)) {
                  eip = kl_dumpeip_i386(task);
                  esp = kl_dumpesp_i386(task);
            } else {
                  if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
                        eip = KL_UINT(K_PTR(tsp, "task_struct", "tss"),
                              "thread_struct", "eip");
                        esp = KL_UINT(K_PTR(tsp, "task_struct", "tss"),
                                    "thread_struct", "esp");
                  } else {
                        eip = KL_UINT(
                              K_PTR(tsp, "task_struct", "thread"), 
                              "thread_struct", "eip");
                        esp = KL_UINT(
                        K_PTR(tsp, "task_struct", "thread"), 
                              "thread_struct", "esp");
                  }
            }
            if (esp < KL_PAGE_OFFSET || eip < KL_PAGE_OFFSET) {
                  fprintf(KL_ERRORFP, "Task in user space, No backtrace\n");
                  return 1;
            }
            setup_trace_rec_i386(saddr, 0, 0, trace);
            if (KL_ERROR) {
                  fprintf(KL_ERRORFP, "Error setting up trace rec!\n");
                  free_trace_rec(trace);
                  return(1);
            }
            find_trace_i386(eip, esp, 0, 0, trace, 0);
            trace_banner(ofp);
            fprintf(ofp, "STACK TRACE FOR TASK: 0x%"FMT64"x", task);
            if (KL_TYPEINFO()) {
                  fprintf(ofp, "(%s)\n\n", 
                        (char *)K_PTR(tsp, "task_struct", "comm"));
            } else {
                  fprintf(ofp, "(%s)\n\n", 
                        (char *)K_PTR(tsp, "task_struct", "comm"));
            }
            print_trace_i386(trace, flags, ofp);
      }
      kl_free_block(tsp);
      free_trace_rec(trace);
      return(0);
}

/*
 * print_traces_i386()
 *
 *   Output a list of all valid code addresses contained in a stack
 *   along with their function name and stack location.
 */
int
print_traces_i386(kaddr_t saddr, int level, int flags, FILE *ofp)
{
      int nfrms;
      char *fname, *cfname;
      uint32_t *wordp, *stackp;
      trace_t *trace;
      kaddr_t addr, isp, caddr, sbase;
      
      stackp = (uint32_t*)kl_alloc_block(STACK_SIZE, K_TEMP);
      sbase = saddr - STACK_SIZE;
      GET_BLOCK(sbase, STACK_SIZE, stackp);
      if (KL_ERROR) {
            kl_free_block(stackp);
            return(1);
      }

      if (!(trace = (trace_t *)alloc_trace_rec(K_TEMP))) {
            fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
            kl_free_block(stackp);
            return(1);
      }
      setup_trace_rec_i386(saddr, 0, 0, trace);

      wordp = stackp;
      while(wordp < (stackp + (STACK_SIZE / 4))) {
            if ((addr =  KL_GET_PTR(wordp))) {

                  /* check to see if this is a valid code address
                   */
                  if ((fname = kl_funcname(addr))) {
                        /* Now use the instruction to back up and
                         * see if this RA was saved after a call.
                         * If it was, then try to determine what 
                         * function was called. At the very least,
                         * only print out info for true return
                         * addresses (coming right after a call
                         * instruction -- even if we can't tell
                         * what function was called).
                         */
                        isp = sbase + 
                              (((uaddr_t)wordp) - ((uaddr_t)stackp));

                        cfname = (char *)NULL;
                        caddr = 0;
                        if (get_jmp_instr(addr, isp, 
                                    &caddr, fname, &cfname)) {
                              wordp++;
                              continue;
                        }

                        /* We have found a valid jump address. Now, 
                         * try and get a backtrace.
                         */
                        nfrms = find_trace_i386(addr, isp, 0, 0,
                                          trace, 0);
                        if (nfrms) {
                              if ((nfrms >= level) &&
                                     (!trace->frame->prev->error ||
                                          (flags & C_ALL))) {
                                    fprintf(ofp, "\nPC=");
                                    print_kaddr(addr, ofp, 0);
                                    fprintf(ofp, "  SP=");
                                    print_kaddr(isp, ofp, 0);
                                    fprintf(ofp, "  SADDR=");
                                    print_kaddr(saddr, ofp, 0);
                                    fprintf(ofp, "\n");
                                    trace_banner(ofp);
                                    print_trace_i386(trace, flags, 
                                                 ofp);
                                    trace_banner(ofp);
                              }
                              free_sframes(trace);
                        }
                  }
                  wordp++;
            } else {
                  wordp++;
            }
      }
      kl_free_block(stackp);
      return(0);
}

/*
 * do_list_i386()
 *
 *   Output a list of all valid code addresses contained in a stack
 *   along with their function name and stack location.
 */
int
do_list_i386(kaddr_t saddr, int size, FILE *ofp)
{
      char *fname, *cfname;
      uaddr_t *wordp, *stackp;
      kaddr_t addr, isp, caddr, sbase;
      
      stackp = (uaddr_t*)kl_alloc_block(size, K_TEMP);
      sbase = saddr - size;
      GET_BLOCK(sbase, size, stackp);
      if (KL_ERROR) {
            kl_free_block(stackp);
            return(1);
      }

      wordp = stackp;
      while(wordp < (stackp + (size / 4))) {
            if ((addr =  (kaddr_t)(*(uaddr_t*)wordp))) {

                  /* check to see if this is a valid code address
                   */
                  if ((fname = kl_funcname(addr))) {
                        /* Now use the instruction to back up and
                         * see if this RA was saved after a call.
                         * If it was, then try to determine what 
                         * function was called. At the very least,
                         * only print out info for true return
                         * addresses (coming right after a call
                         * instruction -- even if we can't tell
                         * what function was called).
                         */
                        isp = sbase + 
                              (((uaddr_t)wordp) - ((uaddr_t)stackp));

                        cfname = (char *)NULL;
                        caddr = 0;
                        if (get_jmp_instr(addr, isp, 
                                    &caddr, fname, &cfname)) {
                              wordp++;
                              continue;
                        }
                        fprintf(ofp, "0x%"FMT64"x -- 0x%"FMT64"x (%s)",
                                    isp, addr, fname);
                        if (cfname) {
                              fprintf(ofp, " --> 0x%"FMT64"x (%s)\n",
                                    caddr, cfname);
                        } else {
                              fprintf(ofp, "\n");
                        }
                  }
                  wordp++;
            } else {
                  wordp++;
            }
      }
      kl_free_block(stackp);
      return(0);
}

/*
 * add_frame_i386()
 */
int
add_frame_i386(trace_t *trace, kaddr_t fp, kaddr_t ra)
{
      sframe_t *cf, *sf;

      /* Check to make sure that sp is from the stack in the trace
       * record.
       *
       * XXX -- todo
       */
      sf = (sframe_t *)alloc_sframe(trace, C_PERM);
      sf->fp = fp;
      sf->ra = ra;
      if ((cf = trace->frame)) {
            do {
                  if (cf->fp && (sf->fp < cf->fp)) {
                        if (cf->next == cf) {
                              cf->prev = sf;
                              sf->next = cf;
                              cf->next = sf;
                              sf->prev = cf;
                              trace->frame = sf;
                        } else {
                              cf->prev->next = sf;
                              sf->prev = cf->prev;
                              cf->prev = sf;
                              sf->next = cf;
                        }
                        return(0);
                  }
                  cf = cf->next;
            } while (cf != trace->frame);
            cf = 0;
      } 
      if (!cf) {
            kl_enqueue((element_t **)&trace->frame, (element_t *)sf);
      }
      return(1);
}

/*
 * finish_trace_i386()
 */
void
finish_trace_i386(trace_t *trace)
{
      int level = 0, curstkidx = 0;
      uaddr_t sbp;
      kaddr_t sbase, saddr;
      sframe_t *sf;

      sbp = (uaddr_t) trace->stack[curstkidx].ptr;
        sbase = trace->stack[curstkidx].addr;
        saddr = sbase + trace->stack[curstkidx].size;

      if ((sf = trace->frame)) {
            do {
                  if (!sf->pc) {
                        if (sf != trace->frame) {
                              sf->sp = sf->prev->fp + 4;
                              sf->pc = get_call_pc_i386(sf->prev->ra);
                        }
                        if (!sf->pc) {
                              sf = sf->next;
                              continue;
                        }
                  }
                  sf->level = level++;
                  sf->frame_size = sf->fp - sf->sp + 4;
                  sf->funcname = kl_funcname(sf->pc);
                  sf->asp = sbp + (STACK_SIZE - (saddr - sf->sp));
                  sf = sf->next;
            } while (sf != trace->frame);

            if (level > 0) {
                  sf = (sframe_t *)alloc_sframe(trace, C_PERM);
                  sf->level = level;
                  sf->sp = trace->frame->prev->fp + 4;
                  sf->pc = get_call_pc_i386(trace->frame->prev->ra);
                  sf->funcname = kl_funcname(sf->pc);
                  if (sf->funcname && 
                      strstr(sf->funcname, "kernel_thread")) {
                        sf->ra = 0;
                        sf->fp = saddr - 4;
                        sf->asp = sbp + (STACK_SIZE - 12);
                  } else {
                        sf->fp = saddr - 20;
                        sf->ra = KL_VREAD_PTR(sf->fp);
                        sf->asp = sbp + (STACK_SIZE - (saddr-sf->sp));
                  }
                  sf->frame_size = sf->fp - sf->sp + 4;
                  kl_enqueue((element_t **)&trace->frame, 
                        (element_t *)sf);
            }
      }
}

/*
 * dumptask_trace_i386()
 */
int
dumptask_trace_i386(kaddr_t curtask, int flags, FILE *ofp)
{
      kaddr_t eip, esp, saddr;
      void *tsp;
      trace_t *trace;
      int i;
      kl_dump_header_i386_t dha;

      if(kl_get_dump_header_i386(&dha)){
            /* fixme: set error code */
            return(1);
      }

      for (i = 0; i < dha.smp_num_cpus; i++) {
            if (curtask == (kaddr_t)dha.smp_current_task[i]) {
                  eip = dha.smp_regs[i].eip;
                  esp = dha.smp_regs[i].esp;
                  break;
            }
      }

      tsp = kl_alloc_block(TASK_STRUCT_SZ, K_TEMP);
      if (!tsp) {
            return(1);
      }
      if (kl_get_task_struct(curtask, 2, tsp)) {
            kl_free_block(tsp);
            return(1);
      }
      if (!(trace = alloc_trace_rec(K_TEMP))) {
            fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
      } else {
            saddr = KL_KERNELSTACK_UINT64(curtask);
            setup_trace_rec_i386(saddr, 0, 0, trace);
            find_trace_i386(eip, esp, 0, 0, trace, 0);
            trace_banner(ofp);
            fprintf(ofp, "STACK TRACE FOR TASK: 0x%"FMTPTR
                  "x (%s)\n\n",
                  curtask, (char*)K_PTR(tsp, "task_struct", "comm"));
            print_trace_i386(trace, flags, ofp);
            trace_banner(ofp);
            free_trace_rec(trace);
      }
      return(0);
}

/*
 * trace_init_i386()
 */
int
trace_init_i386(void)
{
      SETUP_TRACE_REC = setup_trace_rec_i386;
      PRINT_TRACE     = print_trace_i386;
      TASK_TRACE      = task_trace_i386;
      PRINT_TRACES    = print_traces_i386;
      FIND_TRACE      = find_trace_i386;
      DUMPTASK_TRACE  = dumptask_trace_i386;
      DO_LIST         = do_list_i386;
      STACK_SEGMENTS  = STACK_SEGMENTS_I386;
      STACK_SIZE      = STACK_SIZE_I386;
      return(0);
}

Generated by  Doxygen 1.6.0   Back to index