[alt.sources] pmp - poor man's

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