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.
+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
Mon Apr 4 23:47:30 1994 Jeffrey A. Law (law@snake.cs.utah.edu)
- * Makefile.in (MY_MACHINE): Renamed from MACHINE to avoid losing
- makes (osf1) in which the value of MACHINE can not be changed.
- * config/*.mt: Changed appropriately.
+ * Makefile.in (MY_MACHINE): Renamed from MACHINE to avoid losing
+ makes (osf1) in which the value of MACHINE can not be changed.
+ * config/*.mt: Changed appropriately.
Wed Mar 30 16:12:40 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com)
* Makefile.in: Undo 15 June change.
Wed Jun 16 12:54:53 1993 Steve Chamberlain (sac@phydeaux.cygnus.com)
-
+
* gmon.h, gprof.h: structs of chars used to hold external
representations.
* gprof.c (getpfile, openpfile, readsamples): Swap data in using
Fri Oct 9 11:25:41 1992 Mark Eichin (eichin@cygnus.com)
- * gprof.1: updated SYNOPSIS to match actual behavior.
+ * gprof.1: updated SYNOPSIS to match actual behavior.
Mon Oct 5 17:50:16 1992 Per Bothner (bothner@cygnus.com)
###
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) $<
# 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
--- /dev/null
+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.
--- /dev/null
+- 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
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+/*
+ * 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 ***/
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
- }
- }
-}
--- /dev/null
+/*
+ * 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 ***/
--- /dev/null
+#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 */
--- /dev/null
+#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 ***/
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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 ***/
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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 ***/
--- /dev/null
+#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 */
--- /dev/null
+#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 ***/
--- /dev/null
+#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 */
--- /dev/null
+MY_MACHINE=alpha
# per-target:
case "${target}" in
+alpha-*-*) my_target=alpha ;;
i[345]86-*-*) my_target=i386 ;;
sparc-*-*) my_target=sparc ;;
tahoe-*-*) my_target=tahoe ;;
--- /dev/null
+#include "libiberty.h"
+#include "gprof.h"
+#include "core.h"
+#include "symtab.h"
+
+bfd *core_bfd;
+int core_num_syms;
+asymbol **core_syms;
+asection *core_text_sect;
+PTR core_text_space;
+
+
+void
+DEFUN(core_init, (a_out_name), const char *a_out_name)
+{
+ core_bfd = bfd_openr(a_out_name, 0);
+
+ if (!core_bfd) {
+ perror(a_out_name);
+ done(1);
+ } /* if */
+
+ if (!bfd_check_format(core_bfd, bfd_object)) {
+ fprintf(stderr, "%s: %s: not in a.out format\n", whoami, a_out_name);
+ done(1);
+ } /* if */
+
+ /* get core's text section: */
+ core_text_sect = bfd_get_section_by_name(core_bfd, ".text");
+ if (!core_text_sect) {
+ core_text_sect = bfd_get_section_by_name(core_bfd, "$CODE$");
+ if (!core_text_sect) {
+ fprintf(stderr, "%s: can't find .text section in %s\n",
+ whoami, a_out_name);
+ done(1);
+ } /* if */
+ } /* if */
+
+ /* read core's symbol table: */
+
+ /* this will probably give us more than we need, but that's ok: */
+ core_num_syms = bfd_get_symtab_upper_bound(core_bfd);
+ if (core_num_syms < 0) {
+ fprintf(stderr, "%s: %s: %s\n", whoami, a_out_name,
+ bfd_errmsg(bfd_get_error()));
+ done(1);
+ } /* if */
+
+ core_syms = (asymbol**)xmalloc(core_num_syms);
+ core_num_syms = bfd_canonicalize_symtab(core_bfd, core_syms);
+ if (core_num_syms < 0) {
+ fprintf(stderr, "%s: %s: %s\n", whoami, a_out_name,
+ bfd_errmsg(bfd_get_error()));
+ done(1);
+ } /* if */
+} /* core_init */
+
+
+/*
+ * Read in the text space of an a.out file
+ */
+void
+DEFUN(core_get_text_space, (core_bfd), bfd *core_bfd)
+{
+ core_text_space = (PTR) malloc(core_text_sect->_raw_size);
+
+ if (!core_text_space) {
+ fprintf(stderr, "%s: ran out room for %ld bytes of text space\n",
+ whoami, core_text_sect->_raw_size);
+ done(1);
+ } /* if */
+ if (!bfd_get_section_contents(core_bfd, core_text_sect, core_text_space,
+ 0, core_text_sect->_raw_size))
+ {
+ bfd_perror("bfd_get_section_contents");
+ free(core_text_space);
+ core_text_space = 0;
+ } /* if */
+ if (!core_text_space) {
+ fprintf(stderr, "%s: can't do -c\n", whoami);
+ } /* if */
+} /* core_get_text_space */
+
+
+/*
+ * Return class of symbol SYM. The returned class can be any of:
+ * 0 -> symbol is not interesting to us
+ * 'T' -> symbol is a global name
+ * 't' -> symbol is a local (static) name
+ */
+static int
+DEFUN(core_sym_class, (sym), asymbol *sym)
+{
+ symbol_info syminfo;
+ const char *name;
+ char sym_prefix;
+ int i;
+
+ /*
+ * Must be a text symbol, and static text symbols don't qualify if
+ * ignore_static_funcs set.
+ */
+ if (!sym->section) {
+ return 0;
+ } /* if */
+
+ if (ignore_static_funcs && (sym->flags & BSF_LOCAL)) {
+ DBG(AOUTDEBUG, printf("[core_sym_class] %s: not a function\n",
+ sym->name));
+ return 0;
+ } /* if */
+
+ bfd_get_symbol_info(core_bfd, sym, &syminfo);
+ i = syminfo.type;
+
+ if (i == 'T') {
+ return i; /* it's a global symbol */
+ } /* if */
+
+ if (i != 't') {
+ /* not a static text symbol */
+ DBG(AOUTDEBUG, printf("[core_sym_class] %s is of class %c\n",
+ sym->name, i));
+ return 0;
+ } /* if */
+
+ /* do some more filtering on static function-names: */
+
+ if (ignore_static_funcs) {
+ return 0;
+ } /* if */
+ /*
+ * Can't zero-length name or funny characters in name, where
+ * `funny' includes: `.' (.o file names) and `$' (Pascal labels).
+ */
+ if (!sym->name || sym->name[0] == '\0') {
+ return 0;
+ } /* if */
+
+ for (name = sym->name; *name; ++name) {
+ if (*name == '.' || *name == '$') {
+ return 0;
+ } /* if */
+ } /* if */
+ /*
+ * On systems where the C compiler adds an underscore to all
+ * names, static names without underscores seem usually to be
+ * labels in hand written assembler in the library. We don't want
+ * these names. This is certainly necessary on a Sparc running
+ * SunOS 4.1 (try profiling a program that does a lot of
+ * division). I don't know whether it has harmful side effects on
+ * other systems. Perhaps it should be made configurable.
+ */
+ sym_prefix = bfd_get_symbol_leading_char(core_bfd);
+ if (sym_prefix && sym_prefix != sym->name[0]
+ /*
+ * GCC may add special symbols to help gdb figure out the file
+ * language. We want to ignore these, since sometimes they mask
+ * the real function. (dj@ctron)
+ */
+ || !strncmp (sym->name, "__gnu_compiled", 14)
+ || !strncmp (sym->name, "___gnu_compiled", 15))
+ {
+ return 0;
+ } /* if */
+ return 't'; /* it's a static text symbol */
+} /* core_sym_class */
+
+
+/*
+ * Get whatever source info we can get regarding address ADDR:
+ */
+static bool
+DEFUN(get_src_info, (addr, filename, name, line_num),
+ bfd_vma addr AND const char **filename AND const char **name
+ AND int *line_num)
+{
+ const char *fname = 0, *func_name = 0;
+ int l = 0;
+
+ if (bfd_find_nearest_line(core_bfd, core_text_sect, core_syms,
+ addr - core_text_sect->vma,
+ &fname, &func_name, &l)
+ && fname && func_name && l)
+ {
+ DBG(AOUTDEBUG, printf("[get_src_info] 0x%lx -> %s:%d (%s)\n",
+ addr, fname, l, func_name));
+ *filename = fname;
+ *name = func_name;
+ *line_num = l;
+ return TRUE;
+ } else {
+ DBG(AOUTDEBUG, printf("[get_src_info] no info for 0x%lx (%s:%d,%s)\n",
+ (long) addr, fname ? fname : "<unknown>", l,
+ func_name ? func_name : "<unknown>"));
+ return FALSE;
+ } /* if */
+} /* get_src_info */
+
+
+/*
+ * Read in symbol table from core. One symbol per function is
+ * entered.
+ */
+void
+DEFUN(core_create_function_syms, (core_bfd), bfd *core_bfd)
+{
+ bfd_vma min_vma = ~0, max_vma = 0;
+ const char *filename, *func_name;
+ int class;
+ long i;
+
+ /* pass 1 - determine upper bound on number of function names: */
+ symtab.len = 0;
+ for (i = 0; i < core_num_syms; ++i) {
+ if (!core_sym_class(core_syms[i])) {
+ continue;
+ } /* if */
+ ++symtab.len;
+ } /* for */
+
+ if (symtab.len == 0) {
+ fprintf(stderr, "%s: file `%s' has no symbols\n", whoami, a_out_name);
+ done(1);
+ } /* if */
+
+ /* the "+ 2" is for the sentinels: */
+ symtab.base = (Sym*)xmalloc((symtab.len + 2) * sizeof(Sym));
+
+ /* pass 2 - create symbols: */
+
+ symtab.limit = symtab.base;
+ for (i = 0; i < core_num_syms; ++i) {
+ class = core_sym_class(core_syms[i]);
+ if (!class) {
+ DBG(AOUTDEBUG,
+ printf("[core_create_function_syms] rejecting: 0x%lx %s\n",
+ core_syms[i]->value, core_syms[i]->name));
+ continue;
+ } /* if */
+
+ sym_init(symtab.limit);
+
+ /* symbol offsets are always section-relative: */
+
+ symtab.limit->addr = core_syms[i]->value + core_syms[i]->section->vma;
+ symtab.limit->name = core_syms[i]->name;
+
+#ifdef __osf__
+ /*
+ * Suppress symbols that are not function names. This is
+ * useful to suppress code-labels and aliases.
+ *
+ * This is known to be useful under DEC's OSF/1. Under SunOS 4.x,
+ * labels do not appear in the symbol table info, so this isn't
+ * necessary.
+ */
+ if (get_src_info(symtab.limit->addr, &filename, &func_name,
+ &symtab.limit->line_num))
+ {
+ symtab.limit->file = source_file_lookup_path(filename);
+
+ if (strcmp(symtab.limit->name, func_name) != 0) {
+ /*
+ * The symbol's address maps to a different name, so
+ * it can't be a function-entry point. This happens
+ * for labels, for example.
+ */
+ DBG(AOUTDEBUG,
+ printf("[core_create_function_syms: rej %s (maps to %s)\n",
+ symtab.limit->name, func_name));
+ continue;
+ } /* if */
+ } /* if */
+#endif
+
+ symtab.limit->is_func = TRUE;
+ symtab.limit->is_bb_head = TRUE;
+ if (class == 't') {
+ symtab.limit->is_static = TRUE;
+ } /* if */
+
+ min_vma = MIN(symtab.limit->addr, min_vma);
+ max_vma = MAX(symtab.limit->addr, max_vma);
+
+ /*
+ * If we see "main" without an initial '_', we assume names
+ * are *not* prefixed by '_'.
+ */
+ if (symtab.limit->name[0] == 'm' && discard_underscores
+ && strcmp(symtab.limit->name, "main") == 0)
+ {
+ discard_underscores = 0;
+ } /* if */
+
+ DBG(AOUTDEBUG, printf("[core_create_function_syms] %ld %s 0x%lx\n",
+ (long)(symtab.limit - symtab.base),
+ symtab.limit->name, symtab.limit->addr));
+ ++symtab.limit;
+ } /* for */
+
+ /* create sentinels: */
+
+ sym_init(symtab.limit);
+ symtab.limit->name = "<locore>";
+ symtab.limit->addr = 0;
+ symtab.limit->end_addr = min_vma - 1;
+ ++symtab.limit;
+
+ sym_init(symtab.limit);
+ symtab.limit->name = "<hicore>";
+ symtab.limit->addr = max_vma + 1;
+ symtab.limit->end_addr = ~0;
+ ++symtab.limit;
+
+ symtab.len = symtab.limit - symtab.base;
+ symtab_finalize(&symtab);
+} /* core_create_function_syms */
+
+
+/*
+ * Read in symbol table from core. One symbol per line of source code
+ * is entered.
+ */
+void
+DEFUN(core_create_line_syms, (core_bfd), bfd *core_bfd)
+{
+ char prev_name[PATH_MAX], prev_filename[PATH_MAX];
+ bfd_vma vma, min_vma = ~0, max_vma = 0;
+ bfd_vma offset, prev_offset, min_dist;
+ Sym *prev, dummy, *sentinel, *sym;
+ const char *filename;
+ int prev_line_num, i;
+ Sym_Table ltab;
+ /*
+ * Create symbols for functions as usual. This is necessary in
+ * cases where parts of a program were not compiled with -g. For
+ * those parts we still want to get info at the function level:
+ */
+ core_create_function_syms(core_bfd);
+
+ /* pass 1 - counter number of symbols: */
+
+ /*
+ * To find all line information, walk through all possible
+ * text-space addresses (one by one!) and get the debugging
+ * info for each address. When the debugging info changes,
+ * it is time to create a new symbol.
+ *
+ * Of course, this is rather slow and it would be better if
+ * bfd would provide an iterator for enumerating all line
+ * infos, but for now, we try to speed up the second pass
+ * by determining what the minimum code distance between two
+ * lines is.
+ */
+ prev_name[0] = '\0';
+ ltab.len = 0;
+ min_dist = core_text_sect->_raw_size;
+ prev_offset = -min_dist;
+ prev_filename[0] = '\0';
+ prev_line_num = 0;
+ for (offset = 0; offset < core_text_sect->_raw_size; ++offset) {
+ vma = core_text_sect->vma + offset;
+ if (!get_src_info(vma, &filename, &dummy.name, &dummy.line_num)
+ || (prev_line_num == dummy.line_num &&
+ strcmp(prev_name, dummy.name) == 0
+ && strcmp(prev_filename, filename) == 0))
+ {
+ continue;
+ } /* if */
+
+ ++ltab.len;
+ prev_line_num = dummy.line_num;
+ strcpy(prev_name, dummy.name);
+ strcpy(prev_filename, filename);
+
+ if (offset - prev_offset < min_dist) {
+ min_dist = offset - prev_offset;
+ } /* if */
+ prev_offset = offset;
+
+ min_vma = MIN(vma, min_vma);
+ max_vma = MAX(vma, max_vma);
+ } /* for */
+
+ DBG(AOUTDEBUG, printf("[core_create_line_syms] min_dist=%lx\n", min_dist));
+
+ /* make room for function symbols, too: */
+ ltab.len += symtab.len;
+ ltab.base = (Sym*) xmalloc(ltab.len * sizeof(Sym));
+ ltab.limit = ltab.base;
+
+ /* pass 2 - create symbols: */
+
+ prev = 0;
+ for (offset = 0; offset < core_text_sect->_raw_size; offset += min_dist) {
+ sym_init(ltab.limit);
+ if (!get_src_info(core_text_sect->vma + offset, &filename,
+ <ab.limit->name, <ab.limit->line_num)
+ || (prev && prev->line_num == ltab.limit->line_num
+ && strcmp(prev->name, ltab.limit->name) == 0
+ && strcmp(prev->file->name, filename) == 0))
+ {
+ continue;
+ } /* if */
+
+ /* make name pointer a malloc'ed string: */
+ ltab.limit->name = strdup(ltab.limit->name);
+ ltab.limit->file = source_file_lookup_path(filename);
+
+ ltab.limit->addr = core_text_sect->vma + offset;
+ prev = ltab.limit;
+
+ /*
+ * If we see "main" without an initial '_', we assume names
+ * are *not* prefixed by '_'.
+ */
+ if (ltab.limit->name[0] == 'm' && discard_underscores
+ && strcmp(ltab.limit->name, "main") == 0)
+ {
+ discard_underscores = 0;
+ } /* if */
+
+ DBG(AOUTDEBUG, printf("[core_create_line_syms] %d %s 0x%lx\n",
+ ltab.len, ltab.limit->name,
+ ltab.limit->addr));
+ ++ltab.limit;
+ } /* for */
+
+ /* update sentinels: */
+
+ sentinel = sym_lookup(&symtab, 0);
+ if (strcmp(sentinel->name, "<locore>") == 0
+ && min_vma <= sentinel->end_addr)
+ {
+ sentinel->end_addr = min_vma - 1;
+ } /* if */
+
+ sentinel = sym_lookup(&symtab, ~0);
+ if (strcmp(sentinel->name, "<hicore>") == 0 && max_vma >= sentinel->addr) {
+ sentinel->addr = max_vma + 1;
+ } /* if */
+
+ /* copy in function symbols: */
+ memcpy(ltab.limit, symtab.base, symtab.len * sizeof(Sym));
+ ltab.limit += symtab.len;
+
+ if (ltab.limit - ltab.base != ltab.len) {
+ fprintf(stderr,
+ "%s: somebody miscounted: ltab.len=%ld instead of %d\n",
+ whoami, (long) (ltab.limit - ltab.base), ltab.len);
+ done(1);
+ } /* if */
+
+ /* finalize ltab and make it symbol table: */
+
+ symtab_finalize(<ab);
+ free(symtab.base);
+ symtab = ltab;
+
+ /* now go through all core symbols and set is_static accordingly: */
+
+ for (i = 0; i < core_num_syms; ++i) {
+ if (core_sym_class(core_syms[i]) == 't') {
+ sym = sym_lookup(&symtab, core_syms[i]->value
+ + core_syms[i]->section->vma);
+ do {
+ sym++->is_static = TRUE;
+ } while (sym->file == sym[-1].file &&
+ strcmp(sym->name, sym[-1].name) == 0);
+ } /* if */
+ } /* for */
+
+} /* core_create_line_syms */
+
+ /*** end of core.c ***/
--- /dev/null
+#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 */
+++ /dev/null
-/*
- * 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;
-}
#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 ***/
*
* @(#)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.)
- */
-#define OFFSET_OF_CODE 0
-#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
+/*
+ * 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))
enum opermodes { dummy };
-typedef enum opermodes operandenum;
+typedef enum opermodes operandenum;
+
+#endif /* dummy_h */
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.
-\f
*
* @(#)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__
/*
- * histogram counters are unsigned shorts (according to the kernel).
+ * DEC's OSF v3.0 uses 4 bytes of padding to bring the header to
+ * a size that is a multiple of 8.
*/
-#define HISTCOUNTER unsigned short
+ char pad[4];
+#endif
+};
- /*
- * fraction of text space to allocate for histogram counters
- * here, 1/2
- */
+/*
+ * Histogram counters are unsigned shorts:
+ */
+#define HISTCOUNTER unsigned short
+
+/*
+ * 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.
- * Given MIN_SUBR_SEPARATION bytes of separation the value of
- * HASHFRACTION is calculated as:
- *
- * HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
- *
- * For the VAX, the shortest two call sequence is:
- *
- * calls $0,(r0)
- * calls $0,(r0)
- *
- * which is separated by only three bytes, thus HASHFRACTION is
- * calculated as:
- *
- * HASHFRACTION = 3 / (2 * 2 - 1) = 1
- *
- * Note that the division above rounds down, thus if MIN_SUBR_FRACTION
- * is less than three, this algorithm will not work!
- */
-#define HASHFRACTION 1
+/*
+ * 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:
+ *
+ * HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
+ *
+ * For the VAX, the shortest two call sequence is:
+ *
+ * calls $0,(r0)
+ * calls $0,(r0)
+ *
+ * which is separated by only three bytes, thus HASHFRACTION is
+ * calculated as:
+ *
+ * HASHFRACTION = 3 / (2 * 2 - 1) = 1
+ *
+ * Note that the division above rounds down, thus if MIN_SUBR_FRACTION
+ * is less than three, this algorithm will not work!
+ */
+#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.
- */
-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];
+/*
+ * 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 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 */
--- /dev/null
+/*
+ * 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 ***/
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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 */
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
+#include "getopt.h"
+#include "libiberty.h"
+#include "gprof.h"
+#include "basic_blocks.h"
+#include "call_graph.h"
+#include "cg_arcs.h"
+#include "cg_print.h"
+#include "core.h"
+#include "gmon_io.h"
+#include "hertz.h"
+#include "hist.h"
+#include "source.h"
+#include "sym_ids.h"
+
+#define VERSION "2.6"
+
+const char *whoami;
+const char *a_out_name = A_OUTNAME;
+long hz = HZ_WRONG;
-#define VERSION "5.6 (Cygnus)"
+/*
+ * Default options values:
+ */
+int debug_level = 0;
+int output_style = 0;
+int output_width = 80;
+bool bsd_style_output = FALSE;
+bool discard_underscores = TRUE;
+bool ignore_direct_calls = FALSE;
+bool ignore_static_funcs = FALSE;
+bool ignore_zeros = TRUE;
+bool line_granularity = FALSE;
+bool print_descriptions = TRUE;
+bool print_path = FALSE;
+File_Format file_format = FF_AUTO;
+
+bool first_output = TRUE;
-#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-static char sccsid[] = "@(#)gprof.c 5.6 (Berkeley) 6/1/90";
-#endif /* not lint */
-#include "gprof.h"
-
-#ifndef FOPEN_RB
-#define FOPEN_RB "r"
-#endif
+static char *gmon_name = GMONNAME; /* profile filename */
bfd *abfd;
-char *whoami;
+/*
+ * Functions that get excluded by default:
+ */
+static char *default_excluded_list[] = {
+ "_gprof_mcount", "mcount", "_mcount", "__mcleanup",
+ "<locore>", "<hicore>",
+ 0
+};
+static struct option long_options[] =
+{
+ {"line", no_argument, 0, 'l'},
+ {"no-static", no_argument, 0, 'a'},
+
+ /* output styles: */
+
+ {"annotated-source", optional_argument, 0, 'A'},
+ {"no-annotated-source", optional_argument, 0, 'J'},
+ {"flat-profile", optional_argument, 0, 'p'},
+ {"no-flat-profile", optional_argument, 0, 'P'},
+ {"graph", optional_argument, 0, 'q'},
+ {"no-graph", optional_argument, 0, 'Q'},
+ {"exec-counts", optional_argument, 0, 'C'},
+ {"no-exec-counts", optional_argument, 0, 'Z'},
+ {"file-info", no_argument, 0, 'i'},
+ {"sum", no_argument, 0, 's'},
+
+ /* various options to affect output: */
+
+ {"all-lines", no_argument, 0, 'x'},
+ {"directory-path", required_argument, 0, 'I'},
+ {"display-unused-functions", no_argument, 0, 'z'},
+ {"min-count", required_argument, 0, 'm'},
+ {"print-path", no_argument, 0, 'L'},
+ {"separate-files", no_argument, 0, 'y'},
+ {"static-call-graph", no_argument, 0, 'c'},
+ {"table-length", required_argument, 0, 't'},
+ {"time", required_argument, 0, 'n'},
+ {"no-time", required_argument, 0, 'N'},
+ {"width", required_argument, 0, 'w'},
/*
- * things which get -E excluded by default.
+ * These are for backwards-compatibility only. Their functionality
+ * is provided by the output style options already:
*/
-char *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
-
-int discard_underscores = 1; /* Should we discard initial underscores? */
-int bsd_style_output = 0; /* As opposed to FSF style output */
-
-main(argc, argv)
- int argc;
- char **argv;
+ {"", required_argument, 0, 'e'},
+ {"", required_argument, 0, 'E'},
+ {"", required_argument, 0, 'f'},
+ {"", required_argument, 0, 'F'},
+ {"", required_argument, 0, 'k'},
+
+ /* miscellaneous: */
+
+ {"brief", no_argument, 0, 'b'},
+ {"debug", optional_argument, 0, 'd'},
+ {"help", no_argument, 0, 'h'},
+ {"file-format", required_argument, 0, 'O'},
+ {"traditional", no_argument, 0, 'T'},
+ {"version", no_argument, 0, 'v'},
+ {0, no_argument, 0, 0}
+};
+
+
+static void
+DEFUN(usage, (stream, status), FILE *stream AND int status)
+{
+ fprintf(stream, "\
+Usage: %s [-[abchilLsTvwxyz]] [-[ACeEfFJnNOpPqQZ][name]] [-I dirs]\n\
+ [-d[num]] [-k from/to] [-m min-count] [-t table-length]\n\
+ [--[no-]annotated-source[=name]] [--[no-]exec-counts[=name]]\n\
+ [--[no-]flat-profile[=name]] [--[no-]graph[=name]]\n\
+ [--[no-]time=name] [--all-lines] [--brief] [--debug[=level]]\n\
+ [--directory-path=dirs] [--display-unused-functions]\n\
+ [--file-format=name] [--file-info] [--help] [--line] [--min-count=n]\n\
+ [--no-static] [--print-path] [--separate-files]\n\
+ [--static-call-graph] [--sum] [--table-length=len] [--traditional]\n\
+ [--version] [--width=n]\n\
+ [image-file] [profile-file...]\n",
+ whoami);
+ done(status);
+} /* usage */
+
+
+int
+DEFUN(main, (argc, argv), int argc AND char **argv)
{
- char **sp;
- nltype **timesortnlp;
+ char **sp, *str;
+ Sym **cg = 0;
+ int ch, user_specified = 0;
whoami = argv[0];
- --argc;
- argv++;
- debug = 0;
- bflag = TRUE;
- while ( *argv != 0 && **argv == '-' ) {
- (*argv)++;
- switch ( **argv ) {
- case 'a':
- aflag = TRUE;
+ xmalloc_set_program_name(whoami);
+
+ while ((ch = getopt_long(argc, argv,
+ "aA::bBcCd::e:E:f:F:hiI:J::k:lLm:n::N::O:p::P::q::Q::st:Tvw:xyzZ::",
+ long_options, 0))
+ != EOF)
+ {
+ switch (ch) {
+ case 'a': ignore_static_funcs = TRUE; break;
+ case 'A':
+ if (optarg) {
+ sym_id_add(optarg, INCL_ANNO);
+ } /* if */
+ output_style |= STYLE_ANNOTATED_SOURCE;
+ user_specified |= STYLE_ANNOTATED_SOURCE;
break;
- case 'b':
- bflag = FALSE;
+ case 'b': print_descriptions = FALSE; break;
+ case 'B':
+ output_style |= STYLE_CALL_GRAPH;
+ user_specified |= STYLE_CALL_GRAPH;
break;
- case 'c':
- cflag = TRUE;
+ case 'c': ignore_direct_calls = TRUE; break;
+ case 'C':
+ if (optarg) {
+ sym_id_add(optarg, INCL_EXEC);
+ } /* if */
+ output_style |= STYLE_EXEC_COUNTS;
+ user_specified |= STYLE_EXEC_COUNTS;
break;
- case 'd':
- dflag = TRUE;
- (*argv)++;
- debug |= atoi( *argv );
- debug |= ANYDEBUG;
-# ifdef DEBUG
- printf("[main] debug = %d\n", debug);
-# else not DEBUG
- printf("%s: -d ignored\n", whoami);
-# endif DEBUG
+ case 'd':
+ if (optarg) {
+ debug_level |= atoi(optarg);
+ debug_level |= ANYDEBUG;
+ } else {
+ debug_level = ~0;
+ } /* if */
+ DBG(ANYDEBUG, printf("[main] debug-level=0x%x\n", debug_level));
+#ifndef DEBUG
+ printf("%s: debugging not supported; -d ignored\n", whoami);
+#endif DEBUG
break;
- case 'E':
- ++argv;
- addlist( Elist , *argv );
- Eflag = TRUE;
- addlist( elist , *argv );
- eflag = TRUE;
+ case 'E': sym_id_add(optarg, EXCL_TIME);
+ case 'e': sym_id_add(optarg, EXCL_GRAPH); break;
+ case 'F': sym_id_add(optarg, INCL_TIME);
+ case 'f': sym_id_add(optarg, INCL_GRAPH); break;
+ case 'g': sym_id_add(optarg, EXCL_FLAT); break;
+ case 'G': sym_id_add(optarg, INCL_FLAT); break;
+ case 'h': usage(stdout, 0);
+ case 'i':
+ output_style |= STYLE_GMON_INFO;
+ user_specified |= STYLE_GMON_INFO;
break;
- case 'e':
- addlist( elist , *++argv );
- eflag = TRUE;
+ case 'I': search_list_append(&src_search_list, optarg); break;
+ case 'J':
+ if (optarg) {
+ sym_id_add(optarg, EXCL_ANNO);
+ output_style |= STYLE_ANNOTATED_SOURCE;
+ } else {
+ output_style &= ~STYLE_ANNOTATED_SOURCE;
+ } /* if */
+ user_specified |= STYLE_ANNOTATED_SOURCE;
break;
- case 'F':
- ++argv;
- addlist( Flist , *argv );
- Fflag = TRUE;
- addlist( flist , *argv );
- fflag = TRUE;
+ case 'k': sym_id_add(optarg, EXCL_ARCS); break;
+ case 'l': line_granularity = TRUE; break;
+ case 'L': print_path = TRUE; break;
+ case 'm': bb_min_calls = atoi(optarg); break;
+ case 'n': sym_id_add(optarg, INCL_TIME); break;
+ case 'N': sym_id_add(optarg, EXCL_TIME); break;
+ case 'O':
+ switch (optarg[0]) {
+ case 'a': file_format = FF_AUTO; break;
+ case 'm': file_format = FF_MAGIC; break;
+ case 'b': file_format = FF_BSD; break;
+ case 'p': file_format = FF_PROF; break;
+ default:
+ fprintf(stderr, "%s: unknown file format %s\n",
+ optarg, whoami);
+ done(1);
+ } /* switch */
break;
- case 'f':
- addlist( flist , *++argv );
- fflag = TRUE;
+ case 'p':
+ if (optarg) {
+ sym_id_add(optarg, INCL_FLAT);
+ } /* if */
+ output_style |= STYLE_FLAT_PROFILE;
+ user_specified |= STYLE_FLAT_PROFILE;
break;
- case 'k':
- addlist( kfromlist , *++argv );
- addlist( ktolist , *++argv );
- kflag = TRUE;
+ case 'P':
+ if (optarg) {
+ sym_id_add(optarg, EXCL_FLAT);
+ output_style |= STYLE_FLAT_PROFILE;
+ } else {
+ output_style &= ~STYLE_FLAT_PROFILE;
+ } /* if */
+ user_specified |= STYLE_FLAT_PROFILE;
break;
- case 's':
- sflag = TRUE;
+ case 'q':
+ if (optarg) {
+ if (strchr(optarg, '/')) {
+ sym_id_add(optarg, INCL_ARCS);
+ } else {
+ sym_id_add(optarg, INCL_GRAPH);
+ } /* if */
+ } /* if */
+ output_style |= STYLE_CALL_GRAPH;
+ user_specified |= STYLE_CALL_GRAPH;
break;
- case 'T': /* "Traditional" output format */
- bsd_style_output = 1;
+ case 'Q':
+ if (optarg) {
+ if (strchr(optarg, '/')) {
+ sym_id_add(optarg, EXCL_ARCS);
+ } else {
+ sym_id_add(optarg, EXCL_GRAPH);
+ } /* if */
+ output_style |= STYLE_CALL_GRAPH;
+ } else {
+ output_style &= ~STYLE_CALL_GRAPH;
+ } /* if */
+ user_specified |= STYLE_CALL_GRAPH;
break;
- case 'v':
- printf ("gprof version %s\n", VERSION);
- exit(0);
+ case 's':
+ output_style |= STYLE_SUMMARY_FILE;
+ user_specified |= STYLE_SUMMARY_FILE;
break;
- case 'z':
- zflag = TRUE;
+ case 't':
+ bb_table_length = atoi(optarg);
+ if (bb_table_length < 0) {
+ bb_table_length = 0;
+ } /* if */
break;
- default:
- fprintf (stderr, "\
-Usage: %s [-a] [-b] [-c] [-d[num]] [-E function-name] [-e function-name]\n\
- [-F function-name] [-f function-name] [-k from to] [-s] [-T] [-z]\n\
- [image-file] [profile-file...]\n", whoami);
- exit (1);
- }
- argv++;
- }
- if ( *argv != 0 ) {
- a_outname = *argv;
- argv++;
- } else {
- a_outname = A_OUTNAME;
- }
- if ( *argv != 0 ) {
- gmonname = *argv;
- argv++;
- } else {
- gmonname = GMONNAME;
- }
- /*
- * turn off default functions
- */
- for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
- Eflag = TRUE;
- addlist( Elist , *sp );
- eflag = TRUE;
- addlist( elist , *sp );
- }
- /*
- * how many ticks per second?
- * if we can't tell, report time in ticks.
- */
- hz = hertz();
- if (hz == 0) {
- hz = 1;
- fprintf(stderr, "time is in ticks, not seconds\n");
- }
- /*
- * get information about a.out file.
- */
- getnfile();
- /*
- * get information about mon.out file(s).
- */
- do {
- getpfile( gmonname );
- if ( *argv != 0 ) {
- gmonname = *argv;
- }
- } while ( *argv++ != 0 );
- /*
- * dump out a gmon.sum file if requested
- */
- if ( sflag ) {
- dumpsum( GMONSUM );
- }
- /*
- * assign samples to procedures
- */
- asgnsamples();
- /*
- * assemble the dynamic profile
- */
- timesortnlp = doarcs();
-
- if (bsd_style_output) {
- printgprof( timesortnlp ); /* print the dynamic profile */
- printprof(); /* print the flat profile */
- } else {
- printprof(); /* print the flat profile */
- printgprof( timesortnlp ); /* print the dynamic profile */
- }
- /*
- * print the index
- */
- printindex();
- done();
-}
+ case 'T': bsd_style_output = TRUE; break;
+ case 'v': printf ("%s version %s\n", whoami, VERSION); done(0);
+ case 'w':
+ output_width = atoi(optarg);
+ if (output_width < 1) {
+ output_width = 1;
+ } /* if */
+ break;
+ case 'x': bb_annotate_all_lines = TRUE; break;
+ case 'y': create_annotation_files = TRUE; break;
+ case 'z': ignore_zeros = FALSE; break;
+ case 'Z':
+ if (optarg) {
+ sym_id_add(optarg, EXCL_EXEC);
+ output_style |= STYLE_EXEC_COUNTS;
+ } else {
+ output_style &= ~STYLE_EXEC_COUNTS;
+ } /* if */
+ user_specified |= STYLE_ANNOTATED_SOURCE;
+ break;
+ default: usage(stderr, 1);
+ } /* switch */
+ } /* while */
+
+ /* append value of GPROF_PATH to source search list if set: */
+ str = getenv("GPROF_PATH");
+ if (str) {
+ search_list_append(&src_search_list, str);
+ } /* if */
+
+ if (optind < argc) {
+ a_out_name = argv[optind++];
+ } /* if */
+ if (optind < argc) {
+ gmon_name = argv[optind++];
+ } /* if */
/*
- * Set up string and symbol tables from a.out.
- * and optionally the text space.
- * On return symbol table is sorted by value.
+ * Turn off default functions:
*/
-getnfile()
-{
- int valcmp();
-
- abfd = bfd_openr (a_outname, NULL);
-
- if (abfd == NULL) {
- perror (a_outname);
- done();
- }
-
- if (!bfd_check_format (abfd, bfd_object)) {
- fprintf (stderr, "%s: %s: bad format\n", whoami, a_outname);
- done();
- }
-
-/* getstrtab(nfile); */
- getsymtab(abfd);
- gettextspace( abfd );
- qsort(nl, nname, sizeof(nltype), valcmp);
-
-# ifdef DEBUG
- if ( debug & AOUTDEBUG ) {
- register int j;
-
- for (j = 0; j < nname; j++){
- printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
- }
- }
-# endif DEBUG
-}
-
-/*
- * Read in symbol table
- */
-getsymtab(abfd)
-bfd *abfd;
-{
- register long i;
- int askfor;
- long nosyms;
- asymbol **syms;
- i = bfd_get_symtab_upper_bound (abfd);/* This will probably give us more
- * than we need, but that's ok.
- */
- if (i < 0)
- {
- fprintf (stderr, "%s: %s: %s\n", whoami, a_outname,
- bfd_errmsg (bfd_get_error ()));
- exit (1);
- }
- syms = (asymbol**)xmalloc (i);
- nosyms = bfd_canonicalize_symtab (abfd, syms);
- if (nosyms < 0)
- {
- fprintf (stderr, "%s: %s: %s\n", whoami, a_outname,
- bfd_errmsg (bfd_get_error ()));
- exit (1);
- }
-
- nname = 0;
- for (i = 0; i < nosyms; i++) {
- if (!funcsymbol (syms[i]))
- continue;
- nname++;
- }
-
- if (nname == 0) {
- fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
- done();
- }
- askfor = nname + 1;
- nl = (nltype *) calloc( askfor , sizeof(nltype) );
- if (nl == 0) {
- fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
- whoami, askfor * sizeof(nltype) );
- done();
- }
-
- /* pass2 - read symbols */
- npe = nl;
- nname = 0;
- for (i = 0; i < nosyms; i++) {
- if (!funcsymbol (syms[i])) {
-# ifdef DEBUG
- if ( debug & AOUTDEBUG ) {
- printf( "[getsymtab] rejecting: 0x%x %s\n" ,
- syms[i]->value, syms[i]->name);
- }
-# endif DEBUG
- continue;
- }
- /* Symbol offsets are always section-relative. */
- npe->value = syms[i]->value + syms[i]->section->vma;
- npe->name = syms[i]->name;
-
- /* If we see "main" without an initial '_', we assume
- names are *not* prefixed by '_'. */
- if (npe->name[0] == 'm' && discard_underscores
- && strcmp(npe->name, "main") == 0)
- discard_underscores = 0;
-
-# ifdef DEBUG
- if ( debug & AOUTDEBUG ) {
- printf( "[getsymtab] %d %s 0x%08x\n" ,
- nname , npe -> name , npe -> value );
- }
-# endif DEBUG
- npe++;
- nname++;
- }
- npe->value = -1;
-}
-
-/*
- * read in the text space of an a.out file
- */
-gettextspace( abfd )
- bfd *abfd;
-{
- asection *texsec;
-
- if ( cflag == 0 ) {
- return;
- }
-
- texsec = bfd_get_section_by_name (abfd, ".text");
- if (texsec == NULL) {
- return;
- }
-
- textspace = (u_char *) malloc( texsec->_cooked_size );
-
- if ( textspace == 0 ) {
- fprintf( stderr , "%s: ran out room for %d bytes of text space: " ,
- whoami , texsec->_cooked_size);
- fprintf( stderr , "can't do -c\n" );
- return;
- }
- bfd_get_section_contents (abfd, texsec, textspace, texsec->filepos,
- texsec->_cooked_size);
-}
-/*
- * information from a gmon.out file is in two parts:
- * an array of sampling hits within pc ranges,
- * and the arcs.
- */
-getpfile(filename)
- char *filename;
-{
- FILE *pfile;
- FILE *openpfile();
- struct rawarc arc;
- struct veryrawarc rawarc;
-
- pfile = openpfile(filename);
- readsamples(pfile);
- /*
- * the rest of the file consists of
- * a bunch of <from,self,count> tuples.
- */
- while ( fread( &rawarc , sizeof rawarc , 1 , pfile ) == 1 ) {
- arc.raw_frompc = bfd_get_32 (abfd, (bfd_byte *) rawarc.raw_frompc);
- arc.raw_selfpc = bfd_get_32 (abfd, (bfd_byte *) rawarc.raw_selfpc);
- arc.raw_count = bfd_get_32 (abfd, (bfd_byte *) rawarc.raw_count);
-# ifdef DEBUG
- if ( debug & SAMPLEDEBUG ) {
- printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
- arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
- }
-# endif DEBUG
- /*
- * add this arc
- */
- tally( &arc );
- }
- fclose(pfile);
-}
-
-FILE *
-openpfile(filename)
- char *filename;
-{
- struct hdr tmp;
- struct rawhdr raw;
- FILE *pfile;
-
- if((pfile = fopen(filename, FOPEN_RB)) == NULL) {
- perror(filename);
- done();
- }
- if (sizeof(struct rawhdr) != fread(&raw, 1, sizeof(struct rawhdr), pfile))
- {
- fprintf(stderr, "%s: file too short to be a gmon file\n", filename);
- done();
- }
- tmp.lowpc = (UNIT *)bfd_get_32 (abfd, (bfd_byte *) &raw.lowpc[0]);
- tmp.highpc = (UNIT *)bfd_get_32 (abfd, (bfd_byte *) &raw.highpc[0]);
- tmp.ncnt = bfd_get_32 (abfd, (bfd_byte *) &raw.ncnt[0]);
-
- if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
- tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
- fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
- done();
- }
- h = tmp;
- s_lowpc = (unsigned long) h.lowpc;
- s_highpc = (unsigned long) h.highpc;
- lowpc = (unsigned long)h.lowpc / sizeof(UNIT);
- highpc = (unsigned long)h.highpc / sizeof(UNIT);
- sampbytes = h.ncnt - sizeof(struct rawhdr);
- nsamples = sampbytes / sizeof (UNIT);
-# ifdef DEBUG
- if ( debug & SAMPLEDEBUG ) {
- printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
- h.lowpc , h.highpc , h.ncnt );
- printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" ,
- s_lowpc , s_highpc );
- printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" ,
- lowpc , highpc );
- printf( "[openpfile] sampbytes %d nsamples %d\n" ,
- sampbytes , nsamples );
- }
-# endif DEBUG
- return(pfile);
-}
-
-tally( rawp )
- struct rawarc *rawp;
-{
- nltype *parentp;
- nltype *childp;
-
- parentp = nllookup( rawp -> raw_frompc );
- childp = nllookup( rawp -> raw_selfpc );
- if ( kflag
- && onlist( kfromlist , parentp -> name )
- && onlist( ktolist , childp -> name ) ) {
- return;
- }
- childp -> ncall += rawp -> raw_count;
-# ifdef DEBUG
- if ( debug & TALLYDEBUG ) {
- printf( "[tally] arc from %s to %s traversed %d times\n" ,
- parentp -> name , childp -> name , rawp -> raw_count );
- }
-# endif DEBUG
- addarc( parentp , childp , rawp -> raw_count );
-}
+ for (sp = &default_excluded_list[0]; *sp; sp++) {
+ sym_id_add(*sp, EXCL_TIME);
+ sym_id_add(*sp, EXCL_GRAPH);
+#ifdef __osf__
+ sym_id_add(*sp, EXCL_FLAT);
+#endif
+ } /* for */
-/*
- * dump out the gmon.sum file
- */
-dumpsum( sumfile )
- char *sumfile;
-{
- register nltype *nlp;
- register arctype *arcp;
- struct rawarc arc;
- FILE *sfile;
-
- if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
- perror( sumfile );
- done();
- }
/*
- * dump the header; use the last header read in
+ * For line-by-line profiling, also want to keep those
+ * functions off the flat profile:
*/
- if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
- perror( sumfile );
- done();
- }
+ if (line_granularity) {
+ for (sp = &default_excluded_list[0]; *sp; sp++) {
+ sym_id_add(*sp, EXCL_FLAT);
+ } /* for */
+ } /* if */
+
/*
- * dump the samples
+ * Read symbol table from core file:
*/
- if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
- perror( sumfile );
- done();
- }
+ core_init(a_out_name);
+
/*
- * dump the normalized raw arc information
+ * If we should ignore direct function calls, we need to load
+ * to core's text-space:
*/
- for ( nlp = nl ; nlp < npe ; nlp++ ) {
- for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
- arc.raw_frompc = arcp -> arc_parentp -> value;
- arc.raw_selfpc = arcp -> arc_childp -> value;
- arc.raw_count = arcp -> arc_count;
- if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
- perror( sumfile );
- done();
- }
-# ifdef DEBUG
- if ( debug & SAMPLEDEBUG ) {
- printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
- arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
- }
-# endif DEBUG
- }
- }
- fclose( sfile );
-}
-
-valcmp(p1, p2)
- nltype *p1, *p2;
-{
- if ( p1 -> value < p2 -> value ) {
- return LESSTHAN;
- }
- if ( p1 -> value > p2 -> value ) {
- return GREATERTHAN;
- }
- return EQUALTO;
-}
-
-readsamples(pfile)
- FILE *pfile;
-{
- register int i;
-
-
- if (samples == 0) {
- samples = (int *) calloc (nsamples, sizeof(int));
- if (samples == 0) {
- fprintf( stderr , "%s: No room for %d sample pc's\n",
- whoami , nsamples);
- done();
- }
- }
- for (i = 0; i < nsamples; i++) {
- UNIT raw;
- int value;
-
- fread(raw, sizeof (raw), 1, pfile);
- value = bfd_get_16 (abfd, (bfd_byte *) raw);
- if (feof(pfile))
- break;
- samples[i] += value;
- }
- if (i != nsamples) {
- fprintf(stderr,
- "%s: unexpected EOF after reading %d/%d samples\n",
- whoami , --i , nsamples );
- done();
- }
-}
-
-/*
- * Assign samples to the procedures to which they belong.
- *
- * There are three cases as to where pcl and pch can be
- * with respect to the routine entry addresses svalue0 and svalue1
- * as shown in the following diagram. overlap computes the
- * distance between the arrows, the fraction of the sample
- * that is to be credited to the routine which starts at svalue0.
- *
- * svalue0 svalue1
- * | |
- * v v
- *
- * +-----------------------------------------------+
- * | |
- * | ->| |<- ->| |<- ->| |<- |
- * | | | | | |
- * +---------+ +---------+ +---------+
- *
- * ^ ^ ^ ^ ^ ^
- * | | | | | |
- * pcl pch pcl pch pcl pch
- *
- * 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 give call alignentries() to adjust the entry points if
- * the entry mask falls in one bucket but the code for the routine
- * doesn't start until the next bucket. 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).
- */
-asgnsamples()
-{
- register int j;
- int ccnt;
- double time;
- unsigned long pcl, pch;
- register int i;
- unsigned long overlap;
- unsigned long svalue0, svalue1;
-
- /* read samples and assign to namelist symbols */
- scale = highpc - lowpc;
- scale /= nsamples - 1;
- alignentries();
- for (i = 0, j = 1; i < nsamples; i++) {
- ccnt = samples[i];
- if (ccnt == 0)
- continue;
- pcl = lowpc + scale * i;
- pch = lowpc + scale * (i + 1);
- time = ccnt;
-# ifdef DEBUG
- if ( debug & SAMPLEDEBUG ) {
- printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
- pcl , pch , ccnt );
- }
-# endif DEBUG
- totime += time;
- for (j = j - 1; j < nname; j++) {
- svalue0 = nl[j].svalue;
- svalue1 = nl[j+1].svalue;
- /*
- * if high end of tick is below entry address,
- * go for next tick.
- */
- if (pch < svalue0)
- break;
- /*
- * if low end of tick into next routine,
- * go for next routine.
- */
- if (pcl >= svalue1)
- continue;
- overlap = min(pch, svalue1) - max(pcl, svalue0);
- if (overlap > 0) {
-# ifdef DEBUG
- if (debug & SAMPLEDEBUG) {
- printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
- nl[j].value/sizeof(UNIT), svalue0, svalue1,
- nl[j].name,
- overlap * time / scale, overlap);
- }
-# endif DEBUG
- nl[j].time += overlap * time / scale;
- }
- }
- }
-# ifdef DEBUG
- if (debug & SAMPLEDEBUG) {
- printf("[asgnsamples] totime %f\n", totime);
- }
-# endif DEBUG
-}
-
-
-unsigned long
-min(a, b)
- unsigned long a,b;
-{
- if (a<b)
- return(a);
- return(b);
-}
+ if (ignore_direct_calls) {
+ core_get_text_space(core_bfd);
+ } /* if */
-unsigned long
-max(a, b)
- unsigned long a,b;
-{
- if (a>b)
- return(a);
- return(b);
-}
+ /*
+ * Create symbols from core image:
+ */
+ if (line_granularity) {
+ core_create_line_syms(core_bfd);
+ } else {
+ core_create_function_syms(core_bfd);
+ } /* if */
/*
- * calculate scaled entry point addresses (to save time in asgnsamples),
- * and possibly push the scaled entry points over the entry mask,
- * if it turns out that the entry point is in one bucket and the code
- * for a routine is in the next bucket.
+ * Translate sym specs into syms:
*/
-alignentries()
-{
- register struct nl *nlp;
- unsigned long bucket_of_entry;
- unsigned long bucket_of_code;
-
- for (nlp = nl; nlp < npe; nlp++) {
- nlp -> svalue = nlp -> value / sizeof(UNIT);
- bucket_of_entry = (nlp->svalue - lowpc) / scale;
- bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
- if (bucket_of_entry < bucket_of_code) {
-# ifdef DEBUG
- if (debug & SAMPLEDEBUG) {
- printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
- nlp->svalue, nlp->svalue + UNITS_TO_CODE);
- }
-# endif DEBUG
- nlp->svalue += UNITS_TO_CODE;
- }
- }
-}
+ sym_id_parse();
-bool
-funcsymbol( symp )
- asymbol *symp;
-{
- extern char *strtab; /* string table from a.out */
- extern int aflag; /* if static functions aren't desired */
- CONST char *name;
- int i;
- char symprefix;
- symbol_info syminfo;
-
- /*
- * must be a text symbol,
- * and static text symbols don't qualify if aflag set.
- */
-
-
- if (!symp->section)
- return FALSE;
-
- if (aflag && (symp->flags&BSF_LOCAL)) {
-#if defined(DEBUG)
- fprintf (stderr, "%s(%d): %s: not a function\n", __FILE__, __LINE__, symp->name);
+ if (file_format == FF_PROF) {
+#ifdef PROF_SUPPORT_IMPLEMENTED
+ /*
+ * Get information about mon.out file(s):
+ */
+ do {
+ mon_out_read(gmon_name);
+ if (optind < argc) {
+ gmon_name = argv[optind];
+ } /* if */
+ } while (optind++ < argc);
+#else
+ fprintf(stderr,
+ "%s: sorry, file format `prof' is not yet supported\n",
+ whoami);
+ done(1);
#endif
- return FALSE;
- }
-
+ } else {
+ /*
+ * Get information about gmon.out file(s):
+ */
+ do {
+ gmon_out_read(gmon_name);
+ if (optind < argc) {
+ gmon_name = argv[optind];
+ } /* if */
+ } while (optind++ < argc);
+ } /* if */
- symprefix = bfd_get_symbol_leading_char (abfd);
- bfd_get_symbol_info (abfd, symp, &syminfo);
- i = syminfo.type;
-#if defined(DEBUG) && 0
- if (i != 'T' && i != 't')
- fprintf (stderr, "%s(%d): %s is of class %c\n", __FILE__, __LINE__, symp->name, i);
-#endif
+ /*
+ * If user did not specify output style, try to guess something
+ * reasonable:
+ */
+ if (output_style == 0) {
+ if (gmon_input & (INPUT_HISTOGRAM | INPUT_CALL_GRAPH)) {
+ output_style = STYLE_FLAT_PROFILE | STYLE_CALL_GRAPH;
+ } else {
+ output_style = STYLE_EXEC_COUNTS;
+ } /* if */
+ output_style &= ~user_specified;
+ } /* if */
- /*
- * Any external text symbol should be okay. (Only problem would be
- * variables in the text section.)
- */
-
- if (i == 'T')
- return TRUE;
-
- /*
- * 't' is static text; -a says to ignore it. So if it's not
- * a static text symbol, *or* it is and the user gave -a, we
- * ignore it.
- */
-
- if (i != 't' || aflag)
- return FALSE;
-
- /*
- * can't have any `funny' characters in name,
- * where `funny' includes `.', .o file names
- * and `$', pascal labels.
- */
- if (!symp->name)
- return FALSE;
-
- for (name = symp->name; *name; name++) {
- if ( *name == '.' || *name == '$' ) {
- return FALSE;
- }
- }
-
- /* 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.
- */
-
- if (symprefix && symprefix != *symp->name
- /* 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 (symp->name, "__gnu_compiled", 14)
- || !strncmp (symp->name, "___gnu_compiled", 15))
- return FALSE;
-
- return TRUE;
+ /*
+ * Dump a gmon.sum file if requested (before any other processing!):
+ */
+ if (output_style & STYLE_SUMMARY_FILE) {
+ gmon_out_write(GMONSUM);
+ } /* if */
+
+ if (gmon_input & INPUT_HISTOGRAM) {
+ hist_assign_samples();
+ } /* if */
+
+ if (gmon_input & INPUT_CALL_GRAPH) {
+ cg = cg_assemble();
+ } /* if */
+
+ /* do some simple sanity checks: */
+
+ if ((output_style & STYLE_FLAT_PROFILE)
+ && !(gmon_input & INPUT_HISTOGRAM))
+ {
+ fprintf(stderr, "%s: gmon.out file is missing histogram\n", whoami);
+ done(1);
+ } /* if */
+
+ if ((output_style & STYLE_CALL_GRAPH) && !(gmon_input & INPUT_CALL_GRAPH))
+ {
+ fprintf(stderr,
+ "%s: gmon.out file is missing call-graph data\n", whoami);
+ done(1);
+ } /* if */
+
+ /* output whatever user whishes to see: */
+
+ if (cg && (output_style & STYLE_CALL_GRAPH) && bsd_style_output) {
+ cg_print(cg); /* print the dynamic profile */
+ } /* if */
+
+ if (output_style & STYLE_FLAT_PROFILE) {
+ hist_print(); /* print the flat profile */
+ } /* if */
+
+ if (cg && (output_style & STYLE_CALL_GRAPH)) {
+ if (!bsd_style_output) {
+ cg_print(cg); /* print the dynamic profile */
+ } /* if */
+ cg_print_index();
+ } /* if */
+
+ if (output_style & STYLE_EXEC_COUNTS) {
+ print_exec_counts();
+ } /* if */
+
+ if (output_style & STYLE_ANNOTATED_SOURCE) {
+ print_annotated_source();
+ } /* if */
+ return 0;
}
-done()
+void
+done (status)
+ int status;
{
-
- exit(0);
+ exit(status);
}
*
* @(#)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
-#ifdef MACHINE_H
-# include MACHINE_H
+#ifdef MACHINE_H
+# include MACHINE_H
#else
-# if vax
-# include "vax.h"
-# endif
-# if sun
-# include "sun.h"
-# endif
-# if tahoe
-# include "tahoe.h"
-# endif
+# if vax
+# include "vax.h"
+# endif
+# if sun
+# include "sun.h"
+# endif
+# if tahoe
+# include "tahoe.h"
+# 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
- /*
- * who am i, for error messages.
- */
-char *whoami;
+#define A_OUTNAME "a.out" /* default core filename */
+#define GMONNAME "gmon.out" /* default profile filename */
+#define GMONSUM "gmon.sum" /* profile summary filename */
- /*
- * 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. */
+/*
+ * 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;
-
-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;
-
- /*
- * 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.
- */
-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;
-
- /*
- * 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;
-
-nltype *nl; /* the whole namelist */
-nltype *npe; /* the virtual end of the namelist */
-int nname; /* the number of function names */
-
- /*
- * 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;
+#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
-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 */
+typedef enum {
+ FF_AUTO = 0, FF_MAGIC, FF_BSD, FF_PROF
+} File_Format;
- /*
- * 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 */
+typedef int bool;
+typedef unsigned char UNIT[2]; /* unit of profiling */
- /*
- * 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;
+extern const char *whoami; /* command-name, for error messages */
+extern const char *a_out_name; /* core filename */
+extern long hz; /* ticks per second */
- /*
- * 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
+ * Command-line options:
+ */
+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 */
+
+extern bool first_output; /* no output so far? */
+
+extern void done PARAMS((int status));
+
+#endif /* gprof_h */
* 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
+# define HERTZ 18
#endif
+int
hertz()
{
#ifdef HERTZ
- return HERTZ;
+ return HERTZ;
#else
- struct itimerval tim;
+ struct itimerval tim;
- tim.it_interval.tv_sec = 0;
- tim.it_interval.tv_usec = 1;
- tim.it_value.tv_sec = 0;
- 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);
+ tim.it_interval.tv_sec = 0;
+ tim.it_interval.tv_usec = 1;
+ tim.it_value.tv_sec = 0;
+ 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;
+ } /* if */
+ return 1000000 / tim.it_interval.tv_usec;
#endif
-}
+} /* hertz */
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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 ***/
--- /dev/null
+#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 */
* 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"
-#ifndef lint
-static char sccsid[] = "@(#)tahoe.c 1.5 (Berkeley) 6/1/90";
-#endif /* not lint */
-#include "gprof.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;
+ unsigned char *instructp;
+ long length;
+ Sym *child;
+ bfd_vma destpc;
- if ( textspace == 0 ) {
+ if (core_text_space == 0) {
return;
}
- if ( p_lowpc < s_lowpc ) {
+ if (p_lowpc < s_lowpc) {
p_lowpc = s_lowpc;
}
- if ( p_highpc > s_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 ;
- instructp += length ) {
+ 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
+ if (iscall (instructp)) {
+ 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);
- 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 ) {
+ destpc = ((bfd_vma)instructp + 5 - (bfd_vma) core_text_space);
+ if (destpc >= s_lowpc && destpc <= s_highpc) {
+ 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;
}
/*
* 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("[findcall]\tbut it's a botch\n"));
+ length = 1;
+ continue;
}
}
}
* 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"
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
-
+++ /dev/null
-/*
- * 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;
-}
--- /dev/null
+#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 ***/
--- /dev/null
+#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 */
--- /dev/null
+/*
+ * 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 ***/
--- /dev/null
+#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 */
* 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"
-#ifndef lint
-static char sccsid[] = "@(#)tahoe.c 1.5 (Berkeley) 6/1/90";
-#endif /* not lint */
-#include "gprof.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;
-
- if ( textspace == 0 ) {
+ bfd_vma dest_pc, delta;
+ unsigned int *instr;
+ Sym *child;
+
+ delta = (bfd_vma) core_text_space - core_text_sect->vma;
+
+ if (core_text_space == 0) {
return;
- }
- if ( p_lowpc < s_lowpc ) {
+ } /* if */
+ if (p_lowpc < s_lowpc) {
p_lowpc = s_lowpc;
- }
- if ( p_highpc > s_highpc ) {
+ } /* 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 */
- /*
- * 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 */
- continue;
- }
- goto botched;
- }
- /*
- * else:
- * it looked like a callf,
- * but it wasn't to anywhere.
- */
- botched:
- /*
- * something funny going on.
- */
-# ifdef DEBUG
- if ( debug & CALLDEBUG ) {
- printf( "[findcall]\tbut it's a botch\n" );
- }
-# endif DEBUG
- length = 1;
- continue;
- }
- }
- }
+ } /* 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.
+ */
+ 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;
+ } /* if */
+ } /* if */
+ /*
+ * Something funny going on.
+ */
+ DBG(CALLDEBUG, printf("\tbut it's a botch\n"));
+ } /* if */
+ } /* for */
+} /* find_call */
+ /*** end of sparc.c ***/
* 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))
--- /dev/null
+#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 ***/
--- /dev/null
+#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 */
--- /dev/null
+#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 ***/
--- /dev/null
+#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 */
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
+#include "gprof.h"
+#include "time_host.h"
-#ifndef lint
-static char sccsid[] = "@(#)tahoe.c 1.5 (Berkeley) 6/1/90";
-#endif /* not lint */
-
-#include "gprof.h"
+/*
+ * A symbol to be the child of indirect callf:
+ */
+Sym indirectchild;
- /*
- * 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 */
- };
operandenum
-operandmode( modep )
+operandmode(modep)
unsigned char *modep;
{
- long usesreg = ((long)*modep) & 0xf;
+ long usesreg = ((long)*modep) & 0xf;
- switch ( ((long)*modep) >> 4 ) {
+ switch (((long)*modep) >> 4) {
case 0:
case 1:
case 2:
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 */
}
char *
-operandname( mode )
+operandname(mode)
operandenum mode;
{
- switch ( mode ) {
+ switch (mode) {
case literal:
return "literal";
case indexed:
}
long
-operandlength( modep )
+operandlength(modep)
unsigned char *modep;
{
- switch ( operandmode( modep ) ) {
+ switch (operandmode(modep)) {
case literal:
case reg:
case regdef:
case longreldef:
return 5;
case indexed:
- return 1+operandlength( modep + 1 );
+ return 1+operandlength(modep + 1);
}
/* NOTREACHED */
}
-unsigned long
-reladdr( modep )
+bfd_vma
+reladdr(modep)
char *modep;
{
- operandenum mode = operandmode( modep );
+ operandenum mode = operandmode(modep);
char *cp;
short *sp;
long *lp;
long value = 0;
cp = modep;
- cp += 1; /* skip over the mode */
- switch ( mode ) {
+ ++cp; /* skip over the mode */
+ switch (mode) {
default:
- fprintf( stderr , "[reladdr] not relative address\n" );
- return (unsigned long) modep;
+ fprintf(stderr, "[reladdr] not relative address\n");
+ 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;
- operandenum mode;
- operandenum firstmode;
- unsigned long destpc;
+ unsigned char *instructp;
+ long length;
+ Sym *child;
+ operandenum mode;
+ operandenum firstmode;
+ 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 (textspace == 0) {
return;
}
- if ( p_lowpc < s_lowpc ) {
+ if (p_lowpc < s_lowpc) {
p_lowpc = s_lowpc;
}
- if ( p_highpc > s_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 ;
+ 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 ) {
+ instructp += length) {
length = 1;
- if ( *instructp == CALLF ) {
+ if (*instructp == CALLF) {
/*
* 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
- firstmode = operandmode( instructp+length );
- switch ( firstmode ) {
+ DBG(CALLDEBUG, printf("[findcall]\t0x%x:callf",
+ instructp - textspace));
+ firstmode = operandmode(instructp+length);
+ switch (firstmode) {
case literal:
case immediate:
break;
default:
goto botched;
}
- length += operandlength( instructp+length );
- mode = operandmode( instructp + length );
-# ifdef DEBUG
- if ( debug & CALLDEBUG ) {
- printf( "\tfirst operand is %s", operandname( firstmode ) );
- printf( "\tsecond operand is %s\n" , operandname( mode ) );
- }
-# endif DEBUG
- switch ( mode ) {
+ length += operandlength(instructp+length);
+ mode = operandmode(instructp + length);
+ DBG(CALLDEBUG,
+ printf("\tfirst operand is %s", operandname(firstmode));
+ printf("\tsecond operand is %s\n", operandname(mode));
+ );
+ switch (mode) {
case regdef:
case bytedispdef:
case worddispdef:
* [are there others that we miss?,
* e.g. arrays of pointers to functions???]
*/
- addarc( parentp , &indirectchild , (long) 0 );
- length += operandlength( instructp + length );
+ arc_add(parent, &indirectchild, (long) 0);
+ length += operandlength(instructp + length);
continue;
case byterel:
case wordrel:
* check that this is the address of
* a function.
*/
- destpc = reladdr( instructp+length )
- - (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 ) {
+ destpc = reladdr(instructp+length)
+ - (bfd_vma) textspace;
+ if (destpc >= s_lowpc && destpc <= s_highpc) {
+ child = sym_lookup(destpc);
+ DBG(CALLDEBUG,
+ printf("[findcall]\tdestpc 0x%x", 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 );
- length += operandlength( instructp + length );
+ arc_add(parent, child, (long) 0);
+ length += operandlength(instructp + length);
continue;
}
goto botched;
/*
* 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;
}
* 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
--- /dev/null
+/*
+ * 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 ***/
--- /dev/null
+#ifndef utils_h
+#define utils_h
+
+extern int print_name_only PARAMS((Sym *self));
+extern void print_name PARAMS((Sym *self));
+
+#endif /* utils_h */
* 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"
+#include "vax.h"
-#ifndef lint
-static char sccsid[] = "@(#)vax.c 5.6 (Berkeley) 6/1/90";
-#endif /* not lint */
-
-#include "gprof.h"
+/*
+ * A symbol to be the child of indirect calls:
+ */
+Sym indirectchild;
- /*
- * a namelist entry 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 */
- };
-operandenum
-operandmode( modep )
+static operandenum
+operandmode(modep)
struct modebyte *modep;
{
- long usesreg = modep -> regfield;
+ long usesreg = modep->regfield;
- switch ( modep -> modefield ) {
+ switch (modep->modefield) {
case 0:
case 1:
case 2:
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 *
-operandname( mode )
+static char *
+operandname(mode)
operandenum mode;
{
- switch ( mode ) {
+ switch (mode) {
case literal:
return "literal";
case indexed:
/* NOTREACHED */
}
-long
-operandlength( modep )
+static long
+operandlength(modep)
struct modebyte *modep;
{
- switch ( operandmode( modep ) ) {
+ switch (operandmode(modep)) {
case literal:
case reg:
case regdef:
case longreldef:
return 5;
case indexed:
- return 1+operandlength( (struct modebyte *) ((char *) modep) + 1 );
+ return 1+operandlength((struct modebyte *) ((char *) modep) + 1);
}
/* NOTREACHED */
}
-unsigned long
-reladdr( modep )
+static bfd_vma
+reladdr(modep)
struct modebyte *modep;
{
- operandenum mode = operandmode( modep );
+ operandenum mode = operandmode(modep);
char *cp;
short *sp;
long *lp;
cp = (char *) modep;
- cp += 1; /* skip over the mode */
- switch ( mode ) {
+ ++cp; /* skip over the mode */
+ switch (mode) {
default:
- fprintf( stderr , "[reladdr] not relative address\n" );
- return (unsigned long) modep;
+ fprintf(stderr, "[reladdr] not relative address\n");
+ 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;
- operandenum mode;
- operandenum firstmode;
- unsigned long destpc;
+ unsigned char *instructp;
+ long length;
+ Sym *child;
+ operandenum mode;
+ operandenum firstmode;
+ 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 ) {
+ if (p_lowpc < s_lowpc) {
p_lowpc = s_lowpc;
}
- if ( p_highpc > s_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 ;
- instructp += length ) {
+ 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 ) {
+ if (*instructp == CALLS) {
/*
* 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
- firstmode = operandmode( (struct modebyte *) (instructp+length) );
- switch ( firstmode ) {
+ DBG(CALLDEBUG,
+ printf("[findcall]\t0x%x:calls",
+ instructp - (unsigned char*) core_text_space));
+ firstmode = operandmode((struct modebyte *) (instructp+length));
+ switch (firstmode) {
case literal:
case immediate:
break;
default:
goto botched;
}
- length += operandlength( (struct modebyte *) (instructp+length) );
- mode = operandmode( (struct modebyte *) ( instructp + length ) );
-# ifdef DEBUG
- if ( debug & CALLDEBUG ) {
- printf( "\tfirst operand is %s", operandname( firstmode ) );
- printf( "\tsecond operand is %s\n" , operandname( mode ) );
- }
-# endif DEBUG
- switch ( mode ) {
+ length += operandlength((struct modebyte *) (instructp+length));
+ mode = operandmode((struct modebyte *) (instructp + length));
+ DBG(CALLDEBUG,
+ printf("\tfirst operand is %s", operandname(firstmode));
+ printf("\tsecond operand is %s\n", operandname(mode)));
+ switch (mode) {
case regdef:
case bytedispdef:
case worddispdef:
* [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 ) );
+ (struct modebyte *) (instructp + length));
continue;
case byterel:
case wordrel:
* check that this is the address of
* a function.
*/
- destpc = reladdr( (struct modebyte *) (instructp+length) )
- - (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 ) {
+ destpc = reladdr((struct modebyte *) (instructp+length))
+ - (bfd_vma) core_text_space;
+ if (destpc >= s_lowpc && destpc <= s_highpc) {
+ 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 );
- length += operandlength( (struct modebyte *)
- ( instructp + length ) );
+ arc_add(parent, child, (long) 0);
+ length += operandlength((struct modebyte *)
+ (instructp + length));
continue;
}
goto botched;
/*
* 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;
}
* 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