home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / amiga / os / machsun3.tz / machsun3 / mk.kernel / sun3 / db_trace.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-15  |  15.6 KB  |  602 lines

  1. /* 
  2.  * Mach Operating System
  3.  * Copyright (c) 1992 Carnegie Mellon University
  4.  * All Rights Reserved.
  5.  * 
  6.  * Permission to use, copy, modify and distribute this software and its
  7.  * documentation is hereby granted, provided that both the copyright
  8.  * notice and this permission notice appear in all copies of the
  9.  * software, derivative works or modified versions, and any portions
  10.  * thereof, and that both notices appear in supporting documentation.
  11.  * 
  12.  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
  13.  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
  14.  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
  15.  * 
  16.  * Carnegie Mellon requests users of this software to return to
  17.  * 
  18.  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
  19.  *  School of Computer Science
  20.  *  Carnegie Mellon University
  21.  *  Pittsburgh PA 15213-3890
  22.  * 
  23.  * any improvements or extensions that they make and grant Carnegie Mellon 
  24.  * the rights to redistribute these changes.
  25.  */
  26. /*
  27.  * HISTORY
  28.  * $Log:    db_trace.c,v $
  29.  * Revision 2.5  91/10/09  16:17:23  af
  30.  *     Added parens in initializers for db_regs.
  31.  *     [91/10/07            af]
  32.  * 
  33.  * Revision 2.4  91/07/31  18:13:03  dbg
  34.  *     Stack switching support.
  35.  *     [91/07/12            dbg]
  36.  * 
  37.  * Revision 2.3  91/03/16  14:58:27  rpd
  38.  *     Replaced db_jmpbuf with db_recover.
  39.  *     [91/03/14            rpd]
  40.  * 
  41.  * Revision 2.2  90/08/27  22:11:36  dbg
  42.  *     Import ddb/db_sym.h.
  43.  *     [90/08/21            dbg]
  44.  *     Detect the bad return address at the end of the stack trace.
  45.  *     [90/08/07            dbg]
  46.  *     Rewrote for new debugger.
  47.  *     [90/07/23            dbg]
  48.  * 
  49.  * Revision 2.3  90/05/29  18:38:17  rwd
  50.  *     Don't continue trace when cursym == NULL
  51.  *     [90/05/07            rwd]
  52.  * 
  53.  * Revision 2.2  89/12/08  19:52:49  rwd
  54.  *     Make <thread>$C work correctly
  55.  *     [89/11/28            rwd]
  56.  * 
  57.  * Revision 2.1  89/08/03  16:50:22  rwd
  58.  * Created.
  59.  * 
  60.  * 25-May-89  Randall Dean (rwd) at Carnegie-Mellon University
  61.  *    Added copyright to version originated by dbg.  Fixed frame
  62.  *    tracing to recognize fp=0 as end.
  63.  *
  64.  */
  65.  
  66. #include <mach/boolean.h>
  67. #include <machine/db_machdep.h>
  68. #include <ddb/db_access.h>
  69. #include <ddb/db_sym.h>
  70. #include <ddb/db_variables.h>
  71.  
  72. #include <kern/thread.h>
  73.  
  74. #include <machine/setjmp.h>
  75. extern jmp_buf_t *db_recover;
  76.  
  77. /*
  78.  * Register list
  79.  */
  80. struct db_variable db_regs[] = {
  81.     { "d0",    (int *)&ddb_regs.d0,    FCN_NULL },
  82.     { "d1",    (int *)&ddb_regs.d1,    FCN_NULL },
  83.     { "d2",    (int *)&ddb_regs.d2,    FCN_NULL },
  84.     { "d3",    (int *)&ddb_regs.d3,    FCN_NULL },
  85.     { "d4",    (int *)&ddb_regs.d4,    FCN_NULL },
  86.     { "d5",    (int *)&ddb_regs.d5,    FCN_NULL },
  87.     { "d6",    (int *)&ddb_regs.d6,    FCN_NULL },
  88.     { "d7",    (int *)&ddb_regs.d7,    FCN_NULL },
  89.     { "a0",    (int *)&ddb_regs.a0,    FCN_NULL },
  90.     { "a1",    (int *)&ddb_regs.a1,    FCN_NULL },
  91.     { "a2",    (int *)&ddb_regs.a2,    FCN_NULL },
  92.     { "a3",    (int *)&ddb_regs.a3,    FCN_NULL },
  93.     { "a4",    (int *)&ddb_regs.a4,    FCN_NULL },
  94.     { "a5",    (int *)&ddb_regs.a5,    FCN_NULL },
  95.     { "a6",    (int *)&ddb_regs.a6,    FCN_NULL },
  96.     { "sp",    (int *)&ddb_regs.sp,    FCN_NULL },
  97.     { "pc",    (int *)&ddb_regs.pc,    FCN_NULL },
  98.     { "sr",    (int *)&ddb_regs.sr,    FCN_NULL }
  99. };
  100. struct db_variable *db_eregs = db_regs + sizeof(db_regs)/sizeof(db_regs[0]);
  101.  
  102. #define    MAXINT    0x7fffffff
  103.  
  104. #define    INKERNEL(va)    (((vm_offset_t)(va)) >= VM_MIN_KERNEL_ADDRESS)
  105.  
  106. #define    printf    db_printf
  107. #define    get(addr, space) \
  108.         (db_get_value((db_addr_t)(addr), sizeof(int), FALSE))
  109.  
  110. #define    NREGISTERS    48
  111.  
  112. struct stackpos {
  113.      int    k_pc;
  114.      int    k_fp;
  115.      int    k_nargs;
  116.      int    k_entry;
  117.      int    k_caller;
  118.      int    k_flags;
  119.      int    k_regloc[NREGISTERS];
  120. };
  121. #define FR_SAVFP    0
  122. #define FR_SAVPC    4
  123. #define K_CALLTRAMP    1    /* for k_flags: caller is __sigtramp */
  124. #define K_SIGTRAMP    2    /* for k_flags: this is   __sigtramp */
  125.  
  126. stacktop(regs, sp)
  127.     register struct mc68020_saved_state *regs;
  128.     register struct stackpos *sp;
  129. {
  130.     sp->k_regloc[0]  = (int) ®s->d0;
  131.     sp->k_regloc[1]  = (int) ®s->d1;
  132.     sp->k_regloc[2]  = (int) ®s->d2;
  133.     sp->k_regloc[3]  = (int) ®s->d3;
  134.     sp->k_regloc[4]  = (int) ®s->d4;
  135.     sp->k_regloc[5]  = (int) ®s->d5;
  136.     sp->k_regloc[6]  = (int) ®s->d6;
  137.     sp->k_regloc[7]  = (int) ®s->d7;
  138.     sp->k_regloc[8]  = (int) ®s->a0;
  139.     sp->k_regloc[9]  = (int) ®s->a1;
  140.     sp->k_regloc[10] = (int) ®s->a2;
  141.     sp->k_regloc[11] = (int) ®s->a3;
  142.     sp->k_regloc[12] = (int) ®s->a4;
  143.     sp->k_regloc[13] = (int) ®s->a5;
  144.  
  145.     sp->k_fp = db_get_value((db_addr_t) ®s->a6,
  146.                 sizeof(int), FALSE);
  147.     sp->k_pc = db_get_value((db_addr_t) ®s->pc,
  148.                 sizeof(int), FALSE);
  149.     sp->k_flags = 0;
  150.     findentry( sp );
  151. }
  152.  
  153.  
  154. /*
  155.   The VAX has a very nice calling convention, and it is quite easy to
  156.   find saved registers, and the number of parameters. We are not nearly
  157.   so lucky. We must grub around in code for much of this information
  158.   (remember the PDP-11?), and the saved register list seems to be
  159.   especially hard to find.
  160. */
  161.  
  162. #define HIWORD    0xffff0000
  163. #define LOWORD    0x0000ffff
  164. #define LINKA6    0x4e560000    /* link a6,#x    */
  165. #define ADDLSP    0xdffc0000    /* addl #x,sp    */
  166. #define ADDWSP    0xdefc0000    /* addw #x,sp    */
  167. #define LEASP    0x4fef0000    /* lea    sp@(x),sp*/
  168. #define TSTBSP    0x4a2f0000    /* tstb sp@(x)   */
  169. #define INSMSK    0xfff80000
  170. #define MOVLSP    0x2e800000    /* movl dx,sp@   */
  171. #define MOVLD0    0x20000000    /* movl d0,dx     */
  172. #define MOVLA0    0x20400000    /* movl d0,ax     */
  173. #define MVLMSK    0xf1ff0000
  174. #define MOVEML    0x48d70000    /* moveml #x,sp@ */
  175. #define JSR    0x4eb80000    /* jsr x.[WL]    */
  176. #define JSRPC    0x4eba0000    /* jsr PC@( )    */
  177. #define LONGBIT 0x00010000
  178. #define BSR    0x61000000    /* bsr x     */
  179. #ifdef    mc68020
  180. #define BSRL    0x61ff0000    /* bsrl x     */
  181. #endif    mc68020
  182. #define BYTE3    0x0000ff00
  183. #define LOBYTE    0x000000ff
  184. #define ADQMSK    0xf1ff0000
  185. #define ADDQSP    0x508f0000    /* addql #x,sp   */
  186. #define ADDQWSP    0x504f0000    /* addqw #x,sp   */
  187.  
  188. struct nlist *    trampsym = 0;
  189. struct nlist *    funcsym = 0;
  190.  
  191. nextframe(sp, kerneltrace)
  192.     register struct stackpos *sp;
  193.     int kerneltrace;
  194. {
  195.     int val, regp, i;
  196.     db_addr_t    addr;
  197.     db_addr_t    calladdr;
  198.     register int    instruc;
  199.     db_addr_t    oldfp = sp->k_fp;
  200.  
  201.     /*
  202.      * Find our entry point. Then find out
  203.      * which registers we saved, and map them.
  204.      * Our entry point is the address our caller called.
  205.      */
  206.  
  207.     calladdr = sp->k_caller;
  208.     addr     = sp->k_entry;
  209.     if (sp->k_flags & K_CALLTRAMP){
  210. #if    0
  211.     /* we never set CALLTRAMP */
  212.         /*
  213.          * caller was sigtramp:
  214.          * - no registers were saved;
  215.          * - no new frame-pointer
  216.          * - caller found in sigcontext structure.
  217.          * - WE become sigtramp
  218.          * - we have no parameters
  219.          * MUCH MAGIC USED IN FINDING CALLER'S PC.
  220.          */
  221.         sp->k_pc = sp->k_caller;
  222.         sp->k_entry = trampsym->n_value;
  223.         sp->k_flags = 0;
  224.         addr = get( sp->k_fp+(sizeof(int))*(11), DSP);
  225.         sp->k_nargs = 0;
  226. #if DEBUG
  227.         printf("nextframe: sigcontext at 0x%X, signaled at 0x%X\n",
  228.         addr, sp->k_caller);
  229. #endif
  230.         errflg = 0;
  231. #endif    0
  232.     } else {
  233.         if (addr == MAXINT){
  234.         /* we don't know what registers are involved here--invalidate all */
  235.         for(i=0; i<14; i++){
  236.             sp->k_regloc[i] = -1;
  237.         }
  238.         } else {
  239.         findregs( sp, addr );
  240.         }
  241.         /* find caller's pc and fp */
  242.         sp->k_pc = calladdr;
  243.         sp->k_fp = get(sp->k_fp+FR_SAVFP, DSP);
  244.  
  245.         /* 
  246.           now that we have assumed the identity of our caller, find
  247.           how many longwords of argument WE were called with.
  248.         */
  249.         sp->k_flags = 0;
  250.  
  251.         /*
  252.          *    Don't dig around in user stack to find no. of args
  253.          *    and entry point if just tracing the kernel
  254.          */
  255.         if (kerneltrace && !INKERNEL(sp->k_fp)){
  256.         sp->k_nargs = 0;
  257.         sp->k_entry = MAXINT;
  258.         }
  259.         else
  260.         findentry( sp );
  261.     }
  262.     if (sp->k_fp == 0 || oldfp == sp->k_fp)
  263.         return 0;
  264.     return (sp->k_fp);
  265. }
  266.  
  267. findentry( sp )
  268.     register struct stackpos *sp;
  269.     /* 
  270.       set the k_nargs and k_entry fields in the stackpos structure. This
  271.       is called from stacktop() and from nextframe().
  272.       our caller will do an addq or addl or addw to sp just after
  273.       we return to pop off our arguments. find that instruction
  274.       and extract the value.
  275.     */
  276.     register instruc;
  277.     register val;
  278.     db_addr_t    addr, calladdr, nextword;
  279.     jmp_buf_t    db_jmpbuf;
  280.     jmp_buf_t    *savejmp = db_recover;
  281.  
  282.     if (_setjmp(db_recover = &db_jmpbuf)) {
  283.         /* oops -- we touched something we ought not to have */
  284.         /* cannot trace caller of "start" */
  285.         sp->k_entry = MAXINT;
  286.         sp->k_nargs = 0;
  287.         db_recover = savejmp;
  288.         return;
  289.     }
  290.  
  291.     addr = get(sp->k_fp+FR_SAVPC, DSP);
  292.     if (addr == 0) {
  293.         /* oops -- we touched something we ought not to have */
  294.         /* cannot trace caller of "start" */
  295.         sp->k_entry = MAXINT;
  296.         sp->k_nargs = 0;
  297.         db_recover = savejmp;
  298.         return;
  299.     }
  300.     instruc  = get(addr-6, ISP);
  301.     nextword = get(addr-4, ISP);
  302.  
  303.     db_recover = savejmp;
  304.  
  305.     if ((instruc&HIWORD) == (JSR | LONGBIT)) {
  306.         /* longword offset here */
  307.         sp->k_caller = addr-6;
  308.         sp->k_entry  = nextword;
  309. #ifdef    mc68020
  310.     } else if ((instruc&HIWORD) == BSRL) {
  311.         /* longword self-relative offset */
  312.         sp->k_caller = addr-6;
  313.         sp->k_entry  = nextword + (addr-4);
  314. #endif    mc68020
  315.     } else {
  316.         instruc = nextword;
  317.         if ((instruc&HIWORD) == JSR) {
  318.         /* short word offset */
  319.         sp->k_caller = addr-4;
  320.         sp->k_entry  = instruc & LOWORD;
  321.         } else if ((instruc&HIWORD) == BSR ){
  322.         /* short word, self-relative offset */
  323.         sp->k_caller = addr-4;
  324.         sp->k_entry  = (addr-2) + (short)(instruc & LOWORD);
  325.         } else if ((instruc&HIWORD) == JSRPC ){
  326.         /* PC-relative, short word offset */
  327.         sp->k_caller = addr-4;
  328.         sp->k_entry  = (addr-2) + (instruc & LOWORD);
  329.         } else {
  330.         if((instruc&BYTE3) == (BSR>>16)){
  331.             /* byte, self-relative offset */
  332.             sp->k_caller = addr-2;
  333.             sp->k_entry  = addr + (char)(instruc&LOBYTE);
  334.         } else {
  335.             /* was a call through a proc parameter */
  336.             sp->k_caller = addr-2;
  337.             sp->k_entry  = MAXINT;
  338.             /*
  339.              * we know that sigtramp calls your signal catcher
  340.              * this way -- see if this is the tramp: if so then:
  341.              *   - set the K_CALLTRAMP flag, for use by nextframe();
  342.              *   - take k_entry from __sigfunc array.
  343.              */
  344. #if    0
  345.     /* not in kernel */
  346.             /*
  347.              * the number (9) in the below expression is magic:
  348.              * it is the number of stack items below callee`s fp and
  349.              * sigtramp`s copy of the signal number.
  350.              */
  351.             if (trampsym && (findsym( sp->k_caller, ISYM), cursym == trampsym) ){
  352.             int signl;
  353.             sp->k_flags |= K_CALLTRAMP;
  354.             if (funcsym){
  355.                 signl = get( sp->k_fp+(sizeof (int))*(9), DSP );
  356.                 sp->k_entry = get( funcsym->n_value+(sizeof(int(*)()))*signl, DSP);
  357.             } else {
  358.                 sp->k_entry = -1;
  359.             }
  360.  
  361.             errflg = 0;
  362. #ifdef DEBUG
  363.             printf("Caller is sigtramp: signal is %d: entry is %x\n",
  364.                 signl, sp->k_entry);
  365. #endif
  366.             }
  367. #ifdef DEBUG
  368.             else
  369.             printf("Non-tramp jsr a0@\n");
  370. #endif
  371. #endif    0
  372.         }
  373.         }
  374.     }
  375.     instruc = get(addr, ISP);
  376.     /* on bad days, the compiler dumps a register move here */
  377.     if ((instruc&MVLMSK) == MOVLA0 || (instruc&MVLMSK) == MOVLD0 ){
  378.         instruc = get( addr+= 2, ISP);
  379.     }
  380.     if ((instruc&ADQMSK) == ADDQSP
  381.     ||  (instruc&ADQMSK) == ADDQWSP){
  382.         val = (instruc >> (16+9)) & 07;
  383.         if (val==0)
  384.         val = 8;
  385.     } else if ((instruc&HIWORD) == ADDLSP){
  386.         val = get(addr+2, ISP);
  387.     } else if ((instruc&HIWORD) == ADDWSP || (instruc&HIWORD) == LEASP ){
  388.         val = instruc&LOWORD;
  389.     } else {
  390.         val = 0;
  391.     }
  392.     sp->k_nargs = val/4;
  393.  
  394. }
  395.  
  396. /*
  397.  * look at the procedure prolog of the current called procedure.
  398.  * figure out which registers we saved, and where they are
  399.  */
  400.  
  401. findregs( sp , addr)
  402.     register struct stackpos *sp;
  403.     register db_addr_t addr;
  404. {
  405.     register long instruc, val, i;
  406.     int  regp;
  407.  
  408.     instruc = get( addr, ISP);
  409.     if ( (instruc&HIWORD) != LINKA6 )
  410.     goto nolink;
  411.     if (( instruc &= LOWORD) == 0) {
  412.     /* look for addl */
  413.     instruc = get(( addr += 4), ISP);
  414.     if ( (instruc&HIWORD) == ADDLSP){
  415.         instruc = get( addr+2, ISP);
  416.         addr += 6;
  417.     }
  418.     /* else { frame size is zero } */
  419.     } else {
  420.     /* link offset was non-zero -- sign extend it */
  421.     instruc <<= 16 ; instruc >>= 16;
  422.     }
  423.     /* we now have the negative frame size */
  424.     regp = sp->k_fp + instruc;
  425. nolink:
  426.     /* find which registers were saved */
  427.     /* (expecting probe instruction next) */
  428.     instruc = get( addr, ISP );
  429.     if ((instruc&HIWORD) == TSTBSP)
  430.     addr += 4;
  431.     /* now we expect either a moveml or a movl */
  432.     instruc = get( addr, ISP );
  433.     if ((instruc&INSMSK) == MOVLSP){
  434.     /* only saving one register */
  435.     i = (instruc>>16) & 07;
  436.     sp->k_regloc[i] = regp;
  437.     } else if ((instruc&HIWORD) == MOVEML ) {
  438.     /* saving multiple registers or unoptimized code */
  439.     val = instruc & LOWORD;
  440.     i = 0;
  441.     while (val) {
  442.         if (val & 1) {
  443.         sp->k_regloc[i] = regp;
  444.         regp += sizeof (int);
  445.         }
  446.         val >>= 1;
  447.         i++;
  448.     }
  449.     }
  450.     /* else{ no registers saved } */
  451.  
  452. }
  453.  
  454. /*
  455.  *    Frame tracing.
  456.  */
  457. void
  458. db_stack_trace_cmd(addr, have_addr, count, modif)
  459.     db_expr_t    addr;
  460.     int        have_addr;
  461.     db_expr_t    count;
  462.     char        *modif;
  463. {
  464.     int i, val, nargs, spa;
  465.     db_addr_t    regp;
  466.     char *        name;
  467.     struct stackpos pos;
  468.     boolean_t    kernel_only = TRUE;
  469.     boolean_t    trace_thread = FALSE;
  470.  
  471.     {
  472.         register char *cp = modif;
  473.         register char c;
  474.  
  475.         while ((c = *cp++) != 0) {
  476.         if (c == 't')
  477.             trace_thread = TRUE;
  478.         if (c == 'u')
  479.             kernel_only = FALSE;
  480.         }
  481.     }
  482.  
  483.     if (count == -1)
  484.         count = 65535;
  485.  
  486.     if (!have_addr) {
  487.         stacktop(&ddb_regs, &pos);
  488.     }
  489.     else if (trace_thread) {
  490.         /*
  491.          *    Get stack trace for kernel given thread address.
  492.          */
  493.         register thread_t    th;
  494.         register vm_offset_t stack;
  495.  
  496.         th = (thread_t) addr;
  497.         stack = (vm_offset_t) get(&th->kernel_stack, 0);
  498.         if (stack) {
  499.         /*
  500.          * registers are saved in kernel stack
  501.          */
  502.         pos.k_regloc[0] = 0;
  503.         pos.k_regloc[1] = 0;
  504.         pos.k_regloc[2] = (int) &STACK_MKS(stack)->d2;
  505.         pos.k_regloc[3] = (int) &STACK_MKS(stack)->d3;
  506.         pos.k_regloc[4] = (int) &STACK_MKS(stack)->d4;
  507.         pos.k_regloc[5] = (int) &STACK_MKS(stack)->d5;
  508.         pos.k_regloc[6] = (int) &STACK_MKS(stack)->d6;
  509.         pos.k_regloc[7] = (int) &STACK_MKS(stack)->d7;
  510.         pos.k_regloc[8] = 0;
  511.         pos.k_regloc[9] = 0;
  512.         pos.k_regloc[10] = (int) &STACK_MKS(stack)->a2;
  513.         pos.k_regloc[11] = (int) &STACK_MKS(stack)->a3;
  514.         pos.k_regloc[12] = (int) &STACK_MKS(stack)->a4;
  515.         pos.k_regloc[13] = (int) &STACK_MKS(stack)->a5;
  516.  
  517.         pos.k_fp = db_get_value((db_addr_t) &STACK_MKS(stack)->sp,
  518.                     sizeof(int), FALSE);
  519.         pos.k_pc = db_get_value((db_addr_t) &STACK_MKS(stack)->pc,
  520.                     sizeof(int), FALSE);
  521.         pos.k_flags = 0;
  522.         findentry( &pos );
  523.         }
  524.         else {
  525.         /*
  526.          * Only have user register state.
  527.          */
  528.         register pcb_t    t_pcb;
  529.         register struct mc68020_saved_state *user_regs;
  530.  
  531.         t_pcb = (pcb_t) get(&th->pcb, 0);
  532.         user_regs = (struct mc68020_saved_state *)
  533.                 db_get_value((db_addr_t) &t_pcb->user_regs,
  534.                          sizeof(int), FALSE);
  535.  
  536.         stacktop(user_regs, &pos);
  537.         }
  538.     }
  539.     else {
  540.         pos.k_flags = 0;
  541.         pos.k_fp = addr;
  542.  
  543.         pos.k_nargs = 0;
  544.         pos.k_pc = MAXINT;
  545.         pos.k_entry = MAXINT;
  546.         /* sorry, we cannot find our registers without knowing our pc */
  547.         for( i=0; i<NREGISTERS; pos.k_regloc[i++] = 0) ;
  548.         findentry( &pos );
  549.  
  550.     }
  551.     while (count) {
  552.         count--;
  553.  
  554.         /* HACK */
  555.         if (pos.k_pc == MAXINT) {
  556.         name = "?";
  557.         pos.k_pc = 0;
  558.         }
  559.         else {
  560.         db_find_sym_and_offset(pos.k_pc, &name, &val);
  561.         if (name == 0)
  562.             name = "?";
  563.         }
  564.         printf("%s", name);
  565.         if (pos.k_entry != MAXINT && name) {
  566.         char *    entry_name;
  567.         int    e_val;
  568.  
  569.         db_find_sym_and_offset(pos.k_entry, &entry_name, &e_val);
  570.         if (entry_name != 0 && entry_name != name &&
  571.             e_val != val) {
  572.             printf("(?)\n");
  573.             printf("%s", entry_name);
  574.         }
  575.         }
  576.         printf("(");
  577.         regp = pos.k_fp+FR_SAVFP+4;
  578.         if ((nargs = pos.k_nargs)) {
  579.         while (nargs--) {
  580.             printf("%X", get(regp += 4, DSP));
  581.             if (nargs)
  582.             printf(",");
  583.         }
  584.         }
  585.         if (val == MAXINT)
  586.         printf(") at %X\n", pos.k_pc );
  587.         else
  588.         printf(") + %X\n", val);
  589.  
  590.         /*
  591.          *    Stop tracing if frame ptr no longer points into kernel
  592.          *    stack.
  593.          */
  594.         if (kernel_only && !INKERNEL(pos.k_fp))
  595.         break;
  596.         if (nextframe(&pos, kernel_only) == 0)
  597.         break;
  598.     }
  599. }
  600.  
  601.