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

trace_x86_64.c

/*
 *
 * 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.
 *
 * Added support for X86_64 architecture Mar 2004
 *      Prashanth Tamraparni (prasht@in.ibm.com)
 *      Sachin Sant (sachinp@in.ibm.com)
 */

#include <lcrash.h>
#include <strings.h>
#include "lc_trace_x86_64.h"
/*
 * function declarations
 */
static int  eframe_type_x86_64(struct kl_pt_regs_x86_64*);
static void print_eframe_x86_64(FILE*, struct kl_pt_regs_x86_64*);
static void get_pt_regs_x86_64(struct kl_pt_regs_x86_64*, void*);

/*
 * function definitions
 */

#if 0
/*
 * print_pc()
 */
void
print_pc(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);
            }
      }
}
#endif

/* 
 * setup_trace_rec_x86_64()
 */
int
setup_trace_rec_x86_64(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_X86_64;

      /* 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_X86_64, aflag);
      if (KL_ERROR) {
            clean_trace_rec(trace);
            return(1);
      }
      GET_BLOCK(trace->stack[0].addr, STACK_SIZE_X86_64, 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_x86_64 struct
 */
static void
get_pt_regs_x86_64(struct kl_pt_regs_x86_64 *regs, void *buf)
{
      uint32_t *tmp = buf;

      regs->rbx = KL_GET_UINT32(tmp);
      regs->rcx = KL_GET_UINT32(++tmp);
      regs->rdx = KL_GET_UINT32(++tmp);
      regs->rsi = KL_GET_UINT32(++tmp);
      regs->rdi = KL_GET_UINT32(++tmp);
      regs->rbp = KL_GET_UINT32(++tmp);
      regs->rax = KL_GET_UINT32(++tmp);
      regs->rsp = KL_GET_UINT32(++tmp);
      regs->xss = KL_GET_UINT32(++tmp);
      regs->xcs = KL_GET_UINT32(++tmp);
      regs->eflags = KL_GET_UINT32(++tmp);
      regs->rip = KL_GET_UINT32(++tmp);
}

#define _KERNEL_EFRAME_X86_64      0
#define _USER_EFRAME_X86_64        1
#define _KERNEL_EFRAME_SZ_X86_64   13     /* no ss and esp */
#define _USER_EFRAME_SZ_X86_64     15
#define _KERNEL_CS_X86_64          0x10
#define _KERNEL_DS_X86_64          0x18
#define _USER_CS_X86_64          0x33
#define _USER_DS_X86_64          0x2B
/* 
 * Check if the exception frame is of kernel or user type 
 * Is checking only DS and CS values sufficient ?
 */
static int
eframe_type_x86_64(struct kl_pt_regs_x86_64 *regs)
{
      if ((regs->xcs & 0xffff) == _KERNEL_CS_X86_64) 
            return _KERNEL_EFRAME_X86_64;
      else if ((regs->xcs & 0xffff) == _USER_CS_X86_64) 
            return _USER_EFRAME_X86_64;

      return -1;
}

static void
print_eframe_x86_64(FILE *ofp, struct kl_pt_regs_x86_64 *regs)
{
      int type = eframe_type_x86_64(regs);

      fprintf(ofp, "   rbx: %016"FMTPTR"x rcx: %016"FMTPTR"x rdx: %016"FMTPTR"x    rsi: %016"FMTPTR"x\n",
            regs->rbx, regs->rcx, regs->rdx, regs->rsi);
      fprintf(ofp, "   rdi: %016"FMTPTR"x rbp: %016"FMTPTR"x rax: %016"FMTPTR"x\n",
            regs->rdi, regs->rbp, regs->rax);
      fprintf(ofp, "    ss: %04"FMTPTR"x     rip: %08"FMTPTR"x  cs: %04"FMTPTR"x "
            "    eflags: %08"FMTPTR"x\n", (regs->xss & 0xffff), regs->rip,
            (regs->xcs & 0xffff), regs->eflags);      

      fprintf(ofp, "    rip: %016"FMTPTR"x     eflags: %016"FMTPTR"x\n", 
            regs->rip, regs->eflags);     
      if (type == _USER_EFRAME_X86_64){
            fprintf(ofp, "   rsp: %016"FMTPTR"x  \n",
                  regs->rsp);
            fprintf(ofp, "   rsp: %08"FMTPTR"x  ss: %04"FMTPTR"x\n",
                  regs->rsp, regs->xss);
      }
}

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

int add_trace_frames_x86_64(
        kaddr_t start_pc,
        kaddr_t start_sp,
        kaddr_t check_pc,
        kaddr_t check_sp,
        trace_t *trace,
        int flags)
{
        syment_t *sym;
      int curstkidx = 0, n=0;
      kaddr_t addr = KL_VREAD_PTR(start_sp);

        if ((sym = kernel_text_address(addr))) {

            if ((n=trace->nframes) > 0) {
                  sframe_t *prevframe = trace->frame;
                  while(--n)   
                        prevframe = prevframe->next;
                  
                  if (prevframe->funcname == sym->s_name) 
                        return -1;
            }
             
            sframe_t *curframe = alloc_sframe(trace, flags);
                  UPDATE_FRAME_X86_64(sym->s_name, addr, 0, start_sp, 0, 0, 0, 0, 0, 0);

                  if (check_pc && ((addr == check_pc) && 
                (start_sp == check_sp))) {
                  return -1;
            }
      }
        return(trace->nframes);
}

/*
 * find_process_trace_x86_64()
 *
 *   Given a starting pc (start_cp), starting stack pointer (start_sp), 
 *   and stack address, check to see if a valid trace is possible. 
 *
 *   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_process_trace_x86_64(
      kaddr_t start_pc, 
      kaddr_t start_sp, 
      kaddr_t check_pc, 
      kaddr_t check_sp,
      trace_t *trace, 
      int flags)
{
      int err=0;
      while(((start_sp + sizeof(void *) -1) & (STACK_SIZE-sizeof(void*)))) {
                        
            err = add_trace_frames_x86_64(start_pc, start_sp, check_pc, 
                              check_sp, trace, flags);

            if(err == -1) {
                  if (flags & C_ALL) {
                        return(trace->nframes);
                  } else {
                        return(0);
                  }
            }

                ((unsigned long *)start_sp)++;
        }
        return(trace->nframes);
}

 
unsigned long check_exception_x86_64(int mycpu, unsigned long stack) {

        syment_t *tss_sp;
      int i;

        if(!(tss_sp = kl_lkup_symname("init_tss"))){
                /* XXX set error code */
                return 0;
        } else {

                unsigned long start_addr=0, start=0, end=0;
                unsigned long my_tss = tss_sp->s_addr +
                                        (mycpu * kl_struct_len("tss_struct"));

                for(i=0; i<NR_EXCEPTION_STACKS_X86_64; i++) {

                        start_addr = (my_tss + kl_member_offset("tss_struct", "ist")) +
                                      (i * kl_member_size("tss_struct", "ist"));
                        if (KL_ERROR)
                                return 0;

                        start = KL_VREAD_PTR(start_addr);
                        if (KL_ERROR)
                                return 0;
                        end = start + EXCEPTION_STACK_SIZE_X86_64;
                        
                  if(stack >= start && stack <= end)
                                return end;
                }
        }
      return 0;
}

