From: Ken Raeburn Date: Tue, 7 Feb 1995 22:34:18 +0000 (+0000) Subject: Lots of changes from David Mosberger-Tang; see ChangeLog and NOTES for details: X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5489fcc3d9dbb1f529dddb19b615e23d8ed59dc7;p=binutils-gdb.git Lots of changes from David Mosberger-Tang; see ChangeLog and NOTES for details: Alpha support. Long options. New file format to support more information; backwards compatibility. Line-level profiling, on systems where bfd_find_nearest_line works. Selective display of data. --- diff --git a/gprof/.cvsignore b/gprof/.cvsignore new file mode 100644 index 00000000000..e41b2fb3b17 --- /dev/null +++ b/gprof/.cvsignore @@ -0,0 +1 @@ +*_bl.c diff --git a/gprof/.gdbinit b/gprof/.gdbinit new file mode 100644 index 00000000000..e519472ebcd --- /dev/null +++ b/gprof/.gdbinit @@ -0,0 +1 @@ +dir .. diff --git a/gprof/ChangeLog b/gprof/ChangeLog index 5ee36f53abd..4f7fe333a03 100644 --- a/gprof/ChangeLog +++ b/gprof/ChangeLog @@ -1,3 +1,337 @@ +Tue Feb 7 17:24:12 1995 Ken Raeburn + + * 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 + + * 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 + + * 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 + + * Lots of changes from David Mosberger-Tang: + + Tue Oct 25 19:20:14 1994 David Mosberger-Tang + + * 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 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): , added. + + * core.c (core_create_function_syms, core_create_line_syms): + explicitly keep two sentinels "" and "" 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 * printgprof.c (flatprofheader): Always set totime to 1.0 if not @@ -65,9 +399,9 @@ Thu May 5 19:23:24 1994 Ken Raeburn (raeburn@cujo.cygnus.com) 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) @@ -110,7 +444,7 @@ Sun Jun 20 20:58:02 1993 Ken Raeburn (raeburn@poseidon.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 @@ -175,7 +509,7 @@ Wed Jan 6 00:58:09 1993 david d `zoo' zuhn (zoo at cirdan.cygnus.com) 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) diff --git a/gprof/Makefile.in b/gprof/Makefile.in index b279755dab0..f310d25b3a8 100644 --- a/gprof/Makefile.in +++ b/gprof/Makefile.in @@ -34,18 +34,22 @@ texidir = $(srcdir)/../texinfo ### PROG= gprof -SRCS= gprof.c arcs.c dfn.c lookup.c $(MY_MACHINE).c hertz.c \ - printgprof.c printlist.c +SRCS= $(MY_MACHINE).c basic_blocks.c call_graph.c \ + cg_arcs.c cg_dfn.c cg_print.c core.c \ + gmon_io.c gprof.c hertz.c hist.c source.c search_list.c symtab.c \ + sym_ids.c utils.c + LIBS = ../bfd/libbfd.a ../libiberty/libiberty.a -OBJS= gprof.o arcs.o dfn.o lookup.o $(MY_MACHINE).o hertz.o \ - printgprof.o printlist.o \ - flat_bl.o bsd_callg_bl.o fsf_callg_bl.o +OBJS= $(MY_MACHINE).o basic_blocks.o bsd_callg_bl.o call_graph.o \ + cg_arcs.o cg_dfn.o cg_print.o core.o flat_bl.o fsf_callg_bl.o \ + gmon_io.o gprof.o hertz.o hist.o source.o search_list.o symtab.o \ + sym_ids.o utils.o # Files that can be generated, but should be included in distribution. DISTSTUFF = flat_bl.c bsd_callg_bl.c fsf_callg_bl.c -CFLAGS=-g +CFLAGS=-g -DDEBUG LDFLAGS= .c.o: $(CC) -c $(CFLAGS) -I. -I$(srcdir) -I../bfd -I$(srcdir)/../include -I$(srcdir)/../bfd -DMACHINE_H=\"$(MY_MACHINE).h\" $(TCFLAGS) $(HCFLAGS) $< @@ -116,14 +120,22 @@ Makefile : Makefile.in # These get around a bug in Sun Make in SunOS 4.1.1 and Solaris 2 -gprof.o: gprof.c -arcs.o: arcs.c -dfn.o: dfn.c -lookup.o: lookup.c $(MY_MACHINE).o: $(MY_MACHINE).c -hertz.o: hertz.c -printgprof.o: printgprof.c -printlist.o: printlist.c +basic_blocks.o: basic_blocks.c +bsd_call_bl.o: bsd_call_bl.c +call_graph.o: call_graph.c +cg_arcs.o: cg_arcs.c +cg_dfn.o: cg_dfn.c +cg_print.o: cg_print.c +core.o: core.c flat_bl.o: flat_bl.c -bsd_callg_bl.o: bsd_callg_bl.c fsf_callg_bl.o: fsf_callg_bl.c +gmon_io.o: gmon_io.c +gprof.o: gprof.c +hertz.o: hertz.c +hist.o: hist.c +search_list.o: search_list.c +source.o: source.c +symtab.o: symtab.c +sym_ids.o: sym_ids.c +utils.o: utils.c diff --git a/gprof/NOTES b/gprof/NOTES new file mode 100644 index 00000000000..511af302781 --- /dev/null +++ b/gprof/NOTES @@ -0,0 +1,438 @@ +Sun Feb 5 16:09:16 1995 + +This file documents the changes and new features available with this +version of GNU gprof. + +* New Features + + o Long options + + o Supports generalized file format, without breaking backward compatibility: + new file format supports basic-block execution counts and non-realtime + histograms (see below) + + o Supports profiling at the line level: flat profiles, call-graph profiles, + and execution-counts can all be displayed at a level that identifies + individual lines rather than just functions + + o Test-coverage support (similar to Sun tcov program): source files + can be annotated with the number of times a function was invoked + or with the number of times each basic-block in a function was + executed + + o Generalized histograms: not just execution-time, but arbitrary + histograms are support (for example, performance counter based + profiles) + + o Powerful mechanism to select data to be included/excluded from + analysis and/or output + + o Support for DEC OSF/1 v3.0 + + o Full cross-platform profiling support: gprof uses BFD to support + arbitrary, non-native object file formats and non-native byte-orders + (this feature has not been tested yet) + + o In the call-graph function index, static function names are now + printed together with the filename in which the function was defined + (required bfd_find_nearest_line() support and symbolic debugging + information to be present in the executable file) + + o Major overhaul of source code (compiles cleanly with -Wall, etc.) + +* Supported Platforms + +The current version is known to work on: + + o DEC OSF/1 v3.0 + All features supported. + + o SunOS 4.1.x + All features supported. + + o Solaris 2.3 + Line-level profiling unsupported because bfd_find_nearest_line() + is not fully implemented for Elf binaries. + + o HP-UX 9.01 + Line-level profiling unsupported because bfd_find_nearest_line() + is not fully implemented for SOM binaries. + +* Detailed Description + +** User Interface Changes + +The command-line interface is backwards compatible with earlier +versions of GNU gprof and Berkeley gprof. The only exception is +the option to delete arcs from the call graph. The old syntax +was: + + -k fromname toname + +while the new syntax is: + + -k fromname/toname + +This change was necessary to be compatible with long-option parsing. +Also, "fromname" and "toname" can now be arbitrary symspecs rather +than just function names (see below for an explanation of symspecs). +For example, option "-k gprof.c/" suppresses all arcs due to calls out +of file "gprof.c". + +*** Sym Specs + +It is often necessary to apply gprof only to specific parts of a +program. GNU gprof has a simple but powerful mechanism to achieve +this. So called {\em symspecs\/} provide the foundation for this +mechanism. A symspec selects the parts of a profiled program to which +an operation should be applied to. The syntax of a symspec is +simple: + + filename_containing_a_dot + | funcname_not_containing_a_dot + | linenumber + | ( [ any_filename ] `:' ( any_funcname | linenumber ) ) + +Here are some examples: + + main.c Selects everything in file "main.c"---the + dot in the string tells gprof to interpret + the string as a filename, rather than as + a function name. To select a file whose + name does contain a dot, a trailing colon + should be specified. For example, "odd:" is + interpreted as the file named "odd". + + main Selects all functions named "main". Notice + that there may be multiple instances of the + same function name because some of the + definitions may be local (i.e., static). + Unless a function name is unique in a program, + you must use the colon notation explained + below to specify a function from a specific + source file. Sometimes, functionnames contain + dots. In such cases, it is necessar to + add a leading colon to the name. For example, + ":.mul" selects function ".mul". + + main.c:main Selects function "main" in file "main.c". + + main.c:134 Selects line 134 in file "main.c". + +IMPLEMENTATION NOTE: The source code uses the type sym_id for symspecs. +At some point, this probably ought to be changed to "sym_spec" to make +reading the code easier. + +*** Long options + +GNU gprof now supports long options. The following is a list of all +supported options. Options that are listed without description +operate in the same manner as the corresponding option in older +versions of gprof. + +Short Form: Long Form: +----------- ---------- +-l --line + Request profiling at the line-level rather + than just at the function level. Source + lines are identified by symbols of the form: + + func (file:line) + + where "func" is the function name, "file" is the + file name and "line" is the line-number that + corresponds to the line. + + To work properly, the binary must contain symbolic + debugging information. This means that the source + have to be translated with option "-g" specified. + Functions for which there is no symbolic debugging + information available are treated as if "--line" + had not been specified. However, the line number + printed with such symbols is usually incorrect + and should be ignored. + +-a --no-static +-A[symspec] --annotated-source[=symspec] + Request output in the form of annotated source + files. If "symspec" is specified, print output only + for symbols selected by "symspec". If the option + is specified multiple times, annotated output is + generated for the union of all symspecs. + + Examples: + + -A Prints annotated source for all + source files. + -Agprof.c Prints annotated source for file + gprof.c. + -Afoobar Prints annotated source for files + containing a function named "foobar". + The entire file will be printed, but + only the function itself will be + annotated with profile data. + +-J[symspec] --no-annotated-source[=symspec] + Suppress annotated source output. If specified + without argument, annotated output is suppressed + completely. With an argument, annotated output + is suppressed only for the symbols selected by + "symspec". If the option is specified multiple + times, annotated output is suppressed for the + union of all symspecs. This option has lower + precedence than --annotated-source + +-p[symspec] --flat-profile[=symspec] + Request output in the form of a flat profile + (unless any other output-style option is specified, + this option is turned on by default). If + "symspec" is specified, include only symbols + selected by "symspec" in flat profile. If the + option is specified multiple times, the flat + profile includes symbols selected by the union + of all symspecs. + +-P[symspec] --no-flat-profile[=symspec] + Suppress output in the flat profile. If given + without an argument, the flat profile is suppressed + completely. If "symspec" is specified, suppress + the selected symbols in the flat profile. If the + option is specified multiple times, the union of + the selected symbols is suppressed. This option + has lower precedence than --flat-profile. + +-q[symspec] --graph[=symspec] + Request output in the form of a call-graph + (unless any other output-style option is specified, + this option is turned on by default). If "symspec" + is specified, include only symbols selected by + "symspec" in the call-graph. If the option is + specified multiple times, the call-graph includes + symbols selected by the union of all symspecs. + +-Q[symspec] --no-graph[=symspec] + Suppress output in the call-graph. If given without + an argument, the call-graph is suppressed completely. + With a "symspec", suppress the selected symbols + from the call-graph. If the option is specified + multiple times, the union of the selected symbols + is suppressed. This option has lower precedence + than --graph. + +-C[symspec] --exec-counts[=symspec] + Request output in the form of execution counts. + If "symspec" is present, include only symbols + selected by "symspec" in the execution count + listing. If the option is specified multiple + times, the execution count listing includes + symbols selected by the union of all symspecs. + +-Z[symspec] --no-exec-counts[=symspec] + Suppress output in the execution count listing. + If given without an argument, the listing is + suppressed completely. With a "symspec", suppress + the selected symbols from the call-graph. If the + option is specified multiple times, the union of + the selected symbols is suppressed. This option + has lower precedence than --exec-counts. + +-i --file-info + Print information about the profile files that + are read. The information consists of the + number and types of records present in the + profile file. Currently, a profile file can + contain any number and any combination of histogram, + call-graph, or basic-block count records. + +-s --sum + +-x --all-lines + This option affects annotated source output only. + By default, only the lines at the beginning of + a basic-block are annotated. If this option is + specified, every line in a basic-block is annotated + by repeating the annotation for the first line. + This option is identical to tcov's "-a". + +-I dirs --directory-path=dirs + This option affects annotated source output only. + Specifies the list of directories to be searched + for source files. The argument "dirs" is a colon + separated list of directories. By default, gprof + searches for source files relative to the current + working directory only. + +-z --display-unused-functions + +-m num --min-count=num + This option affects annotated source and execution + count output only. Symbols that are executed + less than "num" times are suppressed. For annotated + source output, suppressed symbols are marked + by five hash-marks (#####). In an execution count + output, suppressed symbols do not appear at all. + +-L --print-path + Normally, source filenames are printed with the path + component suppressed. With this option, gprof + can be forced to print the full pathname of + source filenames. The full pathname is determined + from symbolic debugging information in the image file + and is relative to the directory in which the compiler + was invoked. + +-y --separate-files + This option affects annotated source output only. + Normally, gprof prints annotated source files + to standard-output. If this option is specified, + annotated source for a file named "path/filename" + is generated in the file "filename-ann". That is, + annotated output is {\em always\/} generated in + gprof's current working directory. Care has to + be taken if a program consists of files that have + identical filenames, but distinct paths. + +-c --static-call-graph + +-t num --table-length=num + This option affects annotated source output only. + After annotating a source file, gprof generates + an execution count summary consisting of a table + of lines with the top execution counts. By + default, this table is ten entries long. + This option can be used to change the table length + or, by specifying an argument value of 0, it can be + suppressed completely. + +-n symspec --time=symspec + Only symbols selected by "symspec" are considered + in total and percentage time computations. + However, this option does not affect percentage time + computation for the flat profile. + If the option is specified multiple times, the union + of all selected symbols is used in time computations. + +-N --no-time=symspec + Exclude the symbols selected by "symspec" from + total and percentage time computations. + However, this option does not affect percentage time + computation for the flat profile. + This option is ignored if any --time options are + specified. + +-w num --width=num + Sets the output line width. Currently, this option + affects the printing of the call-graph function index + only. + +-e +-E +-f +-F +-k +-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. diff --git a/gprof/TEST b/gprof/TEST new file mode 100644 index 00000000000..15f688b402c --- /dev/null +++ b/gprof/TEST @@ -0,0 +1,7 @@ +- check whether old file format is properly read when input comes from + stdin + +- check whether underscores are properly dealt with (both, on systems + that prepend them to each C name and on systems that don't) + +- ensure gprof fails gracefully when no debugging info available \ No newline at end of file diff --git a/gprof/TODO b/gprof/TODO new file mode 100644 index 00000000000..222a87d34ae --- /dev/null +++ b/gprof/TODO @@ -0,0 +1,69 @@ +Sun Feb 5 16:27:32 1995 + +- documentation +- optimize bfd_find_nearest_line_num() (or replace by different interface) +- add support for prof file format so that prof files can be displayed + at the line-level (this is useful for the uprofile tool under DEC's + OSF/1) + ++ cleanup _bfd_ecoff_find_nearest_line_num() fixes & description ++ ensure "cc -pg" produces good files under OSF/1 v3.0 ++ make sure gprof works together with OSF/1 v3.0's profiling libraries ++ implement symtab_parse(); modify sym_lookup() to consider addr_high ++ change gprof.c to collect lists, then invoke symtab_parse() for + each list ++ Questions: + o is -c (--static-call-graph) useful at all? i can't see + how; if it were deleted, gprof would be completely machine + independent => yup, it is + o are (long) option names appropriate? + o -k (--exclude-arc) cannot be implemented with getopt(); + is new syntax (-k from/to) acceptable? If not, how to + fix it? + o in the FSF output, the call-graph index now prints + the filename of static functions in parentheses; e.g., + static function foo() that is defined in file bar.c + would be printed as: + + [4] foo (bar.c) + + is this acceptable? should it be done only optionally? + o symbols with addresses that map back to a different + name are suppressed (happens with labels, for example); + is this acceptable? should it be done only optionally? ++ generalize to allow arbitrary histograms (not just time histograms) ++ basic-block information currently replaces all symbols created from + the core because of an ugly ordering conflict---for now, the current + solution works, but something cleaner is desirable ==> cleaned up, + but it's slower now ++ convert to very new file format (back to trivial format, that is :) ++ replace "dummy.h" for Alpha (if there is any use to it) ++ add support for execution time profiling at a basic-block level ++ fix filename-off-by-one bug for Alpha (see ~/tmp/d.[ch])---no longer + relevant ++ "-pg -a" doesn't work as expected because mcleanup() will overwrite + the file generated by __bb_exit_func() (or vice versa) ++ first basic-block of fac() seems to get credited to last basic-block + of previous function => bug in basic_blocks.c ++ flat profile should provide automatic scaling for per-call times because + otherwise they'll always be zero on a fast machine with tons of small + functions ++ make "-a" imply to retain line number info (without actually generating + the debugging information (unless -g is specified)---no, this is a + bad idea, because it is not clear what level of debugging info should + be requested (e.g., -g vs. -g3); leaving it up to the user seems best ++ add long options support (or at least use getopt instead of ad-hoc + implementation) ++ split into files according to abstract objects that are manipulated ++ replace sccsid by rcsid & add "end of ..." to every .c file ++ use DBG() everywhere ++ fix spacing (" ," -> "," etc.) ++ use DEFUNs everywhere ++ make compile cleanly with -Wall ++ "gcc -pg -O2" doesn't work on tecc.c unless -fno-omit-frame-pointer is + specified; find out why ++ make things portable (prototypes, const, etc.) ++ if NEW_GMON_OUT is not defined, have a flag that will allow to + read new gmon.out style files. The idea being that everyone + will use the new format for basic-block style profiling but + the old format for regular gpprofiling diff --git a/gprof/alpha.c b/gprof/alpha.c new file mode 100644 index 00000000000..9051634a94a --- /dev/null +++ b/gprof/alpha.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include "gprof.h" +#include "cg_arcs.h" +#include "core.h" +#include "hist.h" +#include "symtab.h" + +/* + * Opcodes of the call instructions: + */ +#define OP_Jxx 0x1a +#define OP_BSR 0x34 + +#define Jxx_FUNC_JMP 0 +#define Jxx_FUNC_JSR 1 +#define Jxx_FUNC_RET 2 +#define Jxx_FUNC_JSR_COROUTINE 3 + +typedef union { + struct { + unsigned other : 26; + unsigned op_code : 6; + } a; /* any format */ + struct { + signed disp : 21; + unsigned ra : 5; + unsigned op_code : 6; + } b; /* branch format */ + struct { + signed hint : 14; + unsigned func : 2; + unsigned rb : 5; + unsigned ra : 5; + unsigned op_code : 6; + } j; /* jump format */ +} Instruction; + +static Sym indirect_child; + + +/* + * On the Alpha we can only detect PC relative calls, which are + * usually generated for calls to functions within the same + * object file only. This is still better than nothing, however. + * (In particular it should be possible to find functions that + * potentially call integer division routines, for example.) + */ +void +find_call(parent, p_lowpc, p_highpc) + Sym *parent; + bfd_vma p_lowpc; + bfd_vma p_highpc; +{ + static bfd_vma delta = 0; + bfd_vma dest_pc; + Instruction *pc; + Sym *child; + + if (!delta) { + delta = (bfd_vma) core_text_space - core_text_sect->vma; + + sym_init(&indirect_child); + indirect_child.name = ""; + indirect_child.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 \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 ***/ diff --git a/gprof/alpha.h b/gprof/alpha.h new file mode 100644 index 00000000000..d66f35911a3 --- /dev/null +++ b/gprof/alpha.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)alpha.h 1.4 (Berkeley) 6/1/90 + */ +#ifndef alpha_h +#define alpha_h + +/* + * Offset (in bytes) of the code from the entry address of a routine. + * (see hist_assign_samples for use and explanation.) + */ +#define OFFSET_TO_CODE 0 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) + +#endif /* alpha_h */ diff --git a/gprof/arcs.c b/gprof/arcs.c deleted file mode 100644 index 906114541dd..00000000000 --- a/gprof/arcs.c +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that: (1) source distributions retain this entire copyright - * notice and comment, and (2) distributions including binaries display - * the following acknowledgement: ``This product includes software - * developed by the University of California, Berkeley and its contributors'' - * in the documentation or other materials provided with the distribution - * and in all advertising materials mentioning features or use of this - * software. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#ifndef lint -static char sccsid[] = "@(#)arcs.c 5.6 (Berkeley) 6/1/90"; -#endif /* not lint */ - -#include "gprof.h" - - /* - * add (or just increment) an arc - */ -addarc( parentp , childp , count ) - nltype *parentp; - nltype *childp; - long count; -{ - arctype *arcp; - -# ifdef DEBUG - if ( debug & TALLYDEBUG ) { - printf( "[addarc] %d arcs from %s to %s\n" , - count , parentp -> name , childp -> name ); - } -# endif DEBUG - arcp = arclookup( parentp , childp ); - if ( arcp != 0 ) { - /* - * a hit: just increment the count. - */ -# ifdef DEBUG - if ( debug & TALLYDEBUG ) { - printf( "[tally] hit %d += %d\n" , - arcp -> arc_count , count ); - } -# endif DEBUG - arcp -> arc_count += count; - return; - } - arcp = (arctype *) calloc( 1 , sizeof *arcp ); - arcp -> arc_parentp = parentp; - arcp -> arc_childp = childp; - arcp -> arc_count = count; - /* - * prepend this child to the children of this parent - */ - arcp -> arc_childlist = parentp -> children; - parentp -> children = arcp; - /* - * prepend this parent to the parents of this child - */ - arcp -> arc_parentlist = childp -> parents; - childp -> parents = arcp; -} - - /* - * the code below topologically sorts the graph (collapsing cycles), - * and propagates time bottom up and flags top down. - */ - - /* - * the topologically sorted name list pointers - */ -nltype **topsortnlp; - -topcmp( npp1 , npp2 ) - nltype **npp1; - nltype **npp2; -{ - return (*npp1) -> toporder - (*npp2) -> toporder; -} - -nltype ** -doarcs() -{ - nltype *parentp, **timesortnlp; - arctype *arcp; - long index; - - /* - * initialize various things: - * zero out child times. - * count self-recursive calls. - * indicate that nothing is on cycles. - */ - for ( parentp = nl ; parentp < npe ; parentp++ ) { - parentp -> childtime = 0.0; - arcp = arclookup( parentp , parentp ); - if ( arcp != 0 ) { - parentp -> ncall -= arcp -> arc_count; - parentp -> selfcalls = arcp -> arc_count; - } else { - parentp -> selfcalls = 0; - } - parentp -> propfraction = 0.0; - parentp -> propself = 0.0; - parentp -> propchild = 0.0; - parentp -> printflag = FALSE; - parentp -> toporder = DFN_NAN; - parentp -> cycleno = 0; - parentp -> cyclehead = parentp; - parentp -> cnext = 0; - if ( cflag ) { - findcall( parentp , parentp -> value , (parentp+1) -> value ); - } - } - /* - * topologically order things - * if any node is unnumbered, - * number it and any of its descendents. - */ - for ( parentp = nl ; parentp < npe ; parentp++ ) { - if ( parentp -> toporder == DFN_NAN ) { - dfn( parentp ); - } - } - /* - * link together nodes on the same cycle - */ - cyclelink(); - /* - * Sort the symbol table in reverse topological order - */ - topsortnlp = (nltype **) calloc( nname , sizeof(nltype *) ); - if ( topsortnlp == (nltype **) 0 ) { - fprintf( stderr , "[doarcs] ran out of memory for topo sorting\n" ); - } - for ( index = 0 ; index < nname ; index += 1 ) { - topsortnlp[ index ] = &nl[ index ]; - } - qsort( topsortnlp , nname , sizeof(nltype *) , topcmp ); -# ifdef DEBUG - if ( debug & DFNDEBUG ) { - printf( "[doarcs] topological sort listing\n" ); - for ( index = 0 ; index < nname ; index += 1 ) { - printf( "[doarcs] " ); - printf( "%d:" , topsortnlp[ index ] -> toporder ); - printname( topsortnlp[ index ] ); - printf( "\n" ); - } - } -# endif DEBUG - /* - * starting from the topological top, - * propagate print flags to children. - * also, calculate propagation fractions. - * this happens before time propagation - * since time propagation uses the fractions. - */ - doflags(); - /* - * starting from the topological bottom, - * propogate children times up to parents. - */ - dotime(); - /* - * Now, sort by propself + propchild. - * sorting both the regular function names - * and cycle headers. - */ - timesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) ); - if ( timesortnlp == (nltype **) 0 ) { - fprintf( stderr , "%s: ran out of memory for sorting\n" , whoami ); - } - for ( index = 0 ; index < nname ; index++ ) { - timesortnlp[index] = &nl[index]; - } - for ( index = 1 ; index <= ncycle ; index++ ) { - timesortnlp[nname+index-1] = &cyclenl[index]; - } - qsort( timesortnlp , nname + ncycle , sizeof(nltype *) , totalcmp ); - for ( index = 0 ; index < nname + ncycle ; index++ ) { - timesortnlp[ index ] -> index = index + 1; - } - return( timesortnlp ); -} - -dotime() -{ - int index; - - cycletime(); - for ( index = 0 ; index < nname ; index += 1 ) { - timepropagate( topsortnlp[ index ] ); - } -} - -timepropagate( parentp ) - nltype *parentp; -{ - arctype *arcp; - nltype *childp; - double share; - double propshare; - - if ( parentp -> propfraction == 0.0 ) { - return; - } - /* - * gather time from children of this parent. - */ - for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) { - childp = arcp -> arc_childp; - if ( arcp -> arc_count == 0 ) { - continue; - } - if ( childp == parentp ) { - continue; - } - if ( childp -> propfraction == 0.0 ) { - continue; - } - if ( childp -> cyclehead != childp ) { - if ( parentp -> cycleno == childp -> cycleno ) { - continue; - } - if ( parentp -> toporder <= childp -> toporder ) { - fprintf( stderr , "[propagate] toporder botches\n" ); - } - childp = childp -> cyclehead; - } else { - if ( parentp -> toporder <= childp -> toporder ) { - fprintf( stderr , "[propagate] toporder botches\n" ); - continue; - } - } - if ( childp -> ncall == 0 ) { - continue; - } - /* - * distribute time for this arc - */ - arcp -> arc_time = childp -> time - * ( ( (double) arcp -> arc_count ) / - ( (double) childp -> ncall ) ); - arcp -> arc_childtime = childp -> childtime - * ( ( (double) arcp -> arc_count ) / - ( (double) childp -> ncall ) ); - share = arcp -> arc_time + arcp -> arc_childtime; - parentp -> childtime += share; - /* - * ( 1 - propfraction ) gets lost along the way - */ - propshare = parentp -> propfraction * share; - /* - * fix things for printing - */ - parentp -> propchild += propshare; - arcp -> arc_time *= parentp -> propfraction; - arcp -> arc_childtime *= parentp -> propfraction; - /* - * add this share to the parent's cycle header, if any. - */ - if ( parentp -> cyclehead != parentp ) { - parentp -> cyclehead -> childtime += share; - parentp -> cyclehead -> propchild += propshare; - } -# ifdef DEBUG - if ( debug & PROPDEBUG ) { - printf( "[dotime] child \t" ); - printname( childp ); - printf( " with %f %f %d/%d\n" , - childp -> time , childp -> childtime , - arcp -> arc_count , childp -> ncall ); - printf( "[dotime] parent\t" ); - printname( parentp ); - printf( "\n[dotime] share %f\n" , share ); - } -# endif DEBUG - } -} - -cyclelink() -{ - register nltype *nlp; - register nltype *cyclenlp; - int cycle; - nltype *memberp; - arctype *arcp; - - /* - * Count the number of cycles, and initialze the cycle lists - */ - ncycle = 0; - for ( nlp = nl ; nlp < npe ; nlp++ ) { - /* - * this is how you find unattached cycles - */ - if ( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) { - ncycle += 1; - } - } - /* - * cyclenl is indexed by cycle number: - * i.e. it is origin 1, not origin 0. - */ - cyclenl = (nltype *) calloc( ncycle + 1 , sizeof( nltype ) ); - if ( cyclenl == 0 ) { - fprintf( stderr , "%s: No room for %d bytes of cycle headers\n" , - whoami , ( ncycle + 1 ) * sizeof( nltype ) ); - done(); - } - /* - * now link cycles to true cycleheads, - * number them, accumulate the data for the cycle - */ - cycle = 0; - for ( nlp = nl ; nlp < npe ; nlp++ ) { - if ( !( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) ) { - continue; - } - cycle += 1; - cyclenlp = &cyclenl[cycle]; - cyclenlp -> name = 0; /* the name */ - cyclenlp -> value = 0; /* the pc entry point */ - cyclenlp -> time = 0.0; /* ticks in this routine */ - cyclenlp -> childtime = 0.0; /* cumulative ticks in children */ - cyclenlp -> ncall = 0; /* how many times called */ - cyclenlp -> selfcalls = 0; /* how many calls to self */ - cyclenlp -> propfraction = 0.0; /* what % of time propagates */ - cyclenlp -> propself = 0.0; /* how much self time propagates */ - cyclenlp -> propchild = 0.0; /* how much child time propagates */ - cyclenlp -> printflag = TRUE; /* should this be printed? */ - cyclenlp -> index = 0; /* index in the graph list */ - cyclenlp -> toporder = DFN_NAN; /* graph call chain top-sort order */ - cyclenlp -> cycleno = cycle; /* internal number of cycle on */ - cyclenlp -> cyclehead = cyclenlp; /* pointer to head of cycle */ - cyclenlp -> cnext = nlp; /* pointer to next member of cycle */ - cyclenlp -> parents = 0; /* list of caller arcs */ - cyclenlp -> children = 0; /* list of callee arcs */ -# ifdef DEBUG - if ( debug & CYCLEDEBUG ) { - printf( "[cyclelink] " ); - printname( nlp ); - printf( " is the head of cycle %d\n" , cycle ); - } -# endif DEBUG - /* - * link members to cycle header - */ - for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) { - memberp -> cycleno = cycle; - memberp -> cyclehead = cyclenlp; - } - /* - * count calls from outside the cycle - * and those among cycle members - */ - for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) { - for ( arcp=memberp->parents ; arcp ; arcp=arcp->arc_parentlist ) { - if ( arcp -> arc_parentp == memberp ) { - continue; - } - if ( arcp -> arc_parentp -> cycleno == cycle ) { - cyclenlp -> selfcalls += arcp -> arc_count; - } else { - cyclenlp -> ncall += arcp -> arc_count; - } - } - } - } -} - -cycletime() -{ - int cycle; - nltype *cyclenlp; - nltype *childp; - - for ( cycle = 1 ; cycle <= ncycle ; cycle += 1 ) { - cyclenlp = &cyclenl[ cycle ]; - for ( childp = cyclenlp -> cnext ; childp ; childp = childp -> cnext ) { - if ( childp -> propfraction == 0.0 ) { - /* - * all members have the same propfraction except those - * that were excluded with -E - */ - continue; - } - cyclenlp -> time += childp -> time; - } - cyclenlp -> propself = cyclenlp -> propfraction * cyclenlp -> time; - } -} - - /* - * in one top to bottom pass over the topologically sorted namelist - * propagate: - * printflag as the union of parents' printflags - * propfraction as the sum of fractional parents' propfractions - * and while we're here, sum time for functions. - */ -doflags() -{ - int index; - nltype *childp; - nltype *oldhead; - - oldhead = 0; - for ( index = nname-1 ; index >= 0 ; index -= 1 ) { - childp = topsortnlp[ index ]; - /* - * if we haven't done this function or cycle, - * inherit things from parent. - * this way, we are linear in the number of arcs - * since we do all members of a cycle (and the cycle itself) - * as we hit the first member of the cycle. - */ - if ( childp -> cyclehead != oldhead ) { - oldhead = childp -> cyclehead; - inheritflags( childp ); - } -# ifdef DEBUG - if ( debug & PROPDEBUG ) { - printf( "[doflags] " ); - printname( childp ); - printf( " inherits printflag %d and propfraction %f\n" , - childp -> printflag , childp -> propfraction ); - } -# endif DEBUG - if ( ! childp -> printflag ) { - /* - * printflag is off - * it gets turned on by - * being on -f list, - * or there not being any -f list and not being on -e list. - */ - if ( onlist( flist , childp -> name ) - || ( !fflag && !onlist( elist , childp -> name ) ) ) { - childp -> printflag = TRUE; - } - } else { - /* - * this function has printing parents: - * maybe someone wants to shut it up - * by putting it on -e list. (but favor -f over -e) - */ - if ( ( !onlist( flist , childp -> name ) ) - && onlist( elist , childp -> name ) ) { - childp -> printflag = FALSE; - } - } - if ( childp -> propfraction == 0.0 ) { - /* - * no parents to pass time to. - * collect time from children if - * its on -F list, - * or there isn't any -F list and its not on -E list. - */ - if ( onlist( Flist , childp -> name ) - || ( !Fflag && !onlist( Elist , childp -> name ) ) ) { - childp -> propfraction = 1.0; - } - } else { - /* - * it has parents to pass time to, - * but maybe someone wants to shut it up - * by puttting it on -E list. (but favor -F over -E) - */ - if ( !onlist( Flist , childp -> name ) - && onlist( Elist , childp -> name ) ) { - childp -> propfraction = 0.0; - } - } - childp -> propself = childp -> time * childp -> propfraction; - printtime += childp -> propself; -# ifdef DEBUG - if ( debug & PROPDEBUG ) { - printf( "[doflags] " ); - printname( childp ); - printf( " ends up with printflag %d and propfraction %f\n" , - childp -> printflag , childp -> propfraction ); - printf( "time %f propself %f printtime %f\n" , - childp -> time , childp -> propself , printtime ); - } -# endif DEBUG - } -} - - /* - * check if any parent of this child - * (or outside parents of this cycle) - * have their print flags on and set the - * print flag of the child (cycle) appropriately. - * similarly, deal with propagation fractions from parents. - */ -inheritflags( childp ) - nltype *childp; -{ - nltype *headp; - arctype *arcp; - nltype *parentp; - nltype *memp; - - headp = childp -> cyclehead; - if ( childp == headp ) { - /* - * just a regular child, check its parents - */ - childp -> printflag = FALSE; - childp -> propfraction = 0.0; - for (arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist) { - parentp = arcp -> arc_parentp; - if ( childp == parentp ) { - continue; - } - childp -> printflag |= parentp -> printflag; - /* - * if the child was never actually called - * (e.g. this arc is static (and all others are, too)) - * no time propagates along this arc. - */ - if ( childp -> ncall ) { - childp -> propfraction += parentp -> propfraction - * ( ( (double) arcp -> arc_count ) - / ( (double) childp -> ncall ) ); - } - } - } else { - /* - * its a member of a cycle, look at all parents from - * outside the cycle - */ - headp -> printflag = FALSE; - headp -> propfraction = 0.0; - for ( memp = headp -> cnext ; memp ; memp = memp -> cnext ) { - for (arcp = memp->parents ; arcp ; arcp = arcp->arc_parentlist) { - if ( arcp -> arc_parentp -> cyclehead == headp ) { - continue; - } - parentp = arcp -> arc_parentp; - headp -> printflag |= parentp -> printflag; - /* - * if the cycle was never actually called - * (e.g. this arc is static (and all others are, too)) - * no time propagates along this arc. - */ - if ( headp -> ncall ) { - headp -> propfraction += parentp -> propfraction - * ( ( (double) arcp -> arc_count ) - / ( (double) headp -> ncall ) ); - } - } - } - for ( memp = headp ; memp ; memp = memp -> cnext ) { - memp -> printflag = headp -> printflag; - memp -> propfraction = headp -> propfraction; - } - } -} diff --git a/gprof/basic_blocks.c b/gprof/basic_blocks.c new file mode 100644 index 00000000000..7d69db6cc62 --- /dev/null +++ b/gprof/basic_blocks.c @@ -0,0 +1,452 @@ +/* + * Basic-block level related code: reading/writing of basic-block info + * to/from gmon.out; computing and formatting of basic-block related + * statistics. + */ +#include +#include +#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: + * + * :: (: + */ +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 : "", 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 ***/ diff --git a/gprof/basic_blocks.h b/gprof/basic_blocks.h new file mode 100644 index 00000000000..0e7070c6c8b --- /dev/null +++ b/gprof/basic_blocks.h @@ -0,0 +1,23 @@ +#ifndef basic_blocks_h +#define basic_blocks_h + +#include +#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 */ diff --git a/gprof/call_graph.c b/gprof/call_graph.c new file mode 100644 index 00000000000..bba8b8ce1a4 --- /dev/null +++ b/gprof/call_graph.c @@ -0,0 +1,96 @@ +#include "cg_arcs.h" +#include "call_graph.h" +#include "core.h" +#include "gmon_io.h" +#include "gmon_out.h" +#include "symtab.h" +#include "sym_ids.h" + +extern void +DEFUN(cg_tally, (from_pc, self_pc, count), + bfd_vma from_pc AND bfd_vma self_pc AND int count) +{ + Sym *parent; + Sym *child; + + parent = sym_lookup(&symtab, from_pc); + child = sym_lookup(&symtab, self_pc); + + /* + * Keep arc if it is on INCL_ARCS table or if the INCL_ARCS table + * is empty and it is not in the EXCL_ARCS table. + */ + if (sym_id_arc_is_present(&syms[INCL_ARCS], parent, child) + || (syms[INCL_ARCS].len == 0 + && !sym_id_arc_is_present(&syms[EXCL_ARCS], parent, child))) + { + child->ncalls += count; + DBG(TALLYDEBUG, + printf("[cg_tally] arc from %s to %s traversed %d times\n", + parent->name, child->name, count)); + arc_add(parent, child, count); + } /* if */ +} /* cg_tally */ + + +/* + * Read a record from file IFP describing an arc in the function + * call-graph and the count of how many times the arc has been + * traversed. FILENAME is the name of file IFP and is provided + * for formatting error-messages only. + */ +void +DEFUN(cg_read_rec, (ifp, filename), FILE *ifp AND CONST char *filename) +{ + bfd_vma from_pc, self_pc; + struct gmon_cg_arc_record arc; + int count; + + if (fread(&arc, sizeof(arc), 1, ifp) != 1) { + fprintf(stderr, "%s: %s: unexpected end of file\n", + whoami, filename); + done(1); + } /* if */ + from_pc = get_vma(core_bfd, (bfd_byte *)arc.from_pc); + self_pc = get_vma(core_bfd, (bfd_byte *)arc.self_pc); + count = bfd_get_32(core_bfd, (bfd_byte *)arc.count); + DBG(SAMPLEDEBUG, + printf("[cg_read_rec] frompc 0x%lx selfpc 0x%lx count %d\n", + from_pc, self_pc, count)); + /* add this arc: */ + cg_tally(from_pc, self_pc, count); +} /* cg_read_rec */ + + +/* + * Write all the arcs in the call-graph to file OFP. FILENAME is + * the name of OFP and is provided for formatting error-messages + * only. + */ +void +DEFUN(cg_write_arcs, (ofp, filename), FILE *ofp AND const char *filename) +{ + const unsigned char tag = GMON_TAG_CG_ARC; + struct gmon_cg_arc_record raw_arc; + Arc *arc; + Sym *sym; + + for (sym = symtab.base; sym < symtab.limit; sym++) { + for (arc = sym->cg.children; arc; arc = arc->next_child) { + put_vma(core_bfd, arc->parent->addr, (bfd_byte*) raw_arc.from_pc); + put_vma(core_bfd, arc->child->addr, (bfd_byte*) raw_arc.self_pc); + bfd_put_32(core_bfd, arc->count, (bfd_byte*) raw_arc.count); + if (fwrite(&tag, sizeof(tag), 1, ofp) != 1 + || fwrite(&raw_arc, sizeof(raw_arc), 1, ofp) != 1) + { + perror(filename); + done(1); + } /* if */ + DBG(SAMPLEDEBUG, + printf("[cg_write_arcs] frompc 0x%lx selfpc 0x%lx count %d\n", + arc->parent->addr, arc->child->addr, arc->count)); + } /* for */ + } /* for */ +} /* cg_write_arcs */ + + /*** end of call_graph.c ***/ diff --git a/gprof/call_graph.h b/gprof/call_graph.h new file mode 100644 index 00000000000..d3d77dae0d5 --- /dev/null +++ b/gprof/call_graph.h @@ -0,0 +1,12 @@ +#ifndef call_graph_h +#define call_graph_h + +#include +#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 */ diff --git a/gprof/cg_arcs.c b/gprof/cg_arcs.c new file mode 100644 index 00000000000..85ab9a7b436 --- /dev/null +++ b/gprof/cg_arcs.c @@ -0,0 +1,580 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include "libiberty.h" +#include "gprof.h" +#include "call_graph.h" +#include "cg_arcs.h" +#include "cg_dfn.h" +#include "cg_print.h" +#include "utils.h" +#include "sym_ids.h" + +Sym *cycle_header; +int num_cycles; + +/* + * Return TRUE iff PARENT has an arc to covers the address + * range covered by CHILD. + */ +Arc* +DEFUN(arc_lookup, (parent, child), Sym *parent AND Sym *child) +{ + Arc *arc; + + if (!parent || !child) { + printf("[arc_lookup] parent == 0 || child == 0\n"); + return 0; + } /* if */ + DBG(LOOKUPDEBUG, printf("[arc_lookup] parent %s child %s\n", + parent->name, child->name)); + for (arc = parent->cg.children; arc; arc = arc->next_child) { + DBG(LOOKUPDEBUG, printf("[arc_lookup]\t parent %s child %s\n", + arc->parent->name, arc->child->name)); + if (child->addr >= arc->child->addr + && child->end_addr <= arc->child->end_addr) + { + return arc; + } /* if */ + } /* for */ + return 0; +} /* arc_lookup */ + + +/* + * Add (or just increment) an arc: + */ +void +DEFUN(arc_add, (parent, child, count), + Sym *parent AND Sym *child AND int count) +{ + Arc *arc; + + DBG(TALLYDEBUG, printf("[arc_add] %d arcs from %s to %s\n", + count, parent->name, child->name)); + arc = arc_lookup(parent, child); + if (arc) { + /* + * A hit: just increment the count. + */ + DBG(TALLYDEBUG, printf("[tally] hit %d += %d\n", + arc->count, count)); + arc->count += count; + return; + } /* if */ + arc = (Arc*)xmalloc(sizeof(*arc)); + arc->parent = parent; + arc->child = child; + arc->count = count; + + /* prepend this child to the children of this parent: */ + arc->next_child = parent->cg.children; + parent->cg.children = arc; + + /* prepend this parent to the parents of this child: */ + arc->next_parent = child->cg.parents; + child->cg.parents = arc; +} /* arc_add */ + + +static int +DEFUN(cmp_topo, (lp, rp), const PTR lp AND const PTR rp) +{ + const Sym *left = *(const Sym **) lp; + const Sym *right = *(const Sym **) rp; + + return left->cg.top_order - right->cg.top_order; +} /* cmp_topo */ + + +static void +DEFUN(propagate_time, (parent), Sym *parent) +{ + Arc *arc; + Sym *child; + double share, prop_share; + + if (parent->cg.prop.fract == 0.0) { + return; + } /* if */ + + /* gather time from children of this parent: */ + + for (arc = parent->cg.children; arc; arc = arc->next_child) { + child = arc->child; + if (arc->count == 0 || child == parent || child->cg.prop.fract == 0) { + continue; + } /* if */ + if (child->cg.cyc.head != child) { + if (parent->cg.cyc.num == child->cg.cyc.num) { + continue; + } /* if */ + if (parent->cg.top_order <= child->cg.top_order) { + fprintf(stderr, "[propagate] toporder botches\n"); + } /* if */ + child = child->cg.cyc.head; + } else { + if (parent->cg.top_order <= child->cg.top_order) { + fprintf(stderr, "[propagate] toporder botches\n"); + continue; + } /* if */ + } /* if */ + if (child->ncalls == 0) { + continue; + } /* if */ + + /* distribute time for this arc: */ + arc->time = child->hist.time * (((double) arc->count) + / ((double) child->ncalls)); + arc->child_time = child->cg.child_time + * (((double) arc->count) / ((double) child->ncalls)); + share = arc->time + arc->child_time; + parent->cg.child_time += share; + + /* (1 - cg.prop.fract) gets lost along the way: */ + prop_share = parent->cg.prop.fract * share; + + /* fix things for printing: */ + parent->cg.prop.child += prop_share; + arc->time *= parent->cg.prop.fract; + arc->child_time *= parent->cg.prop.fract; + + /* add this share to the parent's cycle header, if any: */ + if (parent->cg.cyc.head != parent) { + parent->cg.cyc.head->cg.child_time += share; + parent->cg.cyc.head->cg.prop.child += prop_share; + } /* if */ + DBG(PROPDEBUG, + printf("[prop_time] child \t"); + print_name(child); + printf(" with %f %f %d/%d\n", child->hist.time, + child->cg.child_time, arc->count, child->ncalls); + printf("[prop_time] parent\t"); + print_name(parent); + printf("\n[prop_time] share %f\n", share)); + } /* for */ +} /* propagate_time */ + + +/* + * Compute the time of a cycle as the sum of the times of all + * its members. + */ +static void +DEFUN_VOID(cycle_time) +{ + Sym *member, *cyc; + + for (cyc = &cycle_header[1]; cyc <= &cycle_header[num_cycles]; ++cyc) { + for (member = cyc->cg.cyc.next; member; member = member->cg.cyc.next) { + if (member->cg.prop.fract == 0.0) { + /* + * All members have the same propfraction except those + * that were excluded with -E. + */ + continue; + } /* if */ + cyc->hist.time += member->hist.time; + } /* for */ + cyc->cg.prop.self = cyc->cg.prop.fract * cyc->hist.time; + } /* for */ +} /* cycle_time */ + + +static void +DEFUN_VOID(cycle_link) +{ + Sym *sym, *cyc, *member; + Arc *arc; + int num; + + /* count the number of cycles, and initialize the cycle lists: */ + + num_cycles = 0; + for (sym = symtab.base; sym < symtab.limit; ++sym) { + /* this is how you find unattached cycles: */ + if (sym->cg.cyc.head == sym && sym->cg.cyc.next) { + ++num_cycles; + } /* if */ + } /* for */ + + /* + * cycle_header is indexed by cycle number: i.e. it is origin 1, + * not origin 0. + */ + cycle_header = (Sym*)xmalloc((num_cycles + 1) * sizeof(Sym)); + + /* + * Now link cycles to true cycle-heads, number them, accumulate + * the data for the cycle. + */ + num = 0; cyc = cycle_header; + for (sym = symtab.base; sym < symtab.limit; ++sym) { + if (!(sym->cg.cyc.head == sym && sym->cg.cyc.next != 0)) { + continue; + } /* if */ + ++num; ++cyc; + sym_init(cyc); + cyc->cg.print_flag = TRUE; /* should this be printed? */ + cyc->cg.top_order = DFN_NAN; /* graph call chain top-sort order */ + cyc->cg.cyc.num = num; /* internal number of cycle on */ + cyc->cg.cyc.head = cyc; /* pointer to head of cycle */ + cyc->cg.cyc.next = sym; /* pointer to next member of cycle */ + DBG(CYCLEDEBUG, printf("[cycle_link] "); print_name(sym); + printf(" is the head of cycle %d\n", num)); + + /* link members to cycle header: */ + for (member = sym; member; member = member->cg.cyc.next) { + member->cg.cyc.num = num; + member->cg.cyc.head = cyc; + } /* for */ + + /* + * Count calls from outside the cycle and those among cycle + * members: + */ + for (member = sym; member; member = member->cg.cyc.next) { + for (arc = member->cg.parents; arc; arc = arc->next_parent) { + if (arc->parent == member) { + continue; + } /* if */ + if (arc->parent->cg.cyc.num == num) { + cyc->cg.self_calls += arc->count; + } else { + cyc->ncalls += arc->count; + } /* if */ + } /* for */ + } /* for */ + } /* for */ +} /* cycle_link */ + + +/* + * Check if any parent of this child (or outside parents of this + * cycle) have their print flags on and set the print flag of the + * child (cycle) appropriately. Similarly, deal with propagation + * fractions from parents. + */ +static void +DEFUN(inherit_flags, (child), Sym *child) +{ + Sym *head, *parent, *member; + Arc *arc; + + head = child->cg.cyc.head; + if (child == head) { + /* just a regular child, check its parents: */ + child->cg.print_flag = FALSE; + child->cg.prop.fract = 0.0; + for (arc = child->cg.parents; arc; arc = arc->next_parent) { + parent = arc->parent; + if (child == parent) { + continue; + } /* if */ + child->cg.print_flag |= parent->cg.print_flag; + /* + * If the child was never actually called (e.g., this arc + * is static (and all others are, too)) no time propagates + * along this arc. + */ + if (child->ncalls) { + child->cg.prop.fract += parent->cg.prop.fract + * (((double) arc->count) / ((double) child->ncalls)); + } /* if */ + } /* for */ + } else { + /* + * Its a member of a cycle, look at all parents from outside + * the cycle. + */ + head->cg.print_flag = FALSE; + head->cg.prop.fract = 0.0; + for (member = head->cg.cyc.next; member; member = member->cg.cyc.next) + { + for (arc = member->cg.parents; arc; arc = arc->next_parent) { + if (arc->parent->cg.cyc.head == head) { + continue; + } /* if */ + parent = arc->parent; + head->cg.print_flag |= parent->cg.print_flag; + /* + * If the cycle was never actually called (e.g. this + * arc is static (and all others are, too)) no time + * propagates along this arc. + */ + if (head->ncalls) { + head->cg.prop.fract += parent->cg.prop.fract + * (((double) arc->count) / ((double) head->ncalls)); + } /* if */ + } /* for */ + } /* for */ + for (member = head; member; member = member->cg.cyc.next) { + member->cg.print_flag = head->cg.print_flag; + member->cg.prop.fract = head->cg.prop.fract; + } /* for */ + } /* if */ +} /* inherit_flags */ + + +/* + * In one top-to-bottom pass over the topologically sorted symbols + * propagate: + * cg.print_flag as the union of parents' print_flags + * propfraction as the sum of fractional parents' propfractions + * and while we're here, sum time for functions. + */ +static void +DEFUN(propagate_flags, (symbols), Sym **symbols) +{ + int index; + Sym *old_head, *child; + + old_head = 0; + for (index = symtab.len - 1; index >= 0; --index) { + child = symbols[index]; + /* + * If we haven't done this function or cycle, inherit things + * from parent. This way, we are linear in the number of arcs + * since we do all members of a cycle (and the cycle itself) + * as we hit the first member of the cycle. + */ + if (child->cg.cyc.head != old_head) { + old_head = child->cg.cyc.head; + inherit_flags(child); + } /* if */ + DBG(PROPDEBUG, + printf("[prop_flags] "); + print_name(child); + printf("inherits print-flag %d and prop-fract %f\n", + child->cg.print_flag, child->cg.prop.fract)); + if (!child->cg.print_flag) { + /* + * Printflag is off. It gets turned on by being in the + * INCL_GRAPH table, or there being an empty INCL_GRAPH + * table and not being in the EXCL_GRAPH table. + */ + if (sym_lookup(&syms[INCL_GRAPH], child->addr) + || (syms[INCL_GRAPH].len == 0 + && !sym_lookup(&syms[EXCL_GRAPH], child->addr))) + { + child->cg.print_flag = TRUE; + } /* if */ + } else { + /* + * This function has printing parents: maybe someone wants + * to shut it up by putting it in the EXCL_GRAPH table. + * (But favor INCL_GRAPH over EXCL_GRAPH.) + */ + if (!sym_lookup(&syms[INCL_GRAPH], child->addr) + && sym_lookup(&syms[EXCL_GRAPH], child->addr)) + { + child->cg.print_flag = FALSE; + } /* if */ + } /* if */ + if (child->cg.prop.fract == 0.0) { + /* + * No parents to pass time to. Collect time from children + * if its in the INCL_TIME table, or there is an empty + * INCL_TIME table and its not in the EXCL_TIME table. + */ + if (sym_lookup(&syms[INCL_TIME], child->addr) + || (syms[INCL_TIME].len == 0 + && !sym_lookup(&syms[EXCL_TIME], child->addr))) + { + child->cg.prop.fract = 1.0; + } /* if */ + } else { + /* + * It has parents to pass time to, but maybe someone wants + * to shut it up by puttting it in the EXCL_TIME table. + * (But favor being in INCL_TIME tabe over being in + * EXCL_TIME table.) + */ + if (!sym_lookup(&syms[INCL_TIME], child->addr) + && sym_lookup(&syms[EXCL_TIME], child->addr)) + { + child->cg.prop.fract = 0.0; + } /* if */ + } /* if */ + child->cg.prop.self = child->hist.time * child->cg.prop.fract; + print_time += child->cg.prop.self; + DBG(PROPDEBUG, + printf("[prop_flags] "); + print_name(child); + printf(" ends up with printflag %d and prop-fract %f\n", + child->cg.print_flag, child->cg.prop.fract); + printf("[prop_flags] time %f propself %f print_time %f\n", + child->hist.time, child->cg.prop.self, print_time)); + } /* if */ +} /* propagate_flags */ + + +/* + * Compare by decreasing propagated time. If times are equal, but one + * is a cycle header, say that's first (e.g. less, i.e. -1). If one's + * name doesn't have an underscore and the other does, say that one is + * first. All else being equal, compare by names. + */ +static int +DEFUN(cmp_total, (lp, rp), const PTR lp AND const PTR rp) +{ + const Sym *left = *(const Sym**)lp; + const Sym *right = *(const Sym**)rp; + double diff; + + diff = (left->cg.prop.self + left->cg.prop.child) + - (right->cg.prop.self + right->cg.prop.child); + if (diff < 0.0) { + return 1; + } /* if */ + if (diff > 0.0) { + return -1; + } /* if */ + if (!left->name && left->cg.cyc.num != 0) { + return -1; + } /* if */ + if (!right->name && right->cg.cyc.num != 0) { + return 1; + } /* if */ + if (!left->name) { + return -1; + } /* if */ + if (!right->name) { + return 1; + } /* if */ + if (left->name[0] != '_' && right->name[0] == '_') { + return -1; + } /* if */ + if (left->name[0] == '_' && right->name[0] != '_') { + return 1; + } /* if */ + if (left->ncalls > right->ncalls) { + return -1; + } /* if */ + if (left->ncalls < right->ncalls) { + return 1; + } /* if */ + return strcmp(left->name, right->name); +} /* cmp_total */ + + +/* + * Topologically sort the graph (collapsing cycles), and propagates + * time bottom up and flags top down. + */ +Sym** +DEFUN_VOID(cg_assemble) +{ + Sym *parent, **time_sorted_syms, **top_sorted_syms; + long index; + Arc *arc; + extern void find_call PARAMS((Sym *parent, + bfd_vma p_lowpc, bfd_vma p_highpc)); + /* + * initialize various things: + * zero out child times. + * count self-recursive calls. + * indicate that nothing is on cycles. + */ + for (parent = symtab.base; parent < symtab.limit; parent++) { + parent->cg.child_time = 0.0; + arc = arc_lookup(parent, parent); + if (arc && parent == arc->child) { + parent->ncalls -= arc->count; + parent->cg.self_calls = arc->count; + } else { + parent->cg.self_calls = 0; + } /* if */ + parent->cg.prop.fract = 0.0; + parent->cg.prop.self = 0.0; + parent->cg.prop.child = 0.0; + parent->cg.print_flag = FALSE; + parent->cg.top_order = DFN_NAN; + parent->cg.cyc.num = 0; + parent->cg.cyc.head = parent; + parent->cg.cyc.next = 0; + if (ignore_direct_calls) { + find_call(parent, parent->addr, (parent+1)->addr); + } /* if */ + } /* for */ + /* + * Topologically order things. If any node is unnumbered, number + * it and any of its descendents. + */ + for (parent = symtab.base; parent < symtab.limit; parent++) { + if (parent->cg.top_order == DFN_NAN) { + cg_dfn(parent); + } /* if */ + } /* for */ + + /* link together nodes on the same cycle: */ + cycle_link(); + + /* sort the symbol table in reverse topological order: */ + top_sorted_syms = (Sym**)xmalloc(symtab.len * sizeof(Sym*)); + for (index = 0; index < symtab.len; ++index) { + top_sorted_syms[index] = &symtab.base[index]; + } /* for */ + qsort(top_sorted_syms, symtab.len, sizeof(Sym *), cmp_topo); + DBG(DFNDEBUG, + printf("[cg_assemble] topological sort listing\n"); + for (index = 0; index < symtab.len; ++index) { + printf("[cg_assemble] "); + printf("%d:", top_sorted_syms[index]->cg.top_order); + print_name(top_sorted_syms[index]); + printf("\n"); + } /* for */); + /* + * Starting from the topological top, propagate print flags to + * children. also, calculate propagation fractions. this happens + * before time propagation since time propagation uses the + * fractions. + */ + propagate_flags(top_sorted_syms); + + /* + * Starting from the topological bottom, propogate children times + * up to parents. + */ + cycle_time(); + for (index = 0; index < symtab.len; ++index) { + propagate_time(top_sorted_syms[index]); + } /* for */ + + free(top_sorted_syms); + + /* + * Now, sort by CG.PROP.SELF + CG.PROP.CHILD. Sorting both the regular + * function names and cycle headers. + */ + time_sorted_syms = (Sym**)xmalloc((symtab.len + num_cycles)*sizeof(Sym*)); + for (index = 0; index < symtab.len; index++) { + time_sorted_syms[index] = &symtab.base[index]; + } /* if */ + for (index = 1; index <= num_cycles; index++) { + time_sorted_syms[symtab.len + index - 1] = &cycle_header[index]; + } /* for */ + qsort(time_sorted_syms, symtab.len + num_cycles, sizeof(Sym*), + cmp_total); + for (index = 0; index < symtab.len + num_cycles; index++) { + time_sorted_syms[index]->cg.index = index + 1; + } /* for */ + return time_sorted_syms; +} /* cg_assemble */ + + /*** end of cg_arcs.c ***/ diff --git a/gprof/cg_arcs.h b/gprof/cg_arcs.h new file mode 100644 index 00000000000..73c2c4b589f --- /dev/null +++ b/gprof/cg_arcs.h @@ -0,0 +1,31 @@ +#ifndef cg_arcs_h +#define cg_arcs_h + +#include "gprof.h" +#include "symtab.h" + +/* + * Arc structure for call-graph. + * + * With pointers to the symbols of the parent and the child, a count + * of how many times this arc was traversed, and pointers to the next + * parent of this child and the next child of this parent. + */ +typedef struct arc { + Sym *parent; /* source vertice of arc */ + Sym *child; /* dest vertice of arc */ + int count; /* # of calls from parent to child */ + double time; /* time inherited along arc */ + double child_time; /* child-time inherited along arc */ + struct arc *next_parent; /* next parent of CHILD */ + struct arc *next_child; /* next child of PARENT */ +} Arc; + +extern int num_cycles; /* number of cycles discovered */ +extern Sym *cycle_header; /* cycle headers */ + +extern void arc_add PARAMS((Sym *parent, Sym *child, int count)); +extern Arc *arc_lookup PARAMS((Sym *parent, Sym *child)); +extern Sym **cg_assemble PARAMS((void)); + +#endif /* cg_arcs_h */ diff --git a/gprof/cg_dfn.c b/gprof/cg_dfn.c new file mode 100644 index 00000000000..f93a9d882b0 --- /dev/null +++ b/gprof/cg_dfn.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include +#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(""); + } /* 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 ***/ diff --git a/gprof/cg_dfn.h b/gprof/cg_dfn.h new file mode 100644 index 00000000000..ac20330680b --- /dev/null +++ b/gprof/cg_dfn.h @@ -0,0 +1,17 @@ +#ifndef cg_dfn_h +#define cg_dfn_h + +/* + * Flags which mark a symbol as topologically ``busy'' or as + * topologically ``not_numbered'': + */ +#define DFN_BUSY -1 +#define DFN_NAN 0 + +/* + * Depth-first numbering of a call-graph. + */ + +extern void cg_dfn PARAMS((Sym *root)); + +#endif /* cg_dfn_h */ diff --git a/gprof/cg_print.c b/gprof/cg_print.c new file mode 100644 index 00000000000..d123f3ee937 --- /dev/null +++ b/gprof/cg_print.c @@ -0,0 +1,553 @@ +#include "libiberty.h" +#include "cg_arcs.h" +#include "cg_print.h" +#include "hist.h" +#include "utils.h" + +/* + * Return value of comparison functions used to sort tables: + */ +#define LESSTHAN -1 +#define EQUALTO 0 +#define GREATERTHAN 1 + +/* declarations of automatically generated functions to output blurbs: */ +extern void bsd_callg_blurb PARAMS((FILE *fp)); +extern void fsf_callg_blurb PARAMS((FILE *fp)); + +double print_time = 0.0; + + +static void +DEFUN_VOID(print_header) +{ + if (first_output) { + first_output = FALSE; + } else { + printf("\f\n"); + } /* if */ + if (!bsd_style_output) { + if (print_descriptions) { + printf("\t\t Call graph (explanation follows)\n\n"); + } else { + printf("\t\t\tCall graph\n\n"); + } /* if */ + } /* if */ + printf("\ngranularity: each sample hit covers %ld byte(s)", + (long) hist_scale * sizeof(UNIT)); + if (print_time > 0.0) { + printf(" for %.2f%% of %.2f seconds\n\n", + 100.0/print_time, print_time / hz); + } else { + printf(" no time propagated\n\n"); + /* + * This doesn't hurt, since all the numerators will be 0.0: + */ + print_time = 1.0; + } /* if */ + if (bsd_style_output) { + printf("%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n", + "", "", "", "", "called", "total", "parents"); + printf("%-6.6s %5.5s %7.7s %11.11s %7.7s+%-7.7s %-8.8s\t%5.5s\n", + "index", "%time", "self", "descendents", + "called", "self", "name", "index"); + printf("%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n", + "", "", "", "", "called", "total", "children"); + printf("\n"); + } else { + printf("index %% time self children called name\n"); + } /* if */ +} /* print_header */ + + +/* + * Print a cycle header. + */ +static void +DEFUN(print_cycle, (cyc), Sym *cyc) +{ + char buf[BUFSIZ]; + + sprintf(buf, "[%d]", cyc->cg.index); + printf("%-6.6s %5.1f %7.2f %11.2f %7d", buf, + 100 * (cyc->cg.prop.self + cyc->cg.prop.child) / print_time, + cyc->cg.prop.self / hz, cyc->cg.prop.child / hz, cyc->ncalls); + if (cyc->cg.self_calls != 0) { + printf("+%-7d", cyc->cg.self_calls); + } else { + printf(" %7.7s", ""); + } /* if */ + printf(" \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 \n" + : "%6.6s %5.5s %7.7s %7.7s %7.7s %7.7s \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, "", 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 ***/ diff --git a/gprof/cg_print.h b/gprof/cg_print.h new file mode 100644 index 00000000000..15ed4362db0 --- /dev/null +++ b/gprof/cg_print.h @@ -0,0 +1,12 @@ +#ifndef cg_print_h +#define cg_print_h + +#include "gprof.h" +#include "symtab.h" + +extern double print_time; /* total of time being printed */ + +extern void cg_print PARAMS ((Sym **cg)); +extern void cg_print_index PARAMS((void)); + +#endif /* cg_print_h */ diff --git a/gprof/config/mt-alpha b/gprof/config/mt-alpha new file mode 100644 index 00000000000..5166306a512 --- /dev/null +++ b/gprof/config/mt-alpha @@ -0,0 +1 @@ +MY_MACHINE=alpha diff --git a/gprof/configure.in b/gprof/configure.in index 94d474a3fc5..c0e8280fd76 100644 --- a/gprof/configure.in +++ b/gprof/configure.in @@ -10,6 +10,7 @@ srcname="gprof" # per-target: case "${target}" in +alpha-*-*) my_target=alpha ;; i[345]86-*-*) my_target=i386 ;; sparc-*-*) my_target=sparc ;; tahoe-*-*) my_target=tahoe ;; diff --git a/gprof/core.c b/gprof/core.c new file mode 100644 index 00000000000..bf02be23370 --- /dev/null +++ b/gprof/core.c @@ -0,0 +1,476 @@ +#include "libiberty.h" +#include "gprof.h" +#include "core.h" +#include "symtab.h" + +bfd *core_bfd; +int core_num_syms; +asymbol **core_syms; +asection *core_text_sect; +PTR core_text_space; + + +void +DEFUN(core_init, (a_out_name), const char *a_out_name) +{ + core_bfd = bfd_openr(a_out_name, 0); + + if (!core_bfd) { + perror(a_out_name); + done(1); + } /* if */ + + if (!bfd_check_format(core_bfd, bfd_object)) { + fprintf(stderr, "%s: %s: not in a.out format\n", whoami, a_out_name); + done(1); + } /* if */ + + /* get core's text section: */ + core_text_sect = bfd_get_section_by_name(core_bfd, ".text"); + if (!core_text_sect) { + core_text_sect = bfd_get_section_by_name(core_bfd, "$CODE$"); + if (!core_text_sect) { + fprintf(stderr, "%s: can't find .text section in %s\n", + whoami, a_out_name); + done(1); + } /* if */ + } /* if */ + + /* read core's symbol table: */ + + /* this will probably give us more than we need, but that's ok: */ + core_num_syms = bfd_get_symtab_upper_bound(core_bfd); + if (core_num_syms < 0) { + fprintf(stderr, "%s: %s: %s\n", whoami, a_out_name, + bfd_errmsg(bfd_get_error())); + done(1); + } /* if */ + + core_syms = (asymbol**)xmalloc(core_num_syms); + core_num_syms = bfd_canonicalize_symtab(core_bfd, core_syms); + if (core_num_syms < 0) { + fprintf(stderr, "%s: %s: %s\n", whoami, a_out_name, + bfd_errmsg(bfd_get_error())); + done(1); + } /* if */ +} /* core_init */ + + +/* + * Read in the text space of an a.out file + */ +void +DEFUN(core_get_text_space, (core_bfd), bfd *core_bfd) +{ + core_text_space = (PTR) malloc(core_text_sect->_raw_size); + + if (!core_text_space) { + fprintf(stderr, "%s: ran out room for %ld bytes of text space\n", + whoami, core_text_sect->_raw_size); + done(1); + } /* if */ + if (!bfd_get_section_contents(core_bfd, core_text_sect, core_text_space, + 0, core_text_sect->_raw_size)) + { + bfd_perror("bfd_get_section_contents"); + free(core_text_space); + core_text_space = 0; + } /* if */ + if (!core_text_space) { + fprintf(stderr, "%s: can't do -c\n", whoami); + } /* if */ +} /* core_get_text_space */ + + +/* + * Return class of symbol SYM. The returned class can be any of: + * 0 -> symbol is not interesting to us + * 'T' -> symbol is a global name + * 't' -> symbol is a local (static) name + */ +static int +DEFUN(core_sym_class, (sym), asymbol *sym) +{ + symbol_info syminfo; + const char *name; + char sym_prefix; + int i; + + /* + * Must be a text symbol, and static text symbols don't qualify if + * ignore_static_funcs set. + */ + if (!sym->section) { + return 0; + } /* if */ + + if (ignore_static_funcs && (sym->flags & BSF_LOCAL)) { + DBG(AOUTDEBUG, printf("[core_sym_class] %s: not a function\n", + sym->name)); + return 0; + } /* if */ + + bfd_get_symbol_info(core_bfd, sym, &syminfo); + i = syminfo.type; + + if (i == 'T') { + return i; /* it's a global symbol */ + } /* if */ + + if (i != 't') { + /* not a static text symbol */ + DBG(AOUTDEBUG, printf("[core_sym_class] %s is of class %c\n", + sym->name, i)); + return 0; + } /* if */ + + /* do some more filtering on static function-names: */ + + if (ignore_static_funcs) { + return 0; + } /* if */ + /* + * Can't zero-length name or funny characters in name, where + * `funny' includes: `.' (.o file names) and `$' (Pascal labels). + */ + if (!sym->name || sym->name[0] == '\0') { + return 0; + } /* if */ + + for (name = sym->name; *name; ++name) { + if (*name == '.' || *name == '$') { + return 0; + } /* if */ + } /* if */ + /* + * On systems where the C compiler adds an underscore to all + * names, static names without underscores seem usually to be + * labels in hand written assembler in the library. We don't want + * these names. This is certainly necessary on a Sparc running + * SunOS 4.1 (try profiling a program that does a lot of + * division). I don't know whether it has harmful side effects on + * other systems. Perhaps it should be made configurable. + */ + sym_prefix = bfd_get_symbol_leading_char(core_bfd); + if (sym_prefix && sym_prefix != sym->name[0] + /* + * GCC may add special symbols to help gdb figure out the file + * language. We want to ignore these, since sometimes they mask + * the real function. (dj@ctron) + */ + || !strncmp (sym->name, "__gnu_compiled", 14) + || !strncmp (sym->name, "___gnu_compiled", 15)) + { + return 0; + } /* if */ + return 't'; /* it's a static text symbol */ +} /* core_sym_class */ + + +/* + * Get whatever source info we can get regarding address ADDR: + */ +static bool +DEFUN(get_src_info, (addr, filename, name, line_num), + bfd_vma addr AND const char **filename AND const char **name + AND int *line_num) +{ + const char *fname = 0, *func_name = 0; + int l = 0; + + if (bfd_find_nearest_line(core_bfd, core_text_sect, core_syms, + addr - core_text_sect->vma, + &fname, &func_name, &l) + && fname && func_name && l) + { + DBG(AOUTDEBUG, printf("[get_src_info] 0x%lx -> %s:%d (%s)\n", + addr, fname, l, func_name)); + *filename = fname; + *name = func_name; + *line_num = l; + return TRUE; + } else { + DBG(AOUTDEBUG, printf("[get_src_info] no info for 0x%lx (%s:%d,%s)\n", + (long) addr, fname ? fname : "", l, + func_name ? func_name : "")); + 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 = ""; + symtab.limit->addr = 0; + symtab.limit->end_addr = min_vma - 1; + ++symtab.limit; + + sym_init(symtab.limit); + symtab.limit->name = ""; + 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, "") == 0 + && min_vma <= sentinel->end_addr) + { + sentinel->end_addr = min_vma - 1; + } /* if */ + + sentinel = sym_lookup(&symtab, ~0); + if (strcmp(sentinel->name, "") == 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 ***/ diff --git a/gprof/core.h b/gprof/core.h new file mode 100644 index 00000000000..c74268d2f78 --- /dev/null +++ b/gprof/core.h @@ -0,0 +1,17 @@ +#ifndef core_h +#define core_h + +#include "bfd.h" + +extern bfd *core_bfd; /* bfd for core-file */ +extern int core_num_syms; /* # of entries in symbol-table */ +extern asymbol **core_syms; /* symbol table in a.out */ +extern asection *core_text_sect; /* core text section */ +extern PTR core_text_space; /* text space of a.out in core */ + +extern void core_init PARAMS((const char *a_out_name)); +extern void core_get_text_space PARAMS((bfd *core_bfd)); +extern void core_create_function_syms PARAMS((bfd *core_bfd)); +extern void core_create_line_syms PARAMS((bfd *core_bfd)); + +#endif /* core_h */ diff --git a/gprof/dfn.c b/gprof/dfn.c deleted file mode 100644 index 1d464beb276..00000000000 --- a/gprof/dfn.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that: (1) source distributions retain this entire copyright - * notice and comment, and (2) distributions including binaries display - * the following acknowledgement: ``This product includes software - * developed by the University of California, Berkeley and its contributors'' - * in the documentation or other materials provided with the distribution - * and in all advertising materials mentioning features or use of this - * software. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#ifndef lint -static char sccsid[] = "@(#)dfn.c 5.4 (Berkeley) 6/1/90"; -#endif /* not lint */ - -#include -#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; -} diff --git a/gprof/dummy.c b/gprof/dummy.c index 21a6db3f5e4..a78caeca21b 100644 --- a/gprof/dummy.c +++ b/gprof/dummy.c @@ -1,13 +1,18 @@ #include "gprof.h" +#include "symtab.h" + /* * dummy.c -- This file should be used for an unsupported processor type. * It does nothing, but prevents findcall() from being unresolved. */ -findcall( parentp , p_lowpc , p_highpc ) - nltype *parentp; - unsigned long p_lowpc; - unsigned long p_highpc; +void +DEFUN(find_call, (parent, p_lowpc, p_highpc), + Sym *parent AND bfd_vma p_lowpc AND bfd_vma p_highpc) { -} + fprintf(stderr, "%s: -c supported on this machine architecture\n", + whoami); +} /* find_call */ + + /*** end of dummy.c ***/ diff --git a/gprof/dummy.h b/gprof/dummy.h index 7098963cf68..f8a6bb6d875 100644 --- a/gprof/dummy.h +++ b/gprof/dummy.h @@ -32,17 +32,21 @@ * * @(#)dummy.h 5.1 (Berkeley) 4/18/91 */ +#ifndef dummy_h +#define dummy_h /* * dummy.h -- This file should be used when a processor is not yet supported. */ - /* - * offset (in bytes) of the code from the entry address of a routine. - * (see asgnsamples for use and explanation.) - */ -#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 */ diff --git a/gprof/flat_bl.m b/gprof/flat_bl.m index 6fc3340a6c7..db2871a1384 100644 --- a/gprof/flat_bl.m +++ b/gprof/flat_bl.m @@ -25,4 +25,3 @@ name the name of the function. This is the minor sort the function in the gprof listing. If the index is in parenthesis it shows where it would appear in the gprof listing if it were to be printed. - diff --git a/gprof/gmon.h b/gprof/gmon.h index 2c88a6300d2..0e6c93f2612 100644 --- a/gprof/gmon.h +++ b/gprof/gmon.h @@ -32,80 +32,84 @@ * * @(#)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 */ diff --git a/gprof/gmon_io.c b/gprof/gmon_io.c new file mode 100644 index 00000000000..45d1708aa20 --- /dev/null +++ b/gprof/gmon_io.c @@ -0,0 +1,354 @@ +/* + * Input and output from/to gmon.out files. + */ +#include "cg_arcs.h" +#include "basic_blocks.h" +#include "bfd.h" +#include "core.h" +#include "call_graph.h" +#include "gmon_io.h" +#include "gmon_out.h" +#include "gmon.h" /* fetch header for old format */ +#include "gprof.h" +#include "hertz.h" +#include "hist.h" +#include "libiberty.h" + +int gmon_input = 0; +int gmon_file_version = 0; /* 0 == old (non-versioned) file format */ + +/* + * This probably ought to be in libbfd. + */ +bfd_vma +DEFUN(get_vma, (abfd, addr), bfd *abfd AND bfd_byte *addr) +{ + switch (sizeof(bfd_vma)) { + case 4: return bfd_get_32(abfd, addr); + case 8: return bfd_get_64(abfd, addr); + default: + fprintf(stderr, "%s: bfd_vma has unexpected size of %ld bytes\n", + whoami, (long) sizeof(bfd_vma)); + done(1); + } /* switch */ +} /* get_vma */ + + +/* + * This probably ought to be in libbfd. + */ +void +DEFUN(put_vma, (abfd, val, addr), bfd *abfd AND bfd_vma val AND bfd_byte *addr) +{ + switch (sizeof(bfd_vma)) { + case 4: bfd_put_32(abfd, val, addr); break; + case 8: bfd_put_64(abfd, val, addr); break; + default: + fprintf(stderr, "%s: bfd_vma has unexpected size of %ld bytes\n", + whoami, (long) sizeof(bfd_vma)); + done(1); + } /* switch */ +} /* put_vma */ + + +void +DEFUN(gmon_out_read, (filename), const char *filename) +{ + FILE *ifp; + struct gmon_hdr ghdr; + unsigned char tag; + int nhist = 0, narcs = 0, nbbs = 0; + + /* open gmon.out file: */ + + if (strcmp(filename, "-") == 0) { + ifp = stdin; + } else { + ifp = fopen(filename, FOPEN_RB); + if (!ifp) { + perror(filename); + done(1); + } /* if */ + } /* if */ + if (fread(&ghdr, sizeof(struct gmon_hdr), 1, ifp) != 1) { + fprintf(stderr, "%s: file too short to be a gmon file\n", + filename); + done(1); + } /* if */ + + if ((file_format == FF_MAGIC) || + (file_format == FF_AUTO && !strncmp(&ghdr.cookie[0], GMON_MAGIC, 4))) + { + if (file_format == FF_MAGIC && strncmp(&ghdr.cookie[0], GMON_MAGIC, 4)) + { + fprintf(stderr, "%s: file `%s' has bad magic cookie\n", + whoami, filename); + done(1); + } /* if */ + + /* right magic, so it's probably really a new gmon.out file */ + + gmon_file_version = bfd_get_32(core_bfd, (bfd_byte *) ghdr.version); + if (gmon_file_version != GMON_VERSION && gmon_file_version != 0) { + fprintf(stderr, + "%s: file `%s' has unsupported version %d\n", + whoami, filename, gmon_file_version); + done(1); + } /* if */ + + /* read in all the records: */ + while (fread(&tag, sizeof(tag), 1, ifp) == 1) { + switch (tag) { + case GMON_TAG_TIME_HIST: + ++nhist; + gmon_input |= INPUT_HISTOGRAM; + hist_read_rec(ifp, filename); + break; + + case GMON_TAG_CG_ARC: + ++narcs; + gmon_input |= INPUT_CALL_GRAPH; + cg_read_rec(ifp, filename); + break; + + case GMON_TAG_BB_COUNT: + ++nbbs; + gmon_input |= INPUT_BB_COUNTS; + bb_read_rec(ifp, filename); + break; + + default: + fprintf(stderr, + "%s: %s: found bad tag %d (file corrupted?)\n", + whoami, filename, tag); + done(1); + } /* switch */ + } /* while */ + } else if (file_format == FF_AUTO || file_format == FF_BSD) { + struct hdr { + bfd_vma low_pc; + bfd_vma high_pc; + int ncnt; + }; + int i, samp_bytes, count; + bfd_vma from_pc, self_pc; + struct raw_arc raw_arc; + struct raw_phdr raw; + static struct hdr h; + UNIT raw_bin_count; + struct hdr tmp; + + /* + * Information from a gmon.out file is in two parts: an array of + * sampling hits within pc ranges, and the arcs. + */ + gmon_input = INPUT_HISTOGRAM | INPUT_CALL_GRAPH; + + /* + * This fseek() ought to work even on stdin as long as it's + * not an interactive device (heck, is there anybody who would + * want to type in a gmon.out at the terminal?). + */ + if (fseek(ifp, 0, SEEK_SET) < 0) { + perror(filename); + done(1); + } /* if */ + if (fread(&raw, 1, sizeof(struct raw_phdr), ifp) + != sizeof(struct raw_phdr)) + { + fprintf(stderr, "%s: file too short to be a gmon file\n", + filename); + done(1); + } /* if */ + tmp.low_pc = get_vma(core_bfd, (bfd_byte *) &raw.low_pc[0]); + tmp.high_pc = get_vma(core_bfd, (bfd_byte *) &raw.high_pc[0]); + tmp.ncnt = bfd_get_32(core_bfd, (bfd_byte *) &raw.ncnt[0]); + if (s_highpc && (tmp.low_pc != h.low_pc || + tmp.high_pc != h.high_pc || tmp.ncnt != h.ncnt)) + { + fprintf(stderr, "%s: incompatible with first gmon file\n", + filename); + done(1); + } /* if */ + h = tmp; + s_lowpc = (bfd_vma) h.low_pc; + s_highpc = (bfd_vma) h.high_pc; + lowpc = (bfd_vma) h.low_pc / sizeof(UNIT); + highpc = (bfd_vma) h.high_pc / sizeof(UNIT); + samp_bytes = h.ncnt - sizeof(struct raw_phdr); + hist_num_bins = samp_bytes / sizeof (UNIT); + DBG(SAMPLEDEBUG, + printf("[gmon_out_read] lowpc 0x%lx highpc 0x%lx ncnt %d\n", + h.low_pc, h.high_pc, h.ncnt); + printf("[gmon_out_read] s_lowpc 0x%lx s_highpc 0x%lx\n", + s_lowpc, s_highpc); + printf("[gmon_out_read] lowpc 0x%lx highpc 0x%lx\n", + lowpc, highpc); + printf("[gmon_out_read] samp_bytes %d hist_num_bins %d\n", + samp_bytes, hist_num_bins)); + + if (hist_num_bins) { + ++nhist; + } /* if */ + + if (!hist_sample) { + hist_sample = + (int*) xmalloc(hist_num_bins * sizeof(hist_sample[0])); + memset(hist_sample, 0, hist_num_bins * sizeof(hist_sample[0])); + } /* if */ + + for (i = 0; i < hist_num_bins; ++i) { + if (fread(raw_bin_count, sizeof(raw_bin_count), 1, ifp) != 1) { + fprintf(stderr, + "%s: unexpected EOF after reading %d/%d bins\n", + whoami, --i, hist_num_bins); + done(1); + } /* if */ + hist_sample[i] += bfd_get_16(core_bfd, (bfd_byte*) raw_bin_count); + } /* for */ + + /* + * The rest of the file consists of a bunch of + * 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 ***/ diff --git a/gprof/gmon_io.h b/gprof/gmon_io.h new file mode 100644 index 00000000000..1709eeb8ae0 --- /dev/null +++ b/gprof/gmon_io.h @@ -0,0 +1,20 @@ +#ifndef gmon_io_h +#define gmon_io_h + +#include "bfd.h" +#include "gmon.h" + +#define INPUT_HISTOGRAM (1<<0) +#define INPUT_CALL_GRAPH (1<<1) +#define INPUT_BB_COUNTS (1<<2) + +extern int gmon_input; /* what input did we see? */ +extern int gmon_file_version; /* file version are we dealing with */ + +extern bfd_vma get_vma PARAMS((bfd *abfd, bfd_byte *addr)); +extern void put_vma PARAMS((bfd *abfd, bfd_vma val, bfd_byte *addr)); + +extern void gmon_out_read PARAMS((const char *filename)); +extern void gmon_out_write PARAMS((const char *filename)); + +#endif /* gmon_io_h */ diff --git a/gprof/gmon_out.h b/gprof/gmon_out.h new file mode 100644 index 00000000000..c9638692e90 --- /dev/null +++ b/gprof/gmon_out.h @@ -0,0 +1,46 @@ +/* + * This file specifies the format of gmon.out files. It should have + * as few external dependencies as possible as it is going to be + * included in many different programs. That is, minimize the + * number of #include's. + * + * A gmon.out file consists of a header (defined by gmon_hdr) followed + * by a sequence of records. Each record starts with a one-byte tag + * identifying the type of records, followed by records specific data. + */ +#ifndef gmon_out_h +#define gmon_out_h + +#define GMON_MAGIC "gmon" /* magic cookie */ +#define GMON_VERSION 1 /* version number */ + +/* + * Raw header as it appears on file (without padding): + */ +struct gmon_hdr { + char cookie[4]; + char version[4]; + char spare[3*4]; +}; + +/* types of records in this file: */ +typedef enum { + GMON_TAG_TIME_HIST, GMON_TAG_CG_ARC, GMON_TAG_BB_COUNT +} GMON_Record_Tag; + +struct gmon_hist_hdr { + char low_pc[sizeof(bfd_vma)]; /* base pc address of sample buffer */ + char high_pc[sizeof(bfd_vma)]; /* max pc address of sampled buffer */ + char hist_size[4]; /* size of sample buffer */ + char prof_rate[4]; /* profiling clock rate */ + char dimen[15]; /* phys. dim., usually "seconds" */ + char dimen_abbrev; /* usually 's' for "seconds" */ +}; + +struct gmon_cg_arc_record { + char from_pc[sizeof(bfd_vma)]; /* address within caller's body */ + char self_pc[sizeof(bfd_vma)]; /* address within callee's body */ + char count[4]; /* number of arc traversals */ +}; + +#endif /* gmon_out_h */ diff --git a/gprof/gprof.c b/gprof/gprof.c index 15eb7fd6ee1..eb0e1c53a07 100644 --- a/gprof/gprof.c +++ b/gprof/gprof.c @@ -16,776 +16,460 @@ * 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", + "", "", + 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 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 (ab) - 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); } diff --git a/gprof/gprof.h b/gprof/gprof.h index 0ac4c147d20..c4d9da1d469 100644 --- a/gprof/gprof.h +++ b/gprof/gprof.h @@ -18,284 +18,118 @@ * * @(#)gprof.h 5.9 (Berkeley) 6/1/90 */ +#ifndef gprof_h +#define gprof_h #include #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 */ diff --git a/gprof/hertz.c b/gprof/hertz.c index 0d8d9d31500..979ad3748fb 100644 --- a/gprof/hertz.c +++ b/gprof/hertz.c @@ -16,38 +16,31 @@ * 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 +#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 */ diff --git a/gprof/hertz.h b/gprof/hertz.h new file mode 100644 index 00000000000..6708be1ea66 --- /dev/null +++ b/gprof/hertz.h @@ -0,0 +1,14 @@ +#ifndef hertz_h +#define hertz_h + +#include "gprof.h" + +#define HZ_WRONG 0 /* impossible clock frequency */ + +/* + * Discover the tick frequency of the machine if something goes wrong, + * we return HZ_WRONG, an impossible sampling frequency. + */ +extern int hertz PARAMS((void)); + +#endif /* hertz_h */ diff --git a/gprof/hist.c b/gprof/hist.c new file mode 100644 index 00000000000..0b2719bda7a --- /dev/null +++ b/gprof/hist.c @@ -0,0 +1,507 @@ +/* + * Histogram related operations. + */ +#include +#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 ***/ diff --git a/gprof/hist.h b/gprof/hist.h new file mode 100644 index 00000000000..1335b833319 --- /dev/null +++ b/gprof/hist.h @@ -0,0 +1,23 @@ +#ifndef hist_h +#define hist_h + +#include "bfd.h" + +extern bfd_vma s_lowpc; /* lowpc from the profile file */ +extern bfd_vma s_highpc; /* highpc from the profile file */ +extern bfd_vma lowpc, highpc; /* range profiled, in UNIT's */ +extern int hist_num_bins; /* number of histogram bins */ +extern int *hist_sample; /* code histogram */ +/* + * Scale factor converting samples to pc values: each sample covers + * HIST_SCALE bytes: + */ +extern double hist_scale; + + +extern void hist_read_rec PARAMS((FILE *ifp, const char *filename)); +extern void hist_write_hist PARAMS((FILE *ofp, const char *filename)); +extern void hist_assign_samples PARAMS((void)); +extern void hist_print PARAMS((void)); + +#endif /* hist_h */ diff --git a/gprof/i386.c b/gprof/i386.c index 50e99803efa..219be72091e 100644 --- a/gprof/i386.c +++ b/gprof/i386.c @@ -16,107 +16,71 @@ * 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; } @@ -131,13 +95,9 @@ findcall( parentp , p_lowpc , p_highpc ) /* * something funny going on. */ -# ifdef DEBUG - if ( debug & CALLDEBUG ) { - printf( "[findcall]\tbut it's a botch\n" ); - } -# endif DEBUG - length = 1; - continue; + DBG(CALLDEBUG, printf("[findcall]\tbut it's a botch\n")); + length = 1; + continue; } } } diff --git a/gprof/i386.h b/gprof/i386.h index 79d7b9e652a..baf856a8597 100644 --- a/gprof/i386.h +++ b/gprof/i386.h @@ -30,8 +30,8 @@ * offset (in bytes) of the code from the entry address of a routine. * (see asgnsamples for use and explanation.) */ -#define OFFSET_OF_CODE 0 -#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT)) +#define OFFSET_TO_CODE 0 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) #ifdef __MSDOS__ #define FOPEN_RB "rb" diff --git a/gprof/lookup.c b/gprof/lookup.c deleted file mode 100644 index 56ded0638cc..00000000000 --- a/gprof/lookup.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that: (1) source distributions retain this entire copyright - * notice and comment, and (2) distributions including binaries display - * the following acknowledgement: ``This product includes software - * developed by the University of California, Berkeley and its contributors'' - * in the documentation or other materials provided with the distribution - * and in all advertising materials mentioning features or use of this - * software. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#ifndef lint -static char sccsid[] = "@(#)lookup.c 5.4 (Berkeley) 6/1/90"; -#endif /* not lint */ - -#include "gprof.h" - - /* - * look up an address in a sorted-by-address namelist - * this deals with misses by mapping them to the next lower - * entry point. - */ -#ifdef DEBUG -nltype *dbg_nllookup(); -#endif - -nltype * -nllookup( address ) - unsigned long address; -{ - register long low; - register long middle; - register long high; -# ifdef DEBUG - register int probes; - - probes = 0; -# endif DEBUG - for ( low = 0 , high = nname - 1 ; low != high ; ) { -# ifdef DEBUG - probes += 1; -# endif DEBUG - middle = ( high + low ) >> 1; - if ( nl[ middle ].value <= address && nl[ middle+1 ].value > address ) { -# ifdef DEBUG - if ( debug & LOOKUPDEBUG ) { - printf( "[nllookup] %d (%d) probes\n" , probes , nname-1 ); - } -# endif DEBUG - return &nl[ middle ]; - } - if ( nl[ middle ].value > address ) { - high = middle; - } else { - low = middle + 1; - } - } - if(nl[middle+1].value <= address) { -# ifdef DEBUG - if (debug & LOOKUPDEBUG ) { - printf("[nllookup] %d (%d) probes, fall off\n", probes, nname-1); - } -# endif - return &nl[middle+1]; - } - fprintf( stderr , "[nllookup] binary search fails???\n" ); -#ifdef DEBUG - dbg_nllookup(address); -#endif - return 0; -} - -#ifdef DEBUG -nltype * -dbg_nllookup( address ) - unsigned long address; -{ - register long low; - register long middle; - register long high; - fprintf(stderr,"[nllookup] address 0x%x\n",address); - - for ( low = 0 , high = nname - 1 ; low != high ; ) { - - middle = ( high + low ) >> 1; - fprintf(stderr, "[nllookup] low 0x%x middle 0x%x high 0x%x nl[m] 0x%x nl[m+1] 0x%x\n", - low,middle,high,nl[middle].value,nl[middle+1].value); - if ( nl[ middle ].value <= address && nl[ middle+1 ].value > address) { - return &nl[ middle ]; - } - if ( nl[ middle ].value > address ) { - high = middle; - } else { - low = middle + 1; - } - } - fprintf( stderr , "[nllookup] binary search fails???\n" ); - return 0; -} -#endif DEBUG - -arctype * -arclookup( parentp , childp ) - nltype *parentp; - nltype *childp; -{ - arctype *arcp; - - if ( parentp == 0 || childp == 0 ) { - printf( "[arclookup] parentp == 0 || childp == 0\n" ); - return 0; - } -# ifdef DEBUG - if ( debug & LOOKUPDEBUG ) { - printf( "[arclookup] parent %s child %s\n" , - parentp -> name , childp -> name ); - } -# endif DEBUG - for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) { -# ifdef DEBUG - if ( debug & LOOKUPDEBUG ) { - printf( "[arclookup]\t arc_parent %s arc_child %s\n" , - arcp -> arc_parentp -> name , - arcp -> arc_childp -> name ); - } -# endif DEBUG - if ( arcp -> arc_childp == childp ) { - return arcp; - } - } - return 0; -} diff --git a/gprof/printgprof.c b/gprof/printgprof.c deleted file mode 100644 index f664bcab0b7..00000000000 --- a/gprof/printgprof.c +++ /dev/null @@ -1,787 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that: (1) source distributions retain this entire copyright - * notice and comment, and (2) distributions including binaries display - * the following acknowledgement: ``This product includes software - * developed by the University of California, Berkeley and its contributors'' - * in the documentation or other materials provided with the distribution - * and in all advertising materials mentioning features or use of this - * software. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#ifndef lint -static char sccsid[] = "@(#)printgprof.c 5.7 (Berkeley) 6/1/90"; -#endif /* not lint */ - -#include "gprof.h" -#include - -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 \n" - : "%6.6s %5.5s %7.7s %7.7s %7.7s %7.7s \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( " " , 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( " \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 , "" , 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; -} - diff --git a/gprof/printlist.c b/gprof/printlist.c deleted file mode 100644 index 8e952f05d2b..00000000000 --- a/gprof/printlist.c +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 1983 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that: (1) source distributions retain this entire copyright - * notice and comment, and (2) distributions including binaries display - * the following acknowledgement: ``This product includes software - * developed by the University of California, Berkeley and its contributors'' - * in the documentation or other materials provided with the distribution - * and in all advertising materials mentioning features or use of this - * software. Neither the name of the University nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - */ - -#ifndef lint -static char sccsid[] = "@(#)printlist.c 5.5 (Berkeley) 6/1/90"; -#endif /* not lint */ - -#include "gprof.h" - - /* - * these are the lists of names: - * there is the list head and then the listname - * is a pointer to the list head - * (for ease of passing to stringlist functions). - */ -struct stringlist kfromhead = { 0 , 0 }; -struct stringlist *kfromlist = &kfromhead; -struct stringlist ktohead = { 0 , 0 }; -struct stringlist *ktolist = &ktohead; -struct stringlist fhead = { 0 , 0 }; -struct stringlist *flist = &fhead; -struct stringlist Fhead = { 0 , 0 }; -struct stringlist *Flist = &Fhead; -struct stringlist ehead = { 0 , 0 }; -struct stringlist *elist = &ehead; -struct stringlist Ehead = { 0 , 0 }; -struct stringlist *Elist = &Ehead; - -addlist( listp , funcname ) - struct stringlist *listp; - char *funcname; -{ - struct stringlist *slp; - - slp = (struct stringlist *) malloc( sizeof(struct stringlist)); - if ( slp == (struct stringlist *) 0 ) { - fprintf( stderr, "gprof: ran out room for printlist\n" ); - done(); - } - slp -> next = listp -> next; - slp -> string = funcname; - listp -> next = slp; -} - -bool -onlist( listp , funcname ) - struct stringlist *listp; - char *funcname; -{ - struct stringlist *slp; - - for ( slp = listp -> next ; slp ; slp = slp -> next ) { - if ( ! strcmp( slp -> string , funcname ) ) { - return TRUE; - } - if ( funcname[0] == '_' && ! strcmp( slp -> string , &funcname[1] ) ) { - return TRUE; - } - } - return FALSE; -} diff --git a/gprof/search_list.c b/gprof/search_list.c new file mode 100644 index 00000000000..565b83bca57 --- /dev/null +++ b/gprof/search_list.c @@ -0,0 +1,38 @@ +#include "libiberty.h" +#include "gprof.h" +#include "search_list.h" + + +void +DEFUN(search_list_append, (list, paths), + Search_List *list AND const char *paths) +{ + Search_List_Elem *new_el; + const char *beg, *colon; + int len; + + colon = paths - 1; + do { + beg = colon + 1; + colon = strchr(beg, ':'); + if (colon) { + len = colon - beg; + } else { + len = strlen(beg); + } /* if */ + new_el = (Search_List_Elem*) xmalloc(sizeof(*new_el) + len); + memcpy(new_el->path, beg, len); + new_el->path[len] = '\0'; + + /* append new path at end of list: */ + new_el->next = 0; + if (list->tail) { + list->tail->next = new_el; + } else { + list->head = new_el; + } /* if */ + list->tail = new_el; + } while (colon); +} /* search_list_append */ + + /*** end of search_list.c ***/ diff --git a/gprof/search_list.h b/gprof/search_list.h new file mode 100644 index 00000000000..092d9565222 --- /dev/null +++ b/gprof/search_list.h @@ -0,0 +1,16 @@ +#ifndef search_list_h +#define search_list_h + +typedef struct search_list_elem { + struct search_list_elem *next; + char path[1]; +} Search_List_Elem; + +typedef struct { + struct search_list_elem *head; + struct search_list_elem *tail; +} Search_List; + +extern void search_list_append PARAMS((Search_List *list, const char *paths)); + +#endif /* search_list_h */ diff --git a/gprof/source.c b/gprof/source.c new file mode 100644 index 00000000000..7917bba832e --- /dev/null +++ b/gprof/source.c @@ -0,0 +1,186 @@ +/* + * Keeps track of source files. + */ +#include "gprof.h" +#include "libiberty.h" +#include "search_list.h" +#include "source.h" + +#define EXT_ANNO "-ann" /* postfix of annotated files */ + +/* + * Default option values: + */ +bool create_annotation_files = FALSE; + +Search_List src_search_list = {0, 0}; +Source_File *first_src_file = 0; + + +Source_File* +DEFUN(source_file_lookup_path, (path), const char *path) +{ + Source_File *sf; + + for (sf = first_src_file; sf; sf = sf->next) { + if (strcmp(path, sf->name) == 0) { + break; + } /* if */ + } /* for */ + if (!sf) { + /* create a new source file descriptor: */ + + sf = (Source_File*) xmalloc(sizeof(*sf)); + memset(sf, 0, sizeof(*sf)); + sf->name = strdup(path); + sf->next = first_src_file; + first_src_file = sf; + } /* if */ + return sf; +} /* source_file_lookup_path */ + + +Source_File* +DEFUN(source_file_lookup_name, (filename), const char *filename) +{ + const char *fname; + Source_File *sf; + /* + * The user cannot know exactly how a filename will be stored in + * the debugging info (e.g., ../include/foo.h + * vs. /usr/include/foo.h). So we simply compare the filename + * component of a path only: + */ + for (sf = first_src_file; sf; sf = sf->next) { + fname = strrchr(sf->name, '/'); + if (fname) { + ++fname; + } else { + fname = sf->name; + } /* if */ + if (strcmp(filename, fname) == 0) { + break; + } /* if */ + } /* for */ + return sf; +} /* source_file_lookup_name */ + + +FILE* +DEFUN(annotate_source, (sf, max_width, annote, arg), + Source_File *sf AND int max_width + AND void (*annote) PARAMS((char *buf, int w, int l, void *arg)) + AND void *arg) +{ + static bool first_file = TRUE; + int i, line_num, nread; + bool new_line; + char buf[8192]; + char fname[PATH_MAX]; + char *annotation, *name_only; + FILE *ifp, *ofp; + Search_List_Elem *sle = src_search_list.head; + + /* + * Open input file. If open fails, walk along search-list until + * open succeeds or reaching end of list: + */ + strcpy(fname, sf->name); + if (sf->name[0] == '/') { + sle = 0; /* don't use search list for absolute paths */ + } /* if */ + name_only = 0; + while (TRUE) { + DBG(SRCDEBUG, printf("[annotate_source]: looking for %s, trying %s\n", + sf->name, fname)); + ifp = fopen(fname, FOPEN_RB); + if (ifp) { + break; + } /* if */ + if (!sle && !name_only) { + name_only = strrchr(sf->name, '/'); + if (name_only) { + /* try search-list again, but this time with name only: */ + ++name_only; + sle = src_search_list.head; + } /* if */ + } /* if */ + if (sle) { + strcpy(fname, sle->path); + strcat(fname, "/"); + if (name_only) { + strcat(fname, name_only); + } else { + strcat(fname, sf->name); + } /* if */ + sle = sle->next; + } else { + if (errno == ENOENT) { + fprintf(stderr, "%s: could not locate `%s'\n", + whoami, sf->name); + } else { + perror(sf->name); + } /* if */ + return 0; + } /* if */ + } /* while */ + + ofp = stdout; + if (create_annotation_files) { + /* try to create annotated source file: */ + const char *filename; + + /* create annotation files in the current working directory: */ + filename = strrchr(sf->name, '/'); + if (filename) { + ++filename; + } else { + filename = sf->name; + } /* if */ + + strcpy(fname, filename); + strcat(fname, EXT_ANNO); + ofp = fopen(fname, "w"); + if (!ofp) { + perror(fname); + return 0; + } /* if */ + } /* if */ + + /* + * Print file names if output goes to stdout and there are + * more than one source file: + */ + if (ofp == stdout) { + if (first_file) { + first_file = FALSE; + } else { + fputc('\n', ofp); + } /* if */ + if (first_output) { + first_output = FALSE; + } else { + fprintf(ofp, "\f\n"); + } /* if */ + fprintf(ofp, "*** File %s:\n", sf->name); + } /* if */ + + annotation = xmalloc(max_width + 1); + line_num = 1; + new_line = TRUE; + while ((nread = fread(buf, 1, sizeof(buf), ifp)) > 0) { + for (i = 0; i < nread; ++i) { + if (new_line) { + (*annote)(annotation, max_width, line_num, arg); + fputs(annotation, ofp); + ++line_num; new_line = FALSE; + } /* if */ + new_line = (buf[i] == '\n'); + fputc(buf[i], ofp); + } /* for */ + } /* while */ + free(annotation); + return ofp; +} /* annotate_source */ + + /*** end of source.c ***/ diff --git a/gprof/source.h b/gprof/source.h new file mode 100644 index 00000000000..a542446cb2e --- /dev/null +++ b/gprof/source.h @@ -0,0 +1,53 @@ +#ifndef source_h +#define source_h + +#include +#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 */ diff --git a/gprof/sparc.c b/gprof/sparc.c index 2fd7c65c4ae..98e29ce8554 100644 --- a/gprof/sparc.c +++ b/gprof/sparc.c @@ -16,116 +16,64 @@ * 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 ***/ diff --git a/gprof/sparc.h b/gprof/sparc.h index 0287b88fc69..a5b46ad89f0 100644 --- a/gprof/sparc.h +++ b/gprof/sparc.h @@ -27,6 +27,6 @@ * offset (in bytes) of the code from the entry address of a routine. * (see asgnsamples for use and explanation.) */ -#define OFFSET_OF_CODE 0 -#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT)) +#define OFFSET_TO_CODE 0 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) diff --git a/gprof/sym_ids.c b/gprof/sym_ids.c new file mode 100644 index 00000000000..5bd94f1af0c --- /dev/null +++ b/gprof/sym_ids.c @@ -0,0 +1,315 @@ +#include "libiberty.h" +#include "cg_arcs.h" +#include "sym_ids.h" + +struct sym_id { + struct sym_id *next; + char *spec; /* parsing modifies this */ + Table_Id which_table; + bool has_right; + struct match { + int prev_index; /* index of prev match */ + Sym *prev_match; /* previous match */ + Sym *first_match; /* chain of all matches */ + Sym sym; + } left, right; +} *id_list; + +Sym_Table syms[NUM_TABLES]; + +#ifdef DEBUG +const char *table_name[] = { + "INCL_GRAPH", "EXCL_GRAPH", + "INCL_ARCS", "EXCL_ARCS", + "INCL_FLAT", "EXCL_FLAT", + "INCL_TIME", "EXCL_TIME", + "INCL_ANNO", "EXCL_ANNO", + "INCL_EXEC", "EXCL_EXEC" +}; +#endif /* DEBUG */ + +/* + * This is the table in which we keep all the syms that match + * the right half of an arc id. It is NOT sorted according + * to the addresses, because it is accessed only through + * the left half's CHILDREN pointers (so it's crucial not + * to reorder this table once pointers into it exist). + */ +static Sym_Table right_ids; + +static Source_File non_existent_file = { + 0, "" +}; + + +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 ***/ diff --git a/gprof/sym_ids.h b/gprof/sym_ids.h new file mode 100644 index 00000000000..68604de3fd2 --- /dev/null +++ b/gprof/sym_ids.h @@ -0,0 +1,23 @@ +#ifndef sym_ids_h +#define sym_ids_h + +#include "symtab.h" + +typedef enum { + INCL_GRAPH = 0, EXCL_GRAPH, + INCL_ARCS, EXCL_ARCS, + INCL_FLAT, EXCL_FLAT, + INCL_TIME, EXCL_TIME, + INCL_ANNO, EXCL_ANNO, + INCL_EXEC, EXCL_EXEC, + NUM_TABLES +} Table_Id; + +extern Sym_Table syms[NUM_TABLES]; + +extern void sym_id_add PARAMS((const char *spec, Table_Id which_table)); +extern void sym_id_parse PARAMS((void)); +extern bool sym_id_arc_is_present PARAMS((Sym_Table *symtab, + Sym *from, Sym *to)); + +#endif /* sym_ids_h */ diff --git a/gprof/symtab.c b/gprof/symtab.c new file mode 100644 index 00000000000..6591844d280 --- /dev/null +++ b/gprof/symtab.c @@ -0,0 +1,233 @@ +#include "gprof.h" +#include "cg_arcs.h" +#include "core.h" +#include "symtab.h" + +Sym_Table symtab; + + +/* + * Initialize a symbol (so it's empty). + */ +void +DEFUN(sym_init, (sym), Sym *sym) +{ + memset(sym, 0, sizeof(*sym)); + /* + * It is not safe to assume that a binary zero corresponds to + * a floating-point 0.0, so initialize floats explicitly: + */ + sym->hist.time = 0.0; + sym->cg.child_time = 0.0; + sym->cg.prop.fract = 0.0; + sym->cg.prop.self = 0.0; + sym->cg.prop.child = 0.0; +} /* sym_init */ + + +/* + * Compare the function entry-point of two symbols and return <0, =0, + * or >0 depending on whether the left value is smaller than, equal + * to, or greater than the right value. If two symbols are equal + * but one has is_func set and the other doesn't, we make the + * non-function symbol one "bigger" so that the function symbol will + * survive duplicate removal. Finally, if both symbols have the + * same is_func value, we discriminate against is_static such that + * the global symbol survives. + */ +static int +DEFUN(cmp_addr, (lp, rp), const PTR lp AND const PTR rp) +{ + Sym *left = (Sym*) lp; + Sym *right = (Sym*) rp; + + if (left->addr > right->addr) { + return 1; + } else if (left->addr < right->addr) { + return -1; + } /* if */ + + if (left->is_func != right->is_func) { + return right->is_func - left->is_func; + } /* if */ + + return left->is_static - right->is_static; +} /* cmp_addr */ + + +void +DEFUN(symtab_finalize, (tab), Sym_Table *tab) +{ + Sym *src, *dst; + bfd_vma prev_addr; + + if (!tab->len) { + return; + } /* if */ + + /* + * Sort symbol table in order of increasing function addresses: + */ + qsort(tab->base, tab->len, sizeof(Sym), cmp_addr); + + /* + * Remove duplicate entries to speed-up later processing and + * set end_addr if its not set yet: + */ + prev_addr = tab->base[0].addr + 1; + for (src = dst = tab->base; src < tab->limit; ++src) { + if (src->addr == prev_addr) { + /* + * If same address, favor global symbol over static one. + * If both symbols are either static or global, check + * whether one has name beginning with underscore while + * the other doesn't. In such cases, keep sym without + * underscore. This takes cares of compiler generated + * symbols (such as __gnu_compiled, __c89_used, etc.). + */ + if ((!src->is_static && dst[-1].is_static) + || ((src->is_static == dst[-1].is_static) && + (src->name[0] != '_' && dst[-1].name[0] == '_') + || (src->name[0] + && src->name[1] != '_' && dst[-1].name[1] == '_'))) + { + DBG(AOUTDEBUG|IDDEBUG, + printf("[symtab_finalize] favor %s@%c%c over %s@%c%c", + src->name, src->is_static ? 't' : 'T', + src->is_func ? 'F' : 'f', + dst[-1].name, dst[-1].is_static ? 't' : 'T', + dst[-1].is_func ? 'F' : 'f'); + printf(" (addr=%lx)\n", src->addr)); + dst[-1] = *src; + } else { + DBG(AOUTDEBUG|IDDEBUG, + printf("[symtab_finalize] favor %s@%c%c over %s@%c%c", + dst[-1].name, dst[-1].is_static ? 't' : 'T', + dst[-1].is_func ? 'F' : 'f', + src->name, src->is_static ? 't' : 'T', + src->is_func ? 'F' : 'f'); + printf(" (addr=%lx)\n", src->addr)); + } /* if */ + } else { + if (dst > tab->base && dst[-1].end_addr == 0) { + dst[-1].end_addr = src->addr - 1; + } /* if */ + + /* retain sym only if it has a non-empty address range: */ + if (!src->end_addr || src->addr <= src->end_addr) { + *dst++ = *src; + prev_addr = src->addr; + } /* if */ + } /* if */ + } /* if */ + if (tab->len > 0 && dst[-1].end_addr == 0) { + dst[-1].end_addr = core_text_sect->vma + core_text_sect->_raw_size - 1; + } /* if */ + + DBG(AOUTDEBUG|IDDEBUG, + printf("[symtab_finalize]: removed %d duplicate entries\n", + tab->len - (int) (dst - tab->base))); + + tab->limit = dst; + tab->len = tab->limit - tab->base; + + DBG(AOUTDEBUG|IDDEBUG, + int j; + + for (j = 0; j < tab->len; ++j){ + printf("[symtab_finalize] 0x%lx-0x%lx\t%s\n", + (long) tab->base[j].addr, (long) tab->base[j].end_addr, + tab->base[j].name); + } /* for */); +} /* symtab_finalize */ + + +#ifdef DEBUG + +Sym* +DEFUN(dbg_sym_lookup, (symtab, address), Sym_Table *symtab AND bfd_vma address) +{ + long low, mid, high; + Sym *sym; + + fprintf(stderr,"[sym_lookup] address 0x%lx\n", address); + + sym = symtab->base; + for (low = 0, high = symtab->len - 1 ; low != high ;) { + mid = (high + low) >> 1; + fprintf(stderr, "[dbg_sym_lookup] low=0x%lx, mid=0x%lx, high=0x%lx\n", + low, mid, high); + fprintf(stderr, "[dbg_sym_lookup] sym[m]=0x%lx sym[m + 1]=0x%lx\n", + sym[mid].addr, sym[mid + 1].addr); + if (sym[mid].addr <= address && sym[mid + 1].addr > address) { + return &sym[mid]; + } /* if */ + if (sym[mid].addr > address) { + high = mid; + } else { + low = mid + 1; + } /* if */ + } /* for */ + fprintf(stderr, "[sym_lookup] binary search fails???\n"); + return 0; +} /* dbg_sym_lookup */ + +#endif DEBUG + + +/* + * Look up an address in the symbol-table that is sorted by address. + * If address does not hit any symbol, 0 is returned. + */ +Sym* +DEFUN(sym_lookup, (symtab, address), Sym_Table *symtab AND bfd_vma address) +{ + long low, high; + long mid = -1; + Sym *sym; +#ifdef DEBUG + int probes = 0; +#endif /* DEBUG */ + + if (!symtab->len) { + return 0; + } /* if */ + + sym = symtab->base; + for (low = 0, high = symtab->len - 1 ; low != high ;) { + DBG(LOOKUPDEBUG, ++probes); + mid = (high + low) / 2; + if (sym[mid].addr <= address && sym[mid + 1].addr > address) { + if (address > sym[mid].end_addr) { + /* + * Address falls into gap between sym[mid] and + * sym[mid + 1]: + */ + return 0; + } else { + DBG(LOOKUPDEBUG, + printf("[sym_lookup] %d probes (symtab->len=%d)\n", + probes, symtab->len - 1)); + return &sym[mid]; + } /* if */ + } /* if */ + if (sym[mid].addr > address) { + high = mid; + } else { + low = mid + 1; + } /* if */ + } /* for */ + if (sym[mid + 1].addr <= address) { + if (address > sym[mid + 1].end_addr) { + /* address is beyond end of sym[mid + 1]: */ + return 0; + } else { + DBG(LOOKUPDEBUG, printf("[sym_lookup] %d (%d) probes, fall off\n", + probes, symtab->len - 1)); + return &sym[mid + 1]; + } /* if */ + } /* if */ + return 0; +} /* sym_lookup */ + + /*** end of symtab.c ***/ diff --git a/gprof/symtab.h b/gprof/symtab.h new file mode 100644 index 00000000000..1702a9c6e6f --- /dev/null +++ b/gprof/symtab.h @@ -0,0 +1,88 @@ +#ifndef symtab_h +#define symtab_h + +#include "bfd.h" +#include "gprof.h" + +/* + * For a profile to be intelligible to a human user, it is necessary + * to map code-addresses into source-code information. Source-code + * information can be any combination of: (i) function-name, (ii) + * source file-name, and (iii) source line number. + * + * The symbol table is used to map addresses into source-code + * information. + */ + +#include "source.h" + +/* + * Symbol-entry. For each external in the specified file we gather + * its address, the number of calls and compute its share of cpu time. + */ +typedef struct sym { + /* + * Common information: + * + * In the symbol-table, fields ADDR and FUNC_NAME are guaranteed + * to contain valid information. FILE may be 0, if unknown and + * LINE_NUM maybe 0 if unknown. + */ + bfd_vma addr; /* address of entry point */ + bfd_vma end_addr; /* end-address */ + const char *name; /* name of function this sym is from */ + Source_File *file; /* source file symbol comes from */ + int line_num; /* source line number */ + unsigned int is_func : 1, /* is this a function entry point? */ + is_static : 1, /* is this a local (static) symbol? */ + is_bb_head : 1; /* is this the head of a basic-blk? */ + int ncalls; /* how many times executed */ + struct sym *next; /* for building chains of syms */ + + /* profile-specific information: */ + + /* histogram specific info: */ + struct { + double time; /* (weighted) ticks in this routine */ + bfd_vma scaled_addr; /* scaled entry point */ + } hist; + + /* call-graph specific info: */ + struct { + int self_calls; /* how many calls to self */ + double child_time; /* cumulative ticks in children */ + int index; /* index in the graph list */ + int top_order; /* graph call chain top-sort order */ + bool print_flag; /* should this be printed? */ + struct { + double fract; /* what % of time propagates */ + double self; /* how much self time propagates */ + double child; /* how much child time propagates */ + } prop; + struct { + int num; /* internal number of cycle on */ + struct sym *head; /* head of cycle */ + struct sym *next; /* next member of cycle */ + } cyc; + struct arc *parents; /* list of caller arcs */ + struct arc *children; /* list of callee arcs */ + } cg; +} Sym; + +/* + * Symbol-tables are always assumed to be sorted in increasing order + * of addresses: + */ +typedef struct { + int len; /* # of symbols in this table */ + Sym *base; /* first element in symbol table */ + Sym *limit; /* limit = base + len */ +} Sym_Table; + +extern Sym_Table symtab; /* the symbol table */ + +extern void sym_init PARAMS((Sym *sym)); +extern void symtab_finalize PARAMS((Sym_Table *symtab)); +extern Sym *sym_lookup PARAMS((Sym_Table *symtab, bfd_vma address)); + +#endif /* symtab_h */ diff --git a/gprof/tahoe.c b/gprof/tahoe.c index 924d95d06e1..5e3d7d9bdbc 100644 --- a/gprof/tahoe.c +++ b/gprof/tahoe.c @@ -16,44 +16,22 @@ * 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: @@ -68,31 +46,31 @@ operandmode( modep ) case 7: return autodec; case 8: - return ( usesreg != 0xe ? autoinc : immediate ); + return usesreg != 0xe ? autoinc : immediate; case 9: - return ( usesreg != PC ? autoincdef : absolute ); + return usesreg != PC ? autoincdef : absolute; case 10: - return ( usesreg != PC ? bytedisp : byterel ); + return usesreg != PC ? bytedisp : byterel; case 11: - return ( usesreg != PC ? bytedispdef : bytereldef ); + return usesreg != PC ? bytedispdef : bytereldef; case 12: - return ( usesreg != PC ? worddisp : wordrel ); + return usesreg != PC ? worddisp : wordrel; case 13: - return ( usesreg != PC ? worddispdef : wordreldef ); + return usesreg != PC ? worddispdef : wordreldef; case 14: - return ( usesreg != PC ? longdisp : longrel ); + return usesreg != PC ? longdisp : longrel; case 15: - return ( usesreg != PC ? longdispdef : longreldef ); + return usesreg != PC ? longdispdef : longreldef; } /* NOTREACHED */ } char * -operandname( mode ) +operandname(mode) operandenum mode; { - switch ( mode ) { + switch (mode) { case literal: return "literal"; case indexed: @@ -140,11 +118,11 @@ operandname( mode ) } long -operandlength( modep ) +operandlength(modep) unsigned char *modep; { - switch ( operandmode( modep ) ) { + switch (operandmode(modep)) { case literal: case reg: case regdef: @@ -170,16 +148,16 @@ operandlength( modep ) 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; @@ -187,82 +165,81 @@ reladdr( modep ) 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: @@ -278,8 +255,8 @@ findcall( parentp , p_lowpc , p_highpc ) * [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: @@ -289,24 +266,21 @@ findcall( parentp , p_lowpc , p_highpc ) * 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; @@ -322,11 +296,7 @@ findcall( parentp , p_lowpc , p_highpc ) /* * something funny going on. */ -# ifdef DEBUG - if ( debug & CALLDEBUG ) { - printf( "[findcall]\tbut it's a botch\n" ); - } -# endif DEBUG + DBG(CALLDEBUG, printf("[findcall]\tbut it's a botch\n")); length = 1; continue; } diff --git a/gprof/tahoe.h b/gprof/tahoe.h index 55c1d32e47e..1e311b82ec5 100644 --- a/gprof/tahoe.h +++ b/gprof/tahoe.h @@ -28,8 +28,8 @@ * offset (in bytes) of the code from the entry address of a routine. * (see asgnsamples for use and explanation.) */ -#define OFFSET_OF_CODE 2 -#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT)) +#define OFFSET_TO_CODE 2 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) /* * register for pc relative addressing diff --git a/gprof/utils.c b/gprof/utils.c new file mode 100644 index 00000000000..9549a8d9057 --- /dev/null +++ b/gprof/utils.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include +#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(" ", 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 ***/ diff --git a/gprof/utils.h b/gprof/utils.h new file mode 100644 index 00000000000..7f5d342f6a7 --- /dev/null +++ b/gprof/utils.h @@ -0,0 +1,7 @@ +#ifndef utils_h +#define utils_h + +extern int print_name_only PARAMS((Sym *self)); +extern void print_name PARAMS((Sym *self)); + +#endif /* utils_h */ diff --git a/gprof/vax.c b/gprof/vax.c index 220b7582ad1..7bde4421778 100644 --- a/gprof/vax.c +++ b/gprof/vax.c @@ -16,44 +16,26 @@ * 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: @@ -68,31 +50,31 @@ operandmode( modep ) case 7: return autodec; case 8: - return ( usesreg != PC ? autoinc : immediate ); + return usesreg != PC ? autoinc : immediate; case 9: - return ( usesreg != PC ? autoincdef : absolute ); + return usesreg != PC ? autoincdef : absolute; case 10: - return ( usesreg != PC ? bytedisp : byterel ); + return usesreg != PC ? bytedisp : byterel; case 11: - return ( usesreg != PC ? bytedispdef : bytereldef ); + return usesreg != PC ? bytedispdef : bytereldef; case 12: - return ( usesreg != PC ? worddisp : wordrel ); + return usesreg != PC ? worddisp : wordrel; case 13: - return ( usesreg != PC ? worddispdef : wordreldef ); + return usesreg != PC ? worddispdef : wordreldef; case 14: - return ( usesreg != PC ? longdisp : longrel ); + return usesreg != PC ? longdisp : longrel; case 15: - return ( usesreg != PC ? longdispdef : longreldef ); + return usesreg != PC ? longdispdef : longreldef; } /* NOTREACHED */ } -char * -operandname( mode ) +static char * +operandname(mode) operandenum mode; { - switch ( mode ) { + switch (mode) { case literal: return "literal"; case indexed: @@ -139,12 +121,12 @@ operandname( mode ) /* NOTREACHED */ } -long -operandlength( modep ) +static long +operandlength(modep) struct modebyte *modep; { - switch ( operandmode( modep ) ) { + switch (operandmode(modep)) { case literal: case reg: case regdef: @@ -170,95 +152,96 @@ operandlength( modep ) 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: @@ -274,9 +257,9 @@ findcall( parentp , p_lowpc , p_highpc ) * [are there others that we miss?, * e.g. arrays of pointers to functions???] */ - addarc( parentp , &indirectchild , (long) 0 ); + arc_add(parent, &indirectchild, (long) 0); length += operandlength( - (struct modebyte *) ( instructp + length ) ); + (struct modebyte *) (instructp + length)); continue; case byterel: case wordrel: @@ -286,25 +269,22 @@ findcall( parentp , p_lowpc , p_highpc ) * 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; @@ -320,11 +300,7 @@ findcall( parentp , p_lowpc , p_highpc ) /* * something funny going on. */ -# ifdef DEBUG - if ( debug & CALLDEBUG ) { - printf( "[findcall]\tbut it's a botch\n" ); - } -# endif DEBUG + DBG(CALLDEBUG, printf("[findcall]\tbut it's a botch\n")); length = 1; continue; } diff --git a/gprof/vax.h b/gprof/vax.h index 3e45167520e..a8edc542645 100644 --- a/gprof/vax.h +++ b/gprof/vax.h @@ -28,8 +28,8 @@ * offset (in bytes) of the code from the entry address of a routine. * (see asgnsamples for use and explanation.) */ -#define OFFSET_OF_CODE 2 -#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT)) +#define OFFSET_TO_CODE 2 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) /* * register for pc relative addressing