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

trace_ia64.c

/*
 * $Id: trace_ia64.c,v 1.4 2005/03/01 07:15:50 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 - 2005 Silicon Graphics, Inc. All rights reserved.
 * Copyright (C) 2001, 2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
 * Copyright (C) 2003 NEC 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 "dis-asm.h"
#include <lcrash.h>
#include "lc_unwind.h"

#define KL_IA64_RBS_OFFSET ((TASK_STRUCT_SZ + 15) & ~15)

typedef struct ia64_stack_s {
      kaddr_t           limit;
      kaddr_t           top;
} ia64_stack_t;

typedef struct ia64_stkinfo_s {
      ia64_stack_t      regstk;     /* register stack */
      ia64_stack_t      memstk;     /* memory stack */
      kaddr_t           ip;     /* Current instruction pointer */
      kaddr_t           ra;   /* Current return address */
      kaddr_t           sp;     /* Current memory stack pointer */
      kaddr_t           bsp;  /* Current (bsp) backing store pointer */
      kaddr_t           cfm;  /* Current CFM */
      kaddr_t           rnat;
} ia64_stkinfo_t;

typedef struct ia64_frame_info_s {
      struct ia64_frame_info_s      *next;
      struct ia64_frame_info_s      *prev;
      kaddr_t                        ip;
      kaddr_t                        call_ip;
      kaddr_t                        ra;
      kaddr_t                        sp;
      kaddr_t                        bsp;
      kaddr_t                        pfs;
} ia64_frame_info_t;

/* Forward Declarations
 */
kaddr_t get_call_ip(kaddr_t);
static ia64_frame_info_t *get_frame_list(kaddr_t);

static int64_t
rse_slot_num(kaddr_t addr)
{
      return((((kaddr_t) addr) >> 3) & 0x3f);
}

static kaddr_t
rse_rnat_addr(kaddr_t slot_addr)
{
      return ((kaddr_t)((kaddr_t)slot_addr | (0x3f << 3)));
}
 
static kaddr_t
rse_skip_regs(kaddr_t addr, int num_regs)
{
      int64_t delta = rse_slot_num(addr) + num_regs;

      if (num_regs < 0) {
            delta -= 0x3e;
      }
      return(addr + ((num_regs + delta/0x3f) * 8));
}

static kaddr_t
rse_read_reg(ia64_stkinfo_t *info, int regnum, int *is_nat, trace_t *trace)
{
      kaddr_t addr, rnat_addr, ret_addr;
      uint64_t rnat, off;
      uaddr_t a;

      addr = rse_skip_regs(info->bsp, regnum);
      if ((addr < info->regstk.limit) || (addr >= info->regstk.top) || 
                  (((uint64_t)addr & 0x7)) != 0) {
            *is_nat = 1;
            return (0xdeadbeefdeadbeef);
      }
      rnat_addr = rse_rnat_addr(addr);
      if (rnat_addr >= info->regstk.top) {
            rnat = info->rnat;
      } else {
            off = (rnat_addr - trace->stack[0].addr);
            a = (uaddr_t)trace->stack[0].ptr + off;
            rnat = *(uint64_t*)a;
      }
      *is_nat = (rnat & (1UL << rse_slot_num(addr))) != 0;
      off = (addr - trace->stack[0].addr);
      a = (uaddr_t)trace->stack[0].ptr + off;
      ret_addr = *(kaddr_t*)a;
      return (ret_addr);
}

/*
 * ia64_find_trace() -- A function that locates kernel task backtraces.
 *                      This is an older implementation that does not rely
 *                      on the use of kernel unwind information. It still
 *                      may have some benefits as it can generate a
 *                      backtrace from an arbitrary point in the stack.
 *                      No information about the memory stack frame is
 *                      available, however.
 */
