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:
Ken Raeburn 1995-02-07 22:34:18 +00:00
parent 2559e01429
commit 5489fcc3d9
59 changed files with 6474 additions and 3400 deletions

1
gprof/.cvsignore Normal file
View file

@ -0,0 +1 @@
*_bl.c

1
gprof/.gdbinit Normal file
View file

@ -0,0 +1 @@
dir ..

View file

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

View file

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

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
MY_MACHINE=alpha

View file

@ -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
View 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,
&ltab.limit->name, &ltab.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(&ltab);
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
View 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 */

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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