/* %M% %I% %G% %U% */ /* Copyright (c) 1987, 1990, Benjamin G. Zorn */ /* mprof.c -- code to analyse and print out mprof data */ #include #include #include #ifdef __ELF__ #include #endif #include #include #include "mprof.h" #ifdef mips #include #endif #if defined(mips) || defined(vax) char * strdup(s) char *s; { char *result = malloc(strlen(s) + 1); (void) strcpy(result, s); return result; } #endif extern void mprof_graph_ops(); /* from mpgraph.c */ #define check_fscanf(x) \ { int result = (x); \ if (!result) { \ fprintf(stderr, "fscanf -- can't read input\n"); \ exit(1); \ } \ } struct leakpair { char *func; int offset; }; struct leakentry { struct leakpair path[SHORT_CALLSTACK_SIZE]; int all_no; int all_by; int fre_no; int fre_by; struct leakentry *next; }; lte_str_compar(lte1, lte2) struct leakentry *lte1, *lte2; { struct leakpair *path1, *path2; int i; path1 = lte1->path; path2 = lte2->path; for (i = 0; i < SHORT_CALLSTACK_SIZE; i++) { if (strcmp(path1[i].func, path2[i].func) != 0) { return strcmp(path1[i].func, path2[i].func); } } return 0; } int path_equal(path1, path2) struct leakpair *path1, *path2; { int i; for (i = 0; i < SHORT_CALLSTACK_SIZE; i++) { if (strcmp(path1[i].func, path2[i].func) != 0) { return 0; } } return 1; } int lte_size_compar(lte1, lte2) struct leakentry *lte1, *lte2; { if ((lte1->all_by - lte1->fre_by) < (lte2->all_by - lte2->fre_by)) { return 1; } else if ((lte1->all_by - lte1->fre_by) > (lte2->all_by - lte2->fre_by)) { return -1; } else { return 0; } } #define V_TERSE 0 #define V_NORMAL 1 #define V_VERBOSE 2 int verbosity; #define LEAK_NONE 0 #define LEAK_SHOW 1 #define LEAK_OFFSETS 2 int leak_level; #define TYPE_EQUAL 0 #define TYPE_GREATERTHAN 1 void print_type_list(); extern char *malloc(); void print_bin_table(); void print_leak_table(); char * percent(m, n) int m, n; { int d; char buf[80]; char *result; if (n == 0) { d = 0; } else { d = (100 * m) / n; } sprintf(buf, "%d", d); result = (char *) malloc(strlen(buf) + 1); strcpy(result, buf); return result; } char * percent_string(m, n) int m, n; { int intpart, frac; int d; char buf[80]; char *result; if (n == 0) { goto rest; } else { intpart = (100 * m) / n; frac = (100 * m) % n; if ((intpart == 0) && (frac == 0)) { return ""; } else if (intpart == 0) { return "(.)"; } else if (intpart == 100) { return "(**)"; } } rest: if (n == 0) { d = 0; } else { d = (100 * m) / n; } sprintf(buf, "(%d)", d); result = (char *) malloc(strlen(buf) + 1); strcpy(result, buf); return result; } /* functions for uniquely recording recording each structure type */ struct sthash { int kind; char *str; int size; struct stconscell *numlist; struct sthash *next; }; #define STHASH_SIZE (2<<20 -1) struct sthash *sthmem[STHASH_SIZE]; #define STNIL NULL struct stconscell { int car; struct stconscell *cdr; }; struct sthash **read_and_sort_types(); sthash_compar(ste1, ste2) struct sthash **ste1, **ste2; { if ((*ste1)->size < (*ste2)->size) { return -1; } else if ((*ste1)-> size > (*ste2)->size) { return 1; } else { return strcmp((*ste1)->str, (*ste2)->str); } } struct stconscell *stcons(car, cdr) int car; struct stconscell *cdr; { struct stconscell *newcons; newcons = (struct stconscell *) malloc(sizeof(struct stconscell)); newcons->car = car; newcons->cdr = cdr; return newcons; } struct stconscell *stmember(item, stlist) int item; struct stconscell *stlist; { struct stconscell *s = stlist; while (s != STNIL) { if (s->car == item) return s; s = s->cdr; } return STNIL; } bool stmatchlist(l1, l2) struct stconscell *l1, *l2; { struct stconscell *s1, *s2; int len1, len2; for (len1 = 0, s1 = l1; s1 != STNIL; len1++, s1 = s1->cdr) ; for (len2 = 0, s2 = l2; s2 != STNIL; len2++, s2 = s2->cdr) ; if (len1 == len2) { for (s1 = l1; s1 != STNIL; s1 = s1->cdr) { if (stmember(s1->car, l2) == STNIL) { return FALSE; } } return TRUE; } return FALSE; } #define T_STRUCT 0 #define T_TYPEDEF 1 struct sthash *mpf_new_stlink(kind, str, size, numlist, next) int kind; char *str; int size; struct stconscell *numlist; struct sthash *next; { struct sthash *new_stl; new_stl = (struct sthash *) malloc(sizeof(struct sthash)); new_stl->kind = kind; new_stl->str = str; new_stl->size = size; new_stl->numlist = numlist; new_stl->next = next; return new_stl; } int mpf_sthash(s, len) char *s; int len; { int i; int hash = 0; for (i = 0; i < len; i++) { hash = hash ^ (((int) *(s+i)) << (i % 6)); } return ((hash >> 3) % STHASH_SIZE); } #define N_EXCEPTIONS 11 char *struct_exceptions[] = { "mp_function_struct", "mp_cons_struct", "mp_data_struct", "mp_sstk_struct", "mpheader", "_physadr", "_iobuf", "_quad", "flock", "fd_set", "label_t", #ifdef mips "pdr", "fdr", "runtime_pdr", #endif }; void mpf_intern_type(s, size, structsize, tnumber) char *s; int size; int structsize; int tnumber; { int hash = mpf_sthash(s, size); int i; struct sthash *ste = sthmem[hash]; char *newstr; for (i = 0; i < N_EXCEPTIONS; i++) { if (strncmp(s, struct_exceptions[i], strlen(struct_exceptions[i])) == 0) { return; } } while (ste != NULL) { if ((strncmp(s, ste->str, size) == 0) && (*((ste->str)+size) == NULL)) { /* add the number to the list of numbers */ if (stmember(tnumber, ste->numlist) == STNIL) ste->numlist = stcons(tnumber, ste->numlist); return; } ste = ste->next; } newstr = malloc((unsigned) (size + 1)); strncpy(newstr, s, size); *(char *) ((int) newstr + size) = NULL; ste = mpf_new_stlink(T_STRUCT, newstr, structsize, stcons(tnumber, STNIL), sthmem[hash]); sthmem[hash] = ste; return; } void mpf_intern_typedef(s, size, tnumber) char *s; int size; int tnumber; { int hash = mpf_sthash(s, size); struct sthash *ste = sthmem[hash]; char *newstr; while (ste != NULL) { if ((strncmp(s, ste->str, size) == 0) && (*((ste->str)+size) == NULL)) { /* add the number to the list of numbers */ if (stmember(tnumber, ste->numlist) == STNIL) ste->numlist = stcons(tnumber, ste->numlist); return; } ste = ste->next; } newstr = malloc(size + 1); strncpy(newstr, s, size); *(char *) ((int) newstr + size) = NULL; ste = mpf_new_stlink(T_TYPEDEF, newstr, 0, stcons(tnumber, STNIL), sthmem[hash]); sthmem[hash] = ste; return; } struct sthash ** read_and_sort_types(number) int *number; { struct sthash *ste, *ste1; struct stconscell *typedefs, *tl, *structs, *sl; int tnum; int i, pair_count; struct sthash **result; /* first, make a list of all typedefs and structs */ typedefs = STNIL; structs = STNIL; for (i = 0; i < STHASH_SIZE; i++) { ste = sthmem[i]; while (ste != NULL) { if (ste->kind == T_TYPEDEF) { typedefs = stcons((int) ste, typedefs); } else if (ste->kind == T_STRUCT) { structs = stcons((int) ste, structs); } ste = ste->next; } } /* for each typedef, if it points to a struct, change to a struct, * add the size, and remove the struct */ tl = typedefs; while (tl != STNIL) { ste = (struct sthash *) tl->car; tnum = ste->numlist->car; sl = structs; while (sl != STNIL) { ste1 = (struct sthash *) sl->car; if ((stmember(tnum, ste1->numlist) != STNIL) && (stmatchlist(ste->numlist, ste1->numlist))) { ste->kind = T_STRUCT; ste->size = ste1->size; ste1->kind = T_TYPEDEF; break; } sl = sl->cdr; } tl = tl->cdr; } /* * First, count how many there are */ pair_count = 0; for (i = 0; i < STHASH_SIZE; i++) { ste = sthmem[i]; while (ste != NULL) { if (ste->kind == T_STRUCT) { pair_count += 1; } ste = ste->next; } } /* * Allocate a vector containing that many entries. */ result = (struct sthash **) malloc(sizeof(struct sthash *) * pair_count); pair_count = 0; for (i = 0; i < STHASH_SIZE; i++) { ste = sthmem[i]; while (ste != NULL) { if (ste->kind == T_STRUCT) { result[pair_count] = ste; pair_count += 1; } ste = ste->next; } } qsort((char *) result, pair_count, sizeof(struct sthash *), sthash_compar); *number = pair_count; return result; } struct finfo { char *name; unsigned addr; }; #define stab_name(x) (stab[(x)].name) #define stab_addr(x) (stab[(x)].addr) #define ST_SIZE 500000 #define ST_NOT_FOUND -1 typedef int stindex; struct finfo stab[ST_SIZE]; int stab_i; #define stab_incr(idx) (((idx) < ST_SIZE) ? (idx)++ : \ (fprintf(stderr, "stab_incr -- stab table overflow (%d)\n", \ idx), exit(0))); void st_init(); int stab_compare(); void st_read(); #ifdef mips int st_read_structure(); #else void st_read_structure(); #endif stindex st_locate(); void st_print(); void st_print_one(); char *st_strings; bool mprofing = FALSE; int stab_compare(e1, e2) struct finfo *e1, *e2; { if (e1->addr < e2->addr) { return -1; } else if (e1-> addr > e2-> addr) { return 1; } else { return 0; } } #ifdef mips LDFILE *ldptr; void st_read(exec_name) char *exec_name; { int i; PDR pdr; SYMR asym, asym2; extern char *ldgetname(); /* why isn't this in some header file? */ extern pAUXU ldgetaux(); /* why isn't this in some header file? */ pFDR pfd; pAUXU aux; AUXU localaux, aux2; char *name; int fdindex; int symindex; int rfd, rfi; ldptr = ldopen(exec_name, NULL); ldreadst(ldptr, ST_PSYMS | ST_PAUXS | ST_PFDS | ST_PPDS); /* read in pdr table */ for (pfd = PFD(ldptr); pfd < PFD(ldptr) + SYMHEADER(ldptr).ifdMax; pfd++) { for (i = pfd->ipdFirst; i < pfd->ipdFirst + pfd->cpd; i++) { if (ldgetpd (ldptr, i, &pdr) != SUCCESS) { fprintf(stderr, "can't read pdr %d\n", i); exit (1); } if (pdr.isym != isymNil) { if (ldtbread(ldptr, pfd->csym ? pdr.isym : pdr.isym + SYMHEADER(ldptr).isymMax, &asym) != SUCCESS) { fprintf(stderr, "can't read symbol"); exit (1); } pdr.adr = asym.value; } /* fill in finfo array */ stab[i].addr = pdr.adr; if (pdr.isym == isymNil) { stab[i].name = ""; } else { stab[i].name = ldgetname(ldptr, &asym); } } } stab_name(i) = "unknown"; stab_addr(i) = stab_addr(i - 1) + 0x10000; stab_incr(i); stab_name(i) = "end_marker"; stab_addr(i) = 0xffffffff; stab_incr(i); stab_i = i; /* read in structures this mips symbol table is extremely esoteric */ #ifdef DEBUG #define err_print(str, index) fprintf(stderr, str, index) #else #define err_print(str, index) #endif if (ldtbseek(ldptr) == SUCCESS) { for (i = 0; i < SYMHEADER(ldptr).isymMax + SYMHEADER(ldptr).iextMax - 1; i++) { if (ldtbread(ldptr, i, &asym) != SUCCESS) { err_print("can't read symbol, index = %d\n", asym.index); continue; } /* * check locals and globals for possible structures and unions */ if (asym.st == stLocal || asym.st == stGlobal) { if (asym.index == indexNil) continue; if (!(aux = ldgetaux(ldptr, asym.index))) { err_print("can't read aux symbol, index = %d\n", asym.index); continue; } localaux = *aux; /* have to make copy before swapping */ fdindex = ld_ifd_iaux(ldptr, asym.index); if (LDAUXSWAP(ldptr, fdindex)) swap_aux(&localaux, ST_AUX_TIR, gethostsex()); /* see if it's a struct or union */ if (localaux.ti.bt == btStruct || localaux.ti.bt == btUnion) { /* if width specified, skip it */ if (localaux.ti.fBitfield) { aux++; } ldgetrndx(ldptr, fdindex, aux + 1, &rfd, &aux2); if (!aux2.rndx.index || aux2.rndx.index == ST_ANONINDEX) continue; rfi = ldgetrfd(ldptr, PFD(ldptr)[fdindex].rfdBase + rfd); symindex = PFD(ldptr)[rfi].isymBase + aux2.rndx.index; if (ldtbread(ldptr, symindex, &asym2) != SUCCESS) { err_print("can't read symbol, index = %d\n", symindex); continue; } name = ldgetname(ldptr, &asym2); mpf_intern_type(name, strlen(name), asym2.value, asym2.index); } } else if (asym.st == stTypedef) { if (asym.index == indexNil) continue; if (!(aux = ldgetaux(ldptr, asym.index))) { err_print("can't read aux symbol, index = %d\n", asym.index); continue; } localaux = *aux; /* have to make copy before swapping */ fdindex = ld_ifd_iaux(ldptr, asym.index); if (LDAUXSWAP(ldptr, fdindex)) swap_aux(&localaux, ST_AUX_TIR, gethostsex()); /* if width specified, skip it */ if (localaux.ti.fBitfield) { aux++; } ldgetrndx(ldptr, fdindex, aux + 1, &rfd, &aux2); if (!aux2.rndx.index || aux2.rndx.index == ST_ANONINDEX) continue; rfi = ldgetrfd(ldptr, PFD(ldptr)[fdindex].rfdBase + rfd); symindex = PFD(ldptr)[rfi].isymBase + aux2.rndx.index; if (ldtbread(ldptr, symindex, &asym2) != SUCCESS) { err_print("can't read symbol, index = %d\n", symindex); continue; } name = ldgetname(ldptr, &asym); mpf_intern_typedef(name, strlen(name), asym2.index); } } } } #else void st_read(exec_name) char *exec_name; { #ifdef __ELF__ int elf_file = open(exec_name, (O_RDONLY)); Elf_Ehdr ehdr; Elf_Shdr shdr, shstrhdr, symhdr, strhdr, stabhdr, stabstrhdr; int gotsym, gotstr, gotstab, gotstabstr; char *sh_strings; char *stab_strings; Elf_Sym asym; extern char *index(); extern char *malloc(); char *stmp; int string_size; unsigned char type; char *fname; int i; read(elf_file, &ehdr, sizeof(ehdr)); if (!ehdr.e_shnum) { fprintf(stdout, "st_read -- no symbols in executable\n"); exit(1); } /* read the section names */ lseek(elf_file, ehdr.e_shoff + ehdr.e_shstrndx * ehdr.e_shentsize, L_SET); read(elf_file, &shstrhdr, ehdr.e_shentsize); sh_strings = malloc(shstrhdr.sh_size); lseek(elf_file, shstrhdr.sh_offset, L_SET); read(elf_file, sh_strings, shstrhdr.sh_size); /* find the stab sections */ gotsym = gotstr = 0; for (i = 0; i < ehdr.e_shnum; i++) { lseek(elf_file, ehdr.e_shoff + i * ehdr.e_shentsize, L_SET); read(elf_file, &shdr, ehdr.e_shentsize); if (!strcmp(sh_strings + shdr.sh_name, ".stab")) { stabhdr = shdr; gotstab = 1; } else if (!strcmp(sh_strings + shdr.sh_name, ".stabstr")) { stabstrhdr = shdr; gotstabstr = 1; } else if (shdr.sh_type == SHT_SYMTAB) { symhdr = shdr; gotsym = 1; } else if (shdr.sh_type == SHT_STRTAB) { strhdr = shdr; gotstr = 1; } } if (!gotsym || !gotstr) { fprintf(stdout, "st_read -- no symbols in executable\n"); exit(1); } /* read in the string table */ st_strings = malloc(strhdr.sh_size); lseek(elf_file, strhdr.sh_offset, L_SET); read(elf_file, st_strings, strhdr.sh_size); if (gotstabstr) { stab_strings = malloc(stabstrhdr.sh_size); lseek(elf_file, stabstrhdr.sh_offset, L_SET); read(elf_file, stab_strings, stabstrhdr.sh_size); } /* read in the symbols one at a time */ lseek(elf_file, symhdr.sh_offset, L_SET); for (i = 0; i < symhdr.sh_size / sizeof(Elf_Sym); i++) { read(elf_file, &asym, sizeof(asym)); type = ELF_ST_TYPE(asym.st_info); if (type == STT_FUNC) { /* here's a candidate for a function name */ fname = (char *) (st_strings + asym.st_name); stab_name(stab_i) = fname; stab_addr(stab_i) = asym.st_value; stab_incr(stab_i); } } /* read the stab sections to look for structures */ if (gotstab) { struct nlist stab; lseek(elf_file, stabhdr.sh_offset, L_SET); for (i = 0; i < stabhdr.sh_size / sizeof(struct nlist); i++) { read(elf_file, &stab, sizeof(stab)); if (stab.n_type == N_LSYM || stab.n_type == N_GSYM) { st_read_structure((char *) (stab_strings + stab.n_un.n_strx)); } } } stab_name(stab_i) = "unknown"; stab_addr(stab_i) = stab_addr(stab_i - 1) + 0x10000; stab_incr(stab_i); stab_name(stab_i) = "end_marker"; stab_addr(stab_i) = 0xffffffff; stab_incr(stab_i); qsort(stab, stab_i, sizeof(struct finfo), stab_compare); close(elf_file); #else int aout_file = open(exec_name, (O_RDONLY)); struct exec hdr; struct nlist asym; extern char *index(); extern char *malloc(); char *stmp; int string_size; unsigned char type; char *fname; int i; read(aout_file, &hdr, sizeof(hdr)); if (!hdr.a_syms) { fprintf(stdout, "st_read -- no symbols in executable\n"); exit(1); } /* read in the string table */ lseek(aout_file, N_STROFF(hdr), L_SET); read(aout_file, &string_size, 4); st_strings = malloc(string_size); lseek(aout_file, N_STROFF(hdr), L_SET); read(aout_file, st_strings, string_size); /* read in the symbols one at a time */ lseek(aout_file, N_SYMOFF(hdr), L_SET); for (i = 0; i < hdr.a_syms / sizeof(struct nlist); i++) { read(aout_file, &asym, sizeof(asym)); type = asym.n_type; /* check for functions compiled with -g */ if (type & N_STAB) { if (asym.n_type == N_FUN) { /* stab_name(stab_i) = (char *) (st_strings + asym.n_un.n_strx); stab_addr(stab_i) = asym.n_value; stmp = index(stab_name(stab_i), ':'); *stmp = NULL; stab_incr(stab_i); */ } else if ((asym.n_type == N_LSYM) || (asym.n_type == N_GSYM)) { /* a local symbol that may be a structure definition */ st_read_structure((char *) (st_strings + asym.n_un.n_strx)); } } else { /* here's a candidate for a function name */ if ((type & N_TYPE) == N_TEXT) { fname = (char *) (st_strings + asym.n_un.n_strx); if ((*fname == '_') & !index(fname, '.')) { /* since there is not '.' in the name, its probably a * function name */ stab_name(stab_i) = (char *) ((int) fname + 1); stab_addr(stab_i) = asym.n_value; stab_incr(stab_i); } } } } stab_name(stab_i) = "unknown"; stab_addr(stab_i) = stab_addr(stab_i - 1) + 0x10000; stab_incr(stab_i); stab_name(stab_i) = "end_marker"; stab_addr(stab_i) = 0xffffffff; stab_incr(stab_i); qsort(stab, stab_i, sizeof(struct finfo), stab_compare); #endif } void st_read_structure(symp) char *symp; { char *eqp, *colp; int ssize, tnum; extern int atoi(); extern char *index(); eqp = index(symp, '='); colp = index(symp, ':'); if ((eqp == NULL) && (*(colp+1) == 't')) { /* * Check for a Sun stabs type definition. */ if (*(colp+2) == '(') { char *commap; commap = index(symp, ','); if (commap) *commap = '0'; tnum = atoi((char *) index(symp, '(')+1); } else { tnum = atoi((char *) (colp+2)); } mpf_intern_typedef(symp, colp - symp, tnum); } else if ((eqp != NULL) && *(eqp+1) == 's') { /* we have a structure entry... * 1. get the size, number, and name * 3. enter into the structure hash table */ /* get the size (follows eqp+1) */ ssize = atoi((char *) eqp+2); /* * Get the number (follows :T) */ /* * Check for a Sun stabs type definition. */ if (*(colp+2) == '(') { char *commap; commap = index(symp, ','); if (commap) *commap = '0'; tnum = atoi((char *) index(symp, '(')+1); } else { tnum = atoi((char *) colp+2); } /* enter the name into the structure hash table */ mpf_intern_type(symp, (index(symp, ':') - symp), ssize, tnum); } } #endif stindex st_locate(addr) unsigned addr; { int upper = stab_i - 1; int lower = 0; int middle = (upper + lower) / 2; while (!((stab_addr(middle) <= addr) && (stab_addr(middle + 1) > addr))) { if (middle == upper || middle == lower) { return ST_NOT_FOUND; } if (stab_addr(middle) > addr) { upper = middle; } else { lower = middle; } middle = (upper + lower) / 2; } if (middle >= (stab_i - 2)) { return ST_NOT_FOUND; } else { return middle; } } void st_print() { int i; printf("function symbol table:\n"); for (i = 0; i < stab_i; i++) { st_print_one(i); } } void st_print_one(i) stindex i; { printf(" %d %-15s %10d\n", i,stab_name(i), stab_addr(i)); } void increment_data(dt, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12) mpdata dt; int d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12; { dt_b_small(dt) += d1; dt_b_med(dt) += d2; dt_b_large(dt) += d3; dt_b_xlarge(dt) += d4; dt_n_small(dt) += d5; dt_n_med(dt) += d6; dt_n_large(dt) += d7; dt_n_xlarge(dt) += d8; dt_d_small(dt) += d9; dt_d_med(dt) += d10; dt_d_large(dt) += d11; dt_d_xlarge(dt) += d12; } void st_convert(data_filename) char *data_filename; { FILE *f = fopen(data_filename, "r"); unsigned faddr, paddr; stindex fx, px; mpsym fsym, psym; mpdata dcell; mpcell ppair; int d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12; int i, x; /* * read the prolog containing stats * handle the stats data first as a special case */ check_fscanf(fscanf(f, "alloc=%d free=%d depth=%d same=%d all=%d\n", &d1, &d2, &d3, &d4, &d5)); fflush(stdout); check_fscanf(fscanf(f, "fmem=%d dmem=%d lmem=%d smem=%d\n", &d6, &d7, &d8, &d9)); /* * print the prolog */ printf("--c%2s+--v3.0+--m%d+--+--+--+--+--+--+--+--+ MPROF +--+--+--+--+--+--+--s%d+--f%d+--d%d+--l%d+\n\n\n", percent(d4, d5), d3, d9, d6, d7, d8); fflush(stdout); print_bin_table(f, stdout); print_leak_table(f, stdout); while (fscanf(f, "%d\n", &faddr) != EOF) { fx = st_locate(faddr); fsym = pc_lookup(stab_addr(fx)); fn_name(fsym) = stab_name(fx); fscanf(f, "%d %d %d %d %d %d %d %d %d %d %d %d\n", &d1, &d2, &d3, &d4, &d5, &d6, &d7, &d8, &d9, &d10, &d11, &d12); increment_data(fn_lcount(fsym), d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12); fscanf(f, "%d\n", &paddr); while ((int) paddr != MP_NIL) { px = st_locate(paddr); psym = pc_lookup(stab_addr(px)); ppair = mp_has_parent(fsym, psym); if (mp_null(ppair)) { dcell = mp_new_data(); ppair = mp_cons((int) psym, (int) dcell); fn_parents(fsym) = mp_cons((int) ppair, fn_parents(fsym)); } fscanf(f, "%d %d %d %d %d %d %d %d %d %d %d %d\n", &d1, &d2, &d3, &d4, &d5, &d6, &d7, &d8, &d9, &d10, &d11, &d12); increment_data(mp_cdr(ppair), d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12); fscanf(f, "%d\n", &paddr); } } mprof_graph_ops(stdout); } void print_bin_table(infile, outfile) FILE *infile, *outfile; { int i; int type_count, type_index = 0; struct sthash **type_table; int alloc_bins[MP_NUM_BINS]; int free_bins[MP_NUM_BINS]; int big_alloc_count, big_alloc_bytes; int big_free_count, big_free_bytes; int alloc_count = 0, alloc_bytes = 0, free_count = 0, free_bytes = 0; int other_alloc_count = 0, other_alloc_bytes = 0; int other_free_count = 0, other_free_bytes = 0; int abin, fbin; int byte_difference; /* * Read in and print out the Bin table information. */ type_table = read_and_sort_types(&type_count); /* read in the bins and save the data */ alloc_count = 0; alloc_bytes = 0; for (i = 0; i < (MP_NUM_BINS - 2); i++) { check_fscanf(fscanf(infile, "%d\n", &alloc_bins[i])); alloc_count += alloc_bins[i]; alloc_bytes += alloc_bins[i] * i; } check_fscanf(fscanf(infile, "%d\n", &big_alloc_count)); alloc_count += big_alloc_count; if (alloc_count == 0) { alloc_count = 1; } check_fscanf(fscanf(infile, "%d\n", &big_alloc_bytes)); alloc_bytes += big_alloc_bytes; free_count = 0; free_bytes = 0; for (i = 0; i < (MP_NUM_BINS - 2); i++) { check_fscanf(fscanf(infile, "%d\n", &free_bins[i])); free_count += free_bins[i]; free_bytes += free_bins[i] * i; } check_fscanf(fscanf(infile, "%d\n", &big_free_count)); free_count += big_free_count; if (free_count == 0) { free_count = 1; } check_fscanf(fscanf(infile, "%d\n", &big_free_bytes)); free_bytes += big_free_bytes; byte_difference = alloc_bytes - free_bytes; #define abin_template \ "%11d%10d%10d %-4s%10d%10d %-4s " #define abin_template2 \ "%11s%10d%10d %-4s%10d%10d %-4s " #define abin_titles_template \ "%11s%10s%10s %-4s%10s%10s %-4s %-8s\n" fprintf(outfile, "--------- Allocation Bins with possible Types ------------\n\n"); fprintf(outfile, abin_titles_template, "size:", "allocs", "bytes", "(%)", "frees", "kept", "(%)", "types"); fprintf(outfile, "\n"); for (i = 0; i < (MP_NUM_BINS - 2); i++) { abin = alloc_bins[i]; fbin = free_bins[i]; /* * Print things depending on the level of verbosity. */ if (((verbosity == V_VERBOSE) && ((abin > 0) || (fbin > 0))) || ((verbosity == V_NORMAL) && (((abin > 0) && (i > 100)) || (((double) abin / alloc_count) > 1.0/500.0) || ((type_index != type_count) && (((double) abin / alloc_count) > 1.0/100.0) && ((type_table[type_index])->size == i)))) || ((verbosity == V_TERSE) && ((((double) abin / alloc_count) > 1.0/50.0) || ((type_index != type_count) && (((double) abin / alloc_count) > 1.0/100.0) && ((type_table[type_index])->size == i))))) { fprintf(outfile, abin_template, i, abin, i * abin, percent_string(i * abin, alloc_bytes), fbin, (i * abin) - (i * fbin), percent_string((i * abin) - (i * fbin), byte_difference)); /* * Print out relevant types. */ print_type_list(outfile, type_table, TYPE_EQUAL, i, type_count, &type_index); fprintf(outfile, "\n"); } else { other_alloc_count += abin; other_free_count += fbin; other_alloc_bytes += abin * i; other_free_bytes += fbin * i; } } /* * Print the things at the end of the table. */ fprintf(outfile, abin_template2, "> 1024", big_alloc_count, big_alloc_bytes, percent_string(big_alloc_bytes, alloc_bytes), big_free_count, (big_alloc_bytes - big_free_bytes), percent_string((big_alloc_bytes - big_free_bytes), byte_difference)); print_type_list(outfile, type_table, TYPE_GREATERTHAN, 1024, type_count, &type_index); fprintf(outfile, "\n\n"); if ((other_alloc_count > 0) || (other_free_count > 0)) { fprintf(outfile, abin_template2, "other bins", other_alloc_count, other_alloc_bytes, percent_string(other_alloc_bytes, alloc_bytes), other_free_count, (other_alloc_bytes - other_free_bytes), percent_string((other_alloc_bytes - other_free_bytes), byte_difference)); fprintf(outfile, "\n"); } fprintf(outfile, abin_template2, "", alloc_count, alloc_bytes, "", free_count, byte_difference, ""); fprintf(outfile, "\n\f\n\n"); fflush(outfile); } void print_type_list(outfile, tlist, compar, binsize, type_count, type_index) FILE *outfile; struct sthash **tlist; int compar, binsize, type_count; int *type_index; { int i, cond, pcount = 0; for (i = *type_index; ((i < type_count) && (tlist[i]->size <= binsize)); i++) { if (compar == TYPE_EQUAL) { cond = (tlist[i]->size == binsize); } else if (compar == TYPE_GREATERTHAN) { cond = (tlist[i]->size > binsize); } if (cond) { /* * Make the spacing nice. */ if ((pcount > 0) && ((pcount % 3) == 0)) { fprintf(outfile, "\n%64s", ""); } fprintf(outfile, "%-12s ", tlist[i]->str); pcount += 1; } } *type_index = i; } #define leak_titles_template1 \ "%10s %-4s%10s%10s %-4s%10s%10s %-4s %-8s\n" #define leak_titles_template2 \ "%10s %-4s%10s%10s %-4s %-8s\n" #define leak_template1 \ "%10d %-4s%10d%10d %-4s%10d%10d %-4s " #define leak_template2 \ "%10d %-4s%10d%10d %-4s " void print_leak_table(infile, outfile) FILE *infile, *outfile; { int total_allocs = 0, bytes_alloced = 0; int total_frees= 0, bytes_freed= 0; int byte_diff; struct leakentry *lt_root = NULL, *lte = NULL, *lt_vec = NULL; int lte_count = 0; int d1, d2, d3, d4, d5, i, j, real_i; stindex fx; mpsym fsym; /* read in the leak table and print it back out */ check_fscanf(fscanf(infile, "%d %d %d %d\n", &d1, &d2, &d3, &d4)); while(d1 != -2) { /* * Gather the path for a single leak table entry. */ lte = (struct leakentry *) malloc(sizeof(struct leakentry)); lte_count += 1; for (i = 0; i < SHORT_CALLSTACK_SIZE; i++) { check_fscanf(fscanf(infile, "%d\n", &d5)); if (d5 != 0) { fx = st_locate(d5); fsym = pc_lookup(stab_addr(fx)); if (stab_name(fx) == NULL) { fn_name(fsym) = ""; lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].func = ""; lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].offset = 0; } else { fn_name(fsym) = stab_name(fx); lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].func = fn_name(fsym); lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].offset = d5 - stab_addr(fx); } } else { lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].func = ""; lte->path[SHORT_CALLSTACK_SIZE - (i + 1)].offset = 0; } } lte->all_no = d1; total_allocs += d1; lte->all_by = d2; bytes_alloced += d2; lte->fre_no = d3; total_frees += d3; lte->fre_by = d4; bytes_freed += d4; /* * Add to the list of leak entries. */ lte->next = lt_root; lt_root = lte; check_fscanf(fscanf(infile, "%d %d %d %d", &d1, &d2, &d3, &d4)); } byte_diff = bytes_alloced - bytes_freed; if ((lte_count == 0) || (leak_level == LEAK_NONE)) { return; } fprintf(outfile, "--------- Partial Dynamic Call Paths for Memory Leaks ------------\n\n"); fprintf(outfile, "Total bytes not freed: %d\n\n", byte_diff); if (total_frees > 0) { fprintf(outfile, leak_titles_template1, "kept bytes", "(%)", "allocs", "bytes", "(%)", "frees", "bytes", "(%)", "path"); } else { fprintf(outfile, leak_titles_template2, "kept bytes", "(%)", "allocs", "bytes", "(%)", "path"); } fprintf(outfile, "\n"); /* * Here we put the leak table entries into a vector so we can * manipulate them. */ lt_vec = (struct leakentry *) malloc(sizeof(struct leakentry) * lte_count); lte = lt_root; i = 0; while (lte != NULL) { struct leakentry *tmp; lt_vec[i] = *lte; i++; tmp = lte; lte = lte->next; free(tmp); } if (leak_level == LEAK_SHOW) { /* * Sort the entries so that duplicate paths are together. */ qsort((char *) lt_vec, lte_count, sizeof(struct leakentry), lte_str_compar); if (lte_count > 1) { real_i = 0; for (i = 1; i < lte_count;) { if (path_equal(lt_vec[real_i].path, lt_vec[i].path)) { /* * Merge the data from identical paths together. */ lt_vec[real_i].all_no += lt_vec[i].all_no; lt_vec[real_i].all_by += lt_vec[i].all_by; lt_vec[real_i].fre_no += lt_vec[i].fre_no; lt_vec[real_i].fre_by += lt_vec[i].fre_by; i += 1; } else { real_i += 1; /* * First, copy the data, compressing out bubbles */ if (real_i != i) { lt_vec[real_i] = lt_vec[i]; } i += 1; } } lte_count = real_i + 1; } } qsort((char *) lt_vec, lte_count, sizeof(struct leakentry), lte_size_compar); for (i = 0; i < lte_count; i+=1) { struct leakentry lte; lte = lt_vec[i]; if (verbosity == V_TERSE) { if (((double) lte.all_by / bytes_alloced) < 0.01) { continue; } } else if (verbosity == V_NORMAL) { if (((double) lte.all_by / bytes_alloced) < 0.005) { continue; } } if (total_frees > 0) { fprintf(outfile, leak_template1, (lte.all_by - lte.fre_by), percent_string((lte.all_by - lte.fre_by), byte_diff), lte.all_no, lte.all_by, percent_string(lte.all_by, bytes_alloced), lte.fre_no, lte.fre_by, percent_string(lte.fre_by, bytes_freed)); } else { fprintf(outfile, leak_template2, (lte.all_by - lte.fre_by), percent_string((lte.all_by - lte.fre_by), byte_diff), lte.all_no, lte.all_by, percent_string(lte.all_by, bytes_alloced)); } if ((strcmp(lte.path[0].func, "") == 0) || (strcmp(lte.path[0].func, "main") == 0)) { fprintf(outfile, "|| "); } else { fprintf(outfile, "..."); } for (j = 0; j < SHORT_CALLSTACK_SIZE; j++) { if (lte.path[j].func == NULL) lte.path[j].func = ""; if (strcmp(lte.path[j].func, "") != 0) { if (leak_level == LEAK_SHOW) { fprintf(outfile, "> %s ", lte.path[j].func); } else if (leak_level == LEAK_OFFSETS) { fprintf(outfile, "> %s+%d ", lte.path[j].func, lte.path[j].offset); } } } fprintf(outfile, "\n"); } fprintf(outfile, "\n\f\n\n"); fflush(outfile); } void usage() { fprintf(stderr, "usage: mprof [-leaktable | -noleaktable] \n\ [-verbose | -normal | -terse] \n\ [a.out-name] [data-name]\n"); exit(1); } #define str_equal(s1, s2) (strcmp((s1), (s2)) == 0) int main(argc, argv) int argc; char *argv[]; { int i; char *exec_file = "a.out"; char *data_file = "mprof.data"; /* * Default settings. */ leak_level = LEAK_SHOW; verbosity = V_NORMAL; for (i = 1; i < argc; i++) { if (str_equal(argv[i], "-leaktable")) { leak_level = LEAK_SHOW; } else if (str_equal(argv[i], "-noleaktable")) { leak_level = LEAK_NONE; } else if (str_equal(argv[i], "-offsets")) { leak_level = LEAK_OFFSETS; } else if (str_equal(argv[i], "-verbose")) { verbosity = V_VERBOSE; } else if (str_equal(argv[i], "-normal")) { verbosity = V_NORMAL; } else if (str_equal(argv[i], "-terse")) { verbosity = V_TERSE; } else if (i == (argc - 1)) { exec_file = argv[i]; break; } else if (i == (argc - 2)) { exec_file = argv[i]; data_file = argv[i+1]; break; } else { usage(); } } mpstruct_init(); stab_i = 0; for (i = 0; i < ST_SIZE; i++) { stab_name(i) = NULL; } st_read(exec_file); st_convert(data_file); exit(0); }