brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (09/13/90)
Here's pmp 1.0, the poor man's profiler. It includes a statement counter and a timing profiler. Like every utility in the Poor Man's Series, pmp has some feature missing from almost all the competition: in this case, the ability to profile statements and timing in an optimized program. The statement counting part should be extremely portable. The timing part should be portable to any BSD machine. Send comments to me or to alt.sources.d. ---Dan #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: README Makefile pmp pmp.1 pmplib.c pmplib.h test.c # test.script # Wrapped by brnstnd@kramden on Thu Sep 13 05:20:41 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(1332 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XThis is pmp, the poor man's profiler. X XRecent studies have shown that most UNIX programmers don't even have a Xstatement execution counter. The pmp script and pmplib library should Xrectify this depressing state of affairs. X Xpmp gives you both statement counts and timing counts. If you undefine XPMPSIG in pmplib.h, pmp will provide only statement counts---but with Xridiculously portable code. X Xpmp has one big advantage over every other profiling system I've seen: Xit doesn't force optimization off. You can place profiling marks Xanywhere in your code; you only hurt optimization where you put the Xmarks. So if you're sick of having your program slowed down dramatically Xjust because the profiler sticks its nose into your inner loops, pmp Xshould be a welcome relief. X XOf course, pmp is also the smallest profiler known to man. X XOther than that, it's pretty poor. X XThis is pmp version 1.0, 9/13/90, by Daniel J. Bernstein. Xpmp is public domain. X X XRequirements: X X sed, grep, sh, cc. X X If you want timing information, BSD-style setitimer() and VTALRM. X X X XTo set up pmp, just ``make'', or cc -o pmplib.o pmplib.c -O. X XTo use pmp, follow the example in test.c. test.script shows the results Xof a sample pmp run. A manual page is in pmp.1. X X XCaveats: X X Not many. pmp is probably more reliable than your previous profiler. X X XGood luck! END_OF_FILE if test 1332 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(395 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' XCC=cc XCCOPTS=-O X XNROFF=nroff XNROFFOPTS=-man X Xdefault: all X Xall: pmplib.o pmp.man X Xshar: pmp.shar X Xpmp.man: pmp.1 Makefile X $(NROFF) $(NROFFOPTS) < pmp.1 > pmp.man X Xpmplib.o: pmplib.c Makefile X $(CC) $(CCOPTS) -c pmplib.c X Xpmp.shar: README Makefile pmp pmp.1 pmplib.c pmplib.h test.c test.script X shar README Makefile pmp pmp.1 pmplib.c pmplib.h test.c test.script > pmp.shar X chmod 400 pmp.shar END_OF_FILE if test 395 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'pmp' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pmp'\" else echo shar: Extracting \"'pmp'\" \(96 characters\) sed "s/^X//" >'pmp' <<'END_OF_FILE' X#!/bin/sh Xsed "`grep '^'$1' ' $2 | sed 's?'$1' \([^:]*\): \(.*\)?\1s-\$- \/\* \2 \*\/-?'`" < $1 END_OF_FILE if test 96 -ne `wc -c <'pmp'`; then echo shar: \"'pmp'\" unpacked with wrong size! fi chmod +x 'pmp' # end of 'pmp' fi if test -f 'pmp.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pmp.1'\" else echo shar: Extracting \"'pmp.1'\" \(1152 characters\) sed "s/^X//" >'pmp.1' <<'END_OF_FILE' X.TH pmp 1 X.SH NAME Xpmp \- poor man's profiler X.SH SYNTAX Xpmp X.I foo.c X.I prof.out X.SH DESCRIPTION X.I pmp Xreads profiling information from X.I prof.out Xand prints a Xprofiled copy of X.I foo.c Xon its standard output. X.PP XTo enable pmp's profiling, Xinclude pmplib.h at the top of each program module. XTo set the profiling timer for every X.I us Xmicroseconds, Xcall X.I pmpsigstart(us). XInsert the macro X.I P X(or X.I PMPEXEC) Xas a statement wherever you want profiling. XTo write the results to X.I prof.out, Xjust call X\fIpmp("prof.out")\fB Xbefore exiting. X.PP XThe profiled copy of X.I foo.c Xincludes a comment appended to each line containing a X.I P, Xof the form 1740+39. X1740 is the number of times the X.I P Xstatement was executed. X39 is the number of X.I pmpsigstart() Xticks that occurred at any time between the last executed X.I P Xand this one. X.PP XIf there are several X\fIP'\fBs Xon a line, X.B pmp Xplaces the profile comments in the opposite order from the Xfirst execution of the X\fIP'\fBs. X.PP XThe timing part of X.I pmp Xis not portable outside BSD Xand may be disabled under other systems. X.SH AUTHOR Xpmp was written by Daniel J. Bernstein. XIt is public domain. END_OF_FILE if test 1152 -ne `wc -c <'pmp.1'`; then echo shar: \"'pmp.1'\" unpacked with wrong size! fi # end of 'pmp.1' fi if test -f 'pmplib.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pmplib.c'\" else echo shar: Extracting \"'pmplib.c'\" \(920 characters\) sed "s/^X//" >'pmplib.c' <<'END_OF_FILE' X#include <stdio.h> X#include "pmplib.h" X#ifdef PMPSIG X#include <sys/time.h> X#include <signal.h> Xint pmpsig = 0; X#endif X Xstatic struct pmpcount *cur = (struct pmpcount *) 0; X Xvoid pmpregister(pmpc) Xstruct pmpcount *pmpc; X{ X pmpc->prev = cur; X cur = pmpc; X} X Xpmp(fn) Xchar *fn; X{ X FILE *fi; X struct pmpcount *t; X X if ((fi = fopen(fn,"w")) == NULL) X return(-1); X for (t = cur;t;t = t->prev) X#ifdef PMPSIG X (void) fprintf(fi,"%s %d: %d+%d\n",t->file,t->line,t->scount,t->tcount); X#else X (void) fprintf(fi,"%s %d: %d\n",t->file,t->line,t->scount); X#endif X (void) fclose(fi); X return(0); X} X X#ifdef PMPSIG Xpmpsighandle() X{ X pmpsig++; X} X Xvoid pmpstartsig(us) Xint us; X{ X struct itimerval it; X X it.it_interval.tv_sec = it.it_value.tv_sec = us / 100000; X it.it_interval.tv_usec = it.it_value.tv_usec = us % 100000; X X (void) signal(SIGVTALRM,pmpsighandle); X (void) setitimer(ITIMER_VIRTUAL,&it,(struct itimerval *) 0); X} X#endif END_OF_FILE if test 920 -ne `wc -c <'pmplib.c'`; then echo shar: \"'pmplib.c'\" unpacked with wrong size! fi # end of 'pmplib.c' fi if test -f 'pmplib.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pmplib.h'\" else echo shar: Extracting \"'pmplib.h'\" \(676 characters\) sed "s/^X//" >'pmplib.h' <<'END_OF_FILE' X#ifndef PMPLIB_H X#define PMPLIB_H X X#define PMPSIG X Xpmp(); Xvoid pmpregister(); X X#ifdef PMPSIG Xextern int pmpsig; Xvoid pmpsigstart(); X#endif X Xstruct pmpcount X { X long scount; X#ifdef PMPSIG X long tcount; X#endif X long line; X char *file; X struct pmpcount *prev; X } X; X X#ifdef PMPSIG X#define PMPEXEC do { static struct pmpcount pmpc = {0,0,__LINE__,__FILE__,(struct pmpcount *)0}; if (!((pmpc.scount)++)) pmpregister(&pmpc); if (pmpsig) { pmpc.tcount += pmpsig; pmpsig = 0; } } while(0) X#else X#define PMPEXEC do { static struct pmpcount pmpc = {0,0,__LINE__,__FILE__,(struct pmpcount *)0}; if (!((pmpc.scount)++)) pmpregister(&pmpc); } while(0) X#endif X#define P PMPEXEC X X#endif END_OF_FILE if test 676 -ne `wc -c <'pmplib.h'`; then echo shar: \"'pmplib.h'\" unpacked with wrong size! fi # end of 'pmplib.h' fi if test -f 'test.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'test.c'\" else echo shar: Extracting \"'test.c'\" \(365 characters\) sed "s/^X//" >'test.c' <<'END_OF_FILE' X#include "pmplib.h" X Xmain() X{ X int i; X int j; X X /* pmpstartsig(1000): set the timer for every 1 millisecond. */ X /* P: produce profiling information. */ X /* pmp("test.out"): save pmp's results in test.out. */ X X pmpstartsig(1000); X X for (i = 0;i < 1000;i++) X { X P; X for (j = 0;j < 1000;j++) X { X P; X if (i % 2) X P; X } X } X pmp("test.out"); X} END_OF_FILE if test 365 -ne `wc -c <'test.c'`; then echo shar: \"'test.c'\" unpacked with wrong size! fi # end of 'test.c' fi if test -f 'test.script' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'test.script'\" else echo shar: Extracting \"'test.script'\" \(869 characters\) sed "s/^X//" >'test.script' <<'END_OF_FILE' Xcsh> cat test.c X#include "pmplib.h" X Xmain() X{ X int i; X int j; X X /* pmpstartsig(1000): set the timer for every 1 millisecond. */ X /* P: produce profiling information. */ X /* pmp("test.out"): save pmp's results in test.out. */ X X pmpstartsig(1000); X X for (i = 0;i < 1000;i++) X { X P; X for (j = 0;j < 1000;j++) X { X P; X if (i % 2) X P; X } X } X pmp("test.out"); X} Xcsh> cc -o test test.c pmplib.o Xcsh> ./test Xcsh> pmp test.c test.out X#include "pmplib.h" X Xmain() X{ X int i; X int j; X X /* pmpstartsig(1000): set the timer for every 1 millisecond. */ X /* P: produce profiling information. */ X /* pmp("test.out"): save pmp's results in test.out. */ X X pmpstartsig(1000); X X for (i = 0;i < 1000;i++) X { X P; /* 1000+0 */ X for (j = 0;j < 1000;j++) X { X P; /* 1000000+243 */ X if (i % 2) X P; /* 500000+105 */ X } X } X pmp("test.out"); X} Xcsh> END_OF_FILE if test 869 -ne `wc -c <'test.script'`; then echo shar: \"'test.script'\" unpacked with wrong size! fi # end of 'test.script' fi echo shar: End of shell archive. exit 0