home *** CD-ROM | disk | FTP | other *** search
/ APDL Public Domain 1 / APDL_PD1A.iso / printing / ghostscrip / source / _gs / c / trace < prev    next >
Encoding:
Text File  |  1991-10-26  |  14.4 KB  |  531 lines

  1. /* Copyright (C) 1989, 1990 Aladdin Enterprises.  All rights reserved.
  2.    Distributed by Free Software Foundation, Inc.
  3.  
  4. This file is part of Ghostscript.
  5.  
  6. Ghostscript is distributed in the hope that it will be useful, but
  7. WITHOUT ANY WARRANTY.  No author or distributor accepts responsibility
  8. to anyone for the consequences of using it or for whether it serves any
  9. particular purpose or works at all, unless he says so in writing.  Refer
  10. to the Ghostscript General Public License for full details.
  11.  
  12. Everyone is granted permission to copy, modify and redistribute
  13. Ghostscript, but only under the conditions described in the Ghostscript
  14. General Public License.  A copy of this license is supposed to have been
  15. given to you along with Ghostscript so you can know your rights and
  16. responsibilities.  It should be in a file named COPYING.  Among other
  17. things, the copyright notice and this notice must be preserved on all
  18. copies.  */
  19.  
  20. /* trace.c */
  21. /* Tracing package for Turbo C */
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include <stdlib.h>
  25. #include <stdarg.h>
  26. #include <dos.h>
  27. #include <setjmp.h>
  28. #include "memory_.h"
  29. typedef unsigned char byte;
  30.  
  31. /*
  32.  * NOTE: this package is extremely specialized.  It works only
  33.  * with the Borland Turbo C 2.0 compiler, with large code models,
  34.  * on 80x86 and 80x88 processors and compatibles with x>0.
  35.  * Use at your own risk.
  36.  */
  37.  
  38. /*
  39.  * To trace a procedure, call
  40.  *    trace(proc, "procname", NULL, retsize);
  41.  * or
  42.  *    trace_name("_PROCNAME", mapfile, NULL, retsize);
  43.  * where retsize is sizeof the return value type.
  44.  * To print arguments formatted (with vprintf), use
  45.  *    trace(proc, "procname", "argformat", retsize);
  46.  * or
  47.  *    trace_name("_PROCNAME", mapfile, "argformat", retsize);
  48.  *
  49.  * If trace_flush_flag is true, for crash-prone programs,
  50.  * the trace output is flushed after every call or return.
  51.  *
  52.  * Trace output goes to the file trace_output_file, or to stdout
  53.  * if trace_output_file is not initialized.
  54.  */
  55.  
  56. /*
  57.  * To map through a symbol (.MAP) file, open the file with
  58.  *    mapfile = trace_open_map(filename, &reloc);
  59.  * which returns NULL if the map file isn't available.
  60.  * reloc is set to the relocation offset (i.e., the difference between
  61.  * actual procedure addresses and the values in the map file);
  62.  * if &reloc = NULL, nothing is stored.  Then call
  63.  *    sym = trace_next_symbol(&segaddr, mapfile);
  64.  * until it returns NULL.
  65.  * To look up a symbol in the file, call
  66.  *    segaddr = trace_find_symbol(name, mapfile);
  67.  */
  68.  
  69. #ifdef TRACEDEBUG
  70.  
  71. /* Main program for testing */
  72. main()
  73.    {    int fib(int);
  74.     char far *reloc;
  75.     FILE *trace_open_map();
  76.     FILE *mapf = trace_open_map("trace.map", NULL);
  77.     char far *pfib;
  78.     char far *trace_find_symbol();
  79.     pfib = trace_find_symbol("_FIB", mapf);
  80.     printf("fib at %lx\n", pfib);
  81.     trace_name("_FIB", mapf, "(%d)", sizeof(int));
  82.     fib(5);
  83.    }
  84. int
  85. fib(n)
  86.     int n;
  87.    {    if ( n <= 2 ) return 1;
  88.     return fib(n-1) + fib(n-2);
  89.    }
  90. /* End of main program */
  91.  
  92. #endif
  93.  
  94. /* ------ Symbol file mapping ------ */
  95.  
  96. #define max_sym 40
  97. static char sym[max_sym+1];
  98.  
  99. FILE *
  100. trace_open_map(mapname, preloc)
  101.     char *mapname;
  102.     long *preloc;
  103. {    FILE *mapf = fopen(mapname, "rt");
  104.     char *trace_find_symbol(char *, FILE *);
  105.     if ( mapf == NULL ) return NULL;    /* can't open */
  106.     if ( preloc != NULL )
  107.        {    extern main();
  108.         char far *main_addr = trace_find_symbol("_MAIN", mapf);
  109.         if ( main_addr == NULL )
  110.            {    fclose(mapf);        /* can't find _main */
  111.             return NULL;
  112.            }
  113.         *preloc = (long)main - (long)main_addr;
  114.        }
  115.     return mapf;
  116. }
  117.  
  118. char *
  119. trace_next_symbol(paddr, mapf)
  120.     char far **paddr;
  121.     FILE *mapf;
  122. {    /* A symbol definition line looks like this: */
  123.     /*  ssss:oooo       namestring */
  124.     /* with exactly 7 spaces between the address and the name. */
  125.     while ( 1 )
  126.        {    char ch;
  127.         unsigned parm[2];
  128.         static char term[2] = ": ";
  129.         int pi, i;
  130.         if ( (ch = getc(mapf)) != ' ' ) goto skip;
  131.         for ( pi = 0; pi < 2; pi++ )
  132.            {    unsigned p = 0;
  133.             for ( i = 0; i < 4; i++ )
  134.                {    ch = getc(mapf);
  135.                 if ( !isxdigit(ch) ) goto skip;
  136.                 p = (p << 4) + (isdigit(ch) ? ch - '0' :
  137.                     (ch - 'A' + 10) & 0xf);
  138.                }
  139.             if ( (ch = getc(mapf)) != term[pi] ) goto skip;
  140.             parm[pi] = p;
  141.            }
  142.         for ( i = 1; i < 7; i++ )
  143.             if ( (ch = getc(mapf)) != ' ' ) goto skip;
  144.         i = 0;
  145.         while ( (ch = getc(mapf)) != '\n' )
  146.            {    if ( i == max_sym ) goto skip;    /* name too long */
  147.             sym[i++] = ch;
  148.            }
  149.         sym[i] = 0;
  150.         /* Success.  Adjust the segment and return. */
  151.         *paddr = MK_FP(parm[0] + FP_SEG(abort), parm[1]);
  152.         return sym;
  153. skip:        /* Syntax didn't match, skip the rest of the line. */
  154.         while ( ch != '\n' )
  155.            {    if ( ch == (char)EOF && feof(mapf) ) return NULL;
  156.             ch = getc(mapf);
  157.            }
  158.        }
  159. }
  160.  
  161. /* Look up a symbol in a file */
  162. char far *
  163. trace_find_symbol(name, mapf)
  164.     char *name;
  165.     FILE *mapf;
  166. {    char *s;
  167.     char far *r;
  168.     rewind(mapf);
  169.     while ( (s = trace_next_symbol(&r, mapf)) != NULL )
  170.         if ( !strcmp(s, name) ) return r;
  171.     return (char far *)NULL;
  172. }
  173.  
  174. /* ------ Instruction and register set ------ */
  175.  
  176. /* Opcodes */
  177. #define op_ADD_IMM 0x81
  178. #define op_ADD_IMM8 0x83
  179. #define op_ADD_IMMX_X 0
  180. #define op_CALL_FAR 0x9a
  181. #define op_CMP_MR 0x39
  182. #define op_CMP_RM 0x3b
  183. #define op_ENTER 0xc8
  184. #define op_INC_REG 0x40
  185. #define op_JMP_FAR 0xea
  186. #define op_JMP_FAR_IND 0xff
  187. #define op_JMP_FAR_IND_X 0x28
  188. #define op_MOV_MR 0x8b
  189. #define op_MOV_RM 0x89
  190. #define op_NOP 0x90
  191. #define op_POP_REG 0x58
  192. #define op_PUSH_IMM 0x68
  193. #define op_PUSH_REG 0x50
  194. #define op_RETF 0xcb
  195. #define op_SUB_IMM 0x81
  196. #define op_SUB_IMM8 0x83
  197. #define op_SUB_IMMX_X 0x28
  198. /* Register numbers */
  199. #define r_AX 0
  200. #define r_CX 1
  201. #define r_DX 2
  202. #define r_BX 3
  203. #define r_SP 4
  204. #define r_BP 5
  205. #define r_SI 6
  206. #define r_DI 7
  207.  
  208. /*
  209.  * The standard entry sequence generated by Turbo C consists of:
  210.  *    If there are no arguments and no local variables, nothing;
  211.  *      if there are args but no local variables, PUSH BP, MOV BP,SP;
  212.  *      if there are local variables, ENTER size,0, or
  213.  *        PUSH BP, MOV BP,SP, SUB SP,<size>
  214.  *    Optionally, PUSH SI;
  215.  *      if the PUSH SI is present, optionally PUSH DI.
  216.  *    If the frame is larger than a certain size (0x80?),
  217.  *      CMP BP,SP; JB .+8.
  218.  *    CMP [_STKLEN],SP; JA <ok>.
  219.  */
  220.  
  221. /* ------ Stack parsing ------ */
  222.  
  223. /* Record for BP and return */
  224. typedef struct bp_ret_s {
  225.     unsigned _ss *bp;
  226.     char far *ret;
  227. } bp_ret;
  228.  
  229. /* Forward declarations */
  230. char *stack_next_frame(char *);
  231. void fprint_block(int *, int, FILE *);
  232.  
  233. /* Get the address of the caller's frame */
  234. char *
  235. stack_top_frame()
  236. {    jmp_buf buf;
  237.     setjmp(buf);            /* acquire registers */
  238.     return stack_next_frame((char *)(char _ss *)buf[0].j_bp);
  239. }
  240.  
  241. /* Get the return address of a frame. */
  242. unsigned long
  243. stack_return(bp)
  244.     char *bp;
  245. {    return (unsigned long)*(char far **)(bp + sizeof(char _ss *));
  246. }
  247.  
  248. /* Get the address of the next higher frame, */
  249. /* or 0 if there is none. */
  250. char *
  251. stack_next_frame(bp)
  252.     char *bp;
  253. {    char _ss *nbp = *(char _ss **)bp;
  254.     if ( (char *)nbp < bp ) return 0;
  255.     return nbp;
  256. }
  257.  
  258. /* ------ Dynamic tracing ------ */
  259.  
  260. /* Flush output flag */
  261. int trace_flush_flag = 0;
  262.  
  263. /* The file to use for tracing output */
  264. FILE *trace_output_file = NULL;
  265.  
  266. /* The code sequence that replaces the standard procedure entry: */
  267. typedef struct trace_entry_code_s {
  268.     byte JMP_FAR;
  269.     char far *entry;
  270. } trace_entry_code;
  271. static trace_entry_code trace_entry_template = {
  272.     op_JMP_FAR
  273. };
  274.  
  275. /* The entry code in the trace record */
  276. typedef struct entry_code_s {
  277.     byte PUSH_BP;
  278.     byte MOV_BP_SP, mov_X;
  279.     byte PUSH_BP2;
  280.     byte PUSH_seg; unsigned short rec_seg;
  281.     byte PUSH_off; unsigned short rec_off;    /*struct trace_rec_s near **/
  282.     byte CALL; char far *print_args;
  283.     /* print_args moves the old return and BP above the args, */
  284.     /* and returns a new fake BP as the value in AX. */
  285.     byte MOV_BP_AX, mov_X2;
  286.     byte INC_SP, INC_SP2;        /* pop 1 excess word */
  287.     /* The largest possible finishing code is: */
  288.     /* PUSH BP, MOV SP,BP, SUB SP,size, PUSH SI, PUSH DI, */
  289.     /* CMP [_STKLEN],SP, JMP FAR real_entry */
  290.     byte finish[18];
  291. } entry_code;
  292. static entry_code entry_template = {
  293.     op_PUSH_REG+r_BP,
  294.     op_MOV_MR, 0xc0+(r_BP<<3)+r_SP,
  295.     op_PUSH_REG+r_BP,
  296.     op_PUSH_IMM, 0,
  297.     op_PUSH_IMM, 0,
  298.     op_CALL_FAR, 0L,
  299.     op_MOV_MR, 0xc0+(r_BP<<3)+r_AX,
  300.     op_INC_REG+r_SP, op_INC_REG+r_SP
  301. };
  302. /* The exit code in the trace record */
  303. typedef struct exit_code_s {
  304.     byte PUSH_BP;
  305.     byte MOV_BP_SP, mov_X;
  306.     byte PUSH_BP2;
  307.     byte PUSH_DX;
  308.     byte PUSH_AX;
  309.     byte PUSH_seg; unsigned short rec_seg;
  310.     byte PUSH_off; unsigned short rec_off;    /*struct trace_rec_s near **/
  311.     byte CALL; char far *print_result;
  312.     /* print_result shuffles things back again */
  313.     byte ADD_SP, add_X, c6w;
  314.     byte MOV_BP_SP2, mov_X2;
  315.     byte POP_BP;
  316.     byte RETF;
  317. } exit_code;
  318. static exit_code exit_template = {
  319.     op_PUSH_REG+r_BP,
  320.     op_MOV_MR, 0xc0+(r_BP<<3)+r_SP,
  321.     op_PUSH_REG+r_BP,
  322.     op_PUSH_REG+r_DX,
  323.     op_PUSH_REG+r_AX,
  324.     op_PUSH_IMM, 0,
  325.     op_PUSH_IMM, 0,
  326.     op_CALL_FAR, 0L,
  327.     op_ADD_IMM8, op_ADD_IMMX_X+0xc0+r_SP, 12,
  328.     op_MOV_MR, 0xc0+(r_BP<<3)+r_SP,
  329.     op_POP_REG+r_BP,
  330.     op_RETF
  331. };
  332.  
  333. /* Trace information record */
  334. typedef struct trace_rec_s trace_rec;
  335. struct trace_rec_s {
  336.     entry_code entry_code;
  337.     exit_code exit_code;
  338.     trace_rec *next;
  339.     char *name;
  340.     char *arg_format;
  341.     int retsize;
  342.     long count;
  343. };
  344.  
  345. static trace_rec *trace_list = 0;
  346.  
  347. /* Trace a named procedure */
  348. int
  349. trace_name(name, mapf, arg_format, retsize)
  350.     char *name;
  351.     FILE *mapf;
  352.     char *arg_format;
  353.     int retsize;
  354. {    char far *proc = trace_find_symbol(name, mapf);
  355.     if ( proc == (char far *)NULL ) return -1;    /* name not found */
  356.     return trace(proc, name, arg_format, retsize);
  357. }
  358.  
  359. /* Trace a procedure */
  360. int
  361. trace(proc, name, arg_format, retsize)
  362.     void (*proc)();
  363.     char *name;
  364.     char *arg_format;
  365.     int retsize;
  366. {    char far *pcode = (char far *)proc;
  367.     char far *pc = pcode;
  368.     char far *pt;
  369.     int len;
  370.     trace_entry_code far *ptrace = (trace_entry_code far *)pcode;
  371.     bp_ret *trace_print_arguments();
  372.     long trace_print_result();
  373.     trace_rec *rec = (trace_rec *)malloc(sizeof(trace_rec));
  374.     if ( !rec ) return -1;
  375.     rec->entry_code = entry_template;
  376.     rec->exit_code = exit_template;
  377.     /* Compare the procedure's entry sequence against the */
  378.     /* standard one.  If they differ, we can't trace the procedure. */
  379.     pt = (char far *)&rec->entry_code.finish;
  380.     if ( *pc == op_ENTER )
  381.         pc += 4;
  382.     else if ( *pc == op_PUSH_REG+r_BP && pc[1] == op_MOV_MR &&
  383.             pc[2] == 0xc0+(r_BP<<3)+r_SP )
  384.        {    pc += 3;
  385.         if ( *pc == op_SUB_IMM && pc[1] == op_SUB_IMMX_X+0xc0+r_SP )
  386.             pc += 4;
  387.         else if ( *pc == op_SUB_IMM8 && pc[1] == op_SUB_IMMX_X+0xc0+r_SP )
  388.             pc += 3;
  389.        }
  390.     if ( *pc == op_PUSH_REG+r_SI )
  391.        {    pc++;
  392.         if ( *pc == op_PUSH_REG+r_DI ) pc++;
  393.        }
  394.     if ( *pc == op_CMP_MR && pc[1] == (r_SP<<3)+6 && *(char _ds **)(pc + 2) == (char _ds *)&_stklen )
  395.         pc += 4;
  396.     len = pc - pcode;
  397.     if ( len < sizeof(trace_entry_code) )
  398.        {    /* Not enough room for entry code */
  399.         free((char *)rec);
  400.         return -1;
  401.        }
  402.     /* There is, unfortunately, no far version of memcpy. */
  403.     movedata(FP_SEG(pcode), FP_OFF(pcode), FP_SEG(pt), FP_OFF(pt), len);
  404.     pt += len;
  405.     *pt++ = op_JMP_FAR;
  406.     *(char far **)pt = pc;
  407.     rec->next = trace_list;
  408.     rec->name = name;
  409.     rec->arg_format = arg_format;
  410.     rec->retsize = retsize;
  411.     rec->entry_code.rec_seg = FP_SEG(rec);
  412.     rec->entry_code.rec_off = FP_OFF(rec);
  413.     rec->entry_code.print_args = (char far *)trace_print_arguments;
  414.     rec->exit_code.rec_seg = FP_SEG(rec);
  415.     rec->exit_code.rec_off = FP_OFF(rec);
  416.     rec->exit_code.print_result = (char far *)trace_print_result;
  417.     rec->count = 0;
  418.     trace_list = rec;
  419.     /* Patch the procedure entry */
  420.     *ptrace = trace_entry_template;
  421.     ptrace->entry = (char far *)&rec->entry_code;
  422.     return 0;
  423. }
  424.  
  425. /* Acquire the file to be used for tracing output. */
  426. static FILE *
  427. outfile()
  428. {    return (trace_output_file == NULL ? stdout : trace_output_file);
  429. }
  430.  
  431. /* Flush the output file if appropriate. */
  432. static void
  433. flushout()
  434. {    if ( trace_output_file == NULL || trace_output_file == stdout )
  435.         fflush(stdout);
  436. }
  437.  
  438. /* The following routine gets called at the entry */
  439. /* to the traced procedure.  It prints the arguments and returns. */
  440. bp_ret *
  441. trace_print_arguments(rec, sp)
  442.     trace_rec far *rec;
  443.     bp_ret _ss *sp;
  444. {    bp_ret saved_bp_ret = *sp;
  445.     char far *retcode = saved_bp_ret.ret;
  446.     char far *morecode = &rec->exit_code.PUSH_BP;
  447.     char _ss *args = (char _ss *)(sp + 1);
  448.     int argsize = 0;
  449.     bp_ret _ss *above;
  450.     FILE *f = outfile();
  451.     rec->count++;
  452.     /* Get the size of the arguments by looking at */
  453.     /* the instructions after the return */
  454.     if ( retcode[0] == op_ADD_IMM8 && retcode[1] == op_ADD_IMMX_X+0xc0+r_SP )
  455.         argsize = retcode[2];
  456.     else if ( retcode[0] == op_ADD_IMM && retcode[1] == op_ADD_IMMX_X+0xc0+r_SP )
  457.         argsize = *(int far *)(retcode + 2);
  458.     else if ( retcode[0] == op_INC_REG+r_SP && retcode[1] == op_INC_REG+r_SP )
  459.         argsize = 2;
  460.     else if ( retcode[0] == op_POP_REG+r_CX )
  461.         argsize = (retcode[1] == op_POP_REG+r_CX ? 4 : 2);
  462.     else if ( retcode[0] == op_MOV_MR && retcode[1] == 0xc0+(r_SP<<3)+r_BP )
  463.         argsize = (char _ss *)saved_bp_ret.bp - args;
  464.     else
  465.         printf("Unknown calling sequence at %x:%x, abort\n",
  466.                FP_SEG(retcode), FP_OFF(retcode)),
  467.         exit(1);
  468.     fprintf(f, "%s called from %x:%x with ",
  469.         rec->name, FP_SEG(retcode), FP_OFF(retcode));
  470.     if ( rec->arg_format == NULL )
  471.         fprint_block((int *)args, argsize >> 1, f);
  472.     else
  473.         vfprintf(f, rec->arg_format, (va_list)args);
  474.     fputc('\n', f);
  475.     flushout();
  476.     /* Push the arguments down, saving the return and BP above them. */
  477.     /* Note that this smashes the arguments of trace_print_arguments. */
  478.     memcpy((char *)(args - sizeof(bp_ret)), (char *)args, argsize);
  479.     args -= sizeof(bp_ret);
  480.     above = (bp_ret _ss *)(args + argsize);
  481.     *above = saved_bp_ret;
  482.     ((char far **)args)[-1] = morecode;
  483.     return above;
  484. }
  485.  
  486. /* Print and return the result */
  487. long
  488. trace_print_result(rec, result, sp, bp)
  489.     trace_rec far *rec;
  490.     long result;
  491.     int _ss *sp;
  492.     bp_ret _ss *bp;
  493. {    bp_ret temp_bp_ret;
  494.     FILE *f = outfile();
  495.     fprintf(f, "%s returns", rec->name);
  496.     switch ( rec->retsize )
  497.        {
  498.     case 0: break;
  499.     case 1: fprintf(f, " %x", (unsigned char)result); break;
  500.     case 2: fprintf(f, " %x", (unsigned short)result); break;
  501.     case 3:                /* ??? */
  502.     case 4: fprintf(f, " %lx", result); break;
  503.     default:
  504.        {    /* We are returning a structure */
  505.         fputs(" (struct)", f);
  506.         fprint_block((int *)result, rec->retsize >> 1, f);
  507.        }
  508.        }
  509.     fputc('\n', f);
  510.     flushout();
  511.     /* Move the saved BP and return back down below the args. */
  512.     /* We can smash the args at this point without concern. */
  513.     /* However, since the source and destination may overlap, */
  514.     /* and Turbo C isn't smart enough to handle this case, */
  515.     /* we have to use an intermediate variable. */
  516.     temp_bp_ret = *bp;
  517.     *(bp_ret *)(sp + 1) = temp_bp_ret;
  518.     return result;
  519. }
  520.  
  521. /* Procedure to print a block of words */
  522. static void
  523. fprint_block(ptr, count, f)
  524.     int *ptr;
  525.     int count;
  526.     FILE *f;
  527. {    int i;
  528.     for ( i = 0; i < count; i++ )
  529.         fprintf(f, "%5x", ptr[i]);
  530. }
  531.