[comp.sources.misc] v08i090: cprof Turbo C version

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	|
|____________|__________________________________________________|