Tue Feb 3 14:25:25 1998 Brent Baccala <baccala@freesoft.org>
* 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.
This commit is contained in:
parent
70b3329c0c
commit
7862d7d065
|
@ -1,5 +1,29 @@
|
|||
Tue Feb 3 14:25:25 1998 Brent Baccala <baccala@freesoft.org>
|
||||
|
||||
* 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.
|
||||
|
|
|
@ -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 : "<unknown>", 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 : "<unknown>", 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 : "<unknown>", 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");
|
||||
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] = ' ';
|
||||
}
|
||||
}
|
||||
else if (cnt < bb_min_calls)
|
||||
{
|
||||
strcpy (buf, " ##### -> ");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf (buf, "%12ld -> ", cnt);
|
||||
}
|
||||
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))))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
Loading…
Reference in New Issue