From 7862d7d065c91e32b2d1d12590b0b6219034c7cf Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 4 Feb 1998 00:30:48 +0000 Subject: [PATCH] Tue Feb 3 14:25:25 1998 Brent Baccala * symtab.c (symtab_finalize): Prefer function symbols over line symbols. (dbg_sym_lookup): Correct debugging messages. * gprof.c (main): --sum implies --line. * cg_print.c (cg_print): When doing line by line profiling, don't use a non-function as a main listing item. * call_graph.c (cg_tally): When using line by line profiling, use the function symbol as the child. * symtab.h (NBBS): Define. (Sym): Add bb_addr and bb_calls fields. * basic_blocks.c (bb_read_rec): Save multiple basic blocks per symbol. (bb_write_blocks): Adjust for multiple basic blocks per symbol. (print_exec_counts): Don't check whether a symbol is the start of a basic block. Print all basic blocks for a symbol. (annotate_with_count): Rewrite to print all basic block counts and to pay attention to width argument. (print_annotated_source): Don't check whether symbol is the start of a basic block. --- gprof/ChangeLog | 24 +++++ gprof/basic_blocks.c | 250 +++++++++++++++++++++++++++++++------------ gprof/call_graph.c | 19 ++++ gprof/cg_print.c | 3 +- gprof/gprof.c | 6 ++ gprof/symtab.c | 18 ++-- gprof/symtab.h | 4 + 7 files changed, 248 insertions(+), 76 deletions(-) diff --git a/gprof/ChangeLog b/gprof/ChangeLog index cd8e4266de6..19f2200f92a 100644 --- a/gprof/ChangeLog +++ b/gprof/ChangeLog @@ -1,5 +1,29 @@ Tue Feb 3 14:25:25 1998 Brent Baccala + * symtab.c (symtab_finalize): Prefer function symbols over line + symbols. + (dbg_sym_lookup): Correct debugging messages. + + * gprof.c (main): --sum implies --line. + + * cg_print.c (cg_print): When doing line by line profiling, don't + use a non-function as a main listing item. + + * call_graph.c (cg_tally): When using line by line profiling, use + the function symbol as the child. + + * symtab.h (NBBS): Define. + (Sym): Add bb_addr and bb_calls fields. + * basic_blocks.c (bb_read_rec): Save multiple basic blocks per + symbol. + (bb_write_blocks): Adjust for multiple basic blocks per symbol. + (print_exec_counts): Don't check whether a symbol is the start of + a basic block. Print all basic blocks for a symbol. + (annotate_with_count): Rewrite to print all basic block counts and + to pay attention to width argument. + (print_annotated_source): Don't check whether symbol is the start + of a basic block. + Make it possible to build a cross gprof, although a few cases are still not handled: * configure.in: Don't set MY_TARGET. diff --git a/gprof/basic_blocks.c b/gprof/basic_blocks.c index 25028036340..74201748bc3 100644 --- a/gprof/basic_blocks.c +++ b/gprof/basic_blocks.c @@ -30,10 +30,11 @@ static long num_lines_executed; /* - * Helper for sorting. Compares two basic-blocks and returns result + * Helper for sorting. Compares two symbols and returns result * such that sorting will be increasing according to filename, line - * number, and basic-block address (in that order). + * number, and address (in that order). */ + static int DEFUN (cmp_bb, (lp, rp), const void *lp AND const void *rp) { @@ -179,13 +180,28 @@ DEFUN (bb_read_rec, (ifp, filename), FILE * ifp AND const char *filename) /* convert from target to host endianness: */ addr = get_vma (core_bfd, (bfd_byte *) & addr); + ncalls = bfd_get_32 (core_bfd, (bfd_byte *) &ncalls); sym = sym_lookup (&symtab, addr); - sym->is_bb_head = TRUE; - sym->ncalls += bfd_get_32 (core_bfd, (bfd_byte *) & ncalls); - DBG (BBDEBUG, printf ("[bb_read_rec] 0x%lx->0x%lx (%s) cnt=%d\n", - addr, sym->addr, sym->name, sym->ncalls)); + if (sym) + { + int i; + + DBG (BBDEBUG, + printf ("[bb_read_rec] 0x%lx->0x%lx (%s:%d) cnt=%ld\n", + addr, sym->addr, sym->name, sym->line_num, ncalls)); + + for (i = 0; i < NBBS; i++) + { + if (! sym->bb_addr[i] || sym->bb_addr[i] == addr) + { + sym->bb_addr[i] = addr; + sym->bb_calls[i] += ncalls; + break; + } + } + } } else { @@ -217,15 +233,15 @@ DEFUN (bb_write_blocks, (ofp, filename), FILE * ofp AND const char *filename) bfd_vma addr; long ncalls; Sym *sym; + int i; /* count how many non-zero blocks with have: */ for (sym = symtab.base; sym < symtab.limit; ++sym) { - if (sym->ncalls > 0) - { - ++nblocks; - } + for (i = 0; i < NBBS && sym->bb_addr[i]; i++) + ; + nblocks += i; } /* write header: */ @@ -240,19 +256,17 @@ DEFUN (bb_write_blocks, (ofp, filename), FILE * ofp AND const char *filename) /* write counts: */ for (sym = symtab.base; sym < symtab.limit; ++sym) { - if (sym->ncalls == 0) + for (i = 0; i < NBBS && sym->bb_addr[i]; i++) { - continue; - } + put_vma (core_bfd, sym->bb_addr[i], (bfd_byte *) & addr); + bfd_put_32 (core_bfd, sym->bb_calls[i], (bfd_byte *) & ncalls); - put_vma (core_bfd, sym->addr, (bfd_byte *) & addr); - bfd_put_32 (core_bfd, sym->ncalls, (bfd_byte *) & ncalls); - - if (fwrite (&addr, sizeof (addr), 1, ofp) != 1 - || fwrite (&ncalls, sizeof (ncalls), 1, ofp) != 1) - { - perror (filename); - done (1); + if (fwrite (&addr, sizeof (addr), 1, ofp) != 1 + || fwrite (&ncalls, sizeof (ncalls), 1, ofp) != 1) + { + perror (filename); + done (1); + } } } } @@ -268,7 +282,7 @@ void DEFUN_VOID (print_exec_counts) { Sym **sorted_bbs, *sym; - int i, len; + int i, j, len; if (first_output) { @@ -286,15 +300,13 @@ DEFUN_VOID (print_exec_counts) for (sym = symtab.base; sym < symtab.limit; ++sym) { /* - * Accept symbol if it's the start of a basic-block and it is - * called at least bb_min_calls times and if it's in the - * INCL_EXEC table or there is no INCL_EXEC table and it does - * not appear in the EXCL_EXEC table. + * Accept symbol if it's in the INCL_EXEC table + * or there is no INCL_EXEC table + * and it does not appear in the EXCL_EXEC table. */ - if (sym->is_bb_head && sym->ncalls >= bb_min_calls - && (sym_lookup (&syms[INCL_EXEC], sym->addr) - || (syms[INCL_EXEC].len == 0 - && !sym_lookup (&syms[EXCL_EXEC], sym->addr)))) + if (sym_lookup (&syms[INCL_EXEC], sym->addr) + || (syms[INCL_EXEC].len == 0 + && !sym_lookup (&syms[EXCL_EXEC], sym->addr))) { sorted_bbs[len++] = sym; } @@ -305,72 +317,174 @@ DEFUN_VOID (print_exec_counts) for (i = 0; i < len; ++i) { - sym = sorted_bbs[i]; - printf ("%s:%d: (%s:0x%lx) %d executions\n", - sym->file ? sym->file->name : "", sym->line_num, - sym->name, sym->addr, sym->ncalls); + if (sym->ncalls > 0 || ! ignore_zeros) + { + printf ("%s:%d: (%s:0x%lx) %d executions\n", + sym->file ? sym->file->name : "", sym->line_num, + sym->name, sym->addr, sym->ncalls); + } + for (j = 0; j < NBBS && sym->bb_addr[j]; j ++) + { + if (sym->bb_calls[j] > 0 || ! ignore_zeros) + { + printf ("%s:%d: (%s:0x%lx) %d executions\n", + sym->file ? sym->file->name : "", sym->line_num, + sym->name, sym->bb_addr[j], sym->bb_calls[j]); + } + } } free (sorted_bbs); } - /* * Helper for bb_annotated_source: format annotation containing - * number of line executions. + * number of line executions. Depends on being called on each + * line of a file in sequential order. + * + * Global variable bb_annotate_all_lines enables execution count + * compression (counts are supressed if identical to the last one) + * and prints counts on all executed lines. Otherwise, print + * all basic-block execution counts exactly once on the line + * that starts the basic-block. */ + static void DEFUN (annotate_with_count, (buf, width, line_num, arg), char *buf AND int width AND int line_num AND void *arg) { Source_File *sf = arg; Sym *b; - long cnt; - static long last_count; + int i; + static int last_count; + int last_print=-1; - if (line_num == 1) - { - last_count = -1; - } - - b = 0; + b = NULL; if (line_num <= sf->num_lines) { b = sf->line[line_num - 1]; } if (!b) { - cnt = -1; + for (i = 0; i < width; i++) + buf[i] = ' '; + buf[width] = '\0'; } else { + char tmpbuf[NBBS * 30]; + char *p; + int ncalls; + int len; + ++num_executable_lines; - cnt = b->ncalls; - } - if (cnt > 0) - { + + p = tmpbuf; + *p = '\0'; + + ncalls = -1; + + /* If this is a function entry point, label the line no matter what. + * Otherwise, we're in the middle of a function, so check to see + * if the first basic-block address is larger than the starting + * address of the line. If so, then this line begins with a + * a portion of the previous basic-block, so print that prior + * execution count (if bb_annotate_all_lines is set). + */ + + if (b->is_func) + { + sprintf (p, "%d", b->ncalls); + p += strlen (p); + last_count = b->ncalls; + last_print = last_count; + ncalls = b->ncalls; + } + else if (bb_annotate_all_lines + && b->bb_addr[0] && b->bb_addr[0] > b->addr) + { + sprintf (p, "%d", last_count); + p += strlen (p); + last_print = last_count; + ncalls = last_count; + } + + /* Loop through all of this line's basic-blocks. For each one, + * update last_count, then compress sequential identical counts + * (if bb_annotate_all_lines) and print the execution count. + */ + + for (i = 0; i < NBBS && b->bb_addr[i]; i++) + { + last_count = b->bb_calls[i]; + if (ncalls < 0) + ncalls = 0; + ncalls += last_count; + + if (bb_annotate_all_lines && last_count == last_print) + { + continue; + } + + if (p > tmpbuf) + *p++ = ','; + sprintf (p, "%d", last_count); + p += strlen (p); + + last_print = last_count; + } + + /* We're done. If nothing has been printed on this line, + * print the last execution count (bb_annotate_all_lines), + * which could be from either a previous line (if there were + * no BBs on this line), or from this line (if all our BB + * counts were compressed out because they were identical). + */ + + if (bb_annotate_all_lines && p == tmpbuf) + { + sprintf (p, "%d", last_count); + p += strlen (p); + ncalls = last_count; + } + + if (ncalls < 0) + { + int c; + + for (c = 0; c < width; c++) + buf[c] = ' '; + buf[width] = '\0'; + return; + } + ++num_lines_executed; - } - if (cnt < 0 && bb_annotate_all_lines) - { - cnt = last_count; - } - if (cnt < 0) - { - strcpy (buf, "\t\t"); - } - else if (cnt < bb_min_calls) - { - strcpy (buf, " ##### -> "); - } - else - { - sprintf (buf, "%12ld -> ", cnt); + if (ncalls < bb_min_calls) + { + strcpy (tmpbuf, "#####"); + p = tmpbuf + 5; + } + + strcpy (p, " -> "); + p += 4; + + len = p - tmpbuf; + if (len >= width) + { + strncpy (buf, tmpbuf, width); + buf[width] = '\0'; + } + else + { + int c; + + strcpy (buf + width - len, tmpbuf); + for (c = 0; c < width - len; ++c) + buf[c] = ' '; + } } - last_count = cnt; } - /* * Annotate the files named in SOURCE_FILES with basic-block statistics * (execution counts). After each source files, a few statistics @@ -420,7 +534,7 @@ DEFUN_VOID (print_annotated_source) for (sym = symtab.base; sym < symtab.limit; ++sym) { - if (sym->is_bb_head && sym->file && sym->file->num_lines + if (sym->file && sym->file->num_lines && (sym_lookup (&syms[INCL_ANNO], sym->addr) || (syms[INCL_ANNO].len == 0 && !sym_lookup (&syms[EXCL_ANNO], sym->addr)))) diff --git a/gprof/call_graph.c b/gprof/call_graph.c index a61dd837d0b..de48aa860e3 100644 --- a/gprof/call_graph.c +++ b/gprof/call_graph.c @@ -16,6 +16,25 @@ DEFUN (cg_tally, (from_pc, self_pc, count), parent = sym_lookup (&symtab, from_pc); child = sym_lookup (&symtab, self_pc); + if (child == NULL || parent == NULL) + return; + + /* If we're doing line-by-line profiling, both the parent and the + child will probably point to line symbols instead of function + symbols. For the parent this is fine, since this identifies the + line number in the calling routing, but the child should always + point to a function entry point, so we back up in the symbol + table until we find it. + + For normal profiling, is_func will be set on all symbols, so this + code will do nothing. */ + + while (child >= symtab.base && ! child->is_func) + --child; + + if (child < symtab.base) + return; + /* * Keep arc if it is on INCL_ARCS table or if the INCL_ARCS table * is empty and it is not in the EXCL_ARCS table. diff --git a/gprof/cg_print.c b/gprof/cg_print.c index 015f710ce32..093b9563e54 100644 --- a/gprof/cg_print.c +++ b/gprof/cg_print.c @@ -513,7 +513,8 @@ DEFUN (cg_print, (timesortsym), Sym ** timesortsym) if ((ignore_zeros && parent->ncalls == 0 && parent->cg.self_calls == 0 && parent->cg.prop.self == 0 && parent->cg.prop.child == 0) - || !parent->cg.print_flag) + || !parent->cg.print_flag + || (line_granularity && ! parent->is_func)) { continue; } diff --git a/gprof/gprof.c b/gprof/gprof.c index 449ab203b54..3677c400d4d 100644 --- a/gprof/gprof.c +++ b/gprof/gprof.c @@ -419,6 +419,12 @@ This program is free software. This program has absolutely no warranty.\n"); done (1); } + /* --sum implies --line, otherwise we'd lose b-b counts in gmon.sum */ + if (output_style & STYLE_SUMMARY_FILE) + { + line_granularity = 1; + } + /* append value of GPROF_PATH to source search list if set: */ str = (char *) getenv ("GPROF_PATH"); if (str) diff --git a/gprof/symtab.c b/gprof/symtab.c index b38ae6a62de..0b6a4af9a57 100644 --- a/gprof/symtab.c +++ b/gprof/symtab.c @@ -85,8 +85,9 @@ DEFUN (symtab_finalize, (tab), Sym_Table * tab) if (src->addr == prev_addr) { /* - * If same address, favor global symbol over static one. - * If both symbols are either static or global, check + * If same address, favor global symbol over static one, + * then function over line number. If both symbols are + * either static or global and either function or line, check * whether one has name beginning with underscore while * the other doesn't. In such cases, keep sym without * underscore. This takes cares of compiler generated @@ -94,9 +95,12 @@ DEFUN (symtab_finalize, (tab), Sym_Table * tab) */ if ((!src->is_static && dst[-1].is_static) || ((src->is_static == dst[-1].is_static) - && ((src->name[0] != '_' && dst[-1].name[0] == '_') - || (src->name[0] - && src->name[1] != '_' && dst[-1].name[1] == '_')))) + && ((src->is_func && !dst[-1].is_func) + || ((src->is_func == dst[-1].is_func) + && ((src->name[0] != '_' && dst[-1].name[0] == '_') + || (src->name[0] + && src->name[1] != '_' + && dst[-1].name[1] == '_')))))) { DBG (AOUTDEBUG | IDDEBUG, printf ("[symtab_finalize] favor %s@%c%c over %s@%c%c", @@ -166,7 +170,7 @@ DEFUN (dbg_sym_lookup, (symtab, address), Sym_Table * symtab AND bfd_vma address long low, mid, high; Sym *sym; - fprintf (stderr, "[sym_lookup] address 0x%lx\n", address); + fprintf (stderr, "[dbg_sym_lookup] address 0x%lx\n", address); sym = symtab->base; for (low = 0, high = symtab->len - 1; low != high;) @@ -189,7 +193,7 @@ DEFUN (dbg_sym_lookup, (symtab, address), Sym_Table * symtab AND bfd_vma address low = mid + 1; } } - fprintf (stderr, "[sym_lookup] binary search fails???\n"); + fprintf (stderr, "[dbg_sym_lookup] binary search fails???\n"); return 0; } diff --git a/gprof/symtab.h b/gprof/symtab.h index 9b1a531c2a5..f3b49a933c6 100644 --- a/gprof/symtab.h +++ b/gprof/symtab.h @@ -16,6 +16,8 @@ #include "source.h" +#define NBBS 10 + /* * Symbol-entry. For each external in the specified file we gather * its address, the number of calls and compute its share of cpu time. @@ -42,6 +44,8 @@ typedef struct sym int ncalls; /* how many times executed */ int nuses; /* how many times this symbol appears in a particular context */ + bfd_vma bb_addr[NBBS]; /* address of basic-block start */ + int bb_calls[NBBS]; /* how many times basic-block was called */ struct sym *next; /* for building chains of syms */ struct sym *prev; /* for building chains of syms */ -- 2.30.2