int 
ia64_find_trace(
      kaddr_t start_pc, 
      kaddr_t start_bsp, 
      kaddr_t pfs,
      trace_t *trace, 
      int flags)
{
      int is_nat, curstkidx = 0, after_trap = 0;
      uint64_t sol, sof;
      kaddr_t cfm, limit, top;
      ia64_stkinfo_t info;
      kaddr_t pc, ra, bsp;
      char *func_name;
      sframe_t *curframe;
      syment_t *symp = (syment_t *)NULL;

      limit = (uint64_t)trace->task + KL_IA64_RBS_OFFSET;
      sol = (pfs >> 7) & 0x7f; /* size of locals */
      top = (uint64_t)rse_skip_regs(start_bsp, sol);
      if (top - (uint64_t)trace->task >= KL_STACK_OFFSET) {
            top = limit;
      }
      memset((void*)&info, 0, sizeof(info));
      info.regstk.limit = limit;
      info.regstk.top = top;
      info.bsp = start_bsp;
      info.cfm = pfs;
      info.ra = start_pc;

      while (1) {
            curframe = alloc_sframe(trace, flags);
            ra = info.ra;
            bsp = info.bsp;
            cfm = info.cfm;

            /* When unwinding the stack, we need to take advantage of
             * the fact that gcc stores the return address (ra) and 
             * pfs values in the last two slots in the register stack 
             * frame. This means that the number of local registers 
             * must be at least 2. If it's less than that, we reached 
             * the end of the C call stack. The possible exception to
             * this is when we are in the first frame after a kernel
             * trap. In such a case, there will be pt_regs data in
             * the dump header and we should try and walk back from
             * there (obviously, this is does not apply to a live 
             * system).
             */
            sol = (cfm >> 7) & 0x7f;        /* size of locals */
            if (sol < 2) {
                  void *regs;

                  if (CORE_IS_KMEM) {
                        break;
                  }
                  regs = K_PTR(KL_DUMP_HEADER_ASM, 
                        "_dump_header_asm_s", "dha_regs");

                  if (kl_kaddr(regs, "pt_regs", "cr_iip")) {
                        /* There has been a trap 
                         */
                        if (after_trap) {
                              /* XXX -- what about nested traps?
                               */
                              break;
                        }
                        pc = kl_kaddr(regs, "pt_regs", "cr_iip");
                        ra = kl_kaddr(regs, "pt_regs", "b0");
                        cfm = kl_kaddr(regs, "pt_regs", "cr_ifs");

                        sol = (cfm >> 7) & 0x7f;        
                        sof = cfm & 0x7f;        
                        bsp = rse_skip_regs(info.bsp, -sof);

                        if (!(symp = kl_lkup_symaddr(pc)) || 
                              ((symp->s_type != SYM_LOCAL_TEXT) &&
                               (symp->s_type != SYM_GLOBAL_TEXT))) {
                              curframe->error = KLE_BAD_PC;
                              UPDATE_FRAME(0, pc, ra, 0, 
                                    0, 0, 0, 0, 0);
                              break;
                        } 
                        func_name = symp->s_name;
                        UPDATE_FRAME(func_name, pc, ra, cfm, 
                              bsp, 0, 0, 0, sol);

                        /* Set up for the next frame
                         */
                        info.ra = ra;
                        info.cfm = kl_kaddr(regs, "pt_regs", "ar_pfs");
                        sol = (info.cfm >> 7) & 0x7f;        
                        info.bsp = rse_skip_regs(bsp, -sol);
                        after_trap = 1;
                        continue;
                  }
                  break;
            } 
            if (!ra) {
                  curframe->error = KLE_BAD_RA;
                  UPDATE_FRAME(0, 0, 0, 0, 0, 0, 0, 0, 0);
                  break;
            }
            if (!(pc = get_call_ip(ra))) {
                  /* XXX -- is this right? */
                  pc = ra;
#ifdef NOT
                  curframe->error = KLE_BAD_RA;
                  UPDATE_FRAME(0, 0, ra, 0, 0, 0, 0, 0, 0);
                  break;
#endif
            }
            if (!(symp = kl_lkup_symaddr(pc)) || 
                        ((symp->s_type != SYM_LOCAL_TEXT) &&
                         (symp->s_type != SYM_GLOBAL_TEXT))) {
                  curframe->error = KLE_BAD_PC;
                  UPDATE_FRAME(0, pc, ra, 0, 0, 0, 0, 0, 0);
                  break;
            } 
            func_name = symp->s_name;

            info.ra = rse_read_reg(&info, sol - 2, &is_nat, trace);
            if (is_nat) {
                  break;
            }

            UPDATE_FRAME(func_name, pc, info.ra, cfm, bsp, 0, 0, 0, sol);

            /* Make sure we have the correct frame size (this frame
             * might contain rnat slot).
             */
            if (curframe->level) {
                  curframe->bsize = (curframe->prev->fp - bsp)/8;
            }

            cfm = rse_read_reg(&info, sol - 1, &is_nat, trace);
            if (is_nat) {
                  break;
            }
            sol = (cfm >> 7) & 0x7f;
            info.cfm = cfm;
            info.bsp = rse_skip_regs(info.bsp, -sol);
      }
      return(0);
}

