root/arch/i386/i386/db_trace.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. db_find_trace_symbols
  2. db_numargs
  3. db_nextframe
  4. db_stack_trace_print

    1 /*      $OpenBSD: db_trace.c,v 1.13 2006/11/28 18:56:17 uwe Exp $       */
    2 /*      $NetBSD: db_trace.c,v 1.18 1996/05/03 19:42:01 christos Exp $   */
    3 
    4 /*
    5  * Mach Operating System
    6  * Copyright (c) 1991,1990 Carnegie Mellon University
    7  * All Rights Reserved.
    8  *
    9  * Permission to use, copy, modify and distribute this software and its
   10  * documentation is hereby granted, provided that both the copyright
   11  * notice and this permission notice appear in all copies of the
   12  * software, derivative works or modified versions, and any portions
   13  * thereof, and that both notices appear in supporting documentation.
   14  *
   15  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
   16  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
   17  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
   18  *
   19  * Carnegie Mellon requests users of this software to return to
   20  *
   21  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
   22  *  School of Computer Science
   23  *  Carnegie Mellon University
   24  *  Pittsburgh PA 15213-3890
   25  *
   26  * any improvements or extensions that they make and grant Carnegie Mellon
   27  * the rights to redistribute these changes.
   28  */
   29 
   30 #include <sys/param.h>
   31 #include <sys/systm.h>
   32 #include <sys/proc.h>
   33 #include <sys/user.h>
   34 
   35 #include <machine/db_machdep.h>
   36 
   37 #include <ddb/db_sym.h>
   38 #include <ddb/db_access.h>
   39 #include <ddb/db_variables.h>
   40 #include <ddb/db_output.h>
   41 #include <ddb/db_interface.h>
   42 
   43 /*
   44  * Machine register set.
   45  */
   46 struct db_variable db_regs[] = {
   47         { "ds",         (long *)&ddb_regs.tf_ds,     FCN_NULL },
   48         { "es",         (long *)&ddb_regs.tf_es,     FCN_NULL },
   49         { "fs",         (long *)&ddb_regs.tf_fs,     FCN_NULL },
   50         { "gs",         (long *)&ddb_regs.tf_gs,     FCN_NULL },
   51         { "edi",        (long *)&ddb_regs.tf_edi,    FCN_NULL },
   52         { "esi",        (long *)&ddb_regs.tf_esi,    FCN_NULL },
   53         { "ebp",        (long *)&ddb_regs.tf_ebp,    FCN_NULL },
   54         { "ebx",        (long *)&ddb_regs.tf_ebx,    FCN_NULL },
   55         { "edx",        (long *)&ddb_regs.tf_edx,    FCN_NULL },
   56         { "ecx",        (long *)&ddb_regs.tf_ecx,    FCN_NULL },
   57         { "eax",        (long *)&ddb_regs.tf_eax,    FCN_NULL },
   58         { "eip",        (long *)&ddb_regs.tf_eip,    FCN_NULL },
   59         { "cs",         (long *)&ddb_regs.tf_cs,     FCN_NULL },
   60         { "eflags",     (long *)&ddb_regs.tf_eflags, FCN_NULL },
   61         { "esp",        (long *)&ddb_regs.tf_esp,    FCN_NULL },
   62         { "ss",         (long *)&ddb_regs.tf_ss,     FCN_NULL },
   63 };
   64 struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
   65 
   66 /*
   67  * Stack trace.
   68  */
   69 #define INKERNEL(va)    (((vaddr_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
   70 
   71 struct i386_frame {
   72         struct i386_frame       *f_frame;
   73         int                     f_retaddr;
   74         int                     f_arg0;
   75 };
   76 
   77 #define NONE            0
   78 #define TRAP            1
   79 #define SYSCALL         2
   80 #define INTERRUPT       3
   81 
   82 db_addr_t       db_trap_symbol_value = 0;
   83 db_addr_t       db_syscall_symbol_value = 0;
   84 db_addr_t       db_kdintr_symbol_value = 0;
   85 boolean_t       db_trace_symbols_found = FALSE;
   86 
   87 void db_find_trace_symbols(void);
   88 int db_numargs(struct i386_frame *);
   89 void db_nextframe(struct i386_frame **, db_addr_t *, int *, int,
   90     int (*pr)(const char *, ...));
   91 
   92 void
   93 db_find_trace_symbols(void)
   94 {
   95         db_expr_t       value;
   96 
   97         if (db_value_of_name("trap", &value))
   98                 db_trap_symbol_value = (db_addr_t) value;
   99         if (db_value_of_name("kdintr", &value))
  100                 db_kdintr_symbol_value = (db_addr_t) value;
  101         if (db_value_of_name("syscall", &value))
  102                 db_syscall_symbol_value = (db_addr_t) value;
  103         db_trace_symbols_found = TRUE;
  104 }
  105 
  106 /*
  107  * Figure out how many arguments were passed into the frame at "fp".
  108  */
  109 int
  110 db_numargs(struct i386_frame *fp)
  111 {
  112         int     *argp;
  113         int     inst;
  114         int     args;
  115         extern char     etext[];
  116 
  117         argp = (int *)db_get_value((int)&fp->f_retaddr, 4, FALSE);
  118         if (argp < (int *)VM_MIN_KERNEL_ADDRESS || argp > (int *)etext) {
  119                 args = 5;
  120         } else {
  121                 inst = db_get_value((int)argp, 4, FALSE);
  122                 if ((inst & 0xff) == 0x59)      /* popl %ecx */
  123                         args = 1;
  124                 else if ((inst & 0xffff) == 0xc483)     /* addl %n, %esp */
  125                         args = ((inst >> 16) & 0xff) / 4;
  126                 else
  127                         args = 5;
  128         }
  129         return (args);
  130 }
  131 
  132 /*
  133  * Figure out the next frame up in the call stack.
  134  * For trap(), we print the address of the faulting instruction and
  135  *   proceed with the calling frame.  We return the ip that faulted.
  136  *   If the trap was caused by jumping through a bogus pointer, then
  137  *   the next line in the backtrace will list some random function as
  138  *   being called.  It should get the argument list correct, though.
  139  *   It might be possible to dig out from the next frame up the name
  140  *   of the function that faulted, but that could get hairy.
  141  */
  142 void
  143 db_nextframe(struct i386_frame **fp, db_addr_t  *ip, int *argp, int is_trap,
  144     int (*pr)(const char *, ...))
  145 {
  146 
  147         switch (is_trap) {
  148             case NONE:
  149                 *ip = (db_addr_t)
  150                         db_get_value((int) &(*fp)->f_retaddr, 4, FALSE);
  151                 *fp = (struct i386_frame *)
  152                         db_get_value((int) &(*fp)->f_frame, 4, FALSE);
  153                 break;
  154 
  155             default: {
  156                 struct trapframe *tf;
  157 
  158                 /* The only argument to trap() or syscall() is the trapframe. */
  159                 tf = (struct trapframe *)argp;
  160                 switch (is_trap) {
  161                 case TRAP:
  162                         (*pr)("--- trap (number %d) ---\n", tf->tf_trapno);
  163                         break;
  164                 case SYSCALL:
  165                         (*pr)("--- syscall (number %d) ---\n", tf->tf_eax);
  166                         break;
  167                 case INTERRUPT:
  168                         (*pr)("--- interrupt ---\n");
  169                         break;
  170                 }
  171                 *fp = (struct i386_frame *)tf->tf_ebp;
  172                 *ip = (db_addr_t)tf->tf_eip;
  173                 break;
  174             }
  175         }
  176 }
  177 
  178 void
  179 db_stack_trace_print(db_expr_t addr, boolean_t have_addr, db_expr_t count,
  180     char *modif, int (*pr)(const char *, ...))
  181 {
  182         struct i386_frame *frame, *lastframe;
  183         int             *argp;
  184         db_addr_t       callpc;
  185         int             is_trap = 0;
  186         boolean_t       kernel_only = TRUE;
  187         boolean_t       trace_thread = FALSE;
  188         boolean_t       trace_proc = FALSE;
  189 
  190 #if 0
  191         if (!db_trace_symbols_found)
  192                 db_find_trace_symbols();
  193 #endif
  194 
  195         {
  196                 char *cp = modif;
  197                 char c;
  198 
  199                 while ((c = *cp++) != 0) {
  200                         if (c == 't')
  201                                 trace_thread = TRUE;
  202                         if (c == 'p')
  203                                 trace_proc = TRUE;
  204                         if (c == 'u')
  205                                 kernel_only = FALSE;
  206                 }
  207         }
  208 
  209         if (count == -1)
  210                 count = 65535;
  211 
  212         if (!have_addr) {
  213                 frame = (struct i386_frame *)ddb_regs.tf_ebp;
  214                 callpc = (db_addr_t)ddb_regs.tf_eip;
  215         } else if (trace_thread) {
  216                 (*pr) ("db_trace.c: can't trace thread\n");
  217         } else if (trace_proc) {
  218                 struct proc *p = pfind((pid_t)addr);
  219                 if (p == NULL) {
  220                         (*pr) ("db_trace.c: process not found\n");
  221                         return;
  222                 }
  223                 frame = (struct i386_frame *)p->p_addr->u_pcb.pcb_ebp;
  224                 callpc = (db_addr_t)
  225                     db_get_value((int)&frame->f_retaddr, 4, FALSE);
  226         } else {
  227                 frame = (struct i386_frame *)addr;
  228                 callpc = (db_addr_t)
  229                          db_get_value((int)&frame->f_retaddr, 4, FALSE);
  230         }
  231 
  232         lastframe = 0;
  233         while (count && frame != 0) {
  234                 int             narg;
  235                 char *  name;
  236                 db_expr_t       offset;
  237                 db_sym_t        sym;
  238 #define MAXNARG 16
  239                 char    *argnames[MAXNARG], **argnp = NULL;
  240 
  241                 sym = db_search_symbol(callpc, DB_STGY_ANY, &offset);
  242                 db_symbol_values(sym, &name, NULL);
  243 
  244                 if (lastframe == 0 && sym == NULL) {
  245                         /* Symbol not found, peek at code */
  246                         int     instr = db_get_value(callpc, 4, FALSE);
  247 
  248                         offset = 1;
  249                         if ((instr & 0x00ffffff) == 0x00e58955 ||
  250                                         /* enter: pushl %ebp, movl %esp, %ebp */
  251                             (instr & 0x0000ffff) == 0x0000e589
  252                                         /* enter+1: movl %esp, %ebp */) {
  253                                 offset = 0;
  254                         }
  255                 }
  256                 if (INKERNEL((int)frame) && name) {
  257                         if (!strcmp(name, "trap")) {
  258                                 is_trap = TRAP;
  259                         } else if (!strcmp(name, "syscall")) {
  260                                 is_trap = SYSCALL;
  261                         } else if (!strncmp(name, "Xintr", 5) ||
  262                             !strncmp(name, "Xresume", 7) ||
  263                             !strncmp(name, "Xstray", 6) ||
  264                             !strncmp(name, "Xhold", 5) ||
  265                             !strncmp(name, "Xrecurse", 8) ||
  266                             !strcmp(name, "Xdoreti") ||
  267                             !strncmp(name, "Xsoft", 5)) {
  268                                 is_trap = INTERRUPT;
  269                         } else
  270                                 goto normal;
  271                         narg = 0;
  272                 } else {
  273                 normal:
  274                         is_trap = NONE;
  275                         narg = MAXNARG;
  276                         if (db_sym_numargs(sym, &narg, argnames))
  277                                 argnp = argnames;
  278                         else
  279                                 narg = db_numargs(frame);
  280                 }
  281 
  282                 (*pr)("%s(", name);
  283 
  284                 if (lastframe == 0 && offset == 0 && !have_addr) {
  285                         /*
  286                          * We have a breakpoint before the frame is set up
  287                          * Use %esp instead
  288                          */
  289                         argp = &((struct i386_frame *)(ddb_regs.tf_esp-4))->f_arg0;
  290                 } else {
  291                         argp = &frame->f_arg0;
  292                 }
  293 
  294                 while (narg) {
  295                         if (argnp)
  296                                 (*pr)("%s=", *argnp++);
  297                         (*pr)("%x", db_get_value((int)argp, 4, FALSE));
  298                         argp++;
  299                         if (--narg != 0)
  300                                 (*pr)(",");
  301                 }
  302                 (*pr)(") at ");
  303                 db_printsym(callpc, DB_STGY_PROC, pr);
  304                 (*pr)("\n");
  305 
  306                 if (lastframe == 0 && offset == 0 && !have_addr) {
  307                         /* Frame really belongs to next callpc */
  308                         lastframe = (struct i386_frame *)(ddb_regs.tf_esp-4);
  309                         callpc = (db_addr_t)
  310                                  db_get_value((int)&lastframe->f_retaddr, 4, FALSE);
  311                         continue;
  312                 }
  313 
  314                 lastframe = frame;
  315                 db_nextframe(&frame, &callpc, &frame->f_arg0, is_trap, pr);
  316 
  317                 if (frame == 0) {
  318                         /* end of chain */
  319                         break;
  320                 }
  321                 if (INKERNEL((int)frame)) {
  322                         /* staying in kernel */
  323                         if (frame <= lastframe) {
  324                                 (*pr)("Bad frame pointer: %p\n", frame);
  325                                 break;
  326                         }
  327                 } else if (INKERNEL((int)lastframe)) {
  328                         /* switch from user to kernel */
  329                         if (kernel_only)
  330                                 break;  /* kernel stack only */
  331                 } else {
  332                         /* in user */
  333                         if (frame <= lastframe) {
  334                                 (*pr)("Bad user frame pointer: %p\n",
  335                                           frame);
  336                                 break;
  337                         }
  338                 }
  339                 --count;
  340         }
  341 
  342         if (count && is_trap != NONE) {
  343                 db_printsym(callpc, DB_STGY_XTRN, pr);
  344                 (*pr)(":\n");
  345         }
  346 }

/* [<][>][^][v][top][bottom][index][help] */