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