kaddr_t find_exception_trace_x86_64(
        int mycpu,
        kaddr_t start_pc,
        kaddr_t start_sp,
        kaddr_t check_pc,
        kaddr_t check_sp,
        trace_t *trace,
        int flags)
{

        kaddr_t estack_end;
      int err;

        if((estack_end = check_exception_x86_64(mycpu, (unsigned long)start_sp))) {

                while(start_sp < estack_end) {

                        err = add_trace_frames_x86_64(start_pc, start_sp, check_pc, 
                                    check_sp, trace, flags);
                  if(err == -1)     
                        break;

                  ((unsigned long *)start_sp)++;
                }

                start_sp = KL_VREAD_PTR(estack_end - 2 * sizeof(estack_end));
        }
        return start_sp;
}

unsigned long check_irq_x86_64(int mycpu, unsigned long stack) {

        syment_t *cpu_pda_sp;
        if(!(cpu_pda_sp = kl_lkup_symname("cpu_pda"))) {
                /* XXX set error code */
                return 0;
        } else {

                unsigned long my_cpu_pda = cpu_pda_sp->s_addr +
                                        (mycpu * kl_struct_len("x8664_pda"));
                unsigned long end_addr=0, start=0, end=0;

                if (KL_ERROR)
                        return 0;

                end_addr = (my_cpu_pda + kl_member_offset("x8664_pda","irqstackptr"));
                end = KL_READ_PTR(end_addr - KL_START_KERNEL_map_X86_64);
                start = (end - IRQ_STACK_SIZE_X86_64 + 64);

                if((stack >= start) && (stack < end))
                        return end;
        }
      return 0;
}