/*
 * find_trace_ia64() -- This is just a stub routine for ia64.
 *
 */
int
find_trace_ia64(
      kaddr_t start_pc, 
      kaddr_t start_bsp, 
      kaddr_t pfs, 
      kaddr_t dummy,
      trace_t *trace, 
      int flags)
{
      return(ia64_find_trace(start_pc, start_bsp, pfs, trace, flags));
}

/*
 * find_task_trace_ia64()
 */
static int
find_task_trace_ia64(trace_t *trace, int flags)
{
      int ret, level, cpu, intr_frame = 0;
      uint64_t state;
      kaddr_t sw_stk = 0;
      struct unw_frame_info info;
      void *sw;
      switch_stack_t s;
      sframe_t *curframe;
      syment_t *symp = (syment_t *)NULL;

      memset((void*)&info, 0, sizeof(info));

      /* Check to see if this is a live system and this task
       * is runing. If it is, then we can't go on (the switch
       * stack in the task_struct is invalid).
       */
      if (CORE_IS_KMEM) {
            state = KL_UINT(trace->tsp, "task_struct", "state");
            if (state == 0) {
                  /* running task on live system...can't go on!
                   */
                  KL_ERROR = KLE_TASK_RUNNING;
                  return(1);
            }
      } else {
            /* See if this task is running on a cpu. If it is, we have to
             * use the stack and switch_stack pointers stored in the
             * dump_header_asm struct.
             */
            if (SN2_24X) {
                  void *kdp;

                  if ((kdp = kdb_cpu_ptr(trace->task))) {
                        /* The process is running on a cpu
                         */
                        sw_stk = KDB_CPU_SW(kdp);
                  }
            } else {
                  cpu = dha_cpuid_ia64(trace->task);
                  if (cpu >= 0) {
                        sw_stk = dha_stack_ptr_ia64(cpu);
                  }
            } 
      }

      if (!sw_stk) {
            /* The process is blocked. Get the address of the
             * switch_stack from the task thread struct.
             */
            sw_stk = KL_UINT(K_PTR(trace->tsp, "task_struct", "thread"),
                        "thread_struct", "ksp") + 16;
      }

      if (!sw_stk) {
            /* We can't go on from here...
             */
            KL_ERROR = KLE_NO_SWITCH_STACK;
            return(1);
      }

      sw = kl_alloc_block(SWITCH_STACK_SZ, K_PERM);
      GET_BLOCK(sw_stk, SWITCH_STACK_SZ, sw);
      s.addr = sw_stk;
      s.sw = sw;
      ia64_init_frame_info(&info, trace->task, &s);

      level = 0;
      do {
            curframe = alloc_sframe(trace, flags);
            curframe->level = level++;
            if (level > 0) {
                  if (intr_frame) {
                        curframe->pc = info.ip;
                        intr_frame--;
                  } else if (!(curframe->pc = get_call_ip(info.ip))) {
                        curframe->pc = info.ip;
                  }
            } else {
                  curframe->pc = info.ip;
            }
            if (info.flags & UNW_FLAG_INTERRUPT_FRAME) {
                  intr_frame++;
            }
            curframe->sp = info.sp;
            curframe->ra = (uint64_t)
                  KL_VREAD_UINT64((uint64_t)info.rp_loc);
            curframe->bsp = info.bsp;
            curframe->pfs = (uint64_t)
                  KL_VREAD_UINT64((uint64_t)info.pfs_loc);
            if ((symp = kl_lkup_symaddr(curframe->pc))) {
                  curframe->funcname = symp->s_name;
            }
            kl_enqueue((element_t **)&trace->frame, (element_t *)curframe);
            trace->nframes++;
            ret = ia64_unwind(&info);
      } while (ret == 0);

      /* Now go back through the frames and adjust the memory and 
       * register frame sizes and the cfm value.
       * 
       * XXX -- Some work still needed here...
       */
      curframe = trace->frame;
      do {
            if ((curframe->level < (trace->nframes + 1)) &&
                        curframe->sp < curframe->next->sp) {
                  curframe->msize = (curframe->next->sp - curframe->sp)/8;
            }
            if ((curframe->level < (trace->nframes + 1))) {
                  curframe->next->cfm = curframe->pfs;
                  curframe->bsize = (curframe->cfm >> 7) & 0x7f;
            }
            curframe = curframe->next;
      } while (curframe != trace->frame);
      return(0);
}


