Lots of changes from David Mosberger-Tang; see ChangeLog and NOTES for details:
Alpha support. Long options. New file format to support more information; backwards compatibility. Line-level profiling, on systems where bfd_find_nearest_line works. Selective display of data.
This commit is contained in:
parent
2559e01429
commit
5489fcc3d9
59 changed files with 6474 additions and 3400 deletions
1
gprof/.cvsignore
Normal file
1
gprof/.cvsignore
Normal file
|
@ -0,0 +1 @@
|
|||
*_bl.c
|
1
gprof/.gdbinit
Normal file
1
gprof/.gdbinit
Normal file
|
@ -0,0 +1 @@
|
|||
dir ..
|
334
gprof/ChangeLog
334
gprof/ChangeLog
|
@ -1,3 +1,337 @@
|
|||
Tue Feb 7 17:24:12 1995 Ken Raeburn <raeburn@cujo.cygnus.com>
|
||||
|
||||
* gprof.c (VERSION): Changed to 2.6, to get in sync for next
|
||||
binutils release.
|
||||
|
||||
Sun Feb 5 16:19:46 1995 David Mosberger-Tang <davidm@piston.cs.arizona.edu>
|
||||
|
||||
* symtab.c (symtab_finalize): ensure globals symbols really
|
||||
are favored over static ones---even if their name looks less
|
||||
preferable; this is important for HP-UX; for example, there
|
||||
is a static label Ltext_something that aliases the global
|
||||
symbol _start
|
||||
|
||||
* hist.c (hist_print): auto-scaling is now in effect for FSF-style
|
||||
output only; also, auto-scaling is now performed based on
|
||||
per-call, rather than total execution time, which is what it was
|
||||
meant to be.
|
||||
|
||||
* gprof.h (File_Format): new type.
|
||||
|
||||
* gprof.c (VERSION): upped to 2.7---seems to be completely out of
|
||||
sync with Cygnus version numbers though...
|
||||
|
||||
(long_options): renamed --gmon-info to --file-info, --width added,
|
||||
renamed --old-file-format to --file-format
|
||||
(main): dito; also added support to read prof files, but as
|
||||
mon_out_read() is not implemented, it's #ifdef'd out for now
|
||||
|
||||
(usage): update to reflect new options.
|
||||
|
||||
* gmon_io.c: replaced "old_file_format" by more general
|
||||
"file_format" option
|
||||
|
||||
* gmon.h (struct raw_phdr): fixed declaration for OSF/1.
|
||||
|
||||
* core.c (core_sym_class): added back check for __gnu_compiled and
|
||||
___gnu_compiled for the benefit of systems without
|
||||
bfd_find_nearest_line() support
|
||||
|
||||
(get_src_info): now the libbfd is fixed, invoke bfd_find_nearest_line()
|
||||
with section-relative addresses
|
||||
|
||||
(core_create_function_syms): get_src_info() calls are currently
|
||||
enabled for OSF/1 only. It appears to work allright for SunOS
|
||||
4.1.x as well, but on SPARCs it gets painfully slow with the
|
||||
current implementation of aout_32_find_nearest_line();
|
||||
unfortunately, this means that static functions will not have their
|
||||
filename printed in the call-graph function index; line-level
|
||||
profiling should still work, but requires some patience
|
||||
|
||||
* cg_print.c (cg_print_index): sanitized printing of index when
|
||||
using FSF-style output; in particular, output width is now controlled
|
||||
via option --width and the function tries hard to keep columns
|
||||
aligned even in the presence of (occasional) long names
|
||||
|
||||
* NOTES: a first shot at updating the documentation.
|
||||
|
||||
Wed Feb 1 19:07:44 1995 David Mosberger-Tang <davidm@piston.cs.arizona.edu>
|
||||
|
||||
* core.c (core_create_function_syms): fixed computation of min_vma
|
||||
and max_vma.
|
||||
|
||||
* *.c: removed rcsid.
|
||||
|
||||
Tue Jan 31 16:18:18 1995 Ken Raeburn <raeburn@cujo.cygnus.com>
|
||||
|
||||
* Lots of changes from David Mosberger-Tang:
|
||||
|
||||
Tue Oct 25 19:20:14 1994 David Mosberger-Tang <davidm@piston.cs.arizona.edu>
|
||||
|
||||
* gprof.c (main): put parentheses around & within &&.
|
||||
|
||||
* basic_blocks.c (bb_read_rec): print warning message (once) when
|
||||
ignoring basic-block execution counts.
|
||||
|
||||
* source.c (source_file_lookup_name): corrected second argument to
|
||||
strcmp().
|
||||
|
||||
* hist.c (print_header): merged Fri Oct 21 18:58:02 1994 change by
|
||||
Ken Raeburn <raeburn@cujo.cygnus.com> from binutils-2.5.1.
|
||||
|
||||
* gmon_io.c (gmon_out_read): the output stule STYLE_GMON_INFO is now
|
||||
supported both for old and new (versioned) gmon.out files. Old
|
||||
files are identified as version 0.
|
||||
|
||||
* gmon.h (struct raw_arc): count field is now sizeof(long) bytes
|
||||
long (instead of 4) because that is what OSF/1 v3.0 uses.
|
||||
|
||||
* core.c: minor fixes and debugging info changes.
|
||||
|
||||
Sun Sep 11 18:47:47 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu)
|
||||
|
||||
* core.c (core_init): if .text cannot be found, try $CODE$ (the
|
||||
name of the text-section under HP-UX).
|
||||
|
||||
* hist.c (hist_assign_samples): fixed off-by-one bug: highpc
|
||||
points one past the last sampling bin, so hist_scale should be
|
||||
computed as "hist_scale /= hist_num_bins", not "hist_scale /=
|
||||
hist_num_bins - 1".
|
||||
|
||||
* gmon_io.c, hist.c, hist.h: renamed hist_num_samples to
|
||||
hist_num_bins.
|
||||
|
||||
* configure.in: added alpha-*-*) for per-target config.
|
||||
|
||||
* alpha.c, alpha.h: created.
|
||||
|
||||
* gprof.c (default_excluded_list): <locore>, <hicore> added.
|
||||
|
||||
* core.c (core_create_function_syms, core_create_line_syms):
|
||||
explicitly keep two sentinels "<locore>" and "<hicore>" that catch
|
||||
all addresses outside the text-space. Thus, sym_lookup(&symtab,
|
||||
addr) continues to guarantee not to return 0 on any address. It
|
||||
also avoids incorrectly crediting the first/last symbol in the
|
||||
text-space.
|
||||
|
||||
* core.c (core_create_line_syms): always create function symbols
|
||||
first, then merge in line symbols; this is so that if parts of the
|
||||
program were compiled without -g, function-level symbols are
|
||||
available still.
|
||||
|
||||
* utils.c (print_name_only): support for print_path added.
|
||||
|
||||
* symtab.c (cmp_addr): also use is_func flag in comparison.
|
||||
(symtab_finalize): return immediately when table empty; now
|
||||
more careful about getting rid of the right duplicate symbol.
|
||||
|
||||
* sparc.c (find_call): many fixes---this function was rather
|
||||
botched in binutils-2.4 already; it should work again.
|
||||
|
||||
* source.c (source_file_lookup_path): PATH is now strdup'ed (it is
|
||||
not good to rely on get_src_info() to return distinct string
|
||||
pointers).
|
||||
|
||||
* search_list.c (search_list_append): added cast for xmalloc().
|
||||
|
||||
* hist.c: added explicit initialization to some of the global
|
||||
variables; fixed SItab (scales were off by a factor of 10).
|
||||
|
||||
* hist.h: include of bfd.h added.
|
||||
|
||||
* gprof.c, gprof.h (print_path): added.
|
||||
|
||||
* gprof.h (MAX): fixed.
|
||||
|
||||
* gmon_out.h: renamed gmon_time_hist_hdr to gmon_hist_hdr.
|
||||
|
||||
* gmon_io.c: added some casts to (long) so we can always print as %lx
|
||||
|
||||
* core.c (core_get_text_space): fixed to make it work.
|
||||
|
||||
* cg_print.c (cg_print_index): added support for print_path option.
|
||||
|
||||
* cg_dfn.h (cg_dfn): wrap prototype in PARAMS().
|
||||
|
||||
* call_graph.c, gmon_io.c, hist.c: avoid taking address of array
|
||||
as some compilers complain (e.g., DEC's OSF/1 compiler)
|
||||
|
||||
* basic_blocks.c, gmon_io.c, hist.c, source.c, sym_ids.c,
|
||||
symtab.c: calls to memset() had 2nd and 3rd args reversed.
|
||||
|
||||
Sat Sep 10 21:53:13 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu)
|
||||
|
||||
* gprof.c: added "_mcount" to default_excluded_list.
|
||||
(main): if output_style==0 and there is either a histogram or a
|
||||
call-graph, always generate flat and call-graph, no matter what
|
||||
line_granularity is set to.
|
||||
|
||||
* source.c (source_file_lookup_name): if searching for sf->name
|
||||
fails, try again with filename obtained after stripping off any
|
||||
partial path from sf->name.
|
||||
|
||||
* gprof.h (SRCDEBUG): added.
|
||||
|
||||
* search_list.c (search_list_append): directories were added in wrong
|
||||
order.
|
||||
|
||||
* reimplemented selection mechanism from ground up; it is now possible
|
||||
to accurately control what gets included/excluded in each of the
|
||||
output styles; a "symbol-specification" (spec) is the basic means
|
||||
to select a set of symbols; a spec has the syntax:
|
||||
|
||||
spec == (FILENAME:(FUNCNAME|LINE_NUM) | NAME).
|
||||
arc == spec/spec.
|
||||
|
||||
any of the terminal symbols can be empty, in which case they
|
||||
match anything (wildcards). NAME is interpreted as a FILENAME
|
||||
if it contains a dot (e.g., foo.c), as LINE_NUM if it starts
|
||||
with a digit, and as FUNCNAME otherwise.
|
||||
|
||||
For example, to get a call-graph display that ignores arcs
|
||||
from foo() to bar(), you'd say "--no-graph=foo/bar"; to
|
||||
show only arcs into bar() (no matter what the caller),
|
||||
you'd say "--graph=/bar"; and to get a call-graph without
|
||||
any arc info, you'd say "--graph=/"; similarly, to
|
||||
get a flat profile without mcount, you'd say "--no-flat=mcount"
|
||||
and to get a flat profile that shows includes all functions
|
||||
you'd say "--flat=""" (i.e., an empty spec)
|
||||
|
||||
* hist.c (hist_print): top_time wasn't initialized to 0.0.
|
||||
|
||||
Fri Sep 9 01:10:21 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu)
|
||||
|
||||
* gmon_out.h: all headers now declared in terms of characters
|
||||
to avoid getting into trouble with different compilers introducing
|
||||
different amount of padding; the code already accessed the fields
|
||||
through bfd functions, so that didn't have to change.
|
||||
|
||||
* hist.c (hist_read_rec, hist_write_rec): added support for
|
||||
collection pc histograms measuring quantities other than time;
|
||||
the histogram header now includes a field that specifies the
|
||||
dimension of the quantity measured by the histogram bins
|
||||
(normally, this is "seconds", but other meaningful dimensions
|
||||
include such things as "I-cache misses", "instruction issue stalls"
|
||||
etc.); there is also a field to specify a one-character
|
||||
abbreviation for the dimension; in the case of time, this would
|
||||
be 's'; in most other cases it probably would be '1' (not a physical
|
||||
dimension).
|
||||
|
||||
Thu Sep 8 16:05:08 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu)
|
||||
|
||||
* gprof.c, gmon_io.[ch]: BSD_COMPATIBLE is gone and new_file_version
|
||||
has become old_file_version; gmon_io.c now always supports old-style
|
||||
gmon.out files; it first tries to read gmon.out as a new version
|
||||
file, if that fails, it tries to read it in the old format;
|
||||
although not very likely, it is possible for gprof to mistake an
|
||||
old-style file as a new one (the first 4 bytes would have to
|
||||
be "gmon"---including the trailing '\0'); in that case, it is
|
||||
necessary to specify --old-file-version
|
||||
|
||||
* gprof.h: removed dependency on SYSV; the code now always uses
|
||||
strrchr(), memset(), and memcpy() and does not include either
|
||||
of string.h or strings.h; this should make gprof compile on
|
||||
any (Unix) system without configuration (per suggestion of
|
||||
raeburn@cygnus.com)
|
||||
|
||||
* gprof.c (usage): fixed location of --new-file-format option.
|
||||
|
||||
* cg_arcs.c (propagate_flags): fixed typo in declaration.
|
||||
|
||||
* flat_bl.m: removed formfeed at end of file; the form-feed
|
||||
is now printed cg_print.c only when necessary.
|
||||
|
||||
* major rewrite of gprof---too many changes to mention all of
|
||||
them. new features:
|
||||
|
||||
+ -l now requests profiling at the line level (as opposed
|
||||
to function level); in this mode, gprof creates a "symbol"
|
||||
(aka name-list entry) for each line of source code, instead
|
||||
of one per function)
|
||||
|
||||
+ support for a new gmon.out file format; the new format
|
||||
consists of a header with a magic and a version number,
|
||||
followed by a sequence of profile data; profile data
|
||||
can any of: (a) PC histogram, (b) call-graph arcs, or
|
||||
(c) basic-block execution counts; the version number makes
|
||||
it possible to extend gmon.out in a backwards compatible
|
||||
fashion
|
||||
|
||||
+ support for tcov style annotated output: if the gmon.out file
|
||||
contains basic-block execution counts, the user can request
|
||||
the generation of annotated source files, much like Sun's
|
||||
tcov used to do
|
||||
|
||||
+ long options
|
||||
|
||||
+ new scheme to suppress symbols that aren't function names
|
||||
(e.g., avoids mistaking a goto label as a function)
|
||||
|
||||
+ reorganized source code to make it more managable; as a
|
||||
side effect, gprof now compiles cleanly with "gcc -Wall"
|
||||
|
||||
Thu Sep 1 15:46:49 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu)
|
||||
|
||||
* gprof.c (funcsymbol): bfd_find_nearest_line() is now used as a
|
||||
final cross-check to determine whether a static symbol should be
|
||||
considered as a function-name.
|
||||
|
||||
Fri Aug 5 19:32:36 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu)
|
||||
|
||||
* gmon_io.c (gmon_out_read): recognize "-" as the filename for
|
||||
stdin; this is useful if you wanna keep gmon.out files compressed;
|
||||
this way you can "gzcat" the compressed file into gprof.
|
||||
|
||||
* gprof.c: flag_min_count now initialized with 1 instead of 0.
|
||||
|
||||
* basic_blocks.c (bb_annotate_source): added support for creating
|
||||
.tcov files when option flag_annotate_make_files is TRUE.
|
||||
(annotate_with_count): all counts less than the minimum count
|
||||
specified by -m are now annotated with hash-marks.
|
||||
|
||||
* gprof.c (main): -A is now followed by a string of option chars.
|
||||
|
||||
* basic_blocks.c (annotate_with_count): replaced b->count with
|
||||
cnt.
|
||||
|
||||
* source.c: flag_annotate_source replaced by source_lock_map.
|
||||
|
||||
* source.h: source_lock_map added.
|
||||
|
||||
* gprof.c (main): new command-line syntax: -S simply specifies
|
||||
which source-files user is interested in; -A requests annotated
|
||||
source files and -AA requests that all lines in a source file
|
||||
are annotated.
|
||||
|
||||
Thu Aug 4 23:27:03 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu)
|
||||
|
||||
* basic_blocks.c (PATH_MAX): if undefined, define as 1024.
|
||||
|
||||
* sparc.c, i386.c, tahoe.c, vax.c: added include of "time_hist.h"
|
||||
so s_lowpc etc. get declared.
|
||||
|
||||
* arcs.h (doarcs): created.
|
||||
|
||||
* arcs.c: reordered static functions such that they get defined
|
||||
before use.
|
||||
|
||||
* gprof.c (main): added options:
|
||||
-A: request annotation of all source lines (with -S)
|
||||
-m: minimum execution count (with default basic-block display)
|
||||
-N: force new file format (only if BSD_COMPATIBLE is defined)
|
||||
-S: annotate source file
|
||||
-t: set table length (with -S)
|
||||
|
||||
* Makefile (OBJS): added basic_blocks.o call_graph.o gmon_io.o
|
||||
source.o time_hist.o
|
||||
|
||||
Fri Jul 1 15:23:50 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu)
|
||||
|
||||
* gprof.c (asgnsamples): computation of "pcl" and "pch" depended
|
||||
on the fact being able to store a long in a double without loss of
|
||||
precision; this does not hold on machines with 64 bit longs and 64
|
||||
bit doubles.
|
||||
|
||||
Fri Oct 21 18:58:02 1994 Ken Raeburn <raeburn@cujo.cygnus.com>
|
||||
|
||||
* printgprof.c (flatprofheader): Always set totime to 1.0 if not
|
||||
|
|
|
@ -34,18 +34,22 @@ texidir = $(srcdir)/../texinfo
|
|||
###
|
||||
|
||||
PROG= gprof
|
||||
SRCS= gprof.c arcs.c dfn.c lookup.c $(MY_MACHINE).c hertz.c \
|
||||
printgprof.c printlist.c
|
||||
SRCS= $(MY_MACHINE).c basic_blocks.c call_graph.c \
|
||||
cg_arcs.c cg_dfn.c cg_print.c core.c \
|
||||
gmon_io.c gprof.c hertz.c hist.c source.c search_list.c symtab.c \
|
||||
sym_ids.c utils.c
|
||||
|
||||
LIBS = ../bfd/libbfd.a ../libiberty/libiberty.a
|
||||
|
||||
OBJS= gprof.o arcs.o dfn.o lookup.o $(MY_MACHINE).o hertz.o \
|
||||
printgprof.o printlist.o \
|
||||
flat_bl.o bsd_callg_bl.o fsf_callg_bl.o
|
||||
OBJS= $(MY_MACHINE).o basic_blocks.o bsd_callg_bl.o call_graph.o \
|
||||
cg_arcs.o cg_dfn.o cg_print.o core.o flat_bl.o fsf_callg_bl.o \
|
||||
gmon_io.o gprof.o hertz.o hist.o source.o search_list.o symtab.o \
|
||||
sym_ids.o utils.o
|
||||
|
||||
# Files that can be generated, but should be included in distribution.
|
||||
DISTSTUFF = flat_bl.c bsd_callg_bl.c fsf_callg_bl.c
|
||||
|
||||
CFLAGS=-g
|
||||
CFLAGS=-g -DDEBUG
|
||||
LDFLAGS=
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) -I. -I$(srcdir) -I../bfd -I$(srcdir)/../include -I$(srcdir)/../bfd -DMACHINE_H=\"$(MY_MACHINE).h\" $(TCFLAGS) $(HCFLAGS) $<
|
||||
|
@ -116,14 +120,22 @@ Makefile : Makefile.in
|
|||
|
||||
|
||||
# These get around a bug in Sun Make in SunOS 4.1.1 and Solaris 2
|
||||
gprof.o: gprof.c
|
||||
arcs.o: arcs.c
|
||||
dfn.o: dfn.c
|
||||
lookup.o: lookup.c
|
||||
$(MY_MACHINE).o: $(MY_MACHINE).c
|
||||
hertz.o: hertz.c
|
||||
printgprof.o: printgprof.c
|
||||
printlist.o: printlist.c
|
||||
basic_blocks.o: basic_blocks.c
|
||||
bsd_call_bl.o: bsd_call_bl.c
|
||||
call_graph.o: call_graph.c
|
||||
cg_arcs.o: cg_arcs.c
|
||||
cg_dfn.o: cg_dfn.c
|
||||
cg_print.o: cg_print.c
|
||||
core.o: core.c
|
||||
flat_bl.o: flat_bl.c
|
||||
bsd_callg_bl.o: bsd_callg_bl.c
|
||||
fsf_callg_bl.o: fsf_callg_bl.c
|
||||
gmon_io.o: gmon_io.c
|
||||
gprof.o: gprof.c
|
||||
hertz.o: hertz.c
|
||||
hist.o: hist.c
|
||||
search_list.o: search_list.c
|
||||
source.o: source.c
|
||||
symtab.o: symtab.c
|
||||
sym_ids.o: sym_ids.c
|
||||
utils.o: utils.c
|
||||
|
|
438
gprof/NOTES
Normal file
438
gprof/NOTES
Normal file
|
@ -0,0 +1,438 @@
|
|||
Sun Feb 5 16:09:16 1995
|
||||
|
||||
This file documents the changes and new features available with this
|
||||
version of GNU gprof.
|
||||
|
||||
* New Features
|
||||
|
||||
o Long options
|
||||
|
||||
o Supports generalized file format, without breaking backward compatibility:
|
||||
new file format supports basic-block execution counts and non-realtime
|
||||
histograms (see below)
|
||||
|
||||
o Supports profiling at the line level: flat profiles, call-graph profiles,
|
||||
and execution-counts can all be displayed at a level that identifies
|
||||
individual lines rather than just functions
|
||||
|
||||
o Test-coverage support (similar to Sun tcov program): source files
|
||||
can be annotated with the number of times a function was invoked
|
||||
or with the number of times each basic-block in a function was
|
||||
executed
|
||||
|
||||
o Generalized histograms: not just execution-time, but arbitrary
|
||||
histograms are support (for example, performance counter based
|
||||
profiles)
|
||||
|
||||
o Powerful mechanism to select data to be included/excluded from
|
||||
analysis and/or output
|
||||
|
||||
o Support for DEC OSF/1 v3.0
|
||||
|
||||
o Full cross-platform profiling support: gprof uses BFD to support
|
||||
arbitrary, non-native object file formats and non-native byte-orders
|
||||
(this feature has not been tested yet)
|
||||
|
||||
o In the call-graph function index, static function names are now
|
||||
printed together with the filename in which the function was defined
|
||||
(required bfd_find_nearest_line() support and symbolic debugging
|
||||
information to be present in the executable file)
|
||||
|
||||
o Major overhaul of source code (compiles cleanly with -Wall, etc.)
|
||||
|
||||
* Supported Platforms
|
||||
|
||||
The current version is known to work on:
|
||||
|
||||
o DEC OSF/1 v3.0
|
||||
All features supported.
|
||||
|
||||
o SunOS 4.1.x
|
||||
All features supported.
|
||||
|
||||
o Solaris 2.3
|
||||
Line-level profiling unsupported because bfd_find_nearest_line()
|
||||
is not fully implemented for Elf binaries.
|
||||
|
||||
o HP-UX 9.01
|
||||
Line-level profiling unsupported because bfd_find_nearest_line()
|
||||
is not fully implemented for SOM binaries.
|
||||
|
||||
* Detailed Description
|
||||
|
||||
** User Interface Changes
|
||||
|
||||
The command-line interface is backwards compatible with earlier
|
||||
versions of GNU gprof and Berkeley gprof. The only exception is
|
||||
the option to delete arcs from the call graph. The old syntax
|
||||
was:
|
||||
|
||||
-k fromname toname
|
||||
|
||||
while the new syntax is:
|
||||
|
||||
-k fromname/toname
|
||||
|
||||
This change was necessary to be compatible with long-option parsing.
|
||||
Also, "fromname" and "toname" can now be arbitrary symspecs rather
|
||||
than just function names (see below for an explanation of symspecs).
|
||||
For example, option "-k gprof.c/" suppresses all arcs due to calls out
|
||||
of file "gprof.c".
|
||||
|
||||
*** Sym Specs
|
||||
|
||||
It is often necessary to apply gprof only to specific parts of a
|
||||
program. GNU gprof has a simple but powerful mechanism to achieve
|
||||
this. So called {\em symspecs\/} provide the foundation for this
|
||||
mechanism. A symspec selects the parts of a profiled program to which
|
||||
an operation should be applied to. The syntax of a symspec is
|
||||
simple:
|
||||
|
||||
filename_containing_a_dot
|
||||
| funcname_not_containing_a_dot
|
||||
| linenumber
|
||||
| ( [ any_filename ] `:' ( any_funcname | linenumber ) )
|
||||
|
||||
Here are some examples:
|
||||
|
||||
main.c Selects everything in file "main.c"---the
|
||||
dot in the string tells gprof to interpret
|
||||
the string as a filename, rather than as
|
||||
a function name. To select a file whose
|
||||
name does contain a dot, a trailing colon
|
||||
should be specified. For example, "odd:" is
|
||||
interpreted as the file named "odd".
|
||||
|
||||
main Selects all functions named "main". Notice
|
||||
that there may be multiple instances of the
|
||||
same function name because some of the
|
||||
definitions may be local (i.e., static).
|
||||
Unless a function name is unique in a program,
|
||||
you must use the colon notation explained
|
||||
below to specify a function from a specific
|
||||
source file. Sometimes, functionnames contain
|
||||
dots. In such cases, it is necessar to
|
||||
add a leading colon to the name. For example,
|
||||
":.mul" selects function ".mul".
|
||||
|
||||
main.c:main Selects function "main" in file "main.c".
|
||||
|
||||
main.c:134 Selects line 134 in file "main.c".
|
||||
|
||||
IMPLEMENTATION NOTE: The source code uses the type sym_id for symspecs.
|
||||
At some point, this probably ought to be changed to "sym_spec" to make
|
||||
reading the code easier.
|
||||
|
||||
*** Long options
|
||||
|
||||
GNU gprof now supports long options. The following is a list of all
|
||||
supported options. Options that are listed without description
|
||||
operate in the same manner as the corresponding option in older
|
||||
versions of gprof.
|
||||
|
||||
Short Form: Long Form:
|
||||
----------- ----------
|
||||
-l --line
|
||||
Request profiling at the line-level rather
|
||||
than just at the function level. Source
|
||||
lines are identified by symbols of the form:
|
||||
|
||||
func (file:line)
|
||||
|
||||
where "func" is the function name, "file" is the
|
||||
file name and "line" is the line-number that
|
||||
corresponds to the line.
|
||||
|
||||
To work properly, the binary must contain symbolic
|
||||
debugging information. This means that the source
|
||||
have to be translated with option "-g" specified.
|
||||
Functions for which there is no symbolic debugging
|
||||
information available are treated as if "--line"
|
||||
had not been specified. However, the line number
|
||||
printed with such symbols is usually incorrect
|
||||
and should be ignored.
|
||||
|
||||
-a --no-static
|
||||
-A[symspec] --annotated-source[=symspec]
|
||||
Request output in the form of annotated source
|
||||
files. If "symspec" is specified, print output only
|
||||
for symbols selected by "symspec". If the option
|
||||
is specified multiple times, annotated output is
|
||||
generated for the union of all symspecs.
|
||||
|
||||
Examples:
|
||||
|
||||
-A Prints annotated source for all
|
||||
source files.
|
||||
-Agprof.c Prints annotated source for file
|
||||
gprof.c.
|
||||
-Afoobar Prints annotated source for files
|
||||
containing a function named "foobar".
|
||||
The entire file will be printed, but
|
||||
only the function itself will be
|
||||
annotated with profile data.
|
||||
|
||||
-J[symspec] --no-annotated-source[=symspec]
|
||||
Suppress annotated source output. If specified
|
||||
without argument, annotated output is suppressed
|
||||
completely. With an argument, annotated output
|
||||
is suppressed only for the symbols selected by
|
||||
"symspec". If the option is specified multiple
|
||||
times, annotated output is suppressed for the
|
||||
union of all symspecs. This option has lower
|
||||
precedence than --annotated-source
|
||||
|
||||
-p[symspec] --flat-profile[=symspec]
|
||||
Request output in the form of a flat profile
|
||||
(unless any other output-style option is specified,
|
||||
this option is turned on by default). If
|
||||
"symspec" is specified, include only symbols
|
||||
selected by "symspec" in flat profile. If the
|
||||
option is specified multiple times, the flat
|
||||
profile includes symbols selected by the union
|
||||
of all symspecs.
|
||||
|
||||
-P[symspec] --no-flat-profile[=symspec]
|
||||
Suppress output in the flat profile. If given
|
||||
without an argument, the flat profile is suppressed
|
||||
completely. If "symspec" is specified, suppress
|
||||
the selected symbols in the flat profile. If the
|
||||
option is specified multiple times, the union of
|
||||
the selected symbols is suppressed. This option
|
||||
has lower precedence than --flat-profile.
|
||||
|
||||
-q[symspec] --graph[=symspec]
|
||||
Request output in the form of a call-graph
|
||||
(unless any other output-style option is specified,
|
||||
this option is turned on by default). If "symspec"
|
||||
is specified, include only symbols selected by
|
||||
"symspec" in the call-graph. If the option is
|
||||
specified multiple times, the call-graph includes
|
||||
symbols selected by the union of all symspecs.
|
||||
|
||||
-Q[symspec] --no-graph[=symspec]
|
||||
Suppress output in the call-graph. If given without
|
||||
an argument, the call-graph is suppressed completely.
|
||||
With a "symspec", suppress the selected symbols
|
||||
from the call-graph. If the option is specified
|
||||
multiple times, the union of the selected symbols
|
||||
is suppressed. This option has lower precedence
|
||||
than --graph.
|
||||
|
||||
-C[symspec] --exec-counts[=symspec]
|
||||
Request output in the form of execution counts.
|
||||
If "symspec" is present, include only symbols
|
||||
selected by "symspec" in the execution count
|
||||
listing. If the option is specified multiple
|
||||
times, the execution count listing includes
|
||||
symbols selected by the union of all symspecs.
|
||||
|
||||
-Z[symspec] --no-exec-counts[=symspec]
|
||||
Suppress output in the execution count listing.
|
||||
If given without an argument, the listing is
|
||||
suppressed completely. With a "symspec", suppress
|
||||
the selected symbols from the call-graph. If the
|
||||
option is specified multiple times, the union of
|
||||
the selected symbols is suppressed. This option
|
||||
has lower precedence than --exec-counts.
|
||||
|
||||
-i --file-info
|
||||
Print information about the profile files that
|
||||
are read. The information consists of the
|
||||
number and types of records present in the
|
||||
profile file. Currently, a profile file can
|
||||
contain any number and any combination of histogram,
|
||||
call-graph, or basic-block count records.
|
||||
|
||||
-s --sum
|
||||
|
||||
-x --all-lines
|
||||
This option affects annotated source output only.
|
||||
By default, only the lines at the beginning of
|
||||
a basic-block are annotated. If this option is
|
||||
specified, every line in a basic-block is annotated
|
||||
by repeating the annotation for the first line.
|
||||
This option is identical to tcov's "-a".
|
||||
|
||||
-I dirs --directory-path=dirs
|
||||
This option affects annotated source output only.
|
||||
Specifies the list of directories to be searched
|
||||
for source files. The argument "dirs" is a colon
|
||||
separated list of directories. By default, gprof
|
||||
searches for source files relative to the current
|
||||
working directory only.
|
||||
|
||||
-z --display-unused-functions
|
||||
|
||||
-m num --min-count=num
|
||||
This option affects annotated source and execution
|
||||
count output only. Symbols that are executed
|
||||
less than "num" times are suppressed. For annotated
|
||||
source output, suppressed symbols are marked
|
||||
by five hash-marks (#####). In an execution count
|
||||
output, suppressed symbols do not appear at all.
|
||||
|
||||
-L --print-path
|
||||
Normally, source filenames are printed with the path
|
||||
component suppressed. With this option, gprof
|
||||
can be forced to print the full pathname of
|
||||
source filenames. The full pathname is determined
|
||||
from symbolic debugging information in the image file
|
||||
and is relative to the directory in which the compiler
|
||||
was invoked.
|
||||
|
||||
-y --separate-files
|
||||
This option affects annotated source output only.
|
||||
Normally, gprof prints annotated source files
|
||||
to standard-output. If this option is specified,
|
||||
annotated source for a file named "path/filename"
|
||||
is generated in the file "filename-ann". That is,
|
||||
annotated output is {\em always\/} generated in
|
||||
gprof's current working directory. Care has to
|
||||
be taken if a program consists of files that have
|
||||
identical filenames, but distinct paths.
|
||||
|
||||
-c --static-call-graph
|
||||
|
||||
-t num --table-length=num
|
||||
This option affects annotated source output only.
|
||||
After annotating a source file, gprof generates
|
||||
an execution count summary consisting of a table
|
||||
of lines with the top execution counts. By
|
||||
default, this table is ten entries long.
|
||||
This option can be used to change the table length
|
||||
or, by specifying an argument value of 0, it can be
|
||||
suppressed completely.
|
||||
|
||||
-n symspec --time=symspec
|
||||
Only symbols selected by "symspec" are considered
|
||||
in total and percentage time computations.
|
||||
However, this option does not affect percentage time
|
||||
computation for the flat profile.
|
||||
If the option is specified multiple times, the union
|
||||
of all selected symbols is used in time computations.
|
||||
|
||||
-N --no-time=symspec
|
||||
Exclude the symbols selected by "symspec" from
|
||||
total and percentage time computations.
|
||||
However, this option does not affect percentage time
|
||||
computation for the flat profile.
|
||||
This option is ignored if any --time options are
|
||||
specified.
|
||||
|
||||
-w num --width=num
|
||||
Sets the output line width. Currently, this option
|
||||
affects the printing of the call-graph function index
|
||||
only.
|
||||
|
||||
-e <no long form---for backwards compatibility only>
|
||||
-E <no long form---for backwards compatibility only>
|
||||
-f <no long form---for backwards compatibility only>
|
||||
-F <no long form---for backwards compatibility only>
|
||||
-k <no long form---for backwards compatibility only>
|
||||
-b --brief
|
||||
-dnum --debug[=num]
|
||||
|
||||
-h --help
|
||||
Prints a usage message.
|
||||
|
||||
-O name --file-format=name
|
||||
Selects the format of the profile data files.
|
||||
Recognized formats are "auto", "bsd", "magic",
|
||||
and "prof". The last one is not yet supported.
|
||||
Format "auto" attempts to detect the file format
|
||||
automatically (this is the default behavior).
|
||||
It attempts to read the profile data files as
|
||||
"magic" files and if this fails, falls back to
|
||||
the "bsd" format. "bsd" forces gprof to read
|
||||
the data files in the BSD format. "magic" forces
|
||||
gprof to read the data files in the "magic" format.
|
||||
|
||||
-T --traditional
|
||||
-v --version
|
||||
|
||||
** File Format Changes
|
||||
|
||||
The old BSD-derived format used for profile data does not contain a
|
||||
magic cookie that allows to check whether a data file really is a
|
||||
gprof file. Furthermore, it does not provide a version number, thus
|
||||
rendering changes to the file format almost impossible. GNU gprof
|
||||
uses a new file format that provides these features. For backward
|
||||
compatibility, GNU gprof continues to support the old BSD-derived
|
||||
format, but not all features are supported with it. For example,
|
||||
basic-block execution counts cannot be accommodated by the old file
|
||||
format.
|
||||
|
||||
The new file format is defined in header file \file{gmon_out.h}. It
|
||||
consists of a header containing the magic cookie and a version number,
|
||||
as well as some spare bytes available for future extensions. All data
|
||||
in a profile data file is in the native format of the host on which
|
||||
the profile was collected. GNU gprof adapts automatically to the
|
||||
byte-order in use.
|
||||
|
||||
In the new file format, the header is followed by a sequence of
|
||||
records. Currently, there are three different record types: histogram
|
||||
records, call-graph arc records, and basic-block execution count
|
||||
records. Each file can contain any number of each record type. When
|
||||
reading a file, GNU gprof will ensure records of the same type are
|
||||
compatible with each other and compute the union of all records. For
|
||||
example, for basic-block execution counts, the union is simply the sum
|
||||
of all execution counts for each basic-block.
|
||||
|
||||
*** Histogram Records
|
||||
|
||||
Histogram records consist of a header that is followed by an array of
|
||||
bins. The header contains the text-segment range that the histogram
|
||||
spans, the size of the histogram in bytes (unlike in the old BSD
|
||||
format, this does not include the size of the header), the rate of the
|
||||
profiling clock, and the physical dimension that the bin counts
|
||||
represent after being scaled by the profiling clock rate. The
|
||||
physical dimension is specified in two parts: a long name of up to 15
|
||||
characters and a single character abbreviation. For example, a
|
||||
histogram representing real-time would specify the long name as
|
||||
"seconds" and the abbreviation as "s". This feature is useful for
|
||||
architectures that support performance monitor hardware (which,
|
||||
fortunately, is becoming increasingly common). For example, under DEC
|
||||
OSF/1, the "uprofile" command can be used to produce a histogram of,
|
||||
say, instruction cache misses. In this case, the dimension in the
|
||||
histogram header could be set to "i-cache misses" and the abbreviation
|
||||
could be set to "1" (because it is simply a count, not a physical
|
||||
dimension). Also, the profiling rate would have to be set to 1 in
|
||||
this case.
|
||||
|
||||
Histogram bins are 16-bit numbers and each bin represent an equal
|
||||
amount of text-space. For example, if the text-segment is one
|
||||
thousand bytes long and if there are ten bins in the histogram, each
|
||||
bin represents one hundred bytes.
|
||||
|
||||
|
||||
*** Call-Graph Records
|
||||
|
||||
Call-graph records have a format that is identical to the one used in
|
||||
the BSD-derived file format. It consists of an arc in the call graph
|
||||
and a count indicating the number of times the arc was traversed
|
||||
during program execution. Arcs are specified by a pair of addresses:
|
||||
the first must be within caller's function and the second must be
|
||||
within the callee's function. When performing profiling at the
|
||||
function level, these addresses can point anywhere within the
|
||||
respective function. However, when profiling at the line-level, it is
|
||||
better if the addresses are as close to the call-site/entry-point as
|
||||
possible. This will ensure that the line-level call-graph is able to
|
||||
identify exactly which line of source code performed calls to a
|
||||
function.
|
||||
|
||||
*** Basic-Block Execution Count Records
|
||||
|
||||
Basic-block execution count records consist of a header followed by a
|
||||
sequence of address/count pairs. The header simply specifies the
|
||||
length of the sequence. In an address/count pair, the address
|
||||
identifies a basic-block and the count specifies the number of times
|
||||
that basic-block was executed. Any address within the basic-address can
|
||||
be used.
|
||||
|
||||
IMPLEMENTATION NOTE: gcc -a can be used to instrument a program to
|
||||
record basic-block execution counts. However, the __bb_exit_func()
|
||||
that is currently present in libgcc2.c does not generate a gmon.out
|
||||
file in a suiteable format. This should be fixed for future releases
|
||||
of gcc. In the meantime, contact davidm@cs.arizona.edu for a version
|
||||
of __bb_exit_func() to is appropriate.
|
7
gprof/TEST
Normal file
7
gprof/TEST
Normal file
|
@ -0,0 +1,7 @@
|
|||
- check whether old file format is properly read when input comes from
|
||||
stdin
|
||||
|
||||
- check whether underscores are properly dealt with (both, on systems
|
||||
that prepend them to each C name and on systems that don't)
|
||||
|
||||
- ensure gprof fails gracefully when no debugging info available
|
69
gprof/TODO
Normal file
69
gprof/TODO
Normal file
|
@ -0,0 +1,69 @@
|
|||
Sun Feb 5 16:27:32 1995
|
||||
|
||||
- documentation
|
||||
- optimize bfd_find_nearest_line_num() (or replace by different interface)
|
||||
- add support for prof file format so that prof files can be displayed
|
||||
at the line-level (this is useful for the uprofile tool under DEC's
|
||||
OSF/1)
|
||||
|
||||
+ cleanup _bfd_ecoff_find_nearest_line_num() fixes & description
|
||||
+ ensure "cc -pg" produces good files under OSF/1 v3.0
|
||||
+ make sure gprof works together with OSF/1 v3.0's profiling libraries
|
||||
+ implement symtab_parse(); modify sym_lookup() to consider addr_high
|
||||
+ change gprof.c to collect lists, then invoke symtab_parse() for
|
||||
each list
|
||||
+ Questions:
|
||||
o is -c (--static-call-graph) useful at all? i can't see
|
||||
how; if it were deleted, gprof would be completely machine
|
||||
independent => yup, it is
|
||||
o are (long) option names appropriate?
|
||||
o -k (--exclude-arc) cannot be implemented with getopt();
|
||||
is new syntax (-k from/to) acceptable? If not, how to
|
||||
fix it?
|
||||
o in the FSF output, the call-graph index now prints
|
||||
the filename of static functions in parentheses; e.g.,
|
||||
static function foo() that is defined in file bar.c
|
||||
would be printed as:
|
||||
|
||||
[4] foo (bar.c)
|
||||
|
||||
is this acceptable? should it be done only optionally?
|
||||
o symbols with addresses that map back to a different
|
||||
name are suppressed (happens with labels, for example);
|
||||
is this acceptable? should it be done only optionally?
|
||||
+ generalize to allow arbitrary histograms (not just time histograms)
|
||||
+ basic-block information currently replaces all symbols created from
|
||||
the core because of an ugly ordering conflict---for now, the current
|
||||
solution works, but something cleaner is desirable ==> cleaned up,
|
||||
but it's slower now
|
||||
+ convert to very new file format (back to trivial format, that is :)
|
||||
+ replace "dummy.h" for Alpha (if there is any use to it)
|
||||
+ add support for execution time profiling at a basic-block level
|
||||
+ fix filename-off-by-one bug for Alpha (see ~/tmp/d.[ch])---no longer
|
||||
relevant
|
||||
+ "-pg -a" doesn't work as expected because mcleanup() will overwrite
|
||||
the file generated by __bb_exit_func() (or vice versa)
|
||||
+ first basic-block of fac() seems to get credited to last basic-block
|
||||
of previous function => bug in basic_blocks.c
|
||||
+ flat profile should provide automatic scaling for per-call times because
|
||||
otherwise they'll always be zero on a fast machine with tons of small
|
||||
functions
|
||||
+ make "-a" imply to retain line number info (without actually generating
|
||||
the debugging information (unless -g is specified)---no, this is a
|
||||
bad idea, because it is not clear what level of debugging info should
|
||||
be requested (e.g., -g vs. -g3); leaving it up to the user seems best
|
||||
+ add long options support (or at least use getopt instead of ad-hoc
|
||||
implementation)
|
||||
+ split into files according to abstract objects that are manipulated
|
||||
+ replace sccsid by rcsid & add "end of ..." to every .c file
|
||||
+ use DBG() everywhere
|
||||
+ fix spacing (" ," -> "," etc.)
|
||||
+ use DEFUNs everywhere
|
||||
+ make compile cleanly with -Wall
|
||||
+ "gcc -pg -O2" doesn't work on tecc.c unless -fno-omit-frame-pointer is
|
||||
specified; find out why
|
||||
+ make things portable (prototypes, const, etc.)
|
||||
+ if NEW_GMON_OUT is not defined, have a flag that will allow to
|
||||
read new gmon.out style files. The idea being that everyone
|
||||
will use the new format for basic-block style profiling but
|
||||
the old format for regular gpprofiling
|
154
gprof/alpha.c
Normal file
154
gprof/alpha.c
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
#include "gprof.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "core.h"
|
||||
#include "hist.h"
|
||||
#include "symtab.h"
|
||||
|
||||
/*
|
||||
* Opcodes of the call instructions:
|
||||
*/
|
||||
#define OP_Jxx 0x1a
|
||||
#define OP_BSR 0x34
|
||||
|
||||
#define Jxx_FUNC_JMP 0
|
||||
#define Jxx_FUNC_JSR 1
|
||||
#define Jxx_FUNC_RET 2
|
||||
#define Jxx_FUNC_JSR_COROUTINE 3
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
unsigned other : 26;
|
||||
unsigned op_code : 6;
|
||||
} a; /* any format */
|
||||
struct {
|
||||
signed disp : 21;
|
||||
unsigned ra : 5;
|
||||
unsigned op_code : 6;
|
||||
} b; /* branch format */
|
||||
struct {
|
||||
signed hint : 14;
|
||||
unsigned func : 2;
|
||||
unsigned rb : 5;
|
||||
unsigned ra : 5;
|
||||
unsigned op_code : 6;
|
||||
} j; /* jump format */
|
||||
} Instruction;
|
||||
|
||||
static Sym indirect_child;
|
||||
|
||||
|
||||
/*
|
||||
* On the Alpha we can only detect PC relative calls, which are
|
||||
* usually generated for calls to functions within the same
|
||||
* object file only. This is still better than nothing, however.
|
||||
* (In particular it should be possible to find functions that
|
||||
* potentially call integer division routines, for example.)
|
||||
*/
|
||||
void
|
||||
find_call(parent, p_lowpc, p_highpc)
|
||||
Sym *parent;
|
||||
bfd_vma p_lowpc;
|
||||
bfd_vma p_highpc;
|
||||
{
|
||||
static bfd_vma delta = 0;
|
||||
bfd_vma dest_pc;
|
||||
Instruction *pc;
|
||||
Sym *child;
|
||||
|
||||
if (!delta) {
|
||||
delta = (bfd_vma) core_text_space - core_text_sect->vma;
|
||||
|
||||
sym_init(&indirect_child);
|
||||
indirect_child.name = "<indirect child>";
|
||||
indirect_child.cg.prop.fract = 1.0;
|
||||
indirect_child.cg.cyc.head = &indirect_child;
|
||||
} /* if */
|
||||
|
||||
if (!core_text_space) {
|
||||
return;
|
||||
} /* if */
|
||||
if (p_lowpc < s_lowpc) {
|
||||
p_lowpc = s_lowpc;
|
||||
} /* if */
|
||||
if (p_highpc > s_highpc) {
|
||||
p_highpc = s_highpc;
|
||||
} /* if */
|
||||
DBG(CALLDEBUG, printf("[find_call] %s: 0x%lx to 0x%lx\n",
|
||||
parent->name, p_lowpc, p_highpc));
|
||||
for (pc = (Instruction*)(p_lowpc + delta);
|
||||
pc < (Instruction*)(p_highpc + delta);
|
||||
++pc)
|
||||
{
|
||||
switch (pc->a.op_code) {
|
||||
case OP_Jxx:
|
||||
/*
|
||||
* There is no simple and reliable way to determine the
|
||||
* target of a jsr (the hint bits help, but there aren't
|
||||
* enough bits to get a satisfactory hit rate). Instead,
|
||||
* for any indirect jump we simply add an arc from PARENT
|
||||
* to INDIRECT_CHILD---that way the user it at least able
|
||||
* to see that there are other calls as well.
|
||||
*/
|
||||
if (pc->j.func == Jxx_FUNC_JSR
|
||||
|| pc->j.func == Jxx_FUNC_JSR_COROUTINE)
|
||||
{
|
||||
DBG(CALLDEBUG,
|
||||
printf("[find_call] 0x%lx: jsr%s <indirect_child>\n",
|
||||
(bfd_vma) pc - delta,
|
||||
pc->j.func == Jxx_FUNC_JSR ? "" : "_coroutine"));
|
||||
arc_add(parent, &indirect_child, 0);
|
||||
} /* if */
|
||||
break;
|
||||
|
||||
case OP_BSR:
|
||||
DBG(CALLDEBUG,
|
||||
printf("[find_call] 0x%lx: bsr", (bfd_vma) pc - delta));
|
||||
/*
|
||||
* Regular PC relative addressing. Check that this is the
|
||||
* address of a function. The linker sometimes redirects
|
||||
* the entry point by 8 bytes to skip loading the global
|
||||
* pointer, so we all for either address:
|
||||
*/
|
||||
dest_pc = ((bfd_vma) (pc + 1 + pc->b.disp)) - delta;
|
||||
if (dest_pc >= s_lowpc && dest_pc <= s_highpc) {
|
||||
child = sym_lookup(&symtab, dest_pc);
|
||||
DBG(CALLDEBUG,
|
||||
printf(" 0x%lx\t; name=%s, addr=0x%lx",
|
||||
dest_pc, child->name, child->addr));
|
||||
if (child->addr == dest_pc || child->addr == dest_pc - 8) {
|
||||
DBG(CALLDEBUG, printf("\n"));
|
||||
/* a hit: */
|
||||
arc_add(parent, child, 0);
|
||||
continue;
|
||||
} /* if */
|
||||
} /* if */
|
||||
/*
|
||||
* Something funny going on.
|
||||
*/
|
||||
DBG(CALLDEBUG, printf("\tbut it's a botch\n"));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
} /* switch */
|
||||
} /* for */
|
||||
} /* find_call */
|
||||
/*** end of alpha.c ***/
|
31
gprof/alpha.h
Normal file
31
gprof/alpha.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#)alpha.h 1.4 (Berkeley) 6/1/90
|
||||
*/
|
||||
#ifndef alpha_h
|
||||
#define alpha_h
|
||||
|
||||
/*
|
||||
* Offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see hist_assign_samples for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_TO_CODE 0
|
||||
#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT))
|
||||
|
||||
#endif /* alpha_h */
|
566
gprof/arcs.c
566
gprof/arcs.c
|
@ -1,566 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)arcs.c 5.6 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* add (or just increment) an arc
|
||||
*/
|
||||
addarc( parentp , childp , count )
|
||||
nltype *parentp;
|
||||
nltype *childp;
|
||||
long count;
|
||||
{
|
||||
arctype *arcp;
|
||||
|
||||
# ifdef DEBUG
|
||||
if ( debug & TALLYDEBUG ) {
|
||||
printf( "[addarc] %d arcs from %s to %s\n" ,
|
||||
count , parentp -> name , childp -> name );
|
||||
}
|
||||
# endif DEBUG
|
||||
arcp = arclookup( parentp , childp );
|
||||
if ( arcp != 0 ) {
|
||||
/*
|
||||
* a hit: just increment the count.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & TALLYDEBUG ) {
|
||||
printf( "[tally] hit %d += %d\n" ,
|
||||
arcp -> arc_count , count );
|
||||
}
|
||||
# endif DEBUG
|
||||
arcp -> arc_count += count;
|
||||
return;
|
||||
}
|
||||
arcp = (arctype *) calloc( 1 , sizeof *arcp );
|
||||
arcp -> arc_parentp = parentp;
|
||||
arcp -> arc_childp = childp;
|
||||
arcp -> arc_count = count;
|
||||
/*
|
||||
* prepend this child to the children of this parent
|
||||
*/
|
||||
arcp -> arc_childlist = parentp -> children;
|
||||
parentp -> children = arcp;
|
||||
/*
|
||||
* prepend this parent to the parents of this child
|
||||
*/
|
||||
arcp -> arc_parentlist = childp -> parents;
|
||||
childp -> parents = arcp;
|
||||
}
|
||||
|
||||
/*
|
||||
* the code below topologically sorts the graph (collapsing cycles),
|
||||
* and propagates time bottom up and flags top down.
|
||||
*/
|
||||
|
||||
/*
|
||||
* the topologically sorted name list pointers
|
||||
*/
|
||||
nltype **topsortnlp;
|
||||
|
||||
topcmp( npp1 , npp2 )
|
||||
nltype **npp1;
|
||||
nltype **npp2;
|
||||
{
|
||||
return (*npp1) -> toporder - (*npp2) -> toporder;
|
||||
}
|
||||
|
||||
nltype **
|
||||
doarcs()
|
||||
{
|
||||
nltype *parentp, **timesortnlp;
|
||||
arctype *arcp;
|
||||
long index;
|
||||
|
||||
/*
|
||||
* initialize various things:
|
||||
* zero out child times.
|
||||
* count self-recursive calls.
|
||||
* indicate that nothing is on cycles.
|
||||
*/
|
||||
for ( parentp = nl ; parentp < npe ; parentp++ ) {
|
||||
parentp -> childtime = 0.0;
|
||||
arcp = arclookup( parentp , parentp );
|
||||
if ( arcp != 0 ) {
|
||||
parentp -> ncall -= arcp -> arc_count;
|
||||
parentp -> selfcalls = arcp -> arc_count;
|
||||
} else {
|
||||
parentp -> selfcalls = 0;
|
||||
}
|
||||
parentp -> propfraction = 0.0;
|
||||
parentp -> propself = 0.0;
|
||||
parentp -> propchild = 0.0;
|
||||
parentp -> printflag = FALSE;
|
||||
parentp -> toporder = DFN_NAN;
|
||||
parentp -> cycleno = 0;
|
||||
parentp -> cyclehead = parentp;
|
||||
parentp -> cnext = 0;
|
||||
if ( cflag ) {
|
||||
findcall( parentp , parentp -> value , (parentp+1) -> value );
|
||||
}
|
||||
}
|
||||
/*
|
||||
* topologically order things
|
||||
* if any node is unnumbered,
|
||||
* number it and any of its descendents.
|
||||
*/
|
||||
for ( parentp = nl ; parentp < npe ; parentp++ ) {
|
||||
if ( parentp -> toporder == DFN_NAN ) {
|
||||
dfn( parentp );
|
||||
}
|
||||
}
|
||||
/*
|
||||
* link together nodes on the same cycle
|
||||
*/
|
||||
cyclelink();
|
||||
/*
|
||||
* Sort the symbol table in reverse topological order
|
||||
*/
|
||||
topsortnlp = (nltype **) calloc( nname , sizeof(nltype *) );
|
||||
if ( topsortnlp == (nltype **) 0 ) {
|
||||
fprintf( stderr , "[doarcs] ran out of memory for topo sorting\n" );
|
||||
}
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
topsortnlp[ index ] = &nl[ index ];
|
||||
}
|
||||
qsort( topsortnlp , nname , sizeof(nltype *) , topcmp );
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[doarcs] topological sort listing\n" );
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
printf( "[doarcs] " );
|
||||
printf( "%d:" , topsortnlp[ index ] -> toporder );
|
||||
printname( topsortnlp[ index ] );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* starting from the topological top,
|
||||
* propagate print flags to children.
|
||||
* also, calculate propagation fractions.
|
||||
* this happens before time propagation
|
||||
* since time propagation uses the fractions.
|
||||
*/
|
||||
doflags();
|
||||
/*
|
||||
* starting from the topological bottom,
|
||||
* propogate children times up to parents.
|
||||
*/
|
||||
dotime();
|
||||
/*
|
||||
* Now, sort by propself + propchild.
|
||||
* sorting both the regular function names
|
||||
* and cycle headers.
|
||||
*/
|
||||
timesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) );
|
||||
if ( timesortnlp == (nltype **) 0 ) {
|
||||
fprintf( stderr , "%s: ran out of memory for sorting\n" , whoami );
|
||||
}
|
||||
for ( index = 0 ; index < nname ; index++ ) {
|
||||
timesortnlp[index] = &nl[index];
|
||||
}
|
||||
for ( index = 1 ; index <= ncycle ; index++ ) {
|
||||
timesortnlp[nname+index-1] = &cyclenl[index];
|
||||
}
|
||||
qsort( timesortnlp , nname + ncycle , sizeof(nltype *) , totalcmp );
|
||||
for ( index = 0 ; index < nname + ncycle ; index++ ) {
|
||||
timesortnlp[ index ] -> index = index + 1;
|
||||
}
|
||||
return( timesortnlp );
|
||||
}
|
||||
|
||||
dotime()
|
||||
{
|
||||
int index;
|
||||
|
||||
cycletime();
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
timepropagate( topsortnlp[ index ] );
|
||||
}
|
||||
}
|
||||
|
||||
timepropagate( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
arctype *arcp;
|
||||
nltype *childp;
|
||||
double share;
|
||||
double propshare;
|
||||
|
||||
if ( parentp -> propfraction == 0.0 ) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* gather time from children of this parent.
|
||||
*/
|
||||
for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
|
||||
childp = arcp -> arc_childp;
|
||||
if ( arcp -> arc_count == 0 ) {
|
||||
continue;
|
||||
}
|
||||
if ( childp == parentp ) {
|
||||
continue;
|
||||
}
|
||||
if ( childp -> propfraction == 0.0 ) {
|
||||
continue;
|
||||
}
|
||||
if ( childp -> cyclehead != childp ) {
|
||||
if ( parentp -> cycleno == childp -> cycleno ) {
|
||||
continue;
|
||||
}
|
||||
if ( parentp -> toporder <= childp -> toporder ) {
|
||||
fprintf( stderr , "[propagate] toporder botches\n" );
|
||||
}
|
||||
childp = childp -> cyclehead;
|
||||
} else {
|
||||
if ( parentp -> toporder <= childp -> toporder ) {
|
||||
fprintf( stderr , "[propagate] toporder botches\n" );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( childp -> ncall == 0 ) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* distribute time for this arc
|
||||
*/
|
||||
arcp -> arc_time = childp -> time
|
||||
* ( ( (double) arcp -> arc_count ) /
|
||||
( (double) childp -> ncall ) );
|
||||
arcp -> arc_childtime = childp -> childtime
|
||||
* ( ( (double) arcp -> arc_count ) /
|
||||
( (double) childp -> ncall ) );
|
||||
share = arcp -> arc_time + arcp -> arc_childtime;
|
||||
parentp -> childtime += share;
|
||||
/*
|
||||
* ( 1 - propfraction ) gets lost along the way
|
||||
*/
|
||||
propshare = parentp -> propfraction * share;
|
||||
/*
|
||||
* fix things for printing
|
||||
*/
|
||||
parentp -> propchild += propshare;
|
||||
arcp -> arc_time *= parentp -> propfraction;
|
||||
arcp -> arc_childtime *= parentp -> propfraction;
|
||||
/*
|
||||
* add this share to the parent's cycle header, if any.
|
||||
*/
|
||||
if ( parentp -> cyclehead != parentp ) {
|
||||
parentp -> cyclehead -> childtime += share;
|
||||
parentp -> cyclehead -> propchild += propshare;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & PROPDEBUG ) {
|
||||
printf( "[dotime] child \t" );
|
||||
printname( childp );
|
||||
printf( " with %f %f %d/%d\n" ,
|
||||
childp -> time , childp -> childtime ,
|
||||
arcp -> arc_count , childp -> ncall );
|
||||
printf( "[dotime] parent\t" );
|
||||
printname( parentp );
|
||||
printf( "\n[dotime] share %f\n" , share );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
cyclelink()
|
||||
{
|
||||
register nltype *nlp;
|
||||
register nltype *cyclenlp;
|
||||
int cycle;
|
||||
nltype *memberp;
|
||||
arctype *arcp;
|
||||
|
||||
/*
|
||||
* Count the number of cycles, and initialze the cycle lists
|
||||
*/
|
||||
ncycle = 0;
|
||||
for ( nlp = nl ; nlp < npe ; nlp++ ) {
|
||||
/*
|
||||
* this is how you find unattached cycles
|
||||
*/
|
||||
if ( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) {
|
||||
ncycle += 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* cyclenl is indexed by cycle number:
|
||||
* i.e. it is origin 1, not origin 0.
|
||||
*/
|
||||
cyclenl = (nltype *) calloc( ncycle + 1 , sizeof( nltype ) );
|
||||
if ( cyclenl == 0 ) {
|
||||
fprintf( stderr , "%s: No room for %d bytes of cycle headers\n" ,
|
||||
whoami , ( ncycle + 1 ) * sizeof( nltype ) );
|
||||
done();
|
||||
}
|
||||
/*
|
||||
* now link cycles to true cycleheads,
|
||||
* number them, accumulate the data for the cycle
|
||||
*/
|
||||
cycle = 0;
|
||||
for ( nlp = nl ; nlp < npe ; nlp++ ) {
|
||||
if ( !( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) ) {
|
||||
continue;
|
||||
}
|
||||
cycle += 1;
|
||||
cyclenlp = &cyclenl[cycle];
|
||||
cyclenlp -> name = 0; /* the name */
|
||||
cyclenlp -> value = 0; /* the pc entry point */
|
||||
cyclenlp -> time = 0.0; /* ticks in this routine */
|
||||
cyclenlp -> childtime = 0.0; /* cumulative ticks in children */
|
||||
cyclenlp -> ncall = 0; /* how many times called */
|
||||
cyclenlp -> selfcalls = 0; /* how many calls to self */
|
||||
cyclenlp -> propfraction = 0.0; /* what % of time propagates */
|
||||
cyclenlp -> propself = 0.0; /* how much self time propagates */
|
||||
cyclenlp -> propchild = 0.0; /* how much child time propagates */
|
||||
cyclenlp -> printflag = TRUE; /* should this be printed? */
|
||||
cyclenlp -> index = 0; /* index in the graph list */
|
||||
cyclenlp -> toporder = DFN_NAN; /* graph call chain top-sort order */
|
||||
cyclenlp -> cycleno = cycle; /* internal number of cycle on */
|
||||
cyclenlp -> cyclehead = cyclenlp; /* pointer to head of cycle */
|
||||
cyclenlp -> cnext = nlp; /* pointer to next member of cycle */
|
||||
cyclenlp -> parents = 0; /* list of caller arcs */
|
||||
cyclenlp -> children = 0; /* list of callee arcs */
|
||||
# ifdef DEBUG
|
||||
if ( debug & CYCLEDEBUG ) {
|
||||
printf( "[cyclelink] " );
|
||||
printname( nlp );
|
||||
printf( " is the head of cycle %d\n" , cycle );
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* link members to cycle header
|
||||
*/
|
||||
for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) {
|
||||
memberp -> cycleno = cycle;
|
||||
memberp -> cyclehead = cyclenlp;
|
||||
}
|
||||
/*
|
||||
* count calls from outside the cycle
|
||||
* and those among cycle members
|
||||
*/
|
||||
for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) {
|
||||
for ( arcp=memberp->parents ; arcp ; arcp=arcp->arc_parentlist ) {
|
||||
if ( arcp -> arc_parentp == memberp ) {
|
||||
continue;
|
||||
}
|
||||
if ( arcp -> arc_parentp -> cycleno == cycle ) {
|
||||
cyclenlp -> selfcalls += arcp -> arc_count;
|
||||
} else {
|
||||
cyclenlp -> ncall += arcp -> arc_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cycletime()
|
||||
{
|
||||
int cycle;
|
||||
nltype *cyclenlp;
|
||||
nltype *childp;
|
||||
|
||||
for ( cycle = 1 ; cycle <= ncycle ; cycle += 1 ) {
|
||||
cyclenlp = &cyclenl[ cycle ];
|
||||
for ( childp = cyclenlp -> cnext ; childp ; childp = childp -> cnext ) {
|
||||
if ( childp -> propfraction == 0.0 ) {
|
||||
/*
|
||||
* all members have the same propfraction except those
|
||||
* that were excluded with -E
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
cyclenlp -> time += childp -> time;
|
||||
}
|
||||
cyclenlp -> propself = cyclenlp -> propfraction * cyclenlp -> time;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* in one top to bottom pass over the topologically sorted namelist
|
||||
* propagate:
|
||||
* printflag as the union of parents' printflags
|
||||
* propfraction as the sum of fractional parents' propfractions
|
||||
* and while we're here, sum time for functions.
|
||||
*/
|
||||
doflags()
|
||||
{
|
||||
int index;
|
||||
nltype *childp;
|
||||
nltype *oldhead;
|
||||
|
||||
oldhead = 0;
|
||||
for ( index = nname-1 ; index >= 0 ; index -= 1 ) {
|
||||
childp = topsortnlp[ index ];
|
||||
/*
|
||||
* if we haven't done this function or cycle,
|
||||
* inherit things from parent.
|
||||
* this way, we are linear in the number of arcs
|
||||
* since we do all members of a cycle (and the cycle itself)
|
||||
* as we hit the first member of the cycle.
|
||||
*/
|
||||
if ( childp -> cyclehead != oldhead ) {
|
||||
oldhead = childp -> cyclehead;
|
||||
inheritflags( childp );
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & PROPDEBUG ) {
|
||||
printf( "[doflags] " );
|
||||
printname( childp );
|
||||
printf( " inherits printflag %d and propfraction %f\n" ,
|
||||
childp -> printflag , childp -> propfraction );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( ! childp -> printflag ) {
|
||||
/*
|
||||
* printflag is off
|
||||
* it gets turned on by
|
||||
* being on -f list,
|
||||
* or there not being any -f list and not being on -e list.
|
||||
*/
|
||||
if ( onlist( flist , childp -> name )
|
||||
|| ( !fflag && !onlist( elist , childp -> name ) ) ) {
|
||||
childp -> printflag = TRUE;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* this function has printing parents:
|
||||
* maybe someone wants to shut it up
|
||||
* by putting it on -e list. (but favor -f over -e)
|
||||
*/
|
||||
if ( ( !onlist( flist , childp -> name ) )
|
||||
&& onlist( elist , childp -> name ) ) {
|
||||
childp -> printflag = FALSE;
|
||||
}
|
||||
}
|
||||
if ( childp -> propfraction == 0.0 ) {
|
||||
/*
|
||||
* no parents to pass time to.
|
||||
* collect time from children if
|
||||
* its on -F list,
|
||||
* or there isn't any -F list and its not on -E list.
|
||||
*/
|
||||
if ( onlist( Flist , childp -> name )
|
||||
|| ( !Fflag && !onlist( Elist , childp -> name ) ) ) {
|
||||
childp -> propfraction = 1.0;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* it has parents to pass time to,
|
||||
* but maybe someone wants to shut it up
|
||||
* by puttting it on -E list. (but favor -F over -E)
|
||||
*/
|
||||
if ( !onlist( Flist , childp -> name )
|
||||
&& onlist( Elist , childp -> name ) ) {
|
||||
childp -> propfraction = 0.0;
|
||||
}
|
||||
}
|
||||
childp -> propself = childp -> time * childp -> propfraction;
|
||||
printtime += childp -> propself;
|
||||
# ifdef DEBUG
|
||||
if ( debug & PROPDEBUG ) {
|
||||
printf( "[doflags] " );
|
||||
printname( childp );
|
||||
printf( " ends up with printflag %d and propfraction %f\n" ,
|
||||
childp -> printflag , childp -> propfraction );
|
||||
printf( "time %f propself %f printtime %f\n" ,
|
||||
childp -> time , childp -> propself , printtime );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* check if any parent of this child
|
||||
* (or outside parents of this cycle)
|
||||
* have their print flags on and set the
|
||||
* print flag of the child (cycle) appropriately.
|
||||
* similarly, deal with propagation fractions from parents.
|
||||
*/
|
||||
inheritflags( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
nltype *headp;
|
||||
arctype *arcp;
|
||||
nltype *parentp;
|
||||
nltype *memp;
|
||||
|
||||
headp = childp -> cyclehead;
|
||||
if ( childp == headp ) {
|
||||
/*
|
||||
* just a regular child, check its parents
|
||||
*/
|
||||
childp -> printflag = FALSE;
|
||||
childp -> propfraction = 0.0;
|
||||
for (arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist) {
|
||||
parentp = arcp -> arc_parentp;
|
||||
if ( childp == parentp ) {
|
||||
continue;
|
||||
}
|
||||
childp -> printflag |= parentp -> printflag;
|
||||
/*
|
||||
* if the child was never actually called
|
||||
* (e.g. this arc is static (and all others are, too))
|
||||
* no time propagates along this arc.
|
||||
*/
|
||||
if ( childp -> ncall ) {
|
||||
childp -> propfraction += parentp -> propfraction
|
||||
* ( ( (double) arcp -> arc_count )
|
||||
/ ( (double) childp -> ncall ) );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* its a member of a cycle, look at all parents from
|
||||
* outside the cycle
|
||||
*/
|
||||
headp -> printflag = FALSE;
|
||||
headp -> propfraction = 0.0;
|
||||
for ( memp = headp -> cnext ; memp ; memp = memp -> cnext ) {
|
||||
for (arcp = memp->parents ; arcp ; arcp = arcp->arc_parentlist) {
|
||||
if ( arcp -> arc_parentp -> cyclehead == headp ) {
|
||||
continue;
|
||||
}
|
||||
parentp = arcp -> arc_parentp;
|
||||
headp -> printflag |= parentp -> printflag;
|
||||
/*
|
||||
* if the cycle was never actually called
|
||||
* (e.g. this arc is static (and all others are, too))
|
||||
* no time propagates along this arc.
|
||||
*/
|
||||
if ( headp -> ncall ) {
|
||||
headp -> propfraction += parentp -> propfraction
|
||||
* ( ( (double) arcp -> arc_count )
|
||||
/ ( (double) headp -> ncall ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( memp = headp ; memp ; memp = memp -> cnext ) {
|
||||
memp -> printflag = headp -> printflag;
|
||||
memp -> propfraction = headp -> propfraction;
|
||||
}
|
||||
}
|
||||
}
|
452
gprof/basic_blocks.c
Normal file
452
gprof/basic_blocks.c
Normal file
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* Basic-block level related code: reading/writing of basic-block info
|
||||
* to/from gmon.out; computing and formatting of basic-block related
|
||||
* statistics.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "basic_blocks.h"
|
||||
#include "core.h"
|
||||
#include "gmon_io.h"
|
||||
#include "gmon_out.h"
|
||||
#include "gprof.h"
|
||||
#include "libiberty.h"
|
||||
#include "source.h"
|
||||
#include "sym_ids.h"
|
||||
|
||||
|
||||
/*
|
||||
* Default option values:
|
||||
*/
|
||||
bool bb_annotate_all_lines = FALSE;
|
||||
int bb_min_calls = 1;
|
||||
int bb_table_length = 10;
|
||||
|
||||
/*
|
||||
* Variables used to compute annotated source listing stats:
|
||||
*/
|
||||
static long num_executable_lines;
|
||||
static long num_lines_executed;
|
||||
|
||||
|
||||
/*
|
||||
* Helper for sorting. Compares two basic-blocks and returns result
|
||||
* such that sorting will be increasing according to filename, line
|
||||
* number, and basic-block address (in that order).
|
||||
*/
|
||||
static int
|
||||
DEFUN(cmp_bb, (lp, rp), const void *lp AND const void *rp)
|
||||
{
|
||||
int r;
|
||||
const Sym *left = *(const Sym**)lp;
|
||||
const Sym *right = *(const Sym**)rp;
|
||||
|
||||
if (left->file && right->file) {
|
||||
r = strcmp(left->file->name, right->file->name);
|
||||
if (r) {
|
||||
return r;
|
||||
} /* if */
|
||||
|
||||
if (left->line_num != right->line_num) {
|
||||
return left->line_num - right->line_num;
|
||||
} /* if */
|
||||
} /* if */
|
||||
|
||||
if (left->addr < right->addr) {
|
||||
return -1;
|
||||
} else if (left->addr > right->addr) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
} /* if */
|
||||
} /* cmp_bb */
|
||||
|
||||
|
||||
/*
|
||||
* Helper for sorting. Order basic blocks in decreasing number of
|
||||
* calls, ties are broken in increasing order of line numbers.
|
||||
*/
|
||||
static int
|
||||
DEFUN(cmp_ncalls, (lp, rp), const void *lp AND const void *rp)
|
||||
{
|
||||
const Sym *left = *(const Sym**)lp;
|
||||
const Sym *right = *(const Sym**)rp;
|
||||
|
||||
if (!left) {
|
||||
return 1;
|
||||
} else if (!right) {
|
||||
return -1;
|
||||
} /* if */
|
||||
|
||||
if (right->ncalls != left->ncalls) {
|
||||
return right->ncalls - left->ncalls;
|
||||
} /* if */
|
||||
|
||||
return left->line_num - right->line_num;
|
||||
} /* cmp_ncalls */
|
||||
|
||||
|
||||
/*
|
||||
* Skip over variable length string.
|
||||
*/
|
||||
static void
|
||||
DEFUN(fskip_string, (fp), FILE *fp)
|
||||
{
|
||||
int ch;
|
||||
|
||||
while ((ch = fgetc(fp)) != EOF) {
|
||||
if (ch == '\0') {
|
||||
break;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* fskip_string */
|
||||
|
||||
|
||||
/*
|
||||
* Read a basic-block record from file IFP. FILENAME is the name
|
||||
* of file IFP and is provided for formatting error-messages only.
|
||||
*/
|
||||
void
|
||||
DEFUN(bb_read_rec, (ifp, filename), FILE *ifp AND const char *filename)
|
||||
{
|
||||
int nblocks, b;
|
||||
bfd_vma addr;
|
||||
long ncalls;
|
||||
Sym *sym;
|
||||
|
||||
if (fread(&nblocks, sizeof(nblocks), 1, ifp) != 1) {
|
||||
fprintf(stderr, "%s: %s: unexpected end of file\n", whoami, filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
nblocks = bfd_get_32(core_bfd, (bfd_byte*) &nblocks);
|
||||
if (gmon_file_version == 0) {
|
||||
fskip_string(ifp);
|
||||
} /* if */
|
||||
|
||||
for (b = 0; b < nblocks; ++b) {
|
||||
if (gmon_file_version == 0) {
|
||||
int line_num;
|
||||
/*
|
||||
* Version 0 had lots of extra stuff that we don't
|
||||
* care about anymore.
|
||||
*/
|
||||
if ((fread(&ncalls, sizeof(ncalls), 1, ifp) != 1)
|
||||
|| (fread(&addr, sizeof(addr), 1, ifp) != 1)
|
||||
|| (fskip_string(ifp), FALSE)
|
||||
|| (fskip_string(ifp), FALSE)
|
||||
|| (fread(&line_num, sizeof(line_num), 1, ifp) != 1))
|
||||
{
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
} else {
|
||||
if (fread(&addr, sizeof(addr), 1, ifp) != 1
|
||||
|| fread(&ncalls, sizeof(ncalls), 1, ifp) != 1)
|
||||
{
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
} /* if */
|
||||
|
||||
/*
|
||||
* Basic-block execution counts are meaningful only if we're
|
||||
* profiling at the line-by-line level:
|
||||
*/
|
||||
if (line_granularity) {
|
||||
|
||||
/* convert from target to host endianness: */
|
||||
|
||||
addr = get_vma(core_bfd, (bfd_byte*) &addr);
|
||||
|
||||
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));
|
||||
} else {
|
||||
static bool user_warned = FALSE;
|
||||
|
||||
if (!user_warned) {
|
||||
user_warned = TRUE;
|
||||
fprintf(stderr,
|
||||
"%s: warning: ignoring basic-block exec counts (use -l or --line)\n",
|
||||
whoami);
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* for */
|
||||
return;
|
||||
} /* bb_read_rec */
|
||||
|
||||
|
||||
/*
|
||||
* Write all basic-blocks with non-zero counts to file OFP. FILENAME
|
||||
* is the name of OFP and is provided for producing error-messages
|
||||
* only.
|
||||
*/
|
||||
void
|
||||
DEFUN(bb_write_blocks, (ofp, filename), FILE *ofp AND const char *filename)
|
||||
{
|
||||
const unsigned char tag = GMON_TAG_BB_COUNT;
|
||||
int nblocks = 0;
|
||||
bfd_vma addr;
|
||||
long ncalls;
|
||||
Sym *sym;
|
||||
|
||||
/* count how many non-zero blocks with have: */
|
||||
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
if (sym->ncalls > 0) {
|
||||
++nblocks;
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/* write header: */
|
||||
bfd_put_32(core_bfd, nblocks, (bfd_byte*) &nblocks);
|
||||
if (fwrite(&tag, sizeof(tag), 1, ofp) != 1
|
||||
|| fwrite(&nblocks, sizeof(nblocks), 1, ofp) != 1)
|
||||
{
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
/* write counts: */
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
if (sym->ncalls == 0) {
|
||||
continue;
|
||||
} /* if */
|
||||
|
||||
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 */
|
||||
} /* for */
|
||||
} /* bb_write_blocks */
|
||||
|
||||
|
||||
/*
|
||||
* Output basic-block statistics in a format that is easily parseable.
|
||||
* Current the format is:
|
||||
*
|
||||
* <filename>:<line-number>: (<function-name>:<bb-addr): <ncalls>
|
||||
*/
|
||||
void
|
||||
DEFUN_VOID(print_exec_counts)
|
||||
{
|
||||
Sym **sorted_bbs, *sym;
|
||||
int i, len;
|
||||
|
||||
if (first_output) {
|
||||
first_output = FALSE;
|
||||
} else {
|
||||
printf("\f\n");
|
||||
} /* if */
|
||||
|
||||
/* sort basic-blocks according to function name and line number: */
|
||||
|
||||
sorted_bbs = (Sym**)xmalloc(symtab.len * sizeof(sorted_bbs[0]));
|
||||
len = 0;
|
||||
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.
|
||||
*/
|
||||
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))))
|
||||
{
|
||||
sorted_bbs[len++] = sym;
|
||||
} /* if */
|
||||
} /* for */
|
||||
qsort(sorted_bbs, len, sizeof(sorted_bbs[0]), cmp_bb);
|
||||
|
||||
/* output basic-blocks: */
|
||||
|
||||
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);
|
||||
} /* for */
|
||||
free(sorted_bbs);
|
||||
} /* print_exec_counts */
|
||||
|
||||
|
||||
/*
|
||||
* Helper for bb_annotated_source: format annotation containing
|
||||
* number of line executions.
|
||||
*/
|
||||
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;
|
||||
|
||||
if (line_num == 1) {
|
||||
last_count = -1;
|
||||
} /* if */
|
||||
|
||||
b = 0;
|
||||
if (line_num <= sf->num_lines) {
|
||||
b = sf->line[line_num - 1];
|
||||
} /* if */
|
||||
if (!b) {
|
||||
cnt = -1;
|
||||
} else {
|
||||
++num_executable_lines;
|
||||
cnt = b->ncalls;
|
||||
} /* if */
|
||||
if (cnt > 0) {
|
||||
++num_lines_executed;
|
||||
} /* if */
|
||||
if (cnt < 0 && bb_annotate_all_lines) {
|
||||
cnt = last_count;
|
||||
} /* if */
|
||||
|
||||
if (cnt < 0) {
|
||||
strcpy(buf, "\t\t");
|
||||
} else if (cnt < bb_min_calls) {
|
||||
strcpy(buf, " ##### -> ");
|
||||
} else {
|
||||
sprintf(buf, "%12ld -> ", cnt);
|
||||
} /* if */
|
||||
last_count = cnt;
|
||||
} /* annotate_with_count */
|
||||
|
||||
|
||||
/*
|
||||
* Annotate the files named in SOURCE_FILES with basic-block statistics
|
||||
* (execution counts). After each source files, a few statistics
|
||||
* regarding that source file are printed.
|
||||
*/
|
||||
void
|
||||
DEFUN_VOID(print_annotated_source)
|
||||
{
|
||||
Sym *sym, *line_stats, *new_line;
|
||||
Source_File *sf;
|
||||
int i, table_len;
|
||||
FILE *ofp;
|
||||
|
||||
/*
|
||||
* Find maximum line number for each source file that user is
|
||||
* interested in:
|
||||
*/
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
/*
|
||||
* Accept symbol if it's file is known, its line number is
|
||||
* bigger than anything we have seen for that file so far and
|
||||
* if it's in the INCL_ANNO table or there is no INCL_ANNO
|
||||
* table and it does not appear in the EXCL_ANNO table.
|
||||
*/
|
||||
if (sym->file && sym->line_num > sym->file->num_lines
|
||||
&& (sym_lookup(&syms[INCL_ANNO], sym->addr)
|
||||
|| (syms[INCL_ANNO].len == 0
|
||||
&& !sym_lookup(&syms[EXCL_ANNO], sym->addr))))
|
||||
{
|
||||
sym->file->num_lines = sym->line_num;
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/* allocate line descriptors: */
|
||||
|
||||
for (sf = first_src_file; sf; sf = sf->next) {
|
||||
if (sf->num_lines > 0) {
|
||||
sf->line = (void*) xmalloc(sf->num_lines * sizeof(sf->line[0]));
|
||||
memset(sf->line, 0, sf->num_lines * sizeof(sf->line[0]));
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/* count executions per line: */
|
||||
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
if (sym->is_bb_head && 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))))
|
||||
{
|
||||
sym->file->ncalls += sym->ncalls;
|
||||
line_stats = sym->file->line[sym->line_num - 1];
|
||||
if (!line_stats) {
|
||||
/* common case has at most one basic-block per source line: */
|
||||
sym->file->line[sym->line_num - 1] = sym;
|
||||
} else if (!line_stats->addr) {
|
||||
/* sym is the 3rd .. nth basic block for this line: */
|
||||
line_stats->ncalls += sym->ncalls;
|
||||
} else {
|
||||
/* sym is the second basic block for this line */
|
||||
new_line = (Sym*) xmalloc(sizeof(*new_line));
|
||||
*new_line = *line_stats;
|
||||
new_line->addr = 0;
|
||||
new_line->ncalls += sym->ncalls;
|
||||
sym->file->line[sym->line_num - 1] = new_line;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/* plod over source files, annotating them: */
|
||||
|
||||
for (sf = first_src_file; sf; sf = sf->next) {
|
||||
if (!sf->num_lines || (ignore_zeros && sf->ncalls == 0)) {
|
||||
continue;
|
||||
} /* if */
|
||||
|
||||
num_executable_lines = num_lines_executed = 0;
|
||||
ofp = annotate_source(sf, 16, annotate_with_count, sf);
|
||||
if (!ofp) {
|
||||
continue;
|
||||
} /* if */
|
||||
|
||||
if (bb_table_length > 0) {
|
||||
fprintf(ofp, "\n\nTop %d Lines:\n\n Line Count\n\n",
|
||||
bb_table_length);
|
||||
|
||||
/* abuse line arrays---it's not needed anymore: */
|
||||
qsort(sf->line, sf->num_lines, sizeof(sf->line[0]), cmp_ncalls);
|
||||
table_len = bb_table_length;
|
||||
if (table_len > sf->num_lines) {
|
||||
table_len = sf->num_lines;
|
||||
} /* if */
|
||||
for (i = 0; i < table_len; ++i) {
|
||||
sym = sf->line[i];
|
||||
if (!sym || sym->ncalls <= 0) {
|
||||
break;
|
||||
} /* if */
|
||||
fprintf(ofp, "%9d %10d\n", sym->line_num, sym->ncalls);
|
||||
} /* for */
|
||||
} /* if */
|
||||
|
||||
free(sf->line);
|
||||
sf->line = 0;
|
||||
|
||||
fprintf(ofp, "\nExecution Summary:\n\n");
|
||||
fprintf(ofp, "%9ld Executable lines in this file\n",
|
||||
num_executable_lines);
|
||||
fprintf(ofp, "%9ld Lines executed\n", num_lines_executed);
|
||||
fprintf(ofp, "%9.2f Percent of the file executed\n",
|
||||
num_executable_lines
|
||||
? 100.0 * num_lines_executed / (double) num_executable_lines
|
||||
: 100.0);
|
||||
fprintf(ofp, "\n%9d Total number of line executions\n", sf->ncalls);
|
||||
fprintf(ofp, "%9.2f Average executions per line\n",
|
||||
num_executable_lines
|
||||
? sf->ncalls / (double) num_executable_lines
|
||||
: 0.0);
|
||||
if (ofp != stdout) {
|
||||
fclose(ofp);
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* print_annotated_source */
|
||||
|
||||
/*** end of basic_block.c ***/
|
23
gprof/basic_blocks.h
Normal file
23
gprof/basic_blocks.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef basic_blocks_h
|
||||
#define basic_blocks_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "gprof.h"
|
||||
#include "source.h"
|
||||
#include "symtab.h"
|
||||
|
||||
/*
|
||||
* Options:
|
||||
*/
|
||||
extern bool bb_annotate_all_lines; /* force annotation of all lines? */
|
||||
extern int bb_table_length; /* length of most-used bb table */
|
||||
extern int bb_min_calls; /* minimum execution count */
|
||||
|
||||
extern void bb_read_rec PARAMS((FILE *ifp, const char *filename));
|
||||
extern void bb_write_blocks PARAMS((FILE *ofp, const char *filename));
|
||||
extern void bb_create_syms PARAMS((void));
|
||||
|
||||
extern void print_annotated_source PARAMS((void));
|
||||
extern void print_exec_counts PARAMS((void));
|
||||
|
||||
#endif /* basic_blocks_h */
|
96
gprof/call_graph.c
Normal file
96
gprof/call_graph.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
#include "cg_arcs.h"
|
||||
#include "call_graph.h"
|
||||
#include "core.h"
|
||||
#include "gmon_io.h"
|
||||
#include "gmon_out.h"
|
||||
#include "symtab.h"
|
||||
#include "sym_ids.h"
|
||||
|
||||
extern void
|
||||
DEFUN(cg_tally, (from_pc, self_pc, count),
|
||||
bfd_vma from_pc AND bfd_vma self_pc AND int count)
|
||||
{
|
||||
Sym *parent;
|
||||
Sym *child;
|
||||
|
||||
parent = sym_lookup(&symtab, from_pc);
|
||||
child = sym_lookup(&symtab, self_pc);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (sym_id_arc_is_present(&syms[INCL_ARCS], parent, child)
|
||||
|| (syms[INCL_ARCS].len == 0
|
||||
&& !sym_id_arc_is_present(&syms[EXCL_ARCS], parent, child)))
|
||||
{
|
||||
child->ncalls += count;
|
||||
DBG(TALLYDEBUG,
|
||||
printf("[cg_tally] arc from %s to %s traversed %d times\n",
|
||||
parent->name, child->name, count));
|
||||
arc_add(parent, child, count);
|
||||
} /* if */
|
||||
} /* cg_tally */
|
||||
|
||||
|
||||
/*
|
||||
* Read a record from file IFP describing an arc in the function
|
||||
* call-graph and the count of how many times the arc has been
|
||||
* traversed. FILENAME is the name of file IFP and is provided
|
||||
* for formatting error-messages only.
|
||||
*/
|
||||
void
|
||||
DEFUN(cg_read_rec, (ifp, filename), FILE *ifp AND CONST char *filename)
|
||||
{
|
||||
bfd_vma from_pc, self_pc;
|
||||
struct gmon_cg_arc_record arc;
|
||||
int count;
|
||||
|
||||
if (fread(&arc, sizeof(arc), 1, ifp) != 1) {
|
||||
fprintf(stderr, "%s: %s: unexpected end of file\n",
|
||||
whoami, filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
from_pc = get_vma(core_bfd, (bfd_byte *)arc.from_pc);
|
||||
self_pc = get_vma(core_bfd, (bfd_byte *)arc.self_pc);
|
||||
count = bfd_get_32(core_bfd, (bfd_byte *)arc.count);
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf("[cg_read_rec] frompc 0x%lx selfpc 0x%lx count %d\n",
|
||||
from_pc, self_pc, count));
|
||||
/* add this arc: */
|
||||
cg_tally(from_pc, self_pc, count);
|
||||
} /* cg_read_rec */
|
||||
|
||||
|
||||
/*
|
||||
* Write all the arcs in the call-graph to file OFP. FILENAME is
|
||||
* the name of OFP and is provided for formatting error-messages
|
||||
* only.
|
||||
*/
|
||||
void
|
||||
DEFUN(cg_write_arcs, (ofp, filename), FILE *ofp AND const char *filename)
|
||||
{
|
||||
const unsigned char tag = GMON_TAG_CG_ARC;
|
||||
struct gmon_cg_arc_record raw_arc;
|
||||
Arc *arc;
|
||||
Sym *sym;
|
||||
|
||||
for (sym = symtab.base; sym < symtab.limit; sym++) {
|
||||
for (arc = sym->cg.children; arc; arc = arc->next_child) {
|
||||
put_vma(core_bfd, arc->parent->addr, (bfd_byte*) raw_arc.from_pc);
|
||||
put_vma(core_bfd, arc->child->addr, (bfd_byte*) raw_arc.self_pc);
|
||||
bfd_put_32(core_bfd, arc->count, (bfd_byte*) raw_arc.count);
|
||||
if (fwrite(&tag, sizeof(tag), 1, ofp) != 1
|
||||
|| fwrite(&raw_arc, sizeof(raw_arc), 1, ofp) != 1)
|
||||
{
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf("[cg_write_arcs] frompc 0x%lx selfpc 0x%lx count %d\n",
|
||||
arc->parent->addr, arc->child->addr, arc->count));
|
||||
} /* for */
|
||||
} /* for */
|
||||
} /* cg_write_arcs */
|
||||
|
||||
/*** end of call_graph.c ***/
|
12
gprof/call_graph.h
Normal file
12
gprof/call_graph.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef call_graph_h
|
||||
#define call_graph_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "gprof.h"
|
||||
#include "symtab.h"
|
||||
|
||||
extern void cg_tally PARAMS((bfd_vma from_pc, bfd_vma self_pc, int count));
|
||||
extern void cg_read_rec PARAMS((FILE *ifp, const char *filename));
|
||||
extern void cg_write_arcs PARAMS((FILE *ofp, const char *filename));
|
||||
|
||||
#endif /* call_graph_h */
|
580
gprof/cg_arcs.c
Normal file
580
gprof/cg_arcs.c
Normal file
|
@ -0,0 +1,580 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
#include "libiberty.h"
|
||||
#include "gprof.h"
|
||||
#include "call_graph.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "cg_dfn.h"
|
||||
#include "cg_print.h"
|
||||
#include "utils.h"
|
||||
#include "sym_ids.h"
|
||||
|
||||
Sym *cycle_header;
|
||||
int num_cycles;
|
||||
|
||||
/*
|
||||
* Return TRUE iff PARENT has an arc to covers the address
|
||||
* range covered by CHILD.
|
||||
*/
|
||||
Arc*
|
||||
DEFUN(arc_lookup, (parent, child), Sym *parent AND Sym *child)
|
||||
{
|
||||
Arc *arc;
|
||||
|
||||
if (!parent || !child) {
|
||||
printf("[arc_lookup] parent == 0 || child == 0\n");
|
||||
return 0;
|
||||
} /* if */
|
||||
DBG(LOOKUPDEBUG, printf("[arc_lookup] parent %s child %s\n",
|
||||
parent->name, child->name));
|
||||
for (arc = parent->cg.children; arc; arc = arc->next_child) {
|
||||
DBG(LOOKUPDEBUG, printf("[arc_lookup]\t parent %s child %s\n",
|
||||
arc->parent->name, arc->child->name));
|
||||
if (child->addr >= arc->child->addr
|
||||
&& child->end_addr <= arc->child->end_addr)
|
||||
{
|
||||
return arc;
|
||||
} /* if */
|
||||
} /* for */
|
||||
return 0;
|
||||
} /* arc_lookup */
|
||||
|
||||
|
||||
/*
|
||||
* Add (or just increment) an arc:
|
||||
*/
|
||||
void
|
||||
DEFUN(arc_add, (parent, child, count),
|
||||
Sym *parent AND Sym *child AND int count)
|
||||
{
|
||||
Arc *arc;
|
||||
|
||||
DBG(TALLYDEBUG, printf("[arc_add] %d arcs from %s to %s\n",
|
||||
count, parent->name, child->name));
|
||||
arc = arc_lookup(parent, child);
|
||||
if (arc) {
|
||||
/*
|
||||
* A hit: just increment the count.
|
||||
*/
|
||||
DBG(TALLYDEBUG, printf("[tally] hit %d += %d\n",
|
||||
arc->count, count));
|
||||
arc->count += count;
|
||||
return;
|
||||
} /* if */
|
||||
arc = (Arc*)xmalloc(sizeof(*arc));
|
||||
arc->parent = parent;
|
||||
arc->child = child;
|
||||
arc->count = count;
|
||||
|
||||
/* prepend this child to the children of this parent: */
|
||||
arc->next_child = parent->cg.children;
|
||||
parent->cg.children = arc;
|
||||
|
||||
/* prepend this parent to the parents of this child: */
|
||||
arc->next_parent = child->cg.parents;
|
||||
child->cg.parents = arc;
|
||||
} /* arc_add */
|
||||
|
||||
|
||||
static int
|
||||
DEFUN(cmp_topo, (lp, rp), const PTR lp AND const PTR rp)
|
||||
{
|
||||
const Sym *left = *(const Sym **) lp;
|
||||
const Sym *right = *(const Sym **) rp;
|
||||
|
||||
return left->cg.top_order - right->cg.top_order;
|
||||
} /* cmp_topo */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN(propagate_time, (parent), Sym *parent)
|
||||
{
|
||||
Arc *arc;
|
||||
Sym *child;
|
||||
double share, prop_share;
|
||||
|
||||
if (parent->cg.prop.fract == 0.0) {
|
||||
return;
|
||||
} /* if */
|
||||
|
||||
/* gather time from children of this parent: */
|
||||
|
||||
for (arc = parent->cg.children; arc; arc = arc->next_child) {
|
||||
child = arc->child;
|
||||
if (arc->count == 0 || child == parent || child->cg.prop.fract == 0) {
|
||||
continue;
|
||||
} /* if */
|
||||
if (child->cg.cyc.head != child) {
|
||||
if (parent->cg.cyc.num == child->cg.cyc.num) {
|
||||
continue;
|
||||
} /* if */
|
||||
if (parent->cg.top_order <= child->cg.top_order) {
|
||||
fprintf(stderr, "[propagate] toporder botches\n");
|
||||
} /* if */
|
||||
child = child->cg.cyc.head;
|
||||
} else {
|
||||
if (parent->cg.top_order <= child->cg.top_order) {
|
||||
fprintf(stderr, "[propagate] toporder botches\n");
|
||||
continue;
|
||||
} /* if */
|
||||
} /* if */
|
||||
if (child->ncalls == 0) {
|
||||
continue;
|
||||
} /* if */
|
||||
|
||||
/* distribute time for this arc: */
|
||||
arc->time = child->hist.time * (((double) arc->count)
|
||||
/ ((double) child->ncalls));
|
||||
arc->child_time = child->cg.child_time
|
||||
* (((double) arc->count) / ((double) child->ncalls));
|
||||
share = arc->time + arc->child_time;
|
||||
parent->cg.child_time += share;
|
||||
|
||||
/* (1 - cg.prop.fract) gets lost along the way: */
|
||||
prop_share = parent->cg.prop.fract * share;
|
||||
|
||||
/* fix things for printing: */
|
||||
parent->cg.prop.child += prop_share;
|
||||
arc->time *= parent->cg.prop.fract;
|
||||
arc->child_time *= parent->cg.prop.fract;
|
||||
|
||||
/* add this share to the parent's cycle header, if any: */
|
||||
if (parent->cg.cyc.head != parent) {
|
||||
parent->cg.cyc.head->cg.child_time += share;
|
||||
parent->cg.cyc.head->cg.prop.child += prop_share;
|
||||
} /* if */
|
||||
DBG(PROPDEBUG,
|
||||
printf("[prop_time] child \t");
|
||||
print_name(child);
|
||||
printf(" with %f %f %d/%d\n", child->hist.time,
|
||||
child->cg.child_time, arc->count, child->ncalls);
|
||||
printf("[prop_time] parent\t");
|
||||
print_name(parent);
|
||||
printf("\n[prop_time] share %f\n", share));
|
||||
} /* for */
|
||||
} /* propagate_time */
|
||||
|
||||
|
||||
/*
|
||||
* Compute the time of a cycle as the sum of the times of all
|
||||
* its members.
|
||||
*/
|
||||
static void
|
||||
DEFUN_VOID(cycle_time)
|
||||
{
|
||||
Sym *member, *cyc;
|
||||
|
||||
for (cyc = &cycle_header[1]; cyc <= &cycle_header[num_cycles]; ++cyc) {
|
||||
for (member = cyc->cg.cyc.next; member; member = member->cg.cyc.next) {
|
||||
if (member->cg.prop.fract == 0.0) {
|
||||
/*
|
||||
* All members have the same propfraction except those
|
||||
* that were excluded with -E.
|
||||
*/
|
||||
continue;
|
||||
} /* if */
|
||||
cyc->hist.time += member->hist.time;
|
||||
} /* for */
|
||||
cyc->cg.prop.self = cyc->cg.prop.fract * cyc->hist.time;
|
||||
} /* for */
|
||||
} /* cycle_time */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN_VOID(cycle_link)
|
||||
{
|
||||
Sym *sym, *cyc, *member;
|
||||
Arc *arc;
|
||||
int num;
|
||||
|
||||
/* count the number of cycles, and initialize the cycle lists: */
|
||||
|
||||
num_cycles = 0;
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
/* this is how you find unattached cycles: */
|
||||
if (sym->cg.cyc.head == sym && sym->cg.cyc.next) {
|
||||
++num_cycles;
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/*
|
||||
* cycle_header is indexed by cycle number: i.e. it is origin 1,
|
||||
* not origin 0.
|
||||
*/
|
||||
cycle_header = (Sym*)xmalloc((num_cycles + 1) * sizeof(Sym));
|
||||
|
||||
/*
|
||||
* Now link cycles to true cycle-heads, number them, accumulate
|
||||
* the data for the cycle.
|
||||
*/
|
||||
num = 0; cyc = cycle_header;
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
if (!(sym->cg.cyc.head == sym && sym->cg.cyc.next != 0)) {
|
||||
continue;
|
||||
} /* if */
|
||||
++num; ++cyc;
|
||||
sym_init(cyc);
|
||||
cyc->cg.print_flag = TRUE; /* should this be printed? */
|
||||
cyc->cg.top_order = DFN_NAN; /* graph call chain top-sort order */
|
||||
cyc->cg.cyc.num = num; /* internal number of cycle on */
|
||||
cyc->cg.cyc.head = cyc; /* pointer to head of cycle */
|
||||
cyc->cg.cyc.next = sym; /* pointer to next member of cycle */
|
||||
DBG(CYCLEDEBUG, printf("[cycle_link] "); print_name(sym);
|
||||
printf(" is the head of cycle %d\n", num));
|
||||
|
||||
/* link members to cycle header: */
|
||||
for (member = sym; member; member = member->cg.cyc.next) {
|
||||
member->cg.cyc.num = num;
|
||||
member->cg.cyc.head = cyc;
|
||||
} /* for */
|
||||
|
||||
/*
|
||||
* Count calls from outside the cycle and those among cycle
|
||||
* members:
|
||||
*/
|
||||
for (member = sym; member; member = member->cg.cyc.next) {
|
||||
for (arc = member->cg.parents; arc; arc = arc->next_parent) {
|
||||
if (arc->parent == member) {
|
||||
continue;
|
||||
} /* if */
|
||||
if (arc->parent->cg.cyc.num == num) {
|
||||
cyc->cg.self_calls += arc->count;
|
||||
} else {
|
||||
cyc->ncalls += arc->count;
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* for */
|
||||
} /* for */
|
||||
} /* cycle_link */
|
||||
|
||||
|
||||
/*
|
||||
* Check if any parent of this child (or outside parents of this
|
||||
* cycle) have their print flags on and set the print flag of the
|
||||
* child (cycle) appropriately. Similarly, deal with propagation
|
||||
* fractions from parents.
|
||||
*/
|
||||
static void
|
||||
DEFUN(inherit_flags, (child), Sym *child)
|
||||
{
|
||||
Sym *head, *parent, *member;
|
||||
Arc *arc;
|
||||
|
||||
head = child->cg.cyc.head;
|
||||
if (child == head) {
|
||||
/* just a regular child, check its parents: */
|
||||
child->cg.print_flag = FALSE;
|
||||
child->cg.prop.fract = 0.0;
|
||||
for (arc = child->cg.parents; arc; arc = arc->next_parent) {
|
||||
parent = arc->parent;
|
||||
if (child == parent) {
|
||||
continue;
|
||||
} /* if */
|
||||
child->cg.print_flag |= parent->cg.print_flag;
|
||||
/*
|
||||
* If the child was never actually called (e.g., this arc
|
||||
* is static (and all others are, too)) no time propagates
|
||||
* along this arc.
|
||||
*/
|
||||
if (child->ncalls) {
|
||||
child->cg.prop.fract += parent->cg.prop.fract
|
||||
* (((double) arc->count) / ((double) child->ncalls));
|
||||
} /* if */
|
||||
} /* for */
|
||||
} else {
|
||||
/*
|
||||
* Its a member of a cycle, look at all parents from outside
|
||||
* the cycle.
|
||||
*/
|
||||
head->cg.print_flag = FALSE;
|
||||
head->cg.prop.fract = 0.0;
|
||||
for (member = head->cg.cyc.next; member; member = member->cg.cyc.next)
|
||||
{
|
||||
for (arc = member->cg.parents; arc; arc = arc->next_parent) {
|
||||
if (arc->parent->cg.cyc.head == head) {
|
||||
continue;
|
||||
} /* if */
|
||||
parent = arc->parent;
|
||||
head->cg.print_flag |= parent->cg.print_flag;
|
||||
/*
|
||||
* If the cycle was never actually called (e.g. this
|
||||
* arc is static (and all others are, too)) no time
|
||||
* propagates along this arc.
|
||||
*/
|
||||
if (head->ncalls) {
|
||||
head->cg.prop.fract += parent->cg.prop.fract
|
||||
* (((double) arc->count) / ((double) head->ncalls));
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* for */
|
||||
for (member = head; member; member = member->cg.cyc.next) {
|
||||
member->cg.print_flag = head->cg.print_flag;
|
||||
member->cg.prop.fract = head->cg.prop.fract;
|
||||
} /* for */
|
||||
} /* if */
|
||||
} /* inherit_flags */
|
||||
|
||||
|
||||
/*
|
||||
* In one top-to-bottom pass over the topologically sorted symbols
|
||||
* propagate:
|
||||
* cg.print_flag as the union of parents' print_flags
|
||||
* propfraction as the sum of fractional parents' propfractions
|
||||
* and while we're here, sum time for functions.
|
||||
*/
|
||||
static void
|
||||
DEFUN(propagate_flags, (symbols), Sym **symbols)
|
||||
{
|
||||
int index;
|
||||
Sym *old_head, *child;
|
||||
|
||||
old_head = 0;
|
||||
for (index = symtab.len - 1; index >= 0; --index) {
|
||||
child = symbols[index];
|
||||
/*
|
||||
* If we haven't done this function or cycle, inherit things
|
||||
* from parent. This way, we are linear in the number of arcs
|
||||
* since we do all members of a cycle (and the cycle itself)
|
||||
* as we hit the first member of the cycle.
|
||||
*/
|
||||
if (child->cg.cyc.head != old_head) {
|
||||
old_head = child->cg.cyc.head;
|
||||
inherit_flags(child);
|
||||
} /* if */
|
||||
DBG(PROPDEBUG,
|
||||
printf("[prop_flags] ");
|
||||
print_name(child);
|
||||
printf("inherits print-flag %d and prop-fract %f\n",
|
||||
child->cg.print_flag, child->cg.prop.fract));
|
||||
if (!child->cg.print_flag) {
|
||||
/*
|
||||
* Printflag is off. It gets turned on by being in the
|
||||
* INCL_GRAPH table, or there being an empty INCL_GRAPH
|
||||
* table and not being in the EXCL_GRAPH table.
|
||||
*/
|
||||
if (sym_lookup(&syms[INCL_GRAPH], child->addr)
|
||||
|| (syms[INCL_GRAPH].len == 0
|
||||
&& !sym_lookup(&syms[EXCL_GRAPH], child->addr)))
|
||||
{
|
||||
child->cg.print_flag = TRUE;
|
||||
} /* if */
|
||||
} else {
|
||||
/*
|
||||
* This function has printing parents: maybe someone wants
|
||||
* to shut it up by putting it in the EXCL_GRAPH table.
|
||||
* (But favor INCL_GRAPH over EXCL_GRAPH.)
|
||||
*/
|
||||
if (!sym_lookup(&syms[INCL_GRAPH], child->addr)
|
||||
&& sym_lookup(&syms[EXCL_GRAPH], child->addr))
|
||||
{
|
||||
child->cg.print_flag = FALSE;
|
||||
} /* if */
|
||||
} /* if */
|
||||
if (child->cg.prop.fract == 0.0) {
|
||||
/*
|
||||
* No parents to pass time to. Collect time from children
|
||||
* if its in the INCL_TIME table, or there is an empty
|
||||
* INCL_TIME table and its not in the EXCL_TIME table.
|
||||
*/
|
||||
if (sym_lookup(&syms[INCL_TIME], child->addr)
|
||||
|| (syms[INCL_TIME].len == 0
|
||||
&& !sym_lookup(&syms[EXCL_TIME], child->addr)))
|
||||
{
|
||||
child->cg.prop.fract = 1.0;
|
||||
} /* if */
|
||||
} else {
|
||||
/*
|
||||
* It has parents to pass time to, but maybe someone wants
|
||||
* to shut it up by puttting it in the EXCL_TIME table.
|
||||
* (But favor being in INCL_TIME tabe over being in
|
||||
* EXCL_TIME table.)
|
||||
*/
|
||||
if (!sym_lookup(&syms[INCL_TIME], child->addr)
|
||||
&& sym_lookup(&syms[EXCL_TIME], child->addr))
|
||||
{
|
||||
child->cg.prop.fract = 0.0;
|
||||
} /* if */
|
||||
} /* if */
|
||||
child->cg.prop.self = child->hist.time * child->cg.prop.fract;
|
||||
print_time += child->cg.prop.self;
|
||||
DBG(PROPDEBUG,
|
||||
printf("[prop_flags] ");
|
||||
print_name(child);
|
||||
printf(" ends up with printflag %d and prop-fract %f\n",
|
||||
child->cg.print_flag, child->cg.prop.fract);
|
||||
printf("[prop_flags] time %f propself %f print_time %f\n",
|
||||
child->hist.time, child->cg.prop.self, print_time));
|
||||
} /* if */
|
||||
} /* propagate_flags */
|
||||
|
||||
|
||||
/*
|
||||
* Compare by decreasing propagated time. If times are equal, but one
|
||||
* is a cycle header, say that's first (e.g. less, i.e. -1). If one's
|
||||
* name doesn't have an underscore and the other does, say that one is
|
||||
* first. All else being equal, compare by names.
|
||||
*/
|
||||
static int
|
||||
DEFUN(cmp_total, (lp, rp), const PTR lp AND const PTR rp)
|
||||
{
|
||||
const Sym *left = *(const Sym**)lp;
|
||||
const Sym *right = *(const Sym**)rp;
|
||||
double diff;
|
||||
|
||||
diff = (left->cg.prop.self + left->cg.prop.child)
|
||||
- (right->cg.prop.self + right->cg.prop.child);
|
||||
if (diff < 0.0) {
|
||||
return 1;
|
||||
} /* if */
|
||||
if (diff > 0.0) {
|
||||
return -1;
|
||||
} /* if */
|
||||
if (!left->name && left->cg.cyc.num != 0) {
|
||||
return -1;
|
||||
} /* if */
|
||||
if (!right->name && right->cg.cyc.num != 0) {
|
||||
return 1;
|
||||
} /* if */
|
||||
if (!left->name) {
|
||||
return -1;
|
||||
} /* if */
|
||||
if (!right->name) {
|
||||
return 1;
|
||||
} /* if */
|
||||
if (left->name[0] != '_' && right->name[0] == '_') {
|
||||
return -1;
|
||||
} /* if */
|
||||
if (left->name[0] == '_' && right->name[0] != '_') {
|
||||
return 1;
|
||||
} /* if */
|
||||
if (left->ncalls > right->ncalls) {
|
||||
return -1;
|
||||
} /* if */
|
||||
if (left->ncalls < right->ncalls) {
|
||||
return 1;
|
||||
} /* if */
|
||||
return strcmp(left->name, right->name);
|
||||
} /* cmp_total */
|
||||
|
||||
|
||||
/*
|
||||
* Topologically sort the graph (collapsing cycles), and propagates
|
||||
* time bottom up and flags top down.
|
||||
*/
|
||||
Sym**
|
||||
DEFUN_VOID(cg_assemble)
|
||||
{
|
||||
Sym *parent, **time_sorted_syms, **top_sorted_syms;
|
||||
long index;
|
||||
Arc *arc;
|
||||
extern void find_call PARAMS((Sym *parent,
|
||||
bfd_vma p_lowpc, bfd_vma p_highpc));
|
||||
/*
|
||||
* initialize various things:
|
||||
* zero out child times.
|
||||
* count self-recursive calls.
|
||||
* indicate that nothing is on cycles.
|
||||
*/
|
||||
for (parent = symtab.base; parent < symtab.limit; parent++) {
|
||||
parent->cg.child_time = 0.0;
|
||||
arc = arc_lookup(parent, parent);
|
||||
if (arc && parent == arc->child) {
|
||||
parent->ncalls -= arc->count;
|
||||
parent->cg.self_calls = arc->count;
|
||||
} else {
|
||||
parent->cg.self_calls = 0;
|
||||
} /* if */
|
||||
parent->cg.prop.fract = 0.0;
|
||||
parent->cg.prop.self = 0.0;
|
||||
parent->cg.prop.child = 0.0;
|
||||
parent->cg.print_flag = FALSE;
|
||||
parent->cg.top_order = DFN_NAN;
|
||||
parent->cg.cyc.num = 0;
|
||||
parent->cg.cyc.head = parent;
|
||||
parent->cg.cyc.next = 0;
|
||||
if (ignore_direct_calls) {
|
||||
find_call(parent, parent->addr, (parent+1)->addr);
|
||||
} /* if */
|
||||
} /* for */
|
||||
/*
|
||||
* Topologically order things. If any node is unnumbered, number
|
||||
* it and any of its descendents.
|
||||
*/
|
||||
for (parent = symtab.base; parent < symtab.limit; parent++) {
|
||||
if (parent->cg.top_order == DFN_NAN) {
|
||||
cg_dfn(parent);
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/* link together nodes on the same cycle: */
|
||||
cycle_link();
|
||||
|
||||
/* sort the symbol table in reverse topological order: */
|
||||
top_sorted_syms = (Sym**)xmalloc(symtab.len * sizeof(Sym*));
|
||||
for (index = 0; index < symtab.len; ++index) {
|
||||
top_sorted_syms[index] = &symtab.base[index];
|
||||
} /* for */
|
||||
qsort(top_sorted_syms, symtab.len, sizeof(Sym *), cmp_topo);
|
||||
DBG(DFNDEBUG,
|
||||
printf("[cg_assemble] topological sort listing\n");
|
||||
for (index = 0; index < symtab.len; ++index) {
|
||||
printf("[cg_assemble] ");
|
||||
printf("%d:", top_sorted_syms[index]->cg.top_order);
|
||||
print_name(top_sorted_syms[index]);
|
||||
printf("\n");
|
||||
} /* for */);
|
||||
/*
|
||||
* Starting from the topological top, propagate print flags to
|
||||
* children. also, calculate propagation fractions. this happens
|
||||
* before time propagation since time propagation uses the
|
||||
* fractions.
|
||||
*/
|
||||
propagate_flags(top_sorted_syms);
|
||||
|
||||
/*
|
||||
* Starting from the topological bottom, propogate children times
|
||||
* up to parents.
|
||||
*/
|
||||
cycle_time();
|
||||
for (index = 0; index < symtab.len; ++index) {
|
||||
propagate_time(top_sorted_syms[index]);
|
||||
} /* for */
|
||||
|
||||
free(top_sorted_syms);
|
||||
|
||||
/*
|
||||
* Now, sort by CG.PROP.SELF + CG.PROP.CHILD. Sorting both the regular
|
||||
* function names and cycle headers.
|
||||
*/
|
||||
time_sorted_syms = (Sym**)xmalloc((symtab.len + num_cycles)*sizeof(Sym*));
|
||||
for (index = 0; index < symtab.len; index++) {
|
||||
time_sorted_syms[index] = &symtab.base[index];
|
||||
} /* if */
|
||||
for (index = 1; index <= num_cycles; index++) {
|
||||
time_sorted_syms[symtab.len + index - 1] = &cycle_header[index];
|
||||
} /* for */
|
||||
qsort(time_sorted_syms, symtab.len + num_cycles, sizeof(Sym*),
|
||||
cmp_total);
|
||||
for (index = 0; index < symtab.len + num_cycles; index++) {
|
||||
time_sorted_syms[index]->cg.index = index + 1;
|
||||
} /* for */
|
||||
return time_sorted_syms;
|
||||
} /* cg_assemble */
|
||||
|
||||
/*** end of cg_arcs.c ***/
|
31
gprof/cg_arcs.h
Normal file
31
gprof/cg_arcs.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef cg_arcs_h
|
||||
#define cg_arcs_h
|
||||
|
||||
#include "gprof.h"
|
||||
#include "symtab.h"
|
||||
|
||||
/*
|
||||
* Arc structure for call-graph.
|
||||
*
|
||||
* With pointers to the symbols of the parent and the child, a count
|
||||
* of how many times this arc was traversed, and pointers to the next
|
||||
* parent of this child and the next child of this parent.
|
||||
*/
|
||||
typedef struct arc {
|
||||
Sym *parent; /* source vertice of arc */
|
||||
Sym *child; /* dest vertice of arc */
|
||||
int count; /* # of calls from parent to child */
|
||||
double time; /* time inherited along arc */
|
||||
double child_time; /* child-time inherited along arc */
|
||||
struct arc *next_parent; /* next parent of CHILD */
|
||||
struct arc *next_child; /* next child of PARENT */
|
||||
} Arc;
|
||||
|
||||
extern int num_cycles; /* number of cycles discovered */
|
||||
extern Sym *cycle_header; /* cycle headers */
|
||||
|
||||
extern void arc_add PARAMS((Sym *parent, Sym *child, int count));
|
||||
extern Arc *arc_lookup PARAMS((Sym *parent, Sym *child));
|
||||
extern Sym **cg_assemble PARAMS((void));
|
||||
|
||||
#endif /* cg_arcs_h */
|
238
gprof/cg_dfn.c
Normal file
238
gprof/cg_dfn.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "gprof.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "cg_dfn.h"
|
||||
#include "symtab.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define DFN_DEPTH 100
|
||||
|
||||
typedef struct {
|
||||
Sym *sym;
|
||||
int cycle_top;
|
||||
} DFN_Stack;
|
||||
|
||||
DFN_Stack dfn_stack[DFN_DEPTH];
|
||||
int dfn_depth = 0;
|
||||
int dfn_counter = DFN_NAN;
|
||||
|
||||
|
||||
/*
|
||||
* Is CHILD already numbered?
|
||||
*/
|
||||
static bool
|
||||
DEFUN(is_numbered, (child), Sym *child)
|
||||
{
|
||||
return child->cg.top_order != DFN_NAN && child->cg.top_order != DFN_BUSY;
|
||||
} /* is_numbered */
|
||||
|
||||
|
||||
/*
|
||||
* Is CHILD already busy?
|
||||
*/
|
||||
static bool
|
||||
DEFUN(is_busy, (child), Sym *child)
|
||||
{
|
||||
if (child->cg.top_order == DFN_NAN) {
|
||||
return FALSE;
|
||||
} /* if */
|
||||
return TRUE;
|
||||
} /* is_busy */
|
||||
|
||||
|
||||
/*
|
||||
* CHILD is part of a cycle. Find the top caller into this cycle
|
||||
* that is not part of the cycle and make all functions in cycle
|
||||
* members of that cycle (top caller == caller with smallest
|
||||
* depth-first number).
|
||||
*/
|
||||
static void
|
||||
DEFUN(find_cycle, (child), Sym *child)
|
||||
{
|
||||
Sym *head = 0;
|
||||
Sym *tail;
|
||||
int cycle_top;
|
||||
int index;
|
||||
|
||||
for (cycle_top = dfn_depth; cycle_top > 0; --cycle_top) {
|
||||
head = dfn_stack[cycle_top].sym;
|
||||
if (child == head) {
|
||||
break;
|
||||
} /* if */
|
||||
if (child->cg.cyc.head != child && child->cg.cyc.head == head) {
|
||||
break;
|
||||
} /* if */
|
||||
} /* for */
|
||||
if (cycle_top <= 0) {
|
||||
fprintf(stderr, "[find_cycle] couldn't find head of cycle\n");
|
||||
done(1);
|
||||
} /* if */
|
||||
DBG(DFNDEBUG, printf("[find_cycle] dfn_depth %d cycle_top %d ",
|
||||
dfn_depth, cycle_top);
|
||||
if (head) {
|
||||
print_name(head);
|
||||
} else {
|
||||
printf("<unknown>");
|
||||
} /* if */
|
||||
printf("\n"));
|
||||
if (cycle_top == dfn_depth) {
|
||||
/*
|
||||
* This is previous function, e.g. this calls itself. Sort of
|
||||
* boring.
|
||||
*
|
||||
* Since we are taking out self-cycles elsewhere no need for
|
||||
* the special case, here.
|
||||
*/
|
||||
DBG(DFNDEBUG,
|
||||
printf("[find_cycle] "); print_name(child); printf("\n"));
|
||||
} else {
|
||||
/*
|
||||
* Glom intervening functions that aren't already glommed into
|
||||
* this cycle. Things have been glommed when their cyclehead
|
||||
* field points to the head of the cycle they are glommed
|
||||
* into.
|
||||
*/
|
||||
for (tail = head; tail->cg.cyc.next; tail = tail->cg.cyc.next) {
|
||||
/* void: chase down to tail of things already glommed */
|
||||
DBG(DFNDEBUG,
|
||||
printf("[find_cycle] tail "); print_name(tail); printf("\n"));
|
||||
} /* for */
|
||||
/*
|
||||
* If what we think is the top of the cycle has a cyclehead
|
||||
* field, then it's not really the head of the cycle, which is
|
||||
* really what we want.
|
||||
*/
|
||||
if (head->cg.cyc.head != head) {
|
||||
head = head->cg.cyc.head;
|
||||
DBG(DFNDEBUG, printf("[find_cycle] new cyclehead ");
|
||||
print_name(head); printf("\n"));
|
||||
} /* if */
|
||||
for (index = cycle_top + 1; index <= dfn_depth; ++index) {
|
||||
child = dfn_stack[index].sym;
|
||||
if (child->cg.cyc.head == child) {
|
||||
/*
|
||||
* Not yet glommed anywhere, glom it and fix any
|
||||
* children it has glommed.
|
||||
*/
|
||||
tail->cg.cyc.next = child;
|
||||
child->cg.cyc.head = head;
|
||||
DBG(DFNDEBUG, printf("[find_cycle] glomming ");
|
||||
print_name(child); printf(" onto "); print_name(head);
|
||||
printf("\n"));
|
||||
for (tail = child; tail->cg.cyc.next; tail = tail->cg.cyc.next)
|
||||
{
|
||||
tail->cg.cyc.next->cg.cyc.head = head;
|
||||
DBG(DFNDEBUG, printf("[find_cycle] and its tail ");
|
||||
print_name(tail->cg.cyc.next); printf(" onto ");
|
||||
print_name(head); printf("\n"));
|
||||
} /* for */
|
||||
} else if (child->cg.cyc.head != head /* firewall */) {
|
||||
fprintf(stderr, "[find_cycle] glommed, but not to head\n");
|
||||
done(1);
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* if */
|
||||
} /* find_cycle */
|
||||
|
||||
|
||||
/*
|
||||
* Prepare for visiting the children of PARENT. Push a parent onto
|
||||
* the stack and mark it busy.
|
||||
*/
|
||||
static void
|
||||
DEFUN(pre_visit, (parent), Sym *parent)
|
||||
{
|
||||
++dfn_depth;
|
||||
if (dfn_depth >= DFN_DEPTH) {
|
||||
fprintf(stderr, "[pre_visit] dfn_stack overflow\n");
|
||||
done(1);
|
||||
} /* if */
|
||||
dfn_stack[dfn_depth].sym = parent;
|
||||
dfn_stack[dfn_depth].cycle_top = dfn_depth;
|
||||
parent->cg.top_order = DFN_BUSY;
|
||||
DBG(DFNDEBUG, printf("[pre_visit]\t\t%d:", dfn_depth); print_name(parent);
|
||||
printf("\n"));
|
||||
} /* pre_visit */
|
||||
|
||||
|
||||
/*
|
||||
* Done with visiting node PARENT. Pop PARENT off dfn_stack
|
||||
* and number functions if PARENT is head of a cycle.
|
||||
*/
|
||||
static void
|
||||
DEFUN(post_visit, (parent), Sym *parent)
|
||||
{
|
||||
Sym *member;
|
||||
|
||||
DBG(DFNDEBUG, printf("[post_visit]\t%d: ", dfn_depth);
|
||||
print_name(parent); printf("\n"));
|
||||
/*
|
||||
* Number functions and things in their cycles unless the function
|
||||
* is itself part of a cycle:
|
||||
*/
|
||||
if (parent->cg.cyc.head == parent) {
|
||||
++dfn_counter;
|
||||
for (member = parent; member; member = member->cg.cyc.next) {
|
||||
member->cg.top_order = dfn_counter;
|
||||
DBG(DFNDEBUG, printf("[post_visit]\t\tmember ");
|
||||
print_name(member);
|
||||
printf("-> cg.top_order = %d\n", dfn_counter));
|
||||
} /* for */
|
||||
} else {
|
||||
DBG(DFNDEBUG, printf("[post_visit]\t\tis part of a cycle\n"));
|
||||
} /* if */
|
||||
--dfn_depth;
|
||||
} /* post_visit */
|
||||
|
||||
|
||||
/*
|
||||
* Given this PARENT, depth first number its children.
|
||||
*/
|
||||
void
|
||||
DEFUN(cg_dfn, (parent), Sym *parent)
|
||||
{
|
||||
Arc *arc;
|
||||
|
||||
DBG(DFNDEBUG, printf("[dfn] dfn( "); print_name(parent); printf(")\n"));
|
||||
/*
|
||||
* If we're already numbered, no need to look any further:
|
||||
*/
|
||||
if (is_numbered(parent)) {
|
||||
return;
|
||||
} /* if */
|
||||
/*
|
||||
* If we're already busy, must be a cycle:
|
||||
*/
|
||||
if (is_busy(parent)) {
|
||||
find_cycle(parent);
|
||||
return;
|
||||
} /* if */
|
||||
pre_visit(parent);
|
||||
/*
|
||||
* Recursively visit children:
|
||||
*/
|
||||
for (arc = parent->cg.children; arc; arc = arc->next_child) {
|
||||
cg_dfn(arc->child);
|
||||
} /* for */
|
||||
post_visit(parent);
|
||||
} /* cg_dfn */
|
||||
|
||||
/*** end of cg_dfn.c ***/
|
17
gprof/cg_dfn.h
Normal file
17
gprof/cg_dfn.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef cg_dfn_h
|
||||
#define cg_dfn_h
|
||||
|
||||
/*
|
||||
* Flags which mark a symbol as topologically ``busy'' or as
|
||||
* topologically ``not_numbered'':
|
||||
*/
|
||||
#define DFN_BUSY -1
|
||||
#define DFN_NAN 0
|
||||
|
||||
/*
|
||||
* Depth-first numbering of a call-graph.
|
||||
*/
|
||||
|
||||
extern void cg_dfn PARAMS((Sym *root));
|
||||
|
||||
#endif /* cg_dfn_h */
|
553
gprof/cg_print.c
Normal file
553
gprof/cg_print.c
Normal file
|
@ -0,0 +1,553 @@
|
|||
#include "libiberty.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "cg_print.h"
|
||||
#include "hist.h"
|
||||
#include "utils.h"
|
||||
|
||||
/*
|
||||
* Return value of comparison functions used to sort tables:
|
||||
*/
|
||||
#define LESSTHAN -1
|
||||
#define EQUALTO 0
|
||||
#define GREATERTHAN 1
|
||||
|
||||
/* declarations of automatically generated functions to output blurbs: */
|
||||
extern void bsd_callg_blurb PARAMS((FILE *fp));
|
||||
extern void fsf_callg_blurb PARAMS((FILE *fp));
|
||||
|
||||
double print_time = 0.0;
|
||||
|
||||
|
||||
static void
|
||||
DEFUN_VOID(print_header)
|
||||
{
|
||||
if (first_output) {
|
||||
first_output = FALSE;
|
||||
} else {
|
||||
printf("\f\n");
|
||||
} /* if */
|
||||
if (!bsd_style_output) {
|
||||
if (print_descriptions) {
|
||||
printf("\t\t Call graph (explanation follows)\n\n");
|
||||
} else {
|
||||
printf("\t\t\tCall graph\n\n");
|
||||
} /* if */
|
||||
} /* if */
|
||||
printf("\ngranularity: each sample hit covers %ld byte(s)",
|
||||
(long) hist_scale * sizeof(UNIT));
|
||||
if (print_time > 0.0) {
|
||||
printf(" for %.2f%% of %.2f seconds\n\n",
|
||||
100.0/print_time, print_time / hz);
|
||||
} else {
|
||||
printf(" no time propagated\n\n");
|
||||
/*
|
||||
* This doesn't hurt, since all the numerators will be 0.0:
|
||||
*/
|
||||
print_time = 1.0;
|
||||
} /* if */
|
||||
if (bsd_style_output) {
|
||||
printf("%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n",
|
||||
"", "", "", "", "called", "total", "parents");
|
||||
printf("%-6.6s %5.5s %7.7s %11.11s %7.7s+%-7.7s %-8.8s\t%5.5s\n",
|
||||
"index", "%time", "self", "descendents",
|
||||
"called", "self", "name", "index");
|
||||
printf("%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n",
|
||||
"", "", "", "", "called", "total", "children");
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("index %% time self children called name\n");
|
||||
} /* if */
|
||||
} /* print_header */
|
||||
|
||||
|
||||
/*
|
||||
* Print a cycle header.
|
||||
*/
|
||||
static void
|
||||
DEFUN(print_cycle, (cyc), Sym *cyc)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
|
||||
sprintf(buf, "[%d]", cyc->cg.index);
|
||||
printf("%-6.6s %5.1f %7.2f %11.2f %7d", buf,
|
||||
100 * (cyc->cg.prop.self + cyc->cg.prop.child) / print_time,
|
||||
cyc->cg.prop.self / hz, cyc->cg.prop.child / hz, cyc->ncalls);
|
||||
if (cyc->cg.self_calls != 0) {
|
||||
printf("+%-7d", cyc->cg.self_calls);
|
||||
} else {
|
||||
printf(" %7.7s", "");
|
||||
} /* if */
|
||||
printf(" <cycle %d as a whole>\t[%d]\n", cyc->cg.cyc.num, cyc->cg.index);
|
||||
} /* print_cycle */
|
||||
|
||||
|
||||
/*
|
||||
* Compare LEFT and RIGHT membmer. Major comparison key is
|
||||
* CG.PROP.SELF+CG.PROP.CHILD, secondary key is NCALLS+CG.SELF_CALLS.
|
||||
*/
|
||||
static int
|
||||
DEFUN(cmp_member, (left, right), Sym *left AND Sym *right)
|
||||
{
|
||||
double left_time = left->cg.prop.self + left->cg.prop.child;
|
||||
double right_time = right->cg.prop.self + right->cg.prop.child;
|
||||
long left_calls = left->ncalls + left->cg.self_calls;
|
||||
long right_calls = right->ncalls + right->cg.self_calls;
|
||||
|
||||
if (left_time > right_time) {
|
||||
return GREATERTHAN;
|
||||
} /* if */
|
||||
if (left_time < right_time) {
|
||||
return LESSTHAN;
|
||||
} /* if */
|
||||
|
||||
if (left_calls > right_calls) {
|
||||
return GREATERTHAN;
|
||||
} /* if */
|
||||
if (left_calls < right_calls) {
|
||||
return LESSTHAN;
|
||||
} /* if */
|
||||
return EQUALTO;
|
||||
} /* cmp_member */
|
||||
|
||||
|
||||
/*
|
||||
* Sort members of a cycle.
|
||||
*/
|
||||
static void
|
||||
DEFUN(sort_members, (cyc), Sym *cyc)
|
||||
{
|
||||
Sym *todo, *doing, *prev;
|
||||
/*
|
||||
* Detach cycle members from cyclehead, and insertion sort them
|
||||
* back on.
|
||||
*/
|
||||
todo = cyc->cg.cyc.next;
|
||||
cyc->cg.cyc.next = 0;
|
||||
for (doing = todo; doing && doing->cg.cyc.next; doing = todo) {
|
||||
todo = doing->cg.cyc.next;
|
||||
for (prev = cyc; prev->cg.cyc.next; prev = prev->cg.cyc.next) {
|
||||
if (cmp_member(doing, prev->cg.cyc.next) == GREATERTHAN) {
|
||||
break;
|
||||
} /* if */
|
||||
} /* for */
|
||||
doing->cg.cyc.next = prev->cg.cyc.next;
|
||||
prev->cg.cyc.next = doing;
|
||||
} /* for */
|
||||
} /* sort_members */
|
||||
|
||||
|
||||
/*
|
||||
* Print the members of a cycle.
|
||||
*/
|
||||
static void
|
||||
DEFUN(print_members, (cyc), Sym *cyc)
|
||||
{
|
||||
Sym *member;
|
||||
|
||||
sort_members(cyc);
|
||||
for (member = cyc->cg.cyc.next; member; member = member->cg.cyc.next) {
|
||||
printf("%6.6s %5.5s %7.2f %11.2f %7d",
|
||||
"", "", member->cg.prop.self / hz, member->cg.prop.child / hz,
|
||||
member->ncalls);
|
||||
if (member->cg.self_calls != 0) {
|
||||
printf("+%-7d", member->cg.self_calls);
|
||||
} else {
|
||||
printf(" %7.7s", "");
|
||||
} /* if */
|
||||
printf(" ");
|
||||
print_name(member);
|
||||
printf("\n");
|
||||
} /* for */
|
||||
} /* print_members */
|
||||
|
||||
|
||||
/*
|
||||
* Compare two arcs to/from the same child/parent.
|
||||
* - if one arc is a self arc, it's least.
|
||||
* - if one arc is within a cycle, it's less than.
|
||||
* - if both arcs are within a cycle, compare arc counts.
|
||||
* - if neither arc is within a cycle, compare with
|
||||
* time + child_time as major key
|
||||
* arc count as minor key
|
||||
*/
|
||||
static int
|
||||
DEFUN(cmp_arc, (left, right), Arc *left AND Arc *right)
|
||||
{
|
||||
Sym *left_parent = left->parent;
|
||||
Sym *left_child = left->child;
|
||||
Sym *right_parent = right->parent;
|
||||
Sym *right_child = right->child;
|
||||
double left_time, right_time;
|
||||
|
||||
DBG(TIMEDEBUG,
|
||||
printf("[cmp_arc] ");
|
||||
print_name(left_parent);
|
||||
printf(" calls ");
|
||||
print_name(left_child);
|
||||
printf(" %f + %f %d/%d\n", left->time, left->child_time,
|
||||
left->count, left_child->ncalls);
|
||||
printf("[cmp_arc] ");
|
||||
print_name(right_parent);
|
||||
printf(" calls ");
|
||||
print_name(right_child);
|
||||
printf(" %f + %f %d/%d\n", right->time, right->child_time,
|
||||
right->count, right_child->ncalls);
|
||||
printf("\n");
|
||||
);
|
||||
if (left_parent == left_child) {
|
||||
return LESSTHAN; /* left is a self call */
|
||||
} /* if */
|
||||
if (right_parent == right_child) {
|
||||
return GREATERTHAN; /* right is a self call */
|
||||
} /* if */
|
||||
|
||||
if (left_parent->cg.cyc.num != 0 && left_child->cg.cyc.num != 0
|
||||
&& left_parent->cg.cyc.num == left_child->cg.cyc.num)
|
||||
{
|
||||
/* left is a call within a cycle */
|
||||
if (right_parent->cg.cyc.num != 0 && right_child->cg.cyc.num != 0
|
||||
&& right_parent->cg.cyc.num == right_child->cg.cyc.num)
|
||||
{
|
||||
/* right is a call within the cycle, too */
|
||||
if (left->count < right->count) {
|
||||
return LESSTHAN;
|
||||
} /* if */
|
||||
if (left->count > right->count) {
|
||||
return GREATERTHAN;
|
||||
} /* if */
|
||||
return EQUALTO;
|
||||
} else {
|
||||
/* right isn't a call within the cycle */
|
||||
return LESSTHAN;
|
||||
} /* if */
|
||||
} else {
|
||||
/* left isn't a call within a cycle */
|
||||
if (right_parent->cg.cyc.num != 0 && right_child->cg.cyc.num != 0
|
||||
&& right_parent->cg.cyc.num == right_child->cg.cyc.num)
|
||||
{
|
||||
/* right is a call within a cycle */
|
||||
return GREATERTHAN;
|
||||
} else {
|
||||
/* neither is a call within a cycle */
|
||||
left_time = left->time + left->child_time;
|
||||
right_time = right->time + right->child_time;
|
||||
if (left_time < right_time) {
|
||||
return LESSTHAN;
|
||||
} /* if */
|
||||
if (left_time > right_time) {
|
||||
return GREATERTHAN;
|
||||
} /* if */
|
||||
if (left->count < right->count) {
|
||||
return LESSTHAN;
|
||||
} /* if */
|
||||
if (left->count > right->count) {
|
||||
return GREATERTHAN;
|
||||
} /* if */
|
||||
return EQUALTO;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* cmp_arc */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN(sort_parents, (child), Sym *child)
|
||||
{
|
||||
Arc *arc, *detached, sorted, *prev;
|
||||
|
||||
/*
|
||||
* Unlink parents from child, then insertion sort back on to
|
||||
* sorted's parents.
|
||||
* *arc the arc you have detached and are inserting.
|
||||
* *detached the rest of the arcs to be sorted.
|
||||
* sorted arc list onto which you insertion sort.
|
||||
* *prev arc before the arc you are comparing.
|
||||
*/
|
||||
sorted.next_parent = 0;
|
||||
for (arc = child->cg.parents; arc; arc = detached) {
|
||||
detached = arc->next_parent;
|
||||
|
||||
/* consider *arc as disconnected; insert it into sorted: */
|
||||
for (prev = &sorted; prev->next_parent; prev = prev->next_parent) {
|
||||
if (cmp_arc(arc, prev->next_parent) != GREATERTHAN) {
|
||||
break;
|
||||
} /* if */
|
||||
} /* for */
|
||||
arc->next_parent = prev->next_parent;
|
||||
prev->next_parent = arc;
|
||||
} /* for */
|
||||
|
||||
/* reattach sorted arcs to child: */
|
||||
child->cg.parents = sorted.next_parent;
|
||||
} /* sort_parents */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN(print_parents, (child), Sym *child)
|
||||
{
|
||||
Sym *parent;
|
||||
Arc *arc;
|
||||
Sym *cycle_head;
|
||||
|
||||
if (child->cg.cyc.head != 0) {
|
||||
cycle_head = child->cg.cyc.head;
|
||||
} else {
|
||||
cycle_head = child;
|
||||
} /* if */
|
||||
if (!child->cg.parents) {
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.7s %11.11s %7.7s %7.7s <spontaneous>\n"
|
||||
: "%6.6s %5.5s %7.7s %7.7s %7.7s %7.7s <spontaneous>\n",
|
||||
"", "", "", "", "", "");
|
||||
return;
|
||||
} /* if */
|
||||
sort_parents(child);
|
||||
for (arc = child->cg.parents; arc; arc = arc->next_parent) {
|
||||
parent = arc->parent;
|
||||
if (child == parent || (child->cg.cyc.num != 0
|
||||
&& parent->cg.cyc.num == child->cg.cyc.num))
|
||||
{
|
||||
/* selfcall or call among siblings: */
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.7s %11.11s %7d %7.7s "
|
||||
: "%6.6s %5.5s %7.7s %7.7s %7d %7.7s ",
|
||||
"", "", "", "",
|
||||
arc->count, "");
|
||||
print_name(parent);
|
||||
printf("\n");
|
||||
} else {
|
||||
/* regular parent of child: */
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.2f %11.2f %7d/%-7d "
|
||||
: "%6.6s %5.5s %7.2f %7.2f %7d/%-7d ",
|
||||
"", "",
|
||||
arc->time / hz, arc->child_time / hz,
|
||||
arc->count, cycle_head->ncalls);
|
||||
print_name(parent);
|
||||
printf("\n");
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* print_parents */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN(sort_children, (parent), Sym *parent)
|
||||
{
|
||||
Arc *arc, *detached, sorted, *prev;
|
||||
/*
|
||||
* Unlink children from parent, then insertion sort back on to
|
||||
* sorted's children.
|
||||
* *arc the arc you have detached and are inserting.
|
||||
* *detached the rest of the arcs to be sorted.
|
||||
* sorted arc list onto which you insertion sort.
|
||||
* *prev arc before the arc you are comparing.
|
||||
*/
|
||||
sorted.next_child = 0;
|
||||
for (arc = parent->cg.children; arc; arc = detached) {
|
||||
detached = arc->next_child;
|
||||
|
||||
/* consider *arc as disconnected; insert it into sorted: */
|
||||
for (prev = &sorted; prev->next_child; prev = prev->next_child) {
|
||||
if (cmp_arc(arc, prev->next_child) != LESSTHAN) {
|
||||
break;
|
||||
} /* if */
|
||||
} /* for */
|
||||
arc->next_child = prev->next_child;
|
||||
prev->next_child = arc;
|
||||
} /* for */
|
||||
|
||||
/* reattach sorted children to parent: */
|
||||
parent->cg.children = sorted.next_child;
|
||||
} /* sort_children */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN(print_children, (parent), Sym *parent)
|
||||
{
|
||||
Sym *child;
|
||||
Arc *arc;
|
||||
|
||||
sort_children(parent);
|
||||
arc = parent->cg.children;
|
||||
for (arc = parent->cg.children; arc; arc = arc->next_child) {
|
||||
child = arc->child;
|
||||
if (child == parent || (child->cg.cyc.num != 0
|
||||
&& child->cg.cyc.num == parent->cg.cyc.num))
|
||||
{
|
||||
/* self call or call to sibling: */
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.7s %11.11s %7d %7.7s "
|
||||
: "%6.6s %5.5s %7.7s %7.7s %7d %7.7s ",
|
||||
"", "", "", "", arc->count, "");
|
||||
print_name(child);
|
||||
printf("\n");
|
||||
} else {
|
||||
/* regular child of parent: */
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.2f %11.2f %7d/%-7d "
|
||||
: "%6.6s %5.5s %7.2f %7.2f %7d/%-7d ",
|
||||
"", "",
|
||||
arc->time / hz, arc->child_time / hz,
|
||||
arc->count, child->cg.cyc.head->ncalls);
|
||||
print_name(child);
|
||||
printf("\n");
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* print_children */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN(print_line, (np), Sym *np)
|
||||
{
|
||||
char buf[BUFSIZ];
|
||||
|
||||
sprintf(buf, "[%d]", np->cg.index);
|
||||
printf(bsd_style_output
|
||||
? "%-6.6s %5.1f %7.2f %11.2f"
|
||||
: "%-6.6s %5.1f %7.2f %7.2f", buf,
|
||||
100 * (np->cg.prop.self + np->cg.prop.child) / print_time,
|
||||
np->cg.prop.self / hz, np->cg.prop.child / hz);
|
||||
if ((np->ncalls + np->cg.self_calls) != 0) {
|
||||
printf(" %7d", np->ncalls);
|
||||
if (np->cg.self_calls != 0) {
|
||||
printf("+%-7d ", np->cg.self_calls);
|
||||
} else {
|
||||
printf(" %7.7s ", "");
|
||||
} /* if */
|
||||
} else {
|
||||
printf(" %7.7s %7.7s ", "", "");
|
||||
} /* if */
|
||||
print_name(np);
|
||||
printf("\n");
|
||||
} /* print_line */
|
||||
|
||||
|
||||
/*
|
||||
* Print dynamic call graph.
|
||||
*/
|
||||
void
|
||||
DEFUN(cg_print, (timesortsym), Sym **timesortsym)
|
||||
{
|
||||
int index;
|
||||
Sym *parent;
|
||||
|
||||
if (print_descriptions && bsd_style_output) {
|
||||
bsd_callg_blurb(stdout);
|
||||
} /* if */
|
||||
|
||||
print_header();
|
||||
|
||||
for (index = 0; index < symtab.len + num_cycles; ++index) {
|
||||
parent = timesortsym[index];
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
} /* if */
|
||||
if (!parent->name && parent->cg.cyc.num != 0) {
|
||||
/* cycle header: */
|
||||
print_cycle(parent);
|
||||
print_members(parent);
|
||||
} else {
|
||||
print_parents(parent);
|
||||
print_line(parent);
|
||||
print_children(parent);
|
||||
} /* if */
|
||||
if (bsd_style_output)
|
||||
printf("\n");
|
||||
printf("-----------------------------------------------\n");
|
||||
if (bsd_style_output)
|
||||
printf("\n");
|
||||
}
|
||||
free(timesortsym);
|
||||
if (print_descriptions && !bsd_style_output) {
|
||||
fsf_callg_blurb(stdout);
|
||||
}
|
||||
} /* cg_print */
|
||||
|
||||
|
||||
static int
|
||||
DEFUN(cmp_name, (left, right), const PTR left AND const PTR right)
|
||||
{
|
||||
const Sym **npp1 = (const Sym **)left;
|
||||
const Sym **npp2 = (const Sym **)right;
|
||||
|
||||
return strcmp((*npp1)->name, (*npp2)->name);
|
||||
} /* cmp_name */
|
||||
|
||||
|
||||
void
|
||||
DEFUN_VOID(cg_print_index)
|
||||
{
|
||||
int index, nnames, todo, i, j, col, starting_col;
|
||||
Sym **name_sorted_syms, *sym;
|
||||
const char *filename;
|
||||
char buf[20];
|
||||
int column_width = (output_width - 1) / 3; /* don't write in last col! */
|
||||
/*
|
||||
* Now, sort regular function name alphabetically to create an
|
||||
* index:
|
||||
*/
|
||||
name_sorted_syms = (Sym**)xmalloc((symtab.len + num_cycles)*sizeof(Sym*));
|
||||
for (index = 0, nnames = 0; index < symtab.len; index++) {
|
||||
if (ignore_zeros && symtab.base[index].ncalls == 0
|
||||
&& symtab.base[index].hist.time == 0)
|
||||
{
|
||||
continue;
|
||||
} /* if */
|
||||
name_sorted_syms[nnames++] = &symtab.base[index];
|
||||
} /* for */
|
||||
qsort(name_sorted_syms, nnames, sizeof(Sym *), cmp_name);
|
||||
for (index = 1, todo = nnames; index <= num_cycles; index++) {
|
||||
name_sorted_syms[todo++] = &cycle_header[index];
|
||||
} /* for */
|
||||
printf("\f\nIndex by function name\n\n");
|
||||
index = (todo + 2) / 3;
|
||||
for (i = 0; i < index; i++) {
|
||||
col = 0;
|
||||
starting_col = 0;
|
||||
for (j = i; j < todo; j += index) {
|
||||
sym = name_sorted_syms[j];
|
||||
if (sym->cg.print_flag) {
|
||||
sprintf(buf, "[%d]", sym->cg.index);
|
||||
} else {
|
||||
sprintf(buf, "(%d)", sym->cg.index);
|
||||
} /* if */
|
||||
if (j < nnames) {
|
||||
if (bsd_style_output) {
|
||||
printf("%6.6s %-19.19s", buf, sym->name);
|
||||
} else {
|
||||
col += strlen(buf);
|
||||
for (; col < starting_col + 5; ++col) {
|
||||
putchar(' ');
|
||||
} /* for */
|
||||
printf(" %s ", buf);
|
||||
col += print_name_only(sym);
|
||||
if (!line_granularity && sym->is_static && sym->file) {
|
||||
filename = sym->file->name;
|
||||
if (!print_path) {
|
||||
filename = strrchr(filename, '/');
|
||||
if (filename) {
|
||||
++filename;
|
||||
} else {
|
||||
filename = sym->file->name;
|
||||
} /* if */
|
||||
} /* if */
|
||||
printf(" (%s)", filename);
|
||||
col += strlen(filename) + 3;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} else {
|
||||
printf("%6.6s ", buf);
|
||||
sprintf(buf, "<cycle %d>", sym->cg.cyc.num);
|
||||
printf("%-19.19s", buf);
|
||||
} /* if */
|
||||
starting_col += column_width;
|
||||
} /* for */
|
||||
printf("\n");
|
||||
} /* for */
|
||||
free(name_sorted_syms);
|
||||
} /* cg_print_index */
|
||||
|
||||
/*** end of cg_print.c ***/
|
12
gprof/cg_print.h
Normal file
12
gprof/cg_print.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
#ifndef cg_print_h
|
||||
#define cg_print_h
|
||||
|
||||
#include "gprof.h"
|
||||
#include "symtab.h"
|
||||
|
||||
extern double print_time; /* total of time being printed */
|
||||
|
||||
extern void cg_print PARAMS ((Sym **cg));
|
||||
extern void cg_print_index PARAMS((void));
|
||||
|
||||
#endif /* cg_print_h */
|
1
gprof/config/mt-alpha
Normal file
1
gprof/config/mt-alpha
Normal file
|
@ -0,0 +1 @@
|
|||
MY_MACHINE=alpha
|
|
@ -10,6 +10,7 @@ srcname="gprof"
|
|||
|
||||
# per-target:
|
||||
case "${target}" in
|
||||
alpha-*-*) my_target=alpha ;;
|
||||
i[345]86-*-*) my_target=i386 ;;
|
||||
sparc-*-*) my_target=sparc ;;
|
||||
tahoe-*-*) my_target=tahoe ;;
|
||||
|
|
476
gprof/core.c
Normal file
476
gprof/core.c
Normal file
|
@ -0,0 +1,476 @@
|
|||
#include "libiberty.h"
|
||||
#include "gprof.h"
|
||||
#include "core.h"
|
||||
#include "symtab.h"
|
||||
|
||||
bfd *core_bfd;
|
||||
int core_num_syms;
|
||||
asymbol **core_syms;
|
||||
asection *core_text_sect;
|
||||
PTR core_text_space;
|
||||
|
||||
|
||||
void
|
||||
DEFUN(core_init, (a_out_name), const char *a_out_name)
|
||||
{
|
||||
core_bfd = bfd_openr(a_out_name, 0);
|
||||
|
||||
if (!core_bfd) {
|
||||
perror(a_out_name);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
if (!bfd_check_format(core_bfd, bfd_object)) {
|
||||
fprintf(stderr, "%s: %s: not in a.out format\n", whoami, a_out_name);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
/* get core's text section: */
|
||||
core_text_sect = bfd_get_section_by_name(core_bfd, ".text");
|
||||
if (!core_text_sect) {
|
||||
core_text_sect = bfd_get_section_by_name(core_bfd, "$CODE$");
|
||||
if (!core_text_sect) {
|
||||
fprintf(stderr, "%s: can't find .text section in %s\n",
|
||||
whoami, a_out_name);
|
||||
done(1);
|
||||
} /* if */
|
||||
} /* if */
|
||||
|
||||
/* read core's symbol table: */
|
||||
|
||||
/* this will probably give us more than we need, but that's ok: */
|
||||
core_num_syms = bfd_get_symtab_upper_bound(core_bfd);
|
||||
if (core_num_syms < 0) {
|
||||
fprintf(stderr, "%s: %s: %s\n", whoami, a_out_name,
|
||||
bfd_errmsg(bfd_get_error()));
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
core_syms = (asymbol**)xmalloc(core_num_syms);
|
||||
core_num_syms = bfd_canonicalize_symtab(core_bfd, core_syms);
|
||||
if (core_num_syms < 0) {
|
||||
fprintf(stderr, "%s: %s: %s\n", whoami, a_out_name,
|
||||
bfd_errmsg(bfd_get_error()));
|
||||
done(1);
|
||||
} /* if */
|
||||
} /* core_init */
|
||||
|
||||
|
||||
/*
|
||||
* Read in the text space of an a.out file
|
||||
*/
|
||||
void
|
||||
DEFUN(core_get_text_space, (core_bfd), bfd *core_bfd)
|
||||
{
|
||||
core_text_space = (PTR) malloc(core_text_sect->_raw_size);
|
||||
|
||||
if (!core_text_space) {
|
||||
fprintf(stderr, "%s: ran out room for %ld bytes of text space\n",
|
||||
whoami, core_text_sect->_raw_size);
|
||||
done(1);
|
||||
} /* if */
|
||||
if (!bfd_get_section_contents(core_bfd, core_text_sect, core_text_space,
|
||||
0, core_text_sect->_raw_size))
|
||||
{
|
||||
bfd_perror("bfd_get_section_contents");
|
||||
free(core_text_space);
|
||||
core_text_space = 0;
|
||||
} /* if */
|
||||
if (!core_text_space) {
|
||||
fprintf(stderr, "%s: can't do -c\n", whoami);
|
||||
} /* if */
|
||||
} /* core_get_text_space */
|
||||
|
||||
|
||||
/*
|
||||
* Return class of symbol SYM. The returned class can be any of:
|
||||
* 0 -> symbol is not interesting to us
|
||||
* 'T' -> symbol is a global name
|
||||
* 't' -> symbol is a local (static) name
|
||||
*/
|
||||
static int
|
||||
DEFUN(core_sym_class, (sym), asymbol *sym)
|
||||
{
|
||||
symbol_info syminfo;
|
||||
const char *name;
|
||||
char sym_prefix;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Must be a text symbol, and static text symbols don't qualify if
|
||||
* ignore_static_funcs set.
|
||||
*/
|
||||
if (!sym->section) {
|
||||
return 0;
|
||||
} /* if */
|
||||
|
||||
if (ignore_static_funcs && (sym->flags & BSF_LOCAL)) {
|
||||
DBG(AOUTDEBUG, printf("[core_sym_class] %s: not a function\n",
|
||||
sym->name));
|
||||
return 0;
|
||||
} /* if */
|
||||
|
||||
bfd_get_symbol_info(core_bfd, sym, &syminfo);
|
||||
i = syminfo.type;
|
||||
|
||||
if (i == 'T') {
|
||||
return i; /* it's a global symbol */
|
||||
} /* if */
|
||||
|
||||
if (i != 't') {
|
||||
/* not a static text symbol */
|
||||
DBG(AOUTDEBUG, printf("[core_sym_class] %s is of class %c\n",
|
||||
sym->name, i));
|
||||
return 0;
|
||||
} /* if */
|
||||
|
||||
/* do some more filtering on static function-names: */
|
||||
|
||||
if (ignore_static_funcs) {
|
||||
return 0;
|
||||
} /* if */
|
||||
/*
|
||||
* Can't zero-length name or funny characters in name, where
|
||||
* `funny' includes: `.' (.o file names) and `$' (Pascal labels).
|
||||
*/
|
||||
if (!sym->name || sym->name[0] == '\0') {
|
||||
return 0;
|
||||
} /* if */
|
||||
|
||||
for (name = sym->name; *name; ++name) {
|
||||
if (*name == '.' || *name == '$') {
|
||||
return 0;
|
||||
} /* if */
|
||||
} /* if */
|
||||
/*
|
||||
* On systems where the C compiler adds an underscore to all
|
||||
* names, static names without underscores seem usually to be
|
||||
* labels in hand written assembler in the library. We don't want
|
||||
* these names. This is certainly necessary on a Sparc running
|
||||
* SunOS 4.1 (try profiling a program that does a lot of
|
||||
* division). I don't know whether it has harmful side effects on
|
||||
* other systems. Perhaps it should be made configurable.
|
||||
*/
|
||||
sym_prefix = bfd_get_symbol_leading_char(core_bfd);
|
||||
if (sym_prefix && sym_prefix != sym->name[0]
|
||||
/*
|
||||
* GCC may add special symbols to help gdb figure out the file
|
||||
* language. We want to ignore these, since sometimes they mask
|
||||
* the real function. (dj@ctron)
|
||||
*/
|
||||
|| !strncmp (sym->name, "__gnu_compiled", 14)
|
||||
|| !strncmp (sym->name, "___gnu_compiled", 15))
|
||||
{
|
||||
return 0;
|
||||
} /* if */
|
||||
return 't'; /* it's a static text symbol */
|
||||
} /* core_sym_class */
|
||||
|
||||
|
||||
/*
|
||||
* Get whatever source info we can get regarding address ADDR:
|
||||
*/
|
||||
static bool
|
||||
DEFUN(get_src_info, (addr, filename, name, line_num),
|
||||
bfd_vma addr AND const char **filename AND const char **name
|
||||
AND int *line_num)
|
||||
{
|
||||
const char *fname = 0, *func_name = 0;
|
||||
int l = 0;
|
||||
|
||||
if (bfd_find_nearest_line(core_bfd, core_text_sect, core_syms,
|
||||
addr - core_text_sect->vma,
|
||||
&fname, &func_name, &l)
|
||||
&& fname && func_name && l)
|
||||
{
|
||||
DBG(AOUTDEBUG, printf("[get_src_info] 0x%lx -> %s:%d (%s)\n",
|
||||
addr, fname, l, func_name));
|
||||
*filename = fname;
|
||||
*name = func_name;
|
||||
*line_num = l;
|
||||
return TRUE;
|
||||
} else {
|
||||
DBG(AOUTDEBUG, printf("[get_src_info] no info for 0x%lx (%s:%d,%s)\n",
|
||||
(long) addr, fname ? fname : "<unknown>", l,
|
||||
func_name ? func_name : "<unknown>"));
|
||||
return FALSE;
|
||||
} /* if */
|
||||
} /* get_src_info */
|
||||
|
||||
|
||||
/*
|
||||
* Read in symbol table from core. One symbol per function is
|
||||
* entered.
|
||||
*/
|
||||
void
|
||||
DEFUN(core_create_function_syms, (core_bfd), bfd *core_bfd)
|
||||
{
|
||||
bfd_vma min_vma = ~0, max_vma = 0;
|
||||
const char *filename, *func_name;
|
||||
int class;
|
||||
long i;
|
||||
|
||||
/* pass 1 - determine upper bound on number of function names: */
|
||||
symtab.len = 0;
|
||||
for (i = 0; i < core_num_syms; ++i) {
|
||||
if (!core_sym_class(core_syms[i])) {
|
||||
continue;
|
||||
} /* if */
|
||||
++symtab.len;
|
||||
} /* for */
|
||||
|
||||
if (symtab.len == 0) {
|
||||
fprintf(stderr, "%s: file `%s' has no symbols\n", whoami, a_out_name);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
/* the "+ 2" is for the sentinels: */
|
||||
symtab.base = (Sym*)xmalloc((symtab.len + 2) * sizeof(Sym));
|
||||
|
||||
/* pass 2 - create symbols: */
|
||||
|
||||
symtab.limit = symtab.base;
|
||||
for (i = 0; i < core_num_syms; ++i) {
|
||||
class = core_sym_class(core_syms[i]);
|
||||
if (!class) {
|
||||
DBG(AOUTDEBUG,
|
||||
printf("[core_create_function_syms] rejecting: 0x%lx %s\n",
|
||||
core_syms[i]->value, core_syms[i]->name));
|
||||
continue;
|
||||
} /* if */
|
||||
|
||||
sym_init(symtab.limit);
|
||||
|
||||
/* symbol offsets are always section-relative: */
|
||||
|
||||
symtab.limit->addr = core_syms[i]->value + core_syms[i]->section->vma;
|
||||
symtab.limit->name = core_syms[i]->name;
|
||||
|
||||
#ifdef __osf__
|
||||
/*
|
||||
* Suppress symbols that are not function names. This is
|
||||
* useful to suppress code-labels and aliases.
|
||||
*
|
||||
* This is known to be useful under DEC's OSF/1. Under SunOS 4.x,
|
||||
* labels do not appear in the symbol table info, so this isn't
|
||||
* necessary.
|
||||
*/
|
||||
if (get_src_info(symtab.limit->addr, &filename, &func_name,
|
||||
&symtab.limit->line_num))
|
||||
{
|
||||
symtab.limit->file = source_file_lookup_path(filename);
|
||||
|
||||
if (strcmp(symtab.limit->name, func_name) != 0) {
|
||||
/*
|
||||
* The symbol's address maps to a different name, so
|
||||
* it can't be a function-entry point. This happens
|
||||
* for labels, for example.
|
||||
*/
|
||||
DBG(AOUTDEBUG,
|
||||
printf("[core_create_function_syms: rej %s (maps to %s)\n",
|
||||
symtab.limit->name, func_name));
|
||||
continue;
|
||||
} /* if */
|
||||
} /* if */
|
||||
#endif
|
||||
|
||||
symtab.limit->is_func = TRUE;
|
||||
symtab.limit->is_bb_head = TRUE;
|
||||
if (class == 't') {
|
||||
symtab.limit->is_static = TRUE;
|
||||
} /* if */
|
||||
|
||||
min_vma = MIN(symtab.limit->addr, min_vma);
|
||||
max_vma = MAX(symtab.limit->addr, max_vma);
|
||||
|
||||
/*
|
||||
* If we see "main" without an initial '_', we assume names
|
||||
* are *not* prefixed by '_'.
|
||||
*/
|
||||
if (symtab.limit->name[0] == 'm' && discard_underscores
|
||||
&& strcmp(symtab.limit->name, "main") == 0)
|
||||
{
|
||||
discard_underscores = 0;
|
||||
} /* if */
|
||||
|
||||
DBG(AOUTDEBUG, printf("[core_create_function_syms] %ld %s 0x%lx\n",
|
||||
(long)(symtab.limit - symtab.base),
|
||||
symtab.limit->name, symtab.limit->addr));
|
||||
++symtab.limit;
|
||||
} /* for */
|
||||
|
||||
/* create sentinels: */
|
||||
|
||||
sym_init(symtab.limit);
|
||||
symtab.limit->name = "<locore>";
|
||||
symtab.limit->addr = 0;
|
||||
symtab.limit->end_addr = min_vma - 1;
|
||||
++symtab.limit;
|
||||
|
||||
sym_init(symtab.limit);
|
||||
symtab.limit->name = "<hicore>";
|
||||
symtab.limit->addr = max_vma + 1;
|
||||
symtab.limit->end_addr = ~0;
|
||||
++symtab.limit;
|
||||
|
||||
symtab.len = symtab.limit - symtab.base;
|
||||
symtab_finalize(&symtab);
|
||||
} /* core_create_function_syms */
|
||||
|
||||
|
||||
/*
|
||||
* Read in symbol table from core. One symbol per line of source code
|
||||
* is entered.
|
||||
*/
|
||||
void
|
||||
DEFUN(core_create_line_syms, (core_bfd), bfd *core_bfd)
|
||||
{
|
||||
char prev_name[PATH_MAX], prev_filename[PATH_MAX];
|
||||
bfd_vma vma, min_vma = ~0, max_vma = 0;
|
||||
bfd_vma offset, prev_offset, min_dist;
|
||||
Sym *prev, dummy, *sentinel, *sym;
|
||||
const char *filename;
|
||||
int prev_line_num, i;
|
||||
Sym_Table ltab;
|
||||
/*
|
||||
* Create symbols for functions as usual. This is necessary in
|
||||
* cases where parts of a program were not compiled with -g. For
|
||||
* those parts we still want to get info at the function level:
|
||||
*/
|
||||
core_create_function_syms(core_bfd);
|
||||
|
||||
/* pass 1 - counter number of symbols: */
|
||||
|
||||
/*
|
||||
* To find all line information, walk through all possible
|
||||
* text-space addresses (one by one!) and get the debugging
|
||||
* info for each address. When the debugging info changes,
|
||||
* it is time to create a new symbol.
|
||||
*
|
||||
* Of course, this is rather slow and it would be better if
|
||||
* bfd would provide an iterator for enumerating all line
|
||||
* infos, but for now, we try to speed up the second pass
|
||||
* by determining what the minimum code distance between two
|
||||
* lines is.
|
||||
*/
|
||||
prev_name[0] = '\0';
|
||||
ltab.len = 0;
|
||||
min_dist = core_text_sect->_raw_size;
|
||||
prev_offset = -min_dist;
|
||||
prev_filename[0] = '\0';
|
||||
prev_line_num = 0;
|
||||
for (offset = 0; offset < core_text_sect->_raw_size; ++offset) {
|
||||
vma = core_text_sect->vma + offset;
|
||||
if (!get_src_info(vma, &filename, &dummy.name, &dummy.line_num)
|
||||
|| (prev_line_num == dummy.line_num &&
|
||||
strcmp(prev_name, dummy.name) == 0
|
||||
&& strcmp(prev_filename, filename) == 0))
|
||||
{
|
||||
continue;
|
||||
} /* if */
|
||||
|
||||
++ltab.len;
|
||||
prev_line_num = dummy.line_num;
|
||||
strcpy(prev_name, dummy.name);
|
||||
strcpy(prev_filename, filename);
|
||||
|
||||
if (offset - prev_offset < min_dist) {
|
||||
min_dist = offset - prev_offset;
|
||||
} /* if */
|
||||
prev_offset = offset;
|
||||
|
||||
min_vma = MIN(vma, min_vma);
|
||||
max_vma = MAX(vma, max_vma);
|
||||
} /* for */
|
||||
|
||||
DBG(AOUTDEBUG, printf("[core_create_line_syms] min_dist=%lx\n", min_dist));
|
||||
|
||||
/* make room for function symbols, too: */
|
||||
ltab.len += symtab.len;
|
||||
ltab.base = (Sym*) xmalloc(ltab.len * sizeof(Sym));
|
||||
ltab.limit = ltab.base;
|
||||
|
||||
/* pass 2 - create symbols: */
|
||||
|
||||
prev = 0;
|
||||
for (offset = 0; offset < core_text_sect->_raw_size; offset += min_dist) {
|
||||
sym_init(ltab.limit);
|
||||
if (!get_src_info(core_text_sect->vma + offset, &filename,
|
||||
<ab.limit->name, <ab.limit->line_num)
|
||||
|| (prev && prev->line_num == ltab.limit->line_num
|
||||
&& strcmp(prev->name, ltab.limit->name) == 0
|
||||
&& strcmp(prev->file->name, filename) == 0))
|
||||
{
|
||||
continue;
|
||||
} /* if */
|
||||
|
||||
/* make name pointer a malloc'ed string: */
|
||||
ltab.limit->name = strdup(ltab.limit->name);
|
||||
ltab.limit->file = source_file_lookup_path(filename);
|
||||
|
||||
ltab.limit->addr = core_text_sect->vma + offset;
|
||||
prev = ltab.limit;
|
||||
|
||||
/*
|
||||
* If we see "main" without an initial '_', we assume names
|
||||
* are *not* prefixed by '_'.
|
||||
*/
|
||||
if (ltab.limit->name[0] == 'm' && discard_underscores
|
||||
&& strcmp(ltab.limit->name, "main") == 0)
|
||||
{
|
||||
discard_underscores = 0;
|
||||
} /* if */
|
||||
|
||||
DBG(AOUTDEBUG, printf("[core_create_line_syms] %d %s 0x%lx\n",
|
||||
ltab.len, ltab.limit->name,
|
||||
ltab.limit->addr));
|
||||
++ltab.limit;
|
||||
} /* for */
|
||||
|
||||
/* update sentinels: */
|
||||
|
||||
sentinel = sym_lookup(&symtab, 0);
|
||||
if (strcmp(sentinel->name, "<locore>") == 0
|
||||
&& min_vma <= sentinel->end_addr)
|
||||
{
|
||||
sentinel->end_addr = min_vma - 1;
|
||||
} /* if */
|
||||
|
||||
sentinel = sym_lookup(&symtab, ~0);
|
||||
if (strcmp(sentinel->name, "<hicore>") == 0 && max_vma >= sentinel->addr) {
|
||||
sentinel->addr = max_vma + 1;
|
||||
} /* if */
|
||||
|
||||
/* copy in function symbols: */
|
||||
memcpy(ltab.limit, symtab.base, symtab.len * sizeof(Sym));
|
||||
ltab.limit += symtab.len;
|
||||
|
||||
if (ltab.limit - ltab.base != ltab.len) {
|
||||
fprintf(stderr,
|
||||
"%s: somebody miscounted: ltab.len=%ld instead of %d\n",
|
||||
whoami, (long) (ltab.limit - ltab.base), ltab.len);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
/* finalize ltab and make it symbol table: */
|
||||
|
||||
symtab_finalize(<ab);
|
||||
free(symtab.base);
|
||||
symtab = ltab;
|
||||
|
||||
/* now go through all core symbols and set is_static accordingly: */
|
||||
|
||||
for (i = 0; i < core_num_syms; ++i) {
|
||||
if (core_sym_class(core_syms[i]) == 't') {
|
||||
sym = sym_lookup(&symtab, core_syms[i]->value
|
||||
+ core_syms[i]->section->vma);
|
||||
do {
|
||||
sym++->is_static = TRUE;
|
||||
} while (sym->file == sym[-1].file &&
|
||||
strcmp(sym->name, sym[-1].name) == 0);
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
} /* core_create_line_syms */
|
||||
|
||||
/*** end of core.c ***/
|
17
gprof/core.h
Normal file
17
gprof/core.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#ifndef core_h
|
||||
#define core_h
|
||||
|
||||
#include "bfd.h"
|
||||
|
||||
extern bfd *core_bfd; /* bfd for core-file */
|
||||
extern int core_num_syms; /* # of entries in symbol-table */
|
||||
extern asymbol **core_syms; /* symbol table in a.out */
|
||||
extern asection *core_text_sect; /* core text section */
|
||||
extern PTR core_text_space; /* text space of a.out in core */
|
||||
|
||||
extern void core_init PARAMS((const char *a_out_name));
|
||||
extern void core_get_text_space PARAMS((bfd *core_bfd));
|
||||
extern void core_create_function_syms PARAMS((bfd *core_bfd));
|
||||
extern void core_create_line_syms PARAMS((bfd *core_bfd));
|
||||
|
||||
#endif /* core_h */
|
302
gprof/dfn.c
302
gprof/dfn.c
|
@ -1,302 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)dfn.c 5.4 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "gprof.h"
|
||||
|
||||
#define DFN_DEPTH 100
|
||||
struct dfnstruct {
|
||||
nltype *nlentryp;
|
||||
int cycletop;
|
||||
};
|
||||
typedef struct dfnstruct dfntype;
|
||||
|
||||
dfntype dfn_stack[ DFN_DEPTH ];
|
||||
int dfn_depth = 0;
|
||||
|
||||
int dfn_counter = DFN_NAN;
|
||||
|
||||
/*
|
||||
* given this parent, depth first number its children.
|
||||
*/
|
||||
dfn( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
arctype *arcp;
|
||||
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn] dfn(" );
|
||||
printname( parentp );
|
||||
printf( ")\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* if we're already numbered, no need to look any furthur.
|
||||
*/
|
||||
if ( dfn_numbered( parentp ) ) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* if we're already busy, must be a cycle
|
||||
*/
|
||||
if ( dfn_busy( parentp ) ) {
|
||||
dfn_findcycle( parentp );
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* visit yourself before your children
|
||||
*/
|
||||
dfn_pre_visit( parentp );
|
||||
/*
|
||||
* visit children
|
||||
*/
|
||||
for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
|
||||
dfn( arcp -> arc_childp );
|
||||
}
|
||||
/*
|
||||
* visit yourself after your children
|
||||
*/
|
||||
dfn_post_visit( parentp );
|
||||
}
|
||||
|
||||
/*
|
||||
* push a parent onto the stack and mark it busy
|
||||
*/
|
||||
dfn_pre_visit( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
|
||||
dfn_depth += 1;
|
||||
if ( dfn_depth >= DFN_DEPTH ) {
|
||||
fprintf( stderr , "[dfn] out of my depth (dfn_stack overflow)\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
dfn_stack[ dfn_depth ].nlentryp = parentp;
|
||||
dfn_stack[ dfn_depth ].cycletop = dfn_depth;
|
||||
parentp -> toporder = DFN_BUSY;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_pre_visit]\t\t%d:" , dfn_depth );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
|
||||
/*
|
||||
* are we already numbered?
|
||||
*/
|
||||
bool
|
||||
dfn_numbered( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
|
||||
return ( childp -> toporder != DFN_NAN && childp -> toporder != DFN_BUSY );
|
||||
}
|
||||
|
||||
/*
|
||||
* are we already busy?
|
||||
*/
|
||||
bool
|
||||
dfn_busy( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
|
||||
if ( childp -> toporder == DFN_NAN ) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* MISSING: an explanation
|
||||
*/
|
||||
dfn_findcycle( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
int cycletop;
|
||||
nltype *cycleheadp;
|
||||
nltype *tailp;
|
||||
int index;
|
||||
|
||||
for ( cycletop = dfn_depth ; cycletop > 0 ; cycletop -= 1 ) {
|
||||
cycleheadp = dfn_stack[ cycletop ].nlentryp;
|
||||
if ( childp == cycleheadp ) {
|
||||
break;
|
||||
}
|
||||
if ( childp -> cyclehead != childp &&
|
||||
childp -> cyclehead == cycleheadp ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( cycletop <= 0 ) {
|
||||
fprintf( stderr , "[dfn_findcycle] couldn't find head of cycle\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] dfn_depth %d cycletop %d " ,
|
||||
dfn_depth , cycletop );
|
||||
printname( cycleheadp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( cycletop == dfn_depth ) {
|
||||
/*
|
||||
* this is previous function, e.g. this calls itself
|
||||
* sort of boring
|
||||
*/
|
||||
dfn_self_cycle( childp );
|
||||
} else {
|
||||
/*
|
||||
* glom intervening functions that aren't already
|
||||
* glommed into this cycle.
|
||||
* things have been glommed when their cyclehead field
|
||||
* points to the head of the cycle they are glommed into.
|
||||
*/
|
||||
for ( tailp = cycleheadp ; tailp -> cnext ; tailp = tailp -> cnext ) {
|
||||
/* void: chase down to tail of things already glommed */
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] tail " );
|
||||
printname( tailp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
/*
|
||||
* if what we think is the top of the cycle
|
||||
* has a cyclehead field, then it's not really the
|
||||
* head of the cycle, which is really what we want
|
||||
*/
|
||||
if ( cycleheadp -> cyclehead != cycleheadp ) {
|
||||
cycleheadp = cycleheadp -> cyclehead;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] new cyclehead " );
|
||||
printname( cycleheadp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
for ( index = cycletop + 1 ; index <= dfn_depth ; index += 1 ) {
|
||||
childp = dfn_stack[ index ].nlentryp;
|
||||
if ( childp -> cyclehead == childp ) {
|
||||
/*
|
||||
* not yet glommed anywhere, glom it
|
||||
* and fix any children it has glommed
|
||||
*/
|
||||
tailp -> cnext = childp;
|
||||
childp -> cyclehead = cycleheadp;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] glomming " );
|
||||
printname( childp );
|
||||
printf( " onto " );
|
||||
printname( cycleheadp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( tailp = childp ; tailp->cnext ; tailp = tailp->cnext ) {
|
||||
tailp -> cnext -> cyclehead = cycleheadp;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] and its tail " );
|
||||
printname( tailp -> cnext );
|
||||
printf( " onto " );
|
||||
printname( cycleheadp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
} else if ( childp -> cyclehead != cycleheadp /* firewall */ ) {
|
||||
fprintf( stderr ,
|
||||
"[dfn_busy] glommed, but not to cyclehead\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* deal with self-cycles
|
||||
* for lint: ARGSUSED
|
||||
*/
|
||||
dfn_self_cycle( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
/*
|
||||
* since we are taking out self-cycles elsewhere
|
||||
* no need for the special case, here.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_self_cycle] " );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
|
||||
/*
|
||||
* visit a node after all its children
|
||||
* [MISSING: an explanation]
|
||||
* and pop it off the stack
|
||||
*/
|
||||
dfn_post_visit( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
nltype *memberp;
|
||||
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_post_visit]\t%d: " , dfn_depth );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* number functions and things in their cycles
|
||||
* unless the function is itself part of a cycle
|
||||
*/
|
||||
if ( parentp -> cyclehead == parentp ) {
|
||||
dfn_counter += 1;
|
||||
for ( memberp = parentp ; memberp ; memberp = memberp -> cnext ) {
|
||||
memberp -> toporder = dfn_counter;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_post_visit]\t\tmember " );
|
||||
printname( memberp );
|
||||
printf( " -> toporder = %d\n" , dfn_counter );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
} else {
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_post_visit]\t\tis part of a cycle\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
dfn_depth -= 1;
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
#include "gprof.h"
|
||||
#include "symtab.h"
|
||||
|
||||
|
||||
/*
|
||||
* dummy.c -- This file should be used for an unsupported processor type.
|
||||
* It does nothing, but prevents findcall() from being unresolved.
|
||||
*/
|
||||
|
||||
findcall( parentp , p_lowpc , p_highpc )
|
||||
nltype *parentp;
|
||||
unsigned long p_lowpc;
|
||||
unsigned long p_highpc;
|
||||
void
|
||||
DEFUN(find_call, (parent, p_lowpc, p_highpc),
|
||||
Sym *parent AND bfd_vma p_lowpc AND bfd_vma p_highpc)
|
||||
{
|
||||
}
|
||||
fprintf(stderr, "%s: -c supported on this machine architecture\n",
|
||||
whoami);
|
||||
} /* find_call */
|
||||
|
||||
/*** end of dummy.c ***/
|
||||
|
|
|
@ -32,17 +32,21 @@
|
|||
*
|
||||
* @(#)dummy.h 5.1 (Berkeley) 4/18/91
|
||||
*/
|
||||
#ifndef dummy_h
|
||||
#define dummy_h
|
||||
|
||||
/*
|
||||
* dummy.h -- This file should be used when a processor is not yet supported.
|
||||
*/
|
||||
|
||||
/*
|
||||
* offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see asgnsamples for use and explanation.)
|
||||
* Offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see hist_assign_samples()) for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_OF_CODE 0
|
||||
#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
|
||||
#define OFFSET_TO_CODE 0
|
||||
#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT))
|
||||
|
||||
enum opermodes { dummy };
|
||||
typedef enum opermodes operandenum;
|
||||
|
||||
#endif /* dummy_h */
|
||||
|
|
|
@ -25,4 +25,3 @@ name the name of the function. This is the minor sort
|
|||
the function in the gprof listing. If the index is
|
||||
in parenthesis it shows where it would appear in
|
||||
the gprof listing if it were to be printed.
|
||||
|
||||
|
|
58
gprof/gmon.h
58
gprof/gmon.h
|
@ -32,28 +32,36 @@
|
|||
*
|
||||
* @(#)gmon.h 5.2 (Berkeley) 5/6/91
|
||||
*/
|
||||
#ifndef gmon_h
|
||||
#define gmon_h
|
||||
|
||||
struct phdr {
|
||||
char *lpc;
|
||||
char *hpc;
|
||||
int ncnt;
|
||||
struct raw_phdr {
|
||||
char low_pc[sizeof(bfd_vma)]; /* base pc address of sample buffer */
|
||||
char high_pc[sizeof(bfd_vma)]; /* max pc address of sampled buffer */
|
||||
char ncnt[4]; /* size of sample buffer (plus this header) */
|
||||
#ifdef __osf__
|
||||
/*
|
||||
* DEC's OSF v3.0 uses 4 bytes of padding to bring the header to
|
||||
* a size that is a multiple of 8.
|
||||
*/
|
||||
char pad[4];
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* histogram counters are unsigned shorts (according to the kernel).
|
||||
* Histogram counters are unsigned shorts:
|
||||
*/
|
||||
#define HISTCOUNTER unsigned short
|
||||
|
||||
/*
|
||||
* fraction of text space to allocate for histogram counters
|
||||
* here, 1/2
|
||||
* Fraction of text space to allocate for histogram counters here, 1/2:
|
||||
*/
|
||||
#define HISTFRACTION 2
|
||||
|
||||
/*
|
||||
* Fraction of text space to allocate for from hash buckets.
|
||||
* The value of HASHFRACTION is based on the minimum number of bytes
|
||||
* of separation between two subroutine call points in the object code.
|
||||
* Fraction of text space to allocate for from hash buckets. The
|
||||
* value of HASHFRACTION is based on the minimum number of bytes of
|
||||
* separation between two subroutine call points in the object code.
|
||||
* Given MIN_SUBR_SEPARATION bytes of separation the value of
|
||||
* HASHFRACTION is calculated as:
|
||||
*
|
||||
|
@ -75,37 +83,33 @@ struct phdr {
|
|||
#define HASHFRACTION 1
|
||||
|
||||
/*
|
||||
* percent of text space to allocate for tostructs
|
||||
* with a minimum.
|
||||
* Percent of text space to allocate for tostructs with a minimum:
|
||||
*/
|
||||
#define ARCDENSITY 2
|
||||
#define MINARCS 50
|
||||
|
||||
struct tostruct {
|
||||
char *selfpc;
|
||||
long count;
|
||||
int count;
|
||||
unsigned short link;
|
||||
};
|
||||
|
||||
/*
|
||||
* a raw arc,
|
||||
* with pointers to the calling site and the called site
|
||||
* and a count.
|
||||
* A raw arc, with pointers to the calling site and the called site
|
||||
* and a count. Everything is defined in terms of characters so
|
||||
* as to get a packed representation (otherwise, different compilers
|
||||
* might introduce different padding):
|
||||
*/
|
||||
struct rawarc {
|
||||
unsigned long raw_frompc;
|
||||
unsigned long raw_selfpc;
|
||||
long raw_count;
|
||||
};
|
||||
|
||||
struct veryrawarc {
|
||||
char raw_frompc[4];
|
||||
char raw_selfpc[4];
|
||||
char raw_count[4];
|
||||
struct raw_arc {
|
||||
char from_pc[sizeof(bfd_vma)];
|
||||
char self_pc[sizeof(bfd_vma)];
|
||||
char count[sizeof(long)];
|
||||
};
|
||||
|
||||
/*
|
||||
* general rounding functions.
|
||||
* General rounding functions:
|
||||
*/
|
||||
#define ROUNDDOWN(x,y) (((x)/(y))*(y))
|
||||
#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
|
||||
|
||||
#endif /* gmon_h */
|
||||
|
|
354
gprof/gmon_io.c
Normal file
354
gprof/gmon_io.c
Normal file
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* Input and output from/to gmon.out files.
|
||||
*/
|
||||
#include "cg_arcs.h"
|
||||
#include "basic_blocks.h"
|
||||
#include "bfd.h"
|
||||
#include "core.h"
|
||||
#include "call_graph.h"
|
||||
#include "gmon_io.h"
|
||||
#include "gmon_out.h"
|
||||
#include "gmon.h" /* fetch header for old format */
|
||||
#include "gprof.h"
|
||||
#include "hertz.h"
|
||||
#include "hist.h"
|
||||
#include "libiberty.h"
|
||||
|
||||
int gmon_input = 0;
|
||||
int gmon_file_version = 0; /* 0 == old (non-versioned) file format */
|
||||
|
||||
/*
|
||||
* This probably ought to be in libbfd.
|
||||
*/
|
||||
bfd_vma
|
||||
DEFUN(get_vma, (abfd, addr), bfd *abfd AND bfd_byte *addr)
|
||||
{
|
||||
switch (sizeof(bfd_vma)) {
|
||||
case 4: return bfd_get_32(abfd, addr);
|
||||
case 8: return bfd_get_64(abfd, addr);
|
||||
default:
|
||||
fprintf(stderr, "%s: bfd_vma has unexpected size of %ld bytes\n",
|
||||
whoami, (long) sizeof(bfd_vma));
|
||||
done(1);
|
||||
} /* switch */
|
||||
} /* get_vma */
|
||||
|
||||
|
||||
/*
|
||||
* This probably ought to be in libbfd.
|
||||
*/
|
||||
void
|
||||
DEFUN(put_vma, (abfd, val, addr), bfd *abfd AND bfd_vma val AND bfd_byte *addr)
|
||||
{
|
||||
switch (sizeof(bfd_vma)) {
|
||||
case 4: bfd_put_32(abfd, val, addr); break;
|
||||
case 8: bfd_put_64(abfd, val, addr); break;
|
||||
default:
|
||||
fprintf(stderr, "%s: bfd_vma has unexpected size of %ld bytes\n",
|
||||
whoami, (long) sizeof(bfd_vma));
|
||||
done(1);
|
||||
} /* switch */
|
||||
} /* put_vma */
|
||||
|
||||
|
||||
void
|
||||
DEFUN(gmon_out_read, (filename), const char *filename)
|
||||
{
|
||||
FILE *ifp;
|
||||
struct gmon_hdr ghdr;
|
||||
unsigned char tag;
|
||||
int nhist = 0, narcs = 0, nbbs = 0;
|
||||
|
||||
/* open gmon.out file: */
|
||||
|
||||
if (strcmp(filename, "-") == 0) {
|
||||
ifp = stdin;
|
||||
} else {
|
||||
ifp = fopen(filename, FOPEN_RB);
|
||||
if (!ifp) {
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
} /* if */
|
||||
if (fread(&ghdr, sizeof(struct gmon_hdr), 1, ifp) != 1) {
|
||||
fprintf(stderr, "%s: file too short to be a gmon file\n",
|
||||
filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
if ((file_format == FF_MAGIC) ||
|
||||
(file_format == FF_AUTO && !strncmp(&ghdr.cookie[0], GMON_MAGIC, 4)))
|
||||
{
|
||||
if (file_format == FF_MAGIC && strncmp(&ghdr.cookie[0], GMON_MAGIC, 4))
|
||||
{
|
||||
fprintf(stderr, "%s: file `%s' has bad magic cookie\n",
|
||||
whoami, filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
/* right magic, so it's probably really a new gmon.out file */
|
||||
|
||||
gmon_file_version = bfd_get_32(core_bfd, (bfd_byte *) ghdr.version);
|
||||
if (gmon_file_version != GMON_VERSION && gmon_file_version != 0) {
|
||||
fprintf(stderr,
|
||||
"%s: file `%s' has unsupported version %d\n",
|
||||
whoami, filename, gmon_file_version);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
/* read in all the records: */
|
||||
while (fread(&tag, sizeof(tag), 1, ifp) == 1) {
|
||||
switch (tag) {
|
||||
case GMON_TAG_TIME_HIST:
|
||||
++nhist;
|
||||
gmon_input |= INPUT_HISTOGRAM;
|
||||
hist_read_rec(ifp, filename);
|
||||
break;
|
||||
|
||||
case GMON_TAG_CG_ARC:
|
||||
++narcs;
|
||||
gmon_input |= INPUT_CALL_GRAPH;
|
||||
cg_read_rec(ifp, filename);
|
||||
break;
|
||||
|
||||
case GMON_TAG_BB_COUNT:
|
||||
++nbbs;
|
||||
gmon_input |= INPUT_BB_COUNTS;
|
||||
bb_read_rec(ifp, filename);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"%s: %s: found bad tag %d (file corrupted?)\n",
|
||||
whoami, filename, tag);
|
||||
done(1);
|
||||
} /* switch */
|
||||
} /* while */
|
||||
} else if (file_format == FF_AUTO || file_format == FF_BSD) {
|
||||
struct hdr {
|
||||
bfd_vma low_pc;
|
||||
bfd_vma high_pc;
|
||||
int ncnt;
|
||||
};
|
||||
int i, samp_bytes, count;
|
||||
bfd_vma from_pc, self_pc;
|
||||
struct raw_arc raw_arc;
|
||||
struct raw_phdr raw;
|
||||
static struct hdr h;
|
||||
UNIT raw_bin_count;
|
||||
struct hdr tmp;
|
||||
|
||||
/*
|
||||
* Information from a gmon.out file is in two parts: an array of
|
||||
* sampling hits within pc ranges, and the arcs.
|
||||
*/
|
||||
gmon_input = INPUT_HISTOGRAM | INPUT_CALL_GRAPH;
|
||||
|
||||
/*
|
||||
* This fseek() ought to work even on stdin as long as it's
|
||||
* not an interactive device (heck, is there anybody who would
|
||||
* want to type in a gmon.out at the terminal?).
|
||||
*/
|
||||
if (fseek(ifp, 0, SEEK_SET) < 0) {
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
if (fread(&raw, 1, sizeof(struct raw_phdr), ifp)
|
||||
!= sizeof(struct raw_phdr))
|
||||
{
|
||||
fprintf(stderr, "%s: file too short to be a gmon file\n",
|
||||
filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
tmp.low_pc = get_vma(core_bfd, (bfd_byte *) &raw.low_pc[0]);
|
||||
tmp.high_pc = get_vma(core_bfd, (bfd_byte *) &raw.high_pc[0]);
|
||||
tmp.ncnt = bfd_get_32(core_bfd, (bfd_byte *) &raw.ncnt[0]);
|
||||
if (s_highpc && (tmp.low_pc != h.low_pc ||
|
||||
tmp.high_pc != h.high_pc || tmp.ncnt != h.ncnt))
|
||||
{
|
||||
fprintf(stderr, "%s: incompatible with first gmon file\n",
|
||||
filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
h = tmp;
|
||||
s_lowpc = (bfd_vma) h.low_pc;
|
||||
s_highpc = (bfd_vma) h.high_pc;
|
||||
lowpc = (bfd_vma) h.low_pc / sizeof(UNIT);
|
||||
highpc = (bfd_vma) h.high_pc / sizeof(UNIT);
|
||||
samp_bytes = h.ncnt - sizeof(struct raw_phdr);
|
||||
hist_num_bins = samp_bytes / sizeof (UNIT);
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf("[gmon_out_read] lowpc 0x%lx highpc 0x%lx ncnt %d\n",
|
||||
h.low_pc, h.high_pc, h.ncnt);
|
||||
printf("[gmon_out_read] s_lowpc 0x%lx s_highpc 0x%lx\n",
|
||||
s_lowpc, s_highpc);
|
||||
printf("[gmon_out_read] lowpc 0x%lx highpc 0x%lx\n",
|
||||
lowpc, highpc);
|
||||
printf("[gmon_out_read] samp_bytes %d hist_num_bins %d\n",
|
||||
samp_bytes, hist_num_bins));
|
||||
|
||||
if (hist_num_bins) {
|
||||
++nhist;
|
||||
} /* if */
|
||||
|
||||
if (!hist_sample) {
|
||||
hist_sample =
|
||||
(int*) xmalloc(hist_num_bins * sizeof(hist_sample[0]));
|
||||
memset(hist_sample, 0, hist_num_bins * sizeof(hist_sample[0]));
|
||||
} /* if */
|
||||
|
||||
for (i = 0; i < hist_num_bins; ++i) {
|
||||
if (fread(raw_bin_count, sizeof(raw_bin_count), 1, ifp) != 1) {
|
||||
fprintf(stderr,
|
||||
"%s: unexpected EOF after reading %d/%d bins\n",
|
||||
whoami, --i, hist_num_bins);
|
||||
done(1);
|
||||
} /* if */
|
||||
hist_sample[i] += bfd_get_16(core_bfd, (bfd_byte*) raw_bin_count);
|
||||
} /* for */
|
||||
|
||||
/*
|
||||
* The rest of the file consists of a bunch of <from,self,count>
|
||||
* tuples:
|
||||
*/
|
||||
while (fread(&raw_arc, sizeof(raw_arc), 1, ifp) == 1) {
|
||||
++narcs;
|
||||
from_pc = get_vma(core_bfd, (bfd_byte *) raw_arc.from_pc);
|
||||
self_pc = get_vma(core_bfd, (bfd_byte *) raw_arc.self_pc);
|
||||
count = bfd_get_32(core_bfd, (bfd_byte *) raw_arc.count);
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf("[gmon_out_read] frompc 0x%lx selfpc 0x%lx count %d\n",
|
||||
from_pc, self_pc, count));
|
||||
/* add this arc: */
|
||||
cg_tally(from_pc, self_pc, count);
|
||||
} /* while */
|
||||
fclose(ifp);
|
||||
|
||||
if (hz == HZ_WRONG) {
|
||||
/*
|
||||
* How many ticks per second? If we can't tell, report
|
||||
* time in ticks.
|
||||
*/
|
||||
hz = hertz();
|
||||
if (hz == HZ_WRONG) {
|
||||
hz = 1;
|
||||
fprintf(stderr, "time is in ticks, not seconds\n");
|
||||
} /* if */
|
||||
} /* if */
|
||||
} else {
|
||||
fprintf(stderr, "%s: don't know how to deal with file format %d\n",
|
||||
whoami, file_format);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
if (output_style & STYLE_GMON_INFO) {
|
||||
printf("File `%s' (version %d) contains:\n",
|
||||
filename, gmon_file_version);
|
||||
printf("\t%d histogram record%s\n",
|
||||
nhist, nhist == 1 ? "" : "s");
|
||||
printf("\t%d call-graph record%s\n",
|
||||
narcs, narcs == 1 ? "" : "s");
|
||||
printf("\t%d basic-block count record%s\n",
|
||||
nbbs, nbbs == 1 ? "" : "s");
|
||||
first_output = FALSE;
|
||||
} /* if */
|
||||
} /* gmon_out_read */
|
||||
|
||||
|
||||
void
|
||||
DEFUN(gmon_out_write, (filename), const char *filename)
|
||||
{
|
||||
FILE *ofp;
|
||||
struct gmon_hdr ghdr;
|
||||
|
||||
ofp = fopen(filename, FOPEN_WB);
|
||||
if (!ofp) {
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
if (file_format == FF_AUTO || file_format == FF_MAGIC) {
|
||||
/* write gmon header: */
|
||||
|
||||
memcpy(&ghdr.cookie[0], GMON_MAGIC, 4);
|
||||
bfd_put_32(core_bfd, GMON_VERSION, (bfd_byte*) ghdr.version);
|
||||
if (fwrite(&ghdr, sizeof(ghdr), 1, ofp) != 1) {
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
/* write execution time histogram if we have one: */
|
||||
if (gmon_input & INPUT_HISTOGRAM) {
|
||||
hist_write_hist(ofp, filename);
|
||||
} /* if */
|
||||
|
||||
/* write call graph arcs if we have any: */
|
||||
if (gmon_input & INPUT_CALL_GRAPH) {
|
||||
cg_write_arcs(ofp, filename);
|
||||
} /* if */
|
||||
|
||||
/* write basic-block info if we have it: */
|
||||
if (gmon_input & INPUT_BB_COUNTS) {
|
||||
bb_write_blocks(ofp, filename);
|
||||
} /* if */
|
||||
} else if (file_format == FF_BSD) {
|
||||
struct raw_arc raw_arc;
|
||||
UNIT raw_bin_count;
|
||||
bfd_vma lpc, hpc;
|
||||
int i, ncnt;
|
||||
Arc *arc;
|
||||
Sym *sym;
|
||||
|
||||
put_vma(core_bfd, s_lowpc, (bfd_byte*) &lpc);
|
||||
put_vma(core_bfd, s_highpc, (bfd_byte*) &hpc);
|
||||
bfd_put_32(core_bfd,
|
||||
hist_num_bins * sizeof(UNIT) + sizeof(struct raw_phdr),
|
||||
(bfd_byte*) &ncnt);
|
||||
|
||||
/* write header: */
|
||||
if (fwrite(&lpc, sizeof(lpc), 1, ofp) != 1
|
||||
|| fwrite(&hpc, sizeof(hpc), 1, ofp) != 1
|
||||
|| fwrite(&ncnt, sizeof(ncnt), 1, ofp) != 1)
|
||||
{
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
/* dump the samples: */
|
||||
|
||||
for (i = 0; i < hist_num_bins; ++i) {
|
||||
bfd_put_16(core_bfd, hist_sample[i], (bfd_byte*)&raw_bin_count[0]);
|
||||
if (fwrite(&raw_bin_count[0], sizeof(raw_bin_count), 1, ofp) != 1)
|
||||
{
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/* dump the normalized raw arc information: */
|
||||
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
for (arc = sym->cg.children; arc; arc = arc->next_child) {
|
||||
put_vma(core_bfd, arc->parent->addr,
|
||||
(bfd_byte*) raw_arc.from_pc);
|
||||
put_vma(core_bfd, arc->child->addr,
|
||||
(bfd_byte*) raw_arc.self_pc);
|
||||
bfd_put_32(core_bfd, arc->count, (bfd_byte*) raw_arc.count);
|
||||
if (fwrite(&raw_arc, sizeof(raw_arc), 1, ofp) != 1) {
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf("[dumpsum] frompc 0x%lx selfpc 0x%lx count %d\n",
|
||||
arc->parent->addr, arc->child->addr, arc->count));
|
||||
} /* for */
|
||||
} /* for */
|
||||
fclose(ofp);
|
||||
} else {
|
||||
fprintf(stderr, "%s: don't know how to deal with file format %d\n",
|
||||
whoami, file_format);
|
||||
done(1);
|
||||
} /* if */
|
||||
} /* gmon_out_write */
|
||||
|
||||
/*** gmon_out.c ***/
|
20
gprof/gmon_io.h
Normal file
20
gprof/gmon_io.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef gmon_io_h
|
||||
#define gmon_io_h
|
||||
|
||||
#include "bfd.h"
|
||||
#include "gmon.h"
|
||||
|
||||
#define INPUT_HISTOGRAM (1<<0)
|
||||
#define INPUT_CALL_GRAPH (1<<1)
|
||||
#define INPUT_BB_COUNTS (1<<2)
|
||||
|
||||
extern int gmon_input; /* what input did we see? */
|
||||
extern int gmon_file_version; /* file version are we dealing with */
|
||||
|
||||
extern bfd_vma get_vma PARAMS((bfd *abfd, bfd_byte *addr));
|
||||
extern void put_vma PARAMS((bfd *abfd, bfd_vma val, bfd_byte *addr));
|
||||
|
||||
extern void gmon_out_read PARAMS((const char *filename));
|
||||
extern void gmon_out_write PARAMS((const char *filename));
|
||||
|
||||
#endif /* gmon_io_h */
|
46
gprof/gmon_out.h
Normal file
46
gprof/gmon_out.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* This file specifies the format of gmon.out files. It should have
|
||||
* as few external dependencies as possible as it is going to be
|
||||
* included in many different programs. That is, minimize the
|
||||
* number of #include's.
|
||||
*
|
||||
* A gmon.out file consists of a header (defined by gmon_hdr) followed
|
||||
* by a sequence of records. Each record starts with a one-byte tag
|
||||
* identifying the type of records, followed by records specific data.
|
||||
*/
|
||||
#ifndef gmon_out_h
|
||||
#define gmon_out_h
|
||||
|
||||
#define GMON_MAGIC "gmon" /* magic cookie */
|
||||
#define GMON_VERSION 1 /* version number */
|
||||
|
||||
/*
|
||||
* Raw header as it appears on file (without padding):
|
||||
*/
|
||||
struct gmon_hdr {
|
||||
char cookie[4];
|
||||
char version[4];
|
||||
char spare[3*4];
|
||||
};
|
||||
|
||||
/* types of records in this file: */
|
||||
typedef enum {
|
||||
GMON_TAG_TIME_HIST, GMON_TAG_CG_ARC, GMON_TAG_BB_COUNT
|
||||
} GMON_Record_Tag;
|
||||
|
||||
struct gmon_hist_hdr {
|
||||
char low_pc[sizeof(bfd_vma)]; /* base pc address of sample buffer */
|
||||
char high_pc[sizeof(bfd_vma)]; /* max pc address of sampled buffer */
|
||||
char hist_size[4]; /* size of sample buffer */
|
||||
char prof_rate[4]; /* profiling clock rate */
|
||||
char dimen[15]; /* phys. dim., usually "seconds" */
|
||||
char dimen_abbrev; /* usually 's' for "seconds" */
|
||||
};
|
||||
|
||||
struct gmon_cg_arc_record {
|
||||
char from_pc[sizeof(bfd_vma)]; /* address within caller's body */
|
||||
char self_pc[sizeof(bfd_vma)]; /* address within callee's body */
|
||||
char count[4]; /* number of arc traversals */
|
||||
};
|
||||
|
||||
#endif /* gmon_out_h */
|
1126
gprof/gprof.c
1126
gprof/gprof.c
File diff suppressed because it is too large
Load diff
320
gprof/gprof.h
320
gprof/gprof.h
|
@ -18,11 +18,18 @@
|
|||
*
|
||||
* @(#)gprof.h 5.9 (Berkeley) 6/1/90
|
||||
*/
|
||||
#ifndef gprof_h
|
||||
#define gprof_h
|
||||
|
||||
#include <ansidecl.h>
|
||||
#include "sysdep.h"
|
||||
#include "bfd.h"
|
||||
#include "gmon.h"
|
||||
|
||||
#ifndef MIN
|
||||
# define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
# define MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
/* AIX defines hz as a macro. */
|
||||
#undef hz
|
||||
|
@ -41,261 +48,88 @@
|
|||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FOPEN_RB
|
||||
# define FOPEN_RB "r"
|
||||
#endif
|
||||
#ifndef FOPEN_WB
|
||||
# define FOPEN_WB "w"
|
||||
#endif
|
||||
|
||||
#ifndef PATH_MAX
|
||||
# define PATH_MAX 1024
|
||||
#endif
|
||||
|
||||
#define A_OUTNAME "a.out" /* default core filename */
|
||||
#define GMONNAME "gmon.out" /* default profile filename */
|
||||
#define GMONSUM "gmon.sum" /* profile summary filename */
|
||||
|
||||
/*
|
||||
* who am i, for error messages.
|
||||
* These may already be defined on some systems. We could probably
|
||||
* just use the BFD versions of these, since BFD has already dealt
|
||||
* with this problem.
|
||||
*/
|
||||
char *whoami;
|
||||
|
||||
/*
|
||||
* booleans
|
||||
*/
|
||||
typedef int bool;
|
||||
/* These may already be defined on some systems. We could probably just
|
||||
use the BFD versions of these, since BFD has already dealt with this
|
||||
problem. */
|
||||
#undef FALSE
|
||||
#define FALSE 0
|
||||
#undef TRUE
|
||||
#define TRUE 1
|
||||
|
||||
/*
|
||||
* ticks per second
|
||||
*/
|
||||
long hz;
|
||||
#define STYLE_FLAT_PROFILE (1<<0)
|
||||
#define STYLE_CALL_GRAPH (1<<1)
|
||||
#define STYLE_SUMMARY_FILE (1<<2)
|
||||
#define STYLE_EXEC_COUNTS (1<<3)
|
||||
#define STYLE_ANNOTATED_SOURCE (1<<4)
|
||||
#define STYLE_GMON_INFO (1<<5)
|
||||
|
||||
#define ANYDEBUG (1<<0) /* 1 */
|
||||
#define DFNDEBUG (1<<1) /* 2 */
|
||||
#define CYCLEDEBUG (1<<2) /* 4 */
|
||||
#define ARCDEBUG (1<<3) /* 8 */
|
||||
#define TALLYDEBUG (1<<4) /* 16 */
|
||||
#define TIMEDEBUG (1<<5) /* 32 */
|
||||
#define SAMPLEDEBUG (1<<6) /* 64 */
|
||||
#define AOUTDEBUG (1<<7) /* 128 */
|
||||
#define CALLDEBUG (1<<8) /* 256 */
|
||||
#define LOOKUPDEBUG (1<<9) /* 512 */
|
||||
#define PROPDEBUG (1<<10) /* 1024 */
|
||||
#define BBDEBUG (1<<11) /* 2048 */
|
||||
#define IDDEBUG (1<<12) /* 4096 */
|
||||
#define SRCDEBUG (1<<13) /* 8192 */
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DBG(l,s) if (debug_level & (l)) {s;}
|
||||
#else
|
||||
# define DBG(l,s)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
FF_AUTO = 0, FF_MAGIC, FF_BSD, FF_PROF
|
||||
} File_Format;
|
||||
|
||||
typedef int bool;
|
||||
typedef unsigned char UNIT[2]; /* unit of profiling */
|
||||
char *a_outname;
|
||||
#define A_OUTNAME "a.out"
|
||||
|
||||
char *gmonname;
|
||||
#define GMONNAME "gmon.out"
|
||||
#define GMONSUM "gmon.sum"
|
||||
|
||||
extern int bsd_style_output;
|
||||
extern int discard_underscores;
|
||||
extern const char *whoami; /* command-name, for error messages */
|
||||
extern const char *a_out_name; /* core filename */
|
||||
extern long hz; /* ticks per second */
|
||||
|
||||
/*
|
||||
* a constructed arc,
|
||||
* with pointers to the namelist entry of the parent and the child,
|
||||
* a count of how many times this arc was traversed,
|
||||
* and pointers to the next parent of this child and
|
||||
* the next child of this parent.
|
||||
* Command-line options:
|
||||
*/
|
||||
struct arcstruct {
|
||||
struct nl *arc_parentp; /* pointer to parent's nl entry */
|
||||
struct nl *arc_childp; /* pointer to child's nl entry */
|
||||
long arc_count; /* how calls from parent to child */
|
||||
double arc_time; /* time inherited along arc */
|
||||
double arc_childtime; /* childtime inherited along arc */
|
||||
struct arcstruct *arc_parentlist; /* parents-of-this-child list */
|
||||
struct arcstruct *arc_childlist; /* children-of-this-parent list */
|
||||
};
|
||||
typedef struct arcstruct arctype;
|
||||
extern int debug_level; /* debug level */
|
||||
extern int output_style;
|
||||
extern int output_width; /* controls column width in index */
|
||||
extern bool bsd_style_output; /* as opposed to FSF style output */
|
||||
extern bool discard_underscores; /* discard leading underscores? */
|
||||
extern bool ignore_direct_calls; /* don't count direct calls */
|
||||
extern bool ignore_static_funcs; /* suppress static functions */
|
||||
extern bool ignore_zeros; /* ignore unused symbols/files */
|
||||
extern bool line_granularity; /* function or line granularity? */
|
||||
extern bool print_descriptions; /* output profile description */
|
||||
extern bool print_path; /* print path or just filename? */
|
||||
extern File_Format file_format; /* requested file format */
|
||||
|
||||
/*
|
||||
* The symbol table;
|
||||
* for each external in the specified file we gather
|
||||
* its address, the number of calls and compute its share of cpu time.
|
||||
*/
|
||||
struct nl {
|
||||
CONST char *name; /* the name */
|
||||
unsigned long value; /* the pc entry point */
|
||||
unsigned long svalue; /* entry point aligned to histograms */
|
||||
double time; /* ticks in this routine */
|
||||
double childtime; /* cumulative ticks in children */
|
||||
long ncall; /* how many times called */
|
||||
long selfcalls; /* how many calls to self */
|
||||
double propfraction; /* what % of time propagates */
|
||||
double propself; /* how much self time propagates */
|
||||
double propchild; /* how much child time propagates */
|
||||
bool printflag; /* should this be printed? */
|
||||
int index; /* index in the graph list */
|
||||
int toporder; /* graph call chain top-sort order */
|
||||
int cycleno; /* internal number of cycle on */
|
||||
struct nl *cyclehead; /* pointer to head of cycle */
|
||||
struct nl *cnext; /* pointer to next member of cycle */
|
||||
arctype *parents; /* list of caller arcs */
|
||||
arctype *children; /* list of callee arcs */
|
||||
};
|
||||
typedef struct nl nltype;
|
||||
extern bool first_output; /* no output so far? */
|
||||
|
||||
nltype *nl; /* the whole namelist */
|
||||
nltype *npe; /* the virtual end of the namelist */
|
||||
int nname; /* the number of function names */
|
||||
extern void done PARAMS((int status));
|
||||
|
||||
/*
|
||||
* flag which marks a nl entry as topologically ``busy''
|
||||
* flag which marks a nl entry as topologically ``not_numbered''
|
||||
*/
|
||||
#define DFN_BUSY -1
|
||||
#define DFN_NAN 0
|
||||
|
||||
/*
|
||||
* namelist entries for cycle headers.
|
||||
* the number of discovered cycles.
|
||||
*/
|
||||
nltype *cyclenl; /* cycle header namelist */
|
||||
int ncycle; /* number of cycles discovered */
|
||||
|
||||
/*
|
||||
* The header on the gmon.out file.
|
||||
* gmon.out consists of one of these headers,
|
||||
* and then an array of ncnt samples
|
||||
* representing the discretized program counter values.
|
||||
* this should be a struct phdr, but since everything is done
|
||||
* as UNITs, this is in UNITs too.
|
||||
*/
|
||||
struct hdr {
|
||||
UNIT *lowpc;
|
||||
UNIT *highpc;
|
||||
int ncnt;
|
||||
};
|
||||
|
||||
|
||||
struct rawhdr {
|
||||
char lowpc[4];
|
||||
char highpc[4];
|
||||
char ncnt[4];
|
||||
};
|
||||
|
||||
struct hdr h;
|
||||
|
||||
int debug;
|
||||
|
||||
/*
|
||||
* Each discretized pc sample has
|
||||
* a count of the number of samples in its range
|
||||
*/
|
||||
int *samples;
|
||||
|
||||
unsigned long s_lowpc; /* lowpc from the profile file */
|
||||
unsigned long s_highpc; /* highpc from the profile file */
|
||||
unsigned lowpc, highpc; /* range profiled, in UNIT's */
|
||||
unsigned sampbytes; /* number of bytes of samples */
|
||||
int nsamples; /* number of samples */
|
||||
double actime; /* accumulated time thus far for putprofline */
|
||||
double totime; /* total time for all routines */
|
||||
double printtime; /* total of time being printed */
|
||||
double scale; /* scale factor converting samples to pc
|
||||
values: each sample covers scale bytes */
|
||||
char *strtab; /* string table in core */
|
||||
off_t ssiz; /* size of the string table */
|
||||
unsigned char *textspace; /* text space of a.out in core */
|
||||
|
||||
/*
|
||||
* option flags, from a to z.
|
||||
*/
|
||||
bool aflag; /* suppress static functions */
|
||||
bool bflag; /* blurbs, too */
|
||||
bool cflag; /* discovered call graph, too */
|
||||
bool dflag; /* debugging options */
|
||||
bool eflag; /* specific functions excluded */
|
||||
bool Eflag; /* functions excluded with time */
|
||||
bool fflag; /* specific functions requested */
|
||||
bool Fflag; /* functions requested with time */
|
||||
bool kflag; /* arcs to be deleted */
|
||||
bool sflag; /* sum multiple gmon.out files */
|
||||
bool zflag; /* zero time/called functions, too */
|
||||
|
||||
/*
|
||||
* structure for various string lists
|
||||
*/
|
||||
struct stringlist {
|
||||
struct stringlist *next;
|
||||
char *string;
|
||||
};
|
||||
extern struct stringlist *elist;
|
||||
extern struct stringlist *Elist;
|
||||
extern struct stringlist *flist;
|
||||
extern struct stringlist *Flist;
|
||||
extern struct stringlist *kfromlist;
|
||||
extern struct stringlist *ktolist;
|
||||
|
||||
/*
|
||||
* function declarations
|
||||
*/
|
||||
/*
|
||||
addarc();
|
||||
*/
|
||||
int arccmp();
|
||||
arctype *arclookup();
|
||||
/*
|
||||
asgnsamples();
|
||||
printblurb();
|
||||
cyclelink();
|
||||
dfn();
|
||||
*/
|
||||
bool dfn_busy();
|
||||
/*
|
||||
dfn_findcycle();
|
||||
*/
|
||||
bool dfn_numbered();
|
||||
/*
|
||||
dfn_post_visit();
|
||||
dfn_pre_visit();
|
||||
dfn_self_cycle();
|
||||
*/
|
||||
nltype **doarcs();
|
||||
/*
|
||||
done();
|
||||
findcalls();
|
||||
flatprofheader();
|
||||
flatprofline();
|
||||
*/
|
||||
bool funcsymbol();
|
||||
/*
|
||||
getnfile();
|
||||
getpfile();
|
||||
getstrtab();
|
||||
getsymtab();
|
||||
gettextspace();
|
||||
gprofheader();
|
||||
gprofline();
|
||||
main();
|
||||
*/
|
||||
unsigned long max();
|
||||
int membercmp();
|
||||
unsigned long min();
|
||||
nltype *nllookup();
|
||||
FILE *openpfile();
|
||||
/*
|
||||
printchildren();
|
||||
printcycle();
|
||||
printgprof();
|
||||
printmembers();
|
||||
printname();
|
||||
printparents();
|
||||
printprof();
|
||||
readsamples();
|
||||
*/
|
||||
int printnameonly();
|
||||
unsigned long reladdr();
|
||||
/*
|
||||
sortchildren();
|
||||
sortmembers();
|
||||
sortparents();
|
||||
tally();
|
||||
timecmp();
|
||||
topcmp();
|
||||
*/
|
||||
int totalcmp();
|
||||
/*
|
||||
valcmp();
|
||||
*/
|
||||
|
||||
#define LESSTHAN -1
|
||||
#define EQUALTO 0
|
||||
#define GREATERTHAN 1
|
||||
|
||||
#define DFNDEBUG 1
|
||||
#define CYCLEDEBUG 2
|
||||
#define ARCDEBUG 4
|
||||
#define TALLYDEBUG 8
|
||||
#define TIMEDEBUG 16
|
||||
#define SAMPLEDEBUG 32
|
||||
#define AOUTDEBUG 64
|
||||
#define CALLDEBUG 128
|
||||
#define LOOKUPDEBUG 256
|
||||
#define PROPDEBUG 512
|
||||
#define ANYDEBUG 1024
|
||||
#endif /* gprof_h */
|
||||
|
|
|
@ -16,23 +16,15 @@
|
|||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)hertz.c 5.4 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/time.h>
|
||||
#include "hertz.h"
|
||||
|
||||
/*
|
||||
* discover the tick frequency of the machine
|
||||
* if something goes wrong, we return 0, an impossible hertz.
|
||||
*/
|
||||
#define HZ_WRONG 0
|
||||
|
||||
#ifdef __MSDOS__
|
||||
# define HERTZ 18
|
||||
#endif
|
||||
|
||||
int
|
||||
hertz()
|
||||
{
|
||||
#ifdef HERTZ
|
||||
|
@ -46,8 +38,9 @@ hertz()
|
|||
tim.it_value.tv_usec = 0;
|
||||
setitimer(ITIMER_REAL, &tim, 0);
|
||||
setitimer(ITIMER_REAL, 0, &tim);
|
||||
if (tim.it_interval.tv_usec < 2)
|
||||
return(HZ_WRONG);
|
||||
return (1000000 / tim.it_interval.tv_usec);
|
||||
if (tim.it_interval.tv_usec < 2) {
|
||||
return HZ_WRONG;
|
||||
} /* if */
|
||||
return 1000000 / tim.it_interval.tv_usec;
|
||||
#endif
|
||||
}
|
||||
} /* hertz */
|
||||
|
|
14
gprof/hertz.h
Normal file
14
gprof/hertz.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef hertz_h
|
||||
#define hertz_h
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
#define HZ_WRONG 0 /* impossible clock frequency */
|
||||
|
||||
/*
|
||||
* Discover the tick frequency of the machine if something goes wrong,
|
||||
* we return HZ_WRONG, an impossible sampling frequency.
|
||||
*/
|
||||
extern int hertz PARAMS((void));
|
||||
|
||||
#endif /* hertz_h */
|
507
gprof/hist.c
Normal file
507
gprof/hist.c
Normal file
|
@ -0,0 +1,507 @@
|
|||
/*
|
||||
* Histogram related operations.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "libiberty.h"
|
||||
#include "gprof.h"
|
||||
#include "core.h"
|
||||
#include "gmon_io.h"
|
||||
#include "gmon_out.h"
|
||||
#include "hist.h"
|
||||
#include "symtab.h"
|
||||
#include "sym_ids.h"
|
||||
#include "utils.h"
|
||||
|
||||
/* declarations of automatically generated functions to output blurbs: */
|
||||
extern void flat_blurb PARAMS((FILE *fp));
|
||||
|
||||
bfd_vma s_lowpc; /* lowest address in .text */
|
||||
bfd_vma s_highpc = 0; /* highest address in .text */
|
||||
bfd_vma lowpc, highpc; /* same, but expressed in UNITs */
|
||||
int hist_num_bins = 0; /* number of histogram samples */
|
||||
int *hist_sample = 0; /* histogram samples (shorts in the file!) */
|
||||
double hist_scale;
|
||||
char hist_dimension[sizeof(((struct gmon_hist_hdr*)0)->dimen) + 1] =
|
||||
"seconds";
|
||||
char hist_dimension_abbrev = 's';
|
||||
|
||||
static double accum_time; /* accumulated time so far for print_line() */
|
||||
static double total_time; /* total time for all routines */
|
||||
/*
|
||||
* Table of SI prefixes for powers of 10 (used to automatically
|
||||
* scale some of the values in the flat profile).
|
||||
*/
|
||||
const struct {
|
||||
char prefix;
|
||||
double scale;
|
||||
} SItab[] = {
|
||||
{'T', 1e-12}, /* tera */
|
||||
{'G', 1e-09}, /* giga */
|
||||
{'M', 1e-06}, /* mega */
|
||||
{'K', 1e-03}, /* kilo */
|
||||
{' ', 1e-00},
|
||||
{'m', 1e+03}, /* milli */
|
||||
{'u', 1e+06}, /* micro */
|
||||
{'n', 1e+09}, /* nano */
|
||||
{'p', 1e+12}, /* pico */
|
||||
{'f', 1e+15}, /* femto */
|
||||
{'a', 1e+18}, /* ato */
|
||||
};
|
||||
|
||||
/*
|
||||
* Read the histogram from file IFP. FILENAME is the name of IFP and
|
||||
* is provided for formatting error messages only.
|
||||
*/
|
||||
void
|
||||
DEFUN(hist_read_rec, (ifp, filename), FILE *ifp AND const char *filename)
|
||||
{
|
||||
struct gmon_hist_hdr hdr;
|
||||
bfd_vma n_lowpc, n_highpc;
|
||||
int i, ncnt, profrate;
|
||||
UNIT count;
|
||||
|
||||
if (fread(&hdr, sizeof(hdr), 1, ifp) != 1) {
|
||||
fprintf(stderr, "%s: %s: unexpected end of file\n",
|
||||
whoami, filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
n_lowpc = (bfd_vma) get_vma(core_bfd, (bfd_byte *) hdr.low_pc);
|
||||
n_highpc = (bfd_vma) get_vma(core_bfd, (bfd_byte *) hdr.high_pc);
|
||||
ncnt = bfd_get_32(core_bfd, (bfd_byte *) hdr.hist_size);
|
||||
profrate = bfd_get_32(core_bfd, (bfd_byte *) hdr.prof_rate);
|
||||
strncpy(hist_dimension, hdr.dimen, sizeof(hdr.dimen));
|
||||
hist_dimension[sizeof(hdr.dimen)] = '\0';
|
||||
hist_dimension_abbrev = hdr.dimen_abbrev;
|
||||
|
||||
if (!s_highpc) {
|
||||
|
||||
/* this is the first histogram record: */
|
||||
|
||||
s_lowpc = n_lowpc;
|
||||
s_highpc = n_highpc;
|
||||
lowpc = (bfd_vma) n_lowpc / sizeof(UNIT);
|
||||
highpc = (bfd_vma) n_highpc / sizeof(UNIT);
|
||||
hist_num_bins = ncnt;
|
||||
hz = profrate;
|
||||
} /* if */
|
||||
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf("[hist_read_rec] n_lowpc 0x%lx n_highpc 0x%lx ncnt %d\n",
|
||||
n_lowpc, n_highpc, ncnt);
|
||||
printf("[hist_read_rec] s_lowpc 0x%lx s_highpc 0x%lx nsamples %d\n",
|
||||
s_lowpc, s_highpc, hist_num_bins);
|
||||
printf("[hist_read_rec] lowpc 0x%lx highpc 0x%lx\n",
|
||||
lowpc, highpc));
|
||||
|
||||
if (n_lowpc != s_lowpc || n_highpc != s_highpc
|
||||
|| ncnt != hist_num_bins || hz != profrate)
|
||||
{
|
||||
fprintf(stderr, "%s: `%s' is incompatible with first gmon file\n",
|
||||
whoami, filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
if (!hist_sample) {
|
||||
hist_sample = (int*)xmalloc(hist_num_bins * sizeof(hist_sample[0]));
|
||||
memset(hist_sample, 0, hist_num_bins * sizeof(hist_sample[0]));
|
||||
} /* if */
|
||||
|
||||
for (i = 0; i < hist_num_bins; ++i) {
|
||||
if (fread(&count[0], sizeof(count), 1, ifp) != 1) {
|
||||
fprintf(stderr,
|
||||
"%s: %s: unexpected EOF after reading %d of %d samples\n",
|
||||
whoami, filename, i, hist_num_bins);
|
||||
done(1);
|
||||
} /* if */
|
||||
hist_sample[i] += bfd_get_16(core_bfd, (bfd_byte*) &count[0]);
|
||||
} /* for */
|
||||
} /* hist_read_rec */
|
||||
|
||||
|
||||
/*
|
||||
* Write execution histogram to file OFP. FILENAME is the name
|
||||
* of OFP and is provided for formatting error-messages only.
|
||||
*/
|
||||
void
|
||||
DEFUN(hist_write_hist, (ofp, filename), FILE *ofp AND const char *filename)
|
||||
{
|
||||
struct gmon_hist_hdr hdr;
|
||||
unsigned char tag;
|
||||
UNIT count;
|
||||
int i;
|
||||
|
||||
/* write header: */
|
||||
|
||||
tag = GMON_TAG_TIME_HIST;
|
||||
put_vma(core_bfd, s_lowpc, (bfd_byte*) hdr.low_pc);
|
||||
put_vma(core_bfd, s_highpc, (bfd_byte*) hdr.high_pc);
|
||||
bfd_put_32(core_bfd, hist_num_bins, (bfd_byte*) hdr.hist_size);
|
||||
bfd_put_32(core_bfd, hz, (bfd_byte*) hdr.prof_rate);
|
||||
strncpy(hdr.dimen, hist_dimension, sizeof(hdr.dimen));
|
||||
hdr.dimen_abbrev = hist_dimension_abbrev;
|
||||
|
||||
if (fwrite(&tag, sizeof(tag), 1, ofp) != 1
|
||||
|| fwrite(&hdr, sizeof(hdr), 1, ofp) != 1)
|
||||
{
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
|
||||
for (i = 0; i < hist_num_bins; ++i) {
|
||||
bfd_put_16(core_bfd, hist_sample[i], (bfd_byte*) &count[0]);
|
||||
if (fwrite(&count[0], sizeof(count), 1, ofp) != 1) {
|
||||
perror(filename);
|
||||
done(1);
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* hist_write_hist */
|
||||
|
||||
|
||||
/*
|
||||
* Calculate scaled entry point addresses (to save time in
|
||||
* hist_assign_samples), and, on architectures that have procedure
|
||||
* entry masks at the start of a function, possibly push the scaled
|
||||
* entry points over the procedure entry mask, if it turns out that
|
||||
* the entry point is in one bin and the code for a routine is in the
|
||||
* next bin.
|
||||
*/
|
||||
static void
|
||||
DEFUN_VOID(scale_and_align_entries)
|
||||
{
|
||||
Sym *sym;
|
||||
#if OFFSET_TO_CODE > 0
|
||||
bfd_vma bin_of_entry;
|
||||
bfd_vma bin_of_code;
|
||||
#endif
|
||||
|
||||
for (sym = symtab.base; sym < symtab.limit; sym++) {
|
||||
sym->hist.scaled_addr = sym->addr / sizeof(UNIT);
|
||||
#if OFFSET_TO_CODE > 0
|
||||
bin_of_entry = (sym->hist.scaled_addr - lowpc) / hist_scale;
|
||||
bin_of_code = (sym->hist.scaled_addr + UNITS_TO_CODE - lowpc) / hist_scale;
|
||||
if (bin_of_entry < bin_of_code) {
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf("[scale_and_align_entries] pushing 0x%lx to 0x%lx\n",
|
||||
sym->hist.scaled_addr, sym->aligned_addr + UNITS_TO_CODE));
|
||||
sym->aligned_addr += UNITS_TO_CODE;
|
||||
} /* if */
|
||||
#endif /* OFFSET_TO_CODE > 0 */
|
||||
} /* for */
|
||||
} /* scale_and_align_entries */
|
||||
|
||||
|
||||
/*
|
||||
* Assign samples to the symbol to which they belong.
|
||||
*
|
||||
* Histogram bin I covers some address range [BIN_LOWPC,BIN_HIGH_PC)
|
||||
* which may overlap one more symbol address ranges. If a symbol
|
||||
* overlaps with the bin's address range by O percent, then O percent
|
||||
* of the bin's count is credited to that symbol.
|
||||
*
|
||||
* There are three cases as to where BIN_LOW_PC and BIN_HIGH_PC can be
|
||||
* with respect to the symbol's address range [SYM_LOW_PC,
|
||||
* SYM_HIGH_PC) as shown in the following diagram. OVERLAP computes
|
||||
* the distance (in UNITs) between the arrows, the fraction of the
|
||||
* sample that is to be credited to the symbol which starts at
|
||||
* SYM_LOW_PC.
|
||||
*
|
||||
* sym_low_pc sym_high_pc
|
||||
* | |
|
||||
* v v
|
||||
*
|
||||
* +-----------------------------------------------+
|
||||
* | |
|
||||
* | ->| |<- ->| |<- ->| |<- |
|
||||
* | | | | | |
|
||||
* +---------+ +---------+ +---------+
|
||||
*
|
||||
* ^ ^ ^ ^ ^ ^
|
||||
* | | | | | |
|
||||
* bin_low_pc bin_high_pc bin_low_pc bin_high_pc bin_low_pc bin_high_pc
|
||||
*
|
||||
* For the VAX we assert that samples will never fall in the first two
|
||||
* bytes of any routine, since that is the entry mask, thus we call
|
||||
* scale_and_align_entries() to adjust the entry points if the entry
|
||||
* mask falls in one bin but the code for the routine doesn't start
|
||||
* until the next bin. In conjunction with the alignment of routine
|
||||
* addresses, this should allow us to have only one sample for every
|
||||
* four bytes of text space and never have any overlap (the two end
|
||||
* cases, above).
|
||||
*/
|
||||
void
|
||||
DEFUN_VOID(hist_assign_samples)
|
||||
{
|
||||
bfd_vma bin_low_pc, bin_high_pc;
|
||||
bfd_vma sym_low_pc, sym_high_pc;
|
||||
bfd_vma overlap, addr;
|
||||
int bin_count, i, j;
|
||||
double time, credit;
|
||||
|
||||
/* read samples and assign to symbols: */
|
||||
hist_scale = highpc - lowpc;
|
||||
hist_scale /= hist_num_bins;
|
||||
scale_and_align_entries();
|
||||
|
||||
/* iterate over all sample bins: */
|
||||
|
||||
for (i = 0, j = 1; i < hist_num_bins; ++i) {
|
||||
bin_count = hist_sample[i];
|
||||
if (!bin_count) {
|
||||
continue;
|
||||
} /* if */
|
||||
bin_low_pc = lowpc + (bfd_vma)(hist_scale * i);
|
||||
bin_high_pc = lowpc + (bfd_vma)(hist_scale * (i + 1));
|
||||
time = bin_count;
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf(
|
||||
"[assign_samples] bin_low_pc=0x%lx, bin_high_pc=0x%lx, bin_count=%d\n",
|
||||
sizeof(UNIT) * bin_low_pc, sizeof(UNIT) * bin_high_pc,
|
||||
bin_count));
|
||||
total_time += time;
|
||||
|
||||
/* credit all symbols that are covered by bin I: */
|
||||
|
||||
for (j = j - 1; j < symtab.len; ++j) {
|
||||
sym_low_pc = symtab.base[j].hist.scaled_addr;
|
||||
sym_high_pc = symtab.base[j+1].hist.scaled_addr;
|
||||
/*
|
||||
* If high end of bin is below entry address, go for next
|
||||
* bin:
|
||||
*/
|
||||
if (bin_high_pc < sym_low_pc) {
|
||||
break;
|
||||
} /* if */
|
||||
/*
|
||||
* If low end of bin is above high end of symbol, go for
|
||||
* next symbol.
|
||||
*/
|
||||
if (bin_low_pc >= sym_high_pc) {
|
||||
continue;
|
||||
} /* if */
|
||||
overlap =
|
||||
MIN(bin_high_pc, sym_high_pc) - MAX(bin_low_pc, sym_low_pc);
|
||||
if (overlap > 0) {
|
||||
DBG(SAMPLEDEBUG,
|
||||
printf(
|
||||
"[assign_samples] [0x%lx,0x%lx) %s gets %f ticks %ld overlap\n",
|
||||
symtab.base[j].addr, sizeof(UNIT) * sym_high_pc,
|
||||
symtab.base[j].name, overlap * time / hist_scale,
|
||||
overlap));
|
||||
addr = symtab.base[j].addr;
|
||||
credit = overlap * time / hist_scale;
|
||||
/*
|
||||
* Credit symbol if it appears in INCL_FLAT or that
|
||||
* table is empty and it does not appear it in
|
||||
* EXCL_FLAT.
|
||||
*/
|
||||
if (sym_lookup(&syms[INCL_FLAT], addr)
|
||||
|| (syms[INCL_FLAT].len == 0
|
||||
&& !sym_lookup(&syms[EXCL_FLAT], addr)))
|
||||
{
|
||||
symtab.base[j].hist.time += credit;
|
||||
} else {
|
||||
total_time -= credit;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* for */
|
||||
DBG(SAMPLEDEBUG, printf("[assign_samples] total_time %f\n",
|
||||
total_time));
|
||||
} /* hist_assign_samples */
|
||||
|
||||
|
||||
/*
|
||||
* Print header for flag histogram profile:
|
||||
*/
|
||||
static void
|
||||
DEFUN(print_header, (prefix), const char prefix)
|
||||
{
|
||||
char unit[64];
|
||||
|
||||
sprintf(unit, "%c%c/call", prefix, hist_dimension_abbrev);
|
||||
|
||||
if (bsd_style_output) {
|
||||
printf("\ngranularity: each sample hit covers %ld byte(s)",
|
||||
(long) hist_scale * sizeof(UNIT));
|
||||
if (total_time > 0.0) {
|
||||
printf(" for %.2f%% of %.2f %s\n\n",
|
||||
100.0/total_time, total_time/hz, hist_dimension);
|
||||
} /* if */
|
||||
} else {
|
||||
printf("\nEach sample counts as %g %s.\n", 1.0 / hz, hist_dimension);
|
||||
} /* if */
|
||||
|
||||
if (total_time <= 0.0) {
|
||||
printf(" no time accumulated\n\n");
|
||||
/* this doesn't hurt since all the numerators will be zero: */
|
||||
total_time = 1.0;
|
||||
} /* if */
|
||||
|
||||
printf("%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n",
|
||||
"% ", "cumulative", "self ", "", "self ", "total ", "");
|
||||
printf("%5.5s %9.9s %8.8s %8.8s %8.8s %8.8s %-8.8s\n",
|
||||
"time", hist_dimension, hist_dimension, "calls", unit, unit,
|
||||
"name");
|
||||
} /* print_header */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN(print_line, (sym, scale), Sym *sym AND double scale)
|
||||
{
|
||||
if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0) {
|
||||
return;
|
||||
} /* if */
|
||||
|
||||
accum_time += sym->hist.time;
|
||||
if (bsd_style_output) {
|
||||
printf("%5.1f %10.2f %8.2f",
|
||||
total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
|
||||
accum_time / hz, sym->hist.time / hz);
|
||||
} else {
|
||||
printf("%6.2f %9.2f %8.2f",
|
||||
total_time > 0.0 ? 100 * sym->hist.time / total_time : 0.0,
|
||||
accum_time / hz, sym->hist.time / hz);
|
||||
} /* if */
|
||||
if (sym->ncalls) {
|
||||
printf(" %8d %8.2f %8.2f ",
|
||||
sym->ncalls, scale*sym->hist.time/hz/sym->ncalls,
|
||||
scale*(sym->hist.time + sym->cg.child_time)/hz/sym->ncalls);
|
||||
} else {
|
||||
printf(" %8.8s %8.8s %8.8s ", "", "", "");
|
||||
} /* if */
|
||||
if (bsd_style_output) {
|
||||
print_name(sym);
|
||||
} else {
|
||||
print_name_only(sym);
|
||||
} /* if */
|
||||
printf("\n");
|
||||
} /* print_line */
|
||||
|
||||
|
||||
/*
|
||||
* Compare LP and RP. The primary comparison key is execution time,
|
||||
* the secondary is number of invocation, and the tertiary is the
|
||||
* lexicographic order of the function names.
|
||||
*/
|
||||
static int
|
||||
DEFUN(cmp_time, (lp, rp), const PTR lp AND const PTR rp)
|
||||
{
|
||||
const Sym *left = *(const Sym **)lp;
|
||||
const Sym *right = *(const Sym **)rp;
|
||||
double time_diff;
|
||||
long call_diff;
|
||||
|
||||
time_diff = right->hist.time - left->hist.time;
|
||||
if (time_diff > 0.0) {
|
||||
return 1;
|
||||
} /* if */
|
||||
if (time_diff < 0.0) {
|
||||
return -1;
|
||||
} /* if */
|
||||
|
||||
call_diff = right->ncalls - left->ncalls;
|
||||
if (call_diff > 0) {
|
||||
return 1;
|
||||
} /* if */
|
||||
if (call_diff < 0) {
|
||||
return -1;
|
||||
} /* if */
|
||||
|
||||
return strcmp(left->name, right->name);
|
||||
} /* cmp_time */
|
||||
|
||||
|
||||
/*
|
||||
* Print the flat histogram profile.
|
||||
*/
|
||||
void
|
||||
DEFUN_VOID(hist_print)
|
||||
{
|
||||
Sym **time_sorted_syms, *top_dog, *sym;
|
||||
int index, log_scale;
|
||||
double top_time, time;
|
||||
bfd_vma addr;
|
||||
|
||||
if (first_output) {
|
||||
first_output = FALSE;
|
||||
} else {
|
||||
printf("\f\n");
|
||||
} /* if */
|
||||
|
||||
accum_time = 0.0;
|
||||
if (bsd_style_output) {
|
||||
if (print_descriptions) {
|
||||
printf("\n\n\nflat profile:\n");
|
||||
flat_blurb(stdout);
|
||||
} /* if */
|
||||
} else {
|
||||
printf ("Flat profile:\n");
|
||||
} /* if */
|
||||
/*
|
||||
* Sort the symbol table by time (call-count and name as secondary
|
||||
* and tertiary keys):
|
||||
*/
|
||||
time_sorted_syms = (Sym**)xmalloc(symtab.len * sizeof(Sym*));
|
||||
for (index = 0; index < symtab.len; ++index) {
|
||||
time_sorted_syms[index] = &symtab.base[index];
|
||||
} /* for */
|
||||
qsort(time_sorted_syms, symtab.len, sizeof(Sym *), cmp_time);
|
||||
|
||||
if (bsd_style_output) {
|
||||
log_scale = 5; /* milli-seconds is BSD-default */
|
||||
} else {
|
||||
/*
|
||||
* Search for symbol with highest per-call execution time and
|
||||
* scale accordingly:
|
||||
*/
|
||||
log_scale = 0;
|
||||
top_dog = 0;
|
||||
top_time = 0.0;
|
||||
for (index = 0; index < symtab.len; ++index) {
|
||||
sym = time_sorted_syms[index];
|
||||
if (sym->ncalls) {
|
||||
time = (sym->hist.time + sym->cg.child_time) / sym->ncalls;
|
||||
if (time > top_time) {
|
||||
top_dog = sym;
|
||||
top_time = time;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* for */
|
||||
if (top_dog && top_dog->ncalls && top_time > 0.0) {
|
||||
top_time /= hz;
|
||||
while (SItab[log_scale].scale * top_time < 1000.0
|
||||
&& log_scale < sizeof(SItab)/sizeof(SItab[0]) - 1)
|
||||
{
|
||||
++log_scale;
|
||||
} /* while */
|
||||
} /* if */
|
||||
} /* if */
|
||||
|
||||
/*
|
||||
* For now, the dimension is always seconds. In the future, we
|
||||
* may also want to support other (pseudo-)dimensions (such as
|
||||
* I-cache misses etc.).
|
||||
*/
|
||||
print_header(SItab[log_scale].prefix);
|
||||
for (index = 0; index < symtab.len; ++index) {
|
||||
addr = time_sorted_syms[index]->addr;
|
||||
/*
|
||||
* Print symbol if its in INCL_FLAT table or that table
|
||||
* is empty and the symbol is not in EXCL_FLAT.
|
||||
*/
|
||||
if (sym_lookup(&syms[INCL_FLAT], addr)
|
||||
|| (syms[INCL_FLAT].len == 0
|
||||
&& !sym_lookup(&syms[EXCL_FLAT], addr)))
|
||||
{
|
||||
print_line(time_sorted_syms[index], SItab[log_scale].scale);
|
||||
} /* if */
|
||||
} /* for */
|
||||
free(time_sorted_syms);
|
||||
|
||||
if (print_descriptions && !bsd_style_output) {
|
||||
flat_blurb(stdout);
|
||||
} /* if */
|
||||
} /* hist_print */
|
||||
|
||||
/*** end of hist.c ***/
|
23
gprof/hist.h
Normal file
23
gprof/hist.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef hist_h
|
||||
#define hist_h
|
||||
|
||||
#include "bfd.h"
|
||||
|
||||
extern bfd_vma s_lowpc; /* lowpc from the profile file */
|
||||
extern bfd_vma s_highpc; /* highpc from the profile file */
|
||||
extern bfd_vma lowpc, highpc; /* range profiled, in UNIT's */
|
||||
extern int hist_num_bins; /* number of histogram bins */
|
||||
extern int *hist_sample; /* code histogram */
|
||||
/*
|
||||
* Scale factor converting samples to pc values: each sample covers
|
||||
* HIST_SCALE bytes:
|
||||
*/
|
||||
extern double hist_scale;
|
||||
|
||||
|
||||
extern void hist_read_rec PARAMS((FILE *ifp, const char *filename));
|
||||
extern void hist_write_hist PARAMS((FILE *ofp, const char *filename));
|
||||
extern void hist_assign_samples PARAMS((void));
|
||||
extern void hist_print PARAMS((void));
|
||||
|
||||
#endif /* hist_h */
|
102
gprof/i386.c
102
gprof/i386.c
|
@ -16,61 +16,34 @@
|
|||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)tahoe.c 1.5 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "core.h"
|
||||
#include "hist.h"
|
||||
#include "symtab.h"
|
||||
|
||||
/*
|
||||
* a namelist entry to be the child of indirect callf
|
||||
*/
|
||||
nltype indirectchild = {
|
||||
"(*)" , /* the name */
|
||||
(unsigned long) 0 , /* the pc entry point */
|
||||
(unsigned long) 0 , /* entry point aligned to histogram */
|
||||
(double) 0.0 , /* ticks in this routine */
|
||||
(double) 0.0 , /* cumulative ticks in children */
|
||||
(long) 0 , /* how many times called */
|
||||
(long) 0 , /* how many calls to self */
|
||||
(double) 1.0 , /* propagation fraction */
|
||||
(double) 0.0 , /* self propagation time */
|
||||
(double) 0.0 , /* child propagation time */
|
||||
(bool) 0 , /* print flag */
|
||||
(int) 0 , /* index in the graph list */
|
||||
(int) 0 , /* graph call chain top-sort order */
|
||||
(int) 0 , /* internal number of cycle on */
|
||||
(struct nl *) &indirectchild , /* pointer to head of cycle */
|
||||
(struct nl *) 0 , /* pointer to next member of cycle */
|
||||
(arctype *) 0 , /* list of caller arcs */
|
||||
(arctype *) 0 /* list of callee arcs */
|
||||
};
|
||||
|
||||
#ifdef __STDC__
|
||||
int
|
||||
iscall (unsigned char *ip)
|
||||
#else
|
||||
int iscall(ip)
|
||||
unsigned char *ip;
|
||||
#endif /* __STDC__ */
|
||||
DEFUN(iscall, (ip), unsigned char *ip)
|
||||
{
|
||||
if (*ip == 0xeb || *ip == 0x9a)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
findcall( parentp , p_lowpc , p_highpc )
|
||||
nltype *parentp;
|
||||
unsigned long p_lowpc;
|
||||
unsigned long p_highpc;
|
||||
|
||||
void
|
||||
find_call(parent, p_lowpc, p_highpc)
|
||||
Sym *parent;
|
||||
bfd_vma p_lowpc;
|
||||
bfd_vma p_highpc;
|
||||
{
|
||||
unsigned char *instructp;
|
||||
long length;
|
||||
nltype *childp;
|
||||
unsigned long destpc;
|
||||
Sym *child;
|
||||
bfd_vma destpc;
|
||||
|
||||
if ( textspace == 0 ) {
|
||||
if (core_text_space == 0) {
|
||||
return;
|
||||
}
|
||||
if (p_lowpc < s_lowpc) {
|
||||
|
@ -79,44 +52,35 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
if (p_highpc > s_highpc) {
|
||||
p_highpc = s_highpc;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall] %s: 0x%x to 0x%x\n" ,
|
||||
parentp -> name , p_lowpc , p_highpc );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( instructp = textspace + p_lowpc ;
|
||||
instructp < textspace + p_highpc ;
|
||||
DBG(CALLDEBUG, printf("[findcall] %s: 0x%lx to 0x%lx\n",
|
||||
parent->name, p_lowpc, p_highpc));
|
||||
for ( instructp = (unsigned char*) core_text_space + p_lowpc ;
|
||||
instructp < (unsigned char*) core_text_space + p_highpc ;
|
||||
instructp += length) {
|
||||
length = 1;
|
||||
if (iscall (instructp)) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\t0x%x:callf" , instructp - textspace );
|
||||
}
|
||||
# endif DEBUG
|
||||
DBG(CALLDEBUG,
|
||||
printf("[findcall]\t0x%x:callf",
|
||||
instructp - (unsigned char*) core_text_space));
|
||||
length = 4;
|
||||
/*
|
||||
* regular pc relative addressing
|
||||
* check that this is the address of
|
||||
* a function.
|
||||
*/
|
||||
destpc = ( (unsigned long)instructp + 5 - (unsigned long) textspace);
|
||||
destpc = ((bfd_vma)instructp + 5 - (bfd_vma) core_text_space);
|
||||
if (destpc >= s_lowpc && destpc <= s_highpc) {
|
||||
childp = nllookup( destpc );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tdestpc 0x%x" , destpc );
|
||||
printf( " childp->name %s" , childp -> name );
|
||||
printf( " childp->value 0x%x\n" ,
|
||||
childp -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( childp -> value == destpc ) {
|
||||
child = sym_lookup(&symtab, destpc);
|
||||
DBG(CALLDEBUG,
|
||||
printf("[findcall]\tdestpc 0x%lx", destpc);
|
||||
printf(" child->name %s", child->name);
|
||||
printf(" child->addr 0x%lx\n", child->addr);
|
||||
);
|
||||
if (child->addr == destpc) {
|
||||
/*
|
||||
* a hit
|
||||
*/
|
||||
addarc( parentp , childp , (long) 0 );
|
||||
arc_add(parent, child, (long) 0);
|
||||
length += 4; /* constant lengths */
|
||||
continue;
|
||||
}
|
||||
|
@ -131,11 +95,7 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
/*
|
||||
* something funny going on.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tbut it's a botch\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
DBG(CALLDEBUG, printf("[findcall]\tbut it's a botch\n"));
|
||||
length = 1;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
* offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see asgnsamples for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_OF_CODE 0
|
||||
#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
|
||||
#define OFFSET_TO_CODE 0
|
||||
#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT))
|
||||
|
||||
#ifdef __MSDOS__
|
||||
#define FOPEN_RB "rb"
|
||||
|
|
140
gprof/lookup.c
140
gprof/lookup.c
|
@ -1,140 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)lookup.c 5.4 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* look up an address in a sorted-by-address namelist
|
||||
* this deals with misses by mapping them to the next lower
|
||||
* entry point.
|
||||
*/
|
||||
#ifdef DEBUG
|
||||
nltype *dbg_nllookup();
|
||||
#endif
|
||||
|
||||
nltype *
|
||||
nllookup( address )
|
||||
unsigned long address;
|
||||
{
|
||||
register long low;
|
||||
register long middle;
|
||||
register long high;
|
||||
# ifdef DEBUG
|
||||
register int probes;
|
||||
|
||||
probes = 0;
|
||||
# endif DEBUG
|
||||
for ( low = 0 , high = nname - 1 ; low != high ; ) {
|
||||
# ifdef DEBUG
|
||||
probes += 1;
|
||||
# endif DEBUG
|
||||
middle = ( high + low ) >> 1;
|
||||
if ( nl[ middle ].value <= address && nl[ middle+1 ].value > address ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & LOOKUPDEBUG ) {
|
||||
printf( "[nllookup] %d (%d) probes\n" , probes , nname-1 );
|
||||
}
|
||||
# endif DEBUG
|
||||
return &nl[ middle ];
|
||||
}
|
||||
if ( nl[ middle ].value > address ) {
|
||||
high = middle;
|
||||
} else {
|
||||
low = middle + 1;
|
||||
}
|
||||
}
|
||||
if(nl[middle+1].value <= address) {
|
||||
# ifdef DEBUG
|
||||
if (debug & LOOKUPDEBUG ) {
|
||||
printf("[nllookup] %d (%d) probes, fall off\n", probes, nname-1);
|
||||
}
|
||||
# endif
|
||||
return &nl[middle+1];
|
||||
}
|
||||
fprintf( stderr , "[nllookup] binary search fails???\n" );
|
||||
#ifdef DEBUG
|
||||
dbg_nllookup(address);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nltype *
|
||||
dbg_nllookup( address )
|
||||
unsigned long address;
|
||||
{
|
||||
register long low;
|
||||
register long middle;
|
||||
register long high;
|
||||
fprintf(stderr,"[nllookup] address 0x%x\n",address);
|
||||
|
||||
for ( low = 0 , high = nname - 1 ; low != high ; ) {
|
||||
|
||||
middle = ( high + low ) >> 1;
|
||||
fprintf(stderr, "[nllookup] low 0x%x middle 0x%x high 0x%x nl[m] 0x%x nl[m+1] 0x%x\n",
|
||||
low,middle,high,nl[middle].value,nl[middle+1].value);
|
||||
if ( nl[ middle ].value <= address && nl[ middle+1 ].value > address) {
|
||||
return &nl[ middle ];
|
||||
}
|
||||
if ( nl[ middle ].value > address ) {
|
||||
high = middle;
|
||||
} else {
|
||||
low = middle + 1;
|
||||
}
|
||||
}
|
||||
fprintf( stderr , "[nllookup] binary search fails???\n" );
|
||||
return 0;
|
||||
}
|
||||
#endif DEBUG
|
||||
|
||||
arctype *
|
||||
arclookup( parentp , childp )
|
||||
nltype *parentp;
|
||||
nltype *childp;
|
||||
{
|
||||
arctype *arcp;
|
||||
|
||||
if ( parentp == 0 || childp == 0 ) {
|
||||
printf( "[arclookup] parentp == 0 || childp == 0\n" );
|
||||
return 0;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & LOOKUPDEBUG ) {
|
||||
printf( "[arclookup] parent %s child %s\n" ,
|
||||
parentp -> name , childp -> name );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & LOOKUPDEBUG ) {
|
||||
printf( "[arclookup]\t arc_parent %s arc_child %s\n" ,
|
||||
arcp -> arc_parentp -> name ,
|
||||
arcp -> arc_childp -> name );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( arcp -> arc_childp == childp ) {
|
||||
return arcp;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,787 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)printgprof.c 5.7 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
#include <demangle.h>
|
||||
|
||||
printprof()
|
||||
{
|
||||
register nltype *np;
|
||||
nltype **sortednlp;
|
||||
int index, timecmp();
|
||||
|
||||
actime = 0.0;
|
||||
if ( bsd_style_output ) {
|
||||
printf( "\f\n" );
|
||||
if ( bflag) {
|
||||
printf( "\n\n\nflat profile:\n" );
|
||||
flat_blurb(stdout);
|
||||
}
|
||||
}
|
||||
else
|
||||
printf ("Flat profile:\n");
|
||||
flatprofheader();
|
||||
/*
|
||||
* Sort the symbol table in by time
|
||||
*/
|
||||
sortednlp = (nltype **) calloc( nname , sizeof(nltype *) );
|
||||
if ( sortednlp == (nltype **) 0 ) {
|
||||
fprintf( stderr , "[printprof] ran out of memory for time sorting\n" );
|
||||
}
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
sortednlp[ index ] = &nl[ index ];
|
||||
}
|
||||
qsort( sortednlp , nname , sizeof(nltype *) , timecmp );
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
np = sortednlp[ index ];
|
||||
flatprofline( np );
|
||||
}
|
||||
actime = 0.0;
|
||||
free( sortednlp );
|
||||
if ( bflag && !bsd_style_output ) {
|
||||
flat_blurb(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
timecmp( npp1 , npp2 )
|
||||
nltype **npp1, **npp2;
|
||||
{
|
||||
double timediff;
|
||||
long calldiff;
|
||||
|
||||
timediff = (*npp2) -> time - (*npp1) -> time;
|
||||
if ( timediff > 0.0 )
|
||||
return 1 ;
|
||||
if ( timediff < 0.0 )
|
||||
return -1;
|
||||
calldiff = (*npp2) -> ncall - (*npp1) -> ncall;
|
||||
if ( calldiff > 0 )
|
||||
return 1;
|
||||
if ( calldiff < 0 )
|
||||
return -1;
|
||||
return( strcmp( (*npp1) -> name , (*npp2) -> name ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* header for flatprofline
|
||||
*/
|
||||
flatprofheader()
|
||||
{
|
||||
|
||||
if (bsd_style_output) {
|
||||
printf( "\ngranularity: each sample hit covers %d byte(s)" ,
|
||||
(long) scale * sizeof(UNIT) );
|
||||
if (totime > 0.0)
|
||||
printf(" for %.2f%% of %.2f seconds\n\n", 100.0/totime, totime / hz);
|
||||
}
|
||||
else {
|
||||
printf( "\nEach sample counts as %g seconds.\n",
|
||||
1.0 / hz);
|
||||
}
|
||||
if (totime <= 0.0)
|
||||
{
|
||||
printf(" no time accumulated\n\n");
|
||||
/* This doesn't hurt since all the numerators will be zero. */
|
||||
totime = 1.0;
|
||||
}
|
||||
printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n" ,
|
||||
"% " , "cumulative" , "self " , "" , "self " , "total " , "" );
|
||||
printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n" ,
|
||||
"time" , "seconds " , "seconds" , "calls" ,
|
||||
"ms/call" , "ms/call" , "name" );
|
||||
}
|
||||
|
||||
flatprofline( np )
|
||||
register nltype *np;
|
||||
{
|
||||
|
||||
if ( zflag == 0 && np -> ncall == 0 && np -> time == 0 ) {
|
||||
return;
|
||||
}
|
||||
actime += np -> time;
|
||||
if (bsd_style_output)
|
||||
printf( "%5.1f %10.2f %8.2f" ,
|
||||
100 * np -> time / totime , actime / hz , np -> time / hz );
|
||||
else
|
||||
printf( "%6.2f %9.2f %8.2f" ,
|
||||
100 * np -> time / totime , actime / hz , np -> time / hz );
|
||||
if ( np -> ncall != 0 ) {
|
||||
printf( " %8d %8.2f %8.2f " , np -> ncall ,
|
||||
1000 * np -> time / hz / np -> ncall ,
|
||||
1000 * ( np -> time + np -> childtime ) / hz / np -> ncall );
|
||||
} else {
|
||||
printf( " %8.8s %8.8s %8.8s " , "" , "" , "" );
|
||||
}
|
||||
if (bsd_style_output)
|
||||
printname( np );
|
||||
else
|
||||
printnameonly( np );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
gprofheader()
|
||||
{
|
||||
|
||||
if (!bsd_style_output)
|
||||
if (bflag)
|
||||
printf ("\t\t Call graph (explanation follows)\n\n");
|
||||
else
|
||||
printf ("\t\t\tCall graph\n\n");
|
||||
printf( "\ngranularity: each sample hit covers %d byte(s)" ,
|
||||
(long) scale * sizeof(UNIT) );
|
||||
if ( printtime > 0.0 ) {
|
||||
printf( " for %.2f%% of %.2f seconds\n\n" ,
|
||||
100.0/printtime , printtime / hz );
|
||||
} else {
|
||||
printf( " no time propagated\n\n" );
|
||||
/*
|
||||
* this doesn't hurt, since all the numerators will be 0.0
|
||||
*/
|
||||
printtime = 1.0;
|
||||
}
|
||||
if (bsd_style_output) {
|
||||
printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n" ,
|
||||
"" , "" , "" , "" , "called" , "total" , "parents");
|
||||
printf( "%-6.6s %5.5s %7.7s %11.11s %7.7s+%-7.7s %-8.8s\t%5.5s\n" ,
|
||||
"index" , "%time" , "self" , "descendents" ,
|
||||
"called" , "self" , "name" , "index" );
|
||||
printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n" ,
|
||||
"" , "" , "" , "" , "called" , "total" , "children");
|
||||
printf( "\n" );
|
||||
} else {
|
||||
printf( "index %% time self children called name\n" );
|
||||
}
|
||||
}
|
||||
|
||||
gprofline( np )
|
||||
register nltype *np;
|
||||
{
|
||||
char kirkbuffer[ BUFSIZ ];
|
||||
|
||||
sprintf( kirkbuffer , "[%d]" , np -> index );
|
||||
printf(bsd_style_output
|
||||
? "%-6.6s %5.1f %7.2f %11.2f"
|
||||
: "%-6.6s %5.1f %7.2f %7.2f" ,
|
||||
kirkbuffer ,
|
||||
100 * ( np -> propself + np -> propchild ) / printtime ,
|
||||
np -> propself / hz ,
|
||||
np -> propchild / hz );
|
||||
if ( ( np -> ncall + np -> selfcalls ) != 0 ) {
|
||||
printf( " %7d" , np -> ncall );
|
||||
if ( np -> selfcalls != 0 ) {
|
||||
printf( "+%-7d " , np -> selfcalls );
|
||||
} else {
|
||||
printf( " %7.7s " , "" );
|
||||
}
|
||||
} else {
|
||||
printf( " %7.7s %7.7s " , "" , "" );
|
||||
}
|
||||
printname( np );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
printgprof(timesortnlp)
|
||||
nltype **timesortnlp;
|
||||
{
|
||||
int index;
|
||||
nltype *parentp;
|
||||
|
||||
/*
|
||||
* Print out the structured profiling list
|
||||
*/
|
||||
if ( bflag && bsd_style_output ) {
|
||||
bsd_callg_blurb(stdout);
|
||||
}
|
||||
gprofheader();
|
||||
for ( index = 0 ; index < nname + ncycle ; index ++ ) {
|
||||
parentp = timesortnlp[ index ];
|
||||
if ( zflag == 0 &&
|
||||
parentp -> ncall == 0 &&
|
||||
parentp -> selfcalls == 0 &&
|
||||
parentp -> propself == 0 &&
|
||||
parentp -> propchild == 0 ) {
|
||||
continue;
|
||||
}
|
||||
if ( ! parentp -> printflag ) {
|
||||
continue;
|
||||
}
|
||||
if ( parentp -> name == 0 && parentp -> cycleno != 0 ) {
|
||||
/*
|
||||
* cycle header
|
||||
*/
|
||||
printcycle( parentp );
|
||||
printmembers( parentp );
|
||||
} else {
|
||||
printparents( parentp );
|
||||
gprofline( parentp );
|
||||
printchildren( parentp );
|
||||
}
|
||||
if (bsd_style_output)
|
||||
printf( "\n" );
|
||||
printf( "-----------------------------------------------\n" );
|
||||
if (bsd_style_output)
|
||||
printf( "\n" );
|
||||
}
|
||||
free( timesortnlp );
|
||||
if ( bflag && !bsd_style_output) {
|
||||
fsf_callg_blurb(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sort by decreasing propagated time
|
||||
* if times are equal, but one is a cycle header,
|
||||
* say that's first (e.g. less, i.e. -1).
|
||||
* if one's name doesn't have an underscore and the other does,
|
||||
* say the one is first.
|
||||
* all else being equal, sort by names.
|
||||
*/
|
||||
int
|
||||
totalcmp( npp1 , npp2 )
|
||||
nltype **npp1;
|
||||
nltype **npp2;
|
||||
{
|
||||
register nltype *np1 = *npp1;
|
||||
register nltype *np2 = *npp2;
|
||||
double diff;
|
||||
|
||||
diff = ( np1 -> propself + np1 -> propchild )
|
||||
- ( np2 -> propself + np2 -> propchild );
|
||||
if ( diff < 0.0 )
|
||||
return 1;
|
||||
if ( diff > 0.0 )
|
||||
return -1;
|
||||
if ( np1 -> name == 0 && np1 -> cycleno != 0 )
|
||||
return -1;
|
||||
if ( np2 -> name == 0 && np2 -> cycleno != 0 )
|
||||
return 1;
|
||||
if ( np1 -> name == 0 )
|
||||
return -1;
|
||||
if ( np2 -> name == 0 )
|
||||
return 1;
|
||||
if ( *(np1 -> name) != '_' && *(np2 -> name) == '_' )
|
||||
return -1;
|
||||
if ( *(np1 -> name) == '_' && *(np2 -> name) != '_' )
|
||||
return 1;
|
||||
if ( np1 -> ncall > np2 -> ncall )
|
||||
return -1;
|
||||
if ( np1 -> ncall < np2 -> ncall )
|
||||
return 1;
|
||||
return strcmp( np1 -> name , np2 -> name );
|
||||
}
|
||||
|
||||
printparents( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
nltype *parentp;
|
||||
arctype *arcp;
|
||||
nltype *cycleheadp;
|
||||
|
||||
if ( childp -> cyclehead != 0 ) {
|
||||
cycleheadp = childp -> cyclehead;
|
||||
} else {
|
||||
cycleheadp = childp;
|
||||
}
|
||||
if ( childp -> parents == 0 ) {
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.7s %11.11s %7.7s %7.7s <spontaneous>\n"
|
||||
: "%6.6s %5.5s %7.7s %7.7s %7.7s %7.7s <spontaneous>\n" ,
|
||||
"" , "" , "" , "" , "" , "" );
|
||||
return;
|
||||
}
|
||||
sortparents( childp );
|
||||
for ( arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist ) {
|
||||
parentp = arcp -> arc_parentp;
|
||||
if ( childp == parentp ||
|
||||
( childp->cycleno != 0 && parentp->cycleno == childp->cycleno ) ) {
|
||||
/*
|
||||
* selfcall or call among siblings
|
||||
*/
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.7s %11.11s %7d %7.7s "
|
||||
: "%6.6s %5.5s %7.7s %7.7s %7d %7.7s " ,
|
||||
"" , "" , "" , "" ,
|
||||
arcp -> arc_count , "" );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
} else {
|
||||
/*
|
||||
* regular parent of child
|
||||
*/
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.2f %11.2f %7d/%-7d "
|
||||
: "%6.6s %5.5s %7.2f %7.2f %7d/%-7d ",
|
||||
"" , "" ,
|
||||
arcp -> arc_time / hz , arcp -> arc_childtime / hz ,
|
||||
arcp -> arc_count , cycleheadp -> ncall );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printchildren( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
nltype *childp;
|
||||
arctype *arcp;
|
||||
|
||||
sortchildren( parentp );
|
||||
arcp = parentp -> children;
|
||||
for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
|
||||
childp = arcp -> arc_childp;
|
||||
if ( childp == parentp ||
|
||||
( childp->cycleno != 0 && childp->cycleno == parentp->cycleno ) ) {
|
||||
/*
|
||||
* self call or call to sibling
|
||||
*/
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.7s %11.11s %7d %7.7s "
|
||||
: "%6.6s %5.5s %7.7s %7.7s %7d %7.7s " ,
|
||||
"" , "" , "" , "" , arcp -> arc_count , "" );
|
||||
printname( childp );
|
||||
printf( "\n" );
|
||||
} else {
|
||||
/*
|
||||
* regular child of parent
|
||||
*/
|
||||
printf(bsd_style_output
|
||||
? "%6.6s %5.5s %7.2f %11.2f %7d/%-7d "
|
||||
: "%6.6s %5.5s %7.2f %7.2f %7d/%-7d " ,
|
||||
"" , "" ,
|
||||
arcp -> arc_time / hz , arcp -> arc_childtime / hz ,
|
||||
arcp -> arc_count , childp -> cyclehead -> ncall );
|
||||
printname( childp );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Print name of symbol. Return number of characters printed. */
|
||||
|
||||
int
|
||||
printnameonly ( selfp )
|
||||
nltype *selfp;
|
||||
{
|
||||
int size = 0;
|
||||
CONST char *name = selfp->name;
|
||||
if (name != NULL) {
|
||||
char *demangled = NULL;
|
||||
if (!bsd_style_output) {
|
||||
if (name[0] == '_' && name[1] && discard_underscores)
|
||||
name++;
|
||||
demangled = cplus_demangle (name, DMGL_ANSI|DMGL_PARAMS);
|
||||
if (demangled)
|
||||
name = demangled;
|
||||
}
|
||||
printf( "%s" , name );
|
||||
size = strlen (name);
|
||||
if (demangled)
|
||||
free (demangled);
|
||||
#ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "{%d} " , selfp -> toporder );
|
||||
}
|
||||
if ( debug & PROPDEBUG ) {
|
||||
printf( "%5.2f%% " , selfp -> propfraction );
|
||||
}
|
||||
#endif DEBUG
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
printname( selfp )
|
||||
nltype *selfp;
|
||||
{
|
||||
printnameonly (selfp);
|
||||
|
||||
if ( selfp -> cycleno != 0 ) {
|
||||
printf( " <cycle %d>" , selfp -> cycleno );
|
||||
}
|
||||
if ( selfp -> index != 0 ) {
|
||||
if ( selfp -> printflag ) {
|
||||
printf( " [%d]" , selfp -> index );
|
||||
} else {
|
||||
printf( " (%d)" , selfp -> index );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sortchildren( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
arctype *arcp;
|
||||
arctype *detachedp;
|
||||
arctype sorted;
|
||||
arctype *prevp;
|
||||
|
||||
/*
|
||||
* unlink children from parent,
|
||||
* then insertion sort back on to sorted's children.
|
||||
* *arcp the arc you have detached and are inserting.
|
||||
* *detachedp the rest of the arcs to be sorted.
|
||||
* sorted arc list onto which you insertion sort.
|
||||
* *prevp arc before the arc you are comparing.
|
||||
*/
|
||||
sorted.arc_childlist = 0;
|
||||
for ( (arcp = parentp -> children)&&(detachedp = arcp -> arc_childlist);
|
||||
arcp ;
|
||||
(arcp = detachedp)&&(detachedp = detachedp -> arc_childlist)) {
|
||||
/*
|
||||
* consider *arcp as disconnected
|
||||
* insert it into sorted
|
||||
*/
|
||||
for ( prevp = &sorted ;
|
||||
prevp -> arc_childlist ;
|
||||
prevp = prevp -> arc_childlist ) {
|
||||
if ( arccmp( arcp , prevp -> arc_childlist ) != LESSTHAN ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
arcp -> arc_childlist = prevp -> arc_childlist;
|
||||
prevp -> arc_childlist = arcp;
|
||||
}
|
||||
/*
|
||||
* reattach sorted children to parent
|
||||
*/
|
||||
parentp -> children = sorted.arc_childlist;
|
||||
}
|
||||
|
||||
sortparents( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
arctype *arcp;
|
||||
arctype *detachedp;
|
||||
arctype sorted;
|
||||
arctype *prevp;
|
||||
|
||||
/*
|
||||
* unlink parents from child,
|
||||
* then insertion sort back on to sorted's parents.
|
||||
* *arcp the arc you have detached and are inserting.
|
||||
* *detachedp the rest of the arcs to be sorted.
|
||||
* sorted arc list onto which you insertion sort.
|
||||
* *prevp arc before the arc you are comparing.
|
||||
*/
|
||||
sorted.arc_parentlist = 0;
|
||||
for ( (arcp = childp -> parents)&&(detachedp = arcp -> arc_parentlist);
|
||||
arcp ;
|
||||
(arcp = detachedp)&&(detachedp = detachedp -> arc_parentlist)) {
|
||||
/*
|
||||
* consider *arcp as disconnected
|
||||
* insert it into sorted
|
||||
*/
|
||||
for ( prevp = &sorted ;
|
||||
prevp -> arc_parentlist ;
|
||||
prevp = prevp -> arc_parentlist ) {
|
||||
if ( arccmp( arcp , prevp -> arc_parentlist ) != GREATERTHAN ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
arcp -> arc_parentlist = prevp -> arc_parentlist;
|
||||
prevp -> arc_parentlist = arcp;
|
||||
}
|
||||
/*
|
||||
* reattach sorted arcs to child
|
||||
*/
|
||||
childp -> parents = sorted.arc_parentlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* print a cycle header
|
||||
*/
|
||||
printcycle( cyclep )
|
||||
nltype *cyclep;
|
||||
{
|
||||
char kirkbuffer[ BUFSIZ ];
|
||||
|
||||
sprintf( kirkbuffer , "[%d]" , cyclep -> index );
|
||||
printf( "%-6.6s %5.1f %7.2f %11.2f %7d" ,
|
||||
kirkbuffer ,
|
||||
100 * ( cyclep -> propself + cyclep -> propchild ) / printtime ,
|
||||
cyclep -> propself / hz ,
|
||||
cyclep -> propchild / hz ,
|
||||
cyclep -> ncall );
|
||||
if ( cyclep -> selfcalls != 0 ) {
|
||||
printf( "+%-7d" , cyclep -> selfcalls );
|
||||
} else {
|
||||
printf( " %7.7s" , "" );
|
||||
}
|
||||
printf( " <cycle %d as a whole>\t[%d]\n" ,
|
||||
cyclep -> cycleno , cyclep -> index );
|
||||
}
|
||||
|
||||
/*
|
||||
* print the members of a cycle
|
||||
*/
|
||||
printmembers( cyclep )
|
||||
nltype *cyclep;
|
||||
{
|
||||
nltype *memberp;
|
||||
|
||||
sortmembers( cyclep );
|
||||
for ( memberp = cyclep -> cnext ; memberp ; memberp = memberp -> cnext ) {
|
||||
printf( "%6.6s %5.5s %7.2f %11.2f %7d" ,
|
||||
"" , "" , memberp -> propself / hz , memberp -> propchild / hz ,
|
||||
memberp -> ncall );
|
||||
if ( memberp -> selfcalls != 0 ) {
|
||||
printf( "+%-7d" , memberp -> selfcalls );
|
||||
} else {
|
||||
printf( " %7.7s" , "" );
|
||||
}
|
||||
printf( " " );
|
||||
printname( memberp );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sort members of a cycle
|
||||
*/
|
||||
sortmembers( cyclep )
|
||||
nltype *cyclep;
|
||||
{
|
||||
nltype *todo;
|
||||
nltype *doing;
|
||||
nltype *prev;
|
||||
|
||||
/*
|
||||
* detach cycle members from cyclehead,
|
||||
* and insertion sort them back on.
|
||||
*/
|
||||
todo = cyclep -> cnext;
|
||||
cyclep -> cnext = 0;
|
||||
for ( (doing = todo)&&(todo = doing -> cnext);
|
||||
doing ;
|
||||
(doing = todo )&&(todo = doing -> cnext )){
|
||||
for ( prev = cyclep ; prev -> cnext ; prev = prev -> cnext ) {
|
||||
if ( membercmp( doing , prev -> cnext ) == GREATERTHAN ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
doing -> cnext = prev -> cnext;
|
||||
prev -> cnext = doing;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* major sort is on propself + propchild,
|
||||
* next is sort on ncalls + selfcalls.
|
||||
*/
|
||||
int
|
||||
membercmp( this , that )
|
||||
nltype *this;
|
||||
nltype *that;
|
||||
{
|
||||
double thistime = this -> propself + this -> propchild;
|
||||
double thattime = that -> propself + that -> propchild;
|
||||
long thiscalls = this -> ncall + this -> selfcalls;
|
||||
long thatcalls = that -> ncall + that -> selfcalls;
|
||||
|
||||
if ( thistime > thattime ) {
|
||||
return GREATERTHAN;
|
||||
}
|
||||
if ( thistime < thattime ) {
|
||||
return LESSTHAN;
|
||||
}
|
||||
if ( thiscalls > thatcalls ) {
|
||||
return GREATERTHAN;
|
||||
}
|
||||
if ( thiscalls < thatcalls ) {
|
||||
return LESSTHAN;
|
||||
}
|
||||
return EQUALTO;
|
||||
}
|
||||
/*
|
||||
* compare two arcs to/from the same child/parent.
|
||||
* - if one arc is a self arc, it's least.
|
||||
* - if one arc is within a cycle, it's less than.
|
||||
* - if both arcs are within a cycle, compare arc counts.
|
||||
* - if neither arc is within a cycle, compare with
|
||||
* arc_time + arc_childtime as major key
|
||||
* arc count as minor key
|
||||
*/
|
||||
int
|
||||
arccmp( thisp , thatp )
|
||||
arctype *thisp;
|
||||
arctype *thatp;
|
||||
{
|
||||
nltype *thisparentp = thisp -> arc_parentp;
|
||||
nltype *thischildp = thisp -> arc_childp;
|
||||
nltype *thatparentp = thatp -> arc_parentp;
|
||||
nltype *thatchildp = thatp -> arc_childp;
|
||||
double thistime;
|
||||
double thattime;
|
||||
|
||||
# ifdef DEBUG
|
||||
if ( debug & TIMEDEBUG ) {
|
||||
printf( "[arccmp] " );
|
||||
printname( thisparentp );
|
||||
printf( " calls " );
|
||||
printname ( thischildp );
|
||||
printf( " %f + %f %d/%d\n" ,
|
||||
thisp -> arc_time , thisp -> arc_childtime ,
|
||||
thisp -> arc_count , thischildp -> ncall );
|
||||
printf( "[arccmp] " );
|
||||
printname( thatparentp );
|
||||
printf( " calls " );
|
||||
printname( thatchildp );
|
||||
printf( " %f + %f %d/%d\n" ,
|
||||
thatp -> arc_time , thatp -> arc_childtime ,
|
||||
thatp -> arc_count , thatchildp -> ncall );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( thisparentp == thischildp ) {
|
||||
/* this is a self call */
|
||||
return LESSTHAN;
|
||||
}
|
||||
if ( thatparentp == thatchildp ) {
|
||||
/* that is a self call */
|
||||
return GREATERTHAN;
|
||||
}
|
||||
if ( thisparentp -> cycleno != 0 && thischildp -> cycleno != 0 &&
|
||||
thisparentp -> cycleno == thischildp -> cycleno ) {
|
||||
/* this is a call within a cycle */
|
||||
if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 &&
|
||||
thatparentp -> cycleno == thatchildp -> cycleno ) {
|
||||
/* that is a call within the cycle, too */
|
||||
if ( thisp -> arc_count < thatp -> arc_count ) {
|
||||
return LESSTHAN;
|
||||
}
|
||||
if ( thisp -> arc_count > thatp -> arc_count ) {
|
||||
return GREATERTHAN;
|
||||
}
|
||||
return EQUALTO;
|
||||
} else {
|
||||
/* that isn't a call within the cycle */
|
||||
return LESSTHAN;
|
||||
}
|
||||
} else {
|
||||
/* this isn't a call within a cycle */
|
||||
if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 &&
|
||||
thatparentp -> cycleno == thatchildp -> cycleno ) {
|
||||
/* that is a call within a cycle */
|
||||
return GREATERTHAN;
|
||||
} else {
|
||||
/* neither is a call within a cycle */
|
||||
thistime = thisp -> arc_time + thisp -> arc_childtime;
|
||||
thattime = thatp -> arc_time + thatp -> arc_childtime;
|
||||
if ( thistime < thattime )
|
||||
return LESSTHAN;
|
||||
if ( thistime > thattime )
|
||||
return GREATERTHAN;
|
||||
if ( thisp -> arc_count < thatp -> arc_count )
|
||||
return LESSTHAN;
|
||||
if ( thisp -> arc_count > thatp -> arc_count )
|
||||
return GREATERTHAN;
|
||||
return EQUALTO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
namecmp( npp1 , npp2 )
|
||||
nltype **npp1, **npp2;
|
||||
{
|
||||
return( strcmp( (*npp1) -> name , (*npp2) -> name ) );
|
||||
}
|
||||
|
||||
printindex()
|
||||
{
|
||||
nltype **namesortnlp;
|
||||
register nltype *nlp;
|
||||
int index, nnames, todo, i, j;
|
||||
char peterbuffer[20];
|
||||
|
||||
/*
|
||||
* Now, sort regular function name alphbetically
|
||||
* to create an index.
|
||||
*/
|
||||
namesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) );
|
||||
if ( namesortnlp == (nltype **) 0 ) {
|
||||
fprintf( stderr , "%s: ran out of memory for sorting\n" , whoami );
|
||||
}
|
||||
for ( index = 0 , nnames = 0 ; index < nname ; index++ ) {
|
||||
if ( zflag == 0 && nl[index].ncall == 0 && nl[index].time == 0 )
|
||||
continue;
|
||||
namesortnlp[nnames++] = &nl[index];
|
||||
}
|
||||
qsort( namesortnlp , nnames , sizeof(nltype *) , namecmp );
|
||||
for ( index = 1 , todo = nnames ; index <= ncycle ; index++ ) {
|
||||
namesortnlp[todo++] = &cyclenl[index];
|
||||
}
|
||||
printf( "\f\nIndex by function name\n\n" );
|
||||
index = ( todo + 2 ) / 3;
|
||||
for ( i = 0; i < index ; i++ ) {
|
||||
for ( j = i; j < todo ; j += index ) {
|
||||
nlp = namesortnlp[ j ];
|
||||
if ( nlp -> printflag ) {
|
||||
sprintf( peterbuffer , "[%d]" , nlp -> index );
|
||||
} else {
|
||||
sprintf( peterbuffer , "(%d)" , nlp -> index );
|
||||
}
|
||||
if ( j < nnames ) {
|
||||
printf( "%6.6s " , peterbuffer );
|
||||
if (bsd_style_output)
|
||||
printf ("%-19.19s" , nlp->name);
|
||||
else {
|
||||
int size = printnameonly(nlp);
|
||||
for ( ; size < 19; size++) putchar(' ');
|
||||
}
|
||||
} else {
|
||||
printf( "%6.6s " , peterbuffer );
|
||||
sprintf( peterbuffer , "<cycle %d>" , nlp -> cycleno );
|
||||
printf( "%-19.19s" , peterbuffer );
|
||||
}
|
||||
}
|
||||
printf( "\n" );
|
||||
}
|
||||
free( namesortnlp );
|
||||
}
|
||||
|
||||
PTR
|
||||
xmalloc (size)
|
||||
long size;
|
||||
{
|
||||
PTR val = (PTR) malloc (size);
|
||||
if (val == NULL) {
|
||||
fprintf (stderr, "virtual memory exhaused\n");
|
||||
exit (1);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
PTR
|
||||
xrealloc (oldval, size)
|
||||
PTR oldval;
|
||||
long size;
|
||||
{
|
||||
PTR val = (PTR) realloc (oldval, size);
|
||||
if (val == NULL) {
|
||||
fprintf (stderr, "virtual memory exhaused\n");
|
||||
exit (1);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)printlist.c 5.5 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* these are the lists of names:
|
||||
* there is the list head and then the listname
|
||||
* is a pointer to the list head
|
||||
* (for ease of passing to stringlist functions).
|
||||
*/
|
||||
struct stringlist kfromhead = { 0 , 0 };
|
||||
struct stringlist *kfromlist = &kfromhead;
|
||||
struct stringlist ktohead = { 0 , 0 };
|
||||
struct stringlist *ktolist = &ktohead;
|
||||
struct stringlist fhead = { 0 , 0 };
|
||||
struct stringlist *flist = &fhead;
|
||||
struct stringlist Fhead = { 0 , 0 };
|
||||
struct stringlist *Flist = &Fhead;
|
||||
struct stringlist ehead = { 0 , 0 };
|
||||
struct stringlist *elist = &ehead;
|
||||
struct stringlist Ehead = { 0 , 0 };
|
||||
struct stringlist *Elist = &Ehead;
|
||||
|
||||
addlist( listp , funcname )
|
||||
struct stringlist *listp;
|
||||
char *funcname;
|
||||
{
|
||||
struct stringlist *slp;
|
||||
|
||||
slp = (struct stringlist *) malloc( sizeof(struct stringlist));
|
||||
if ( slp == (struct stringlist *) 0 ) {
|
||||
fprintf( stderr, "gprof: ran out room for printlist\n" );
|
||||
done();
|
||||
}
|
||||
slp -> next = listp -> next;
|
||||
slp -> string = funcname;
|
||||
listp -> next = slp;
|
||||
}
|
||||
|
||||
bool
|
||||
onlist( listp , funcname )
|
||||
struct stringlist *listp;
|
||||
char *funcname;
|
||||
{
|
||||
struct stringlist *slp;
|
||||
|
||||
for ( slp = listp -> next ; slp ; slp = slp -> next ) {
|
||||
if ( ! strcmp( slp -> string , funcname ) ) {
|
||||
return TRUE;
|
||||
}
|
||||
if ( funcname[0] == '_' && ! strcmp( slp -> string , &funcname[1] ) ) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
38
gprof/search_list.c
Normal file
38
gprof/search_list.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
#include "libiberty.h"
|
||||
#include "gprof.h"
|
||||
#include "search_list.h"
|
||||
|
||||
|
||||
void
|
||||
DEFUN(search_list_append, (list, paths),
|
||||
Search_List *list AND const char *paths)
|
||||
{
|
||||
Search_List_Elem *new_el;
|
||||
const char *beg, *colon;
|
||||
int len;
|
||||
|
||||
colon = paths - 1;
|
||||
do {
|
||||
beg = colon + 1;
|
||||
colon = strchr(beg, ':');
|
||||
if (colon) {
|
||||
len = colon - beg;
|
||||
} else {
|
||||
len = strlen(beg);
|
||||
} /* if */
|
||||
new_el = (Search_List_Elem*) xmalloc(sizeof(*new_el) + len);
|
||||
memcpy(new_el->path, beg, len);
|
||||
new_el->path[len] = '\0';
|
||||
|
||||
/* append new path at end of list: */
|
||||
new_el->next = 0;
|
||||
if (list->tail) {
|
||||
list->tail->next = new_el;
|
||||
} else {
|
||||
list->head = new_el;
|
||||
} /* if */
|
||||
list->tail = new_el;
|
||||
} while (colon);
|
||||
} /* search_list_append */
|
||||
|
||||
/*** end of search_list.c ***/
|
16
gprof/search_list.h
Normal file
16
gprof/search_list.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef search_list_h
|
||||
#define search_list_h
|
||||
|
||||
typedef struct search_list_elem {
|
||||
struct search_list_elem *next;
|
||||
char path[1];
|
||||
} Search_List_Elem;
|
||||
|
||||
typedef struct {
|
||||
struct search_list_elem *head;
|
||||
struct search_list_elem *tail;
|
||||
} Search_List;
|
||||
|
||||
extern void search_list_append PARAMS((Search_List *list, const char *paths));
|
||||
|
||||
#endif /* search_list_h */
|
186
gprof/source.c
Normal file
186
gprof/source.c
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Keeps track of source files.
|
||||
*/
|
||||
#include "gprof.h"
|
||||
#include "libiberty.h"
|
||||
#include "search_list.h"
|
||||
#include "source.h"
|
||||
|
||||
#define EXT_ANNO "-ann" /* postfix of annotated files */
|
||||
|
||||
/*
|
||||
* Default option values:
|
||||
*/
|
||||
bool create_annotation_files = FALSE;
|
||||
|
||||
Search_List src_search_list = {0, 0};
|
||||
Source_File *first_src_file = 0;
|
||||
|
||||
|
||||
Source_File*
|
||||
DEFUN(source_file_lookup_path, (path), const char *path)
|
||||
{
|
||||
Source_File *sf;
|
||||
|
||||
for (sf = first_src_file; sf; sf = sf->next) {
|
||||
if (strcmp(path, sf->name) == 0) {
|
||||
break;
|
||||
} /* if */
|
||||
} /* for */
|
||||
if (!sf) {
|
||||
/* create a new source file descriptor: */
|
||||
|
||||
sf = (Source_File*) xmalloc(sizeof(*sf));
|
||||
memset(sf, 0, sizeof(*sf));
|
||||
sf->name = strdup(path);
|
||||
sf->next = first_src_file;
|
||||
first_src_file = sf;
|
||||
} /* if */
|
||||
return sf;
|
||||
} /* source_file_lookup_path */
|
||||
|
||||
|
||||
Source_File*
|
||||
DEFUN(source_file_lookup_name, (filename), const char *filename)
|
||||
{
|
||||
const char *fname;
|
||||
Source_File *sf;
|
||||
/*
|
||||
* The user cannot know exactly how a filename will be stored in
|
||||
* the debugging info (e.g., ../include/foo.h
|
||||
* vs. /usr/include/foo.h). So we simply compare the filename
|
||||
* component of a path only:
|
||||
*/
|
||||
for (sf = first_src_file; sf; sf = sf->next) {
|
||||
fname = strrchr(sf->name, '/');
|
||||
if (fname) {
|
||||
++fname;
|
||||
} else {
|
||||
fname = sf->name;
|
||||
} /* if */
|
||||
if (strcmp(filename, fname) == 0) {
|
||||
break;
|
||||
} /* if */
|
||||
} /* for */
|
||||
return sf;
|
||||
} /* source_file_lookup_name */
|
||||
|
||||
|
||||
FILE*
|
||||
DEFUN(annotate_source, (sf, max_width, annote, arg),
|
||||
Source_File *sf AND int max_width
|
||||
AND void (*annote) PARAMS((char *buf, int w, int l, void *arg))
|
||||
AND void *arg)
|
||||
{
|
||||
static bool first_file = TRUE;
|
||||
int i, line_num, nread;
|
||||
bool new_line;
|
||||
char buf[8192];
|
||||
char fname[PATH_MAX];
|
||||
char *annotation, *name_only;
|
||||
FILE *ifp, *ofp;
|
||||
Search_List_Elem *sle = src_search_list.head;
|
||||
|
||||
/*
|
||||
* Open input file. If open fails, walk along search-list until
|
||||
* open succeeds or reaching end of list:
|
||||
*/
|
||||
strcpy(fname, sf->name);
|
||||
if (sf->name[0] == '/') {
|
||||
sle = 0; /* don't use search list for absolute paths */
|
||||
} /* if */
|
||||
name_only = 0;
|
||||
while (TRUE) {
|
||||
DBG(SRCDEBUG, printf("[annotate_source]: looking for %s, trying %s\n",
|
||||
sf->name, fname));
|
||||
ifp = fopen(fname, FOPEN_RB);
|
||||
if (ifp) {
|
||||
break;
|
||||
} /* if */
|
||||
if (!sle && !name_only) {
|
||||
name_only = strrchr(sf->name, '/');
|
||||
if (name_only) {
|
||||
/* try search-list again, but this time with name only: */
|
||||
++name_only;
|
||||
sle = src_search_list.head;
|
||||
} /* if */
|
||||
} /* if */
|
||||
if (sle) {
|
||||
strcpy(fname, sle->path);
|
||||
strcat(fname, "/");
|
||||
if (name_only) {
|
||||
strcat(fname, name_only);
|
||||
} else {
|
||||
strcat(fname, sf->name);
|
||||
} /* if */
|
||||
sle = sle->next;
|
||||
} else {
|
||||
if (errno == ENOENT) {
|
||||
fprintf(stderr, "%s: could not locate `%s'\n",
|
||||
whoami, sf->name);
|
||||
} else {
|
||||
perror(sf->name);
|
||||
} /* if */
|
||||
return 0;
|
||||
} /* if */
|
||||
} /* while */
|
||||
|
||||
ofp = stdout;
|
||||
if (create_annotation_files) {
|
||||
/* try to create annotated source file: */
|
||||
const char *filename;
|
||||
|
||||
/* create annotation files in the current working directory: */
|
||||
filename = strrchr(sf->name, '/');
|
||||
if (filename) {
|
||||
++filename;
|
||||
} else {
|
||||
filename = sf->name;
|
||||
} /* if */
|
||||
|
||||
strcpy(fname, filename);
|
||||
strcat(fname, EXT_ANNO);
|
||||
ofp = fopen(fname, "w");
|
||||
if (!ofp) {
|
||||
perror(fname);
|
||||
return 0;
|
||||
} /* if */
|
||||
} /* if */
|
||||
|
||||
/*
|
||||
* Print file names if output goes to stdout and there are
|
||||
* more than one source file:
|
||||
*/
|
||||
if (ofp == stdout) {
|
||||
if (first_file) {
|
||||
first_file = FALSE;
|
||||
} else {
|
||||
fputc('\n', ofp);
|
||||
} /* if */
|
||||
if (first_output) {
|
||||
first_output = FALSE;
|
||||
} else {
|
||||
fprintf(ofp, "\f\n");
|
||||
} /* if */
|
||||
fprintf(ofp, "*** File %s:\n", sf->name);
|
||||
} /* if */
|
||||
|
||||
annotation = xmalloc(max_width + 1);
|
||||
line_num = 1;
|
||||
new_line = TRUE;
|
||||
while ((nread = fread(buf, 1, sizeof(buf), ifp)) > 0) {
|
||||
for (i = 0; i < nread; ++i) {
|
||||
if (new_line) {
|
||||
(*annote)(annotation, max_width, line_num, arg);
|
||||
fputs(annotation, ofp);
|
||||
++line_num; new_line = FALSE;
|
||||
} /* if */
|
||||
new_line = (buf[i] == '\n');
|
||||
fputc(buf[i], ofp);
|
||||
} /* for */
|
||||
} /* while */
|
||||
free(annotation);
|
||||
return ofp;
|
||||
} /* annotate_source */
|
||||
|
||||
/*** end of source.c ***/
|
53
gprof/source.h
Normal file
53
gprof/source.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#ifndef source_h
|
||||
#define source_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include "gprof.h"
|
||||
#include "search_list.h"
|
||||
|
||||
typedef struct source_file {
|
||||
struct source_file *next;
|
||||
const char *name; /* name of source file */
|
||||
int ncalls; /* # of "calls" to this file */
|
||||
int num_lines; /* # of lines in file */
|
||||
int nalloced; /* number of lines allocated */
|
||||
void **line; /* usage-dependent per-line data */
|
||||
} Source_File;
|
||||
|
||||
/*
|
||||
* Options:
|
||||
*/
|
||||
extern bool create_annotation_files; /* create annotated output files? */
|
||||
|
||||
/*
|
||||
* List of directories to search for source files:
|
||||
*/
|
||||
extern Search_List src_search_list;
|
||||
|
||||
/*
|
||||
* Chain of source-file descriptors:
|
||||
*/
|
||||
extern Source_File *first_src_file;
|
||||
|
||||
/*
|
||||
* Returns pointer to source file descriptor for PATH/FILENAME.
|
||||
*/
|
||||
extern Source_File *source_file_lookup_path PARAMS((const char *path));
|
||||
extern Source_File *source_file_lookup_name PARAMS((const char *filename));
|
||||
|
||||
/*
|
||||
* Read source file SF output annotated source. The annotation is at
|
||||
* MAX_WIDTH characters wide and for each source-line an annotation is
|
||||
* obtained by invoking function ANNOTE. ARG is an argument passed to
|
||||
* ANNOTE that is left uninterpreted by annotate_source().
|
||||
*
|
||||
* Returns a pointer to the output file (which maybe stdout) such
|
||||
* that summary statistics can be printed. If the returned file
|
||||
* is not stdout, it should be closed when done with it.
|
||||
*/
|
||||
extern FILE *annotate_source PARAMS((Source_File *sf, int max_width,
|
||||
void (*annote) (char *b, int w, int l,
|
||||
void *arg),
|
||||
void *arg));
|
||||
|
||||
#endif /* source_h */
|
144
gprof/sparc.c
144
gprof/sparc.c
|
@ -16,116 +16,64 @@
|
|||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)tahoe.c 1.5 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "core.h"
|
||||
#include "hist.h"
|
||||
#include "symtab.h"
|
||||
|
||||
/*
|
||||
* a namelist entry to be the child of indirect callf
|
||||
*/
|
||||
nltype indirectchild = {
|
||||
"(*)" , /* the name */
|
||||
(unsigned long) 0 , /* the pc entry point */
|
||||
(unsigned long) 0 , /* entry point aligned to histogram */
|
||||
(double) 0.0 , /* ticks in this routine */
|
||||
(double) 0.0 , /* cumulative ticks in children */
|
||||
(long) 0 , /* how many times called */
|
||||
(long) 0 , /* how many calls to self */
|
||||
(double) 1.0 , /* propagation fraction */
|
||||
(double) 0.0 , /* self propagation time */
|
||||
(double) 0.0 , /* child propagation time */
|
||||
(bool) 0 , /* print flag */
|
||||
(int) 0 , /* index in the graph list */
|
||||
(int) 0 , /* graph call chain top-sort order */
|
||||
(int) 0 , /* internal number of cycle on */
|
||||
(struct nl *) &indirectchild , /* pointer to head of cycle */
|
||||
(struct nl *) 0 , /* pointer to next member of cycle */
|
||||
(arctype *) 0 , /* list of caller arcs */
|
||||
(arctype *) 0 /* list of callee arcs */
|
||||
};
|
||||
|
||||
findcall( parentp , p_lowpc , p_highpc )
|
||||
nltype *parentp;
|
||||
unsigned long p_lowpc;
|
||||
unsigned long p_highpc;
|
||||
void
|
||||
find_call(parent, p_lowpc, p_highpc)
|
||||
Sym *parent;
|
||||
bfd_vma p_lowpc;
|
||||
bfd_vma p_highpc;
|
||||
{
|
||||
unsigned char *instructp;
|
||||
long length;
|
||||
nltype *childp;
|
||||
unsigned long destpc;
|
||||
bfd_vma dest_pc, delta;
|
||||
unsigned int *instr;
|
||||
Sym *child;
|
||||
|
||||
if ( textspace == 0 ) {
|
||||
delta = (bfd_vma) core_text_space - core_text_sect->vma;
|
||||
|
||||
if (core_text_space == 0) {
|
||||
return;
|
||||
}
|
||||
} /* if */
|
||||
if (p_lowpc < s_lowpc) {
|
||||
p_lowpc = s_lowpc;
|
||||
}
|
||||
} /* if */
|
||||
if (p_highpc > s_highpc) {
|
||||
p_highpc = s_highpc;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall] %s: 0x%x to 0x%x\n" ,
|
||||
parentp -> name , p_lowpc , p_highpc );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( instructp = textspace + p_lowpc ;
|
||||
instructp < textspace + p_highpc ;
|
||||
instructp += length ) {
|
||||
length = 1;
|
||||
if ( (*instructp & CALL) ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\t0x%x:callf" , instructp - textspace );
|
||||
}
|
||||
# endif DEBUG
|
||||
length = 4; /* constant length in a SPARC */
|
||||
} /* if */
|
||||
DBG(CALLDEBUG, printf("[find_call] %s: 0x%lx to 0x%lx\n",
|
||||
parent->name, p_lowpc, p_highpc));
|
||||
for (instr = (unsigned int*)(p_lowpc + delta);
|
||||
instr < (unsigned int*)(p_highpc + delta);
|
||||
++instr)
|
||||
{
|
||||
if ((*instr & CALL)) {
|
||||
DBG(CALLDEBUG,
|
||||
printf("[find_call] 0x%lx: callf", (bfd_vma) instr - delta));
|
||||
/*
|
||||
* regular pc relative addressing
|
||||
* check that this is the address of
|
||||
* a function.
|
||||
* Regular pc relative addressing check that this is the
|
||||
* address of a function.
|
||||
*/
|
||||
destpc = ( (unsigned long)instructp + (*(long *)instructp & ~CALL) )
|
||||
- (unsigned long) textspace;
|
||||
if ( destpc >= s_lowpc && destpc <= s_highpc ) {
|
||||
childp = nllookup( destpc );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tdestpc 0x%x" , destpc );
|
||||
printf( " childp->name %s" , childp -> name );
|
||||
printf( " childp->value 0x%x\n" ,
|
||||
childp -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( childp -> value == destpc ) {
|
||||
/*
|
||||
* a hit
|
||||
*/
|
||||
addarc( parentp , childp , (long) 0 );
|
||||
length += 4; /* constant lengths */
|
||||
dest_pc = ((bfd_vma) (instr + (*instr & ~CALL))) - delta;
|
||||
if (dest_pc >= s_lowpc && dest_pc <= s_highpc) {
|
||||
child = sym_lookup(&symtab, dest_pc);
|
||||
DBG(CALLDEBUG,
|
||||
printf("\tdest_pc=0x%lx, (name=%s, addr=0x%lx)\n",
|
||||
dest_pc, child->name, child->addr));
|
||||
if (child->addr == dest_pc) {
|
||||
/* a hit: */
|
||||
arc_add(parent, child, 0);
|
||||
continue;
|
||||
}
|
||||
goto botched;
|
||||
}
|
||||
} /* if */
|
||||
} /* if */
|
||||
/*
|
||||
* else:
|
||||
* it looked like a callf,
|
||||
* but it wasn't to anywhere.
|
||||
* Something funny going on.
|
||||
*/
|
||||
botched:
|
||||
/*
|
||||
* something funny going on.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tbut it's a botch\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
length = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
DBG(CALLDEBUG, printf("\tbut it's a botch\n"));
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* find_call */
|
||||
/*** end of sparc.c ***/
|
||||
|
|
|
@ -27,6 +27,6 @@
|
|||
* offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see asgnsamples for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_OF_CODE 0
|
||||
#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
|
||||
#define OFFSET_TO_CODE 0
|
||||
#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT))
|
||||
|
||||
|
|
315
gprof/sym_ids.c
Normal file
315
gprof/sym_ids.c
Normal file
|
@ -0,0 +1,315 @@
|
|||
#include "libiberty.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "sym_ids.h"
|
||||
|
||||
struct sym_id {
|
||||
struct sym_id *next;
|
||||
char *spec; /* parsing modifies this */
|
||||
Table_Id which_table;
|
||||
bool has_right;
|
||||
struct match {
|
||||
int prev_index; /* index of prev match */
|
||||
Sym *prev_match; /* previous match */
|
||||
Sym *first_match; /* chain of all matches */
|
||||
Sym sym;
|
||||
} left, right;
|
||||
} *id_list;
|
||||
|
||||
Sym_Table syms[NUM_TABLES];
|
||||
|
||||
#ifdef DEBUG
|
||||
const char *table_name[] = {
|
||||
"INCL_GRAPH", "EXCL_GRAPH",
|
||||
"INCL_ARCS", "EXCL_ARCS",
|
||||
"INCL_FLAT", "EXCL_FLAT",
|
||||
"INCL_TIME", "EXCL_TIME",
|
||||
"INCL_ANNO", "EXCL_ANNO",
|
||||
"INCL_EXEC", "EXCL_EXEC"
|
||||
};
|
||||
#endif /* DEBUG */
|
||||
|
||||
/*
|
||||
* This is the table in which we keep all the syms that match
|
||||
* the right half of an arc id. It is NOT sorted according
|
||||
* to the addresses, because it is accessed only through
|
||||
* the left half's CHILDREN pointers (so it's crucial not
|
||||
* to reorder this table once pointers into it exist).
|
||||
*/
|
||||
static Sym_Table right_ids;
|
||||
|
||||
static Source_File non_existent_file = {
|
||||
0, "<non-existent-file>"
|
||||
};
|
||||
|
||||
|
||||
void
|
||||
DEFUN(sym_id_add, (spec, which_table),
|
||||
const char *spec AND Table_Id which_table)
|
||||
{
|
||||
struct sym_id *id;
|
||||
int len = strlen(spec);
|
||||
|
||||
id = (struct sym_id*) xmalloc(sizeof(*id) + len + 1);
|
||||
memset(id, 0, sizeof(*id));
|
||||
|
||||
id->spec = (char*)id + sizeof(*id);
|
||||
strcpy(id->spec, spec);
|
||||
id->which_table = which_table;
|
||||
|
||||
id->next = id_list;
|
||||
id_list = id;
|
||||
} /* sym_id_add */
|
||||
|
||||
|
||||
/*
|
||||
* A spec has the syntax FILENAME:(FUNCNAME|LINENUM). As a convenience
|
||||
* to the user, a spec without a colon is interpreted as:
|
||||
*
|
||||
* (i) a FILENAME if it contains a dot
|
||||
* (ii) a FUNCNAME if it starts with a non-digit character
|
||||
* (iii) a LINENUM if it starts with a digit
|
||||
*
|
||||
* A FUNCNAME containing a dot can be specified by :FUNCNAME, a
|
||||
* FILENAME not containing a dot can be specified by FILENAME:.
|
||||
*/
|
||||
static void
|
||||
DEFUN(parse_spec, (spec, sym), char *spec AND Sym *sym)
|
||||
{
|
||||
char *colon;
|
||||
|
||||
sym_init(sym);
|
||||
colon = strrchr(spec, ':');
|
||||
if (colon) {
|
||||
*colon = '\0';
|
||||
if (colon > spec) {
|
||||
sym->file = source_file_lookup_name(spec);
|
||||
if (!sym->file) {
|
||||
sym->file = &non_existent_file;
|
||||
} /* if */
|
||||
} /* if */
|
||||
spec = colon + 1;
|
||||
if (strlen(spec)) {
|
||||
if (isdigit(spec[0])) {
|
||||
sym->line_num = atoi(spec);
|
||||
} else {
|
||||
sym->name = spec;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} else if (strlen(spec)) {
|
||||
/* no colon: spec is a filename if it contains a dot: */
|
||||
if (strchr(spec, '.')) {
|
||||
sym->file = source_file_lookup_name(spec);
|
||||
if (!sym->file) {
|
||||
sym->file = &non_existent_file;
|
||||
} /* if */
|
||||
} else if (isdigit(*spec)) {
|
||||
sym->line_num = atoi(spec);
|
||||
} else if (strlen(spec)) {
|
||||
sym->name = spec;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* parse_spec */
|
||||
|
||||
|
||||
/*
|
||||
* A symbol id has the syntax SPEC[/SPEC], where SPEC is is defined
|
||||
* by parse_spec().
|
||||
*/
|
||||
static void
|
||||
DEFUN(parse_id, (id), struct sym_id *id)
|
||||
{
|
||||
char *slash;
|
||||
|
||||
DBG(IDDEBUG, printf("[parse_id] %s -> ", id->spec));
|
||||
|
||||
slash = strchr(id->spec, '/');
|
||||
if (slash) {
|
||||
parse_spec(slash + 1, &id->right.sym);
|
||||
*slash = '\0';
|
||||
id->has_right = TRUE;
|
||||
} /* if */
|
||||
parse_spec(id->spec, &id->left.sym);
|
||||
|
||||
DBG(IDDEBUG,
|
||||
printf("%s:", id->left.sym.file ? id->left.sym.file->name : "*");
|
||||
if (id->left.sym.name) {
|
||||
printf("%s", id->left.sym.name);
|
||||
} else if (id->left.sym.line_num) {
|
||||
printf("%d", id->left.sym.line_num);
|
||||
} else {
|
||||
printf("*");
|
||||
} /* if */
|
||||
if (id->has_right) {
|
||||
printf("/%s:",
|
||||
id->right.sym.file ? id->right.sym.file->name : "*");
|
||||
if (id->right.sym.name) {
|
||||
printf("%s", id->right.sym.name);
|
||||
} else if (id->right.sym.line_num) {
|
||||
printf("%d", id->right.sym.line_num);
|
||||
} else {
|
||||
printf("*");
|
||||
} /* if */
|
||||
} /* if */
|
||||
printf("\n"));
|
||||
} /* parse_id */
|
||||
|
||||
|
||||
/*
|
||||
* Return TRUE iff PATTERN matches SYM.
|
||||
*/
|
||||
static bool
|
||||
DEFUN(match, (pattern, sym), Sym *pattern AND Sym *sym)
|
||||
{
|
||||
return (pattern->file ? pattern->file == sym->file : TRUE)
|
||||
&& (pattern->line_num ? pattern->line_num == sym->line_num : TRUE)
|
||||
&& (pattern->name ? strcmp(pattern->name, sym->name) == 0 : TRUE);
|
||||
} /* match */
|
||||
|
||||
|
||||
static void
|
||||
DEFUN(extend_match, (m, sym, tab, second_pass),
|
||||
struct match *m AND Sym *sym AND Sym_Table *tab AND bool second_pass)
|
||||
{
|
||||
if (m->prev_match != sym - 1) {
|
||||
/* discontinuity: add new match to table: */
|
||||
if (second_pass) {
|
||||
tab->base[tab->len] = *sym;
|
||||
m->prev_index = tab->len;
|
||||
|
||||
/* link match into match's chain: */
|
||||
tab->base[tab->len].next = m->first_match;
|
||||
m->first_match = &tab->base[tab->len];
|
||||
} /* if */
|
||||
++tab->len;
|
||||
} /* if */
|
||||
|
||||
/* extend match to include this symbol: */
|
||||
if (second_pass) {
|
||||
tab->base[m->prev_index].end_addr = sym->end_addr;
|
||||
} /* if */
|
||||
m->prev_match = sym;
|
||||
} /* extend_match */
|
||||
|
||||
|
||||
/*
|
||||
* Go through sym_id list produced by option processing and fill
|
||||
* in the various symbol tables indicating what symbols should
|
||||
* be displayed or suppressed for the various kinds of outputs.
|
||||
*
|
||||
* This can potentially produce huge tables and in particulars
|
||||
* tons of arcs, but this happens only if the user makes silly
|
||||
* requests---you get what you ask for!
|
||||
*/
|
||||
void
|
||||
DEFUN_VOID(sym_id_parse)
|
||||
{
|
||||
Sym *sym, *left, *right;
|
||||
struct sym_id *id;
|
||||
Sym_Table *tab;
|
||||
|
||||
/*
|
||||
* Convert symbol ids into Syms, so we can deal with them more easily:
|
||||
*/
|
||||
for (id = id_list; id; id = id->next) {
|
||||
parse_id(id);
|
||||
} /* for */
|
||||
|
||||
/* first determine size of each table: */
|
||||
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
for (id = id_list; id; id = id->next) {
|
||||
if (match(&id->left.sym, sym)) {
|
||||
extend_match(&id->left, sym, &syms[id->which_table], FALSE);
|
||||
} /* if */
|
||||
if (id->has_right && match(&id->right.sym, sym)) {
|
||||
extend_match(&id->right, sym, &right_ids, FALSE);
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* for */
|
||||
|
||||
/* create tables of appropriate size and reset lengths: */
|
||||
|
||||
for (tab = syms; tab < &syms[NUM_TABLES]; ++tab) {
|
||||
if (tab->len) {
|
||||
tab->base = (Sym*) xmalloc(tab->len * sizeof(Sym));
|
||||
tab->limit = tab->base + tab->len;
|
||||
tab->len = 0;
|
||||
} /* if */
|
||||
} /* for */
|
||||
if (right_ids.len) {
|
||||
right_ids.base = (Sym*) xmalloc(right_ids.len * sizeof(Sym));
|
||||
right_ids.limit = right_ids.base + right_ids.len;
|
||||
right_ids.len = 0;
|
||||
} /* if */
|
||||
|
||||
/* make a second pass through symtab, creating syms as necessary: */
|
||||
|
||||
for (sym = symtab.base; sym < symtab.limit; ++sym) {
|
||||
for (id = id_list; id; id = id->next) {
|
||||
if (match(&id->left.sym, sym)) {
|
||||
extend_match(&id->left, sym, &syms[id->which_table], TRUE);
|
||||
} /* if */
|
||||
if (id->has_right && match(&id->right.sym, sym)) {
|
||||
extend_match(&id->right, sym, &right_ids, TRUE);
|
||||
} /* if */
|
||||
} /* for */
|
||||
} /* for */
|
||||
|
||||
/* go through ids creating arcs as needed: */
|
||||
|
||||
for (id = id_list; id; id = id->next) {
|
||||
if (id->has_right) {
|
||||
for (left = id->left.first_match; left; left = left->next) {
|
||||
for (right = id->right.first_match; right; right = right->next)
|
||||
{
|
||||
DBG(IDDEBUG,
|
||||
printf(
|
||||
"[sym_id_parse]: arc %s:%s(%lx-%lx) -> %s:%s(%lx-%lx) to %s\n",
|
||||
left->file ? left->file->name : "*",
|
||||
left->name ? left->name : "*", left->addr,
|
||||
left->end_addr,
|
||||
right->file ? right->file->name : "*",
|
||||
right->name ? right->name : "*", right->addr,
|
||||
right->end_addr,
|
||||
table_name[id->which_table]));
|
||||
arc_add(left, right, 0);
|
||||
} /* for */
|
||||
} /* for */
|
||||
} /* if */
|
||||
} /* for */
|
||||
|
||||
/* finally, we can sort the tables and we're done: */
|
||||
|
||||
for (tab = &syms[0]; tab < &syms[NUM_TABLES]; ++tab) {
|
||||
DBG(IDDEBUG, printf("[sym_id_parse] syms[%s]:\n",
|
||||
table_name[tab - &syms[0]]));
|
||||
symtab_finalize(tab);
|
||||
} /* for */
|
||||
} /* sym_id_parse */
|
||||
|
||||
|
||||
/*
|
||||
* Symbol tables storing the FROM symbols of arcs do not necessarily
|
||||
* have distinct address ranges. For example, somebody might request
|
||||
* -k /_mcount to suppress any arcs into _mcount, while at the same
|
||||
* time requesting -k a/b. Fortunately, those symbol tables don't get
|
||||
* very big (the user has to type them!), so a linear search is probably
|
||||
* tolerable.
|
||||
*/
|
||||
bool
|
||||
DEFUN(sym_id_arc_is_present, (symtab, from, to),
|
||||
Sym_Table *symtab AND Sym *from AND Sym *to)
|
||||
{
|
||||
Sym *sym;
|
||||
|
||||
for (sym = symtab->base; sym < symtab->limit; ++sym) {
|
||||
if (from->addr >= sym->addr && from->addr <= sym->end_addr
|
||||
&& arc_lookup(sym, to))
|
||||
{
|
||||
return TRUE;
|
||||
} /* if */
|
||||
} /* for */
|
||||
return FALSE;
|
||||
} /* sym_id_arc_is_present */
|
||||
|
||||
/*** end of sym_ids.h ***/
|
23
gprof/sym_ids.h
Normal file
23
gprof/sym_ids.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef sym_ids_h
|
||||
#define sym_ids_h
|
||||
|
||||
#include "symtab.h"
|
||||
|
||||
typedef enum {
|
||||
INCL_GRAPH = 0, EXCL_GRAPH,
|
||||
INCL_ARCS, EXCL_ARCS,
|
||||
INCL_FLAT, EXCL_FLAT,
|
||||
INCL_TIME, EXCL_TIME,
|
||||
INCL_ANNO, EXCL_ANNO,
|
||||
INCL_EXEC, EXCL_EXEC,
|
||||
NUM_TABLES
|
||||
} Table_Id;
|
||||
|
||||
extern Sym_Table syms[NUM_TABLES];
|
||||
|
||||
extern void sym_id_add PARAMS((const char *spec, Table_Id which_table));
|
||||
extern void sym_id_parse PARAMS((void));
|
||||
extern bool sym_id_arc_is_present PARAMS((Sym_Table *symtab,
|
||||
Sym *from, Sym *to));
|
||||
|
||||
#endif /* sym_ids_h */
|
233
gprof/symtab.c
Normal file
233
gprof/symtab.c
Normal file
|
@ -0,0 +1,233 @@
|
|||
#include "gprof.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "core.h"
|
||||
#include "symtab.h"
|
||||
|
||||
Sym_Table symtab;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize a symbol (so it's empty).
|
||||
*/
|
||||
void
|
||||
DEFUN(sym_init, (sym), Sym *sym)
|
||||
{
|
||||
memset(sym, 0, sizeof(*sym));
|
||||
/*
|
||||
* It is not safe to assume that a binary zero corresponds to
|
||||
* a floating-point 0.0, so initialize floats explicitly:
|
||||
*/
|
||||
sym->hist.time = 0.0;
|
||||
sym->cg.child_time = 0.0;
|
||||
sym->cg.prop.fract = 0.0;
|
||||
sym->cg.prop.self = 0.0;
|
||||
sym->cg.prop.child = 0.0;
|
||||
} /* sym_init */
|
||||
|
||||
|
||||
/*
|
||||
* Compare the function entry-point of two symbols and return <0, =0,
|
||||
* or >0 depending on whether the left value is smaller than, equal
|
||||
* to, or greater than the right value. If two symbols are equal
|
||||
* but one has is_func set and the other doesn't, we make the
|
||||
* non-function symbol one "bigger" so that the function symbol will
|
||||
* survive duplicate removal. Finally, if both symbols have the
|
||||
* same is_func value, we discriminate against is_static such that
|
||||
* the global symbol survives.
|
||||
*/
|
||||
static int
|
||||
DEFUN(cmp_addr, (lp, rp), const PTR lp AND const PTR rp)
|
||||
{
|
||||
Sym *left = (Sym*) lp;
|
||||
Sym *right = (Sym*) rp;
|
||||
|
||||
if (left->addr > right->addr) {
|
||||
return 1;
|
||||
} else if (left->addr < right->addr) {
|
||||
return -1;
|
||||
} /* if */
|
||||
|
||||
if (left->is_func != right->is_func) {
|
||||
return right->is_func - left->is_func;
|
||||
} /* if */
|
||||
|
||||
return left->is_static - right->is_static;
|
||||
} /* cmp_addr */
|
||||
|
||||
|
||||
void
|
||||
DEFUN(symtab_finalize, (tab), Sym_Table *tab)
|
||||
{
|
||||
Sym *src, *dst;
|
||||
bfd_vma prev_addr;
|
||||
|
||||
if (!tab->len) {
|
||||
return;
|
||||
} /* if */
|
||||
|
||||
/*
|
||||
* Sort symbol table in order of increasing function addresses:
|
||||
*/
|
||||
qsort(tab->base, tab->len, sizeof(Sym), cmp_addr);
|
||||
|
||||
/*
|
||||
* Remove duplicate entries to speed-up later processing and
|
||||
* set end_addr if its not set yet:
|
||||
*/
|
||||
prev_addr = tab->base[0].addr + 1;
|
||||
for (src = dst = tab->base; src < tab->limit; ++src) {
|
||||
if (src->addr == prev_addr) {
|
||||
/*
|
||||
* If same address, favor global symbol over static one.
|
||||
* If both symbols are either static or global, 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
|
||||
* symbols (such as __gnu_compiled, __c89_used, etc.).
|
||||
*/
|
||||
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] == '_')))
|
||||
{
|
||||
DBG(AOUTDEBUG|IDDEBUG,
|
||||
printf("[symtab_finalize] favor %s@%c%c over %s@%c%c",
|
||||
src->name, src->is_static ? 't' : 'T',
|
||||
src->is_func ? 'F' : 'f',
|
||||
dst[-1].name, dst[-1].is_static ? 't' : 'T',
|
||||
dst[-1].is_func ? 'F' : 'f');
|
||||
printf(" (addr=%lx)\n", src->addr));
|
||||
dst[-1] = *src;
|
||||
} else {
|
||||
DBG(AOUTDEBUG|IDDEBUG,
|
||||
printf("[symtab_finalize] favor %s@%c%c over %s@%c%c",
|
||||
dst[-1].name, dst[-1].is_static ? 't' : 'T',
|
||||
dst[-1].is_func ? 'F' : 'f',
|
||||
src->name, src->is_static ? 't' : 'T',
|
||||
src->is_func ? 'F' : 'f');
|
||||
printf(" (addr=%lx)\n", src->addr));
|
||||
} /* if */
|
||||
} else {
|
||||
if (dst > tab->base && dst[-1].end_addr == 0) {
|
||||
dst[-1].end_addr = src->addr - 1;
|
||||
} /* if */
|
||||
|
||||
/* retain sym only if it has a non-empty address range: */
|
||||
if (!src->end_addr || src->addr <= src->end_addr) {
|
||||
*dst++ = *src;
|
||||
prev_addr = src->addr;
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* if */
|
||||
if (tab->len > 0 && dst[-1].end_addr == 0) {
|
||||
dst[-1].end_addr = core_text_sect->vma + core_text_sect->_raw_size - 1;
|
||||
} /* if */
|
||||
|
||||
DBG(AOUTDEBUG|IDDEBUG,
|
||||
printf("[symtab_finalize]: removed %d duplicate entries\n",
|
||||
tab->len - (int) (dst - tab->base)));
|
||||
|
||||
tab->limit = dst;
|
||||
tab->len = tab->limit - tab->base;
|
||||
|
||||
DBG(AOUTDEBUG|IDDEBUG,
|
||||
int j;
|
||||
|
||||
for (j = 0; j < tab->len; ++j){
|
||||
printf("[symtab_finalize] 0x%lx-0x%lx\t%s\n",
|
||||
(long) tab->base[j].addr, (long) tab->base[j].end_addr,
|
||||
tab->base[j].name);
|
||||
} /* for */);
|
||||
} /* symtab_finalize */
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
Sym*
|
||||
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);
|
||||
|
||||
sym = symtab->base;
|
||||
for (low = 0, high = symtab->len - 1 ; low != high ;) {
|
||||
mid = (high + low) >> 1;
|
||||
fprintf(stderr, "[dbg_sym_lookup] low=0x%lx, mid=0x%lx, high=0x%lx\n",
|
||||
low, mid, high);
|
||||
fprintf(stderr, "[dbg_sym_lookup] sym[m]=0x%lx sym[m + 1]=0x%lx\n",
|
||||
sym[mid].addr, sym[mid + 1].addr);
|
||||
if (sym[mid].addr <= address && sym[mid + 1].addr > address) {
|
||||
return &sym[mid];
|
||||
} /* if */
|
||||
if (sym[mid].addr > address) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
} /* if */
|
||||
} /* for */
|
||||
fprintf(stderr, "[sym_lookup] binary search fails???\n");
|
||||
return 0;
|
||||
} /* dbg_sym_lookup */
|
||||
|
||||
#endif DEBUG
|
||||
|
||||
|
||||
/*
|
||||
* Look up an address in the symbol-table that is sorted by address.
|
||||
* If address does not hit any symbol, 0 is returned.
|
||||
*/
|
||||
Sym*
|
||||
DEFUN(sym_lookup, (symtab, address), Sym_Table *symtab AND bfd_vma address)
|
||||
{
|
||||
long low, high;
|
||||
long mid = -1;
|
||||
Sym *sym;
|
||||
#ifdef DEBUG
|
||||
int probes = 0;
|
||||
#endif /* DEBUG */
|
||||
|
||||
if (!symtab->len) {
|
||||
return 0;
|
||||
} /* if */
|
||||
|
||||
sym = symtab->base;
|
||||
for (low = 0, high = symtab->len - 1 ; low != high ;) {
|
||||
DBG(LOOKUPDEBUG, ++probes);
|
||||
mid = (high + low) / 2;
|
||||
if (sym[mid].addr <= address && sym[mid + 1].addr > address) {
|
||||
if (address > sym[mid].end_addr) {
|
||||
/*
|
||||
* Address falls into gap between sym[mid] and
|
||||
* sym[mid + 1]:
|
||||
*/
|
||||
return 0;
|
||||
} else {
|
||||
DBG(LOOKUPDEBUG,
|
||||
printf("[sym_lookup] %d probes (symtab->len=%d)\n",
|
||||
probes, symtab->len - 1));
|
||||
return &sym[mid];
|
||||
} /* if */
|
||||
} /* if */
|
||||
if (sym[mid].addr > address) {
|
||||
high = mid;
|
||||
} else {
|
||||
low = mid + 1;
|
||||
} /* if */
|
||||
} /* for */
|
||||
if (sym[mid + 1].addr <= address) {
|
||||
if (address > sym[mid + 1].end_addr) {
|
||||
/* address is beyond end of sym[mid + 1]: */
|
||||
return 0;
|
||||
} else {
|
||||
DBG(LOOKUPDEBUG, printf("[sym_lookup] %d (%d) probes, fall off\n",
|
||||
probes, symtab->len - 1));
|
||||
return &sym[mid + 1];
|
||||
} /* if */
|
||||
} /* if */
|
||||
return 0;
|
||||
} /* sym_lookup */
|
||||
|
||||
/*** end of symtab.c ***/
|
88
gprof/symtab.h
Normal file
88
gprof/symtab.h
Normal file
|
@ -0,0 +1,88 @@
|
|||
#ifndef symtab_h
|
||||
#define symtab_h
|
||||
|
||||
#include "bfd.h"
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* For a profile to be intelligible to a human user, it is necessary
|
||||
* to map code-addresses into source-code information. Source-code
|
||||
* information can be any combination of: (i) function-name, (ii)
|
||||
* source file-name, and (iii) source line number.
|
||||
*
|
||||
* The symbol table is used to map addresses into source-code
|
||||
* information.
|
||||
*/
|
||||
|
||||
#include "source.h"
|
||||
|
||||
/*
|
||||
* Symbol-entry. For each external in the specified file we gather
|
||||
* its address, the number of calls and compute its share of cpu time.
|
||||
*/
|
||||
typedef struct sym {
|
||||
/*
|
||||
* Common information:
|
||||
*
|
||||
* In the symbol-table, fields ADDR and FUNC_NAME are guaranteed
|
||||
* to contain valid information. FILE may be 0, if unknown and
|
||||
* LINE_NUM maybe 0 if unknown.
|
||||
*/
|
||||
bfd_vma addr; /* address of entry point */
|
||||
bfd_vma end_addr; /* end-address */
|
||||
const char *name; /* name of function this sym is from */
|
||||
Source_File *file; /* source file symbol comes from */
|
||||
int line_num; /* source line number */
|
||||
unsigned int is_func : 1, /* is this a function entry point? */
|
||||
is_static : 1, /* is this a local (static) symbol? */
|
||||
is_bb_head : 1; /* is this the head of a basic-blk? */
|
||||
int ncalls; /* how many times executed */
|
||||
struct sym *next; /* for building chains of syms */
|
||||
|
||||
/* profile-specific information: */
|
||||
|
||||
/* histogram specific info: */
|
||||
struct {
|
||||
double time; /* (weighted) ticks in this routine */
|
||||
bfd_vma scaled_addr; /* scaled entry point */
|
||||
} hist;
|
||||
|
||||
/* call-graph specific info: */
|
||||
struct {
|
||||
int self_calls; /* how many calls to self */
|
||||
double child_time; /* cumulative ticks in children */
|
||||
int index; /* index in the graph list */
|
||||
int top_order; /* graph call chain top-sort order */
|
||||
bool print_flag; /* should this be printed? */
|
||||
struct {
|
||||
double fract; /* what % of time propagates */
|
||||
double self; /* how much self time propagates */
|
||||
double child; /* how much child time propagates */
|
||||
} prop;
|
||||
struct {
|
||||
int num; /* internal number of cycle on */
|
||||
struct sym *head; /* head of cycle */
|
||||
struct sym *next; /* next member of cycle */
|
||||
} cyc;
|
||||
struct arc *parents; /* list of caller arcs */
|
||||
struct arc *children; /* list of callee arcs */
|
||||
} cg;
|
||||
} Sym;
|
||||
|
||||
/*
|
||||
* Symbol-tables are always assumed to be sorted in increasing order
|
||||
* of addresses:
|
||||
*/
|
||||
typedef struct {
|
||||
int len; /* # of symbols in this table */
|
||||
Sym *base; /* first element in symbol table */
|
||||
Sym *limit; /* limit = base + len */
|
||||
} Sym_Table;
|
||||
|
||||
extern Sym_Table symtab; /* the symbol table */
|
||||
|
||||
extern void sym_init PARAMS((Sym *sym));
|
||||
extern void symtab_finalize PARAMS((Sym_Table *symtab));
|
||||
extern Sym *sym_lookup PARAMS((Sym_Table *symtab, bfd_vma address));
|
||||
|
||||
#endif /* symtab_h */
|
126
gprof/tahoe.c
126
gprof/tahoe.c
|
@ -16,36 +16,14 @@
|
|||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)tahoe.c 1.5 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
#include "time_host.h"
|
||||
|
||||
/*
|
||||
* a namelist entry to be the child of indirect callf
|
||||
* A symbol to be the child of indirect callf:
|
||||
*/
|
||||
nltype indirectchild = {
|
||||
"(*)" , /* the name */
|
||||
(unsigned long) 0 , /* the pc entry point */
|
||||
(unsigned long) 0 , /* entry point aligned to histogram */
|
||||
(double) 0.0 , /* ticks in this routine */
|
||||
(double) 0.0 , /* cumulative ticks in children */
|
||||
(long) 0 , /* how many times called */
|
||||
(long) 0 , /* how many calls to self */
|
||||
(double) 1.0 , /* propagation fraction */
|
||||
(double) 0.0 , /* self propagation time */
|
||||
(double) 0.0 , /* child propagation time */
|
||||
(bool) 0 , /* print flag */
|
||||
(int) 0 , /* index in the graph list */
|
||||
(int) 0 , /* graph call chain top-sort order */
|
||||
(int) 0 , /* internal number of cycle on */
|
||||
(struct nl *) &indirectchild , /* pointer to head of cycle */
|
||||
(struct nl *) 0 , /* pointer to next member of cycle */
|
||||
(arctype *) 0 , /* list of caller arcs */
|
||||
(arctype *) 0 /* list of callee arcs */
|
||||
};
|
||||
Sym indirectchild;
|
||||
|
||||
|
||||
operandenum
|
||||
operandmode(modep)
|
||||
|
@ -68,21 +46,21 @@ operandmode( modep )
|
|||
case 7:
|
||||
return autodec;
|
||||
case 8:
|
||||
return ( usesreg != 0xe ? autoinc : immediate );
|
||||
return usesreg != 0xe ? autoinc : immediate;
|
||||
case 9:
|
||||
return ( usesreg != PC ? autoincdef : absolute );
|
||||
return usesreg != PC ? autoincdef : absolute;
|
||||
case 10:
|
||||
return ( usesreg != PC ? bytedisp : byterel );
|
||||
return usesreg != PC ? bytedisp : byterel;
|
||||
case 11:
|
||||
return ( usesreg != PC ? bytedispdef : bytereldef );
|
||||
return usesreg != PC ? bytedispdef : bytereldef;
|
||||
case 12:
|
||||
return ( usesreg != PC ? worddisp : wordrel );
|
||||
return usesreg != PC ? worddisp : wordrel;
|
||||
case 13:
|
||||
return ( usesreg != PC ? worddispdef : wordreldef );
|
||||
return usesreg != PC ? worddispdef : wordreldef;
|
||||
case 14:
|
||||
return ( usesreg != PC ? longdisp : longrel );
|
||||
return usesreg != PC ? longdisp : longrel;
|
||||
case 15:
|
||||
return ( usesreg != PC ? longdispdef : longreldef );
|
||||
return usesreg != PC ? longdispdef : longreldef;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
@ -175,7 +153,7 @@ operandlength( modep )
|
|||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
unsigned long
|
||||
bfd_vma
|
||||
reladdr(modep)
|
||||
char *modep;
|
||||
{
|
||||
|
@ -187,35 +165,43 @@ reladdr( modep )
|
|||
long value = 0;
|
||||
|
||||
cp = modep;
|
||||
cp += 1; /* skip over the mode */
|
||||
++cp; /* skip over the mode */
|
||||
switch (mode) {
|
||||
default:
|
||||
fprintf(stderr, "[reladdr] not relative address\n");
|
||||
return (unsigned long) modep;
|
||||
return (bfd_vma) modep;
|
||||
case byterel:
|
||||
return (unsigned long) ( cp + sizeof *cp + *cp );
|
||||
return (bfd_vma) (cp + sizeof *cp + *cp);
|
||||
case wordrel:
|
||||
for (i = 0; i < sizeof *sp; i++)
|
||||
value = (value << 8) + (cp[i] & 0xff);
|
||||
return (unsigned long) ( cp + sizeof *sp + value );
|
||||
return (bfd_vma) (cp + sizeof *sp + value);
|
||||
case longrel:
|
||||
for (i = 0; i < sizeof *lp; i++)
|
||||
value = (value << 8) + (cp[i] & 0xff);
|
||||
return (unsigned long) ( cp + sizeof *lp + value );
|
||||
return (bfd_vma) (cp + sizeof *lp + value);
|
||||
}
|
||||
}
|
||||
|
||||
findcall( parentp , p_lowpc , p_highpc )
|
||||
nltype *parentp;
|
||||
unsigned long p_lowpc;
|
||||
unsigned long p_highpc;
|
||||
find_call(parent, p_lowpc, p_highpc)
|
||||
Sym *parent;
|
||||
bfd_vma p_lowpc;
|
||||
bfd_vma p_highpc;
|
||||
{
|
||||
unsigned char *instructp;
|
||||
long length;
|
||||
nltype *childp;
|
||||
Sym *child;
|
||||
operandenum mode;
|
||||
operandenum firstmode;
|
||||
unsigned long destpc;
|
||||
bfd_vma destpc;
|
||||
static bool inited = FALSE;
|
||||
|
||||
if (!inited) {
|
||||
inited = TRUE;
|
||||
sym_init(&indirectchild);
|
||||
indirectchild.cg.prop.fract = 1.0;
|
||||
indirectchild.cg.cyc.head = &indirectchild;
|
||||
} /* if */
|
||||
|
||||
if (textspace == 0) {
|
||||
return;
|
||||
|
@ -226,12 +212,8 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
if (p_highpc > s_highpc) {
|
||||
p_highpc = s_highpc;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall] %s: 0x%x to 0x%x\n" ,
|
||||
parentp -> name , p_lowpc , p_highpc );
|
||||
}
|
||||
# endif DEBUG
|
||||
DBG(CALLDEBUG, printf("[findcall] %s: 0x%x to 0x%x\n",
|
||||
parent -> name, p_lowpc, p_highpc));
|
||||
for ( instructp = textspace + p_lowpc ;
|
||||
instructp < textspace + p_highpc ;
|
||||
instructp += length) {
|
||||
|
@ -241,11 +223,8 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
* maybe a callf, better check it out.
|
||||
* skip the count of the number of arguments.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\t0x%x:callf" , instructp - textspace );
|
||||
}
|
||||
# endif DEBUG
|
||||
DBG(CALLDEBUG, printf("[findcall]\t0x%x:callf",
|
||||
instructp - textspace));
|
||||
firstmode = operandmode(instructp+length);
|
||||
switch (firstmode) {
|
||||
case literal:
|
||||
|
@ -256,12 +235,10 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
}
|
||||
length += operandlength(instructp+length);
|
||||
mode = operandmode(instructp + length);
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
DBG(CALLDEBUG,
|
||||
printf("\tfirst operand is %s", operandname(firstmode));
|
||||
printf("\tsecond operand is %s\n", operandname(mode));
|
||||
}
|
||||
# endif DEBUG
|
||||
);
|
||||
switch (mode) {
|
||||
case regdef:
|
||||
case bytedispdef:
|
||||
|
@ -278,7 +255,7 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
* [are there others that we miss?,
|
||||
* e.g. arrays of pointers to functions???]
|
||||
*/
|
||||
addarc( parentp , &indirectchild , (long) 0 );
|
||||
arc_add(parent, &indirectchild, (long) 0);
|
||||
length += operandlength(instructp + length);
|
||||
continue;
|
||||
case byterel:
|
||||
|
@ -290,22 +267,19 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
* a function.
|
||||
*/
|
||||
destpc = reladdr(instructp+length)
|
||||
- (unsigned long) textspace;
|
||||
- (bfd_vma) textspace;
|
||||
if (destpc >= s_lowpc && destpc <= s_highpc) {
|
||||
childp = nllookup( destpc );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
child = sym_lookup(destpc);
|
||||
DBG(CALLDEBUG,
|
||||
printf("[findcall]\tdestpc 0x%x", destpc);
|
||||
printf( " childp->name %s" , childp -> name );
|
||||
printf( " childp->value 0x%x\n" ,
|
||||
childp -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( childp -> value == destpc ) {
|
||||
printf(" child->name %s", child -> name);
|
||||
printf(" child->addr 0x%x\n", child -> addr);
|
||||
);
|
||||
if (child -> addr == destpc) {
|
||||
/*
|
||||
* a hit
|
||||
*/
|
||||
addarc( parentp , childp , (long) 0 );
|
||||
arc_add(parent, child, (long) 0);
|
||||
length += operandlength(instructp + length);
|
||||
continue;
|
||||
}
|
||||
|
@ -322,11 +296,7 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
/*
|
||||
* something funny going on.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tbut it's a botch\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
DBG(CALLDEBUG, printf("[findcall]\tbut it's a botch\n"));
|
||||
length = 1;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
* offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see asgnsamples for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_OF_CODE 2
|
||||
#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
|
||||
#define OFFSET_TO_CODE 2
|
||||
#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT))
|
||||
|
||||
/*
|
||||
* register for pc relative addressing
|
||||
|
|
90
gprof/utils.c
Normal file
90
gprof/utils.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
#include <demangle.h>
|
||||
#include "gprof.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "symtab.h"
|
||||
|
||||
|
||||
/*
|
||||
* Print name of symbol. Return number of characters printed.
|
||||
*/
|
||||
int
|
||||
DEFUN(print_name_only, (self), Sym *self)
|
||||
{
|
||||
const char *name = self->name;
|
||||
const char *filename;
|
||||
char *demangled = 0;
|
||||
char buf[PATH_MAX];
|
||||
int size = 0;
|
||||
|
||||
if (name) {
|
||||
if (!bsd_style_output) {
|
||||
if (name[0] == '_' && name[1] && discard_underscores) {
|
||||
name++;
|
||||
} /* if */
|
||||
demangled = cplus_demangle(name, DMGL_ANSI|DMGL_PARAMS);
|
||||
if (demangled) {
|
||||
name = demangled;
|
||||
} /* if */
|
||||
} /* if */
|
||||
printf("%s", name);
|
||||
size = strlen(name);
|
||||
if (line_granularity && self->file) {
|
||||
filename = self->file->name;
|
||||
if (!print_path) {
|
||||
filename = strrchr(filename, '/');
|
||||
if (filename) {
|
||||
++filename;
|
||||
} else {
|
||||
filename = self->file->name;
|
||||
} /* if */
|
||||
} /* if */
|
||||
sprintf(buf, " (%s:%d)", filename, self->line_num);
|
||||
printf(buf);
|
||||
size += strlen(buf);
|
||||
} /* if */
|
||||
if (demangled) {
|
||||
free(demangled);
|
||||
} /* if */
|
||||
DBG(DFNDEBUG, printf("{%d} ", self->cg.top_order));
|
||||
DBG(PROPDEBUG, printf("%4.0f%% ", 100.0 * self->cg.prop.fract));
|
||||
} /* if */
|
||||
return size;
|
||||
} /* print_name_only */
|
||||
|
||||
|
||||
void
|
||||
DEFUN(print_name, (self), Sym *self)
|
||||
{
|
||||
print_name_only(self);
|
||||
|
||||
if (self->cg.cyc.num != 0) {
|
||||
printf(" <cycle %d>", self->cg.cyc.num);
|
||||
} /* if */
|
||||
if (self->cg.index != 0) {
|
||||
if (self->cg.print_flag) {
|
||||
printf(" [%d]", self->cg.index);
|
||||
} else {
|
||||
printf(" (%d)", self->cg.index);
|
||||
} /* if */
|
||||
} /* if */
|
||||
} /* print_name */
|
||||
|
||||
/*** end of utils.c ***/
|
7
gprof/utils.h
Normal file
7
gprof/utils.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
#ifndef utils_h
|
||||
#define utils_h
|
||||
|
||||
extern int print_name_only PARAMS((Sym *self));
|
||||
extern void print_name PARAMS((Sym *self));
|
||||
|
||||
#endif /* utils_h */
|
148
gprof/vax.c
148
gprof/vax.c
|
@ -16,38 +16,20 @@
|
|||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)vax.c 5.6 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
#include "cg_arcs.h"
|
||||
#include "core.h"
|
||||
#include "hist.h"
|
||||
#include "symtab.h"
|
||||
#include "vax.h"
|
||||
|
||||
/*
|
||||
* a namelist entry to be the child of indirect calls
|
||||
* A symbol to be the child of indirect calls:
|
||||
*/
|
||||
nltype indirectchild = {
|
||||
"(*)" , /* the name */
|
||||
(unsigned long) 0 , /* the pc entry point */
|
||||
(unsigned long) 0 , /* entry point aligned to histogram */
|
||||
(double) 0.0 , /* ticks in this routine */
|
||||
(double) 0.0 , /* cumulative ticks in children */
|
||||
(long) 0 , /* how many times called */
|
||||
(long) 0 , /* how many calls to self */
|
||||
(double) 1.0 , /* propagation fraction */
|
||||
(double) 0.0 , /* self propagation time */
|
||||
(double) 0.0 , /* child propagation time */
|
||||
(bool) 0 , /* print flag */
|
||||
(int) 0 , /* index in the graph list */
|
||||
(int) 0 , /* graph call chain top-sort order */
|
||||
(int) 0 , /* internal number of cycle on */
|
||||
(struct nl *) &indirectchild , /* pointer to head of cycle */
|
||||
(struct nl *) 0 , /* pointer to next member of cycle */
|
||||
(arctype *) 0 , /* list of caller arcs */
|
||||
(arctype *) 0 /* list of callee arcs */
|
||||
};
|
||||
Sym indirectchild;
|
||||
|
||||
operandenum
|
||||
|
||||
static operandenum
|
||||
operandmode(modep)
|
||||
struct modebyte *modep;
|
||||
{
|
||||
|
@ -68,26 +50,26 @@ operandmode( modep )
|
|||
case 7:
|
||||
return autodec;
|
||||
case 8:
|
||||
return ( usesreg != PC ? autoinc : immediate );
|
||||
return usesreg != PC ? autoinc : immediate;
|
||||
case 9:
|
||||
return ( usesreg != PC ? autoincdef : absolute );
|
||||
return usesreg != PC ? autoincdef : absolute;
|
||||
case 10:
|
||||
return ( usesreg != PC ? bytedisp : byterel );
|
||||
return usesreg != PC ? bytedisp : byterel;
|
||||
case 11:
|
||||
return ( usesreg != PC ? bytedispdef : bytereldef );
|
||||
return usesreg != PC ? bytedispdef : bytereldef;
|
||||
case 12:
|
||||
return ( usesreg != PC ? worddisp : wordrel );
|
||||
return usesreg != PC ? worddisp : wordrel;
|
||||
case 13:
|
||||
return ( usesreg != PC ? worddispdef : wordreldef );
|
||||
return usesreg != PC ? worddispdef : wordreldef;
|
||||
case 14:
|
||||
return ( usesreg != PC ? longdisp : longrel );
|
||||
return usesreg != PC ? longdisp : longrel;
|
||||
case 15:
|
||||
return ( usesreg != PC ? longdispdef : longreldef );
|
||||
return usesreg != PC ? longdispdef : longreldef;
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
char *
|
||||
static char *
|
||||
operandname(mode)
|
||||
operandenum mode;
|
||||
{
|
||||
|
@ -139,7 +121,7 @@ operandname( mode )
|
|||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
long
|
||||
static long
|
||||
operandlength(modep)
|
||||
struct modebyte *modep;
|
||||
{
|
||||
|
@ -175,7 +157,7 @@ operandlength( modep )
|
|||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
unsigned long
|
||||
static bfd_vma
|
||||
reladdr(modep)
|
||||
struct modebyte *modep;
|
||||
{
|
||||
|
@ -185,35 +167,45 @@ reladdr( modep )
|
|||
long *lp;
|
||||
|
||||
cp = (char *) modep;
|
||||
cp += 1; /* skip over the mode */
|
||||
++cp; /* skip over the mode */
|
||||
switch (mode) {
|
||||
default:
|
||||
fprintf(stderr, "[reladdr] not relative address\n");
|
||||
return (unsigned long) modep;
|
||||
return (bfd_vma) modep;
|
||||
case byterel:
|
||||
return (unsigned long) ( cp + sizeof *cp + *cp );
|
||||
return (bfd_vma) (cp + sizeof *cp + *cp);
|
||||
case wordrel:
|
||||
sp = (short *) cp;
|
||||
return (unsigned long) ( cp + sizeof *sp + *sp );
|
||||
return (bfd_vma) (cp + sizeof *sp + *sp);
|
||||
case longrel:
|
||||
lp = (long *) cp;
|
||||
return (unsigned long) ( cp + sizeof *lp + *lp );
|
||||
return (bfd_vma) (cp + sizeof *lp + *lp);
|
||||
}
|
||||
}
|
||||
|
||||
findcall( parentp , p_lowpc , p_highpc )
|
||||
nltype *parentp;
|
||||
unsigned long p_lowpc;
|
||||
unsigned long p_highpc;
|
||||
|
||||
void
|
||||
find_call(parent, p_lowpc, p_highpc)
|
||||
Sym *parent;
|
||||
bfd_vma p_lowpc;
|
||||
bfd_vma p_highpc;
|
||||
{
|
||||
unsigned char *instructp;
|
||||
long length;
|
||||
nltype *childp;
|
||||
Sym *child;
|
||||
operandenum mode;
|
||||
operandenum firstmode;
|
||||
unsigned long destpc;
|
||||
bfd_vma destpc;
|
||||
static bool inited = FALSE;
|
||||
|
||||
if ( textspace == 0 ) {
|
||||
if (!inited) {
|
||||
inited = TRUE;
|
||||
sym_init(&indirectchild);
|
||||
indirectchild.cg.prop.fract = 1.0;
|
||||
indirectchild.cg.cyc.head = &indirectchild;
|
||||
} /* if */
|
||||
|
||||
if (core_text_space == 0) {
|
||||
return;
|
||||
}
|
||||
if (p_lowpc < s_lowpc) {
|
||||
|
@ -222,14 +214,10 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
if (p_highpc > s_highpc) {
|
||||
p_highpc = s_highpc;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall] %s: 0x%x to 0x%x\n" ,
|
||||
parentp -> name , p_lowpc , p_highpc );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( instructp = textspace + p_lowpc ;
|
||||
instructp < textspace + p_highpc ;
|
||||
DBG(CALLDEBUG, printf("[findcall] %s: 0x%lx to 0x%lx\n",
|
||||
parent->name, p_lowpc, p_highpc));
|
||||
for ( instructp = (unsigned char*) core_text_space + p_lowpc ;
|
||||
instructp < (unsigned char*) core_text_space + p_highpc ;
|
||||
instructp += length) {
|
||||
length = 1;
|
||||
if (*instructp == CALLS) {
|
||||
|
@ -237,11 +225,9 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
* maybe a calls, better check it out.
|
||||
* skip the count of the number of arguments.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\t0x%x:calls" , instructp - textspace );
|
||||
}
|
||||
# endif DEBUG
|
||||
DBG(CALLDEBUG,
|
||||
printf("[findcall]\t0x%x:calls",
|
||||
instructp - (unsigned char*) core_text_space));
|
||||
firstmode = operandmode((struct modebyte *) (instructp+length));
|
||||
switch (firstmode) {
|
||||
case literal:
|
||||
|
@ -252,12 +238,9 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
}
|
||||
length += operandlength((struct modebyte *) (instructp+length));
|
||||
mode = operandmode((struct modebyte *) (instructp + length));
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
DBG(CALLDEBUG,
|
||||
printf("\tfirst operand is %s", operandname(firstmode));
|
||||
printf( "\tsecond operand is %s\n" , operandname( mode ) );
|
||||
}
|
||||
# endif DEBUG
|
||||
printf("\tsecond operand is %s\n", operandname(mode)));
|
||||
switch (mode) {
|
||||
case regdef:
|
||||
case bytedispdef:
|
||||
|
@ -274,7 +257,7 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
* [are there others that we miss?,
|
||||
* e.g. arrays of pointers to functions???]
|
||||
*/
|
||||
addarc( parentp , &indirectchild , (long) 0 );
|
||||
arc_add(parent, &indirectchild, (long) 0);
|
||||
length += operandlength(
|
||||
(struct modebyte *) (instructp + length));
|
||||
continue;
|
||||
|
@ -287,22 +270,19 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
* a function.
|
||||
*/
|
||||
destpc = reladdr((struct modebyte *) (instructp+length))
|
||||
- (unsigned long) textspace;
|
||||
- (bfd_vma) core_text_space;
|
||||
if (destpc >= s_lowpc && destpc <= s_highpc) {
|
||||
childp = nllookup( destpc );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tdestpc 0x%x" , destpc );
|
||||
printf( " childp->name %s" , childp -> name );
|
||||
printf( " childp->value 0x%x\n" ,
|
||||
childp -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( childp -> value == destpc ) {
|
||||
child = sym_lookup(&symtab, destpc);
|
||||
DBG(CALLDEBUG,
|
||||
printf("[findcall]\tdestpc 0x%lx", destpc);
|
||||
printf(" child->name %s", child->name);
|
||||
printf(" child->addr 0x%lx\n", child->addr);
|
||||
);
|
||||
if (child->addr == destpc) {
|
||||
/*
|
||||
* a hit
|
||||
*/
|
||||
addarc( parentp , childp , (long) 0 );
|
||||
arc_add(parent, child, (long) 0);
|
||||
length += operandlength((struct modebyte *)
|
||||
(instructp + length));
|
||||
continue;
|
||||
|
@ -320,11 +300,7 @@ findcall( parentp , p_lowpc , p_highpc )
|
|||
/*
|
||||
* something funny going on.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tbut it's a botch\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
DBG(CALLDEBUG, printf("[findcall]\tbut it's a botch\n"));
|
||||
length = 1;
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
* offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see asgnsamples for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_OF_CODE 2
|
||||
#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
|
||||
#define OFFSET_TO_CODE 2
|
||||
#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT))
|
||||
|
||||
/*
|
||||
* register for pc relative addressing
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue