allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (10/04/89)
Posting-number: Volume 8, Issue 90 Submitted-by: hjphr@honey.UUCP Archive-name: cprof_tc [...by which time I've switched to my Mac. (No, don't port it there for me! I'm using Pascal instead. ;-) ++bsa] About a month ago, a profiler for Microsoft C was posted to comp.sources.misc and you asked about a Turbo-C version. Well, here it is. I have tested it with Turbo C 2.0 and it works fine. It should still work with MSC, but I had no possibility to test it. Regards, Peter. ---------------------------------------------------------------- /* * A C program profiler. * * (C) Copyright 1988, 1989 Diomidis D. Spinellis. All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by Diomidis Spinellis. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * Author: Diomidis D. Spinellis (dds@cc.ic.ac.uk) * Myrsinis 1 * GR-145 62 Kifisia * GREECE * * Peter J. Holzer (hjphr@ehoney.tuwien.ac.at) * Erlachgasse 70 * A-1100 Wien * AUSTRIA * * Modification history: * * $Log: PROF.C^v $ * Revision 1.1 88/11/20 17:33:16 dds * Initial revision * * Revision 1.2 89-09-03 hjp * Support for Turbo C added * */ #include <stddef.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <dos.h> #ifdef __TURBOC__ #define _dos_getvect(x) getvect(x) #define _dos_setvect(i,f) setvect(i,f) #define onexit(x) atexit(x) #define ENDCODE_SYMB "DATA" #else #define ENDCODE_SYMB "ENDCODE" #endif /* Linker output maximum line length */ #define LINELEN 129 /* Linker output maximum symbol length */ #define STRLEN 65 /* Entries can be absolute or relocatable */ enum relocation { absolute, relocatable }; /* Function prototypes */ static void add_proc(char * name, unsigned long addr, enum relocation rel); static void adjust_proc(long main_offset); static void install_int(void); #ifdef __TURBOC__ static void prof_end(void); #else static int prof_end(void); #endif static void * xmalloc(size_t size); static void * xrealloc(void * buffer, size_t size); static char * strsave(char * string); #ifdef __TURBOC__ static void interrupt far timer_handler ( unsigned bp,unsigned di,unsigned si,unsigned ds,unsigned es, unsigned dx,unsigned cx,unsigned bx,unsigned ax, unsigned ip,unsigned cs,unsigned flags); #else static void interrupt far timer_handler (unsigned es,unsigned ds,unsigned di,unsigned si,unsigned bp, unsigned sp,unsigned bx,unsigned dx,unsigned cx,unsigned ax,unsigned ip,unsigned cs,unsigned flags); #endif void main(int argc, char *argv[]); #ifdef DEBUG static void dump_table(void); static void test_search(void); static void disp(long n); #endif static char rcsid[] = "$Header: PROF.C^v 1.1 88/11/20 17:33:16 dds Rel $"; static char VER[] = "@(#) prof.c 1.2 89-09-03 hjp"; void prof_start(char * argv0) { FILE *f; static char fname[65]; static char line[LINELEN]; static char str1[STRLEN], str2[STRLEN], str3[STRLEN], str4[STRLEN], str5[STRLEN]; enum {searchsegs, scansegs, searchprocs, scanprocs } state; static char *state_errs[] = { "start of segment definitions", ENDCODE_SYMB " segment definition", "the ``Publics by Value'' line", "address greater than " ENDCODE_SYMB " was found" }; unsigned int seg, off; unsigned long endcode; int linenum = 0; unsigned long main_addr, addr; long main_offset = -1; void far * main_p; /* Find the address of main to adjust everything else */ main_p = (void far *)main; main_addr = ((unsigned long)FP_SEG(main_p) << 4) + (unsigned long)FP_OFF(main_p); #ifdef DEBUG printf("main=%08lx\n", main_addr); #endif add_proc("DOS", 0l, absolute); strcpy(fname, argv0); strcpy(strrchr(fname, '.'), ".MAP"); if ((f = fopen(fname, "r")) == NULL) { perror(fname); exit(1); } state = searchsegs; while (fgets(line, LINELEN, f)) { linenum++; if (* line == '\n') continue; /* ignore empty lines */ switch (state) { case searchsegs : if (sscanf(line, " %s %s %s %s %s ", str1, str2, str3, str4, str5) == 5 && strcmp(str1, "Start") == 0 && strcmp(str2, "Stop") == 0 && strcmp(str3, "Length") == 0 && strcmp(str4, "Name") == 0 && strcmp(str5, "Class") == 0) state = scansegs; break; case scansegs : if (sscanf(line, " %lxH %*lxH %*lxH %*s %s ", &endcode, str1) != 2) { fprintf(stderr, "%s(%d) : Unable to parse line : %s\n", fname, linenum, line); exit(1); } if (strcmp(str1, ENDCODE_SYMB) == 0) state = searchprocs; break; case searchprocs : if (sscanf(line, " %s %s %s %s ", str1, str2, str3, str4) == 4 && strcmp(str1, "Address") == 0 && strcmp(str2, "Publics") == 0 && strcmp(str3, "by") == 0 && strcmp(str4, "Value") == 0) state = scanprocs; break; case scanprocs : if (*line == '\n' || sscanf(line, " %x:%x Abs %s ", &seg, &off, str1) == 3) break; if (sscanf(line, " %x:%x %s ", &seg, &off, str1) != 3) { fprintf(stderr, "%s(%d) : Unable to parse line : %s\n", fname, linenum, line); exit(1); } addr = ((unsigned long)seg << 4) + (unsigned long)off; if (strcmp(str1, "_main") == 0) main_offset = addr; add_proc(str1, addr + main_addr, relocatable); if (addr > endcode) { /* * Add here in ascending order any important * memory bounds. One idea would be to partition * the BIOS in tasks e.g. printer, screen etc. */ add_proc("UNKOWN", addr + main_addr + 1, relocatable); add_proc("EGA_BIOS", 0xc0000l, absolute); add_proc("FDISK_BIOS", 0xc8000l, absolute); add_proc("SYSTEM_ROM", 0xf0000l, absolute); add_proc("SYSTEM_BIOS", 0xfe000l, absolute); add_proc("OUTER_SPACE", (unsigned long)-1l, absolute); fclose(f); if (main_offset == -1) { fputs("_main address not found\n", stderr); exit(1); } adjust_proc(main_offset); #ifdef __TURBOC__ if (atexit(prof_end) != 0) { fputs("atexit failed\n", stderr); exit(1); } #else if (onexit(prof_end) == NULL) { fputs("onexit failed\n", stderr); exit(1); } #endif #ifdef DEBUG dump_table(); test_search(); #endif install_int(); return ; } } } /* Something went wrong */ fprintf(stderr, "%s(%d) EOF reached before %s\n", fname, linenum, state_errs[state]); exit(1); } /* The structure where procedures are kept */ static struct proc_data { unsigned long addr ; /* Procedure start address */ unsigned long count ; /* Hit count set by interrupt */ char *name ; /* Procedure name */ enum relocation rel ; /* Relocation type */ } *procs; /* Number of procedures and allocated memory */ static int procnum, procalloc; /* Size of memory allocation chunk */ #define BLK 30 /* Define a procedure */ static void add_proc(char * name, unsigned long addr, enum relocation rel) { if (procs == NULL) { procs = xmalloc(sizeof(struct proc_data) * BLK); procalloc = BLK; } procs[procnum].addr = addr; procs[procnum].count = 0l; procs[procnum].name = strsave(name); procs[procnum].rel = rel; procnum++; if (procnum >= procalloc) { procalloc += BLK; procs = xrealloc(procs, sizeof(struct proc_data) * procalloc); } } /* * Adjust downwards the memory allocated for procedure data storage * and subtract main_offset. */ static void adjust_proc(long main_offset) { struct proc_data *pp; procs = xrealloc(procs, sizeof(struct proc_data) * procnum); for (pp = procs ; pp < &procs[procnum] ; pp++) if (pp->rel == relocatable) pp->addr -= main_offset; } /* Timer interrupt (Do not use 0x1c since it is always called from BIOS) */ #define TIMER_INT 8 /* Old timer handler to chain to */ static void (interrupt far * old_timer_handler)(void); /* Disable timer handler and print the profiling results */ #ifdef __TURBOC__ static void #else static int #endif prof_end(void) { register i; FILE *f; _dos_setvect(TIMER_INT, old_timer_handler); if ((f = fopen("prof.out", "w")) == NULL) { perror("prof.out"); #ifdef __TURBOC__ return; #else return 1; #endif } for (i = 0 ; i < procnum ; i++) fprintf(f, "%s %ld\n", procs[i].name, procs[i].count); fclose(f); #ifdef __TURBOC__ return; #else return 0; #endif } /* Allocate memory with error checking. */ static void * xmalloc(size_t size) { void * p; if ((p = malloc(size)) == NULL) { fputs("Profiler : Out of memory\n", stderr); exit(1); } return p; } /* Reallocate memory with error checking. */ static void * xrealloc(void * buffer, size_t size) { void * p; if ((p = realloc(buffer, size)) == NULL) { fputs("Profiler : Out of memory\n", stderr); exit(1); } return p; } /* Save a string in allocated memory */ static char * strsave(char * string) { return strcpy(xmalloc(strlen(string) + 1), string); } /* The timer interrupt handler */ #ifdef __TURBOC__ static void interrupt far timer_handler(unsigned bp,unsigned di,unsigned si,unsigned ds,unsigned es, unsigned dx,unsigned cx,unsigned bx,unsigned ax, unsigned ip,unsigned cs,unsigned flags) #else static void interrupt far timer_handler(unsigned es,unsigned ds,unsigned di,unsigned si,unsigned bp, unsigned sp,unsigned bx,unsigned dx,unsigned cx,unsigned ax, unsigned ip,unsigned cs,unsigned flags) #endif { long addr; int lower, upper, middle; addr = ((unsigned long)cs << 4) + (unsigned long)ip; #ifdef DEBUG disp(addr); #endif /* * Precondition : * { a[k] < a[k+1] & 0 <= k <= procnum & a[0] <= addr < a[procnum] } */ lower = 0; upper = procnum - 2; /* * Invariant : * { a[l] <= addr < a[u] } * Variant : * { u - l } */ while (upper - lower > 1) { middle = (lower + upper) / 2; /* * m = l + (u - l) / 2 = (u + l) / 2 * m = l + (u - l) / 2 & u - l > 1 implies l < m < u as: * m = (u + l) / 2 < (u + u) / 2 = u implies m < u * m = l + (u - l) / 2 >= l + 1 implies m > l */ if (procs[middle].addr <= addr) lower = middle; else upper = middle; } /* * Postcondition : * { a[f] <= addr < a[f + 1] } which can be expressed as: * { a[l] <= addr < a[u] & u = l + 1 } */ procs[lower].count++; (*old_timer_handler)(); #ifndef __TURBOC__ /* Silence warnings */ (void)(es,ds,di,si,bp,sp,bx,dx,cx,ax,ip,cs,flags); #endif } /* Install the interrupt driver */ static void install_int(void) { old_timer_handler = _dos_getvect(TIMER_INT); _dos_setvect(TIMER_INT, timer_handler); } #ifdef DEBUG /* Very fast display of a number on the screen. (Define MDA for mono adapter) */ #ifdef MDA #define REGEN_BASE 0xb0000000 #else /* CGA */ #define REGEN_BASE 0xb8000000 #endif static void disp(long n) { register i; char far * sb = (char far *)(REGEN_BASE + 20); for (i = 0 ; i < 8 ; i++) { *sb = "0123456789abcdef"[(int)n & 0xF]; n >>= 4; sb -= 2; } } /* Test the binary search algorithm */ static void pr_name(long addr) { int lower, upper, middle; /* * Precondition : * { a[k] < a[k+1] & 0 <= k <= procnum & a[0] <= addr < a[procnum] } */ lower = 0; upper = procnum - 2; /* * Invariant : * { a[l] <= addr < a[u] } * Variant : * { u - l } */ while (upper - lower > 1) { middle = (lower + upper) / 2; /* * m = l + (u - l) / 2 = (u + l) / 2 * m = l + (u - l) / 2 & u - l > 1 implies l < m < u as: * m = (u + l) / 2 < (u + u) / 2 = u implies m < u * m = l + (u - l) / 2 >= l + 1 implies m > l */ if (procs[middle].addr <= addr) lower = middle; else upper = middle; printf("%5d %5d %5d\n", lower, middle, upper); } /* * Postcondition : * { a[f] <= addr < a[f + 1] } which can be expressed as: * { a[l] <= addr < a[u] & u = l + 1 } */ puts(procs[lower].name); } /* Interact with the user testing the search algorithm */ static void test_search() { char buff[80]; long addr; puts("Enter -1 to finish"); do{ gets(buff); sscanf(buff, " %lx ", &addr); pr_name(addr); } while (addr != -1l); } /* Dump the procedure table */ static void dump_table() { struct proc_data *pd; for (pd = procs ; pd < &procs[procnum] ; pd++) printf("%08lx %s\n", pd->addr, pd->name); } #endif ---------------------------------------------------------------- _______________________________________________________________ | __ | | | | | \ | Peter J. Holzer | | |___|__/ | Technische Universitaet Wien | | | | | | | | | | ...!uunet!mcvax!tuvie!asupa!honey!hjphr | | ____/ |--------------------------------------------------| | | Think of it as evolution in action -- Tony Rand | |____________|__________________________________________________|