#define SLOT(pc) (((pc & 0xf) < 6) ? 0 : (((pc & 0xf) < 0xc) ? 1 : 2))
#define OP_CODE(instr) ((instr >> 37) & 0x1f)

/*
 * is_branch() 
 */
int 
is_branch(bfd_byte *bundle, int slot)
{
      unsigned char template;
      uint64_t t0;

      t0 = bfd_getl64(bundle);
      template = (t0 & 0x1f);
      switch (slot) {
            case 0:
                  if ((template == 0x16) || (template == 0x17)) {
                        return(1);
                  }
                  break;
            case 1:
                  if ((template == 0x12) || (template == 0x13) ||
                      (template == 0x16) || (template == 0x17)) {
                        return(1);
                  }
                  break;
            case 2:
                  if (((template >= 0x10) || (template <= 0x13)) ||
                      ((template >= 0x16) || (template <= 0x19)) ||
                      (template == 0x1c) || (template == 0x1d)) {
                        return(1);
                  }
                  break;
      }
      return(0);
}

/*
 * get_call_ip()
 */
kaddr_t
get_call_ip(kaddr_t ra)
{
      unsigned char opcode;
      int count = 20, slot;
      uint64_t t0, t1, slot0, slot1, slot2;
      kaddr_t bundlep, pc, addr = 0;
      bfd_byte bundle[16];
      syment_t *sp;

      /* Check to see if ra is the first instruction of a function.
       * This check is necessary because the ra is replaced by the
       * pointer to another function (e.g., ia64_ret_from_syscall)
       * before the call.
       */
      if ((sp = kl_lkup_symaddr(ra))) {
            if (sp->s_addr == ra) {
                  return(ra);
            }
      }

      addr = ra;
      bundlep = (addr & 0xfffffffffffffff0);
      slot = SLOT(addr);
      if (slot == 0) {
            bundlep -= 16;
            slot = 2;
      }
      GET_BLOCK(bundlep, sizeof(bundle), bundle);
      t0 = bfd_getl64(bundle);
      t1 = bfd_getl64(bundle + 8);
      slot0 = (t0 >>  5) & 0x1ffffffffffLL;
      slot1 = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18);
      slot2 = (t1 >> 23) & 0x1ffffffffffLL;
      while (count) {
            if (is_branch(bundle, slot)) {
                  switch (slot) {
                        case 0:
                              opcode = OP_CODE(slot0);
                              pc = bundlep;
                              break;
                        case 1:
                              opcode = OP_CODE(slot1);
                              pc = bundlep|0x6;
                              break;
                        case 2:
                              pc = bundlep|0xc;
                              opcode = OP_CODE(slot2);
                              break;
                  }
                  if ((opcode == 5) || (opcode == 1)) {
                        /* Call instruction */
                        return(pc);
                  }
            }
            switch(slot) {
                  case 0:
                        bundlep -= 16;
                        GET_BLOCK(bundlep, sizeof(bundle), bundle);
                        t0 = bfd_getl64(bundle);
                        t1 = bfd_getl64(bundle + 8);
                        slot0 = (t0 >>  5) & 0x1ffffffffffLL;
                        slot1 = ((t0 >> 46) & 0x3ffff) |
                                    ((t1 & 0x7fffff) << 18);
                        slot2 = (t1 >> 23) & 0x1ffffffffffLL;
                        slot = 2;
                        break;
                  case 1:
                  case 2:
                        slot--;
                        break;
            }
            count--;
      }
      return(0);
}

