xref: /original-bsd/old/prof/prof.c (revision 23a40993)
1 #ifndef lint
2 static	char *sccsid = "@(#)prof.c	4.2 (Berkeley) 06/17/83";
3 #endif
4 /*
5  * prof
6  */
7 #include <stdio.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <a.out.h>
11 
12 typedef	short UNIT;		/* unit of profiling */
13 #define	A_OUTNAME	"a.out"
14 #define	MON_OUTNAME	"mon.out"
15 #define	MON_SUMNAME	"mon.sum"
16 
17 /*
18  * The symbol table;
19  * for each external in the specified file we gather
20  * its address, the number of calls and compute its share of cpu time.
21  */
22 struct nl {
23 	char	*name;
24 	unsigned value;
25 	float	time;
26 	long	ncall;
27 } *nl;
28 int	nname;
29 struct	nl *np;
30 struct	nl *npe;
31 
32 /*
33  * The header on the mon.out file.
34  * Mon.out consists of one of these headers, an array of ncount
35  * cnt structures (as below) and then an array of samples
36  * representing the discretized program counter values.
37  */
38 struct hdr {
39 	UNIT	*lowpc, *highpc;
40 	int	ncount;
41 } h;
42 
43 /*
44  * Each counter has an address and a number of calls.
45  */
46 struct cnt {
47 	unsigned cvalue;
48 	long	cncall;
49 } *cbuf;
50 
51 /*
52  * Each discretized pc sample has
53  * a count of the number of samples in its range
54  */
55 unsigned UNIT	*samples;
56 
57 FILE	*pfile, *nfile;
58 
59 unsigned lowpc, highpc;		/* range profiled */
60 double	ransca, ranoff;		/* scaling for blowing up plots */
61 unsigned sampbytes;		/* number of bytes of samples */
62 int	nsamples;		/* number of samples */
63 double	totime;			/* total time for all routines */
64 double	maxtime;		/* maximum time of any routine (for plot) */
65 double	scale;			/* scale factor converting samples to pc
66 				   values: each sample covers scale bytes */
67 char	*strtab;		/* string table in core */
68 off_t	ssiz;			/* size of the string table */
69 struct	exec xbuf;		/* exec header of a.out */
70 
71 int	aflg;
72 int	nflg;
73 int	vflg;
74 int	lflg;
75 int	zflg;
76 int	sflag;
77 
78 char	*namfil;
79 
80 int	timcmp(), valcmp(), cntcmp();
81 
82 main(argc, argv)
83 	char **argv;
84 {
85 	int lowpct, highpct;
86 
87 	/*
88 	 * Use highpct and lowpc as percentages, temporarily
89 	 * for graphing options involving blow-up
90 	 */
91 	lowpct = -1;
92 	highpct = -1;
93 	argv++;
94 	while ( *argv != 0 && **argv == '-' ) {
95 		*argv += 1;
96 		if (**argv == 'l')
97 			lflg++;
98 		else if (**argv == 'a')
99 			aflg++;
100 		else if (**argv == 'n')
101 			nflg++;
102 		else if (**argv == 'z')
103 			zflg++;
104 		else if (**argv == 'v')
105 			vflg++;
106 		else if ( **argv == 's' )
107 			sflag++;
108 		else if (**argv >= '0' && **argv <= '9') {
109 			int i = atoi(*argv);
110 			if (lowpct == -1)
111 				lowpct = i;
112 			else
113 				highpct = i;
114 		}
115 		argv++;
116 	}
117 	if ( *argv != 0 ) {
118 		namfil = *argv;
119 		argv++;
120 	} else {
121 		namfil = A_OUTNAME;
122 	}
123 	if (lowpct >= 100)
124 		lowpct = 0;
125 	if (highpct <= lowpct || highpct > 100)
126 		highpct = 100;
127 	ransca = 100./(highpct-lowpct);
128 	ranoff = 2040. + 40.8*lowpc*ransca;
129 		/*
130 		 *	get information about a.out file.
131 		 */
132 	getnfile();
133 		/*
134 		 *	get information about mon.out file(s).
135 		 */
136 	if ( *argv == 0 ) {
137 		getpfile( MON_OUTNAME );
138 	} else {
139 		do {
140 			getpfile( *argv );
141 			argv++;
142 		} while ( *argv != 0 );
143 	}
144 	asgnsamples();		/* assign samples to procedures */
145 #ifdef plot
146 	if (vflg)
147 		plotprof();	/* a plotted or ... */
148 	else
149 #endif
150 		printprof();	/* a printed profile */
151 	if ( sflag != 0 ) {
152 		putprof();
153 	}
154 	done();
155 }
156 
157 printprof()
158 {
159 	double time, actime;
160 
161 	actime = 0;
162 	printf(" %%time  cumsecs  #call  ms/call  name\n");
163 	if (!lflg)
164 		qsort(nl, nname, sizeof(struct nl), timcmp);
165 	for (np = nl; np<npe-1; np++) {
166 		if (zflg == 0 && np->time == 0 && np->ncall == 0)
167 			continue;
168 		time = np->time/totime;
169 		actime += np->time;
170 		printf("%6.1f%9.2f", 100*time, actime/60);
171 		if (np->ncall != 0)
172 			printf("%7ld %8.2f",
173 			    np->ncall, np->time/(np->ncall*.06));
174 		else
175 			printf("%7.7s %8.8s", "", "");
176 		printf("  %s\n", np->name);
177 	}
178 }
179 
180 /*
181  * Set up string and symbol tables from a.out.
182  * On return symbol table is sorted by value.
183  */
184 getnfile()
185 {
186 
187 	nfile = fopen(namfil,"r");
188 	if (nfile == NULL) {
189 		perror(namfil);
190 		done();
191 	}
192 	fread(&xbuf, 1, sizeof(xbuf), nfile);
193 	if (N_BADMAG(xbuf)) {
194 		fprintf(stderr, "%s: bad format\n", namfil);
195 		done();
196 	}
197 	getstrtab();
198 	getsymtab();
199 	qsort(nl, nname, sizeof(struct nl), valcmp);
200 }
201 
202 getstrtab()
203 {
204 
205 	fseek(nfile, N_SYMOFF(xbuf) + xbuf.a_syms, 0);
206 	if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
207 		fprintf(stderr, "%s: no string table (old format?)\n", namfil);
208 		done();
209 	}
210 	strtab = (char *)calloc(ssiz, 1);
211 	if (strtab == NULL) {
212 		fprintf(stderr, "%s: no room for %d bytes of string table",
213 		    namfil, ssiz);
214 		done();
215 	}
216 	if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
217 		fprintf(stderr, "%s: error reading string table\n", namfil);
218 		done();
219 	}
220 }
221 
222 /*
223  * Read in symbol table
224  */
225 getsymtab()
226 {
227 	register int i;
228 
229 	/* pass1 - count symbols */
230 	fseek(nfile, N_SYMOFF(xbuf), 0);
231 	nname = 0;
232 	for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
233 		struct nlist nbuf;
234 		fread(&nbuf, sizeof(nbuf), 1, nfile);
235 		if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT)
236 			continue;
237 		if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT)
238 			continue;
239 		nname++;
240 	}
241 	if (nname == 0) {
242 		fprintf(stderr, "%s: no symbols\n", namfil);
243 		done();
244 	}
245 	nl = (struct nl *)calloc((nname+1), sizeof (struct nl));
246 	if (nl == 0) {
247 		fprintf(stderr, "prof: No room for %d bytes of symbol table\n",
248 		    (nname+1) * sizeof (struct nlist));
249 		done();
250 	}
251 
252 	/* pass2 - read symbols */
253 	fseek(nfile, N_SYMOFF(xbuf), 0);
254 	npe = nl;
255 	nname = 0;
256 	for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
257 		struct nlist nbuf;
258 		fread(&nbuf, sizeof(nbuf), 1, nfile);
259 		if (nbuf.n_type!=N_TEXT && nbuf.n_type!=N_TEXT+N_EXT)
260 			continue;
261 		if (aflg==0 && nbuf.n_type!=N_TEXT+N_EXT)
262 			continue;
263 		npe->value = nbuf.n_value/sizeof(UNIT);
264 		npe->name = strtab+nbuf.n_un.n_strx;
265 		npe++;
266 		nname++;
267 	}
268 	npe->value = -1;
269 	npe++;
270 }
271 
272 /*
273  * information from a mon.out file is in two parts:
274  * the counters of how many times each procedure was called,
275  * if it was called at all;
276  * and an array of sampling hits within pc ranges.
277  * the counters must be dealt with on a file-by-file basis,
278  * since which procedures are represented may vary.
279  * the samples ranges are fixed, but must be summed across
280  * files, and then distributed among procedures, because
281  * of the wierd way the plotting is done.
282  */
283 getpfile(filename)
284 	char *filename;
285 {
286 
287 	openpfile(filename);
288 	readcntrs();
289 	asgncntrs();		/* assign counts to procedures */
290 	readsamples();
291 	closepfile();
292 }
293 
294 openpfile(filename)
295 	char *filename;
296 {
297 	struct stat stb;
298 
299 	if((pfile = fopen(filename, "r")) == NULL) {
300 		perror(filename);
301 		done();
302 	}
303 	fstat(fileno(pfile), &stb);
304 	fread(&h, sizeof(struct hdr), 1, pfile);
305 	lowpc = h.lowpc - (UNIT *)0;
306 	highpc = h.highpc - (UNIT *)0;
307 	sampbytes =
308 	    stb.st_size - sizeof(struct hdr) - h.ncount*sizeof(struct cnt);
309 	nsamples = sampbytes / sizeof (unsigned UNIT);
310 }
311 
312 closepfile()
313 {
314 
315 	fclose(pfile);
316 	free(cbuf);
317 }
318 
319 readcntrs()
320 {
321 	struct cnt *kp;
322 
323 	cbuf = (struct cnt *)calloc((h.ncount+1), sizeof (struct cnt));
324 	if (cbuf == 0) {
325 		fprintf(stderr, "prof: No room for %d bytes of count buffer\n",
326 		    (h.ncount+1) * sizeof (struct cnt));
327 		exit(1);
328 	}
329 	fread(cbuf, sizeof(struct cnt), h.ncount, pfile);
330 	/* eliminate zero counters and scale counter pc values */
331 	if (h.ncount) {
332 		kp = &cbuf[h.ncount - 1];
333 		for (;;) {
334 			if (kp->cvalue==0) {
335 				h.ncount=kp-cbuf;
336 				++kp;
337 				break;
338 			}
339 			if (kp == cbuf) {
340 				h.ncount = 0;
341 				break;
342 			}
343 			--kp;
344 		}
345 		for (; --kp>=cbuf; )
346 			kp->cvalue /= sizeof(UNIT);
347 	}
348 	/* sort counters */
349 	qsort(cbuf, h.ncount, sizeof(struct cnt), cntcmp);
350 }
351 
352 /*
353  * Assign counters to the procedures to which they belong
354  */
355 asgncntrs()
356 {
357 	register int i;
358 	struct cnt *kp;
359 
360 	kp = &cbuf[h.ncount-1];
361 	np = npe;
362 	while (--np>=nl) {
363 		if (kp<cbuf || np->value > kp->cvalue)
364 			continue;
365 			/* skip ``static'' functions */
366 		while (kp >= cbuf && kp->cvalue > np->value + 11)
367 			--kp;
368 		if (kp->cvalue >= np->value) {
369 			np->ncall += kp->cncall;
370 			--kp;
371 		}
372 	}
373 }
374 
375 readsamples()
376 {
377 	register i;
378 	unsigned UNIT	sample;
379 	int totalt;
380 
381 	if (samples == 0) {
382 		samples = (unsigned UNIT *)
383 		    calloc(sampbytes, sizeof (unsigned UNIT));
384 		if (samples == 0) {
385 			printf("prof: No room for %d sample pc's\n",
386 			    sampbytes / sizeof (unsigned UNIT));
387 			done();
388 		}
389 	}
390 	for (i = 0; ; i++) {
391 		fread(&sample, sizeof (unsigned UNIT), 1, pfile);
392 		if (feof(pfile))
393 			break;
394 		samples[i] += sample;
395 		totalt += sample;
396 	}
397 	if (i != nsamples) {
398 		fprintf(stderr,
399 		    "prof: unexpected EOF after reading %d/%d samples\n",
400 			--i, nsamples);
401 		done();
402 	}
403 }
404 
405 /*
406  * Assign samples to the procedures to which they belong.
407  */
408 asgnsamples()
409 {
410 	register j;
411 	unsigned UNIT	ccnt;
412 	double time;
413 	unsigned pcl, pch;
414 	register int i;
415 	int overlap;
416 
417 	/* read samples and assign to namelist symbols */
418 	scale = highpc - lowpc;
419 	scale /= nsamples;
420 	for (i=0; i < nsamples; i++) {
421 		ccnt = samples[i];
422 		if (ccnt == 0)
423 			continue;
424 		pcl = lowpc + scale*i;
425 		pch = lowpc + scale*(i+1);
426 		time = ccnt;
427 		totime += time;
428 		if(time > maxtime)
429 			maxtime = time;
430 		for (j=0; j<nname; j++) {
431 			if (pch < nl[j].value)
432 				break;
433 			if (pcl >= nl[j+1].value)
434 				continue;
435 			overlap=(min(pch,nl[j+1].value)-max(pcl,nl[j].value));
436 			if (overlap>0)
437 				nl[j].time += overlap*time/scale;
438 		}
439 	}
440 	if (totime==0.0) {
441 		fprintf(stderr, "No time accumulated\n");
442 /*
443 		done();
444  */
445 		totime=1.0;
446 	}
447 }
448 
449 /*
450  * dump what you have out to a mon.out style file.
451  */
452 putprof()
453 {
454 	FILE *sfile;
455 	struct nl *np;
456 	struct cnt kp;
457 	int i;
458 
459 	sfile = fopen(MON_SUMNAME, "w");
460 	if (sfile == NULL) {
461 		perror(MON_SUMNAME);
462 		done();
463 	}
464 	/*
465 	 * build a new header.
466 	 * h.lowpc and h.highpc are already fine.
467 	 * fix h.ncount to count non-zero calls,
468 	 * and the one zero call which marks the end.
469 	 */
470 	h.ncount = 0;
471 	for (np = nl; np < npe-1 ; np++)
472 		if (np->ncall > 0)
473 			h.ncount++;
474 	h.ncount++;
475 	fwrite(&h, sizeof (struct hdr), 1, sfile);
476 	for (np = nl; np < npe-1; np++) {
477 		if (np->ncall > 0) {
478 			kp.cvalue = np->value * sizeof (unsigned UNIT);
479 			kp.cncall = np->ncall;
480 			fwrite(&kp, sizeof (struct cnt), 1, sfile);
481 		}
482 	}
483 	kp.cvalue = 0;
484 	kp.cncall = 0;
485 	fwrite(&kp, sizeof (struct cnt), 1, sfile);
486 	fwrite(samples, sizeof (unsigned UNIT), nsamples, sfile);
487 	fclose(sfile);
488 }
489 
490 min(a, b)
491 {
492 	if (a<b)
493 		return(a);
494 	return(b);
495 }
496 
497 max(a, b)
498 {
499 	if (a>b)
500 		return(a);
501 	return(b);
502 }
503 
504 valcmp(p1, p2)
505 	struct nl *p1, *p2;
506 {
507 
508 	return(p1->value - p2->value);
509 }
510 
511 timcmp(p1, p2)
512 	struct nl *p1, *p2;
513 {
514 	float d;
515 
516 	if (nflg && p2->ncall != p1->ncall)
517 		return (p2->ncall - p1->ncall);
518 	d = p2->time - p1->time;
519 	if (d > 0.0)
520 		return(1);
521 	if (d < 0.0)
522 		return(-1);
523 	return(strcmp(p1->name,p2->name));
524 }
525 
526 cntcmp(p1, p2)
527 	struct cnt *p1, *p2;
528 {
529 
530 	return(p1->cvalue - p2->cvalue);
531 }
532 
533 done()
534 {
535 
536 #ifdef plot
537 	if(vflg) {
538 		point(0, -2040);
539 		closepl();
540 	}
541 #endif
542 	exit(0);
543 }
544 
545 #ifdef plot
546 plotprof()
547 {
548 	double time, lastx, lasty, lastsx;
549 	register i;
550 
551 	openpl();
552 	erase();
553 	space(-2048, -2048, 2048, 2048);
554 	line(-2040, -2040, -2040, 2040);
555 	line(0, 2040, 0, -2040);
556 	for(i=0; i<11; i++)
557 		line(-2040, 2040-i*408, 0, 2040-i*408);
558 	lastx = 0.;
559 	lasty = ranoff;
560 	scale = (4080.*ransca)/(sampbytes/sizeof(UNIT));
561 	lastsx = 0.0;
562 	for(i = 0; i < nsamples; i++) {
563 		unsigned UNIT ccnt;
564 		double tx, ty;
565 		ccnt = samples[i];
566 		time = ccnt;
567 		tx = lastsx;
568 		ty = lasty;
569 		lastsx -= 2000.*time/totime;
570 		lasty -= scale;
571 		if(lasty >= -2040. && ty <= 2040.) {
572 			line((int)tx, (int)ty, (int)lastsx, (int)lasty);
573 			if (ccnt!=0 || lastx!=0.0) {
574 				tx = lastx;
575 				lastx = -time*2000./maxtime;
576 				ty += scale/2;
577 				line(0, (int)ty, (int)tx, (int)ty);
578 			}
579 		}
580 	}
581 	scale = (4080.*ransca)/(highpc-lowpc);
582 	lastx = 50.;
583 	for(np = nl; np<npe;  np++) {
584 		if(np->value < lowpc)
585 			continue;
586 		if(np->value >= highpc)
587 			continue;
588 		if(zflg == 0 && np->time == 0 && np->ncall == 0)
589 			continue;
590 		time = np->time/totime;
591 		lasty = ranoff - (np->value - lowpc)*scale;
592 		if(lasty >= -2040. && lasty <= 2040.) {
593 			char bufl[BUFSIZ], *namp;
594 			register j;
595 			line(0, (int)lasty, 50, (int)lasty);
596 			line((int)(lastx-50),(int)lasty,(int)lastx,(int)lasty);
597 			move((int)(lastx+30), (int)(lasty+10));
598 			sprintf(bufl, "%s", np->name + (np->name[0] == '_'));
599 			label(bufl);
600 		}
601 		lastx += 500.;
602 		if(lastx > 2000.)
603 			lastx = 50.;
604 	}
605 }
606 #endif
607