rsalz@uunet.uu.net (Rich Salz) (10/28/89)
Submitted-by: Paul Haahr <haahr@princeton.edu> Posting-number: Volume 20, Issue 82 Archive-name: lcomp [ This is a poor summary of Paul's excellent README; I'm not sure why I think it's so great, but I do. /r$ ] This package is based on Peter Weinberger's "Cheap Dynamic Instruction Counting" from the AT&T Bell Labs Tech Journal Unix(tm) issue of a few years ago (Vol 63, No 8, Oct 1984, pp 1815-1826). These programs were written for a class taught by David Hanson at Princeton University in Spring 1988. (Computer Science 596, Systems Programming Workshop) This code runs on Sun-3s (68020s) and Vaxen (4.3 bsd and ultrix). The files 68020.l and vax.l are lex programs that match the instructions in an assembly program. I did the 68020 port in about an hour. If people are interested, I will maintain a library of machine.l files. paul haahr princeton!haahr haahr@princeton.edu # to unbundle, sh this file # bundled by haahr on elliot at Sun May 8 14:48:31 EDT 1988 echo README 1>&2 sed 's/^-//' > README <<'end of README' -README for lcomp and lprint -cheap dynamic instruction counting - -This package is based on Peter Weinberger's "Cheap Dynamic Instruction -Counting" from the AT&T Bell Labs Tech Journal Unix(tm) issue of a -few years ago (Vol 63, No 8, Oct 1984, pp 1815-1826). These programs -were written for a class taught by David Hanson at Princeton University -in Spring 1988. (Computer Science 596, Systems Programming Workshop) - -This code runs on Sun-3s (68020s) and Vaxen (4.3 bsd and ultrix). Since it -works with the assembly language output of compilers, it needs to make -some assumptions. The files 68020.l and vax.l are lex programs that match -the instructions in an assembly program. For new machines, if the assembler -is a unix-like assembler and the compiler doesn't do too many strange things, -it should be possible to easily change one of the included .l files to work -if you look at the code a bit and know the relevant assembly language. If -people are interested, I will maintain a library of machine.l files. - -To get this working on a vax or a sun, edit the makefile to set TARGET -to 68020 or vax, and change BIN, LIB, and MAN and do a make install. -BIN, LIB, and MAN may not be the current directory. If you do not want -to install the programs, edit lcomp to change the LIB directory to the -current directory. - -See the enclosed manual page (and Weinberger's paper) for how to use -and interpret the results of these programs. - -The interesting thing about this package is the use of lex to drive the -instruction recognizer. I like this approach, and would defend it, -because i did my port to the sun 68020 compiler in less than an hour by -changing the patterns matched and the inserted assembly code (all in -the lex source). Only two small changes had to be made to the machine -independent code (bb.c) to support the sun, but this was just because -of a few vax-centrist assumptions I had made when writing the original code. -See the file INTERNALS for a description of how to write the driver for -a new machine. - -lcomp functions as either cc or f77. I have not found a case where it -breaks. Note that you must use -F when linking fortran .o files if -there are no .f files listed as arguments. - -lprint is modelled on the Ninth Edition manual page for Weinberger's code, -but I wrote it from memory and confused the meaning of all of his -options. Since my lprint and his have different functionality and I -thought my names made more sense for my program, I kept my version of -the option names. - -For a different approach to writing a package like this, see Sun's tcov, -which add profiling code to C source rather than compiler output. - - -This code is wholly within the public domain. It is not copy(right|left)ed -by anyone or any organization. Do with it what you wish. - -Paul Haahr -princeton!haahr (haahr@princeton.edu) end of README echo makefile 1>&2 sed 's/^-//' > makefile <<'end of makefile' -CFLAGS = -O -LIBS = -ll -TARGET = vax -GENDEP = /lib/cpp -M # on ultrix, use /lib/cpp -Em - -SRC = README makefile INTERNALS lcomp.1 lcomp \ - bbexit.c bbfile.c \ - bb.h new.h bool.h \ - bb.c lprint.c ealloc.c efopen.c \ - vax.l 68020.l - -LPRINT = lprint.o ealloc.o efopen.o -BB = bb.o ealloc.o efopen.o $(TARGET).o - -HOME = /u/haahr -BIN = $(HOME)/bin -LIB = $(HOME)/lib/bb -MAN = $(HOME)/lib/man/man1 - -# normal targets - -all : bb bbexit.o bbfile.o lprint - -bb : $(BB) - $(CC) $(CFLAGS) -o bb $(BB) $(LIBS) - -lprint : $(LPRINT) - $(CC) $(CFLAGS) -o lprint $(LPRINT) - - -# c code checking - -# using V8 cyntax -CYNTAX = cyntax -CYN = O -CYNLIB = - -# using sun system V lint -#CYNTAX = /usr/5bin/lint -#CYN = ln -#CYNLIB = -ll - - -CLPRINT = lprint.$(CYN) ealloc.$(CYN) efopen.$(CYN) -CBB = bb.$(CYN) ealloc.$(CYN) efopen.$(CYN) $(TARGET).$(CYN) - -.SUFFIXES: .$(CYN) - -.c.$(CYN): - -$(CYNTAX) $(CFLAGS) -c $*.c - -cyntax : Cbb Clprint bbexit.$(CYN) bbfile.$(CYN) - -Cbb : $(CBB) - -$(CYNTAX) $(CFLAGS) $(CBB) $(CYNLIB) - -Clprint : $(CLPRINT) - -$(CYNTAX) $(CFLAGS) $(CLPRINT) - -$(TARGET).$(CYN) : $(TARGET).l - lex $(LFLAGS) -t $(TARGET).l > $(TARGET).c - -$(CYNTAX) $(CFLAGS) -c $(TARGET).c - rm -f $(TARGET).c - - -# installation procedure - -install : bin lib man -bin : $(BIN)/lprint $(BIN)/lcomp -lib : $(LIB)/bb $(LIB)/bbexit.o $(LIB)/bbfile.o -man : $(MAN)/lcomp.1 - -$(BIN)/lcomp : lcomp - sed 's+^LIB=.*$$+LIB='$(LIB)+ lcomp > $(BIN)/lcomp - -$(BIN)/lprint : lprint - cp lprint $(BIN) - strip $(BIN)/lprint - -$(LIB)/bb : bb - cp bb $(LIB) - strip $(LIB)/bb - -$(LIB)/bbexit.o : bbexit.o - cp bbexit.o $(LIB) - -$(LIB)/bbfile.o : bbfile.o - cp bbfile.o $(LIB) - -$(MAN)/lcomp.1 : lcomp.1 - sed 's+\$$LIB+'$(LIB)+g lcomp.1 > $(MAN)/lcomp.1 - -bundle : $(SRC) - @bundle $(SRC) - -wc : - @wc $(SRC) - -delta : $(SRC) - @echo "message for $?" - @cat > .cimsg - @ci -q -l -m"`cat .cimsg`" $? - @rm -f .cimsg - @touch delta - -clean : - rm -f a.out prof.out bb lprint lex.yy.c makefile.dep *.sL *.[Oos] *% - -depend : - sed '/^# --- cut here ---$$/q' makefile > makefile.dep - for i in *.[cly]; do $(GENDEP) $$i; done >> makefile.dep - mv makefile.dep makefile - -# --- cut here --- -68020.o: 68020.l -68020.o: /usr/include/stdio.h -68020.o: /usr/include/ctype.h -68020.o: /usr/include/string.h -68020.o: ./bool.h -68020.o: ./bb.h -bb.o: bb.c -bb.o: /usr/include/stdio.h -bb.o: /usr/include/ctype.h -bb.o: /usr/include/string.h -bb.o: ./new.h -bb.o: ./bool.h -bb.o: ./bb.h -bbexit.o: bbexit.c -bbexit.o: /usr/include/stdio.h -ealloc.o: ealloc.c -ealloc.o: /usr/include/stdio.h -ealloc.o: ./new.h -ealloc.o: /usr/include/string.h -efopen.o: efopen.c -efopen.o: /usr/include/stdio.h -lprint.o: lprint.c -lprint.o: /usr/include/stdio.h -lprint.o: /usr/include/string.h -lprint.o: ./bool.h -lprint.o: ./new.h -max.o: max.c -panic.o: panic.c -panic.o: /usr/include/stdio.h -vax.o: vax.l -vax.o: /usr/include/stdio.h -vax.o: /usr/include/ctype.h -vax.o: /usr/include/string.h -vax.o: ./bool.h -vax.o: ./bb.h end of makefile echo INTERNALS 1>&2 sed 's/^-//' > INTERNALS <<'end of INTERNALS' -INTERNALS for lcomp -(or how to write a driver for a new instruction set) - -The driver for lcomp (actually, bb) is a lex program which matches the -instruction field of a compiler's assembly output. General machine -code for most architectures would be very hard to write basic block -counting code for, as many things that one wants to be able to find are -hard to identify (for example, can one tell the difference -between a normal label and the start of a functions). Therefore, the -driver for most machines will have to take advantage of the idiosyncratic -nature of compiler output. - -The included drivers both are based on the output from the Portable C -Compiler (PCC). 68020.l was written for the Sun 3; it has been tested -with SunOS 3.3 throught 3.5. vax.l is for Digital Vaxen; it has been -tested on BSD 4.3 and Ultrix 2.0. I have not tried using either with -(and assume they will break on) the output of the GNU C compiler, but I -am sure it would not take long to port from a PCC version to a GNU -version. It would be very difficult to port this code to work with a -compiler that directly generates object code with no provision for -creating assembler source. Sorry. - -My approach for writing a new driver is to start with one of the -existing drivers included in this package and modify it appropriately. -A good way to become familiar with one of these drivers is to examine -some compiler output for a vax or a sun and compare it with the output -of bb for that machine. Reasonable knowledge of the machine you are -working on and it's function call/return protocol is useful if not -necessary for porting the driver. - -The driver reads one file (a .s file generated by the compiler) and -generates two (a .s file with block counting code and a .sL file which -maps basic blocks to line numbers). The generated code, when executed, -appends to a file called prof.out which contains a counter for each -basic block. (The -r option to lcomp and lprint picks a name other -than prof.out. Using an absolute path name can be extremely useful for -profiling a program that is run from a public bin directory, so that -users don't get a prof.out file every time they run the program, and -the developer of the program can get useful profiling data.) - -The prof.out file is a series of entries of the form - <.sL file> <n> - <count for block 0> - <count for block 1> - . - . - . - <count for block n-1> -One such entry is written for each file compiled with lcomp. - -A .sL file has two parts. The first part is a set of n (obtained from -prof.out) lines, one per basic block, each with four whitespace -separated records, containing the sourcefile name (these should all be -the same in any one .sL if one does not make too creative use of the C -preprocessor), first line of the basic block, last line of the block, -and number of instructions in the block. This section is followed by a -line of the form "<m> functions", followed by m lines, one for each -function in the compilation unit (source file). Each function line is -the name of the function and its first basic block. - -The .l file must match instructions and generate code to do the -instruction counting (and link a file's count table with the routine that -prints counts), and write a .sL file. Start with the vax or 68020 code -and modify it appropriately. The following functions are predefined -(in bb.c) to handle common cases: - - passline() -- pass a line through unchanged - inst() -- a normal instruction that does not use - the condition codes and does alter them - safe() -- an instruction that uses the condition codes - or does not change them - branch() -- a jump or branch (conditional or unconditional) - unconditional subroutine call does not go here - stabd() -- handle unix .stabd or .stabn lines - stabs() -- handle unix .stabs lines - function(s, n) -- declare that function named s starts at block n - functionmap() -- write the function map for the end of the .sL file - -stabd() and stabs() read the .stab directives put out by the compiler for -debugger information. They should work with both dbx and sdb style compiler -output. - -The following functions must be provided by the driver: - - labelstartsblock(label) -- return a non-zero value if the named label - could be the target of a branch. if there - is a systematic way of telling that a label - only exists for a debugger, this function - should return false (zero) in that case. - this feature is used in the sun version - increment() -- increment a basic block counter. output - at the beginning of a basic block. - safeincrement() -- same as increment(), but does not change condition - codes. this is normally possible on most machines, - but can require tricky code. - epilogue() -- takes one argument, the name of a map file, - which must be linked with count information into - the counting table. see bbexit.c. the structures - are normally defined here. - - -There's more to it than what I have described here, but the best way to -figure it out is to try to write a driver for some machine. I'll be -glad to answer questions on how to write a driver. I will also be -willing to keep an archive of drivers that other people have written -for other machines, if there is interest. - -paul haahr -princeton!haahr haahr@princeton.edu end of INTERNALS echo lcomp.1 1>&2 sed 's/^-//' > lcomp.1 <<'end of lcomp.1' -.TH LCOMP 1 "15 April 1988" -.SH NAME -lcomp, lprint \- statement and instruction counting -.SH SYNOPSIS -.B lcomp -[ -.B \-r -.I prof.out -file -] -[ -.B \-FC -] -.I cc -or -.I f77 -arguments ... -.PP -.B lprint -[ -.B \-blaipfc -] -[ -.B \-r -.I prof.out -file -] [ srcfile ] ... -.SH DESCRIPTION -.LP -.I Lcomp -compiles a C or \s-2FORTRAN\s0 program into an executable that produces -instruction counts. \fILcomp\fP behaves similarly to -.I cc -and \fIf77\fP, -and accepts the same arguments and options as those programs, except that: -.TP -.B \-C -All named .c files are C++ programs and should be compiled with -.I CC -instead of -.I cc . -.TP -.B \-F -Link with \s-2FORTRAN\s0 libraries. Implied if a named .f file is included, -but necessary if only object files are listed. -.TP -.BI \-r " file" -Generate code that puts the output in the named file, rather than -\fIprof.out\fP. -.LP -For each .c or .f file named, a .sL file is created, which maps -basic blocks to line numbers. The source is compiled into a .o -file which is instrumented for counting basic blocks. -.LP -In the executable file, the function \fI_bbdump\fP(), which is called -automatically be \fIexit\fP(), writes the -.I prof.out -file. \fI_bbdump\fP() clears it's data structures, so it is safe to -call from a signal handler (as long as it is safe to I/O from a signal -handler on your machine). This is useful for gethering statistics on -a program that is not supposed to exit. -.LP -.I Lprint -analyzes the results of executing a program compiled with -.I lcomp . -The default behavior is to print an execution count in the left margin -of each named file for the first basic block on that line. -If no files are listed on the command line, -all that were executed are printed. -.SH OPTIONS -.TP -.B \-b -Print counts for basic blocks (default, unless one of -.B \-ipf -is selected). -.TP -.B \-l -Print counts for all lines, not just the first line of each basic block. -.TP -.B \-a -Print all basic blocks, not just the first one from a line. -.TP -.B \-i -Print instruction counts. If a basic block is not executed, the -number of instructions in that block is printed in parentheses. -.TP -.B \-f -Summarize by file. For instructions and basic blocks, the number executed, -the total number in the file, and the number not executed are printed. -.TP -.B \-p -Summarize by function. As \fB-f\fP, but the number of times each -function is called is also printed. -.TP -.B \-c -Compresses the -.I prof.out -file. The file grows every time the profile program is executed. -.TP -.BI \-r " file" -Read counting data from the named file, instead of \fIprof.out\fP. -.SH "EXAMPLES" -.ta 2.0i -.nf -$ lcomp file.c # compile with counting code -$ a.out # generate instruction counts -$ lprint file.c # print statement counts for file.c -.PP -$ lcomp -o prog *.f # compile all fortran programs in directory -$ prog ... # generate instruction counts -$ lprint -fp # print summary information for all files -.PP -$ make "CC=lcomp" # tell make to use count generating compiler -.fi -.SH "SEE ALSO" -cc(1), f77(1), prof(1), gprof(1), exit(2) -.br -tcov(1) in SunOS -.PP -Peter J. Weinberger, ``Cheap Dynamic Instruction Counting,'' -in \fIAT&T Bell Laboratories Technical Journal\fP, -Volume 63, No. 8, October 1984. -.SH FILES -.PD 0 -.TP 1.5i -.I file .sL -mapping from basic blocks to lines and functions -.TP -.I prof.out -default output file for counts -.TP -$LIB/bb -assembly language post-processor to insert statement counts -.TP -$LIB/bbexit.o -exit routine for statement counting -.TP -$LIB/bbfile.o -contains name of file to dump to, normally -.I prof.out -.PD -.SH BUGS -.LP -The analyzed program must call -.I exit (2), -either explictly or implictly by returning from -\fImain\fP() for the coverage information to be written to the -.I prof.out -file. -.LP -The order that \fIlprint\fP prints files in if no names are -mentioned on the command line is determinate but not too useful. -.LP -Profiling the kernel is possible, but some code must be written -to grab the data out of kernel memory, -rather than using \fI_bbdump\fP(). end of lcomp.1 echo lcomp 1>&2 sed 's/^-//' > lcomp <<'end of lcomp' -#! /bin/sh - -LIB=$HOME/cs/596/lcomp -PATH=$LIB:$PATH -export PATH -CC=cc - -CFILES= -FFILES= -SFILES= -OFILES= -RMFILES= - -LIBS= -FLIBS= -PROF=prof.out -LINK=true -EXEC=a.out -FFLAGS='-u _MAIN_' -FORTRAN='-lF77 -lI77 -lU77 -lm' - -while [ $# != 0 ] -do - case "$1" in - *.c) CFILES="$CFILES $1" ;; - *.[fF]) FFILES="$FFILES $1" ; FLIBS="$FORTRAN" ;; - *.[sS]) SFILES="$SFILES $1" ;; - *.o) OFILES="$OFILES $1" ;; - -r) shift ; PROF=$1 ;; - -C) CC=CC ;; - -F) FLIBS="$FORTRAN" ;; - -O) ;; - -c) LINK=false ;; - -o) shift ; EXEC=$1 ;; - -l*|*.a)LIBS="$LIBS $1" ;; - -*) FLAGS="$FLAGS $1" ;; - esac - shift -done - -for i in $CFILES -do - file=`echo $i | sed 's/\.c$//'` - $CC -g -S $FLAGS $file.c || exit $? - bb $file.s || exit $? - SFILES="$SFILES $file.s" - RMFILES="$RMFILES $file.s" -done - -for i in $FFILES -do - file=`echo $i | sed 's/\.[fF]$//'` - f77 -g -S $FLAGS $i || exit $? - bb $file.s || exit $? - SFILES="$SFILES $file.s" - RMFILES="$RMFILES $file.s" -done - -for i in $SFILES -do - file=`echo $i | sed 's/\.[sS]$//'` - $CC -c $FLAGS $i || exit $? - OFILES="$OFILES $file.o" -done - -if $LINK -then - if [ "$PROF" = prof.out ] - then - OFILES="$OFILES $LIB/bbfile.o" - else - echo "char *_bbfile = \"$PROF\";" > $$bbfile.c - $CC -c $$bbfile.c - OFILES="$OFILES $$bbfile.o" - RMFILES="$RMFILES $$bbfile.c $$bbfile.o" - fi - - if [ "$FLIBS" != "" ] - then - FLAGS="$FLAGS $FFLAGS" - fi - $CC -o $EXEC $FLAGS $OFILES $LIB/bbexit.o $LIBS $FLIBS || exit $? -fi -rm -f $RMFILES end of lcomp chmod +x lcomp echo bbexit.c 1>&2 sed 's/^-//' > bbexit.c <<'end of bbexit.c' -/* bbexit.c -- exit routine for basic block counting */ - -#include <stdio.h> - -typedef struct Entry Entry; -struct Entry { - Entry *next; - int len, *counts; - char *mapfile; -}; - -Entry *_bblist = NULL; -extern char *_bbfile; - -extern char *malloc(); - -_bbdump() -{ - Entry *e; - FILE *fp = fopen(_bbfile, "a"); - if (fp == NULL) { - fprintf(stderr, "couldn't open %s\n", _bbfile); - return; - } - for (e = _bblist; e != NULL; e = e->next) { - int i; - fprintf(fp, "%s %d\n", e->mapfile, e->len); - for (i = 0; i < e->len; i++) { - fprintf(fp, "%d\n", e->counts[i]); - e->counts[i] = 0; - } - } - fclose(fp); -} - -exit(status) - int status; -{ - _bbdump(); - _cleanup(); - _exit(status); -} end of bbexit.c echo bbfile.c 1>&2 sed 's/^-//' > bbfile.c <<'end of bbfile.c' -char *_bbfile = "prof.out"; end of bbfile.c echo bb.h 1>&2 sed 's/^-//' > bb.h <<'end of bb.h' -/* bb.h -- basic block counting definitions */ - -#define WORDSIZE 256 -#define NOPRINT -1000 - -#define streq(s, t) (strcmp((s), (t)) == 0) -#define atoi(s) strtol((s), (char *) NULL, 0) - - -extern FILE *out; /* generated .s file */ -extern FILE *map; /* generated .sL file */ - -extern int lineno; /* original source line number */ -extern char filename[]; /* original source filename */ - -extern char *yystring; /* input to lex */ -extern char *tail; /* tail of line (everything after opcode) */ -extern char line[]; /* input line */ -extern char label[]; /* most recent label */ - -extern bool text; /* in text segment? */ -extern bool newblock; /* started new basic block? */ -extern int block; /* current basic block number */ -extern int instructions; /* counter of instructions */ -extern int reached; /* last line reached in basic block */ - - -extern void inst(); /* normal instruction */ -extern void safe(); /* instruction that uses condition codes */ -extern void branch(); /* any flow of control */ -extern void stabd(), stabs(); /* debugger symbol table */ -extern void passline(); /* no-op */ - - -extern bool labelstartsblock(); /* supplied in $TARGET.l */ - - -#ifdef YYLERR /* lex */ - -#undef output -#undef input -#undef unput - -#define input() (*yystring == '\0' ? 0 : *yystring++) -#define unput(c) (*--yystring = (c)) -#define output(c) (c) /* force evaluation */ - -#endif end of bb.h echo new.h 1>&2 sed 's/^-//' > new.h <<'end of new.h' -/* new.h -- dynamic memory allocation */ - -extern char *malloc(), *ealloc(), *realloc(), *erealloc(), *strdup(); -extern int free(); -extern void panic(); - -#ifndef NULL -#define NULL 0 -#endif - -#define new(t, n) ((t *) ealloc((n) * sizeof (t))) -#define renew(t, p, n) (((p) == NULL) \ - ? new(t, n) \ - : ((t *) erealloc((char *) (p), (n) * sizeof (t)))) -#define delete(p) (((p) == NULL) ? 0 : free(p)) end of new.h echo bool.h 1>&2 sed 's/^-//' > bool.h <<'end of bool.h' -/* bool.h -- boolean type */ - -typedef int bool; -#define FALSE 0 -#define TRUE 1 - -#define strbool(t) ((t) ? "TRUE" : "FALSE") end of bool.h echo bb.c 1>&2 sed 's/^-//' > bb.c <<'end of bb.c' -/* bb.c -- insert basic block counting code */ - -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include "new.h" -#include "bool.h" -#include "bb.h" - -extern FILE *efopen(); -extern char *memcpy(); - -char *progname = "bb"; - -static void usage() -{ - fprintf(stderr, "usage: %s file.s ...\n", progname); - exit(1); -} - - -/* globals shared with $TARGET.l */ - -FILE *out = NULL; /* generated .s file */ -FILE *map = NULL; /* generated .sL file */ - -int lineno = 0; /* original source line number */ -char filename[WORDSIZE]; /* original source filename */ - -char *yystring; /* input to lex */ -char *tail; /* tail of line (everything after opcode) */ -char line[BUFSIZ]; /* input line */ -char label[WORDSIZE]; /* most recent label */ - -bool text = TRUE; /* in text segment? */ -bool newblock = TRUE; /* started new basic block? */ -int block = 0; /* current basic block number */ -int instructions = NOPRINT; /* counter of instructions */ -int reached = 0; /* last line reached in basic block */ - - -/* associate functions with basic blocks*/ - -#define MAXFUNC 1000 -int nfunc; -struct { - char *name; - int bb; -} func[MAXFUNC]; - -function(s, bb) - char *s; - int bb; -{ - if (nfunc >= MAXFUNC) - panic("too many functions (>%d), on %s\n", MAXFUNC, s); - func[nfunc].name = strdup(s); - func[nfunc].bb = bb; - nfunc++; -} - -functionmap() -{ - int i; - fprintf(map, "%d functions\n", nfunc); - for (i = 0; i < nfunc; i++) { - fprintf(map, "%s %d\n", func[i].name, func[i].bb); - free(func[i].name); - func[i].name = NULL; - } - nfunc = 0; -} - - -/* parse file, pass to yylex from $TARGET.l */ -void bb(infile) - char *infile; -{ - char outfile[BUFSIZ], mapfile[BUFSIZ]; - static char pid[10] = ""; - FILE *in; - - in = efopen(infile, "r"); - - strcpy(mapfile, infile); - strcat(mapfile, "L"); - map = efopen(mapfile, "w"); - - if (pid[0] == '\0') - sprintf(pid, ".%d", getpid()); - strcpy(outfile, infile); - strcat(outfile, pid); - out = efopen(outfile, "w"); - - lineno = 0; - filename[0] = '\0'; - - while (fgets(line, sizeof line, in) != NULL) { - char *s = line, opcode[WORDSIZE]; - int i; -startofline: - for (; isspace(*s); s++) - ; - if (*s == '\0') { - fputs(line, out); - continue; - } - for (i = 0; isgraph(s[i]); i++) - if (s[i] == ':') { - if (text) { - memcpy(label, s, i); - label[i] = '\0'; - if (labelstartsblock(label)) - newblock = TRUE; - } - s += i + 1; - goto startofline; - } - tail = s + i; - memcpy(opcode, s, i); - opcode[i] = '\n'; - opcode[i + 1] = '\0'; - yystring = opcode; - yylex(); - } - - epilogue(mapfile); - functionmap(); - - fclose(in); - fclose(out); - fclose(map); - map = out = NULL; - - if (unlink(infile) == -1) - panic("couldn't unlink %s -- output in %s\n", infile, outfile); - if (rename(outfile, infile) == -1) - panic("couldn't rename %s to %s\n", outfile, infile); -} - -int main(argc, argv) - int argc; - char *argv[]; -{ - progname = argv[0]; - if (argc == 1) - usage(); - for (--argc, ++argv; argc != 0; --argc, ++argv) - bb(*argv); - return 0; -} - -/* functions for use in $TARGET.l -- common to most machines */ - -void passline() -{ - fputs(line, out); -} - -void inst() -{ - if (text) { - if (newblock) - increment(); - reached = lineno; - instructions++; - } - passline(); -} - -void safe() -{ - if (text) { - if (newblock) - safeincrement(); - reached = lineno; - instructions++; - } - passline(); -} - -void branch() -{ - if (text) { - if (newblock) - safeincrement(); - reached = lineno; - instructions++; - newblock = TRUE; - } - passline(); -} - -#define STAB_LINE 0104 /* N_SLINE from <stab.h> */ -#define STAB_FILE 0144 /* N_SO from <stab.h> */ - -void stabd() -{ - char *s; - passline(); - if (atoi(tail) != STAB_LINE) - return; - if ((s = strchr(line, ',')) == NULL || (s = strchr(s + 1, ',')) == NULL) - panic("bad directive: .stabn%s", tail); - lineno = atoi(s + 1); -} - -void stabs() -{ - char *s, *t; - int len; - passline(); - if ((s = strchr(tail, ',')) == NULL) - panic("bad directive: .stabs%s", tail); - if (atoi(s + 1) != STAB_FILE) - return; - if ((s = strchr(tail, '"')) == NULL || (t = strchr(s + 1, '"')) == NULL) - panic("bad directive: .stabs%s", tail); - len = t - s - 1; - memcpy(filename, s + 1, len); - filename[len] = '\0'; -} end of bb.c echo lprint.c 1>&2 sed 's/^-//' > lprint.c <<'end of lprint.c' -/* lprint.c -- print out statement counts */ - -#include <stdio.h> -#include <string.h> -#include "bool.h" -#include "new.h" - -#define streq(s, t) (strcmp((s), (t)) == 0) - -extern FILE *efopen(); - -char *progname = "lprint"; - -void usage() -{ - fprintf(stderr, "usage: %s [-blaipf] [-c] [-r file] [file ...]\n", progname); - fprintf(stderr, " -b print basic block counts (default)\n"); - fprintf(stderr, " -l print counts for all lines\n"); - fprintf(stderr, " -a print all basic blocks\n"); - fprintf(stderr, " -i print instruction counts\n"); - fprintf(stderr, " -p print function summaries\n"); - fprintf(stderr, " -f print file summaries\n"); - fprintf(stderr, " -c compress prof.out file\n"); - fprintf(stderr, " -r select alternate prof.out file\n"); - exit(1); -} - -int flags = 0; - -#define BLOCKS 0x01 -#define LINES 0x02 -#define ALL 0x04 -#define INST 0x08 -#define FILESUM 0x10 -#define FUNCSUM 0x20 - -#define DEFAULT BLOCKS - -typedef struct Mapfile Mapfile; -typedef struct Mapblock Mapblock; -typedef struct Sourcefile Sourcefile; -typedef struct Block Block; -typedef struct Func Func; - -struct Mapfile { - char *name; - int n, nfunc; - struct Mapblock { - long count; - int inst; - Sourcefile *source; - } *block; - Mapfile *next; -}; - -Mapfile *maplist = NULL; - -struct Block { - int line, last, inst; - long count; -}; - -struct Sourcefile { - char *name; - int nblock, maxblock; - Block *block; - Sourcefile *next; -}; -Sourcefile *sourcelist = NULL; - -struct Func { - char *name; - Mapfile *map; - int block; -}; - -Func *functab = NULL; -int nfunc = 0, maxfunc = 0; - -void gather(fp) - FILE *fp; -{ - int i, n; - char name[BUFSIZ]; - while (fscanf(fp, "%s %d\n", name, &n) != EOF) { - Mapfile *p; - for (p = maplist; p != NULL; p = p->next) - if (streq(name, p->name)) { - if (n != p->n) - panic("prof.out: bad counts for %s: %d and %d\n", - name, p->n, n); - goto found; - } - p = new(Mapfile, 1); - p->next = maplist; - maplist = p; - p->name = strdup(name); - p->n = n; - p->block = new(struct Mapblock, n); - for (i = 0; i < n; i++) - p->block[i].count = 0; - found: - for (i = 0; i < n; i++) { - long count; - if (fscanf(fp, "%ld", &count) == EOF) - panic("prof.out: early EOF\n"); - p->block[i].count += count; - } - } -} - -void writeprof(fp) - FILE *fp; -{ - Mapfile *p; - for (p = maplist; p != NULL; p = p->next) { - int i; - fprintf(fp, "%s %d\n", p->name, p->n); - for (i = 0; i < p->n; i++) - fprintf(fp, "%ld\n", p->block[i].count); - } -} - -Sourcefile *install(name, line, last, inst, count, n) - char *name; - int line, last, inst, n; - long count; -{ - int i; - static Sourcefile *p = NULL; - if (p != NULL && streq(name, p->name)) - goto found; - for (p = sourcelist; p != NULL; p = p->next) - if (streq(name, p->name)) - goto found; - - p = new(Sourcefile, 1); - p->name = strdup(name); - p->block = new(Block, n); - p->maxblock = n; - p->nblock = 0; - p->next = sourcelist; - sourcelist = p; - -found: - if (p->nblock >= p->maxblock) { - p->maxblock *= 4; - p->block = renew(Block, p->block, p->maxblock); - } - - /* insertion sort, but (in practice) very rarely executed */ - for (i = p->nblock++; i > 0 && line < p->block[i - 1].line; i--) - p->block[i] = p->block[i - 1]; - - p->block[i].line = line; - p->block[i].last = last; - p->block[i].inst = inst; - p->block[i].count = count; - return p; -} - -void correlate(map) - Mapfile *map; -{ - int i; - FILE *fp = efopen(map->name, "r"); - for (i = 0; i < map->n; i++) { - char filename[BUFSIZ]; - int line, lastline; - if (fscanf(fp, "%s %d %d %d\n", filename, - &line, &lastline, &map->block[i].inst) != 4) - panic("%s, line %d: bad map entry\n", map->name, i + 1); - map->block[i].source = install(filename, line, lastline, - map->block[i].inst, map->block[i].count, map->n); - } - if (fscanf(fp, "%d functions\n", &map->nfunc) != 1) - panic("%s, line %d: bad function line\n", map->name, map->n); - while (nfunc + map->nfunc >= maxfunc) { - if (maxfunc == 0) - maxfunc = 200; - else - maxfunc *= 8; - functab = renew(Func, functab, maxfunc); - } - for (i = 0; i < map->nfunc; i++, nfunc++) { - char name[BUFSIZ]; - int block; - if (fscanf(fp, "%s %d\n", name, &block) != 2) - panic("%s, line %d: bad function entry\n", - map->name, map->n + 1 + 1); - functab[nfunc].name = strdup(name); - functab[nfunc].map = map; - functab[nfunc].block = block; - } - fclose(fp); -} - -void printline(s, count, inst) - char *s; - long count; - int inst; -{ - if (flags & BLOCKS) - printf("%10ld ", count); - if (flags & INST) { - if (count == 0) { - char buf[20]; - sprintf(buf, "(%d)", inst); - printf("%11s ", buf); - } else - printf("%10ld ", count * inst); - } - printf("%s", s); -} - -void printsource(p) - Sourcefile *p; -{ - int line = 0, last = 0, i = 0, inst = 0; - long count = 0; - char buf[BUFSIZ]; - FILE *fp = efopen(p->name, "r"); - - static int fill = 0; - static bool firsttime = TRUE; - if (firsttime) { - firsttime = FALSE; - if (flags & BLOCKS) - fill += 12; - if (flags & INST) - fill += 12; - } else - printf("\f"); - - while (fgets(buf, sizeof buf, fp) != NULL) { - for (; i < p->nblock && line >= p->block[i].line; i++) { - if (flags & ALL) - printline("\n", p->block[i].count, - p->block[i].inst); - if (last <= p->block[i].last) - last = p->block[i].last; - } - line++; - if (i < p->nblock && line == p->block[i].line && last < line) { - count = p->block[i].count; - inst = p->block[i].inst; - last = p->block[i].last; - printline(buf, count, inst); - while (++i < p->nblock && line == p->block[i].line) - if (flags & ALL) - printline("\n", p->block[i].count, - p->block[i].inst); - } else if (flags & LINES) - printline(buf, count, inst); - else - printf("%*s%s", fill, "", buf); - } - fclose(fp); -} - -typedef struct Sum { - long calls, i, ie, ine, bb, bbe, bbne; -} Sum; - -void printsumline(name, sum, isfunc) - char *name; - Sum sum; - bool isfunc; -{ - printf("%9ldbbe %4ldbb %4ldbbne ", sum.bbe, sum.bb, sum.bbne); - printf("%9ldie %4ldi %4ldine ", sum.ie, sum.i, sum.ine); - if (isfunc) - printf("%7ldcalls ", sum.calls); - else - printf("%13s", ""); - printf(" %s\n", name); -} - -static Sum zerosum = { 0, 0, 0, 0, 0, 0, 0 }; - -void printfunctionsummary(p) - Sourcefile *p; -{ - Func *func; - - for (func = &functab[0]; func < &functab[nfunc]; func++) - if (func->map->block[func->block].source == p) { - Sum sum; - Mapblock *block = func->map->block; - int i, last; - sum = zerosum; - if (func < &functab[nfunc] && func->map == (func + 1)->map) - last = (func + 1)->block; - else - last = func->map->n; - sum.calls += block[func->block].count; - for (i = func->block; i < last; i++) { - Mapblock *b = &block[i]; - sum.bb += 1; - sum.bbe += b->count; - sum.i += b->inst; - sum.ie += b->inst * b->count; - if (b->count == 0) { - sum.bbne += 1; - sum.ine += b->inst; - } - } - printsumline(func->name, sum, TRUE); - } -} - -void printfilesummary(p) - Sourcefile *p; -{ - Sum sum; - Mapfile *map; - Mapblock *block; - - sum = zerosum; - for (map = maplist; map != NULL; map = map->next) - for (block = &map->block[0]; block < &map->block[map->n]; block++) - if (block->source == p) { - sum.bb += 1; - sum.bbe += block->count; - sum.i += block->inst; - sum.ie += block->inst * block->count; - if (block->count == 0) { - sum.bbne += 1; - sum.ine += block->inst; - } - } - printsumline(p->name, sum, FALSE); -} - -void analyze(name) - char *name; -{ - Sourcefile *p; - for (p = sourcelist; p == NULL || !streq(p->name, name); p = p->next) - if (p == NULL) { - fprintf(stderr, "%s: not in prof.out\n", name); - return; - } - - if (flags & (INST|BLOCKS)) { - printsource(p); - if (flags & (FUNCSUM|FILESUM)) - printf("\n\n\n\n"); - } - if (flags & FUNCSUM) { - printfunctionsummary(p); - if (flags & FILESUM) - printf("\n"); - } - if (flags & FILESUM) { - printfilesummary(p); - if ((flags & FUNCSUM) && (flags & (INST|BLOCKS)) == 0) - printf("\n\n"); - } -} - -int main(argc, argv) - int argc; - char *argv[]; -{ - int c; - bool compact = FALSE; - char *prof = "prof.out"; - Mapfile *map; - - extern int optind; - extern char *optarg; - - progname = argv[0]; - while ((c = getopt(argc, argv, "blaipfcr:?")) != EOF) - switch(c) { - case 'b': flags |= BLOCKS; break; - case 'l': flags |= LINES; break; - case 'a': flags |= ALL; break; - case 'i': flags |= INST; break; - case 'p': flags |= FUNCSUM; break; - case 'f': flags |= FILESUM; break; - case 'c': compact = TRUE; break; - case 'r': prof = optarg; break; - case '?': usage(); - } - - if (streq(prof, "-")) - gather(stdin); - else { - FILE *fp = efopen(prof, "r"); - gather(fp); - fclose(fp); - } - if (compact) { - if (streq(prof, "-")) - writeprof(stdout); - else { - FILE *fp = efopen(prof, "w"); - writeprof(fp); - fclose(fp); - } - if (optind == argc && flags == 0) - return 0; - } - - if (flags == 0) - flags = DEFAULT; - if ((flags & (ALL|LINES)) && (flags & (INST|BLOCKS)) == 0) - flags |= BLOCKS; - - for (map = maplist; map != NULL; map = map->next) - correlate(map); - if (optind == argc) { - Sourcefile *p; - for (p = sourcelist; p != NULL; p = p->next) - analyze(p->name); - } else - for (; optind < argc; optind++) - analyze(argv[optind]); - return 0; -} end of lprint.c echo ealloc.c 1>&2 sed 's/^-//' > ealloc.c <<'end of ealloc.c' -/* ealloc.c -- error checking malloc */ - -#include <stdio.h> -#include "new.h" - -extern char *progname; - -/* VARARGS1 */ /* PRINTFLIKE0 */ -void panic(fmt, a, b, c, d, e, f, g, h) - char *fmt; -{ - fprintf(stderr, "%s: ", progname); - fprintf(stderr, fmt, a, b, c, d, e, f, g, h); - exit(1); -} - -char *ealloc(n) - int n; -{ - char *p = malloc(n); - if (p == NULL) - panic("malloc(%d) returned 0\n", n); - return p; -} - -char *erealloc(p, n) - char *p; - int n; -{ - p = realloc(p, n); - if (p == NULL) - panic("realloc(%d) returned 0\n", n); - return p; -} - -#include <string.h> - -char *strdup(s) - char *s; -{ - return strcpy(ealloc(strlen(s) + 1), s); -} end of ealloc.c echo efopen.c 1>&2 sed 's/^-//' > efopen.c <<'end of efopen.c' -/* efopen.c -- open stdio file, check for errors */ - -#include <stdio.h> - -extern char *progname; - -FILE *efopen(file, mode) - char *file, *mode; -{ - FILE *fp = fopen(file, mode); - if (fp == NULL) { - fprintf(stderr, "%s: can't open file %s, mode %s\n", - progname, file, mode); - exit(1); - } - return fp; -} end of efopen.c echo vax.l 1>&2 sed 's/^-//' > vax.l <<'end of vax.l' -/* vax.l -- basic block counting driver for vaxen */ - -%{ - -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include "bool.h" -#include "bb.h" - -extern void panic(); - -bool newfunc = FALSE; /* started new function? */ - -%} - -%% - -\.stab[dn]\n { stabd(); return; } -\.stabs\n { stabs(); return; } -\.word\n { word(); return; } -\.text\n { text = TRUE; passline(); return; } -\.data\n { text = FALSE; passline(); return; } -\..*\n { passline(); return; } - -nop\n { safe(); return; } -movpsl\n { safe(); return; } -adwc\n { safe(); return; } - -[as]ob.+\n { branch(); return; } -case[lwb]\n { branch(); return; } - -bi[sc]psw\n { safe(); return; } -bi.+\n { inst(); return; } -b.+\n { safe(); return; } - -jsb\n { inst(); return; } -jbr\n { jbr(); return; } -j.+\n { branch(); return; } -.+\n { inst(); return; } -\n { panic("null opcode"); } - -%% - -/* ARGSUSED */ -bool labelstartsblock(s) - char *s; -{ - return TRUE; -} - -increment() -{ - if (instructions >= 0) - fprintf(map, "%d %d\n", reached, instructions); - instructions = 0; - fprintf(out, " incl bb+%d\n", 4 * block++); - fprintf(map, "%s %d ", filename, lineno); - newblock = FALSE; -} - -safeincrement() -{ - fputs(" movpsl -(sp)\n", out); - increment(); - fputs(" movw (sp)+, (sp)\n bicpsw $0xf\n bispsw (sp)+\n", out); -} - -jbr() -{ - if (text && newfunc) { - funcprologue(); - newfunc = FALSE; - increment(); - instructions++; - reached = lineno; - newblock = TRUE; - passline(); - } else - branch(); -} - -funcprologue() -{ - function(label, block); - fprintf(out, " tstl bb_init\n jneq bb_%s\n", label); - fprintf(out, " calls $0, bb_init_func\nbb_%s:\n", label); -} - -word() -{ - if (text && newblock && label[0] == '_') - newfunc = TRUE; - passline(); -} - -epilogue(mapfile) - char *mapfile; -{ - if (instructions >= 0) - fprintf(map, "%d %d\n", reached, instructions); - if (text) - fprintf(out, " .data\n"); - fprintf(out, " .lcomm bb, %d\n", 4 * block); - fprintf(out, "bb_mapfile:\n"); - fprintf(out, " .asciz \"%s\"\n", mapfile); - fprintf(out, "bb_init:\n"); - fprintf(out, " .long 0\n"); - fprintf(out, "bb_entry:\n"); - fprintf(out, " .long 0\n"); /* next */ - fprintf(out, " .long %d\n", block); /* len */ - fprintf(out, " .long bb\n"); /* count */ - fprintf(out, " .long bb_mapfile\n"); /* mapfile */ - fprintf(out, " .text\n"); - fprintf(out, "bb_init_func:\n"); - fprintf(out, " .word 0\n"); - fprintf(out, " movl __bblist, bb_entry\n"); - fprintf(out, " movl $bb_entry, __bblist\n"); - fprintf(out, " incl bb_init\n"); - fprintf(out, " ret\n"); -} end of vax.l echo 68020.l 1>&2 sed 's/^-//' > 68020.l <<'end of 68020.l' -/* 68020.l -- basic block counting driver for motorola 68020s */ - -%{ - -#include <stdio.h> -#include <ctype.h> -#include <string.h> -#include "bool.h" -#include "bb.h" - -extern void panic(); - -%} - -%% - -\.stab[dn]\n { stabd(); return; } -\.stabs\n { stabs(); return; } -\.text\n { text = TRUE; passline(); return; } -\.data\n { text = FALSE; passline(); return; } -\..*\n { passline(); return; } -\|.+\n { passline(); return; } - -link\n { linkprologue(); return; } - -movem.+\n { safe(); return; } -trap\n { safe(); return; } -pea\n { safe(); return; } -nop\n { safe(); return; } -[ans]bcd\n { safe(); return; } -(add|neg|sub)x\n { safe(); return; } -un.+\n { safe(); return; } -pack\n { safe(); return; } -rox[lr]\n { safe(); return; } - -bchg\n { inst(); return; } -btst\n { inst(); return; } -bclr\n { inst(); return; } -bf.+\n { inst(); return; } -bs.+\n { inst(); return; } -b.+\n { branch(); return; } - -jbsr\n { inst(); return; } -jsr\n { inst(); return; } -j.+\n { branch(); } - -.+\n { inst(); return; } -\n { panic("null opcode"); } - -%% - -increment() -{ - if (instructions >= 0) - fprintf(map, "%d %d\n", reached, instructions); - instructions = 0; - fprintf(out, " addql #1, bb+%d\n", 4 * block++); - fprintf(map, "%s %d ", filename, lineno); - newblock = FALSE; -} - -safeincrement() -{ - fputs(" movw cc, sp@-\n", out); - increment(); - fputs(" movw sp@+, cc\n", out); -} - -linkprologue() -{ - if (text && label[0] == '_') { - funcprologue(); - increment(); - } - instructions++; - reached = lineno; - passline(); -} - -funcprologue() -{ - function(label, block); - fprintf(out, " tstl bb_init\n jne Lbb_%s\n", label); - fprintf(out, " jbsr bb_init_func\nLbb_%s:\n", label); -} - -bool labelstartsblock(s) - char *s; -{ - if (s[0] == 'L' && s[1] == 'L') /* only used by stab */ - return FALSE; - return TRUE; -} - -epilogue(mapfile) - char *mapfile; -{ - if (instructions >= 0) - fprintf(map, "%d %d\n", reached, instructions); - if (text) - fprintf(out, " .data\n"); - fprintf(out, " .even\n"); - fprintf(out, " .lcomm bb, %d\n", 4 * block); - fprintf(out, "bb_init:\n"); - fprintf(out, " .long 0\n"); - fprintf(out, "bb_map:\n"); - fprintf(out, " .asciz \"%s\"\n", mapfile); - fprintf(out, " .even\n"); - fprintf(out, "bb_entry:\n"); - fprintf(out, " .long 0\n"); /* next */ - fprintf(out, " .long %d\n", block); /* len */ - fprintf(out, " .long bb\n"); /* count */ - fprintf(out, " .long bb_map\n"); /* mapfile */ - fprintf(out, " .text\n"); - fprintf(out, "bb_init_func:\n"); - fprintf(out, " movl __bblist, sp@-\n"); - fprintf(out, " movl sp@+, bb_entry\n"); - fprintf(out, " movl #bb_entry, __bblist\n"); - fprintf(out, " movl #1, bb_init\n"); - fprintf(out, " rts\n"); -} end of 68020.l -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.