/* 
 * setup_trace_rec(_ia64)
 */
int
setup_trace_rec_ia64(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->task = 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);
}

/*
 * pc_offset_ia64()
 */
int
pc_offset_ia64(kaddr_t pc) 
{
      kaddr_t func_addr;
      syment_t *symp;

      if ((func_addr = kl_funcaddr(pc))) {
            return(pc - func_addr);
      }
      /* There are some cases where the function is valid, but 
       * the memory behind the address is not accessible. That's 
       * the for __kernel_syscall_via_break. If the address is 
       * the address of the respective symbol, treat it as valid
       * (without any offset).
       */
      if ((symp = kl_lkup_symaddr(pc))) {
            if (symp->s_addr == pc) {
                  return(0);
            }
      }
      return(-1);
}


/*
 * dump_memory_frame()
 */
void
dump_memory_frame(trace_t *trace, sframe_t *frmp, FILE *ofp)
{
      int i, first_time = 1;
      uint64_t offset;
      kaddr_t sp;
      uint64_t *asp;
      uaddr_t val;

      if (frmp->msize == 0) {
            return;
      }

      sp = frmp->sp;
      offset = (sp - trace->stack[0].addr);
      val = (uaddr_t)trace->stack[0].ptr;
      val += offset;
      asp = (uint64_t*)val;
      fprintf(ofp, "   MEMORY FRAME:\n\n");
      for (i = 0; i < frmp->msize; i++) {
            if (!(i % 2)) {
                  if (first_time) {
                        first_time = 0;
                        fprintf(ofp, "   %"FMTPTR"x: %016"FMTPTR"x  ", 
                              sp, *asp++);
                  } else {
                        fprintf(ofp, "\n   %"FMTPTR"x: ", sp);
                        fprintf(ofp, "%016"FMTPTR"x  ", *asp++);
                  }
                  sp += 16;
            } else  {
                  fprintf(ofp, "%016"FMTPTR"x  ", *asp++);
            }
      }
      if (frmp->msize % 2) {
                fprintf(ofp, "\n");
        }
      fprintf(ofp, "\n\n");
}

/*
 * dump_register_frame()
 */
void
dump_register_frame(trace_t *trace, sframe_t *frmp, FILE *ofp)
{
      int i;
      kaddr_t addr, val;
      uint64_t offset;
      uaddr_t a;

      if (frmp->bsize == 0) {
            return;
      }
      addr = frmp->bsp;
      fprintf(ofp, "   REGISTER FRAME:\n\n");
      for (i = 0; i < frmp->bsize; i++) {
            offset = (addr - trace->stack[0].addr);
            a = (uaddr_t)trace->stack[0].ptr + offset;
            val = *(kaddr_t*)a;
            fprintf(ofp, "   %2d: 0x%016"FMTPTR"x", i, val); 
            if (!((i + 1) % 2)) {
                  fprintf(ofp, "\n");
            }
            addr += 8;
      }
      if (frmp->bsize % 2) {
            fprintf(ofp, "\n");
      }
      fprintf(ofp, "\n");
}

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

      if ((frmp = trace->frame)) {
            do {
                  /* For now, just bail out of the backtrace if there
                   * is an error. It makes for much cleaner looking 
                   * backtraces.
                   */
                  if (frmp->error) {
#ifdef TRACE_DEBUG
                        fprintf(ofp, "TRACE ERROR "
                              "0x%"FMT64"x\n", frmp->error);
#endif
                        break;
                  }
                  if (frmp->flag & FRMFLG_MODNAME) {
                        /* This cold be the name of a
                         * module due to the fact that
                         * the actual function name could
                         * not be determined. Print the
                         * name in angle brackets to 
                         * highlight that it's not a function
                         * name.
                         */
                        fprintf(ofp, "%2d <MODULE:%s>", 
                              frmp->level, frmp->funcname);
                              
                  } else if (!frmp->funcname) {
                        fprintf(ofp, "%2d <ERROR>", frmp->level);
                  } else {
                        fprintf(ofp, "%2d %s", 
                              frmp->level, frmp->funcname);
                  }
                  offset = pc_offset_ia64(frmp->pc);
                  if (offset > 0) {
                        fprintf(ofp, "+0x%x", offset);
                  } else if (offset < 0) {
                        fprintf(ofp, "+<ERROR>");
                  }
                  fprintf(ofp, " [0x%"FMTPTR"x]\n", frmp->pc);
                  if (flags & C_FULL) {

                        fprintf(ofp, "\n");
                        fprintf(ofp, "   RA=0x%"FMTPTR"x, "
                              "PFS=0x%"FMTPTR"x, CFM=0x%"FMTPTR"x\n",
                              frmp->ra, frmp->pfs, frmp->cfm);
                        fprintf(ofp, "   SP=0x%"FMTPTR"x, MSIZE=%d\n",
                              frmp->sp, frmp->msize);
                        fprintf(ofp, "   BSP=0x%"FMTPTR"x, BSIZE=%d\n",
                              frmp->bsp, frmp->bsize);
                        fprintf(ofp, "\n");

                        dump_register_frame(trace, frmp, ofp);
                        dump_memory_frame(trace, frmp, ofp);
                  }
                  frmp = frmp->next;
            } while (frmp != trace->frame);
      }
}

