static char *sccsid
= "@(#)prof.c 4.5 (Berkeley) %G%";
typedef unsigned short UNIT
; /* unit of profiling */
#define A_OUTNAME "a.out"
#define MON_OUTNAME "mon.out"
#define MON_SUMNAME "mon.sum"
* for each external in the specified file we gather
* its address, the number of calls and compute its share of cpu time.
* The header on the mon.out file.
* Mon.out consists of one of these headers, an array of ncount
* cnt structures (as below) and then an array of samples
* representing the discretized program counter values.
* Each counter has an address and a number of calls.
* Each discretized pc sample has
* a count of the number of samples in its range
unsigned lowpc
, highpc
; /* range profiled */
double ransca
, ranoff
; /* scaling for blowing up plots */
unsigned sampbytes
; /* number of bytes of samples */
int nsamples
; /* number of samples */
double totime
; /* total time for all routines */
double maxtime
; /* maximum time of any routine (for plot) */
double scale
; /* scale factor converting samples to pc
values: each sample covers scale bytes */
char *strtab
; /* string table in core */
off_t ssiz
; /* size of the string table */
struct exec xbuf
; /* exec header of a.out */
int timcmp(), valcmp(), cntcmp();
* Use highpct and lowpc as percentages, temporarily
* for graphing options involving blow-up
while ( *argv
!= 0 && **argv
== '-' ) {
else if ( **argv
== 's' )
else if (**argv
>= '0' && **argv
<= '9') {
if (highpct
<= lowpct
|| highpct
> 100)
ransca
= 100./(highpct
-lowpct
);
ranoff
= 2040. + 40.8*lowpc
*ransca
;
* get information about a.out file.
* get information about mon.out file(s).
asgnsamples(); /* assign samples to procedures */
plotprof(); /* a plotted or ... */
printprof(); /* a printed profile */
printf(" %%time cumsecs #call ms/call name\n");
qsort(nl
, nname
, sizeof(struct nl
), timcmp
);
for (np
= nl
; np
<npe
-1; np
++) {
if (zflg
== 0 && np
->time
== 0 && np
->ncall
== 0)
printf("%6.1f%9.2f", 100*time
, actime
/hz
);
np
->ncall
, (np
->time
*1000/hz
)/np
->ncall
);
printf("%7.7s %8.8s", "", "");
printf(" %s\n", np
->name
);
* Set up string and symbol tables from a.out.
* On return symbol table is sorted by value.
nfile
= fopen(namfil
,"r");
fread(&xbuf
, 1, sizeof(xbuf
), nfile
);
fprintf(stderr
, "%s: bad format\n", namfil
);
qsort(nl
, nname
, sizeof(struct nl
), valcmp
);
fseek(nfile
, N_SYMOFF(xbuf
) + xbuf
.a_syms
, 0);
if (fread(&ssiz
, sizeof (ssiz
), 1, nfile
) == 0) {
fprintf(stderr
, "%s: no string table (old format?)\n", namfil
);
strtab
= (char *)calloc(ssiz
, 1);
fprintf(stderr
, "%s: no room for %d bytes of string table",
if (fread(strtab
+sizeof(ssiz
), ssiz
-sizeof(ssiz
), 1, nfile
) != 1) {
fprintf(stderr
, "%s: error reading string table\n", namfil
);
/* pass1 - count symbols */
fseek(nfile
, N_SYMOFF(xbuf
), 0);
for (i
= xbuf
.a_syms
; i
> 0; i
-= sizeof(struct nlist
)) {
fread(&nbuf
, sizeof(nbuf
), 1, nfile
);
if (nbuf
.n_type
!=N_TEXT
&& nbuf
.n_type
!=N_TEXT
+N_EXT
)
if (aflg
==0 && nbuf
.n_type
!=N_TEXT
+N_EXT
)
fprintf(stderr
, "%s: no symbols\n", namfil
);
nl
= (struct nl
*)calloc((nname
+1), sizeof (struct nl
));
fprintf(stderr
, "prof: No room for %d bytes of symbol table\n",
(nname
+1) * sizeof (struct nlist
));
/* pass2 - read symbols */
fseek(nfile
, N_SYMOFF(xbuf
), 0);
for (i
= xbuf
.a_syms
; i
> 0; i
-= sizeof(struct nlist
)) {
fread(&nbuf
, sizeof(nbuf
), 1, nfile
);
if (nbuf
.n_type
!=N_TEXT
&& nbuf
.n_type
!=N_TEXT
+N_EXT
)
if (aflg
==0 && nbuf
.n_type
!=N_TEXT
+N_EXT
)
npe
->value
= nbuf
.n_value
/sizeof(UNIT
);
npe
->name
= strtab
+nbuf
.n_un
.n_strx
;
* information from a mon.out file is in two parts:
* the counters of how many times each procedure was called,
* if it was called at all;
* and an array of sampling hits within pc ranges.
* the counters must be dealt with on a file-by-file basis,
* since which procedures are represented may vary.
* the samples ranges are fixed, but must be summed across
* files, and then distributed among procedures, because
* of the wierd way the plotting is done.
asgncntrs(); /* assign counts to procedures */
if((pfile
= fopen(filename
, "r")) == NULL
) {
fstat(fileno(pfile
), &stb
);
fread(&h
, sizeof(struct hdr
), 1, pfile
);
lowpc
= h
.lowpc
- (UNIT
*)0;
highpc
= h
.highpc
- (UNIT
*)0;
stb
.st_size
- sizeof(struct hdr
) - h
.ncount
*sizeof(struct cnt
);
nsamples
= sampbytes
/ sizeof (UNIT
);
cbuf
= (struct cnt
*)calloc((h
.ncount
+1), sizeof (struct cnt
));
fprintf(stderr
, "prof: No room for %d bytes of count buffer\n",
(h
.ncount
+1) * sizeof (struct cnt
));
fread(cbuf
, sizeof(struct cnt
), h
.ncount
, pfile
);
/* eliminate zero counters and scale counter pc values */
kp
= &cbuf
[h
.ncount
- 1];
kp
->cvalue
/= sizeof(UNIT
);
qsort(cbuf
, h
.ncount
, sizeof(struct cnt
), cntcmp
);
* Assign counters to the procedures to which they belong
if (kp
<cbuf
|| np
->value
> kp
->cvalue
)
/* skip ``static'' functions */
while (kp
>= cbuf
&& kp
->cvalue
> np
->value
+ PCFUDGE
)
if (kp
->cvalue
>= np
->value
) {
calloc(sampbytes
, sizeof (UNIT
));
printf("prof: No room for %d sample pc's\n",
sampbytes
/ sizeof (UNIT
));
fread(&sample
, sizeof (UNIT
), 1, pfile
);
"prof: unexpected EOF after reading %d/%d samples\n",
* Assign samples to the procedures to which they belong.
/* read samples and assign to namelist symbols */
for (i
=0; i
< nsamples
; i
++) {
pch
= lowpc
+ scale
*(i
+1);
for (j
=0; j
<nname
; j
++) {
if (pcl
>= nl
[j
+1].value
)
overlap
=(min(pch
,nl
[j
+1].value
)-max(pcl
,nl
[j
].value
));
nl
[j
].time
+= overlap
*time
/scale
;
fprintf(stderr
, "No time accumulated\n");
* dump what you have out to a mon.out style file.
sfile
= fopen(MON_SUMNAME
, "w");
* h.lowpc and h.highpc are already fine.
* fix h.ncount to count non-zero calls,
* and the one zero call which marks the end.
for (np
= nl
; np
< npe
-1 ; np
++)
fwrite(&h
, sizeof (struct hdr
), 1, sfile
);
for (np
= nl
; np
< npe
-1; np
++) {
kp
.cvalue
= np
->value
* sizeof (UNIT
);
fwrite(&kp
, sizeof (struct cnt
), 1, sfile
);
fwrite(&kp
, sizeof (struct cnt
), 1, sfile
);
fwrite(samples
, sizeof (UNIT
), nsamples
, sfile
);
* discover the tick frequency of the machine
* if something goes wrong, we return 1.
tim
.it_interval
.tv_sec
= 0;
tim
.it_interval
.tv_usec
= 1;
tim
.it_value
.tv_usec
= 0;
setitimer(ITIMER_REAL
, &tim
, 0);
setitimer(ITIMER_REAL
, 0, &tim
);
if (tim
.it_interval
.tv_usec
< 1)
return (1000000 / tim
.it_interval
.tv_usec
);
return(p1
->value
- p2
->value
);
if (nflg
&& p2
->ncall
!= p1
->ncall
)
return (p2
->ncall
- p1
->ncall
);
return(strcmp(p1
->name
,p2
->name
));
return(p1
->cvalue
- p2
->cvalue
);
double time
, lastx
, lasty
, lastsx
;
space(-2048, -2048, 2048, 2048);
line(-2040, -2040, -2040, 2040);
line(-2040, 2040-i
*408, 0, 2040-i
*408);
scale
= (4080.*ransca
)/(sampbytes
/sizeof(UNIT
));
for(i
= 0; i
< nsamples
; i
++) {
lastsx
-= 2000.*time
/totime
;
if(lasty
>= -2040. && ty
<= 2040.) {
line((int)tx
, (int)ty
, (int)lastsx
, (int)lasty
);
if (ccnt
!=0 || lastx
!=0.0) {
lastx
= -time
*2000./maxtime
;
line(0, (int)ty
, (int)tx
, (int)ty
);
scale
= (4080.*ransca
)/(highpc
-lowpc
);
for(np
= nl
; np
<npe
; np
++) {
if(zflg
== 0 && np
->time
== 0 && np
->ncall
== 0)
lasty
= ranoff
- (np
->value
- lowpc
)*scale
;
if(lasty
>= -2040. && lasty
<= 2040.) {
char bufl
[BUFSIZ
], *namp
;
line(0, (int)lasty
, 50, (int)lasty
);
line((int)(lastx
-50),(int)lasty
,(int)lastx
,(int)lasty
);
move((int)(lastx
+30), (int)(lasty
+10));
sprintf(bufl
, "%s", np
->name
+ (np
->name
[0] == '_'));