BSD 4_4 development
[unix-history] / usr / src / contrib / mprof / mprof.c
CommitLineData
f85cd9fb
C
1/* mprof.c 2.6 9/14/90 16:01:20 */
2/* Copyright (c) 1987, 1990, Benjamin G. Zorn */
3
4/* mprof.c -- code to analyse and print out mprof data
5 */
6
7
8#include <stdio.h>
9#include <sys/file.h>
10#include <ctype.h>
11#include <a.out.h>
12#include <stab.h>
13#include "mprof.h"
14
15#ifdef mips
16#include <ldfcn.h>
17#endif
18
19#if defined(mips) || defined(vax)
20char *
21strdup(s)
22char *s;
23{
24 char *result = malloc(strlen(s) + 1);
25 (void) strcpy(result, s);
26 return result;
27}
28#endif
29
30extern void mprof_graph_ops(); /* from mpgraph.c */
31
32#define check_fscanf(x) \
33 { int result = (x); \
34 if (!result) { \
35 fprintf(stderr, "fscanf -- can't read input\n"); \
36 exit(1); \
37 } \
38 }
39
40struct leakpair {
41 char *func;
42 int offset;
43};
44
45struct leakentry {
46 struct leakpair path[SHORT_CALLSTACK_SIZE];
47 int all_no;
48 int all_by;
49 int fre_no;
50 int fre_by;
51 struct leakentry *next;
52};
53
54
55lte_str_compar(lte1, lte2)
56struct leakentry *lte1, *lte2;
57{
58 struct leakpair *path1, *path2;
59 int i;
60
61 path1 = lte1->path;
62 path2 = lte2->path;
63
64 for (i = 0; i < SHORT_CALLSTACK_SIZE; i++) {
65 if (strcmp(path1[i].func, path2[i].func) != 0) {
66 return strcmp(path1[i].func, path2[i].func);
67 }
68 }
69 return 0;
70}
71
72int
73path_equal(path1, path2)
74struct leakpair *path1, *path2;
75{
76 int i;
77
78 for (i = 0; i < SHORT_CALLSTACK_SIZE; i++) {
79 if (strcmp(path1[i].func, path2[i].func) != 0) {
80 return 0;
81 }
82 }
83 return 1;
84}
85
86
87int
88lte_size_compar(lte1, lte2)
89struct leakentry *lte1, *lte2;
90{
91 if ((lte1->all_by - lte1->fre_by) <
92 (lte2->all_by - lte2->fre_by)) {
93 return 1;
94 } else if ((lte1->all_by - lte1->fre_by) >
95 (lte2->all_by - lte2->fre_by)) {
96 return -1;
97 } else {
98 return 0;
99 }
100}
101
102#define V_TERSE 0
103#define V_NORMAL 1
104#define V_VERBOSE 2
105int verbosity;
106
107#define LEAK_NONE 0
108#define LEAK_SHOW 1
109#define LEAK_OFFSETS 2
110int leak_level;
111
112#define TYPE_EQUAL 0
113#define TYPE_GREATERTHAN 1
114void print_type_list();
115
116extern char *malloc();
117
118void print_bin_table();
119void print_leak_table();
120
121char *
122percent(m, n)
123int m, n;
124{
125 int d;
126 char buf[80];
127 char *result;
128
129 if (n == 0) {
130 d = 0;
131 } else {
132 d = (100 * m) / n;
133 }
134 sprintf(buf, "%d", d);
135 result = (char *) malloc(strlen(buf) + 1);
136 strcpy(result, buf);
137 return result;
138}
139
140char *
141percent_string(m, n)
142int m, n;
143{
144 int intpart, frac;
145 int d;
146 char buf[80];
147 char *result;
148
149 if (n == 0) {
150 goto rest;
151 } else {
152 intpart = (100 * m) / n;
153 frac = (100 * m) % n;
154 if ((intpart == 0) && (frac == 0)) {
155 return "";
156 } else if (intpart == 0) {
157 return "(.)";
158 } else if (intpart == 100) {
159 return "(**)";
160 }
161 }
162 rest:
163 if (n == 0) {
164 d = 0;
165 } else {
166 d = (100 * m) / n;
167 }
168 sprintf(buf, "(%d)", d);
169 result = (char *) malloc(strlen(buf) + 1);
170 strcpy(result, buf);
171 return result;
172}
173
174\f
175/* functions for uniquely recording recording each structure type
176 */
177struct sthash {
178 int kind;
179 char *str;
180 int size;
181 struct stconscell *numlist;
182 struct sthash *next;
183};
184
185
186#define STHASH_SIZE 2047
187struct sthash *sthmem[STHASH_SIZE];
188
189#define STNIL NULL
190
191struct stconscell {
192 int car;
193 struct stconscell *cdr;
194};
195
196struct sthash **read_and_sort_types();
197
198sthash_compar(ste1, ste2)
199struct sthash **ste1, **ste2;
200{
201 if ((*ste1)->size < (*ste2)->size) {
202 return -1;
203 } else if ((*ste1)-> size > (*ste2)->size) {
204 return 1;
205 } else {
206 return strcmp((*ste1)->str, (*ste2)->str);
207 }
208}
209
210
211struct stconscell
212*stcons(car, cdr)
213int car;
214struct stconscell *cdr;
215{
216 struct stconscell *newcons;
217 newcons = (struct stconscell *) malloc(sizeof(struct stconscell));
218 newcons->car = car;
219 newcons->cdr = cdr;
220 return newcons;
221}
222
223
224
225struct stconscell
226*stmember(item, stlist)
227int item;
228struct stconscell *stlist;
229{
230 struct stconscell *s = stlist;
231
232 while (s != STNIL) {
233 if (s->car == item)
234 return s;
235 s = s->cdr;
236 }
237 return STNIL;
238}
239
240
241bool
242stmatchlist(l1, l2)
243struct stconscell *l1, *l2;
244{
245 struct stconscell *s1, *s2;
246 int len1, len2;
247
248 for (len1 = 0, s1 = l1; s1 != STNIL; len1++, s1 = s1->cdr) ;
249 for (len2 = 0, s2 = l2; s2 != STNIL; len2++, s2 = s2->cdr) ;
250
251 if (len1 == len2) {
252 for (s1 = l1; s1 != STNIL; s1 = s1->cdr) {
253 if (stmember(s1->car, l2) == STNIL) {
254 return FALSE;
255 }
256 }
257 return TRUE;
258 }
259 return FALSE;
260}
261
262
263#define T_STRUCT 0
264#define T_TYPEDEF 1
265
266
267struct sthash
268*mpf_new_stlink(kind, str, size, numlist, next)
269int kind;
270char *str;
271int size;
272struct stconscell *numlist;
273struct sthash *next;
274{
275 struct sthash *new_stl;
276
277 new_stl = (struct sthash *) malloc(sizeof(struct sthash));
278 new_stl->kind = kind;
279 new_stl->str = str;
280 new_stl->size = size;
281 new_stl->numlist = numlist;
282 new_stl->next = next;
283 return new_stl;
284}
285
286int
287mpf_sthash(s, len)
288char *s;
289int len;
290{
291 int i;
292 int hash = 0;
293
294 for (i = 0; i < len; i++) {
295 hash = hash ^ (((int) *(s+i)) << (i % 6));
296 }
297 return ((hash >> 3) % STHASH_SIZE);
298}
299
300#define N_EXCEPTIONS 11
301
302char *struct_exceptions[] = {
303 "mp_function_struct",
304 "mp_cons_struct",
305 "mp_data_struct",
306 "mp_sstk_struct",
307 "mpheader",
308 "_physadr",
309 "_iobuf",
310 "_quad",
311 "flock",
312 "fd_set",
313 "label_t",
314#ifdef mips
315 "pdr",
316 "fdr",
317 "runtime_pdr",
318#endif
319 };
320
321
322void
323mpf_intern_type(s, size, structsize, tnumber)
324char *s;
325int size;
326int structsize;
327int tnumber;
328{
329 int hash = mpf_sthash(s, size);
330 int i;
331 struct sthash *ste = sthmem[hash];
332 char *newstr;
333
334 for (i = 0; i < N_EXCEPTIONS; i++) {
335 if (strncmp(s, struct_exceptions[i],
336 strlen(struct_exceptions[i])) == 0) {
337 return;
338 }
339 }
340 while (ste != NULL) {
341 if ((strncmp(s, ste->str, size) == 0) &&
342 (*((ste->str)+size) == NULL)) {
343 /* add the number to the list of numbers
344 */
345 if (stmember(tnumber, ste->numlist) == STNIL)
346 ste->numlist = stcons(tnumber, ste->numlist);
347 return;
348 }
349 ste = ste->next;
350 }
351 newstr = malloc((unsigned) (size + 1));
352 strncpy(newstr, s, size);
353 *(char *) ((int) newstr + size) = NULL;
354 ste = mpf_new_stlink(T_STRUCT,
355 newstr,
356 structsize,
357 stcons(tnumber, STNIL),
358 sthmem[hash]);
359 sthmem[hash] = ste;
360 return;
361}
362
363
364void
365mpf_intern_typedef(s, size, tnumber)
366char *s;
367int size;
368int tnumber;
369{
370 int hash = mpf_sthash(s, size);
371 struct sthash *ste = sthmem[hash];
372 char *newstr;
373
374 while (ste != NULL) {
375 if ((strncmp(s, ste->str, size) == 0) &&
376 (*((ste->str)+size) == NULL)) {
377
378 /* add the number to the list of numbers
379 */
380 if (stmember(tnumber, ste->numlist) == STNIL)
381 ste->numlist = stcons(tnumber, ste->numlist);
382 return;
383 }
384 ste = ste->next;
385 }
386 newstr = malloc(size + 1);
387 strncpy(newstr, s, size);
388 *(char *) ((int) newstr + size) = NULL;
389 ste = mpf_new_stlink(T_TYPEDEF,
390 newstr,
391 0,
392 stcons(tnumber, STNIL),
393 sthmem[hash]);
394 sthmem[hash] = ste;
395 return;
396}
397
398struct sthash **
399read_and_sort_types(number)
400int *number;
401{
402 struct sthash *ste, *ste1;
403 struct stconscell *typedefs, *tl, *structs, *sl;
404 int tnum;
405 int i, pair_count;
406 struct sthash **result;
407
408 /* first, make a list of all typedefs and structs
409 */
410 typedefs = STNIL;
411 structs = STNIL;
412 for (i = 0; i < STHASH_SIZE; i++) {
413 ste = sthmem[i];
414 while (ste != NULL) {
415 if (ste->kind == T_TYPEDEF) {
416 typedefs = stcons((int) ste, typedefs);
417 } else if (ste->kind == T_STRUCT) {
418 structs = stcons((int) ste, structs);
419 }
420 ste = ste->next;
421 }
422 }
423
424 /* for each typedef, if it points to a struct, change to a struct,
425 * add the size, and remove the struct
426 */
427 tl = typedefs;
428 while (tl != STNIL) {
429 ste = (struct sthash *) tl->car;
430 tnum = ste->numlist->car;
431 sl = structs;
432 while (sl != STNIL) {
433 ste1 = (struct sthash *) sl->car;
434 if ((stmember(tnum, ste1->numlist) != STNIL) &&
435 (stmatchlist(ste->numlist, ste1->numlist))) {
436 ste->kind = T_STRUCT;
437 ste->size = ste1->size;
438 ste1->kind = T_TYPEDEF;
439 break;
440 }
441 sl = sl->cdr;
442 }
443 tl = tl->cdr;
444 }
445
446 /*
447 * First, count how many there are
448 */
449 pair_count = 0;
450 for (i = 0; i < STHASH_SIZE; i++) {
451 ste = sthmem[i];
452 while (ste != NULL) {
453 if (ste->kind == T_STRUCT) {
454 pair_count += 1;
455 }
456 ste = ste->next;
457 }
458 }
459 /*
460 * Allocate a vector containing that many entries.
461 */
462 result = (struct sthash **) malloc(sizeof(struct sthash *) * pair_count);
463 pair_count = 0;
464 for (i = 0; i < STHASH_SIZE; i++) {
465 ste = sthmem[i];
466 while (ste != NULL) {
467 if (ste->kind == T_STRUCT) {
468 result[pair_count] = ste;
469 pair_count += 1;
470 }
471 ste = ste->next;
472 }
473 }
474
475 qsort((char *) result, pair_count, sizeof(struct sthash *), sthash_compar);
476
477 *number = pair_count;
478 return result;
479}
480
481\f
482
483struct finfo {
484 char *name;
485 unsigned addr;
486};
487
488#define stab_name(x) (stab[(x)].name)
489#define stab_addr(x) (stab[(x)].addr)
490
491#define ST_SIZE 5000
492#define ST_NOT_FOUND -1
493typedef int stindex;
494
495struct finfo stab[ST_SIZE];
496int stab_i;
497#define stab_incr(idx) (((idx) < ST_SIZE) ? (idx)++ : \
498 (fprintf(stderr, "stab_incr -- stab table overflow (%d)\n", \
499 idx), exit(0)));
500
501void st_init();
502int stab_compare();
503void st_read();
504#ifdef mips
505int st_read_structure();
506#else
507void st_read_structure();
508#endif
509stindex st_locate();
510void st_print();
511void st_print_one();
512
513char *st_strings;
514bool mprofing = FALSE;
515
516
517int
518stab_compare(e1, e2)
519struct finfo *e1, *e2;
520{
521 if (e1->addr < e2->addr) {
522 return -1;
523 } else if (e1-> addr > e2-> addr) {
524 return 1;
525 } else {
526 return 0;
527 }
528}
529
530#ifdef mips
531
532LDFILE *ldptr;
533
534void
535st_read(exec_name)
536char *exec_name;
537{
538 int i;
539 PDR pdr;
540 SYMR asym, asym2;
541 extern char *ldgetname(); /* why isn't this in some header file? */
542 extern pAUXU ldgetaux(); /* why isn't this in some header file? */
543 pFDR pfd;
544 pAUXU aux;
545 AUXU localaux, aux2;
546 char *name;
547 int fdindex;
548 int symindex;
549 int rfd, rfi;
550
551 ldptr = ldopen(exec_name, NULL);
552 ldreadst(ldptr, ST_PSYMS | ST_PAUXS | ST_PFDS | ST_PPDS);
553
554 /* read in pdr table */
555 for (pfd = PFD(ldptr); pfd < PFD(ldptr) + SYMHEADER(ldptr).ifdMax;
556 pfd++) {
557 for (i = pfd->ipdFirst; i < pfd->ipdFirst + pfd->cpd; i++) {
558 if (ldgetpd (ldptr, i, &pdr) != SUCCESS) {
559 fprintf(stderr, "can't read pdr %d\n", i);
560 exit (1);
561 }
562 if (pdr.isym != isymNil) {
563 if (ldtbread(ldptr, pfd->csym ? pdr.isym :
564 pdr.isym + SYMHEADER(ldptr).isymMax, &asym) != SUCCESS) {
565 fprintf(stderr, "can't read symbol");
566 exit (1);
567 }
568 pdr.adr = asym.value;
569 }
570
571 /* fill in finfo array */
572 stab[i].addr = pdr.adr;
573 if (pdr.isym == isymNil) {
574 stab[i].name = "<stripped>";
575 } else {
576 stab[i].name = ldgetname(ldptr, &asym);
577 }
578
579 }
580 }
581
582 stab_name(i) = "unknown";
583 stab_addr(i) = stab_addr(i - 1) + 0x10000;
584 stab_incr(i);
585 stab_name(i) = "end_marker";
586 stab_addr(i) = 0xffffffff;
587 stab_incr(i);
588
589 stab_i = i;
590
591 /* read in structures
592 this mips symbol table is extremely esoteric */
593
594#ifdef DEBUG
595#define err_print(str, index) fprintf(stderr, str, index)
596#else
597#define err_print(str, index)
598#endif
599
600 if (ldtbseek(ldptr) == SUCCESS) {
601
602 for (i = 0; i < SYMHEADER(ldptr).isymMax + SYMHEADER(ldptr).iextMax - 1;
603 i++) {
604
605 if (ldtbread(ldptr, i, &asym) != SUCCESS) {
606 err_print("can't read symbol, index = %d\n", asym.index);
607 continue;
608 }
609
610 /*
611 * check locals and globals for possible structures and unions
612 */
613 if (asym.st == stLocal || asym.st == stGlobal) {
614
615 if (asym.index == indexNil)
616 continue;
617
618 if (!(aux = ldgetaux(ldptr, asym.index))) {
619 err_print("can't read aux symbol, index = %d\n", asym.index);
620 continue;
621 }
622 localaux = *aux; /* have to make copy before swapping */
623
624 fdindex = ld_ifd_iaux(ldptr, asym.index);
625 if (LDAUXSWAP(ldptr, fdindex))
626 swap_aux(&localaux, ST_AUX_TIR, gethostsex());
627
628 /* see if it's a struct or union */
629 if (localaux.ti.bt == btStruct || localaux.ti.bt == btUnion) {
630
631 /* if width specified, skip it */
632 if (localaux.ti.fBitfield) {
633 aux++;
634 }
635
636 ldgetrndx(ldptr, fdindex, aux + 1, &rfd, &aux2);
637 if (!aux2.rndx.index || aux2.rndx.index == ST_ANONINDEX)
638 continue;
639
640 rfi = ldgetrfd(ldptr, PFD(ldptr)[fdindex].rfdBase + rfd);
641 symindex = PFD(ldptr)[rfi].isymBase + aux2.rndx.index;
642
643 if (ldtbread(ldptr, symindex, &asym2) != SUCCESS) {
644 err_print("can't read symbol, index = %d\n", symindex);
645 continue;
646 }
647
648 name = ldgetname(ldptr, &asym2);
649 mpf_intern_type(name, strlen(name), asym2.value, asym2.index);
650
651 }
652
653 } else if (asym.st == stTypedef) {
654
655 if (asym.index == indexNil)
656 continue;
657
658 if (!(aux = ldgetaux(ldptr, asym.index))) {
659 err_print("can't read aux symbol, index = %d\n", asym.index);
660 continue;
661 }
662 localaux = *aux; /* have to make copy before swapping */
663
664 fdindex = ld_ifd_iaux(ldptr, asym.index);
665 if (LDAUXSWAP(ldptr, fdindex))
666 swap_aux(&localaux, ST_AUX_TIR, gethostsex());
667
668 /* if width specified, skip it */
669 if (localaux.ti.fBitfield) {
670 aux++;
671 }
672
673 ldgetrndx(ldptr, fdindex, aux + 1, &rfd, &aux2);
674 if (!aux2.rndx.index || aux2.rndx.index == ST_ANONINDEX)
675 continue;
676
677 rfi = ldgetrfd(ldptr, PFD(ldptr)[fdindex].rfdBase + rfd);
678 symindex = PFD(ldptr)[rfi].isymBase + aux2.rndx.index;
679
680 if (ldtbread(ldptr, symindex, &asym2) != SUCCESS) {
681 err_print("can't read symbol, index = %d\n", symindex);
682 continue;
683 }
684
685 name = ldgetname(ldptr, &asym);
686 mpf_intern_typedef(name, strlen(name), asym2.index);
687
688 }
689
690 }
691
692 }
693
694}
695
696#else
697
698void
699st_read(exec_name)
700char *exec_name;
701{
702 int aout_file = open(exec_name, (O_RDONLY));
703 struct exec hdr;
704 struct nlist asym;
705 extern char *index();
706 extern char *malloc();
707 char *stmp;
708 int string_size;
709 unsigned char type;
710 char *fname;
711 int i;
712
713 read(aout_file, &hdr, sizeof(hdr));
714 if (!hdr.a_syms) {
715 fprintf(stdout, "st_read -- no symbols in executable\n");
716 exit(1);
717 }
718
719 /* read in the string table
720 */
721 lseek(aout_file, N_STROFF(hdr), L_SET);
722 read(aout_file, &string_size, 4);
723
724 st_strings = malloc(string_size);
725
726 lseek(aout_file, N_STROFF(hdr), L_SET);
727 read(aout_file, st_strings, string_size);
728
729 /* read in the symbols one at a time
730 */
731 lseek(aout_file, N_SYMOFF(hdr), L_SET);
732 for (i = 0; i < hdr.a_syms / sizeof(struct nlist); i++) {
733 read(aout_file, &asym, sizeof(asym));
734 type = asym.n_type;
735 /* check for functions compiled with -g
736 */
737 if (type & N_STAB) {
738 if (asym.n_type == N_FUN) {
739/*
740 stab_name(stab_i) = (char *) (st_strings + asym.n_un.n_strx);
741 stab_addr(stab_i) = asym.n_value;
742 stmp = index(stab_name(stab_i), ':');
743 *stmp = NULL;
744 stab_incr(stab_i);
745*/
746 } else if ((asym.n_type == N_LSYM) ||
747 (asym.n_type == N_GSYM)) {
748 /* a local symbol that may be a structure definition
749 */
750 st_read_structure((char *) (st_strings + asym.n_un.n_strx));
751 }
752 } else {
753 /* here's a candidate for a function name
754 */
755 if ((type & N_TYPE) == N_TEXT) {
756 fname = (char *) (st_strings + asym.n_un.n_strx);
757 if ((*fname == '_') & !index(fname, '.')) {
758 /* since there is not '.' in the name, its probably a
759 * function name
760 */
761 stab_name(stab_i) = (char *) ((int) fname + 1);
762 stab_addr(stab_i) = asym.n_value;
763 stab_incr(stab_i);
764 }
765 }
766 }
767 }
768 stab_name(stab_i) = "unknown";
769 stab_addr(stab_i) = stab_addr(stab_i - 1) + 0x10000;
770 stab_incr(stab_i);
771 stab_name(stab_i) = "end_marker";
772 stab_addr(stab_i) = 0xffffffff;
773 stab_incr(stab_i);
774 qsort(stab, stab_i, sizeof(struct finfo), stab_compare);
775}
776
777void
778st_read_structure(symp)
779char *symp;
780{
781 char *eqp, *colp;
782 int ssize, tnum;
783 extern int atoi();
784 extern char *index();
785
786 eqp = index(symp, '=');
787 colp = index(symp, ':');
788
789 if ((eqp == NULL) && (*(colp+1) == 't')) {
790 /*
791 * Check for a Sun stabs type definition.
792 */
793 if (*(colp+2) == '(') {
794 char *commap;
795 commap = index(symp, ',');
796 *commap = '0';
797 tnum = atoi((char *) index(symp, '(')+1);
798 } else {
799 tnum = atoi((char *) (colp+2));
800 }
801 mpf_intern_typedef(symp, colp - symp, tnum);
802
803 } else if ((eqp != NULL) && *(eqp+1) == 's') {
804
805 /* we have a structure entry...
806 * 1. get the size, number, and name
807 * 3. enter into the structure hash table
808 */
809 /* get the size (follows eqp+1)
810 */
811 ssize = atoi((char *) eqp+2);
812
813 /*
814 * Get the number (follows :T)
815 */
816
817 /*
818 * Check for a Sun stabs type definition.
819 */
820 if (*(colp+2) == '(') {
821 char *commap;
822 commap = index(symp, ',');
823 *commap = '0';
824 tnum = atoi((char *) index(symp, '(')+1);
825 } else {
826 tnum = atoi((char *) colp+2);
827 }
828
829 /* enter the name into the structure hash table
830 */
831 mpf_intern_type(symp, (index(symp, ':') - symp), ssize, tnum);
832 }
833}
834
835#endif
836
837stindex
838st_locate(addr)
839unsigned addr;
840{
841 int upper = stab_i - 1;
842 int lower = 0;
843 int middle = (upper + lower) / 2;
844
845 while (!((stab_addr(middle) <= addr) && (stab_addr(middle + 1) > addr))) {
846 if (middle == upper || middle == lower) {
847 return ST_NOT_FOUND;
848 }
849 if (stab_addr(middle) > addr) {
850 upper = middle;
851 } else {
852 lower = middle;
853 }
854 middle = (upper + lower) / 2;
855 }
856 if (middle >= (stab_i - 2)) {
857 return ST_NOT_FOUND;
858 } else {
859 return middle;
860 }
861}
862
863void
864st_print()
865{
866 int i;
867
868 printf("function symbol table:\n");
869 for (i = 0; i < stab_i; i++) {
870 st_print_one(i);
871 }
872}
873
874void
875st_print_one(i)
876stindex i;
877{
878 printf(" %d %-15s %10d\n", i,stab_name(i), stab_addr(i));
879}
880
881void
882increment_data(dt, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12)
883mpdata dt;
884int d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12;
885{
886 dt_b_small(dt) += d1;
887 dt_b_med(dt) += d2;
888 dt_b_large(dt) += d3;
889 dt_b_xlarge(dt) += d4;
890 dt_n_small(dt) += d5;
891 dt_n_med(dt) += d6;
892 dt_n_large(dt) += d7;
893 dt_n_xlarge(dt) += d8;
894 dt_d_small(dt) += d9;
895 dt_d_med(dt) += d10;
896 dt_d_large(dt) += d11;
897 dt_d_xlarge(dt) += d12;
898}
899
900void
901st_convert(data_filename)
902char *data_filename;
903{
904 FILE *f = fopen(data_filename, "r");
905 unsigned faddr, paddr;
906 stindex fx, px;
907 mpsym fsym, psym;
908 mpdata dcell;
909 mpcell ppair;
910 int d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12;
911 int i, x;
912
913 /*
914 * read the prolog containing stats
915 * handle the stats data first as a special case
916 */
917 check_fscanf(fscanf(f, "alloc=%d free=%d depth=%d same=%d all=%d\n", &d1, &d2, &d3, &d4, &d5));
918 fflush(stdout);
919 check_fscanf(fscanf(f, "fmem=%d dmem=%d lmem=%d smem=%d\n", &d6, &d7, &d8, &d9));
920 /*
921 * print the prolog
922 */
923 printf("--c%2s+--v3.0+--m%d+--+--+--+--+--+--+--+--+ MPROF +--+--+--+--+--+--+--s%d+--f%d+--d%d+--l%d+\n\n\n",
924 percent(d4, d5), d3, d9, d6, d7, d8);
925
926 fflush(stdout);
927
928 print_bin_table(f, stdout);
929
930 print_leak_table(f, stdout);
931
932 while (fscanf(f, "%d\n", &faddr) != EOF) {
933 fx = st_locate(faddr);
934 fsym = pc_lookup(stab_addr(fx));
935 fn_name(fsym) = stab_name(fx);
936 fscanf(f, "%d %d %d %d %d %d %d %d %d %d %d %d\n",
937 &d1, &d2, &d3, &d4, &d5, &d6, &d7, &d8, &d9, &d10, &d11, &d12);
938 increment_data(fn_lcount(fsym),
939 d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12);
940 fscanf(f, "%d\n", &paddr);
941 while ((int) paddr != MP_NIL) {
942 px = st_locate(paddr);
943 psym = pc_lookup(stab_addr(px));
944 ppair = mp_has_parent(fsym, psym);
945 if (mp_null(ppair)) {
946 dcell = mp_new_data();
947 ppair = mp_cons((int) psym, (int) dcell);
948 fn_parents(fsym) = mp_cons((int) ppair,
949 fn_parents(fsym));
950 }
951 fscanf(f, "%d %d %d %d %d %d %d %d %d %d %d %d\n",
952 &d1, &d2, &d3, &d4, &d5, &d6,
953 &d7, &d8, &d9, &d10, &d11, &d12);
954 increment_data(mp_cdr(ppair),
955 d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12);
956 fscanf(f, "%d\n", &paddr);
957 }
958 }
959 mprof_graph_ops(stdout);
960}
961
962\f
963void
964print_bin_table(infile, outfile)
965FILE *infile, *outfile;
966{
967 int i;
968 int type_count, type_index = 0;
969 struct sthash **type_table;
970 int alloc_bins[MP_NUM_BINS];
971 int free_bins[MP_NUM_BINS];
972 int big_alloc_count, big_alloc_bytes;
973 int big_free_count, big_free_bytes;
974 int alloc_count = 0, alloc_bytes = 0, free_count = 0, free_bytes = 0;
975 int other_alloc_count = 0, other_alloc_bytes = 0;
976 int other_free_count = 0, other_free_bytes = 0;
977 int abin, fbin;
978 int byte_difference;
979
980 /*
981 * Read in and print out the Bin table information.
982 */
983 type_table = read_and_sort_types(&type_count);
984
985 /* read in the bins and save the data
986 */
987 alloc_count = 0;
988 alloc_bytes = 0;
989 for (i = 0; i < (MP_NUM_BINS - 2); i++) {
990 check_fscanf(fscanf(infile, "%d\n", &alloc_bins[i]));
991 alloc_count += alloc_bins[i];
992 alloc_bytes += alloc_bins[i] * i;
993 }
994 check_fscanf(fscanf(infile, "%d\n", &big_alloc_count));
995 alloc_count += big_alloc_count;
996 if (alloc_count == 0) {
997 alloc_count = 1;
998 }
999 check_fscanf(fscanf(infile, "%d\n", &big_alloc_bytes));
1000 alloc_bytes += big_alloc_bytes;
1001
1002 free_count = 0;
1003 free_bytes = 0;
1004 for (i = 0; i < (MP_NUM_BINS - 2); i++) {
1005 check_fscanf(fscanf(infile, "%d\n", &free_bins[i]));
1006 free_count += free_bins[i];
1007 free_bytes += free_bins[i] * i;
1008 }
1009 check_fscanf(fscanf(infile, "%d\n", &big_free_count));
1010 free_count += big_free_count;
1011 if (free_count == 0) {
1012 free_count = 1;
1013 }
1014 check_fscanf(fscanf(infile, "%d\n", &big_free_bytes));
1015 free_bytes += big_free_bytes;
1016
1017 byte_difference = alloc_bytes - free_bytes;
1018
1019#define abin_template \
1020 "%11d%10d%10d %-4s%10d%10d %-4s "
1021#define abin_template2 \
1022 "%11s%10d%10d %-4s%10d%10d %-4s "
1023#define abin_titles_template \
1024 "%11s%10s%10s %-4s%10s%10s %-4s %-8s\n"
1025
1026 fprintf(outfile, "--------- Allocation Bins with possible Types ------------\n\n");
1027 fprintf(outfile, abin_titles_template,
1028 "size:", "allocs", "bytes", "(%)", "frees", "kept", "(%)", "types");
1029 fprintf(outfile, "\n");
1030
1031 for (i = 0; i < (MP_NUM_BINS - 2); i++) {
1032 abin = alloc_bins[i];
1033 fbin = free_bins[i];
1034 /*
1035 * Print things depending on the level of verbosity.
1036 */
1037 if (((verbosity == V_VERBOSE) &&
1038 ((abin > 0) || (fbin > 0)))
1039 ||
1040 ((verbosity == V_NORMAL) &&
1041 (((abin > 0) && (i > 100)) ||
1042 (((double) abin / alloc_count) > 1.0/500.0) ||
1043 ((type_index != type_count) &&
1044 (((double) abin / alloc_count) > 1.0/100.0) &&
1045 ((type_table[type_index])->size == i))))
1046 ||
1047 ((verbosity == V_TERSE) &&
1048 ((((double) abin / alloc_count) > 1.0/50.0) ||
1049 ((type_index != type_count) &&
1050 (((double) abin / alloc_count) > 1.0/100.0) &&
1051 ((type_table[type_index])->size == i))))) {
1052 fprintf(outfile, abin_template,
1053 i, abin, i * abin,
1054 percent_string(i * abin, alloc_bytes),
1055 fbin, (i * abin) - (i * fbin),
1056 percent_string((i * abin) - (i * fbin),
1057 byte_difference));
1058 /*
1059 * Print out relevant types.
1060 */
1061 print_type_list(outfile, type_table, TYPE_EQUAL, i,
1062 type_count, &type_index);
1063
1064 fprintf(outfile, "\n");
1065 } else {
1066 other_alloc_count += abin;
1067 other_free_count += fbin;
1068 other_alloc_bytes += abin * i;
1069 other_free_bytes += fbin * i;
1070 }
1071 }
1072 /*
1073 * Print the things at the end of the table.
1074 */
1075 fprintf(outfile, abin_template2,
1076 "> 1024", big_alloc_count, big_alloc_bytes,
1077 percent_string(big_alloc_bytes, alloc_bytes),
1078 big_free_count, (big_alloc_bytes - big_free_bytes),
1079 percent_string((big_alloc_bytes - big_free_bytes),
1080 byte_difference));
1081 print_type_list(outfile, type_table, TYPE_GREATERTHAN, 1024,
1082 type_count, &type_index);
1083 fprintf(outfile, "\n\n");
1084 if ((other_alloc_count > 0) ||
1085 (other_free_count > 0)) {
1086 fprintf(outfile, abin_template2,
1087 "other bins", other_alloc_count, other_alloc_bytes,
1088 percent_string(other_alloc_bytes, alloc_bytes),
1089 other_free_count, (other_alloc_bytes - other_free_bytes),
1090 percent_string((other_alloc_bytes - other_free_bytes),
1091 byte_difference));
1092 fprintf(outfile, "\n");
1093 }
1094 fprintf(outfile, abin_template2,
1095 "<TOTAL>", alloc_count, alloc_bytes, "",
1096 free_count, byte_difference, "");
1097 fprintf(outfile, "\n\f\n\n");
1098 fflush(outfile);
1099}
1100
1101
1102void
1103print_type_list(outfile, tlist, compar, binsize, type_count, type_index)
1104FILE *outfile;
1105struct sthash **tlist;
1106int compar, binsize, type_count;
1107int *type_index;
1108{
1109 int i, cond, pcount = 0;
1110
1111 for (i = *type_index;
1112 ((i < type_count) && (tlist[i]->size <= binsize));
1113 i++) {
1114 if (compar == TYPE_EQUAL) {
1115 cond = (tlist[i]->size == binsize);
1116 } else if (compar == TYPE_GREATERTHAN) {
1117 cond = (tlist[i]->size > binsize);
1118 }
1119 if (cond) {
1120 /*
1121 * Make the spacing nice.
1122 */
1123 if ((pcount > 0) && ((pcount % 3) == 0)) {
1124 fprintf(outfile, "\n%64s", "");
1125 }
1126 fprintf(outfile, "%-12s ", tlist[i]->str);
1127 pcount += 1;
1128 }
1129 }
1130 *type_index = i;
1131}
1132
1133\f
1134
1135#define leak_titles_template1 \
1136 "%10s %-4s%10s%10s %-4s%10s%10s %-4s %-8s\n"
1137#define leak_titles_template2 \
1138 "%10s %-4s%10s%10s %-4s %-8s\n"
1139#define leak_template1 \
1140 "%10d %-4s%10d%10d %-4s%10d%10d %-4s "
1141#define leak_template2 \
1142 "%10d %-4s%10d%10d %-4s "
1143
1144void
1145print_leak_table(infile, outfile)
1146FILE *infile, *outfile;
1147{
1148 int total_allocs = 0, bytes_alloced = 0;
1149 int total_frees= 0, bytes_freed= 0;
1150 int byte_diff;
1151 struct leakentry *lt_root = NULL, *lte = NULL, *lt_vec = NULL;
1152 int lte_count = 0;
1153 int d1, d2, d3, d4, d5, i, j, real_i;
1154 stindex fx;
1155 mpsym fsym;
1156
1157 /* read in the leak table and print it back out
1158 */
1159 check_fscanf(fscanf(infile, "%d %d %d %d\n", &d1, &d2, &d3, &d4));
1160 while(d1 != -2) {
1161 /*
1162 * Gather the path for a single leak table entry.
1163 */
1164 lte = (struct leakentry *) malloc(sizeof(struct leakentry));
1165 lte_count += 1;
1166
1167 for (i = 0; i < SHORT_CALLSTACK_SIZE; i++) {
1168 check_fscanf(fscanf(infile, "%d\n", &d5));
1169 if (d5 != 0) {
1170 fx = st_locate(d5);
1171 fsym = pc_lookup(stab_addr(fx));
1172 fn_name(fsym) = stab_name(fx);
1173 lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].func = fn_name(fsym);
1174 lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].offset = d5 - stab_addr(fx);
1175 } else {
1176 lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].func = "";
1177 lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].offset = 0;
1178 }
1179 }
1180 lte->all_no = d1;
1181 total_allocs += d1;
1182 lte->all_by = d2;
1183 bytes_alloced += d2;
1184 lte->fre_no = d3;
1185 total_frees += d3;
1186 lte->fre_by = d4;
1187 bytes_freed += d4;
1188
1189 /*
1190 * Add to the list of leak entries.
1191 */
1192 lte->next = lt_root;
1193 lt_root = lte;
1194
1195 check_fscanf(fscanf(infile, "%d %d %d %d", &d1, &d2, &d3, &d4));
1196 }
1197 byte_diff = bytes_alloced - bytes_freed;
1198
1199 if ((lte_count == 0) || (leak_level == LEAK_NONE)) {
1200 return;
1201 }
1202 fprintf(outfile, "--------- Partial Dynamic Call Paths for Memory Leaks ------------\n\n");
1203 fprintf(outfile, "Total bytes not freed: %d\n\n", byte_diff);
1204
1205 if (total_frees > 0) {
1206 fprintf(outfile, leak_titles_template1,
1207 "kept bytes", "(%)", "allocs", "bytes", "(%)",
1208 "frees", "bytes", "(%)", "path");
1209 } else {
1210 fprintf(outfile, leak_titles_template2,
1211 "kept bytes", "(%)", "allocs", "bytes", "(%)", "path");
1212 }
1213 fprintf(outfile, "\n");
1214
1215 /*
1216 * Here we put the leak table entries into a vector so we can
1217 * manipulate them.
1218 */
1219 lt_vec = (struct leakentry *) malloc(sizeof(struct leakentry) * lte_count);
1220 lte = lt_root;
1221 i = 0;
1222 while (lte != NULL) {
1223 struct leakentry *tmp;
1224
1225 lt_vec[i] = *lte;
1226 i++;
1227 tmp = lte;
1228 lte = lte->next;
1229 free(tmp);
1230 }
1231
1232 if (leak_level == LEAK_SHOW) {
1233 /*
1234 * Sort the entries so that duplicate paths are together.
1235 */
1236 qsort((char *) lt_vec, lte_count, sizeof(struct leakentry), lte_str_compar);
1237 if (lte_count > 1) {
1238 real_i = 0;
1239 for (i = 1; i < lte_count;) {
1240 if (path_equal(lt_vec[real_i].path, lt_vec[i].path)) {
1241 /*
1242 * Merge the data from identical paths together.
1243 */
1244 lt_vec[real_i].all_no += lt_vec[i].all_no;
1245 lt_vec[real_i].all_by += lt_vec[i].all_by;
1246 lt_vec[real_i].fre_no += lt_vec[i].fre_no;
1247 lt_vec[real_i].fre_by += lt_vec[i].fre_by;
1248 i += 1;
1249 } else {
1250 real_i += 1;
1251 /*
1252 * First, copy the data, compressing out bubbles
1253 */
1254 if (real_i != i) {
1255 lt_vec[real_i] = lt_vec[i];
1256 }
1257 i += 1;
1258 }
1259 }
1260 lte_count = real_i + 1;
1261 }
1262 }
1263
1264 qsort((char *) lt_vec, lte_count, sizeof(struct leakentry), lte_size_compar);
1265
1266 for (i = 0; i < lte_count; i+=1) {
1267 struct leakentry lte;
1268 lte = lt_vec[i];
1269 if (verbosity == V_TERSE) {
1270 if (((double) lte.all_by / bytes_alloced) < 0.01) {
1271 continue;
1272 }
1273 } else if (verbosity == V_NORMAL) {
1274 if (((double) lte.all_by / bytes_alloced) < 0.005) {
1275 continue;
1276 }
1277 }
1278 if (total_frees > 0) {
1279 fprintf(outfile, leak_template1,
1280 (lte.all_by - lte.fre_by),
1281 percent_string((lte.all_by - lte.fre_by), byte_diff),
1282 lte.all_no, lte.all_by,
1283 percent_string(lte.all_by, bytes_alloced),
1284 lte.fre_no, lte.fre_by,
1285 percent_string(lte.fre_by, bytes_freed));
1286 } else {
1287 fprintf(outfile, leak_template2,
1288 (lte.all_by - lte.fre_by),
1289 percent_string((lte.all_by - lte.fre_by), byte_diff),
1290 lte.all_no, lte.all_by,
1291 percent_string(lte.all_by, bytes_alloced));
1292 }
1293 if ((strcmp(lte.path[0].func, "") == 0) ||
1294 (strcmp(lte.path[0].func, "main") == 0)) {
1295 fprintf(outfile, "|| ");
1296 } else {
1297 fprintf(outfile, "...");
1298 }
1299 for (j = 0; j < SHORT_CALLSTACK_SIZE; j++) {
1300 if (strcmp(lte.path[j].func, "") != 0) {
1301 if (leak_level == LEAK_SHOW) {
1302 fprintf(outfile, "> %s ", lte.path[j].func);
1303 } else if (leak_level == LEAK_OFFSETS) {
1304 fprintf(outfile, "> %s+%d ", lte.path[j].func, lte.path[j].offset);
1305 }
1306 }
1307 }
1308 fprintf(outfile, "\n");
1309 }
1310 fprintf(outfile, "\n\f\n\n");
1311 fflush(outfile);
1312}
1313
1314
1315
1316\f
1317void
1318usage()
1319{
1320 fprintf(stderr, "usage: mprof [-leaktable | -noleaktable] \n\
1321 [-verbose | -normal | -terse] \n\
1322 [a.out-name] [data-name]\n");
1323 exit(1);
1324}
1325
1326#define str_equal(s1, s2) (strcmp((s1), (s2)) == 0)
1327
1328int
1329main(argc, argv)
1330int argc;
1331char *argv[];
1332{
1333 int i;
1334 char *exec_file = "a.out";
1335 char *data_file = "mprof.data";
1336
1337 /*
1338 * Default settings.
1339 */
1340 leak_level = LEAK_SHOW;
1341 verbosity = V_NORMAL;
1342
1343 for (i = 1; i < argc; i++) {
1344 if (str_equal(argv[i], "-leaktable")) {
1345 leak_level = LEAK_SHOW;
1346 } else if (str_equal(argv[i], "-noleaktable")) {
1347 leak_level = LEAK_NONE;
1348 } else if (str_equal(argv[i], "-offsets")) {
1349 leak_level = LEAK_OFFSETS;
1350 } else if (str_equal(argv[i], "-verbose")) {
1351 verbosity = V_VERBOSE;
1352 } else if (str_equal(argv[i], "-normal")) {
1353 verbosity = V_NORMAL;
1354 } else if (str_equal(argv[i], "-terse")) {
1355 verbosity = V_TERSE;
1356 } else if (i == (argc - 1)) {
1357 exec_file = argv[i];
1358 break;
1359 } else if (i == (argc - 2)) {
1360 exec_file = argv[i];
1361 data_file = argv[i+1];
1362 break;
1363 } else {
1364 usage();
1365 }
1366 }
1367
1368 mpstruct_init();
1369 stab_i = 0;
1370 for (i = 0; i < ST_SIZE; i++) {
1371 stab_name(i) = NULL;
1372 }
1373
1374 st_read(exec_file);
1375 st_convert(data_file);
1376
1377 exit(0);
1378}
1379