/*
 * task_trace_ia64()
 */
int
task_trace_ia64(kaddr_t task, int flags, FILE *ofp)
{
      int ret;
      kaddr_t saddr;
      trace_t *trace;

      if (!(trace = (trace_t *)alloc_trace_rec(C_TEMP))) {
            fprintf(KL_ERRORFP, "Could not alloc trace rec!\n");
            return(1);
      } else {
            saddr = KL_KERNELSTACK_UINT64(task);
            setup_trace_rec_ia64(saddr, task, 0, trace);
            if (KL_ERROR) {
                  fprintf(KL_ERRORFP, "Error setting up trace rec!\n");
                  free_trace_rec(trace);
                  return(1);
            }
            ret = find_task_trace_ia64(trace, 0);
            trace_banner(ofp);
            fprintf(ofp, "STACK TRACE FOR TASK: ");
            print_kaddr(task, ofp, 0);
            fprintf(ofp, " (%s)\n\n", 
                  (char*)K_PTR(trace->tsp, "task_struct", "comm"));
            if (ret == 0) {
                  PRINT_TRACE(trace, flags, ofp);
            } else {
                  switch (KL_ERROR) {
                        case KLE_TASK_RUNNING:
                              fprintf(KL_ERRORFP, "Task is running "
                                    "on a CPU!\n");
                              break;


                        case KLE_NO_SWITCH_STACK:
                              fprintf(KL_ERRORFP, "Unable to find "
                                    "a switch_stack!\n");
                              break;

                        default:
                              fprintf(KL_ERRORFP, "Error generating "
                                    "stack trace!\n");
                              break;
                  }
            }
      }
      free_trace_rec(trace);
      return(0);
}

/*
 * get_frame_list()
 */
static ia64_frame_info_t *
get_frame_list(kaddr_t task)
{
      uint64_t sol, sof;
      kaddr_t *stackp, *wordp, *endp, pfs;
      kaddr_t limit, addr, sp, bsp;
      ia64_frame_info_t *fip, *frame_info = (ia64_frame_info_t*)NULL;

      stackp = (kaddr_t*)kl_alloc_block(STACK_SIZE, K_TEMP);
      limit = (kaddr_t)task + KL_IA64_RBS_OFFSET;
      GET_BLOCK(task, STACK_SIZE, stackp);
      if (KL_ERROR) {
            kl_free_block(stackp);
            return((ia64_frame_info_t *)NULL);
      }
      wordp = (stackp + ((STACK_SIZE / KL_NBPW) - 1));
      endp = (stackp + ((limit - task) / KL_NBPW));
      while(wordp >= endp) {
            if ((addr = *wordp)) {
                  if (kl_funcname(addr)) {
                        sp = (kaddr_t)(task + 
                              ((uaddr_t)wordp - (uaddr_t)stackp));
                        pfs = (kaddr_t)(*(kaddr_t*)(wordp + 1));
                        if (frame_info) {
                              frame_info->prev = (ia64_frame_info_t*)
                                    kl_alloc_block(sizeof(
                                    ia64_frame_info_t), K_TEMP);
                              frame_info->prev->next = frame_info;
                              frame_info = frame_info->prev;
                        } else {
                              frame_info = (ia64_frame_info_t*)
                                    kl_alloc_block(
                                    sizeof(ia64_frame_info_t), 
                                          K_TEMP);
                        }
                        frame_info->sp = sp;
                        frame_info->ra = addr;
                        frame_info->pfs = pfs;
                  }     
                  wordp--;
            } else {
                  wordp--;
            }
      }
      kl_free_block(stackp);

      /* Walk out to the end of the list (the earliest frame) and
       * then fill in some more detail.
       */
      if ((fip = frame_info)) {
            while (fip->next) {
                  fip = fip->next;
            }
      } else {
            return((ia64_frame_info_t *)NULL);
      }

      while (1) {
            sol = ((fip->pfs >> 7) & 0x7f);
            sof = (fip->pfs & 0x7f);
            fip->call_ip = get_call_ip(fip->ra);
            if (fip->prev) {
                  /* XXX - Need a sanity check to make sure we have a
                   * valid bsp value.
                   */
                  bsp = fip->prev->sp + 16;
                  bsp = rse_skip_regs(bsp, -(sol));
                  fip->bsp = bsp;
            } 
            if (!(fip = fip->prev)) {
                  break;
            }
      }     
      return(frame_info);
}

