/* * (C) 1986 Radical Eye Software. This code may be used and distributed * freely, so long as this notice stays in it and the banner messages * are unchanged except for the version notices. Written by Tomas Rokicki * on December 19 through December 21 1986. * * This is the first part of the profiler. It takes an executable * and a (ln -t) symbol file, and changes all of the links and unlinks * to traps. It also writes a data file for the second part of the * profiler containing sizes of the data segments for the links, the * names of the routines, and the numbers assigned to the routines. * * Usage: p1 filename * * Reads filename (the executable) * filename.sym (the symbol file) * * Writes filename.exe (the executable for profiling) * filename.pdt (the profiler data file) */ #include "stdio.h" /* * Globals. (Yuck!) */ char filename[100] ; FILE *exein, *exeout, *datin, *datout ; long hunk_pos ; long hunk_length ; long proc_address ; char proc_name[255] ; int __exit_seen = 0; int proc_num, assigned = 1 ; char cur_name[100] ; /* * String declarations. */ char *strcat(), *strcpy() ; /* * Main routine. */ main(argc, argv) int argc ; char *argv[] ; { printf("This is p1, v1.0, (C) 1986 Radical Eye Software\n") ; if (argc != 2) { printf("Usage: p1 filename\n") ; exit(1) ; } if ((exein = fopen(argv[1], "r"))==NULL) { printf("Couldn't open %s\n", argv[1]) ; exit(1) ; } if ((datin = fopen(strcat(strcpy(filename, argv[1]), ".sym"), "r"))==NULL) { printf("Couldn't open %s; use ln -t to create\n", filename) ; exit(1) ; } if ((exeout = fopen(strcat(strcpy(filename, argv[1]), ".exe"), "w"))==NULL) { printf("Couldn't open %s for writing\n", filename) ; exit(1) ; } if ((datout = fopen(strcat(strcpy(filename, argv[1]), ".pdt"), "w"))==NULL) { printf("Couldn't open %s for writing\n", filename) ; exit(1) ; } find_code_hunk() ; process_hunk() ; finish_up() ; exit(0) ; } /* * The following routines are used to read from the input executable and * write to the output. The output functions return their value. */ int getword() { register int i = getc(exein) ; hunk_pos += 2 ; return (i << 8) + (getc(exein) & 0xff) ; } long getlong() { register long i = getword() ; return (i << 16) + (getword() & 0xffffL) ; } int putword(i) int i ; { putc(i >> 8, exeout) ; putc(i & 255, exeout) ; return(i) ; } long putlong(i) long i ; { putword((int)(i >> 16)) ; putword((int)(i & 0xffffL)) ; return(i) ; } /* * find_code_hunk() skips over the header information in the executable, * copying it to the output. Note that at the moment we only support * executables which are in one contiguous chunk. */ find_code_hunk() { register long i, j ; int dmy1 ; if (putlong(getlong()) != 0x3f3) error("! expected hunk_header longword") ; /* * Skip over names. */ while ((i=putlong(getlong()))!=0) for (j=0; j") ; } } } /* * error() prints an error message and exits if it was fatal. */ error(s) char *s ; { printf("%s\n", s) ; if (*s=='!') exit(1) ; } /* * Process_hunk() actually does the work for a particular hunk. */ process_hunk() { while (hunk_pos < hunk_length) { if (! search_for_link()) break ; if (get_procedure_name()) { write_data() ; scan_for_unlink() ; } else { printf("Warning: Found a link with no procedure.\n") ; putword(0x4e55) ; } } } /* * search_for_link() searches for a link a5 instruction. */ search_for_link() { int i ; while ((i=getword()) != 0x4e55) { putword(i) ; if (hunk_pos >= hunk_length) return(0) ; } return(1) ; } /* * Get_procedure_name() looks for a procedure which has * the address given. */ get_procedure_name() { while (proc_address < hunk_pos - 2) get_name_and_address() ; if (proc_address != hunk_pos - 2) printf("Procedure %ld hunk pos %ld\n", proc_address, hunk_pos - 2) ; return (proc_address == hunk_pos - 2) ; } /* * We have found a procedure with a link. We write out the * appropriate data to the dataout file, and change the link * instruction to a trap #3. */ write_data() { int data_size = getword() ; if (strcmp(proc_name, "__exit")==0) { __exit_seen = 1 ; proc_num = 0 ; } else proc_num = assigned ++ ; fprintf(datout, "%d %s %d\n", proc_num, proc_name + 1, data_size) ; putword(0x4e43) ; putword(proc_num) ; strcpy(cur_name, proc_name) ; get_name_and_address() ; } /* * Scan_for_unlink() looks through the rest of the procedure * for an unlink a5 followed by an rts. If none are found, it * complains. If more than one is found, the subsequent ones * are ignored (hidden static procedures!). */ scan_for_unlink() { int count = 0 ; int i, j ; while (hunk_pos < proc_address - 2) { i = getword() ; loop: if (i == 0x4e5d) if ((j=getword()) == 0x4e75) { count++ ; putword(0x4e43) ; putword(0x8000 + proc_num) ; break ; } else { putword(i) ; i = j ; goto loop ; } else putword(i) ; } while (hunk_pos < proc_address - 2) putword(getword()) ; if (count != 1) printf("Warning: %d unlinks found in procedure %s\n", count, cur_name) ; } /* * Finish_up() finishes copying the executable file, with no * changes. */ finish_up() { register int i ; while (proc_address < hunk_length) get_name_and_address() ; if (proc_address != hunk_length) printf("The .sym (%ld) and executable (%ld) don't seem to match!\n", proc_address, hunk_length) ; while ((i = getc(exein))!=EOF) putc(i, exeout) ; if (! __exit_seen) error("didn't see a call to __exit()") ; }