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:
Ian Lance Taylor 1998-02-04 00:30:48 +00:00
parent 70b3329c0c
commit 7862d7d065
7 changed files with 248 additions and 76 deletions

View File

@ -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.

View File

@ -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))))

View File

@ -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.

View File

@ -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;
}

View File

@ -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)

View File

@ -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;
}

View File

@ -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 */