|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T t
Length: 13464 (0x3498) Types: TextFile Names: »trace.c«
└─⟦a05ed705a⟧ Bits:30007078 DKUUG GNU 2/12/89 └─⟦ff23ba0e6⟧ »./ghostscript-1.3.tar.Z« └─⟦a24a58cd3⟧ └─⟦this⟧ »trace.c«
/* Copyright (C) 1989 Aladdin Enterprises. All rights reserved. Distributed by Free Software Foundation, Inc. This file is part of Ghostscript. Ghostscript is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. Refer to the Ghostscript General Public License for full details. Everyone is granted permission to copy, modify and redistribute Ghostscript, but only under the conditions described in the Ghostscript General Public License. A copy of this license is supposed to have been given to you along with Ghostscript so you can know your rights and responsibilities. It should be in a file named COPYING. Among other things, the copyright notice and this notice must be preserved on all copies. */ /* trace.c */ /* Tracing package for Turbo C */ #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <stdarg.h> #include <dos.h> #include <setjmp.h> typedef unsigned char byte; /* * NOTE: this package is extremely specialized. It works only * with large code models, on 80186 or 80286 processors (or on * V20 processors using the -k1 switch). Use at your own risk. */ /* To trace a procedure, call */ /* trace(proc, "procname", NULL, retsize) */ /* or */ /* trace_name("_PROCNAME", mapfile, NULL, retsize) */ /* where retsize is sizeof the return value type. */ /* To print arguments formatted (with vprintf), use */ /* trace(proc, "procname", "argformat", retsize) */ /* or */ /* trace_name("_PROCNAME", mapfile, "argformat", retsize) */ /* If trace_flush_flag is true, for crash-prone programs, */ /* the trace output is flushed after every call or return. */ /* To map through a symbol (.MAP) file, reset the file, and then call */ /* sym = trace_next_symbol(&segaddr, mapfile) */ /* until it returns NULL. */ /* To look up a symbol in the file, call */ /* segaddr = trace_find_symbol(name, mapfile) */ #ifdef DEBUG /* Main program for testing */ main() { int fib(int); FILE *mapf = fopen("trace.map", "r"); char far *pfib; char far *trace_find_symbol(); pfib = trace_find_symbol("_FIB", mapf); printf("fib at %lx\n", pfib); trace_name("_FIB", mapf, "(%d)", sizeof(int)); fib(5); } int fib(n) int n; { if ( n <= 2 ) return 1; return fib(n-1) + fib(n-2); } /* End of main program */ #endif /* ------ Symbol file mapping ------ */ #define max_sym 40 static char sym[max_sym+1]; char * trace_next_symbol(paddr, mapf) char far **paddr; FILE *mapf; { /* A symbol definition line looks like this: */ /* ssss:oooo namestring */ /* with exactly 7 spaces between the address and the name. */ while ( 1 ) { char ch; unsigned parm[2]; static char term[2] = ": "; int pi, i; if ( (ch = getc(mapf)) != ' ' ) goto skip; for ( pi = 0; pi < 2; pi++ ) { unsigned p = 0; for ( i = 0; i < 4; i++ ) { ch = getc(mapf); if ( !isxdigit(ch) ) goto skip; p = (p << 4) + (isdigit(ch) ? ch - '0' : (ch - 'A' + 10) & 0xf); } if ( (ch = getc(mapf)) != term[pi] ) goto skip; parm[pi] = p; } for ( i = 1; i < 7; i++ ) if ( (ch = getc(mapf)) != ' ' ) goto skip; i = 0; while ( (ch = getc(mapf)) != '\n' ) { if ( i == max_sym ) goto skip; /* name too long */ sym[i++] = ch; } sym[i] = 0; /* Success. Adjust the segment and return. */ *paddr = MK_FP(parm[0] + FP_SEG(abort), parm[1]); return sym; skip: /* Syntax didn't match, skip the rest of the line. */ while ( ch != '\n' ) { if ( ch == (char)EOF && feof(mapf) ) return NULL; ch = getc(mapf); } } } /* Look up a symbol in a file */ char far * trace_find_symbol(name, mapf) char *name; FILE *mapf; { char *s; char far *r; rewind(mapf); while ( (s = trace_next_symbol(&r, mapf)) != NULL ) if ( !strcmp(s, name) ) return r; return (char far *)NULL; } /* ------ Instruction and register set ------ */ /* Opcodes */ #define op_ADD_IMM8 0x83 #define op_ADD_IMM8_X 0 #define op_CALL_FAR 0x9a #define op_CMP_MR 0x39 #define op_CMP_RM 0x3b #define op_ENTER 0xc8 #define op_INC_REG 0x40 #define op_JMP_FAR 0xea #define op_JMP_FAR_IND 0xff #define op_JMP_FAR_IND_X 0x28 #define op_LEAVE 0xc9 #define op_MOV_MR 0x8b #define op_MOV_RM 0x89 #define op_NOP 0x90 #define op_POP_REG 0x58 #define op_PUSH_IMM 0x68 #define op_PUSH_REG 0x50 #define op_RETF 0xcb #define op_SUB_IMM8 0x83 #define op_SUB_IMM8_X 0x28 /* Register numbers */ #define r_AX 0 #define r_CX 1 #define r_DX 2 #define r_BX 3 #define r_SP 4 #define r_BP 5 #define r_SI 6 #define r_DI 7 /* * The standard entry sequence generated by Turbo C consists of: * Optionally, PUSH SI; * if the PUSH SI is present, optionally PUSH DI. * If there are are no arguments and no local variables, nothing; * if there are args but no local variables, PUSH BP, MOV BP,SP; * if there are local variables, ENTER size,0. * If the frame is larger than a certain size (0x80?), * CMP BP,SP; JB .+8. * CMP [_STKLEN],SP; JA <ok>. */ /* ------ Stack parsing ------ */ /* Record for BP and return */ typedef struct bp_ret_s { unsigned _ss *bp; char far *ret; } bp_ret; /* Forward declarations */ char *stack_next_frame(char *); /* Get the address of the caller's frame */ char * stack_top_frame() { jmp_buf buf; setjmp(buf); /* acquire registers */ return stack_next_frame((char *)(char _ss *)buf[0].j_bp); } /* Get the return address of a frame. */ unsigned long stack_return(bp) char *bp; { /* There doesn't seem to be a way to figure out */ /* whether SI and DI have been saved, so we have no way */ /* to tell where the return address really is! */ /* We apply a heuristic test as to whether the return */ /* address is plausible. */ #define is_plausible_return(ptr)\ (FP_SEG(ptr) > 3 && FP_OFF(ptr) > 3) char far *ret; do { ret = *(char far **)(bp + sizeof(char _ss *)); bp += sizeof(short); /* saved register */ } while ( !is_plausible_return(ret) ); return (unsigned long)ret; } /* Get the address of the next higher frame, */ /* or 0 if there is none. */ char * stack_next_frame(bp) char *bp; { char _ss *nbp = *(char _ss **)bp; if ( (char *)nbp < bp ) return 0; return nbp; } /* ------ Dynamic tracing ------ */ /* Flush output flag */ int trace_flush_flag = 0; #define flushout() if ( trace_flush_flag ) fflush(stdout) /* The code sequence that replaces the standard procedure entry: */ typedef struct trace_entry_code_s { byte JMP_FAR; char far *entry; } trace_entry_code; static trace_entry_code trace_entry_template = { op_JMP_FAR }; /* The entry code in the trace record */ typedef struct entry_code_s { byte PUSH_BP; byte MOV_BP_SP, mov_X; byte PUSH_BP2; byte PUSH_seg; unsigned short rec_seg; byte PUSH_off; unsigned short rec_off; /*struct trace_rec_s near **/ byte CALL; char far *print_args; /* print_args moves the old return and BP above the args, */ /* and returns a new fake BP as the value in AX. */ byte MOV_BP_AX, mov_X2; byte INC_SP, INC_SP2; /* pop 1 excess word */ /* The largest possible finishing code is: */ /* PUSH SI, PUSH DI, ENTER size,0, */ /* CMP [_STKLEN],SP, JMP FAR real_entry */ byte finish[15]; } entry_code; static entry_code entry_template = { op_PUSH_REG+r_BP, op_MOV_MR, 0xc0+(r_BP<<3)+r_SP, op_PUSH_REG+r_BP, op_PUSH_IMM, 0, op_PUSH_IMM, 0, op_CALL_FAR, 0L, op_MOV_MR, 0xc0+(r_BP<<3)+r_AX, op_INC_REG+r_SP, op_INC_REG+r_SP }; /* The exit code in the trace record */ typedef struct exit_code_s { byte PUSH_BP; byte MOV_BP_SP, mov_X; byte PUSH_BP2; byte PUSH_DX; byte PUSH_AX; byte PUSH_seg; unsigned short rec_seg; byte PUSH_off; unsigned short rec_off; /*struct trace_rec_s near **/ byte CALL; char far *print_result; /* print_result shuffles things back again */ byte ADD_SP, add_X, c6w; byte MOV_BP_SP2, mov_X2; byte LEAVE; byte RETF; } exit_code; static exit_code exit_template = { op_PUSH_REG+r_BP, op_MOV_MR, 0xc0+(r_BP<<3)+r_SP, op_PUSH_REG+r_BP, op_PUSH_REG+r_DX, op_PUSH_REG+r_AX, op_PUSH_IMM, 0, op_PUSH_IMM, 0, op_CALL_FAR, 0L, op_ADD_IMM8, op_ADD_IMM8_X+0xc0+r_SP, 12, op_MOV_MR, 0xc0+(r_BP<<3)+r_SP, op_LEAVE, op_RETF }; /* Trace information record */ typedef struct trace_rec_s trace_rec; struct trace_rec_s { entry_code entry_code; exit_code exit_code; trace_rec *next; char *name; char *arg_format; int retsize; long count; }; static trace_rec *trace_list = 0; /* Trace a named procedure */ int trace_name(name, mapf, arg_format, retsize) char *name; FILE *mapf; char *arg_format; int retsize; { char far *proc = trace_find_symbol(name, mapf); if ( proc == (char far *)NULL ) return -1; /* name not found */ return trace(proc, name, arg_format, retsize); } /* Trace a procedure */ int trace(proc, name, arg_format, retsize) void (*proc)(); char *name; char *arg_format; int retsize; { char far *pcode = (char far *)proc; char far *pc = pcode; char far *pt; int len; trace_entry_code far *ptrace = (trace_entry_code far *)pcode; bp_ret *trace_print_arguments(); long trace_print_result(); trace_rec *rec = (trace_rec *)malloc(sizeof(trace_rec)); if ( !rec ) return -1; rec->entry_code = entry_template; rec->exit_code = exit_template; /* Compare the procedure's entry sequence against the */ /* standard one. If they differ, we can't trace the procedure. */ pt = (char far *)&rec->entry_code.finish; if ( *pc == op_PUSH_REG+r_SI ) { pc++; if ( *pc == op_PUSH_REG+r_DI ) pc++; } if ( *pc == op_ENTER ) pc += 4; else if ( *pc == op_PUSH_REG+r_BP && pc[1] == op_MOV_MR && pc[2] == 0xc0+(r_BP<<3)+r_SP ) pc += 3; if ( *pc == op_PUSH_REG+r_SI ) { pc++; if ( *pc == op_PUSH_REG+r_DI ) pc++; } if ( *pc == op_CMP_MR && pc[1] == (r_SP<<3)+6 && *(char _ds **)(pc + 2) == (char _ds *)&_stklen ) pc += 4; len = pc - pcode; if ( len < sizeof(trace_entry_code) ) { /* Not enough room for entry code */ free((char *)rec); return -1; } /* There is, unfortunately, no far version of memcpy. */ movedata(FP_SEG(pcode), FP_OFF(pcode), FP_SEG(pt), FP_OFF(pt), len); pt += len; *pt++ = op_JMP_FAR; *(char far **)pt = pc; rec->next = trace_list; rec->name = name; rec->arg_format = arg_format; rec->retsize = retsize; rec->entry_code.rec_seg = FP_SEG(rec); rec->entry_code.rec_off = FP_OFF(rec); rec->entry_code.print_args = (char far *)trace_print_arguments; rec->exit_code.rec_seg = FP_SEG(rec); rec->exit_code.rec_off = FP_OFF(rec); rec->exit_code.print_result = (char far *)trace_print_result; rec->count = 0; trace_list = rec; /* Patch the procedure entry */ *ptrace = trace_entry_template; ptrace->entry = (char far *)&rec->entry_code; return 0; } /* The following routine gets called at the entry */ /* to the traced procedure. It prints the arguments and returns. */ bp_ret * trace_print_arguments(rec, sp) trace_rec far *rec; bp_ret _ss *sp; { bp_ret saved_bp_ret = *sp; char far *retcode = saved_bp_ret.ret; char far *morecode = &rec->exit_code.PUSH_BP; char _ss *args = (char _ss *)(sp + 1); int argsize = 0; int i; bp_ret _ss *above; rec->count++; /* Get the size of the arguments by looking at */ /* the instructions after the return */ if ( retcode[0] == op_ADD_IMM8 && retcode[1] == op_ADD_IMM8_X+0xc0+r_SP ) argsize = retcode[2]; else if ( retcode[0] == op_POP_REG+r_CX ) argsize = (retcode[1] == op_POP_REG+r_CX ? 4 : 2); else if ( retcode[0] == op_MOV_MR && retcode[1] == 0xc0+(r_SP<<3)+r_BP ) argsize = (char _ss *)saved_bp_ret.bp - args; else printf("Unknown calling sequence at %x:%x, abort\n", FP_SEG(retcode), FP_OFF(retcode)), exit(1); printf("%s called from %x:%x with ", rec->name, FP_SEG(retcode), FP_OFF(retcode)); if ( rec->arg_format == NULL ) print_block((int *)args, argsize >> 1); else vprintf(rec->arg_format, (va_list)args); printf("\n"); flushout(); /* Push the arguments down, saving the return and BP above them. */ /* Note that this smashes the arguments of trace_print_arguments. */ memcpy((char *)(args - sizeof(bp_ret)), (char *)args, argsize); args -= sizeof(bp_ret); above = (bp_ret _ss *)(args + argsize); *above = saved_bp_ret; ((char far **)args)[-1] = morecode; return above; } /* Print and return the result */ long trace_print_result(rec, result, sp, bp) trace_rec far *rec; long result; int _ss *sp; bp_ret _ss *bp; { bp_ret temp_bp_ret; printf("%s returns", rec->name); switch ( rec->retsize ) { case 0: break; case 1: printf(" %x", (unsigned char)result); break; case 2: printf(" %x", (unsigned short)result); break; case 3: /* ??? */ case 4: printf(" %lx", result); break; default: { /* We are returning a structure */ printf(" (struct)"); print_block((int *)result, rec->retsize >> 1); } } printf("\n"); flushout(); /* Move the saved BP and return back down below the args. */ /* We can smash the args at this point without concern. */ /* However, since the source and destination may overlap, */ /* and Turbo C isn't smart enough to handle this case, */ /* we have to use an intermediate variable. */ temp_bp_ret = *bp; *(bp_ret *)(sp + 1) = temp_bp_ret; return result; } /* Procedure to print a block of words */ static print_block(ptr, count) int *ptr; int count; { int i; for ( i = 0; i < count; i++ ) printf("%5x", ptr[i]); }