kaddr_t find_irq_trace_x86_64(
        int mycpu,
        kaddr_t start_pc,
        kaddr_t start_sp,
        kaddr_t check_pc,
        kaddr_t check_sp,
        trace_t *trace,
        int flags)
{
        kaddr_t irqstack_end;
      int err;

        if((irqstack_end = check_irq_x86_64(mycpu, (unsigned long)start_sp))) {

                while(start_sp < irqstack_end) {

                        err = add_trace_frames_x86_64(start_pc, start_sp, check_pc,
                                                check_sp, trace,flags);

                  if(err == -1)     
                        break;

                  ((unsigned long *)start_sp++);
                }

                start_sp = KL_VREAD_PTR(irqstack_end - 1 * sizeof(irqstack_end));
        }
        return start_sp;
}

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

      if ((frmp = trace->frame)) {
            do {
                  fprintf(ofp, "%2d %s", frmp->level, 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_X86_64){
                        struct kl_pt_regs_x86_64 regs;
                        get_pt_regs_x86_64(&regs, (void*) frmp->asp);
                        print_eframe_x86_64(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);
                        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_x86_64()
 */
int
task_trace_x86_64(kaddr_t task, int flags, FILE *ofp)
{
      void *tsp;
      kaddr_t saddr, eip=0, esp;
      trace_t *trace;
      int mycpu;

        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 ((mycpu = kl_smp_dumptask_x86_64(task))) {
                  mycpu--;
                  esp = kl_dumprsp_x86_64(task);

                  esp = find_exception_trace_x86_64
                        (mycpu, eip, esp, 0, 0, trace, 0);
                  
                  esp = find_irq_trace_x86_64
                        (mycpu, eip, esp, 0, 0, trace, 0);
            } else {
                  if (LINUX_2_2_X(KL_LINUX_RELEASE)) {
                        esp = KL_UINT(K_PTR(tsp, "task_struct", "tss"),
                                    "thread_struct", "rsp");
                  } else {
                        esp = KL_UINT(
                        K_PTR(tsp, "task_struct", "thread"), 
                              "thread_struct", "rsp");
                  }
            }
            
            if (esp < KL_PAGE_OFFSET ) {
                  fprintf(KL_ERRORFP, "Task in user space, No backtrace\n");
                  return 1;
            } 
            setup_trace_rec_x86_64(saddr, 0, 0, trace);
            if (KL_ERROR) {
                  fprintf(KL_ERRORFP, "Error setting up trace rec!\n");
                  free_trace_rec(trace);
                  return(1);
            }
            find_process_trace_x86_64(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_x86_64(trace, flags, ofp);
      }
      kl_free_block(tsp);
      free_trace_rec(trace);
      return(0);
}

/*
 * trace_init_x86_64()
 */
int
trace_init_x86_64(void)
{
      SETUP_TRACE_REC = setup_trace_rec_x86_64;
      PRINT_TRACE     = print_trace_x86_64;
      TASK_TRACE      = task_trace_x86_64;
      PRINT_TRACES    = NULL;
      FIND_TRACE      = find_process_trace_x86_64;
      DO_LIST         = NULL;
      STACK_SEGMENTS  = STACK_SEGMENTS_X86_64;
      STACK_SIZE      = STACK_SIZE_X86_64;
      return(0);
}

Generated by  Doxygen 1.6.0   Back to index