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