/*
 * free_frame_info()
 */
static void
free_frame_info(ia64_frame_info_t *frame_info)
{
      ia64_frame_info_t *fip;

      while (frame_info) {
            fip = frame_info->next;
            kl_free_block(frame_info);    
            frame_info = fip;
      }
}

/*
 * _do_list_ia64()
 *
 *   Output a list of all valid code addresses contained in a stack
 *   along with their function name and stack location.
 */
static int
_do_list_ia64(kaddr_t task, FILE *ofp)
{
      uint64_t sol, sof;
      ia64_frame_info_t *fip, *frame_info;

      if (!(frame_info = get_frame_list(task))) {
            return(1);
      }

      /* Walk to the end of the list (the earliest frame).
       */
      fip = frame_info;
      while (fip->next) {
            fip = fip->next;
      }

      while (1) {
            sol = ((fip->pfs >> 7) & 0x7f);
            sof = (fip->pfs & 0x7f);
            fprintf(ofp, "0x%"FMTPTR"x: ra=0x%"FMTPTR"x (%s)\n", 
                  fip->sp, fip->ra, kl_funcname(fip->ra));
            fprintf(ofp, "                  : call_ip=0x%"FMTPTR"x (%s)\n",
                  fip->call_ip, kl_funcname(fip->call_ip));

            fprintf(ofp, "                  : sol=%"FMTPTR"d, "
                  "sof=%"FMTPTR"d, pfs=0x%"FMTPTR"x",
                  sol, sof, fip->pfs);
            if (fip->bsp) {
                  fprintf(ofp, ", bsp=0x%"FMTPTR"x\n", fip->bsp);
            } else {
                  fprintf(ofp, ", bsp=BAD\n");
            }
            if (!(fip = fip->prev)) {
                  break;
            }
      }     
      free_frame_info(frame_info);
      return(0);
}

/*
 * do_list_ia64() -- This is just a stab routine for ia64.
 */
int
do_list_ia64(kaddr_t saddr, int size, FILE *ofp)
{
      return(_do_list_ia64(saddr, ofp));  
}

/*
 * dumptask_trace_ia64()
 */
int
dumptask_trace_ia64(kaddr_t curtask, int flags, FILE *ofp)
{
      TASK_TRACE(curtask, flags, ofp);
      return(0);
}

/*      
 * trace_init_ia64()
 */     
int     
trace_init_ia64(void)
{
        SETUP_TRACE_REC = setup_trace_rec_ia64;
        PRINT_TRACE     = print_trace_ia64; 
        TASK_TRACE      = task_trace_ia64;
        FIND_TRACE      = find_trace_ia64;
        DUMPTASK_TRACE  = dumptask_trace_ia64;
        DO_LIST         = do_list_ia64;
        STACK_SEGMENTS  = STACK_SEGMENTS_IA64;
        STACK_SIZE      = STACK_SIZE_IA64;
        return(0);
}       

Generated by  Doxygen 1.6.0   Back to index