diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1996-01-08 11:10:27 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1996-01-08 11:10:27 +0000 |
commit | 8b46c09925a80623c289e346c12921bc09fd1678 (patch) | |
tree | 01507d0da339cc7e5e6f5d16dfa625f94910b091 /gnu/usr.bin/binutils/gprof | |
parent | 5d56227f9458a53138642c1b4488b4a30f85f334 (diff) |
Initial GNU binutils 2.6 import
Diffstat (limited to 'gnu/usr.bin/binutils/gprof')
66 files changed, 12421 insertions, 0 deletions
diff --git a/gnu/usr.bin/binutils/gprof/.gdbinit b/gnu/usr.bin/binutils/gprof/.gdbinit new file mode 100644 index 00000000000..e519472ebcd --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/.gdbinit @@ -0,0 +1 @@ +dir .. diff --git a/gnu/usr.bin/binutils/gprof/ChangeLog b/gnu/usr.bin/binutils/gprof/ChangeLog new file mode 100644 index 00000000000..c5089b5e098 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/ChangeLog @@ -0,0 +1,719 @@ +Thu Nov 16 17:34:01 1995 Ken Raeburn <raeburn@cygnus.com> + + Version 2.6 released. + +Wed Nov 8 11:40:04 1995 Ian Lance Taylor <ian@cygnus.com> + + * gprof.c (main): Cast getenv return value. + +Mon Nov 6 15:05:00 1995 Ken Raeburn <raeburn@cygnus.com> + + * Makefile.in (TAGS): New target. + +Wed Nov 1 12:51:21 1995 Per Bothner <bothner@kalessin.cygnus.com> + + * Makefile.in (DISTSTUFF): Rename to GEN_FILES, to avoid confusion. + (all): Depend on $(GEN_FILES), not diststuff (which also depends + on info). + +Wed Nov 1 15:23:15 1995 Manfred Hollstein KS/EF4A 60/1F/110 #40283 <manfred@lts.sel.alcatel.de> + + * sym_ids.c: Include <ctype.h>. + +Wed Oct 25 13:24:31 1995 Per Bothner <bothner@kalessin.cygnus.com> + + * Makefile.in (diststuff): Also make info. + (mostlyclean): Don't remove gprof.info*. + (maintainer-clean realclean): Also remove *.info*. + +Fri Oct 6 16:25:32 1995 Ken Raeburn <raeburn@cygnus.com> + + Mon Sep 25 22:49:32 1995 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de> + + * Makefile.in: Add dependecies for $(OBJS) on header files. + + * cg_print.c (print_cycle, print_members, cg_print_index): Fix new + style output format to make it consistent. + * dummy.c (find_call): Fix typo in error message. + +Wed Sep 20 13:21:02 1995 Ian Lance Taylor <ian@cygnus.com> + + * Makefile.in (maintainer-clean): New target, synonym for + realclean. + +Fri Sep 8 14:38:08 1995 Ian Lance Taylor <ian@cygnus.com> + + * Makefile.in (install): Don't install in $(tooldir). + +Fri Aug 25 15:30:05 1995 Ken Raeburn <raeburn@cygnus.com> + + NS32K changes from Ian Dall: + * configure.in: Use ns32k, not ns532. + * ns532.c: Include symtab.h. + (find_call): Renamed from findcall. Print a message. + * ns532.h: Remove dummy.h comments. + +Tue Aug 22 10:00:45 1995 Jeffrey A. Law <law@rtl.cygnus.com> + + * Makefile.in (install): Remove "brokensed" hack, unnecessary now + that we're using autoconf. + +Wed Jul 19 18:46:13 1995 Fred Fish <fnf@cygnus.com> + + * core.c (get_src_info): Cast arg 7 of bfd_find_nearest_line + to proper type of "unsigned int *". + +Fri Jun 16 15:29:36 1995 Ken Raeburn <raeburn@cujo.cygnus.com> + + * configure.in: Use changequote around use of []. + +Mon Jun 12 12:14:52 1995 J.T. Conklin <jtc@rtl.cygnus.com> + + * Makefile.in (distclean, realclean): Remove config.cache and + config.log. + +Wed May 17 17:56:53 1995 J.T. Conklin <jtc@rtl.cygnus.com> + + * Makefile.in (Makefile): Added config.status to dependency list. + (config.status): New target. + (SHELL): New definition. + +Tue Apr 25 21:11:12 1995 Ken Raeburn <raeburn@cujo.cygnus.com> + + * Makefile.in (install): Depend on "all". + +Thu Apr 20 17:29:07 1995 Ken Raeburn <raeburn@cujo.cygnus.com> + + * Makefile.in: Change all references to MY_MACHINE to MY_TARGET, + to match configure script. + +Wed Apr 19 11:19:37 1995 J.T. Conklin <jtc@rtl.cygnus.com> + + * gen-c-prog.awk: Changed reference to "make-c-prog.awk" in + comment emitted by this script to gen-c-prog.awk. + + * Makefile.in, configure.in: Converted to use autoconf. + * configure: New file, generated with autoconf 2.3. + * config/{mt-alpha, mt-dummy, mt-i386, mt-ns532, mt-sparc, + mt-tahoe, mt-vax}: Removed. + +Mon Mar 13 21:44:24 1995 Ken Raeburn <raeburn@cujo.cygnus.com> + + * __bb_exit_func.c: New file, from David Mosberger-Tang. + + Thu Feb 9 16:56:07 1995 David Mosberger-Tang <davidm@piston.cs.arizona.edu> + + * All *.c: More cleanup towards GNU format. + + * gmon_out.h (struct gmon_hist_hdr, struct gmon_cg_arc_record): + replaced sizeof (bfd_vma) by size (char*) because Ken tells me + that bfd_vma is only guaranteed to be at least as big as a pointer. + + (GMON_Record_tag): added explicit enumeration values to ensure + compatibility across compilers. + + * gmon_io.c (get_vma, put_vma): replaced sizeof(bfd_vma) by + sizeof(char*). + +Tue Feb 7 17:24:12 1995 Ken Raeburn <raeburn@cujo.cygnus.com> + + * All *.c and *.h files: Ran "indent -gnu". Cleaned up a couple + of constructs GNU indent couldn't handle. Block comments not yet + rewritten in GNU format. + + * gprof.c (VERSION): Changed to 2.6, to get in sync for next + binutils release. + +Sun Feb 5 16:19:46 1995 David Mosberger-Tang <davidm@piston.cs.arizona.edu> + + * symtab.c (symtab_finalize): ensure globals symbols really + are favored over static ones---even if their name looks less + preferable; this is important for HP-UX; for example, there + is a static label Ltext_something that aliases the global + symbol _start + + * hist.c (hist_print): auto-scaling is now in effect for FSF-style + output only; also, auto-scaling is now performed based on + per-call, rather than total execution time, which is what it was + meant to be. + + * gprof.h (File_Format): new type. + + * gprof.c (VERSION): upped to 2.7---seems to be completely out of + sync with Cygnus version numbers though... + + (long_options): renamed --gmon-info to --file-info, --width added, + renamed --old-file-format to --file-format + (main): dito; also added support to read prof files, but as + mon_out_read() is not implemented, it's #ifdef'd out for now + + (usage): update to reflect new options. + + * gmon_io.c: replaced "old_file_format" by more general + "file_format" option + + * gmon.h (struct raw_phdr): fixed declaration for OSF/1. + + * core.c (core_sym_class): added back check for __gnu_compiled and + ___gnu_compiled for the benefit of systems without + bfd_find_nearest_line() support + + (get_src_info): now the libbfd is fixed, invoke bfd_find_nearest_line() + with section-relative addresses + + (core_create_function_syms): get_src_info() calls are currently + enabled for OSF/1 only. It appears to work allright for SunOS + 4.1.x as well, but on SPARCs it gets painfully slow with the + current implementation of aout_32_find_nearest_line(); + unfortunately, this means that static functions will not have their + filename printed in the call-graph function index; line-level + profiling should still work, but requires some patience + + * cg_print.c (cg_print_index): sanitized printing of index when + using FSF-style output; in particular, output width is now controlled + via option --width and the function tries hard to keep columns + aligned even in the presence of (occasional) long names + + * NOTES: a first shot at updating the documentation. + +Wed Feb 1 19:07:44 1995 David Mosberger-Tang <davidm@piston.cs.arizona.edu> + + * core.c (core_create_function_syms): fixed computation of min_vma + and max_vma. + + * *.c: removed rcsid. + +Tue Jan 31 16:18:18 1995 Ken Raeburn <raeburn@cujo.cygnus.com> + + * Lots of changes from David Mosberger-Tang: + + Tue Oct 25 19:20:14 1994 David Mosberger-Tang <davidm@piston.cs.arizona.edu> + + * gprof.c (main): put parentheses around & within &&. + + * basic_blocks.c (bb_read_rec): print warning message (once) when + ignoring basic-block execution counts. + + * source.c (source_file_lookup_name): corrected second argument to + strcmp(). + + * hist.c (print_header): merged Fri Oct 21 18:58:02 1994 change by + Ken Raeburn <raeburn@cujo.cygnus.com> from binutils-2.5.1. + + * gmon_io.c (gmon_out_read): the output stule STYLE_GMON_INFO is now + supported both for old and new (versioned) gmon.out files. Old + files are identified as version 0. + + * gmon.h (struct raw_arc): count field is now sizeof(long) bytes + long (instead of 4) because that is what OSF/1 v3.0 uses. + + * core.c: minor fixes and debugging info changes. + + Sun Sep 11 18:47:47 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu) + + * core.c (core_init): if .text cannot be found, try $CODE$ (the + name of the text-section under HP-UX). + + * hist.c (hist_assign_samples): fixed off-by-one bug: highpc + points one past the last sampling bin, so hist_scale should be + computed as "hist_scale /= hist_num_bins", not "hist_scale /= + hist_num_bins - 1". + + * gmon_io.c, hist.c, hist.h: renamed hist_num_samples to + hist_num_bins. + + * configure.in: added alpha-*-*) for per-target config. + + * alpha.c, alpha.h: created. + + * gprof.c (default_excluded_list): <locore>, <hicore> added. + + * core.c (core_create_function_syms, core_create_line_syms): + explicitly keep two sentinels "<locore>" and "<hicore>" that catch + all addresses outside the text-space. Thus, sym_lookup(&symtab, + addr) continues to guarantee not to return 0 on any address. It + also avoids incorrectly crediting the first/last symbol in the + text-space. + + * core.c (core_create_line_syms): always create function symbols + first, then merge in line symbols; this is so that if parts of the + program were compiled without -g, function-level symbols are + available still. + + * utils.c (print_name_only): support for print_path added. + + * symtab.c (cmp_addr): also use is_func flag in comparison. + (symtab_finalize): return immediately when table empty; now + more careful about getting rid of the right duplicate symbol. + + * sparc.c (find_call): many fixes---this function was rather + botched in binutils-2.4 already; it should work again. + + * source.c (source_file_lookup_path): PATH is now strdup'ed (it is + not good to rely on get_src_info() to return distinct string + pointers). + + * search_list.c (search_list_append): added cast for xmalloc(). + + * hist.c: added explicit initialization to some of the global + variables; fixed SItab (scales were off by a factor of 10). + + * hist.h: include of bfd.h added. + + * gprof.c, gprof.h (print_path): added. + + * gprof.h (MAX): fixed. + + * gmon_out.h: renamed gmon_time_hist_hdr to gmon_hist_hdr. + + * gmon_io.c: added some casts to (long) so we can always print as %lx + + * core.c (core_get_text_space): fixed to make it work. + + * cg_print.c (cg_print_index): added support for print_path option. + + * cg_dfn.h (cg_dfn): wrap prototype in PARAMS(). + + * call_graph.c, gmon_io.c, hist.c: avoid taking address of array + as some compilers complain (e.g., DEC's OSF/1 compiler) + + * basic_blocks.c, gmon_io.c, hist.c, source.c, sym_ids.c, + symtab.c: calls to memset() had 2nd and 3rd args reversed. + + Sat Sep 10 21:53:13 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu) + + * gprof.c: added "_mcount" to default_excluded_list. + (main): if output_style==0 and there is either a histogram or a + call-graph, always generate flat and call-graph, no matter what + line_granularity is set to. + + * source.c (source_file_lookup_name): if searching for sf->name + fails, try again with filename obtained after stripping off any + partial path from sf->name. + + * gprof.h (SRCDEBUG): added. + + * search_list.c (search_list_append): directories were added in wrong + order. + + * reimplemented selection mechanism from ground up; it is now possible + to accurately control what gets included/excluded in each of the + output styles; a "symbol-specification" (spec) is the basic means + to select a set of symbols; a spec has the syntax: + + spec == (FILENAME:(FUNCNAME|LINE_NUM) | NAME). + arc == spec/spec. + + any of the terminal symbols can be empty, in which case they + match anything (wildcards). NAME is interpreted as a FILENAME + if it contains a dot (e.g., foo.c), as LINE_NUM if it starts + with a digit, and as FUNCNAME otherwise. + + For example, to get a call-graph display that ignores arcs + from foo() to bar(), you'd say "--no-graph=foo/bar"; to + show only arcs into bar() (no matter what the caller), + you'd say "--graph=/bar"; and to get a call-graph without + any arc info, you'd say "--graph=/"; similarly, to + get a flat profile without mcount, you'd say "--no-flat=mcount" + and to get a flat profile that shows includes all functions + you'd say "--flat=""" (i.e., an empty spec) + + * hist.c (hist_print): top_time wasn't initialized to 0.0. + + Fri Sep 9 01:10:21 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu) + + * gmon_out.h: all headers now declared in terms of characters + to avoid getting into trouble with different compilers introducing + different amount of padding; the code already accessed the fields + through bfd functions, so that didn't have to change. + + * hist.c (hist_read_rec, hist_write_rec): added support for + collection pc histograms measuring quantities other than time; + the histogram header now includes a field that specifies the + dimension of the quantity measured by the histogram bins + (normally, this is "seconds", but other meaningful dimensions + include such things as "I-cache misses", "instruction issue stalls" + etc.); there is also a field to specify a one-character + abbreviation for the dimension; in the case of time, this would + be 's'; in most other cases it probably would be '1' (not a physical + dimension). + + Thu Sep 8 16:05:08 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu) + + * gprof.c, gmon_io.[ch]: BSD_COMPATIBLE is gone and new_file_version + has become old_file_version; gmon_io.c now always supports old-style + gmon.out files; it first tries to read gmon.out as a new version + file, if that fails, it tries to read it in the old format; + although not very likely, it is possible for gprof to mistake an + old-style file as a new one (the first 4 bytes would have to + be "gmon"---including the trailing '\0'); in that case, it is + necessary to specify --old-file-version + + * gprof.h: removed dependency on SYSV; the code now always uses + strrchr(), memset(), and memcpy() and does not include either + of string.h or strings.h; this should make gprof compile on + any (Unix) system without configuration (per suggestion of + raeburn@cygnus.com) + + * gprof.c (usage): fixed location of --new-file-format option. + + * cg_arcs.c (propagate_flags): fixed typo in declaration. + + * flat_bl.m: removed formfeed at end of file; the form-feed + is now printed cg_print.c only when necessary. + + * major rewrite of gprof---too many changes to mention all of + them. new features: + + + -l now requests profiling at the line level (as opposed + to function level); in this mode, gprof creates a "symbol" + (aka name-list entry) for each line of source code, instead + of one per function) + + + support for a new gmon.out file format; the new format + consists of a header with a magic and a version number, + followed by a sequence of profile data; profile data + can any of: (a) PC histogram, (b) call-graph arcs, or + (c) basic-block execution counts; the version number makes + it possible to extend gmon.out in a backwards compatible + fashion + + + support for tcov style annotated output: if the gmon.out file + contains basic-block execution counts, the user can request + the generation of annotated source files, much like Sun's + tcov used to do + + + long options + + + new scheme to suppress symbols that aren't function names + (e.g., avoids mistaking a goto label as a function) + + + reorganized source code to make it more managable; as a + side effect, gprof now compiles cleanly with "gcc -Wall" + + Thu Sep 1 15:46:49 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu) + + * gprof.c (funcsymbol): bfd_find_nearest_line() is now used as a + final cross-check to determine whether a static symbol should be + considered as a function-name. + + Fri Aug 5 19:32:36 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu) + + * gmon_io.c (gmon_out_read): recognize "-" as the filename for + stdin; this is useful if you wanna keep gmon.out files compressed; + this way you can "gzcat" the compressed file into gprof. + + * gprof.c: flag_min_count now initialized with 1 instead of 0. + + * basic_blocks.c (bb_annotate_source): added support for creating + .tcov files when option flag_annotate_make_files is TRUE. + (annotate_with_count): all counts less than the minimum count + specified by -m are now annotated with hash-marks. + + * gprof.c (main): -A is now followed by a string of option chars. + + * basic_blocks.c (annotate_with_count): replaced b->count with + cnt. + + * source.c: flag_annotate_source replaced by source_lock_map. + + * source.h: source_lock_map added. + + * gprof.c (main): new command-line syntax: -S simply specifies + which source-files user is interested in; -A requests annotated + source files and -AA requests that all lines in a source file + are annotated. + + Thu Aug 4 23:27:03 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu) + + * basic_blocks.c (PATH_MAX): if undefined, define as 1024. + + * sparc.c, i386.c, tahoe.c, vax.c: added include of "time_hist.h" + so s_lowpc etc. get declared. + + * arcs.h (doarcs): created. + + * arcs.c: reordered static functions such that they get defined + before use. + + * gprof.c (main): added options: + -A: request annotation of all source lines (with -S) + -m: minimum execution count (with default basic-block display) + -N: force new file format (only if BSD_COMPATIBLE is defined) + -S: annotate source file + -t: set table length (with -S) + + * Makefile (OBJS): added basic_blocks.o call_graph.o gmon_io.o + source.o time_hist.o + + Fri Jul 1 15:23:50 1994 David Mosberger-Tang (davidm@piston.cs.arizona.edu) + + * gprof.c (asgnsamples): computation of "pcl" and "pch" depended + on the fact being able to store a long in a double without loss of + precision; this does not hold on machines with 64 bit longs and 64 + bit doubles. + +Fri Oct 21 18:58:02 1994 Ken Raeburn <raeburn@cujo.cygnus.com> + + * printgprof.c (flatprofheader): Always set totime to 1.0 if not + greater than 0.0. Suggested by Harold Assink + <carlo@sg.tn.tudelft.nl>. + +Fri Sep 23 15:06:45 1994 Ken Raeburn <raeburn@cujo.cygnus.com> + + * printgprof.c (printprof): Use free, not cfree. + (printgprof, printindex): Ditto. + +Thu Sep 1 10:40:45 1994 Jeff Law (law@snake.cs.utah.edu) + + * gprof.h (kfromlist, ktolist, flist, Flist, elist, Elist): Make + decls extern to keep native HP compiler quiet. + +Tue Aug 30 11:12:13 1994 Ian Lance Taylor (ian@sanguine.cygnus.com) + + * gprof.c (funcsymbol): Ignore ___gnu_compiled as well as + __gnu_compiled, for the benefit of systems which add a leading + underscore. + +Wed Aug 24 12:49:13 1994 Ian Lance Taylor (ian@sanguine.cygnus.com) + + * configure.in: Change i386-*-* to i[345]86-*-*. + +Sun Jul 10 00:35:31 1994 Ian Dall (dall@hfrd.dsto.gov.au) + + * ns532.c, ns532.h: New Files. ns532 support. + + * config/mt-ns532: New File. ns532 support. + + * gprof.c: user register int i instead of defaulting the int. + Allows compilation with -Dregister= for debugging. + + * configure.in: Add ns532 support. + +Thu Jun 23 11:22:41 1994 Jeff Law (law@snake.cs.utah.edu) + + * Makefile.in (gprof): Depend on $(LIBS). + +Fri May 27 12:24:57 1994 Ken Raeburn (raeburn@cujo.cygnus.com) + + From binutils-2.4 release: + + Wed May 11 22:32:00 1994 DJ Delorie (dj@ctron.com) + + * configure.bat: [new] build makefile from makefile.in (dos) + * hertz.c: allow static HERTZ (msdos needs it) + * gprof.c: allow target to select "r" or "rb" for fopen + * gprof.c: ignore __gnu_compiled symbols + * i386.h: dfine FOPEN_RB to "rb" for dos. + +Tue May 17 15:30:22 1994 E. Michael Smith (ems@cygnus.com) + + * Makefile.in (.m.c:): Added .SUFFIXES : .m + so flat_bl.c would make from flat_bl.m file. + +Thu May 5 19:23:24 1994 Ken Raeburn (raeburn@cujo.cygnus.com) + + * Makefile.in (install-info): Check for gprof.info in build dir, + fall back to srcdir. Depend on it. + + * gprof.h (TRUE, FALSE): Always use undef before defining them. + +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. + +Wed Mar 30 16:12:40 1994 Ian Lance Taylor (ian@tweedledumb.cygnus.com) + + * gprof.c (getsymtab): Change nosyms to long. Rename + get_symtab_upper_bound to bfd_get_symtab_upper_bound. Check for + errors from bfd_get_symtab_upper_bound and + bfd_canonicalize_symtab. + +Tue Mar 22 10:50:52 1994 Jeffrey A. Law (law@snake.cs.utah.edu) + + * gprof.c (funcsymbol): Use bfd_get_symbol_info instead of + bfd_decode_symclass. + +Sun Mar 20 15:40:21 1994 Jeffrey A. Law (law@snake.cs.utah.edu) + + * Makefile.in: Avoid bug in hpux sed. + +Wed Dec 15 20:16:40 1993 david d `zoo' zuhn (zoo@andros.cygnus.com) + + * gprof.texi (Invoking): add text about -v flag + + * gprof.1: add text about -v flag + +Wed Dec 8 16:55:06 1993 david d `zoo' zuhn (zoo@andros.cygnus.com) + + * gprof.c (VERSION): defined a version macro, print the value + when the -v option is used + +Tue Jul 6 10:11:56 1993 Steve Chamberlain (sac@phydeaux.cygnus.com) + + * Makefile.in: Install correctly. + +Thu Jun 24 14:43:22 1993 David J. Mackenzie (djm@thepub.cygnus.com) + + * gprof.c (main): Get whoami from argv, instead of hardcoding. + Use it in usage message. Split usage message to fit in 80 cols. + +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 + new structures. + +Tue Jun 15 23:09:17 1993 Ken Raeburn (raeburn@cambridge.cygnus.com) + + * Makefile.in (.c.o): Look in ../include, not ../bfd, for bfd.h. + +Mon Jun 14 16:22:59 1993 david d `zoo' zuhn (zoo at rtl.cygnus.com) + + * Makefile.in: remove parentdir support + +Mon Jun 7 12:56:17 1993 Per Bothner (bothner@rtl.cygnus.com) + + * Makefile.in (INCLUDES): Add -I../bfd for sysdep.h and bfd.h. + * configure.in: No longer need to configure to get sysdep.h. + +Tue May 18 21:44:11 1993 david d `zoo' zuhn (zoo at cirdan.cygnus.com) + + * Makefile.in (install): should not depend on install-info + +Mon Apr 26 12:37:46 1993 Ian Lance Taylor (ian@cygnus.com) + + * gprof.h: Include ansidecl.h before sysdep.h. Undefine hz. + +Tue Apr 13 16:14:03 1993 Per Bothner (bothner@cygnus.com) + + * M Makefile.in: Add -g to CFLAGS. + Ads LDFLAGS and use in place of CFLAGS where appropriate. + * configure.in: Make a sysdep.hlink in the same way other + bfd-based directories do. + * gprof.h (UNIT): Replace non-standard 'u_short' by 'unsigned + short'. + * gprof.h: #include sysdep.h instead of a bunch of stuff. + * gprof.c (main): Fix typo gproff->gprof. + +Thu Mar 25 19:00:37 1993 david d `zoo' zuhn (zoo at cirdan.cygnus.com) + + * gprof.texi: add INFO-DIR-ENTRY + +Tue Mar 23 00:03:11 1993 david d `zoo' zuhn (zoo at cirdan.cygnus.com) + + * Makefile.in: add installcheck target + +Sat Feb 27 18:17:10 1993 Per Bothner (bothner@rtl.cygnus.com) + + * gprof.c (funcsymbol): Invert test for aflag. + +Thu Feb 25 16:01:50 1993 Per Bothner (bothner@rtl.cygnus.com) + + * printgprof (xmalloc, xrealloc): Cast results of malloc + and realloc to PTR. + +Wed Feb 3 13:55:33 1993 Jeffrey Osier (jeffrey@fowanton.cygnus.com) + + * Makefile.in: created info, install-info, dvi + +Wed Jan 6 00:58:09 1993 david d `zoo' zuhn (zoo at cirdan.cygnus.com) + + * Makefile.in: fix install rule for $(PROG) + +Fri Oct 9 11:25:41 1992 Mark Eichin (eichin@cygnus.com) + + * gprof.1: updated SYNOPSIS to match actual behavior. + +Mon Oct 5 17:50:16 1992 Per Bothner (bothner@cygnus.com) + + * gen-c-prog.awk: New awk script, lightly changed from + previously deleted make-c-prog.awk. Converts a text file + to a c function that prints that text. + * flat_bl.m, fsf_callg_bl.m, bsd_callg_bl.m: New files. + Inputs to gen-c-prog.awk, containing text describing + gprof output. + * blurbs.c: Removed. Use *_bl.c instead. + * Makefile.in: Use gen-cprog.awk to generate *_bl.c files + from *_bl.m files. Also, improve *clean rules. + * printgprof.c (printgprof): Usw new function names from *_bl.c. + + +Sun Aug 30 19:54:53 1992 Per Bothner (bothner@rtl.cygnus.com) + + * gprof.h, gprof.c, printfgprof.c: Add support for two + output styles: The default is similar to the old FSF gprof, + while -T sets the variable bsd_style_output, which causes + output matching Berkeley's gprof. The biggest differences + are that with the FSF style output, the flat profile comes + before the call graph; numbers come before explanations; + and there is less gratuitous white space. + * gprof.h, gprof.c, printfgprof.c: New discard_underscores + variable causes discarding of initial underscores when + printing symbol names. It is set unless there is a "main" + symbol (without an underscore). + * printfgprof.c: New function printnameonly(), called + by printname(). It handles stripping of initial '_', + as well as C++ name-demangling. + * gprof.callg, gprof.flat, make-c-prog.awk: Removed. + It is just as convenient to edit blurbs.c directly. + * Makefile.in: Removed rule for making blurbs.c. + * blurbs.c: This is now a true source file (as opposed + to being generated from gprof.callg and gprof.flat). + Change style to use one long string literal, instead of + one literal per output line. Add FSF-style blurb for call graph. + +Wed Aug 19 14:36:39 1992 Ian Lance Taylor (ian@cygnus.com) + + * Makefile.in: always create installation directories. + +Wed Aug 12 15:14:14 1992 Mark Eichin (eichin@cygnus.com) + + * Makefile.in: change ${MACHINE} to $(MACHINE). + +Sun Jul 19 17:34:01 1992 david d `zoo' zuhn (zoo at cirdan.cygnus.com) + + * Makefile.in: removed installation of the now useless + call.{flag,callg} files. + + * gprof.1: now uses the standard man macros instead of the new BSD + mandoc macros. + +Sun Jul 12 19:06:00 1992 John Gilmore (gnu at cygnus.com) + + * configure.in: Remove host section, expand target section. + * config/mt-{tahoe,vax}: Add, to match existing support files. + * config/tmake-*: Remove leftover crud. + + * blurbs.c: New file, created from gprof.flat and gprof.callg by + * make-c-prog.awk: which processes text files into C programs. + * printgprof.c (flatprofheader, gprofheader): Call new functions + to print blurbs. + (printblurb): Remove. + * Makefile.in: Infrastructure to build blurbs. + * pathnames.h: has been removed. Gprof now has no filename + dependencies in it. + * gprof.c: Lint. + +Sat Jul 11 18:07:21 1992 david d `zoo' zuhn (zoo at cirdan.cygnus.com) + + * Makefile.in: define man1dir and install the man page + +Fri Jul 10 21:14:08 1992 david d `zoo' zuhn (zoo@cygnus.com) + + * Makefile.in: added dummy info and install-info targets + +Thu Jun 4 11:34:02 1992 Mark Eichin (eichin at cygnus.com) + + * lookup.c: fixed fencepost in nllookup and added dbg_nllookup for + help in debugging the problem (with -DDEBUG) + * gprof.c: symbol values are now real values, don't add the vma + anymore. (done for solaris; should verify this on other platforms) + * ChangeLog: created. diff --git a/gnu/usr.bin/binutils/gprof/Makefile.in b/gnu/usr.bin/binutils/gprof/Makefile.in new file mode 100644 index 00000000000..d9a6f24fe77 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/Makefile.in @@ -0,0 +1,148 @@ +# @(#)Makefile 5.17 (Berkeley) 5/11/90 + +.SUFFIXES : .m + +VPATH = @srcdir@ +srcdir = @srcdir@ +prefix = @prefix@ + +exec_prefix = $(prefix) +program_transform_name = @program_transform_name@ + +bindir = $(exec_prefix)/bin +libdir = $(exec_prefix)/lib +tooldir = $(libdir) +mandir = $(prefix)/man +man1dir = $(mandir)/man1 + +infodir = $(prefix)/info +datadir = $(prefix)/lib + +SHELL = /bin/sh + +INSTALL = `cd $(srcdir); pwd`/../install.sh -c +INSTALL_PROGRAM = $(INSTALL) +INSTALL_DATA = $(INSTALL) +INSTALL_XFORM = $(INSTALL) -t='$(program_transform_name)' +INSTALL_XFORM1 = $(INSTALL_XFORM) -b=.1 +MAKEINFO = makeinfo +TEX = tex +TEXINDEX = texindex + +# this is the directory we look in to find Texinfo +texidir = $(srcdir)/../texinfo + +MY_TARGET= @MY_TARGET@ + +PROG= gprof +SRCS= $(MY_TARGET).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= $(MY_TARGET).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. +GEN_FILES = flat_bl.c bsd_callg_bl.c fsf_callg_bl.c + +CFLAGS=-g -DDEBUG +LDFLAGS= +.c.o: + $(CC) -c $(CFLAGS) -I. -I$(srcdir) -I../bfd -I$(srcdir)/../include -I$(srcdir)/../bfd -DMACHINE_H=\"$(MY_TARGET).h\" $(TCFLAGS) $(HCFLAGS) $< + +all: $(GEN_FILES) $(PROG) + +.PHONY: check installcheck info install-info +.SUFFIXES: .m + +.m.c: + awk -f $(srcdir)/gen-c-prog.awk > ./$*.c \ + FUNCTION=`(echo $*|sed -e 's/_bl//')`_blurb \ + FILE=$*.m $(srcdir)/$*.m + +diststuff: $(GEN_FILES) info + +gprof.info: gprof.texi + $(MAKEINFO) -o gprof.info $(srcdir)/gprof.texi + +gprof.dvi: gprof.texi + TEXINPUTS=${TEXIDIR}:$(srcdir):$$TEXINPUTS $(TEX) $(srcdir)/gprof.texi + $(TEXINDEX) gprof.?? + TEXINPUTS=${TEXIDIR}:$(srcdir):$$TEXINPUTS $(TEX) $(srcdir)/gprof.texi + +info: gprof.info + +dvi: gprof.dvi + +check: +installcheck: + +TAGS: + etags $(srcdir)/*.[ch] +tags: TAGS + +install-info: gprof.info + if [ -r gprof.info ]; then \ + dir=. ; \ + else \ + dir=$(srcdir) ; \ + fi ; \ + for i in `cd $$dir; echo gprof.info*` ; do \ + $(INSTALL_DATA) $$dir/$$i $(infodir)/$$i ; \ + done + +install: all + $(INSTALL_XFORM) gprof $(bindir)/gprof + $(INSTALL_XFORM1) $(srcdir)/gprof.1 $(man1dir)/gprof.1 + +gprof: $(OBJS) $(LIBS) + $(CC) -o $(PROG) $(LDFLAGS) $(OBJS) $(LIBS) + +mostlyclean: + -rm -f *.o core gprof nohup.out \ + gprof.cps gprof.fns gprof.log gprof.ps gprof.tps\ + gprof.aux gprof.dvi gprof.ky gprof.pg gprof.toc gprof.vr\ + gprof.cp gprof.fn gprof.kys gprof.pgs gprof.tp gprof.vrs +clean: mostlyclean + -rm -f gprof +distclean: clean + -rm -f config.cache config.log config.status Makefile +maintainer-clean realclean: distclean + -rm -f $(GEN_FILES) *.info* + +Makefile: Makefile.in config.status + $(SHELL) config.status + +config.status: configure + $(SHELL) config.status --recheck + +$(OBJS): ../bfd/bfd.h call_graph.h cg_arcs.h cg_print.h core.h gmon_io.h \ + gmon_out.h gprof.h hertz.h hist.h search_list.h source.h sym_ids.h \ + symtab.h utils.h $(srcdir)/../include/libiberty.h \ + $(srcdir)/../bfd/sysdep.h $(MY_TARGET).h + +# These get around a bug in Sun Make in SunOS 4.1.1 and Solaris 2 +$(MY_TARGET).o: $(MY_TARGET).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 +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/gnu/usr.bin/binutils/gprof/NOTES b/gnu/usr.bin/binutils/gprof/NOTES new file mode 100644 index 00000000000..511af302781 --- /dev/null +++ b/gnu/usr.bin/binutils/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 <no long form---for backwards compatibility only> +-E <no long form---for backwards compatibility only> +-f <no long form---for backwards compatibility only> +-F <no long form---for backwards compatibility only> +-k <no long form---for backwards compatibility only> +-b --brief +-dnum --debug[=num] + +-h --help + Prints a usage message. + +-O name --file-format=name + Selects the format of the profile data files. + Recognized formats are "auto", "bsd", "magic", + and "prof". The last one is not yet supported. + Format "auto" attempts to detect the file format + automatically (this is the default behavior). + It attempts to read the profile data files as + "magic" files and if this fails, falls back to + the "bsd" format. "bsd" forces gprof to read + the data files in the BSD format. "magic" forces + gprof to read the data files in the "magic" format. + +-T --traditional +-v --version + +** File Format Changes + +The old BSD-derived format used for profile data does not contain a +magic cookie that allows to check whether a data file really is a +gprof file. Furthermore, it does not provide a version number, thus +rendering changes to the file format almost impossible. GNU gprof +uses a new file format that provides these features. For backward +compatibility, GNU gprof continues to support the old BSD-derived +format, but not all features are supported with it. For example, +basic-block execution counts cannot be accommodated by the old file +format. + +The new file format is defined in header file \file{gmon_out.h}. It +consists of a header containing the magic cookie and a version number, +as well as some spare bytes available for future extensions. All data +in a profile data file is in the native format of the host on which +the profile was collected. GNU gprof adapts automatically to the +byte-order in use. + +In the new file format, the header is followed by a sequence of +records. Currently, there are three different record types: histogram +records, call-graph arc records, and basic-block execution count +records. Each file can contain any number of each record type. When +reading a file, GNU gprof will ensure records of the same type are +compatible with each other and compute the union of all records. For +example, for basic-block execution counts, the union is simply the sum +of all execution counts for each basic-block. + +*** Histogram Records + +Histogram records consist of a header that is followed by an array of +bins. The header contains the text-segment range that the histogram +spans, the size of the histogram in bytes (unlike in the old BSD +format, this does not include the size of the header), the rate of the +profiling clock, and the physical dimension that the bin counts +represent after being scaled by the profiling clock rate. The +physical dimension is specified in two parts: a long name of up to 15 +characters and a single character abbreviation. For example, a +histogram representing real-time would specify the long name as +"seconds" and the abbreviation as "s". This feature is useful for +architectures that support performance monitor hardware (which, +fortunately, is becoming increasingly common). For example, under DEC +OSF/1, the "uprofile" command can be used to produce a histogram of, +say, instruction cache misses. In this case, the dimension in the +histogram header could be set to "i-cache misses" and the abbreviation +could be set to "1" (because it is simply a count, not a physical +dimension). Also, the profiling rate would have to be set to 1 in +this case. + +Histogram bins are 16-bit numbers and each bin represent an equal +amount of text-space. For example, if the text-segment is one +thousand bytes long and if there are ten bins in the histogram, each +bin represents one hundred bytes. + + +*** Call-Graph Records + +Call-graph records have a format that is identical to the one used in +the BSD-derived file format. It consists of an arc in the call graph +and a count indicating the number of times the arc was traversed +during program execution. Arcs are specified by a pair of addresses: +the first must be within caller's function and the second must be +within the callee's function. When performing profiling at the +function level, these addresses can point anywhere within the +respective function. However, when profiling at the line-level, it is +better if the addresses are as close to the call-site/entry-point as +possible. This will ensure that the line-level call-graph is able to +identify exactly which line of source code performed calls to a +function. + +*** Basic-Block Execution Count Records + +Basic-block execution count records consist of a header followed by a +sequence of address/count pairs. The header simply specifies the +length of the sequence. In an address/count pair, the address +identifies a basic-block and the count specifies the number of times +that basic-block was executed. Any address within the basic-address can +be used. + +IMPLEMENTATION NOTE: gcc -a can be used to instrument a program to +record basic-block execution counts. However, the __bb_exit_func() +that is currently present in libgcc2.c does not generate a gmon.out +file in a suiteable format. This should be fixed for future releases +of gcc. In the meantime, contact davidm@cs.arizona.edu for a version +of __bb_exit_func() to is appropriate. diff --git a/gnu/usr.bin/binutils/gprof/TEST b/gnu/usr.bin/binutils/gprof/TEST new file mode 100644 index 00000000000..78a90300cae --- /dev/null +++ b/gnu/usr.bin/binutils/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 diff --git a/gnu/usr.bin/binutils/gprof/TODO b/gnu/usr.bin/binutils/gprof/TODO new file mode 100644 index 00000000000..30a3cb25c02 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/TODO @@ -0,0 +1,72 @@ +Thu Feb 9 16:48:04 1995 + +- documentation +- optimize bfd_find_nearest_line_num() (or replace by different interface) +- gmon_io.c cannot deal with target architecture that have a pointer size + that is different from the host architectures pointer size---fix this + (gmon_out.h, and gmon_io.c) +- 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/gnu/usr.bin/binutils/gprof/__bb_exit_func.c b/gnu/usr.bin/binutils/gprof/__bb_exit_func.c new file mode 100644 index 00000000000..512056ed4af --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/__bb_exit_func.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1994 David Mosberger-Tang. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or (at + * your option) any later version. + * + * __bb_exit_func() dumps all the basic-block statistics linked into + * the bb_head chain to .d files. + */ +#include <stdio.h> +#include <strings.h> +#include "bfd.h" +#include "gmon_out.h" + +/* structure emitted by -a */ +struct bb { + long zero_word; + const char *filename; + long *counts; + long ncounts; + struct bb *next; + const unsigned long *addresses; +}; + +struct bb *__bb_head = (struct bb *)0; + + +void +__bb_exit_func (void) +{ + const int version = GMON_VERSION; + struct gmon_hdr ghdr; + struct bb *ptr; + FILE *fp; + /* + * GEN_GMON_CNT_FILE should be defined on systems with mcleanup() + * functions that do not write basic-block to gmon.out. In such + * cases profiling with "-pg -a" would result in a gmon.out file + * without basic-block info (because the file written here would + * be overwritten. Thus, a separate file is generated instead. + * The two files can easily be combined by specifying them + * on gprof's command line (and possibly generating a gmon.sum + * file with "gprof -s"). + */ +#ifndef GEN_GMON_CNT_FILE +# define OUT_NAME "gmon.out" +#else +# define OUT_NAME "gmon.cnt" +#endif + fp = fopen(OUT_NAME, "wb"); + if (!fp) { + perror(OUT_NAME); + return; + } /* if */ + bcopy(GMON_MAGIC, &ghdr.cookie[0], 4); + bcopy(&version, &ghdr.version, sizeof(version)); + fwrite(&ghdr, sizeof(ghdr), 1, fp); + + for (ptr = __bb_head; ptr != 0; ptr = ptr->next) { + u_int ncounts = ptr->ncounts; + u_char tag; + u_int i; + + tag = GMON_TAG_BB_COUNT; + fwrite(&tag, sizeof(tag), 1, fp); + fwrite(&ncounts, sizeof(ncounts), 1, fp); + + for (i = 0; i < ncounts; ++i) { + fwrite(&ptr->addresses[i], sizeof(ptr->addresses[0]), 1, fp); + fwrite(&ptr->counts[i], sizeof(ptr->counts[0]), 1, fp); + } /* for */ + } /* for */ + fclose (fp); +} /* __bb_exit_func */ + + /*** end of __bb_exit_func.c ***/ diff --git a/gnu/usr.bin/binutils/gprof/alpha.c b/gnu/usr.bin/binutils/gprof/alpha.c new file mode 100644 index 00000000000..e99fee53e99 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/alpha.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include "gprof.h" +#include "cg_arcs.h" +#include "core.h" +#include "hist.h" +#include "symtab.h" + +/* + * Opcodes of the call instructions: + */ +#define OP_Jxx 0x1a +#define OP_BSR 0x34 + +#define Jxx_FUNC_JMP 0 +#define Jxx_FUNC_JSR 1 +#define Jxx_FUNC_RET 2 +#define Jxx_FUNC_JSR_COROUTINE 3 + +typedef union + { + struct + { + unsigned other:26; + unsigned op_code:6; + } + a; /* any format */ + struct + { + signed disp:21; + unsigned ra:5; + unsigned op_code:6; + } + b; /* branch format */ + struct + { + signed hint:14; + unsigned func:2; + unsigned rb:5; + unsigned ra:5; + unsigned op_code:6; + } + j; /* jump format */ + } +Instruction; + +static Sym indirect_child; + + +/* + * On the Alpha we can only detect PC relative calls, which are + * usually generated for calls to functions within the same + * object file only. This is still better than nothing, however. + * (In particular it should be possible to find functions that + * potentially call integer division routines, for example.) + */ +void +find_call (parent, p_lowpc, p_highpc) + Sym *parent; + bfd_vma p_lowpc; + bfd_vma p_highpc; +{ + static bfd_vma delta = 0; + bfd_vma dest_pc; + Instruction *pc; + Sym *child; + + if (!delta) + { + delta = (bfd_vma) core_text_space - core_text_sect->vma; + + sym_init (&indirect_child); + indirect_child.name = "<indirect child>"; + indirect_child.cg.prop.fract = 1.0; + indirect_child.cg.cyc.head = &indirect_child; + } + + if (!core_text_space) + { + return; + } + if (p_lowpc < s_lowpc) + { + p_lowpc = s_lowpc; + } + if (p_highpc > s_highpc) + { + p_highpc = s_highpc; + } + DBG (CALLDEBUG, printf ("[find_call] %s: 0x%lx to 0x%lx\n", + parent->name, p_lowpc, p_highpc)); + for (pc = (Instruction *) (p_lowpc + delta); + pc < (Instruction *) (p_highpc + delta); + ++pc) + { + switch (pc->a.op_code) + { + case OP_Jxx: + /* + * There is no simple and reliable way to determine the + * target of a jsr (the hint bits help, but there aren't + * enough bits to get a satisfactory hit rate). Instead, + * for any indirect jump we simply add an arc from PARENT + * to INDIRECT_CHILD---that way the user it at least able + * to see that there are other calls as well. + */ + if (pc->j.func == Jxx_FUNC_JSR + || pc->j.func == Jxx_FUNC_JSR_COROUTINE) + { + DBG (CALLDEBUG, + printf ("[find_call] 0x%lx: jsr%s <indirect_child>\n", + (bfd_vma) pc - delta, + pc->j.func == Jxx_FUNC_JSR ? "" : "_coroutine")); + arc_add (parent, &indirect_child, 0); + } + 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; + } + } + /* + * Something funny going on. + */ + DBG (CALLDEBUG, printf ("\tbut it's a botch\n")); + break; + + default: + break; + } + } +} diff --git a/gnu/usr.bin/binutils/gprof/alpha.h b/gnu/usr.bin/binutils/gprof/alpha.h new file mode 100644 index 00000000000..9eb92bc2620 --- /dev/null +++ b/gnu/usr.bin/binutils/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/gnu/usr.bin/binutils/gprof/basic_blocks.c b/gnu/usr.bin/binutils/gprof/basic_blocks.c new file mode 100644 index 00000000000..25028036340 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/basic_blocks.c @@ -0,0 +1,512 @@ +/* + * Basic-block level related code: reading/writing of basic-block info + * to/from gmon.out; computing and formatting of basic-block related + * statistics. + */ +#include <stdio.h> +#include <unistd.h> +#include "basic_blocks.h" +#include "core.h" +#include "gmon_io.h" +#include "gmon_out.h" +#include "gprof.h" +#include "libiberty.h" +#include "source.h" +#include "sym_ids.h" + + +/* + * Default option values: + */ +bool bb_annotate_all_lines = FALSE; +int bb_min_calls = 1; +int bb_table_length = 10; + +/* + * Variables used to compute annotated source listing stats: + */ +static long num_executable_lines; +static long num_lines_executed; + + +/* + * Helper for sorting. Compares two basic-blocks and returns result + * such that sorting will be increasing according to filename, line + * number, and basic-block address (in that order). + */ +static int +DEFUN (cmp_bb, (lp, rp), const void *lp AND const void *rp) +{ + int r; + const Sym *left = *(const Sym **) lp; + const Sym *right = *(const Sym **) rp; + + if (left->file && right->file) + { + r = strcmp (left->file->name, right->file->name); + if (r) + { + return r; + } + + if (left->line_num != right->line_num) + { + return left->line_num - right->line_num; + } + } + + if (left->addr < right->addr) + { + return -1; + } + else if (left->addr > right->addr) + { + return 1; + } + else + { + return 0; + } +} + + +/* + * 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 (right->ncalls != left->ncalls) + { + return right->ncalls - left->ncalls; + } + + return left->line_num - right->line_num; +} + + +/* + * Skip over variable length string. + */ +static void +DEFUN (fskip_string, (fp), FILE * fp) +{ + int ch; + + while ((ch = fgetc (fp)) != EOF) + { + if (ch == '\0') + { + break; + } + } +} + + +/* + * 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); + } + + nblocks = bfd_get_32 (core_bfd, (bfd_byte *) & nblocks); + if (gmon_file_version == 0) + { + fskip_string (ifp); + } + + 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); + } + } + else + { + if (fread (&addr, sizeof (addr), 1, ifp) != 1 + || fread (&ncalls, sizeof (ncalls), 1, ifp) != 1) + { + perror (filename); + done (1); + } + } + + /* + * 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); + } + } + } + return; +} + + +/* + * 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; + } + } + + /* 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); + } + + /* write counts: */ + for (sym = symtab.base; sym < symtab.limit; ++sym) + { + if (sym->ncalls == 0) + { + continue; + } + + 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); + } + } +} + + +/* + * Output basic-block statistics in a format that is easily parseable. + * Current the format is: + * + * <filename>:<line-number>: (<function-name>:<bb-addr): <ncalls> + */ +void +DEFUN_VOID (print_exec_counts) +{ + Sym **sorted_bbs, *sym; + int i, len; + + if (first_output) + { + first_output = FALSE; + } + else + { + printf ("\f\n"); + } + + /* 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; + } + } + qsort (sorted_bbs, len, sizeof (sorted_bbs[0]), cmp_bb); + + /* output basic-blocks: */ + + for (i = 0; i < len; ++i) + { + sym = sorted_bbs[i]; + printf ("%s:%d: (%s:0x%lx) %d executions\n", + sym->file ? sym->file->name : "<unknown>", sym->line_num, + sym->name, sym->addr, sym->ncalls); + } + free (sorted_bbs); +} + + +/* + * 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; + } + + b = 0; + if (line_num <= sf->num_lines) + { + b = sf->line[line_num - 1]; + } + if (!b) + { + cnt = -1; + } + else + { + ++num_executable_lines; + cnt = b->ncalls; + } + if (cnt > 0) + { + ++num_lines_executed; + } + if (cnt < 0 && bb_annotate_all_lines) + { + cnt = last_count; + } + + if (cnt < 0) + { + strcpy (buf, "\t\t"); + } + else if (cnt < bb_min_calls) + { + strcpy (buf, " ##### -> "); + } + else + { + sprintf (buf, "%12ld -> ", cnt); + } + last_count = cnt; +} + + +/* + * Annotate the files named in SOURCE_FILES with basic-block statistics + * (execution counts). After each source files, a few statistics + * 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; + } + } + + /* 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])); + } + } + + /* 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; + } + } + } + + /* 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; + } + + num_executable_lines = num_lines_executed = 0; + ofp = annotate_source (sf, 16, annotate_with_count, sf); + if (!ofp) + { + continue; + } + + 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; + } + for (i = 0; i < table_len; ++i) + { + sym = sf->line[i]; + if (!sym || sym->ncalls <= 0) + { + break; + } + fprintf (ofp, "%9d %10d\n", sym->line_num, sym->ncalls); + } + } + + 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); + } + } +} diff --git a/gnu/usr.bin/binutils/gprof/basic_blocks.h b/gnu/usr.bin/binutils/gprof/basic_blocks.h new file mode 100644 index 00000000000..daba408eb67 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/basic_blocks.h @@ -0,0 +1,23 @@ +#ifndef basic_blocks_h +#define basic_blocks_h + +#include <stdio.h> +#include "gprof.h" +#include "source.h" +#include "symtab.h" + +/* + * Options: + */ +extern bool bb_annotate_all_lines; /* force annotation of all lines? */ +extern int bb_table_length; /* length of most-used bb table */ +extern int bb_min_calls; /* minimum execution count */ + +extern void bb_read_rec PARAMS ((FILE * ifp, const char *filename)); +extern void bb_write_blocks PARAMS ((FILE * ofp, const char *filename)); +extern void bb_create_syms PARAMS ((void)); + +extern void print_annotated_source PARAMS ((void)); +extern void print_exec_counts PARAMS ((void)); + +#endif /* basic_blocks_h */ diff --git a/gnu/usr.bin/binutils/gprof/bsd_callg_bl.c b/gnu/usr.bin/binutils/gprof/bsd_callg_bl.c new file mode 100644 index 00000000000..5be63b9a24c --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/bsd_callg_bl.c @@ -0,0 +1,118 @@ +/* ==> Do not modify this file!! It is created automatically + from bsd_callg_bl.m using the gen-c-prog.awk script. <== */ + +#include <stdio.h> + +void +bsd_callg_blurb (file) + FILE *file; +{ + fputs ("\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs ("call graph profile:\n", file); + fputs (" The sum of self and descendents is the major sort\n", file); + fputs (" for this listing.\n", file); + fputs ("\n", file); + fputs (" function entries:\n", file); + fputs ("\n", file); + fputs ("index the index of the function in the call graph\n", file); + fputs (" listing, as an aid to locating it (see below).\n", file); + fputs ("\n", file); + fputs ("%time the percentage of the total time of the program\n", file); + fputs (" accounted for by this function and its\n", file); + fputs (" descendents.\n", file); + fputs ("\n", file); + fputs ("self the number of seconds spent in this function\n", file); + fputs (" itself.\n", file); + fputs ("\n", file); + fputs ("descendents\n", file); + fputs (" the number of seconds spent in the descendents of\n", file); + fputs (" this function on behalf of this function.\n", file); + fputs ("\n", file); + fputs ("called the number of times this function is called (other\n", file); + fputs (" than recursive calls).\n", file); + fputs ("\n", file); + fputs ("self the number of times this function calls itself\n", file); + fputs (" recursively.\n", file); + fputs ("\n", file); + fputs ("name the name of the function, with an indication of\n", file); + fputs (" its membership in a cycle, if any.\n", file); + fputs ("\n", file); + fputs ("index the index of the function in the call graph\n", file); + fputs (" listing, as an aid to locating it.\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs (" parent listings:\n", file); + fputs ("\n", file); + fputs ("self* the number of seconds of this function's self time\n", file); + fputs (" which is due to calls from this parent.\n", file); + fputs ("\n", file); + fputs ("descendents*\n", file); + fputs (" the number of seconds of this function's\n", file); + fputs (" descendent time which is due to calls from this\n", file); + fputs (" parent.\n", file); + fputs ("\n", file); + fputs ("called** the number of times this function is called by\n", file); + fputs (" this parent. This is the numerator of the\n", file); + fputs (" fraction which divides up the function's time to\n", file); + fputs (" its parents.\n", file); + fputs ("\n", file); + fputs ("total* the number of times this function was called by\n", file); + fputs (" all of its parents. This is the denominator of\n", file); + fputs (" the propagation fraction.\n", file); + fputs ("\n", file); + fputs ("parents the name of this parent, with an indication of the\n", file); + fputs (" parent's membership in a cycle, if any.\n", file); + fputs ("\n", file); + fputs ("index the index of this parent in the call graph\n", file); + fputs (" listing, as an aid in locating it.\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs (" children listings:\n", file); + fputs ("\n", file); + fputs ("self* the number of seconds of this child's self time\n", file); + fputs (" which is due to being called by this function.\n", file); + fputs ("\n", file); + fputs ("descendent*\n", file); + fputs (" the number of seconds of this child's descendent's\n", file); + fputs (" time which is due to being called by this\n", file); + fputs (" function.\n", file); + fputs ("\n", file); + fputs ("called** the number of times this child is called by this\n", file); + fputs (" function. This is the numerator of the\n", file); + fputs (" propagation fraction for this child.\n", file); + fputs ("\n", file); + fputs ("total* the number of times this child is called by all\n", file); + fputs (" functions. This is the denominator of the\n", file); + fputs (" propagation fraction.\n", file); + fputs ("\n", file); + fputs ("children the name of this child, and an indication of its\n", file); + fputs (" membership in a cycle, if any.\n", file); + fputs ("\n", file); + fputs ("index the index of this child in the call graph listing,\n", file); + fputs (" as an aid to locating it.\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs (" * these fields are omitted for parents (or\n", file); + fputs (" children) in the same cycle as the function. If\n", file); + fputs (" the function (or child) is a member of a cycle,\n", file); + fputs (" the propagated times and propagation denominator\n", file); + fputs (" represent the self time and descendent time of the\n", file); + fputs (" cycle as a whole.\n", file); + fputs ("\n", file); + fputs (" ** static-only parents and children are indicated\n", file); + fputs (" by a call count of 0.\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs (" cycle listings:\n", file); + fputs (" the cycle as a whole is listed with the same\n", file); + fputs (" fields as a function entry. Below it are listed\n", file); + fputs (" the members of the cycle, and their contributions\n", file); + fputs (" to the time and call counts of the cycle.\n", file); + fputs ("\n", file); +} diff --git a/gnu/usr.bin/binutils/gprof/bsd_callg_bl.m b/gnu/usr.bin/binutils/gprof/bsd_callg_bl.m new file mode 100644 index 00000000000..533c96ca439 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/bsd_callg_bl.m @@ -0,0 +1,108 @@ + + + +call graph profile: + The sum of self and descendents is the major sort + for this listing. + + function entries: + +index the index of the function in the call graph + listing, as an aid to locating it (see below). + +%time the percentage of the total time of the program + accounted for by this function and its + descendents. + +self the number of seconds spent in this function + itself. + +descendents + the number of seconds spent in the descendents of + this function on behalf of this function. + +called the number of times this function is called (other + than recursive calls). + +self the number of times this function calls itself + recursively. + +name the name of the function, with an indication of + its membership in a cycle, if any. + +index the index of the function in the call graph + listing, as an aid to locating it. + + + + parent listings: + +self* the number of seconds of this function's self time + which is due to calls from this parent. + +descendents* + the number of seconds of this function's + descendent time which is due to calls from this + parent. + +called** the number of times this function is called by + this parent. This is the numerator of the + fraction which divides up the function's time to + its parents. + +total* the number of times this function was called by + all of its parents. This is the denominator of + the propagation fraction. + +parents the name of this parent, with an indication of the + parent's membership in a cycle, if any. + +index the index of this parent in the call graph + listing, as an aid in locating it. + + + + children listings: + +self* the number of seconds of this child's self time + which is due to being called by this function. + +descendent* + the number of seconds of this child's descendent's + time which is due to being called by this + function. + +called** the number of times this child is called by this + function. This is the numerator of the + propagation fraction for this child. + +total* the number of times this child is called by all + functions. This is the denominator of the + propagation fraction. + +children the name of this child, and an indication of its + membership in a cycle, if any. + +index the index of this child in the call graph listing, + as an aid to locating it. + + + + * these fields are omitted for parents (or + children) in the same cycle as the function. If + the function (or child) is a member of a cycle, + the propagated times and propagation denominator + represent the self time and descendent time of the + cycle as a whole. + + ** static-only parents and children are indicated + by a call count of 0. + + + + cycle listings: + the cycle as a whole is listed with the same + fields as a function entry. Below it are listed + the members of the cycle, and their contributions + to the time and call counts of the cycle. + diff --git a/gnu/usr.bin/binutils/gprof/call_graph.c b/gnu/usr.bin/binutils/gprof/call_graph.c new file mode 100644 index 00000000000..a61dd837d0b --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/call_graph.c @@ -0,0 +1,97 @@ +#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); + } +} + + +/* + * 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); + } + 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); +} + + +/* + * 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); + } + DBG (SAMPLEDEBUG, + printf ("[cg_write_arcs] frompc 0x%lx selfpc 0x%lx count %d\n", + arc->parent->addr, arc->child->addr, arc->count)); + } + } +} diff --git a/gnu/usr.bin/binutils/gprof/call_graph.h b/gnu/usr.bin/binutils/gprof/call_graph.h new file mode 100644 index 00000000000..6e9413ad0a6 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/call_graph.h @@ -0,0 +1,12 @@ +#ifndef call_graph_h +#define call_graph_h + +#include <stdio.h> +#include "gprof.h" +#include "symtab.h" + +extern void cg_tally PARAMS ((bfd_vma from_pc, bfd_vma self_pc, int count)); +extern void cg_read_rec PARAMS ((FILE * ifp, const char *filename)); +extern void cg_write_arcs PARAMS ((FILE * ofp, const char *filename)); + +#endif /* call_graph_h */ diff --git a/gnu/usr.bin/binutils/gprof/cg_arcs.c b/gnu/usr.bin/binutils/gprof/cg_arcs.c new file mode 100644 index 00000000000..8b6184b6bcc --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/cg_arcs.c @@ -0,0 +1,651 @@ +/* + * 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; + } + 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; + } + } + return 0; +} + + +/* + * 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; + } + 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; +} + + +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; +} + + +static void +DEFUN (propagate_time, (parent), Sym * parent) +{ + Arc *arc; + Sym *child; + double share, prop_share; + + if (parent->cg.prop.fract == 0.0) + { + return; + } + + /* 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 (child->cg.cyc.head != child) + { + if (parent->cg.cyc.num == child->cg.cyc.num) + { + continue; + } + if (parent->cg.top_order <= child->cg.top_order) + { + fprintf (stderr, "[propagate] toporder botches\n"); + } + child = child->cg.cyc.head; + } + else + { + if (parent->cg.top_order <= child->cg.top_order) + { + fprintf (stderr, "[propagate] toporder botches\n"); + continue; + } + } + if (child->ncalls == 0) + { + continue; + } + + /* 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; + } + 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)); + } +} + + +/* + * 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; + } + cyc->hist.time += member->hist.time; + } + cyc->cg.prop.self = cyc->cg.prop.fract * cyc->hist.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; + } + } + + /* + * 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; + } + ++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; + } + + /* + * 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 (arc->parent->cg.cyc.num == num) + { + cyc->cg.self_calls += arc->count; + } + else + { + cyc->ncalls += arc->count; + } + } + } + } +} + + +/* + * 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; + } + 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)); + } + } + } + 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; + } + 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)); + } + } + } + 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; + } + } +} + + +/* + * 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); + } + 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; + } + } + 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 (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; + } + } + 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; + } + } + 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)); + } +} + + +/* + * 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 (diff > 0.0) + { + return -1; + } + if (!left->name && left->cg.cyc.num != 0) + { + return -1; + } + if (!right->name && right->cg.cyc.num != 0) + { + return 1; + } + if (!left->name) + { + return -1; + } + if (!right->name) + { + return 1; + } + if (left->name[0] != '_' && right->name[0] == '_') + { + return -1; + } + if (left->name[0] == '_' && right->name[0] != '_') + { + return 1; + } + if (left->ncalls > right->ncalls) + { + return -1; + } + if (left->ncalls < right->ncalls) + { + return 1; + } + return strcmp (left->name, right->name); +} + + +/* + * 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; + } + 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); + } + } + /* + * 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); + } + } + + /* 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]; + } + 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"); + } + ); + /* + * 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]); + } + + 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]; + } + for (index = 1; index <= num_cycles; index++) + { + time_sorted_syms[symtab.len + index - 1] = &cycle_header[index]; + } + 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; + } + return time_sorted_syms; +} diff --git a/gnu/usr.bin/binutils/gprof/cg_arcs.h b/gnu/usr.bin/binutils/gprof/cg_arcs.h new file mode 100644 index 00000000000..25d5b616737 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/cg_arcs.h @@ -0,0 +1,33 @@ +#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/gnu/usr.bin/binutils/gprof/cg_dfn.c b/gnu/usr.bin/binutils/gprof/cg_dfn.c new file mode 100644 index 00000000000..c9e37ab29e5 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/cg_dfn.c @@ -0,0 +1,281 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include <stdio.h> +#include "gprof.h" +#include "cg_arcs.h" +#include "cg_dfn.h" +#include "symtab.h" +#include "utils.h" + +#define DFN_DEPTH 100 + +typedef struct + { + Sym *sym; + int cycle_top; + } +DFN_Stack; + +DFN_Stack dfn_stack[DFN_DEPTH]; +int dfn_depth = 0; +int dfn_counter = DFN_NAN; + + +/* + * Is CHILD already numbered? + */ +static bool +DEFUN (is_numbered, (child), Sym * child) +{ + return child->cg.top_order != DFN_NAN && child->cg.top_order != DFN_BUSY; +} + + +/* + * Is CHILD already busy? + */ +static bool +DEFUN (is_busy, (child), Sym * child) +{ + if (child->cg.top_order == DFN_NAN) + { + return FALSE; + } + return TRUE; +} + + +/* + * 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 (child->cg.cyc.head != child && child->cg.cyc.head == head) + { + break; + } + } + if (cycle_top <= 0) + { + fprintf (stderr, "[find_cycle] couldn't find head of cycle\n"); + done (1); + } +#ifdef DEBUG + if (debug_level & DFNDEBUG) + { + printf ("[find_cycle] dfn_depth %d cycle_top %d ", + dfn_depth, cycle_top); + if (head) + { + print_name (head); + } + else + { + printf ("<unknown>"); + } + printf ("\n"); + } +#endif + 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")); + } + /* + * 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")); + } + 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")); + } + } + else if (child->cg.cyc.head != head /* firewall */ ) + { + fprintf (stderr, "[find_cycle] glommed, but not to head\n"); + done (1); + } + } + } +} + + +/* + * 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); + } + 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")); +} + + +/* + * 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)); + } + } + else + { + DBG (DFNDEBUG, printf ("[post_visit]\t\tis part of a cycle\n")); + } + --dfn_depth; +} + + +/* + * 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 we're already busy, must be a cycle: + */ + if (is_busy (parent)) + { + find_cycle (parent); + return; + } + pre_visit (parent); + /* + * Recursively visit children: + */ + for (arc = parent->cg.children; arc; arc = arc->next_child) + { + cg_dfn (arc->child); + } + post_visit (parent); +} diff --git a/gnu/usr.bin/binutils/gprof/cg_dfn.h b/gnu/usr.bin/binutils/gprof/cg_dfn.h new file mode 100644 index 00000000000..4bd3030257c --- /dev/null +++ b/gnu/usr.bin/binutils/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/gnu/usr.bin/binutils/gprof/cg_print.c b/gnu/usr.bin/binutils/gprof/cg_print.c new file mode 100644 index 00000000000..460bc045c47 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/cg_print.c @@ -0,0 +1,656 @@ +#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 (!bsd_style_output) + { + if (print_descriptions) + { + printf ("\t\t Call graph (explanation follows)\n\n"); + } + else + { + printf ("\t\t\tCall graph\n\n"); + } + } + 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 (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"); + } +} + + +/* + * Print a cycle header. + */ +static void +DEFUN (print_cycle, (cyc), Sym * cyc) +{ + char buf[BUFSIZ]; + + sprintf (buf, "[%d]", cyc->cg.index); + printf (bsd_style_output + ? "%-6.6s %5.1f %7.2f %11.2f %7d" + : "%-6.6s %5.1f %7.2f %7.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", ""); + } + printf (" <cycle %d as a whole> [%d]\n", cyc->cg.cyc.num, cyc->cg.index); +} + + +/* + * 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 (left_time < right_time) + { + return LESSTHAN; + } + + if (left_calls > right_calls) + { + return GREATERTHAN; + } + if (left_calls < right_calls) + { + return LESSTHAN; + } + return EQUALTO; +} + + +/* + * 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; + } + } + doing->cg.cyc.next = prev->cg.cyc.next; + prev->cg.cyc.next = doing; + } +} + + +/* + * 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 (bsd_style_output + ? "%6.6s %5.5s %7.2f %11.2f %7d" + : "%6.6s %5.5s %7.2f %7.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", ""); + } + printf (" "); + print_name (member); + printf ("\n"); + } +} + + +/* + * 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 (right_parent == right_child) + { + return GREATERTHAN; /* right is a self call */ + } + + 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 (left->count > right->count) + { + return GREATERTHAN; + } + return EQUALTO; + } + else + { + /* right isn't a call within the cycle */ + return LESSTHAN; + } + } + 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 (left_time > right_time) + { + return GREATERTHAN; + } + if (left->count < right->count) + { + return LESSTHAN; + } + if (left->count > right->count) + { + return GREATERTHAN; + } + return EQUALTO; + } + } +} + + +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; + } + } + arc->next_parent = prev->next_parent; + prev->next_parent = arc; + } + + /* reattach sorted arcs to child: */ + child->cg.parents = sorted.next_parent; +} + + +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 (!child->cg.parents) + { + printf (bsd_style_output + ? "%6.6s %5.5s %7.7s %11.11s %7.7s %7.7s <spontaneous>\n" + : "%6.6s %5.5s %7.7s %7.7s %7.7s %7.7s <spontaneous>\n", + "", "", "", "", "", ""); + return; + } + 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"); + } + } +} + + +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; + } + } + arc->next_child = prev->next_child; + prev->next_child = arc; + } + + /* reattach sorted children to parent: */ + parent->cg.children = sorted.next_child; +} + + +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"); + } + } +} + + +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 ", ""); + } + } + else + { + printf (" %7.7s %7.7s ", "", ""); + } + print_name (np); + printf ("\n"); +} + + +/* + * 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); + } + + 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 (!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 (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); + } +} + + +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); +} + + +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; + } + name_sorted_syms[nnames++] = &symtab.base[index]; + } + 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]; + } + 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 (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 (' '); + } + 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; + } + } + printf (" (%s)", filename); + col += strlen (filename) + 3; + } + } + } + else + { + if (bsd_style_output) + { + printf ("%6.6s ", buf); + sprintf (buf, "<cycle %d>", sym->cg.cyc.num); + printf ("%-19.19s", buf); + } + else + { + col += strlen (buf); + for (; col < starting_col + 5; ++col) + putchar (' '); + printf (" %s ", buf); + sprintf (buf, "<cycle %d>", sym->cg.cyc.num); + printf ("%s", buf); + col += strlen (buf); + } + } + starting_col += column_width; + } + printf ("\n"); + } + free (name_sorted_syms); +} diff --git a/gnu/usr.bin/binutils/gprof/cg_print.h b/gnu/usr.bin/binutils/gprof/cg_print.h new file mode 100644 index 00000000000..13511c728a0 --- /dev/null +++ b/gnu/usr.bin/binutils/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/gnu/usr.bin/binutils/gprof/configure b/gnu/usr.bin/binutils/gprof/configure new file mode 100644 index 00000000000..2408cbd8f26 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/configure @@ -0,0 +1,805 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.4 +# Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE + +# Initialize some other variables. +subdirs= + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -build | --build | --buil | --bui | --bu | --b) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=* | --b=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=PREFIX install architecture-dependent files in PREFIX + [same as prefix] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +--enable and --with options recognized:$ac_help +EOF + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.4" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LANG+set}" = set; then LANG=C; export LANG; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=gprof.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5 2>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5 2>&5' + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +CC=${CC-cc} +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="cc" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if ${CC-cc} -E conftest.c 2>&5 | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 +if test $ac_cv_prog_gcc = yes; then + GCC=yes + if test "${CFLAGS+set}" != set; then + echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_gcc_g=yes +else + ac_cv_prog_gcc_g=no +fi +rm -f conftest* + +fi + echo "$ac_t""$ac_cv_prog_gcc_g" 1>&6 + if test $ac_cv_prog_gcc_g = yes; then + CFLAGS="-g -O" + else + CFLAGS="-O" + fi + fi +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-g" +fi + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + + +# Do some error checking and defaulting for the host and target type. +# The inputs are: +# configure --host=HOST --target=TARGET --build=BUILD NONOPT +# +# The rules are: +# 1. You are not allowed to specify --host, --target, and nonopt at the +# same time. +# 2. Host defaults to nonopt. +# 3. If nonopt is not specified, then host defaults to the current host, +# as determined by config.guess. +# 4. Target and build default to nonopt. +# 5. If nonopt is not specified, then target and build default to host. + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +case $host---$target---$nonopt in +NONE---*---* | *---NONE---* | *---*---NONE) ;; +*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;; +esac + + +# Make sure we can run config.sub. +if $ac_config_sub sun4 >/dev/null 2>&1; then : +else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; } +fi + +echo $ac_n "checking host system type""... $ac_c" 1>&6 + +host_alias=$host +case "$host_alias" in +NONE) + case $nonopt in + NONE) + if host_alias=`$ac_config_guess`; then : + else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; } + fi ;; + *) host_alias=$nonopt ;; + esac ;; +esac + +host=`$ac_config_sub $host_alias` +host_cpu=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\3/'` +echo "$ac_t""$host" 1>&6 + +echo $ac_n "checking target system type""... $ac_c" 1>&6 + +target_alias=$target +case "$target_alias" in +NONE) + case $nonopt in + NONE) target_alias=$host_alias ;; + *) target_alias=$nonopt ;; + esac ;; +esac + +target=`$ac_config_sub $target_alias` +target_cpu=`echo $target | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\1/'` +target_vendor=`echo $target | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\2/'` +target_os=`echo $target | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\3/'` +echo "$ac_t""$target" 1>&6 + +echo $ac_n "checking build system type""... $ac_c" 1>&6 + +build_alias=$build +case "$build_alias" in +NONE) + case $nonopt in + NONE) build_alias=$host_alias ;; + *) build_alias=$nonopt ;; + esac ;; +esac + +build=`$ac_config_sub $build_alias` +build_cpu=`echo $build | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\1/'` +build_vendor=`echo $build | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\2/'` +build_os=`echo $build | sed 's/^\(.*\)-\(.*\)-\(.*\)$/\3/'` +echo "$ac_t""$build" 1>&6 + +test "$host_alias" != "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +if test "$program_transform_name" = s,x,x,; then + program_transform_name= +else + # Double any \ or $. + echo 's,\\,\\\\,g; s,\$,$$,g' > conftestsed + program_transform_name="`echo $program_transform_name|sed -f conftestsed`" + rm -f conftestsed +fi +test "$program_prefix" != NONE && + program_transform_name="s,^,${program_prefix},; $program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" + +# sed with no file args requires a program. +test "$program_transform_name" = "" && program_transform_name="s,x,x," + + +case "${target}" in +alpha-*-*) MY_TARGET=alpha ;; +i[345]86-*-*) MY_TARGET=i386 ;; +sparc-*-*) MY_TARGET=sparc ;; +tahoe-*-*) MY_TARGET=tahoe ;; +vax-*-*) MY_TARGET=vax ;; +ns32k-*-*|*-pc532-*) MY_TARGET=ns532;; +*-*-*) MY_TARGET=dummy ;; +esac + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + sed -n "s/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=\${\1='\2'}/p" \ + >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) \(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS <<EOF +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.4" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir + +trap 'rm -fr `echo "Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g$/@g/; /@g$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g$/%g/' > conftest.subs <<\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@CC@%$CC%g +s%@host@%$host%g +s%@host_alias@%$host_alias%g +s%@host_cpu@%$host_cpu%g +s%@host_vendor@%$host_vendor%g +s%@host_os@%$host_os%g +s%@target@%$target%g +s%@target_alias@%$target_alias%g +s%@target_cpu@%$target_cpu%g +s%@target_vendor@%$target_vendor%g +s%@target_os@%$target_os%g +s%@build@%$build%g +s%@build_alias@%$build_alias%g +s%@build_cpu@%$build_cpu%g +s%@build_vendor@%$build_vendor%g +s%@build_os@%$build_os%g +s%@MY_TARGET@%$MY_TARGET%g + +CEOF +EOF +cat >> $CONFIG_STATUS <<EOF + +CONFIG_FILES=\${CONFIG_FILES-"Makefile"} +EOF +cat >> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%.*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust relative srcdir, etc. for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +" -f conftest.subs $ac_given_srcdir/$ac_file_in > $ac_file +fi; done +rm -f conftest.subs + + + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/gnu/usr.bin/binutils/gprof/configure.bat b/gnu/usr.bin/binutils/gprof/configure.bat new file mode 100644 index 00000000000..22ef37eff88 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/configure.bat @@ -0,0 +1,18 @@ +@echo off
+echo Configuring gprof for go32
+rem This batch file assumes a unix-type "sed" program
+
+echo # Makefile generated by "configure.bat"> Makefile
+
+if exist config.sed del config.sed
+
+echo "/^###$/ i\ ">>config.sed
+echo "MY_MACHINE=i386\ ">>config.sed
+echo "CC=gcc ">>config.sed
+
+echo # >> config.sed
+
+sed -e "s/^\"//" -e "s/\"$//" -e "s/[ ]*$//" config.sed > config2.sed
+sed -f config2.sed Makefile.in >> Makefile
+del config.sed
+del config2.sed
diff --git a/gnu/usr.bin/binutils/gprof/configure.in b/gnu/usr.bin/binutils/gprof/configure.in new file mode 100644 index 00000000000..7849a858962 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/configure.in @@ -0,0 +1,24 @@ +dnl Process this file with autoconf to produce a configure script. +AC_PREREQ(2.3)dnl +AC_INIT(gprof.c) + +CC=${CC-cc} +AC_PROG_CC + +AC_CANONICAL_SYSTEM +AC_ARG_PROGRAM + +case "${target}" in +alpha-*-*) MY_TARGET=alpha ;; +changequote(,)dnl +i[345]86-*-*) MY_TARGET=i386 ;; +changequote([,])dnl +sparc-*-*) MY_TARGET=sparc ;; +tahoe-*-*) MY_TARGET=tahoe ;; +vax-*-*) MY_TARGET=vax ;; +ns32k-*-*) MY_TARGET=ns532;; +*-*-*) MY_TARGET=dummy ;; +esac + +AC_SUBST(MY_TARGET) +AC_OUTPUT(Makefile) diff --git a/gnu/usr.bin/binutils/gprof/core.c b/gnu/usr.bin/binutils/gprof/core.c new file mode 100644 index 00000000000..19c6e590f99 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/core.c @@ -0,0 +1,508 @@ +#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 (!bfd_check_format (core_bfd, bfd_object)) + { + fprintf (stderr, "%s: %s: not in a.out format\n", whoami, a_out_name); + done (1); + } + + /* 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); + } + } + + /* 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); + } + + 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); + } +} + + +/* + * 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 (!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 (!core_text_space) + { + fprintf (stderr, "%s: can't do -c\n", whoami); + } +} + + +/* + * 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 (ignore_static_funcs && (sym->flags & BSF_LOCAL)) + { + DBG (AOUTDEBUG, printf ("[core_sym_class] %s: not a function\n", + sym->name)); + return 0; + } + + bfd_get_symbol_info (core_bfd, sym, &syminfo); + i = syminfo.type; + + if (i == 'T') + { + return i; /* it's a global symbol */ + } + + 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; + } + + /* do some more filtering on static function-names: */ + + if (ignore_static_funcs) + { + return 0; + } + /* + * 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; + } + + for (name = sym->name; *name; ++name) + { + if (*name == '.' || *name == '$') + { + return 0; + } + } + /* + * 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; + } + return 't'; /* it's a static text symbol */ +} + + +/* + * 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, (unsigned int *) &l) + && fname && func_name && l) + { + DBG (AOUTDEBUG, printf ("[get_src_info] 0x%lx -> %s:%d (%s)\n", + addr, fname, l, func_name)); + *filename = fname; + *name = func_name; + *line_num = l; + return TRUE; + } + else + { + DBG (AOUTDEBUG, printf ("[get_src_info] no info for 0x%lx (%s:%d,%s)\n", + (long) addr, fname ? fname : "<unknown>", l, + func_name ? func_name : "<unknown>")); + return FALSE; + } +} + + +/* + * 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; + } + ++symtab.len; + } + + if (symtab.len == 0) + { + fprintf (stderr, "%s: file `%s' has no symbols\n", whoami, a_out_name); + done (1); + } + + /* 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; + } + + 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; + } + } +#endif + + symtab.limit->is_func = TRUE; + symtab.limit->is_bb_head = TRUE; + if (class == 't') + { + symtab.limit->is_static = TRUE; + } + + 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; + } + + 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; + } + + /* create sentinels: */ + + sym_init (symtab.limit); + symtab.limit->name = "<locore>"; + symtab.limit->addr = 0; + symtab.limit->end_addr = min_vma - 1; + ++symtab.limit; + + sym_init (symtab.limit); + symtab.limit->name = "<hicore>"; + symtab.limit->addr = max_vma + 1; + symtab.limit->end_addr = ~0; + ++symtab.limit; + + symtab.len = symtab.limit - symtab.base; + symtab_finalize (&symtab); +} + + +/* + * 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; + } + + ++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; + } + prev_offset = offset; + + min_vma = MIN (vma, min_vma); + max_vma = MAX (vma, max_vma); + } + + 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; + } + + /* 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; + } + + DBG (AOUTDEBUG, printf ("[core_create_line_syms] %d %s 0x%lx\n", + ltab.len, ltab.limit->name, + ltab.limit->addr)); + ++ltab.limit; + } + + /* update sentinels: */ + + sentinel = sym_lookup (&symtab, 0); + if (strcmp (sentinel->name, "<locore>") == 0 + && min_vma <= sentinel->end_addr) + { + sentinel->end_addr = min_vma - 1; + } + + sentinel = sym_lookup (&symtab, ~0); + if (strcmp (sentinel->name, "<hicore>") == 0 && max_vma >= sentinel->addr) + { + sentinel->addr = max_vma + 1; + } + + /* 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); + } + + /* 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); + } + } + +} diff --git a/gnu/usr.bin/binutils/gprof/core.h b/gnu/usr.bin/binutils/gprof/core.h new file mode 100644 index 00000000000..81c79ad4559 --- /dev/null +++ b/gnu/usr.bin/binutils/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/gnu/usr.bin/binutils/gprof/dummy.c b/gnu/usr.bin/binutils/gprof/dummy.c new file mode 100644 index 00000000000..ca602dfa887 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/dummy.c @@ -0,0 +1,16 @@ +#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. + */ + +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 not supported on this machine architecture\n", + whoami); +} diff --git a/gnu/usr.bin/binutils/gprof/dummy.h b/gnu/usr.bin/binutils/gprof/dummy.h new file mode 100644 index 00000000000..4c37c15f072 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/dummy.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)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 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; + +#endif /* dummy_h */ diff --git a/gnu/usr.bin/binutils/gprof/flat_bl.c b/gnu/usr.bin/binutils/gprof/flat_bl.c new file mode 100644 index 00000000000..e02c209798c --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/flat_bl.c @@ -0,0 +1,37 @@ +/* ==> Do not modify this file!! It is created automatically + from flat_bl.m using the gen-c-prog.awk script. <== */ + +#include <stdio.h> + +void +flat_blurb (file) + FILE *file; +{ + fputs ("\n", file); + fputs (" % the percentage of the total running time of the\n", file); + fputs ("time program used by this function.\n", file); + fputs ("\n", file); + fputs ("cumulative a running sum of the number of seconds accounted\n", file); + fputs (" seconds for by this function and those listed above it.\n", file); + fputs ("\n", file); + fputs (" self the number of seconds accounted for by this\n", file); + fputs ("seconds function alone. This is the major sort for this\n", file); + fputs (" listing.\n", file); + fputs ("\n", file); + fputs ("calls the number of times this function was invoked, if\n", file); + fputs (" this function is profiled, else blank.\n", file); + fputs (" \n", file); + fputs (" self the average number of milliseconds spent in this\n", file); + fputs ("ms/call function per call, if this function is profiled,\n", file); + fputs (" else blank.\n", file); + fputs ("\n", file); + fputs (" total the average number of milliseconds spent in this\n", file); + fputs ("ms/call function and its descendents per call, if this \n", file); + fputs (" function is profiled, else blank.\n", file); + fputs ("\n", file); + fputs ("name the name of the function. This is the minor sort\n", file); + fputs (" for this listing. The index shows the location of\n", file); + fputs (" the function in the gprof listing. If the index is\n", file); + fputs (" in parenthesis it shows where it would appear in\n", file); + fputs (" the gprof listing if it were to be printed.\n", file); +} diff --git a/gnu/usr.bin/binutils/gprof/flat_bl.m b/gnu/usr.bin/binutils/gprof/flat_bl.m new file mode 100644 index 00000000000..db2871a1384 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/flat_bl.m @@ -0,0 +1,27 @@ + + % the percentage of the total running time of the +time program used by this function. + +cumulative a running sum of the number of seconds accounted + seconds for by this function and those listed above it. + + self the number of seconds accounted for by this +seconds function alone. This is the major sort for this + listing. + +calls the number of times this function was invoked, if + this function is profiled, else blank. + + self the average number of milliseconds spent in this +ms/call function per call, if this function is profiled, + else blank. + + total the average number of milliseconds spent in this +ms/call function and its descendents per call, if this + function is profiled, else blank. + +name the name of the function. This is the minor sort + for this listing. The index shows the location of + 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/gnu/usr.bin/binutils/gprof/fsf_callg_bl.c b/gnu/usr.bin/binutils/gprof/fsf_callg_bl.c new file mode 100644 index 00000000000..d8182b551b0 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/fsf_callg_bl.c @@ -0,0 +1,93 @@ +/* ==> Do not modify this file!! It is created automatically + from fsf_callg_bl.m using the gen-c-prog.awk script. <== */ + +#include <stdio.h> + +void +fsf_callg_blurb (file) + FILE *file; +{ + fputs ("\n", file); + fputs (" This table describes the call tree of the program, and was sorted by\n", file); + fputs (" the total amount of time spent in each function and its children.\n", file); + fputs ("\n", file); + fputs (" Each entry in this table consists of several lines. The line with the\n", file); + fputs (" index number at the left hand margin lists the current function.\n", file); + fputs (" The lines above it list the functions that called this function,\n", file); + fputs (" and the lines below it list the functions this one called.\n", file); + fputs (" This line lists:\n", file); + fputs (" index A unique number given to each element of the table.\n", file); + fputs (" Index numbers are sorted numerically.\n", file); + fputs (" The index number is printed next to every function name so\n", file); + fputs (" it is easier to look up where the function in the table.\n", file); + fputs ("\n", file); + fputs (" % time This is the percentage of the `total' time that was spent\n", file); + fputs (" in this function and its children. Note that due to\n", file); + fputs (" different viewpoints, functions excluded by options, etc,\n", file); + fputs (" these numbers will NOT add up to 100%.\n", file); + fputs ("\n", file); + fputs (" self This is the total amount of time spent in this function.\n", file); + fputs ("\n", file); + fputs (" children This is the total amount of time propagated into this\n", file); + fputs (" function by its children.\n", file); + fputs ("\n", file); + fputs (" called This is the number of times the function was called.\n", file); + fputs (" If the function called itself recursively, the number\n", file); + fputs (" only includes non-recursive calls, and is followed by\n", file); + fputs (" a `+' and the number of recursive calls.\n", file); + fputs ("\n", file); + fputs (" name The name of the current function. The index number is\n", file); + fputs (" printed after it. If the function is a member of a\n", file); + fputs (" cycle, the cycle number is printed between the\n", file); + fputs (" function's name and the index number.\n", file); + fputs ("\n", file); + fputs ("\n", file); + fputs (" For the function's parents, the fields have the following meanings:\n", file); + fputs ("\n", file); + fputs (" self This is the amount of time that was propagated directly\n", file); + fputs (" from the function into this parent.\n", file); + fputs ("\n", file); + fputs (" children This is the amount of time that was propagated from\n", file); + fputs (" the function's children into this parent.\n", file); + fputs ("\n", file); + fputs (" called This is the number of times this parent called the\n", file); + fputs (" function `/' the total number of times the function\n", file); + fputs (" was called. Recursive calls to the function are not\n", file); + fputs (" included in the number after the `/'.\n", file); + fputs ("\n", file); + fputs (" name This is the name of the parent. The parent's index\n", file); + fputs (" number is printed after it. If the parent is a\n", file); + fputs (" member of a cycle, the cycle number is printed between\n", file); + fputs (" the name and the index number.\n", file); + fputs ("\n", file); + fputs (" If the parents of the function cannot be determined, the word\n", file); + fputs (" `<spontaneous>' is printed in the `name' field, and all the other\n", file); + fputs (" fields are blank.\n", file); + fputs ("\n", file); + fputs (" For the function's children, the fields have the following meanings:\n", file); + fputs ("\n", file); + fputs (" self This is the amount of time that was propagated directly\n", file); + fputs (" from the child into the function.\n", file); + fputs ("\n", file); + fputs (" children This is the amount of time that was propagated from the\n", file); + fputs (" child's children to the function.\n", file); + fputs ("\n", file); + fputs (" called This is the number of times the function called\n", file); + fputs (" this child `/' the total number of times the child\n", file); + fputs (" was called. Recursive calls by the child are not\n", file); + fputs (" listed in the number after the `/'.\n", file); + fputs ("\n", file); + fputs (" name This is the name of the child. The child's index\n", file); + fputs (" number is printed after it. If the child is a\n", file); + fputs (" member of a cycle, the cycle number is printed\n", file); + fputs (" between the name and the index number.\n", file); + fputs ("\n", file); + fputs (" If there are any cycles (circles) in the call graph, there is an\n", file); + fputs (" entry for the cycle-as-a-whole. This entry shows who called the\n", file); + fputs (" cycle (as parents) and the members of the cycle (as children.)\n", file); + fputs (" The `+' recursive calls entry shows the number of function calls that\n", file); + fputs (" were internal to the cycle, and the calls entry for each member shows,\n", file); + fputs (" for that member, how many times it was called from other members of\n", file); + fputs (" the cycle.\n", file); + fputs ("\n", file); +} diff --git a/gnu/usr.bin/binutils/gprof/fsf_callg_bl.m b/gnu/usr.bin/binutils/gprof/fsf_callg_bl.m new file mode 100644 index 00000000000..7e16821ede2 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/fsf_callg_bl.m @@ -0,0 +1,83 @@ + + This table describes the call tree of the program, and was sorted by + the total amount of time spent in each function and its children. + + Each entry in this table consists of several lines. The line with the + index number at the left hand margin lists the current function. + The lines above it list the functions that called this function, + and the lines below it list the functions this one called. + This line lists: + index A unique number given to each element of the table. + Index numbers are sorted numerically. + The index number is printed next to every function name so + it is easier to look up where the function in the table. + + % time This is the percentage of the `total' time that was spent + in this function and its children. Note that due to + different viewpoints, functions excluded by options, etc, + these numbers will NOT add up to 100%. + + self This is the total amount of time spent in this function. + + children This is the total amount of time propagated into this + function by its children. + + called This is the number of times the function was called. + If the function called itself recursively, the number + only includes non-recursive calls, and is followed by + a `+' and the number of recursive calls. + + name The name of the current function. The index number is + printed after it. If the function is a member of a + cycle, the cycle number is printed between the + function's name and the index number. + + + For the function's parents, the fields have the following meanings: + + self This is the amount of time that was propagated directly + from the function into this parent. + + children This is the amount of time that was propagated from + the function's children into this parent. + + called This is the number of times this parent called the + function `/' the total number of times the function + was called. Recursive calls to the function are not + included in the number after the `/'. + + name This is the name of the parent. The parent's index + number is printed after it. If the parent is a + member of a cycle, the cycle number is printed between + the name and the index number. + + If the parents of the function cannot be determined, the word + `<spontaneous>' is printed in the `name' field, and all the other + fields are blank. + + For the function's children, the fields have the following meanings: + + self This is the amount of time that was propagated directly + from the child into the function. + + children This is the amount of time that was propagated from the + child's children to the function. + + called This is the number of times the function called + this child `/' the total number of times the child + was called. Recursive calls by the child are not + listed in the number after the `/'. + + name This is the name of the child. The child's index + number is printed after it. If the child is a + member of a cycle, the cycle number is printed + between the name and the index number. + + If there are any cycles (circles) in the call graph, there is an + entry for the cycle-as-a-whole. This entry shows who called the + cycle (as parents) and the members of the cycle (as children.) + The `+' recursive calls entry shows the number of function calls that + were internal to the cycle, and the calls entry for each member shows, + for that member, how many times it was called from other members of + the cycle. + diff --git a/gnu/usr.bin/binutils/gprof/gen-c-prog.awk b/gnu/usr.bin/binutils/gprof/gen-c-prog.awk new file mode 100644 index 00000000000..b59c1f8d8dd --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gen-c-prog.awk @@ -0,0 +1,26 @@ +NR == 1 { + FS="\""; + print "/* ==> Do not modify this file!! It is created automatically" + printf " from %s using the gen-c-prog.awk script. <== */\n\n", FILE + print "#include <stdio.h>" +} + + { + if (curfun != FUNCTION) + { + if (curfun) + print "}" + curfun = FUNCTION + print "" + print "void"; + printf "%s (file)\n", FUNCTION + print " FILE *file;"; + print "{"; + } + printf " fputs (\""; + for (i = 1; i < NF; i++) + printf "%s\\\"", $i; + printf "%s\\n\", file);\n", $NF; +} + +END { print "}" } diff --git a/gnu/usr.bin/binutils/gprof/gmon.h b/gnu/usr.bin/binutils/gprof/gmon.h new file mode 100644 index 00000000000..74cd4b8c552 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gmon.h @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)gmon.h 5.2 (Berkeley) 5/6/91 + */ +#ifndef gmon_h +#define gmon_h + +struct raw_phdr + { + char low_pc[sizeof (bfd_vma)]; /* base pc address of sample buffer */ + char high_pc[sizeof (bfd_vma)]; /* max pc address of sampled buffer */ + char ncnt[4]; /* size of sample buffer (plus this header) */ +#ifdef __osf__ + /* + * DEC's OSF v3.0 uses 4 bytes of padding to bring the header to + * a size that is a multiple of 8. + */ + char pad[4]; +#endif + }; + +/* + * Histogram counters are unsigned shorts: + */ +#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 + +/* + * Percent of text space to allocate for tostructs with a minimum: + */ +#define ARCDENSITY 2 +#define MINARCS 50 + +struct tostruct + { + char *selfpc; + int count; + unsigned short link; + }; + +/* + * 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: + */ +#define ROUNDDOWN(x,y) (((x)/(y))*(y)) +#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y)) + +#endif /* gmon_h */ diff --git a/gnu/usr.bin/binutils/gprof/gmon_io.c b/gnu/usr.bin/binutils/gprof/gmon_io.c new file mode 100644 index 00000000000..82c3caa37ae --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gmon_io.c @@ -0,0 +1,396 @@ +/* + * 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 (char*)) + { + 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 (char*)); + done (1); + } +} + + +/* + * 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 (char*)) + { + 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 (char*)); + done (1); + } +} + + +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 (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 ((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); + } + + /* 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); + } + + /* 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); + } + } + } + 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 (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); + } + 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); + } + 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 (!hist_sample) + { + hist_sample = + (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0])); + memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0])); + } + + 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); + } + hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) raw_bin_count); + } + + /* + * The rest of the file consists of a bunch of <from,self,count> + * tuples: + */ + while (fread (&raw_arc, sizeof (raw_arc), 1, ifp) == 1) + { + ++narcs; + from_pc = get_vma (core_bfd, (bfd_byte *) raw_arc.from_pc); + self_pc = get_vma (core_bfd, (bfd_byte *) raw_arc.self_pc); + count = bfd_get_32 (core_bfd, (bfd_byte *) raw_arc.count); + DBG (SAMPLEDEBUG, + printf ("[gmon_out_read] frompc 0x%lx selfpc 0x%lx count %d\n", + from_pc, self_pc, count)); + /* add this arc: */ + cg_tally (from_pc, self_pc, count); + } + 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"); + } + } + } + else + { + fprintf (stderr, "%s: don't know how to deal with file format %d\n", + whoami, file_format); + done (1); + } + + 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; + } +} + + +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 (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); + } + + /* write execution time histogram if we have one: */ + if (gmon_input & INPUT_HISTOGRAM) + { + hist_write_hist (ofp, filename); + } + + /* write call graph arcs if we have any: */ + if (gmon_input & INPUT_CALL_GRAPH) + { + cg_write_arcs (ofp, filename); + } + + /* write basic-block info if we have it: */ + if (gmon_input & INPUT_BB_COUNTS) + { + bb_write_blocks (ofp, filename); + } + } + 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); + } + + /* 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); + } + } + + /* 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); + } + DBG (SAMPLEDEBUG, + printf ("[dumpsum] frompc 0x%lx selfpc 0x%lx count %d\n", + arc->parent->addr, arc->child->addr, arc->count)); + } + } + fclose (ofp); + } + else + { + fprintf (stderr, "%s: don't know how to deal with file format %d\n", + whoami, file_format); + done (1); + } +} diff --git a/gnu/usr.bin/binutils/gprof/gmon_io.h b/gnu/usr.bin/binutils/gprof/gmon_io.h new file mode 100644 index 00000000000..bf257a49df6 --- /dev/null +++ b/gnu/usr.bin/binutils/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/gnu/usr.bin/binutils/gprof/gmon_out.h b/gnu/usr.bin/binutils/gprof/gmon_out.h new file mode 100644 index 00000000000..2144fb5954b --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gmon_out.h @@ -0,0 +1,51 @@ +/* + * 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 = 0, GMON_TAG_CG_ARC = 1, GMON_TAG_BB_COUNT = 2 + } +GMON_Record_Tag; + +struct gmon_hist_hdr + { + char low_pc[sizeof (char*)]; /* base pc address of sample buffer */ + char high_pc[sizeof (char*)]; /* 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 (char*)]; /* address within caller's body */ + char self_pc[sizeof (char*)]; /* address within callee's body */ + char count[4]; /* number of arc traversals */ + }; + +#endif /* gmon_out_h */ diff --git a/gnu/usr.bin/binutils/gprof/gprof.1 b/gnu/usr.bin/binutils/gprof/gprof.1 new file mode 100644 index 00000000000..d86a3918dc6 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gprof.1 @@ -0,0 +1,252 @@ +.\" Copyright (c) 1983, 1990 The 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. +.\" +.\" @(#)gprof.1 6.6 (Berkeley) 7/24/90 +.\" +.TH GPROF 1 "January 29, 1993" +.SH NAME +gprof \- display call graph profile data +.SH SYNOPSIS +.B gprof [ \-abcsz ] [ \-e|\-E +.I name +.B ] [ \-f|\-F +.I name +.B ] +.B [ \-k +.I fromname toname +.B ] [ +.I objfile +.B [ +.I gmon.out +.B ] +.B ] +.SH DESCRIPTION +.B gprof +produces an execution profile of C, Pascal, or Fortran77 programs. +The effect of called routines is incorporated in the profile of each caller. +The profile data is taken from the call graph profile file +\&(`gmon.out' default) which is created by programs +that are compiled with the +.B \-pg +option of +.BR cc ( 1 ) , +.BR pc ( 1 ) , +and +.BR f77 ( 1 ) . +The +.B \-pg +option also links in versions of the library routines +that are compiled for profiling. +.B Gprof +reads the given object file (the default is `a.out') +and establishes the relation between its symbol table +and the call graph profile from `gmon.out'. +If more than one profile file is specified, +the +.B gprof +output shows the sum of the profile information in the given profile files. +.PP +.B Gprof +calculates the amount of time spent in each routine. +Next, these times are propagated along the edges of the call graph. +Cycles are discovered, and calls into a cycle are made to share the time +of the cycle. +The first listing shows the functions +sorted according to the time they represent +including the time of their call graph descendents. +Below each function entry is shown its (direct) call graph children, +and how their times are propagated to this function. +A similar display above the function shows how this function's time and the +time of its descendents is propagated to its (direct) call graph parents. +.PP +Cycles are also shown, with an entry for the cycle as a whole and +a listing of the members of the cycle and their contributions to the +time and call counts of the cycle. +.PP +Second, a flat profile is given, +similar to that provided by +.BR prof ( 1 ) . +This listing gives the total execution times, the call counts, +the time in milleseconds the call spent in the routine itself, and +the time in milleseconds the call spent in the routine itself including +its descendents. +.PP +Finally, an index of the function names is provided. +.SH OPTIONS +The following options are available: +.TP +.B \-a +suppresses the printing of statically declared functions. +If this option is given, all relevant information about the static function +(e.g., time samples, calls to other functions, calls from other functions) +belongs to the function loaded just before the static function in the +\&`objfile' file. +.TP +.B \-b +suppresses the printing of a description of each field in the profile. +.TP +.B \-c +the static call graph of the program is discovered by a heuristic +that examines the text space of the object file. +Static-only parents or children are shown +with call counts of 0. +.TP +.BI "\-e " name +suppresses the printing of the graph profile entry for routine +.I name +and all its descendants +(unless they have other ancestors that aren't suppressed). +More than one +.B \-e +option may be given. +Only one +.I name +may be given with each +.B \-e +option. +.TP +.BI "\-E " name +suppresses the printing of the graph profile entry for routine +.I name +(and its descendants) as +.B \-e , +above, and also excludes the time spent in +.I name +(and its descendants) from the total and percentage time computations. +(For example, +.BI "\-E " mcount +.BI "\-E " mcleanup +is the default.) +.TP +.BI "\-f " name +prints the graph profile entry of only the specified routine +.I name +and its descendants. +More than one +.B \-f +option may be given. +Only one +.I name +may be given with each +.B \-f +option. +.TP +.BI "\-F " name +prints the graph profile entry of only the routine +.I name +and its descendants (as +.B \-f , +above) and also uses only the times of the printed routines +in total time and percentage computations. +More than one +.B \-F +option may be given. +Only one +.I name +may be given with each +.B \-F +option. +The +.B \-F +option +overrides +the +.B \-E +option. +.TP +.BI "\-k " "fromname toname" +will delete any arcs from routine +.I fromname +to routine +.IR toname . +This can be used to break undesired cycles. +More than one +.B \-k +option may be given. +Only one pair of routine names may be given with each +.B \-k +option. +.TP +.B \-s +a profile file `gmon.sum' is produced that represents +the sum of the profile information in all the specified profile files. +This summary profile file may be given to later +executions of gprof (probably also with a +.BR \-s ) +to accumulate profile data across several runs of an `objfile' file. +.TP +.B -v +prints the version number for gprof, and then exits. +.TP +.B -z +displays routines that have zero usage (as shown by call counts +and accumulated time). +This is useful with the +.B \-c +option for discovering which routines were never called. +.PP +.SH FILES +.ta \w'gmon.sum 'u +a.out the namelist and text space. +.br +gmon.out dynamic call graph and profile. +.br +gmon.sum summarized dynamic call graph and profile. +.SH SEE ALSO +.BR monitor ( 3 ) , +.BR profil ( 2 ) , +.BR cc ( 1 ) , +.BR prof ( 1 ) +.sp +``An Execution Profiler for Modular Programs'', +by S. Graham, P. Kessler, M. McKusick; +.I +Software \- Practice and Experience, +Vol. 13, pp. 671-685, 1983. +.sp +``gprof: A Call Graph Execution Profiler'', +by S. Graham, P. Kessler, M. McKusick; +.I +Proceedings of the SIGPLAN '82 Symposium on Compiler Construction, +SIGPLAN Notices, Vol. 17, No 6, pp. 120-126, June 1982. +.SH HISTORY +.B Gprof +appeared in 4.2 BSD. +.SH BUGS +The granularity of the sampling is shown, but remains +statistical at best. +We assume that the time for each execution of a function +can be expressed by the total time for the function divided +by the number of times the function is called. +Thus the time propagated along the call graph arcs to the function's +parents is directly proportional to the number of times that +arc is traversed. +.PP +Parents that are not themselves profiled will have the time of +their profiled children propagated to them, but they will appear +to be spontaneously invoked in the call graph listing, and will +not have their time propagated further. +Similarly, signal catchers, even though profiled, will appear +to be spontaneous (although for more obscure reasons). +Any profiled children of signal catchers should have their times +propagated properly, unless the signal catcher was invoked during +the execution of the profiling routine, in which case all is lost. +.PP +The profiled program must call +.BR exit ( 2 ) +or return normally for the profiling information to be saved +in the `gmon.out' file. diff --git a/gnu/usr.bin/binutils/gprof/gprof.c b/gnu/usr.bin/binutils/gprof/gprof.c new file mode 100644 index 00000000000..5da3180a93e --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gprof.c @@ -0,0 +1,587 @@ +/* + * 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 "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; + +/* + * 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; + +char copyright[] = +"@(#) Copyright (c) 1983 Regents of the University of California.\n\ + All rights reserved.\n"; + +static char *gmon_name = GMONNAME; /* profile filename */ + +bfd *abfd; + +/* + * Functions that get excluded by default: + */ +static char *default_excluded_list[] = +{ + "_gprof_mcount", "mcount", "_mcount", "__mcleanup", + "<locore>", "<hicore>", + 0 +}; + +static struct option long_options[] = +{ + {"line", no_argument, 0, 'l'}, + {"no-static", no_argument, 0, 'a'}, + + /* output styles: */ + + {"annotated-source", optional_argument, 0, 'A'}, + {"no-annotated-source", optional_argument, 0, 'J'}, + {"flat-profile", optional_argument, 0, 'p'}, + {"no-flat-profile", optional_argument, 0, 'P'}, + {"graph", optional_argument, 0, 'q'}, + {"no-graph", optional_argument, 0, 'Q'}, + {"exec-counts", optional_argument, 0, 'C'}, + {"no-exec-counts", optional_argument, 0, 'Z'}, + {"file-info", no_argument, 0, 'i'}, + {"sum", no_argument, 0, 's'}, + + /* various options to affect output: */ + + {"all-lines", no_argument, 0, 'x'}, + {"directory-path", required_argument, 0, 'I'}, + {"display-unused-functions", no_argument, 0, 'z'}, + {"min-count", required_argument, 0, 'm'}, + {"print-path", no_argument, 0, 'L'}, + {"separate-files", no_argument, 0, 'y'}, + {"static-call-graph", no_argument, 0, 'c'}, + {"table-length", required_argument, 0, 't'}, + {"time", required_argument, 0, 'n'}, + {"no-time", required_argument, 0, 'N'}, + {"width", required_argument, 0, 'w'}, + /* + * These are for backwards-compatibility only. Their functionality + * is provided by the output style options already: + */ + {"", 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); +} + + +int +DEFUN (main, (argc, argv), int argc AND char **argv) +{ + char **sp, *str; + Sym **cg = 0; + int ch, user_specified = 0; + + whoami = argv[0]; + 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); + } + output_style |= STYLE_ANNOTATED_SOURCE; + user_specified |= STYLE_ANNOTATED_SOURCE; + break; + case 'b': + print_descriptions = FALSE; + break; + case 'B': + output_style |= STYLE_CALL_GRAPH; + user_specified |= STYLE_CALL_GRAPH; + break; + case 'c': + ignore_direct_calls = TRUE; + break; + case 'C': + if (optarg) + { + sym_id_add (optarg, INCL_EXEC); + } + output_style |= STYLE_EXEC_COUNTS; + user_specified |= STYLE_EXEC_COUNTS; + break; + case 'd': + if (optarg) + { + debug_level |= atoi (optarg); + debug_level |= ANYDEBUG; + } + else + { + debug_level = ~0; + } + 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': + 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 '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; + } + user_specified |= STYLE_ANNOTATED_SOURCE; + break; + 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); + } + break; + case 'p': + if (optarg) + { + sym_id_add (optarg, INCL_FLAT); + } + output_style |= STYLE_FLAT_PROFILE; + user_specified |= STYLE_FLAT_PROFILE; + break; + case 'P': + if (optarg) + { + sym_id_add (optarg, EXCL_FLAT); + output_style |= STYLE_FLAT_PROFILE; + } + else + { + output_style &= ~STYLE_FLAT_PROFILE; + } + user_specified |= STYLE_FLAT_PROFILE; + break; + case 'q': + if (optarg) + { + if (strchr (optarg, '/')) + { + sym_id_add (optarg, INCL_ARCS); + } + else + { + sym_id_add (optarg, INCL_GRAPH); + } + } + output_style |= STYLE_CALL_GRAPH; + user_specified |= STYLE_CALL_GRAPH; + break; + case 'Q': + if (optarg) + { + if (strchr (optarg, '/')) + { + sym_id_add (optarg, EXCL_ARCS); + } + else + { + sym_id_add (optarg, EXCL_GRAPH); + } + output_style |= STYLE_CALL_GRAPH; + } + else + { + output_style &= ~STYLE_CALL_GRAPH; + } + user_specified |= STYLE_CALL_GRAPH; + break; + case 's': + output_style |= STYLE_SUMMARY_FILE; + user_specified |= STYLE_SUMMARY_FILE; + break; + case 't': + bb_table_length = atoi (optarg); + if (bb_table_length < 0) + { + bb_table_length = 0; + } + break; + 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; + } + 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; + } + user_specified |= STYLE_ANNOTATED_SOURCE; + break; + default: + usage (stderr, 1); + } + } + + /* append value of GPROF_PATH to source search list if set: */ + str = (char *) getenv ("GPROF_PATH"); + if (str) + { + search_list_append (&src_search_list, str); + } + + if (optind < argc) + { + a_out_name = argv[optind++]; + } + if (optind < argc) + { + gmon_name = argv[optind++]; + } + + /* + * Turn off default functions: + */ + 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 line-by-line profiling, also want to keep those + * functions off the flat profile: + */ + if (line_granularity) + { + for (sp = &default_excluded_list[0]; *sp; sp++) + { + sym_id_add (*sp, EXCL_FLAT); + } + } + + /* + * Read symbol table from core file: + */ + core_init (a_out_name); + + /* + * If we should ignore direct function calls, we need to load + * to core's text-space: + */ + if (ignore_direct_calls) + { + core_get_text_space (core_bfd); + } + + /* + * Create symbols from core image: + */ + if (line_granularity) + { + core_create_line_syms (core_bfd); + } + else + { + core_create_function_syms (core_bfd); + } + + /* + * Translate sym specs into syms: + */ + sym_id_parse (); + + 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]; + } + } + while (optind++ < argc); +#else + fprintf (stderr, + "%s: sorry, file format `prof' is not yet supported\n", + whoami); + done (1); +#endif + } + else + { + /* + * Get information about gmon.out file(s): + */ + do + { + gmon_out_read (gmon_name); + if (optind < argc) + { + gmon_name = argv[optind]; + } + } + while (optind++ < argc); + } + + /* + * 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; + } + output_style &= ~user_specified; + } + + /* + * Dump a gmon.sum file if requested (before any other processing!): + */ + if (output_style & STYLE_SUMMARY_FILE) + { + gmon_out_write (GMONSUM); + } + + if (gmon_input & INPUT_HISTOGRAM) + { + hist_assign_samples (); + } + + if (gmon_input & INPUT_CALL_GRAPH) + { + cg = cg_assemble (); + } + + /* 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 ((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); + } + + /* output whatever user whishes to see: */ + + if (cg && (output_style & STYLE_CALL_GRAPH) && bsd_style_output) + { + cg_print (cg); /* print the dynamic profile */ + } + + if (output_style & STYLE_FLAT_PROFILE) + { + hist_print (); /* print the flat profile */ + } + + if (cg && (output_style & STYLE_CALL_GRAPH)) + { + if (!bsd_style_output) + { + cg_print (cg); /* print the dynamic profile */ + } + cg_print_index (); + } + + if (output_style & STYLE_EXEC_COUNTS) + { + print_exec_counts (); + } + + if (output_style & STYLE_ANNOTATED_SOURCE) + { + print_annotated_source (); + } + return 0; +} + +void +done (status) + int status; +{ + exit (status); +} diff --git a/gnu/usr.bin/binutils/gprof/gprof.h b/gnu/usr.bin/binutils/gprof/gprof.h new file mode 100644 index 00000000000..e4177a9a0bb --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gprof.h @@ -0,0 +1,137 @@ +/* + * 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. + * + * @(#)gprof.h 5.9 (Berkeley) 6/1/90 + */ +#ifndef gprof_h +#define gprof_h + +#include <ansidecl.h> +#include "sysdep.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 +#else +#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 + +#define A_OUTNAME "a.out" /* default core filename */ +#define GMONNAME "gmon.out" /* default profile filename */ +#define GMONSUM "gmon.sum" /* profile summary filename */ + +/* + * 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 + +#define STYLE_FLAT_PROFILE (1<<0) +#define STYLE_CALL_GRAPH (1<<1) +#define STYLE_SUMMARY_FILE (1<<2) +#define STYLE_EXEC_COUNTS (1<<3) +#define STYLE_ANNOTATED_SOURCE (1<<4) +#define STYLE_GMON_INFO (1<<5) + +#define ANYDEBUG (1<<0) /* 1 */ +#define DFNDEBUG (1<<1) /* 2 */ +#define CYCLEDEBUG (1<<2) /* 4 */ +#define ARCDEBUG (1<<3) /* 8 */ +#define TALLYDEBUG (1<<4) /* 16 */ +#define TIMEDEBUG (1<<5) /* 32 */ +#define SAMPLEDEBUG (1<<6) /* 64 */ +#define AOUTDEBUG (1<<7) /* 128 */ +#define CALLDEBUG (1<<8) /* 256 */ +#define LOOKUPDEBUG (1<<9) /* 512 */ +#define PROPDEBUG (1<<10) /* 1024 */ +#define BBDEBUG (1<<11) /* 2048 */ +#define IDDEBUG (1<<12) /* 4096 */ +#define SRCDEBUG (1<<13) /* 8192 */ + +#ifdef DEBUG +#define DBG(l,s) if (debug_level & (l)) {s;} +#else +#define DBG(l,s) +#endif + +typedef enum + { + FF_AUTO = 0, FF_MAGIC, FF_BSD, FF_PROF + } +File_Format; + +typedef int bool; +typedef unsigned char UNIT[2]; /* unit of profiling */ + +extern const char *whoami; /* command-name, for error messages */ +extern const char *a_out_name; /* core filename */ +extern long hz; /* ticks per second */ + +/* + * 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/gnu/usr.bin/binutils/gprof/gprof.info b/gnu/usr.bin/binutils/gprof/gprof.info new file mode 100644 index 00000000000..60a2d9a9f43 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gprof.info @@ -0,0 +1,951 @@ +This is Info file gprof.info, produced by Makeinfo-1.55 from the input +file ./gprof.texi. + +START-INFO-DIR-ENTRY +* gprof: (gprof). Profiling your program's execution +END-INFO-DIR-ENTRY + + This file documents the gprof profiler of the GNU system. + + Copyright (C) 1988, 1992 Free Software Foundation, Inc. + + Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + + Permission is granted to copy and distribute modified versions of +this manual under the conditions for verbatim copying, provided that +the entire resulting derived work is distributed under the terms of a +permission notice identical to this one. + + Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions. + + +File: gprof.info, Node: Top, Next: Why, Prev: (DIR), Up: (DIR) + +Profiling a Program: Where Does It Spend Its Time? +************************************************** + + This manual describes the GNU profiler, `gprof', and how you can use +it to determine which parts of a program are taking most of the +execution time. We assume that you know how to write, compile, and +execute programs. GNU `gprof' was written by Jay Fenlason. + + This manual was updated January 1993. + +* Menu: + +* Why:: What profiling means, and why it is useful. +* Compiling:: How to compile your program for profiling. +* Executing:: How to execute your program to generate the + profile data file `gmon.out'. +* Invoking:: How to run `gprof', and how to specify + options for it. + +* Flat Profile:: The flat profile shows how much time was spent + executing directly in each function. +* Call Graph:: The call graph shows which functions called which + others, and how much time each function used + when its subroutine calls are included. + +* Implementation:: How the profile data is recorded and written. +* Sampling Error:: Statistical margins of error. + How to accumulate data from several runs + to make it more accurate. + +* Assumptions:: Some of `gprof''s measurements are based + on assumptions about your program + that could be very wrong. + +* Incompatibilities:: (between GNU `gprof' and Unix `gprof'.) + + +File: gprof.info, Node: Why, Next: Compiling, Prev: Top, Up: Top + +Why Profile +*********** + + Profiling allows you to learn where your program spent its time and +which functions called which other functions while it was executing. +This information can show you which pieces of your program are slower +than you expected, and might be candidates for rewriting to make your +program execute faster. It can also tell you which functions are being +called more or less often than you expected. This may help you spot +bugs that had otherwise been unnoticed. + + Since the profiler uses information collected during the actual +execution of your program, it can be used on programs that are too +large or too complex to analyze by reading the source. However, how +your program is run will affect the information that shows up in the +profile data. If you don't use some feature of your program while it +is being profiled, no profile information will be generated for that +feature. + + Profiling has several steps: + + * You must compile and link your program with profiling enabled. + *Note Compiling::. + + * You must execute your program to generate a profile data file. + *Note Executing::. + + * You must run `gprof' to analyze the profile data. *Note + Invoking::. + + The next three chapters explain these steps in greater detail. + + The result of the analysis is a file containing two tables, the +"flat profile" and the "call graph" (plus blurbs which briefly explain +the contents of these tables). + + The flat profile shows how much time your program spent in each +function, and how many times that function was called. If you simply +want to know which functions burn most of the cycles, it is stated +concisely here. *Note Flat Profile::. + + The call graph shows, for each function, which functions called it, +which other functions it called, and how many times. There is also an +estimate of how much time was spent in the subroutines of each +function. This can suggest places where you might try to eliminate +function calls that use a lot of time. *Note Call Graph::. + + +File: gprof.info, Node: Compiling, Next: Executing, Prev: Why, Up: Top + +Compiling a Program for Profiling +********************************* + + The first step in generating profile information for your program is +to compile and link it with profiling enabled. + + To compile a source file for profiling, specify the `-pg' option when +you run the compiler. (This is in addition to the options you normally +use.) + + To link the program for profiling, if you use a compiler such as `cc' +to do the linking, simply specify `-pg' in addition to your usual +options. The same option, `-pg', alters either compilation or linking +to do what is necessary for profiling. Here are examples: + + cc -g -c myprog.c utils.c -pg + cc -o myprog myprog.o utils.o -pg + + The `-pg' option also works with a command that both compiles and +links: + + cc -o myprog myprog.c utils.c -g -pg + + If you run the linker `ld' directly instead of through a compiler +such as `cc', you must specify the profiling startup file +`/lib/gcrt0.o' as the first input file instead of the usual startup +file `/lib/crt0.o'. In addition, you would probably want to specify +the profiling C library, `/usr/lib/libc_p.a', by writing `-lc_p' +instead of the usual `-lc'. This is not absolutely necessary, but +doing this gives you number-of-calls information for standard library +functions such as `read' and `open'. For example: + + ld -o myprog /lib/gcrt0.o myprog.o utils.o -lc_p + + If you compile only some of the modules of the program with `-pg', +you can still profile the program, but you won't get complete +information about the modules that were compiled without `-pg'. The +only information you get for the functions in those modules is the +total time spent in them; there is no record of how many times they +were called, or from where. This will not affect the flat profile +(except that the `calls' field for the functions will be blank), but +will greatly reduce the usefulness of the call graph. + + +File: gprof.info, Node: Executing, Next: Invoking, Prev: Compiling, Up: Top + +Executing the Program to Generate Profile Data +********************************************** + + Once the program is compiled for profiling, you must run it in order +to generate the information that `gprof' needs. Simply run the program +as usual, using the normal arguments, file names, etc. The program +should run normally, producing the same output as usual. It will, +however, run somewhat slower than normal because of the time spent +collecting and the writing the profile data. + + The way you run the program--the arguments and input that you give +it--may have a dramatic effect on what the profile information shows. +The profile data will describe the parts of the program that were +activated for the particular input you use. For example, if the first +command you give to your program is to quit, the profile data will show +the time used in initialization and in cleanup, but not much else. + + You program will write the profile data into a file called `gmon.out' +just before exiting. If there is already a file called `gmon.out', its +contents are overwritten. There is currently no way to tell the +program to write the profile data under a different name, but you can +rename the file afterward if you are concerned that it may be +overwritten. + + In order to write the `gmon.out' file properly, your program must +exit normally: by returning from `main' or by calling `exit'. Calling +the low-level function `_exit' does not write the profile data, and +neither does abnormal termination due to an unhandled signal. + + The `gmon.out' file is written in the program's *current working +directory* at the time it exits. This means that if your program calls +`chdir', the `gmon.out' file will be left in the last directory your +program `chdir''d to. If you don't have permission to write in this +directory, the file is not written. You may get a confusing error +message if this happens. (We have not yet replaced the part of Unix +responsible for this; when we do, we will make the error message +comprehensible.) + + +File: gprof.info, Node: Invoking, Next: Flat Profile, Prev: Executing, Up: Top + +`gprof' Command Summary +*********************** + + After you have a profile data file `gmon.out', you can run `gprof' +to interpret the information in it. The `gprof' program prints a flat +profile and a call graph on standard output. Typically you would +redirect the output of `gprof' into a file with `>'. + + You run `gprof' like this: + + gprof OPTIONS [EXECUTABLE-FILE [PROFILE-DATA-FILES...]] [> OUTFILE] + +Here square-brackets indicate optional arguments. + + If you omit the executable file name, the file `a.out' is used. If +you give no profile data file name, the file `gmon.out' is used. If +any file is not in the proper format, or if the profile data file does +not appear to belong to the executable file, an error message is +printed. + + You can give more than one profile data file by entering all their +names after the executable file name; then the statistics in all the +data files are summed together. + + The following options may be used to selectively include or exclude +functions in the output: + +`-a' + The `-a' option causes `gprof' to suppress the printing of + statically declared (private) functions. (These are functions + whose names are not listed as global, and which are not visible + outside the file/function/block where they were defined.) Time + spent in these functions, calls to/from them, etc, will all be + attributed to the function that was loaded directly before it in + the executable file. This option affects both the flat profile + and the call graph. + +`-e FUNCTION_NAME' + The `-e FUNCTION' option tells `gprof' to not print information + about the function FUNCTION_NAME (and its children...) in the call + graph. The function will still be listed as a child of any + functions that call it, but its index number will be shown as + `[not printed]'. More than one `-e' option may be given; only one + FUNCTION_NAME may be indicated with each `-e' option. + +`-E FUNCTION_NAME' + The `-E FUNCTION' option works like the `-e' option, but time + spent in the function (and children who were not called from + anywhere else), will not be used to compute the + percentages-of-time for the call graph. More than one `-E' option + may be given; only one FUNCTION_NAME may be indicated with each + `-E' option. + +`-f FUNCTION_NAME' + The `-f FUNCTION' option causes `gprof' to limit the call graph to + the function FUNCTION_NAME and its children (and their + children...). More than one `-f' option may be given; only one + FUNCTION_NAME may be indicated with each `-f' option. + +`-F FUNCTION_NAME' + The `-F FUNCTION' option works like the `-f' option, but only time + spent in the function and its children (and their children...) + will be used to determine total-time and percentages-of-time for + the call graph. More than one `-F' option may be given; only one + FUNCTION_NAME may be indicated with each `-F' option. The `-F' + option overrides the `-E' option. + +`-k FROM... TO...' + The `-k' option allows you to delete from the profile any arcs from + routine FROM to routine TO. + +`-v' + The `-v' flag causes `gprof' to print the current version number, + and then exit. + +`-z' + If you give the `-z' option, `gprof' will mention all functions in + the flat profile, even those that were never called, and that had + no time spent in them. This is useful in conjunction with the + `-c' option for discovering which routines were never called. + + The order of these options does not matter. + + Note that only one function can be specified with each `-e', `-E', +`-f' or `-F' option. To specify more than one function, use multiple +options. For example, this command: + + gprof -e boring -f foo -f bar myprogram > gprof.output + +lists in the call graph all functions that were reached from either +`foo' or `bar' and were not reachable from `boring'. + + There are a few other useful `gprof' options: + +`-b' + If the `-b' option is given, `gprof' doesn't print the verbose + blurbs that try to explain the meaning of all of the fields in the + tables. This is useful if you intend to print out the output, or + are tired of seeing the blurbs. + +`-c' + The `-c' option causes the static call-graph of the program to be + discovered by a heuristic which examines the text space of the + object file. Static-only parents or children are indicated with + call counts of `0'. + +`-d NUM' + The `-d NUM' option specifies debugging options. + +`-s' + The `-s' option causes `gprof' to summarize the information in the + profile data files it read in, and write out a profile data file + called `gmon.sum', which contains all the information from the + profile data files that `gprof' read in. The file `gmon.sum' may + be one of the specified input files; the effect of this is to + merge the data in the other input files into `gmon.sum'. *Note + Sampling Error::. + + Eventually you can run `gprof' again without `-s' to analyze the + cumulative data in the file `gmon.sum'. + +`-T' + The `-T' option causes `gprof' to print its output in + "traditional" BSD style. + + +File: gprof.info, Node: Flat Profile, Next: Call Graph, Prev: Invoking, Up: Top + +How to Understand the Flat Profile +********************************** + + The "flat profile" shows the total amount of time your program spent +executing each function. Unless the `-z' option is given, functions +with no apparent time spent in them, and no apparent calls to them, are +not mentioned. Note that if a function was not compiled for profiling, +and didn't run long enough to show up on the program counter histogram, +it will be indistinguishable from a function that was never called. + + This is part of a flat profile for a small program: + + Flat profile: + + Each sample counts as 0.01 seconds. + % cumulative self self total + time seconds seconds calls ms/call ms/call name + 33.34 0.02 0.02 7208 0.00 0.00 open + 16.67 0.03 0.01 244 0.04 0.12 offtime + 16.67 0.04 0.01 8 1.25 1.25 memccpy + 16.67 0.05 0.01 7 1.43 1.43 write + 16.67 0.06 0.01 mcount + 0.00 0.06 0.00 236 0.00 0.00 tzset + 0.00 0.06 0.00 192 0.00 0.00 tolower + 0.00 0.06 0.00 47 0.00 0.00 strlen + 0.00 0.06 0.00 45 0.00 0.00 strchr + 0.00 0.06 0.00 1 0.00 50.00 main + 0.00 0.06 0.00 1 0.00 0.00 memcpy + 0.00 0.06 0.00 1 0.00 10.11 print + 0.00 0.06 0.00 1 0.00 0.00 profil + 0.00 0.06 0.00 1 0.00 50.00 report + ... + +The functions are sorted by decreasing run-time spent in them. The +functions `mcount' and `profil' are part of the profiling aparatus and +appear in every flat profile; their time gives a measure of the amount +of overhead due to profiling. + + The sampling period estimates the margin of error in each of the time +figures. A time figure that is not much larger than this is not +reliable. In this example, the `self seconds' field for `mcount' might +well be `0' or `0.04' in another run. *Note Sampling Error::, for a +complete discussion. + + Here is what the fields in each line mean: + +`% time' + This is the percentage of the total execution time your program + spent in this function. These should all add up to 100%. + +`cumulative seconds' + This is the cumulative total number of seconds the computer spent + executing this functions, plus the time spent in all the functions + above this one in this table. + +`self seconds' + This is the number of seconds accounted for by this function alone. + The flat profile listing is sorted first by this number. + +`calls' + This is the total number of times the function was called. If the + function was never called, or the number of times it was called + cannot be determined (probably because the function was not + compiled with profiling enabled), the "calls" field is blank. + +`self ms/call' + This represents the average number of milliseconds spent in this + function per call, if this function is profiled. Otherwise, this + field is blank for this function. + +`total ms/call' + This represents the average number of milliseconds spent in this + function and its descendants per call, if this function is + profiled. Otherwise, this field is blank for this function. + +`name' + This is the name of the function. The flat profile is sorted by + this field alphabetically after the "self seconds" field is sorted. + + +File: gprof.info, Node: Call Graph, Next: Implementation, Prev: Flat Profile, Up: Top + +How to Read the Call Graph +************************** + + The "call graph" shows how much time was spent in each function and +its children. From this information, you can find functions that, +while they themselves may not have used much time, called other +functions that did use unusual amounts of time. + + Here is a sample call from a small program. This call came from the +same `gprof' run as the flat profile example in the previous chapter. + + granularity: each sample hit covers 2 byte(s) for 20.00% of 0.05 seconds + + index % time self children called name + <spontaneous> + [1] 100.0 0.00 0.05 start [1] + 0.00 0.05 1/1 main [2] + 0.00 0.00 1/2 on_exit [28] + 0.00 0.00 1/1 exit [59] + ----------------------------------------------- + 0.00 0.05 1/1 start [1] + [2] 100.0 0.00 0.05 1 main [2] + 0.00 0.05 1/1 report [3] + ----------------------------------------------- + 0.00 0.05 1/1 main [2] + [3] 100.0 0.00 0.05 1 report [3] + 0.00 0.03 8/8 timelocal [6] + 0.00 0.01 1/1 print [9] + 0.00 0.01 9/9 fgets [12] + 0.00 0.00 12/34 strncmp <cycle 1> [40] + 0.00 0.00 8/8 lookup [20] + 0.00 0.00 1/1 fopen [21] + 0.00 0.00 8/8 chewtime [24] + 0.00 0.00 8/16 skipspace [44] + ----------------------------------------------- + [4] 59.8 0.01 0.02 8+472 <cycle 2 as a whole> [4] + 0.01 0.02 244+260 offtime <cycle 2> [7] + 0.00 0.00 236+1 tzset <cycle 2> [26] + ----------------------------------------------- + + The lines full of dashes divide this table into "entries", one for +each function. Each entry has one or more lines. + + In each entry, the primary line is the one that starts with an index +number in square brackets. The end of this line says which function +the entry is for. The preceding lines in the entry describe the +callers of this function and the following lines describe its +subroutines (also called "children" when we speak of the call graph). + + The entries are sorted by time spent in the function and its +subroutines. + + The internal profiling function `mcount' (*note Flat Profile::.) is +never mentioned in the call graph. + +* Menu: + +* Primary:: Details of the primary line's contents. +* Callers:: Details of caller-lines' contents. +* Subroutines:: Details of subroutine-lines' contents. +* Cycles:: When there are cycles of recursion, + such as `a' calls `b' calls `a'... + + +File: gprof.info, Node: Primary, Next: Callers, Up: Call Graph + +The Primary Line +================ + + The "primary line" in a call graph entry is the line that describes +the function which the entry is about and gives the overall statistics +for this function. + + For reference, we repeat the primary line from the entry for function +`report' in our main example, together with the heading line that shows +the names of the fields: + + index % time self children called name + ... + [3] 100.0 0.00 0.05 1 report [3] + + Here is what the fields in the primary line mean: + +`index' + Entries are numbered with consecutive integers. Each function + therefore has an index number, which appears at the beginning of + its primary line. + + Each cross-reference to a function, as a caller or subroutine of + another, gives its index number as well as its name. The index + number guides you if you wish to look for the entry for that + function. + +`% time' + This is the percentage of the total time that was spent in this + function, including time spent in subroutines called from this + function. + + The time spent in this function is counted again for the callers of + this function. Therefore, adding up these percentages is + meaningless. + +`self' + This is the total amount of time spent in this function. This + should be identical to the number printed in the `seconds' field + for this function in the flat profile. + +`children' + This is the total amount of time spent in the subroutine calls + made by this function. This should be equal to the sum of all the + `self' and `children' entries of the children listed directly + below this function. + +`called' + This is the number of times the function was called. + + If the function called itself recursively, there are two numbers, + separated by a `+'. The first number counts non-recursive calls, + and the second counts recursive calls. + + In the example above, the function `report' was called once from + `main'. + +`name' + This is the name of the current function. The index number is + repeated after it. + + If the function is part of a cycle of recursion, the cycle number + is printed between the function's name and the index number (*note + Cycles::.). For example, if function `gnurr' is part of cycle + number one, and has index number twelve, its primary line would be + end like this: + + gnurr <cycle 1> [12] + + +File: gprof.info, Node: Callers, Next: Subroutines, Prev: Primary, Up: Call Graph + +Lines for a Function's Callers +============================== + + A function's entry has a line for each function it was called by. +These lines' fields correspond to the fields of the primary line, but +their meanings are different because of the difference in context. + + For reference, we repeat two lines from the entry for the function +`report', the primary line and one caller-line preceding it, together +with the heading line that shows the names of the fields: + + index % time self children called name + ... + 0.00 0.05 1/1 main [2] + [3] 100.0 0.00 0.05 1 report [3] + + Here are the meanings of the fields in the caller-line for `report' +called from `main': + +`self' + An estimate of the amount of time spent in `report' itself when it + was called from `main'. + +`children' + An estimate of the amount of time spent in subroutines of `report' + when `report' was called from `main'. + + The sum of the `self' and `children' fields is an estimate of the + amount of time spent within calls to `report' from `main'. + +`called' + Two numbers: the number of times `report' was called from `main', + followed by the total number of nonrecursive calls to `report' from + all its callers. + +`name and index number' + The name of the caller of `report' to which this line applies, + followed by the caller's index number. + + Not all functions have entries in the call graph; some options to + `gprof' request the omission of certain functions. When a caller + has no entry of its own, it still has caller-lines in the entries + of the functions it calls. + + If the caller is part of a recursion cycle, the cycle number is + printed between the name and the index number. + + If the identity of the callers of a function cannot be determined, a +dummy caller-line is printed which has `<spontaneous>' as the "caller's +name" and all other fields blank. This can happen for signal handlers. + + +File: gprof.info, Node: Subroutines, Next: Cycles, Prev: Callers, Up: Call Graph + +Lines for a Function's Subroutines +================================== + + A function's entry has a line for each of its subroutines--in other +words, a line for each other function that it called. These lines' +fields correspond to the fields of the primary line, but their meanings +are different because of the difference in context. + + For reference, we repeat two lines from the entry for the function +`main', the primary line and a line for a subroutine, together with the +heading line that shows the names of the fields: + + index % time self children called name + ... + [2] 100.0 0.00 0.05 1 main [2] + 0.00 0.05 1/1 report [3] + + Here are the meanings of the fields in the subroutine-line for `main' +calling `report': + +`self' + An estimate of the amount of time spent directly within `report' + when `report' was called from `main'. + +`children' + An estimate of the amount of time spent in subroutines of `report' + when `report' was called from `main'. + + The sum of the `self' and `children' fields is an estimate of the + total time spent in calls to `report' from `main'. + +`called' + Two numbers, the number of calls to `report' from `main' followed + by the total number of nonrecursive calls to `report'. + +`name' + The name of the subroutine of `main' to which this line applies, + followed by the subroutine's index number. + + If the caller is part of a recursion cycle, the cycle number is + printed between the name and the index number. + + +File: gprof.info, Node: Cycles, Prev: Subroutines, Up: Call Graph + +How Mutually Recursive Functions Are Described +============================================== + + The graph may be complicated by the presence of "cycles of +recursion" in the call graph. A cycle exists if a function calls +another function that (directly or indirectly) calls (or appears to +call) the original function. For example: if `a' calls `b', and `b' +calls `a', then `a' and `b' form a cycle. + + Whenever there are call-paths both ways between a pair of functions, +they belong to the same cycle. If `a' and `b' call each other and `b' +and `c' call each other, all three make one cycle. Note that even if +`b' only calls `a' if it was not called from `a', `gprof' cannot +determine this, so `a' and `b' are still considered a cycle. + + The cycles are numbered with consecutive integers. When a function +belongs to a cycle, each time the function name appears in the call +graph it is followed by `<cycle NUMBER>'. + + The reason cycles matter is that they make the time values in the +call graph paradoxical. The "time spent in children" of `a' should +include the time spent in its subroutine `b' and in `b''s +subroutines--but one of `b''s subroutines is `a'! How much of `a''s +time should be included in the children of `a', when `a' is indirectly +recursive? + + The way `gprof' resolves this paradox is by creating a single entry +for the cycle as a whole. The primary line of this entry describes the +total time spent directly in the functions of the cycle. The +"subroutines" of the cycle are the individual functions of the cycle, +and all other functions that were called directly by them. The +"callers" of the cycle are the functions, outside the cycle, that +called functions in the cycle. + + Here is an example portion of a call graph which shows a cycle +containing functions `a' and `b'. The cycle was entered by a call to +`a' from `main'; both `a' and `b' called `c'. + + index % time self children called name + ---------------------------------------- + 1.77 0 1/1 main [2] + [3] 91.71 1.77 0 1+5 <cycle 1 as a whole> [3] + 1.02 0 3 b <cycle 1> [4] + 0.75 0 2 a <cycle 1> [5] + ---------------------------------------- + 3 a <cycle 1> [5] + [4] 52.85 1.02 0 0 b <cycle 1> [4] + 2 a <cycle 1> [5] + 0 0 3/6 c [6] + ---------------------------------------- + 1.77 0 1/1 main [2] + 2 b <cycle 1> [4] + [5] 38.86 0.75 0 1 a <cycle 1> [5] + 3 b <cycle 1> [4] + 0 0 3/6 c [6] + ---------------------------------------- + +(The entire call graph for this program contains in addition an entry +for `main', which calls `a', and an entry for `c', with callers `a' and +`b'.) + + index % time self children called name + <spontaneous> + [1] 100.00 0 1.93 0 start [1] + 0.16 1.77 1/1 main [2] + ---------------------------------------- + 0.16 1.77 1/1 start [1] + [2] 100.00 0.16 1.77 1 main [2] + 1.77 0 1/1 a <cycle 1> [5] + ---------------------------------------- + 1.77 0 1/1 main [2] + [3] 91.71 1.77 0 1+5 <cycle 1 as a whole> [3] + 1.02 0 3 b <cycle 1> [4] + 0.75 0 2 a <cycle 1> [5] + 0 0 6/6 c [6] + ---------------------------------------- + 3 a <cycle 1> [5] + [4] 52.85 1.02 0 0 b <cycle 1> [4] + 2 a <cycle 1> [5] + 0 0 3/6 c [6] + ---------------------------------------- + 1.77 0 1/1 main [2] + 2 b <cycle 1> [4] + [5] 38.86 0.75 0 1 a <cycle 1> [5] + 3 b <cycle 1> [4] + 0 0 3/6 c [6] + ---------------------------------------- + 0 0 3/6 b <cycle 1> [4] + 0 0 3/6 a <cycle 1> [5] + [6] 0.00 0 0 6 c [6] + ---------------------------------------- + + The `self' field of the cycle's primary line is the total time spent +in all the functions of the cycle. It equals the sum of the `self' +fields for the individual functions in the cycle, found in the entry in +the subroutine lines for these functions. + + The `children' fields of the cycle's primary line and subroutine +lines count only subroutines outside the cycle. Even though `a' calls +`b', the time spent in those calls to `b' is not counted in `a''s +`children' time. Thus, we do not encounter the problem of what to do +when the time in those calls to `b' includes indirect recursive calls +back to `a'. + + The `children' field of a caller-line in the cycle's entry estimates +the amount of time spent *in the whole cycle*, and its other +subroutines, on the times when that caller called a function in the +cycle. + + The `calls' field in the primary line for the cycle has two numbers: +first, the number of times functions in the cycle were called by +functions outside the cycle; second, the number of times they were +called by functions in the cycle (including times when a function in +the cycle calls itself). This is a generalization of the usual split +into nonrecursive and recursive calls. + + The `calls' field of a subroutine-line for a cycle member in the +cycle's entry says how many time that function was called from +functions in the cycle. The total of all these is the second number in +the primary line's `calls' field. + + In the individual entry for a function in a cycle, the other +functions in the same cycle can appear as subroutines and as callers. +These lines show how many times each function in the cycle called or +was called from each other function in the cycle. The `self' and +`children' fields in these lines are blank because of the difficulty of +defining meanings for them when recursion is going on. + + +File: gprof.info, Node: Implementation, Next: Sampling Error, Prev: Call Graph, Up: Top + +Implementation of Profiling +*************************** + + Profiling works by changing how every function in your program is +compiled so that when it is called, it will stash away some information +about where it was called from. From this, the profiler can figure out +what function called it, and can count how many times it was called. +This change is made by the compiler when your program is compiled with +the `-pg' option. + + Profiling also involves watching your program as it runs, and +keeping a histogram of where the program counter happens to be every +now and then. Typically the program counter is looked at around 100 +times per second of run time, but the exact frequency may vary from +system to system. + + A special startup routine allocates memory for the histogram and +sets up a clock signal handler to make entries in it. Use of this +special startup routine is one of the effects of using `gcc ... -pg' to +link. The startup file also includes an `exit' function which is +responsible for writing the file `gmon.out'. + + Number-of-calls information for library routines is collected by +using a special version of the C library. The programs in it are the +same as in the usual C library, but they were compiled with `-pg'. If +you link your program with `gcc ... -pg', it automatically uses the +profiling version of the library. + + The output from `gprof' gives no indication of parts of your program +that are limited by I/O or swapping bandwidth. This is because samples +of the program counter are taken at fixed intervals of run time. +Therefore, the time measurements in `gprof' output say nothing about +time that your program was not running. For example, a part of the +program that creates so much data that it cannot all fit in physical +memory at once may run very slowly due to thrashing, but `gprof' will +say it uses little time. On the other hand, sampling by run time has +the advantage that the amount of load due to other users won't directly +affect the output you get. + + +File: gprof.info, Node: Sampling Error, Next: Assumptions, Prev: Implementation, Up: Top + +Statistical Inaccuracy of `gprof' Output +**************************************** + + The run-time figures that `gprof' gives you are based on a sampling +process, so they are subject to statistical inaccuracy. If a function +runs only a small amount of time, so that on the average the sampling +process ought to catch that function in the act only once, there is a +pretty good chance it will actually find that function zero times, or +twice. + + By contrast, the number-of-calls figures are derived by counting, not +sampling. They are completely accurate and will not vary from run to +run if your program is deterministic. + + The "sampling period" that is printed at the beginning of the flat +profile says how often samples are taken. The rule of thumb is that a +run-time figure is accurate if it is considerably bigger than the +sampling period. + + The actual amount of error is usually more than one sampling period. +In fact, if a value is N times the sampling period, the *expected* +error in it is the square-root of N sampling periods. If the sampling +period is 0.01 seconds and `foo''s run-time is 1 second, the expected +error in `foo''s run-time is 0.1 seconds. It is likely to vary this +much *on the average* from one profiling run to the next. (*Sometimes* +it will vary more.) + + This does not mean that a small run-time figure is devoid of +information. If the program's *total* run-time is large, a small +run-time for one function does tell you that that function used an +insignificant fraction of the whole program's time. Usually this means +it is not worth optimizing. + + One way to get more accuracy is to give your program more (but +similar) input data so it will take longer. Another way is to combine +the data from several runs, using the `-s' option of `gprof'. Here is +how: + + 1. Run your program once. + + 2. Issue the command `mv gmon.out gmon.sum'. + + 3. Run your program again, the same as before. + + 4. Merge the new data in `gmon.out' into `gmon.sum' with this command: + + gprof -s EXECUTABLE-FILE gmon.out gmon.sum + + 5. Repeat the last two steps as often as you wish. + + 6. Analyze the cumulative data using this command: + + gprof EXECUTABLE-FILE gmon.sum > OUTPUT-FILE + + +File: gprof.info, Node: Assumptions, Next: Incompatibilities, Prev: Sampling Error, Up: Top + +Estimating `children' Times Uses an Assumption +********************************************** + + Some of the figures in the call graph are estimates--for example, the +`children' time values and all the the time figures in caller and +subroutine lines. + + There is no direct information about these measurements in the +profile data itself. Instead, `gprof' estimates them by making an +assumption about your program that might or might not be true. + + The assumption made is that the average time spent in each call to +any function `foo' is not correlated with who called `foo'. If `foo' +used 5 seconds in all, and 2/5 of the calls to `foo' came from `a', +then `foo' contributes 2 seconds to `a''s `children' time, by +assumption. + + This assumption is usually true enough, but for some programs it is +far from true. Suppose that `foo' returns very quickly when its +argument is zero; suppose that `a' always passes zero as an argument, +while other callers of `foo' pass other arguments. In this program, +all the time spent in `foo' is in the calls from callers other than `a'. +But `gprof' has no way of knowing this; it will blindly and incorrectly +charge 2 seconds of time in `foo' to the children of `a'. + + We hope some day to put more complete data into `gmon.out', so that +this assumption is no longer needed, if we can figure out how. For the +nonce, the estimated figures are usually more useful than misleading. + + +File: gprof.info, Node: Incompatibilities, Prev: Assumptions, Up: Top + +Incompatibilities with Unix `gprof' +*********************************** + + GNU `gprof' and Berkeley Unix `gprof' use the same data file +`gmon.out', and provide essentially the same information. But there +are a few differences. + + * For a recursive function, Unix `gprof' lists the function as a + parent and as a child, with a `calls' field that lists the number + of recursive calls. GNU `gprof' omits these lines and puts the + number of recursive calls in the primary line. + + * When a function is suppressed from the call graph with `-e', GNU + `gprof' still lists it as a subroutine of functions that call it. + + * The blurbs, field widths, and output formats are different. GNU + `gprof' prints blurbs after the tables, so that you can see the + tables without skipping the blurbs. + + + +Tag Table: +Node: Top888 +Node: Why2593 +Node: Compiling4687 +Node: Executing6671 +Node: Invoking8782 +Node: Flat Profile14056 +Node: Call Graph17742 +Node: Primary20981 +Node: Callers23514 +Node: Subroutines25621 +Node: Cycles27280 +Node: Implementation34044 +Node: Sampling Error36144 +Node: Assumptions38463 +Node: Incompatibilities39988 + +End Tag Table diff --git a/gnu/usr.bin/binutils/gprof/gprof.texi b/gnu/usr.bin/binutils/gprof/gprof.texi new file mode 100644 index 00000000000..ba05b594d65 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/gprof.texi @@ -0,0 +1,1055 @@ +\input texinfo @c -*-texinfo-*- +@setfilename gprof.info +@settitle GNU gprof +@setchapternewpage odd + +@ifinfo +@c This is a dir.info fragment to support semi-automated addition of +@c manuals to an info tree. zoo@cygnus.com is developing this facility. +@format +START-INFO-DIR-ENTRY +* gprof: (gprof). Profiling your program's execution +END-INFO-DIR-ENTRY +@end format +@end ifinfo + +@ifinfo +This file documents the gprof profiler of the GNU system. + +Copyright (C) 1988, 1992 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through Tex and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the above conditions for modified versions. +@end ifinfo + +@finalout +@smallbook + +@titlepage +@title GNU gprof +@subtitle The @sc{gnu} Profiler +@author Jay Fenlason and Richard Stallman + +@page + +This manual describes the @sc{gnu} profiler, @code{gprof}, and how you +can use it to determine which parts of a program are taking most of the +execution time. We assume that you know how to write, compile, and +execute programs. @sc{gnu} @code{gprof} was written by Jay Fenlason. + +This manual was edited January 1993 by Jeffrey Osier. + +@vskip 0pt plus 1filll +Copyright @copyright{} 1988, 1992 Free Software Foundation, Inc. + +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission notice +are preserved on all copies. + +@ignore +Permission is granted to process this file through TeX and print the +results, provided the printed document carries copying permission +notice identical to this one except for the removal of this paragraph +(this paragraph not being relevant to the printed manual). + +@end ignore +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the entire +resulting derived work is distributed under the terms of a permission +notice identical to this one. + +Permission is granted to copy and distribute translations of this manual +into another language, under the same conditions as for modified versions. + +@end titlepage + +@ifinfo +@node Top +@top Profiling a Program: Where Does It Spend Its Time? + +This manual describes the @sc{gnu} profiler, @code{gprof}, and how you +can use it to determine which parts of a program are taking most of the +execution time. We assume that you know how to write, compile, and +execute programs. @sc{gnu} @code{gprof} was written by Jay Fenlason. + +This manual was updated January 1993. + +@menu +* Why:: What profiling means, and why it is useful. +* Compiling:: How to compile your program for profiling. +* Executing:: How to execute your program to generate the + profile data file @file{gmon.out}. +* Invoking:: How to run @code{gprof}, and how to specify + options for it. + +* Flat Profile:: The flat profile shows how much time was spent + executing directly in each function. +* Call Graph:: The call graph shows which functions called which + others, and how much time each function used + when its subroutine calls are included. + +* Implementation:: How the profile data is recorded and written. +* Sampling Error:: Statistical margins of error. + How to accumulate data from several runs + to make it more accurate. + +* Assumptions:: Some of @code{gprof}'s measurements are based + on assumptions about your program + that could be very wrong. + +* Incompatibilities:: (between GNU @code{gprof} and Unix @code{gprof}.) +@end menu +@end ifinfo + +@node Why +@chapter Why Profile + +Profiling allows you to learn where your program spent its time and which +functions called which other functions while it was executing. This +information can show you which pieces of your program are slower than you +expected, and might be candidates for rewriting to make your program +execute faster. It can also tell you which functions are being called more +or less often than you expected. This may help you spot bugs that had +otherwise been unnoticed. + +Since the profiler uses information collected during the actual execution +of your program, it can be used on programs that are too large or too +complex to analyze by reading the source. However, how your program is run +will affect the information that shows up in the profile data. If you +don't use some feature of your program while it is being profiled, no +profile information will be generated for that feature. + +Profiling has several steps: + +@itemize @bullet +@item +You must compile and link your program with profiling enabled. +@xref{Compiling}. + +@item +You must execute your program to generate a profile data file. +@xref{Executing}. + +@item +You must run @code{gprof} to analyze the profile data. +@xref{Invoking}. +@end itemize + +The next three chapters explain these steps in greater detail. + +The result of the analysis is a file containing two tables, the +@dfn{flat profile} and the @dfn{call graph} (plus blurbs which briefly +explain the contents of these tables). + +The flat profile shows how much time your program spent in each function, +and how many times that function was called. If you simply want to know +which functions burn most of the cycles, it is stated concisely here. +@xref{Flat Profile}. + +The call graph shows, for each function, which functions called it, which +other functions it called, and how many times. There is also an estimate +of how much time was spent in the subroutines of each function. This can +suggest places where you might try to eliminate function calls that use a +lot of time. @xref{Call Graph}. + +@node Compiling +@chapter Compiling a Program for Profiling + +The first step in generating profile information for your program is +to compile and link it with profiling enabled. + +To compile a source file for profiling, specify the @samp{-pg} option when +you run the compiler. (This is in addition to the options you normally +use.) + +To link the program for profiling, if you use a compiler such as @code{cc} +to do the linking, simply specify @samp{-pg} in addition to your usual +options. The same option, @samp{-pg}, alters either compilation or linking +to do what is necessary for profiling. Here are examples: + +@example +cc -g -c myprog.c utils.c -pg +cc -o myprog myprog.o utils.o -pg +@end example + +The @samp{-pg} option also works with a command that both compiles and links: + +@example +cc -o myprog myprog.c utils.c -g -pg +@end example + +If you run the linker @code{ld} directly instead of through a compiler +such as @code{cc}, you must specify the profiling startup file +@file{/lib/gcrt0.o} as the first input file instead of the usual startup +file @file{/lib/crt0.o}. In addition, you would probably want to +specify the profiling C library, @file{/usr/lib/libc_p.a}, by writing +@samp{-lc_p} instead of the usual @samp{-lc}. This is not absolutely +necessary, but doing this gives you number-of-calls information for +standard library functions such as @code{read} and @code{open}. For +example: + +@example +ld -o myprog /lib/gcrt0.o myprog.o utils.o -lc_p +@end example + +If you compile only some of the modules of the program with @samp{-pg}, you +can still profile the program, but you won't get complete information about +the modules that were compiled without @samp{-pg}. The only information +you get for the functions in those modules is the total time spent in them; +there is no record of how many times they were called, or from where. This +will not affect the flat profile (except that the @code{calls} field for +the functions will be blank), but will greatly reduce the usefulness of the +call graph. + +@node Executing +@chapter Executing the Program to Generate Profile Data + +Once the program is compiled for profiling, you must run it in order to +generate the information that @code{gprof} needs. Simply run the program +as usual, using the normal arguments, file names, etc. The program should +run normally, producing the same output as usual. It will, however, run +somewhat slower than normal because of the time spent collecting and the +writing the profile data. + +The way you run the program---the arguments and input that you give +it---may have a dramatic effect on what the profile information shows. The +profile data will describe the parts of the program that were activated for +the particular input you use. For example, if the first command you give +to your program is to quit, the profile data will show the time used in +initialization and in cleanup, but not much else. + +You program will write the profile data into a file called @file{gmon.out} +just before exiting. If there is already a file called @file{gmon.out}, +its contents are overwritten. There is currently no way to tell the +program to write the profile data under a different name, but you can rename +the file afterward if you are concerned that it may be overwritten. + +In order to write the @file{gmon.out} file properly, your program must exit +normally: by returning from @code{main} or by calling @code{exit}. Calling +the low-level function @code{_exit} does not write the profile data, and +neither does abnormal termination due to an unhandled signal. + +The @file{gmon.out} file is written in the program's @emph{current working +directory} at the time it exits. This means that if your program calls +@code{chdir}, the @file{gmon.out} file will be left in the last directory +your program @code{chdir}'d to. If you don't have permission to write in +this directory, the file is not written. You may get a confusing error +message if this happens. (We have not yet replaced the part of Unix +responsible for this; when we do, we will make the error message +comprehensible.) + +@node Invoking +@chapter @code{gprof} Command Summary + +After you have a profile data file @file{gmon.out}, you can run @code{gprof} +to interpret the information in it. The @code{gprof} program prints a +flat profile and a call graph on standard output. Typically you would +redirect the output of @code{gprof} into a file with @samp{>}. + +You run @code{gprof} like this: + +@smallexample +gprof @var{options} [@var{executable-file} [@var{profile-data-files}@dots{}]] [> @var{outfile}] +@end smallexample + +@noindent +Here square-brackets indicate optional arguments. + +If you omit the executable file name, the file @file{a.out} is used. If +you give no profile data file name, the file @file{gmon.out} is used. If +any file is not in the proper format, or if the profile data file does not +appear to belong to the executable file, an error message is printed. + +You can give more than one profile data file by entering all their names +after the executable file name; then the statistics in all the data files +are summed together. + +The following options may be used to selectively include or exclude +functions in the output: + +@table @code +@item -a +The @samp{-a} option causes @code{gprof} to suppress the printing of +statically declared (private) functions. (These are functions whose +names are not listed as global, and which are not visible outside the +file/function/block where they were defined.) Time spent in these +functions, calls to/from them, etc, will all be attributed to the +function that was loaded directly before it in the executable file. +@c This is compatible with Unix @code{gprof}, but a bad idea. +This option affects both the flat profile and the call graph. + +@item -e @var{function_name} +The @samp{-e @var{function}} option tells @code{gprof} to not print +information about the function @var{function_name} (and its +children@dots{}) in the call graph. The function will still be listed +as a child of any functions that call it, but its index number will be +shown as @samp{[not printed]}. More than one @samp{-e} option may be +given; only one @var{function_name} may be indicated with each @samp{-e} +option. + +@item -E @var{function_name} +The @code{-E @var{function}} option works like the @code{-e} option, but +time spent in the function (and children who were not called from +anywhere else), will not be used to compute the percentages-of-time for +the call graph. More than one @samp{-E} option may be given; only one +@var{function_name} may be indicated with each @samp{-E} option. + +@item -f @var{function_name} +The @samp{-f @var{function}} option causes @code{gprof} to limit the +call graph to the function @var{function_name} and its children (and +their children@dots{}). More than one @samp{-f} option may be given; +only one @var{function_name} may be indicated with each @samp{-f} +option. + +@item -F @var{function_name} +The @samp{-F @var{function}} option works like the @code{-f} option, but +only time spent in the function and its children (and their +children@dots{}) will be used to determine total-time and +percentages-of-time for the call graph. More than one @samp{-F} option +may be given; only one @var{function_name} may be indicated with each +@samp{-F} option. The @samp{-F} option overrides the @samp{-E} option. + +@item -k @var{from@dots{}} @var{to@dots{}} +The @samp{-k} option allows you to delete from the profile any arcs from +routine @var{from} to routine @var{to}. + +@item -v +The @samp{-v} flag causes @code{gprof} to print the current version +number, and then exit. + +@item -z +If you give the @samp{-z} option, @code{gprof} will mention all +functions in the flat profile, even those that were never called, and +that had no time spent in them. This is useful in conjunction with the +@samp{-c} option for discovering which routines were never called. +@end table + +The order of these options does not matter. + +Note that only one function can be specified with each @code{-e}, +@code{-E}, @code{-f} or @code{-F} option. To specify more than one +function, use multiple options. For example, this command: + +@example +gprof -e boring -f foo -f bar myprogram > gprof.output +@end example + +@noindent +lists in the call graph all functions that were reached from either +@code{foo} or @code{bar} and were not reachable from @code{boring}. + +There are a few other useful @code{gprof} options: + +@table @code +@item -b +If the @samp{-b} option is given, @code{gprof} doesn't print the +verbose blurbs that try to explain the meaning of all of the fields in +the tables. This is useful if you intend to print out the output, or +are tired of seeing the blurbs. + +@item -c +The @samp{-c} option causes the static call-graph of the program to be +discovered by a heuristic which examines the text space of the object +file. Static-only parents or children are indicated with call counts of +@samp{0}. + +@item -d @var{num} +The @samp{-d @var{num}} option specifies debugging options. +@c @xref{debugging}. + +@item -s +The @samp{-s} option causes @code{gprof} to summarize the information +in the profile data files it read in, and write out a profile data +file called @file{gmon.sum}, which contains all the information from +the profile data files that @code{gprof} read in. The file @file{gmon.sum} +may be one of the specified input files; the effect of this is to +merge the data in the other input files into @file{gmon.sum}. +@xref{Sampling Error}. + +Eventually you can run @code{gprof} again without @samp{-s} to analyze the +cumulative data in the file @file{gmon.sum}. + +@item -T +The @samp{-T} option causes @code{gprof} to print its output in +``traditional'' BSD style. +@end table + +@node Flat Profile +@chapter How to Understand the Flat Profile +@cindex flat profile + +The @dfn{flat profile} shows the total amount of time your program +spent executing each function. Unless the @samp{-z} option is given, +functions with no apparent time spent in them, and no apparent calls +to them, are not mentioned. Note that if a function was not compiled +for profiling, and didn't run long enough to show up on the program +counter histogram, it will be indistinguishable from a function that +was never called. + +This is part of a flat profile for a small program: + +@smallexample +@group +Flat profile: + +Each sample counts as 0.01 seconds. + % cumulative self self total + time seconds seconds calls ms/call ms/call name + 33.34 0.02 0.02 7208 0.00 0.00 open + 16.67 0.03 0.01 244 0.04 0.12 offtime + 16.67 0.04 0.01 8 1.25 1.25 memccpy + 16.67 0.05 0.01 7 1.43 1.43 write + 16.67 0.06 0.01 mcount + 0.00 0.06 0.00 236 0.00 0.00 tzset + 0.00 0.06 0.00 192 0.00 0.00 tolower + 0.00 0.06 0.00 47 0.00 0.00 strlen + 0.00 0.06 0.00 45 0.00 0.00 strchr + 0.00 0.06 0.00 1 0.00 50.00 main + 0.00 0.06 0.00 1 0.00 0.00 memcpy + 0.00 0.06 0.00 1 0.00 10.11 print + 0.00 0.06 0.00 1 0.00 0.00 profil + 0.00 0.06 0.00 1 0.00 50.00 report +@dots{} +@end group +@end smallexample + +@noindent +The functions are sorted by decreasing run-time spent in them. The +functions @samp{mcount} and @samp{profil} are part of the profiling +aparatus and appear in every flat profile; their time gives a measure of +the amount of overhead due to profiling. + +The sampling period estimates the margin of error in each of the time +figures. A time figure that is not much larger than this is not +reliable. In this example, the @samp{self seconds} field for +@samp{mcount} might well be @samp{0} or @samp{0.04} in another run. +@xref{Sampling Error}, for a complete discussion. + +Here is what the fields in each line mean: + +@table @code +@item % time +This is the percentage of the total execution time your program spent +in this function. These should all add up to 100%. + +@item cumulative seconds +This is the cumulative total number of seconds the computer spent +executing this functions, plus the time spent in all the functions +above this one in this table. + +@item self seconds +This is the number of seconds accounted for by this function alone. +The flat profile listing is sorted first by this number. + +@item calls +This is the total number of times the function was called. If the +function was never called, or the number of times it was called cannot +be determined (probably because the function was not compiled with +profiling enabled), the @dfn{calls} field is blank. + +@item self ms/call +This represents the average number of milliseconds spent in this +function per call, if this function is profiled. Otherwise, this field +is blank for this function. + +@item total ms/call +This represents the average number of milliseconds spent in this +function and its descendants per call, if this function is profiled. +Otherwise, this field is blank for this function. + +@item name +This is the name of the function. The flat profile is sorted by this +field alphabetically after the @dfn{self seconds} field is sorted. +@end table + +@node Call Graph +@chapter How to Read the Call Graph +@cindex call graph + +The @dfn{call graph} shows how much time was spent in each function +and its children. From this information, you can find functions that, +while they themselves may not have used much time, called other +functions that did use unusual amounts of time. + +Here is a sample call from a small program. This call came from the +same @code{gprof} run as the flat profile example in the previous +chapter. + +@smallexample +@group +granularity: each sample hit covers 2 byte(s) for 20.00% of 0.05 seconds + +index % time self children called name + <spontaneous> +[1] 100.0 0.00 0.05 start [1] + 0.00 0.05 1/1 main [2] + 0.00 0.00 1/2 on_exit [28] + 0.00 0.00 1/1 exit [59] +----------------------------------------------- + 0.00 0.05 1/1 start [1] +[2] 100.0 0.00 0.05 1 main [2] + 0.00 0.05 1/1 report [3] +----------------------------------------------- + 0.00 0.05 1/1 main [2] +[3] 100.0 0.00 0.05 1 report [3] + 0.00 0.03 8/8 timelocal [6] + 0.00 0.01 1/1 print [9] + 0.00 0.01 9/9 fgets [12] + 0.00 0.00 12/34 strncmp <cycle 1> [40] + 0.00 0.00 8/8 lookup [20] + 0.00 0.00 1/1 fopen [21] + 0.00 0.00 8/8 chewtime [24] + 0.00 0.00 8/16 skipspace [44] +----------------------------------------------- +[4] 59.8 0.01 0.02 8+472 <cycle 2 as a whole> [4] + 0.01 0.02 244+260 offtime <cycle 2> [7] + 0.00 0.00 236+1 tzset <cycle 2> [26] +----------------------------------------------- +@end group +@end smallexample + +The lines full of dashes divide this table into @dfn{entries}, one for each +function. Each entry has one or more lines. + +In each entry, the primary line is the one that starts with an index number +in square brackets. The end of this line says which function the entry is +for. The preceding lines in the entry describe the callers of this +function and the following lines describe its subroutines (also called +@dfn{children} when we speak of the call graph). + +The entries are sorted by time spent in the function and its subroutines. + +The internal profiling function @code{mcount} (@pxref{Flat Profile}) +is never mentioned in the call graph. + +@menu +* Primary:: Details of the primary line's contents. +* Callers:: Details of caller-lines' contents. +* Subroutines:: Details of subroutine-lines' contents. +* Cycles:: When there are cycles of recursion, + such as @code{a} calls @code{b} calls @code{a}@dots{} +@end menu + +@node Primary +@section The Primary Line + +The @dfn{primary line} in a call graph entry is the line that +describes the function which the entry is about and gives the overall +statistics for this function. + +For reference, we repeat the primary line from the entry for function +@code{report} in our main example, together with the heading line that +shows the names of the fields: + +@smallexample +@group +index % time self children called name +@dots{} +[3] 100.0 0.00 0.05 1 report [3] +@end group +@end smallexample + +Here is what the fields in the primary line mean: + +@table @code +@item index +Entries are numbered with consecutive integers. Each function +therefore has an index number, which appears at the beginning of its +primary line. + +Each cross-reference to a function, as a caller or subroutine of +another, gives its index number as well as its name. The index number +guides you if you wish to look for the entry for that function. + +@item % time +This is the percentage of the total time that was spent in this +function, including time spent in subroutines called from this +function. + +The time spent in this function is counted again for the callers of +this function. Therefore, adding up these percentages is meaningless. + +@item self +This is the total amount of time spent in this function. This +should be identical to the number printed in the @code{seconds} field +for this function in the flat profile. + +@item children +This is the total amount of time spent in the subroutine calls made by +this function. This should be equal to the sum of all the @code{self} +and @code{children} entries of the children listed directly below this +function. + +@item called +This is the number of times the function was called. + +If the function called itself recursively, there are two numbers, +separated by a @samp{+}. The first number counts non-recursive calls, +and the second counts recursive calls. + +In the example above, the function @code{report} was called once from +@code{main}. + +@item name +This is the name of the current function. The index number is +repeated after it. + +If the function is part of a cycle of recursion, the cycle number is +printed between the function's name and the index number +(@pxref{Cycles}). For example, if function @code{gnurr} is part of +cycle number one, and has index number twelve, its primary line would +be end like this: + +@example +gnurr <cycle 1> [12] +@end example +@end table + +@node Callers, Subroutines, Primary, Call Graph +@section Lines for a Function's Callers + +A function's entry has a line for each function it was called by. +These lines' fields correspond to the fields of the primary line, but +their meanings are different because of the difference in context. + +For reference, we repeat two lines from the entry for the function +@code{report}, the primary line and one caller-line preceding it, together +with the heading line that shows the names of the fields: + +@smallexample +index % time self children called name +@dots{} + 0.00 0.05 1/1 main [2] +[3] 100.0 0.00 0.05 1 report [3] +@end smallexample + +Here are the meanings of the fields in the caller-line for @code{report} +called from @code{main}: + +@table @code +@item self +An estimate of the amount of time spent in @code{report} itself when it was +called from @code{main}. + +@item children +An estimate of the amount of time spent in subroutines of @code{report} +when @code{report} was called from @code{main}. + +The sum of the @code{self} and @code{children} fields is an estimate +of the amount of time spent within calls to @code{report} from @code{main}. + +@item called +Two numbers: the number of times @code{report} was called from @code{main}, +followed by the total number of nonrecursive calls to @code{report} from +all its callers. + +@item name and index number +The name of the caller of @code{report} to which this line applies, +followed by the caller's index number. + +Not all functions have entries in the call graph; some +options to @code{gprof} request the omission of certain functions. +When a caller has no entry of its own, it still has caller-lines +in the entries of the functions it calls. + +If the caller is part of a recursion cycle, the cycle number is +printed between the name and the index number. +@end table + +If the identity of the callers of a function cannot be determined, a +dummy caller-line is printed which has @samp{<spontaneous>} as the +``caller's name'' and all other fields blank. This can happen for +signal handlers. +@c What if some calls have determinable callers' names but not all? +@c FIXME - still relevant? + +@node Subroutines, Cycles, Callers, Call Graph +@section Lines for a Function's Subroutines + +A function's entry has a line for each of its subroutines---in other +words, a line for each other function that it called. These lines' +fields correspond to the fields of the primary line, but their meanings +are different because of the difference in context. + +For reference, we repeat two lines from the entry for the function +@code{main}, the primary line and a line for a subroutine, together +with the heading line that shows the names of the fields: + +@smallexample +index % time self children called name +@dots{} +[2] 100.0 0.00 0.05 1 main [2] + 0.00 0.05 1/1 report [3] +@end smallexample + +Here are the meanings of the fields in the subroutine-line for @code{main} +calling @code{report}: + +@table @code +@item self +An estimate of the amount of time spent directly within @code{report} +when @code{report} was called from @code{main}. + +@item children +An estimate of the amount of time spent in subroutines of @code{report} +when @code{report} was called from @code{main}. + +The sum of the @code{self} and @code{children} fields is an estimate +of the total time spent in calls to @code{report} from @code{main}. + +@item called +Two numbers, the number of calls to @code{report} from @code{main} +followed by the total number of nonrecursive calls to @code{report}. + +@item name +The name of the subroutine of @code{main} to which this line applies, +followed by the subroutine's index number. + +If the caller is part of a recursion cycle, the cycle number is +printed between the name and the index number. +@end table + +@node Cycles,, Subroutines, Call Graph +@section How Mutually Recursive Functions Are Described +@cindex cycle +@cindex recursion cycle + +The graph may be complicated by the presence of @dfn{cycles of +recursion} in the call graph. A cycle exists if a function calls +another function that (directly or indirectly) calls (or appears to +call) the original function. For example: if @code{a} calls @code{b}, +and @code{b} calls @code{a}, then @code{a} and @code{b} form a cycle. + +Whenever there are call-paths both ways between a pair of functions, they +belong to the same cycle. If @code{a} and @code{b} call each other and +@code{b} and @code{c} call each other, all three make one cycle. Note that +even if @code{b} only calls @code{a} if it was not called from @code{a}, +@code{gprof} cannot determine this, so @code{a} and @code{b} are still +considered a cycle. + +The cycles are numbered with consecutive integers. When a function +belongs to a cycle, each time the function name appears in the call graph +it is followed by @samp{<cycle @var{number}>}. + +The reason cycles matter is that they make the time values in the call +graph paradoxical. The ``time spent in children'' of @code{a} should +include the time spent in its subroutine @code{b} and in @code{b}'s +subroutines---but one of @code{b}'s subroutines is @code{a}! How much of +@code{a}'s time should be included in the children of @code{a}, when +@code{a} is indirectly recursive? + +The way @code{gprof} resolves this paradox is by creating a single entry +for the cycle as a whole. The primary line of this entry describes the +total time spent directly in the functions of the cycle. The +``subroutines'' of the cycle are the individual functions of the cycle, and +all other functions that were called directly by them. The ``callers'' of +the cycle are the functions, outside the cycle, that called functions in +the cycle. + +Here is an example portion of a call graph which shows a cycle containing +functions @code{a} and @code{b}. The cycle was entered by a call to +@code{a} from @code{main}; both @code{a} and @code{b} called @code{c}. + +@smallexample +index % time self children called name +---------------------------------------- + 1.77 0 1/1 main [2] +[3] 91.71 1.77 0 1+5 <cycle 1 as a whole> [3] + 1.02 0 3 b <cycle 1> [4] + 0.75 0 2 a <cycle 1> [5] +---------------------------------------- + 3 a <cycle 1> [5] +[4] 52.85 1.02 0 0 b <cycle 1> [4] + 2 a <cycle 1> [5] + 0 0 3/6 c [6] +---------------------------------------- + 1.77 0 1/1 main [2] + 2 b <cycle 1> [4] +[5] 38.86 0.75 0 1 a <cycle 1> [5] + 3 b <cycle 1> [4] + 0 0 3/6 c [6] +---------------------------------------- +@end smallexample + +@noindent +(The entire call graph for this program contains in addition an entry for +@code{main}, which calls @code{a}, and an entry for @code{c}, with callers +@code{a} and @code{b}.) + +@smallexample +index % time self children called name + <spontaneous> +[1] 100.00 0 1.93 0 start [1] + 0.16 1.77 1/1 main [2] +---------------------------------------- + 0.16 1.77 1/1 start [1] +[2] 100.00 0.16 1.77 1 main [2] + 1.77 0 1/1 a <cycle 1> [5] +---------------------------------------- + 1.77 0 1/1 main [2] +[3] 91.71 1.77 0 1+5 <cycle 1 as a whole> [3] + 1.02 0 3 b <cycle 1> [4] + 0.75 0 2 a <cycle 1> [5] + 0 0 6/6 c [6] +---------------------------------------- + 3 a <cycle 1> [5] +[4] 52.85 1.02 0 0 b <cycle 1> [4] + 2 a <cycle 1> [5] + 0 0 3/6 c [6] +---------------------------------------- + 1.77 0 1/1 main [2] + 2 b <cycle 1> [4] +[5] 38.86 0.75 0 1 a <cycle 1> [5] + 3 b <cycle 1> [4] + 0 0 3/6 c [6] +---------------------------------------- + 0 0 3/6 b <cycle 1> [4] + 0 0 3/6 a <cycle 1> [5] +[6] 0.00 0 0 6 c [6] +---------------------------------------- +@end smallexample + +The @code{self} field of the cycle's primary line is the total time +spent in all the functions of the cycle. It equals the sum of the +@code{self} fields for the individual functions in the cycle, found +in the entry in the subroutine lines for these functions. + +The @code{children} fields of the cycle's primary line and subroutine lines +count only subroutines outside the cycle. Even though @code{a} calls +@code{b}, the time spent in those calls to @code{b} is not counted in +@code{a}'s @code{children} time. Thus, we do not encounter the problem of +what to do when the time in those calls to @code{b} includes indirect +recursive calls back to @code{a}. + +The @code{children} field of a caller-line in the cycle's entry estimates +the amount of time spent @emph{in the whole cycle}, and its other +subroutines, on the times when that caller called a function in the cycle. + +The @code{calls} field in the primary line for the cycle has two numbers: +first, the number of times functions in the cycle were called by functions +outside the cycle; second, the number of times they were called by +functions in the cycle (including times when a function in the cycle calls +itself). This is a generalization of the usual split into nonrecursive and +recursive calls. + +The @code{calls} field of a subroutine-line for a cycle member in the +cycle's entry says how many time that function was called from functions in +the cycle. The total of all these is the second number in the primary line's +@code{calls} field. + +In the individual entry for a function in a cycle, the other functions in +the same cycle can appear as subroutines and as callers. These lines show +how many times each function in the cycle called or was called from each other +function in the cycle. The @code{self} and @code{children} fields in these +lines are blank because of the difficulty of defining meanings for them +when recursion is going on. + +@node Implementation, Sampling Error, Call Graph, Top +@chapter Implementation of Profiling + +Profiling works by changing how every function in your program is compiled +so that when it is called, it will stash away some information about where +it was called from. From this, the profiler can figure out what function +called it, and can count how many times it was called. This change is made +by the compiler when your program is compiled with the @samp{-pg} option. + +Profiling also involves watching your program as it runs, and keeping a +histogram of where the program counter happens to be every now and then. +Typically the program counter is looked at around 100 times per second of +run time, but the exact frequency may vary from system to system. + +A special startup routine allocates memory for the histogram and sets up +a clock signal handler to make entries in it. Use of this special +startup routine is one of the effects of using @samp{gcc @dots{} -pg} to +link. The startup file also includes an @samp{exit} function which is +responsible for writing the file @file{gmon.out}. + +Number-of-calls information for library routines is collected by using a +special version of the C library. The programs in it are the same as in +the usual C library, but they were compiled with @samp{-pg}. If you +link your program with @samp{gcc @dots{} -pg}, it automatically uses the +profiling version of the library. + +The output from @code{gprof} gives no indication of parts of your program that +are limited by I/O or swapping bandwidth. This is because samples of the +program counter are taken at fixed intervals of run time. Therefore, the +time measurements in @code{gprof} output say nothing about time that your +program was not running. For example, a part of the program that creates +so much data that it cannot all fit in physical memory at once may run very +slowly due to thrashing, but @code{gprof} will say it uses little time. On +the other hand, sampling by run time has the advantage that the amount of +load due to other users won't directly affect the output you get. + +@node Sampling Error, Assumptions, Implementation, Top +@chapter Statistical Inaccuracy of @code{gprof} Output + +The run-time figures that @code{gprof} gives you are based on a sampling +process, so they are subject to statistical inaccuracy. If a function runs +only a small amount of time, so that on the average the sampling process +ought to catch that function in the act only once, there is a pretty good +chance it will actually find that function zero times, or twice. + +By contrast, the number-of-calls figures are derived by counting, not +sampling. They are completely accurate and will not vary from run to run +if your program is deterministic. + +The @dfn{sampling period} that is printed at the beginning of the flat +profile says how often samples are taken. The rule of thumb is that a +run-time figure is accurate if it is considerably bigger than the sampling +period. + +The actual amount of error is usually more than one sampling period. In +fact, if a value is @var{n} times the sampling period, the @emph{expected} +error in it is the square-root of @var{n} sampling periods. If the +sampling period is 0.01 seconds and @code{foo}'s run-time is 1 second, the +expected error in @code{foo}'s run-time is 0.1 seconds. It is likely to +vary this much @emph{on the average} from one profiling run to the next. +(@emph{Sometimes} it will vary more.) + +This does not mean that a small run-time figure is devoid of information. +If the program's @emph{total} run-time is large, a small run-time for one +function does tell you that that function used an insignificant fraction of +the whole program's time. Usually this means it is not worth optimizing. + +One way to get more accuracy is to give your program more (but similar) +input data so it will take longer. Another way is to combine the data from +several runs, using the @samp{-s} option of @code{gprof}. Here is how: + +@enumerate +@item +Run your program once. + +@item +Issue the command @samp{mv gmon.out gmon.sum}. + +@item +Run your program again, the same as before. + +@item +Merge the new data in @file{gmon.out} into @file{gmon.sum} with this command: + +@example +gprof -s @var{executable-file} gmon.out gmon.sum +@end example + +@item +Repeat the last two steps as often as you wish. + +@item +Analyze the cumulative data using this command: + +@example +gprof @var{executable-file} gmon.sum > @var{output-file} +@end example +@end enumerate + +@node Assumptions, Incompatibilities, Sampling Error, Top +@chapter Estimating @code{children} Times Uses an Assumption + +Some of the figures in the call graph are estimates---for example, the +@code{children} time values and all the the time figures in caller and +subroutine lines. + +There is no direct information about these measurements in the profile +data itself. Instead, @code{gprof} estimates them by making an assumption +about your program that might or might not be true. + +The assumption made is that the average time spent in each call to any +function @code{foo} is not correlated with who called @code{foo}. If +@code{foo} used 5 seconds in all, and 2/5 of the calls to @code{foo} came +from @code{a}, then @code{foo} contributes 2 seconds to @code{a}'s +@code{children} time, by assumption. + +This assumption is usually true enough, but for some programs it is far +from true. Suppose that @code{foo} returns very quickly when its argument +is zero; suppose that @code{a} always passes zero as an argument, while +other callers of @code{foo} pass other arguments. In this program, all the +time spent in @code{foo} is in the calls from callers other than @code{a}. +But @code{gprof} has no way of knowing this; it will blindly and +incorrectly charge 2 seconds of time in @code{foo} to the children of +@code{a}. + +@c FIXME - has this been fixed? +We hope some day to put more complete data into @file{gmon.out}, so that +this assumption is no longer needed, if we can figure out how. For the +nonce, the estimated figures are usually more useful than misleading. + +@node Incompatibilities, , Assumptions, Top +@chapter Incompatibilities with Unix @code{gprof} + +@sc{gnu} @code{gprof} and Berkeley Unix @code{gprof} use the same data +file @file{gmon.out}, and provide essentially the same information. But +there are a few differences. + +@itemize @bullet +@item +For a recursive function, Unix @code{gprof} lists the function as a +parent and as a child, with a @code{calls} field that lists the number +of recursive calls. @sc{gnu} @code{gprof} omits these lines and puts +the number of recursive calls in the primary line. + +@item +When a function is suppressed from the call graph with @samp{-e}, @sc{gnu} +@code{gprof} still lists it as a subroutine of functions that call it. + +@ignore - it does this now +@item +The function names printed in @sc{gnu} @code{gprof} output do not include +the leading underscores that are added internally to the front of all +C identifiers on many operating systems. +@end ignore + +@item +The blurbs, field widths, and output formats are different. @sc{gnu} +@code{gprof} prints blurbs after the tables, so that you can see the +tables without skipping the blurbs. + +@contents +@bye + +NEEDS AN INDEX + +Still relevant? + The @file{gmon.out} file is written in the program's @emph{current working + directory} at the time it exits. This means that if your program calls + @code{chdir}, the @file{gmon.out} file will be left in the last directory + your program @code{chdir}'d to. If you don't have permission to write in + this directory, the file is not written. You may get a confusing error + message if this happens. (We have not yet replaced the part of Unix + responsible for this; when we do, we will make the error message + comprehensible.) + +-k from to...? + +-d debugging...? should this be documented? + +-T - "traditional BSD style": How is it different? Should the +differences be documented? + +what is this about? (and to think, I *wrote* it...) + @item -c + The @samp{-c} option causes the static call-graph of the program to be + discovered by a heuristic which examines the text space of the object + file. Static-only parents or children are indicated with call counts of + @samp{0}. + +example flat file adds up to 100.01%... + +note: time estimates now only go out to one decimal place (0.0), where +they used to extend two (78.67). diff --git a/gnu/usr.bin/binutils/gprof/hertz.c b/gnu/usr.bin/binutils/gprof/hertz.c new file mode 100644 index 00000000000..9dbc89ffc61 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/hertz.c @@ -0,0 +1,47 @@ +/* + * 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 <sys/time.h> +#include "hertz.h" + + +#ifdef __MSDOS__ +#define HERTZ 18 +#endif + +int +hertz () +{ +#ifdef HERTZ + return HERTZ; +#else + 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; +#endif +} diff --git a/gnu/usr.bin/binutils/gprof/hertz.h b/gnu/usr.bin/binutils/gprof/hertz.h new file mode 100644 index 00000000000..d5a23d3224a --- /dev/null +++ b/gnu/usr.bin/binutils/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/gnu/usr.bin/binutils/gprof/hist.c b/gnu/usr.bin/binutils/gprof/hist.c new file mode 100644 index 00000000000..69cc3ea004a --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/hist.c @@ -0,0 +1,594 @@ +/* + * Histogram related operations. + */ +#include <stdio.h> +#include "libiberty.h" +#include "gprof.h" +#include "core.h" +#include "gmon_io.h" +#include "gmon_out.h" +#include "hist.h" +#include "symtab.h" +#include "sym_ids.h" +#include "utils.h" + +/* declarations of automatically generated functions to output blurbs: */ +extern void flat_blurb PARAMS ((FILE * fp)); + +bfd_vma s_lowpc; /* lowest address in .text */ +bfd_vma s_highpc = 0; /* highest address in .text */ +bfd_vma lowpc, highpc; /* same, but expressed in UNITs */ +int hist_num_bins = 0; /* number of histogram samples */ +int *hist_sample = 0; /* histogram samples (shorts in the file!) */ +double hist_scale; +char hist_dimension[sizeof (((struct gmon_hist_hdr *) 0)->dimen) + 1] = + "seconds"; +char hist_dimension_abbrev = 's'; + +static double accum_time; /* accumulated time so far for print_line() */ +static double total_time; /* total time for all routines */ +/* + * Table of SI prefixes for powers of 10 (used to automatically + * scale some of the values in the flat profile). + */ +const struct + { + char prefix; + double scale; + } +SItab[] = +{ + { + 'T', 1e-12 + } + , /* tera */ + { + 'G', 1e-09 + } + , /* giga */ + { + 'M', 1e-06 + } + , /* mega */ + { + 'K', 1e-03 + } + , /* kilo */ + { + ' ', 1e-00 + } + , + { + 'm', 1e+03 + } + , /* milli */ + { + 'u', 1e+06 + } + , /* micro */ + { + 'n', 1e+09 + } + , /* nano */ + { + 'p', 1e+12 + } + , /* pico */ + { + 'f', 1e+15 + } + , /* femto */ + { + 'a', 1e+18 + } + , /* ato */ +}; + +/* + * Read the histogram from file IFP. FILENAME is the name of IFP and + * is provided for formatting error messages only. + */ +void +DEFUN (hist_read_rec, (ifp, filename), FILE * ifp AND const char *filename) +{ + struct gmon_hist_hdr hdr; + bfd_vma n_lowpc, n_highpc; + int i, ncnt, profrate; + UNIT count; + + if (fread (&hdr, sizeof (hdr), 1, ifp) != 1) + { + fprintf (stderr, "%s: %s: unexpected end of file\n", + whoami, filename); + done (1); + } + + 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; + } + + 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 (!hist_sample) + { + hist_sample = (int *) xmalloc (hist_num_bins * sizeof (hist_sample[0])); + memset (hist_sample, 0, hist_num_bins * sizeof (hist_sample[0])); + } + + 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); + } + hist_sample[i] += bfd_get_16 (core_bfd, (bfd_byte *) & count[0]); + } +} + + +/* + * 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); + } + + 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); + } + } +} + + +/* + * 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; + } +#endif /* OFFSET_TO_CODE > 0 */ + } +} + + +/* + * 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; + } + 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 low end of bin is above high end of symbol, go for + * next symbol. + */ + if (bin_low_pc >= sym_high_pc) + { + continue; + } + 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; + } + } + } + } + DBG (SAMPLEDEBUG, printf ("[assign_samples] total_time %f\n", + total_time)); +} + + +/* + * 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); + } + } + else + { + printf ("\nEach sample counts as %g %s.\n", 1.0 / hz, hist_dimension); + } + + 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; + } + + 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"); +} + + +static void +DEFUN (print_line, (sym, scale), Sym * sym AND double scale) +{ + if (ignore_zeros && sym->ncalls == 0 && sym->hist.time == 0) + { + return; + } + + 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 (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 (bsd_style_output) + { + print_name (sym); + } + else + { + print_name_only (sym); + } + printf ("\n"); +} + + +/* + * 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 (time_diff < 0.0) + { + return -1; + } + + call_diff = right->ncalls - left->ncalls; + if (call_diff > 0) + { + return 1; + } + if (call_diff < 0) + { + return -1; + } + + return strcmp (left->name, right->name); +} + + +/* + * 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"); + } + + accum_time = 0.0; + if (bsd_style_output) + { + if (print_descriptions) + { + printf ("\n\n\nflat profile:\n"); + flat_blurb (stdout); + } + } + else + { + printf ("Flat profile:\n"); + } + /* + * 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]; + } + 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 (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; + } + } + } + + /* + * 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); + } + } + free (time_sorted_syms); + + if (print_descriptions && !bsd_style_output) + { + flat_blurb (stdout); + } +} diff --git a/gnu/usr.bin/binutils/gprof/hist.h b/gnu/usr.bin/binutils/gprof/hist.h new file mode 100644 index 00000000000..df62ef770f4 --- /dev/null +++ b/gnu/usr.bin/binutils/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/gnu/usr.bin/binutils/gprof/i386.c b/gnu/usr.bin/binutils/gprof/i386.c new file mode 100644 index 00000000000..01202a7c854 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/i386.c @@ -0,0 +1,110 @@ +/* + * 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" + + +int +DEFUN (iscall, (ip), unsigned char *ip) +{ + if (*ip == 0xeb || *ip == 0x9a) + return 1; + return 0; +} + + +void +find_call (parent, p_lowpc, p_highpc) + Sym *parent; + bfd_vma p_lowpc; + bfd_vma p_highpc; +{ + unsigned char *instructp; + long length; + Sym *child; + bfd_vma destpc; + + if (core_text_space == 0) + { + return; + } + if (p_lowpc < s_lowpc) + { + p_lowpc = s_lowpc; + } + if (p_highpc > s_highpc) + { + p_highpc = s_highpc; + } + DBG (CALLDEBUG, printf ("[findcall] %s: 0x%lx to 0x%lx\n", + parent->name, p_lowpc, p_highpc)); + for (instructp = (unsigned char *) core_text_space + p_lowpc; + instructp < (unsigned char *) core_text_space + p_highpc; + instructp += length) + { + length = 1; + if (iscall (instructp)) + { + 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 = ((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 + */ + arc_add (parent, child, (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. + */ + DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n")); + length = 1; + continue; + } + } +} diff --git a/gnu/usr.bin/binutils/gprof/i386.h b/gnu/usr.bin/binutils/gprof/i386.h new file mode 100644 index 00000000000..f85f55cbbd9 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/i386.h @@ -0,0 +1,38 @@ +/* + * 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. + * + * @(#)tahoe.h 1.4 (Berkeley) 6/1/90 + */ + +/* + * Right now, this does very little + */ + + /* + * opcode of the `callf' instruction + */ + /* + * offset (in bytes) of the code from the entry address of a routine. + * (see asgnsamples for use and explanation.) + */ +#define OFFSET_TO_CODE 0 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) + +#ifdef __MSDOS__ +#define FOPEN_RB "rb" +#endif diff --git a/gnu/usr.bin/binutils/gprof/ns532.c b/gnu/usr.bin/binutils/gprof/ns532.c new file mode 100644 index 00000000000..c68fae4473c --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/ns532.c @@ -0,0 +1,17 @@ +#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. + */ + +void +find_call (parent, p_lowpc, p_highpc) + Sym *parent; + bfd_vma p_lowpc; + bfd_vma p_highpc; +{ + fprintf (stderr, "%s: -c supported on this machine architecture\n", + whoami); +} diff --git a/gnu/usr.bin/binutils/gprof/ns532.h b/gnu/usr.bin/binutils/gprof/ns532.h new file mode 100644 index 00000000000..2fa22334e03 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/ns532.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dummy.h 5.1 (Berkeley) 4/18/91 + */ + +/* + * offset (in bytes) of the code from the entry address of a routine. + * (see asgnsamples for use and explanation.) + */ +#ifdef MACH +#include <machine/mach_param.h> +#define hertz() (HZ) +#endif +#define OFFSET_OF_CODE 0 +#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT)) + +enum opermodes + { + dummy + }; +typedef enum opermodes operandenum; diff --git a/gnu/usr.bin/binutils/gprof/search_list.c b/gnu/usr.bin/binutils/gprof/search_list.c new file mode 100644 index 00000000000..d475dbf6a48 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/search_list.c @@ -0,0 +1,44 @@ +#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); + } + 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; + } + list->tail = new_el; + } + while (colon); +} diff --git a/gnu/usr.bin/binutils/gprof/search_list.h b/gnu/usr.bin/binutils/gprof/search_list.h new file mode 100644 index 00000000000..54dfe3590f7 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/search_list.h @@ -0,0 +1,20 @@ +#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/gnu/usr.bin/binutils/gprof/source.c b/gnu/usr.bin/binutils/gprof/source.c new file mode 100644 index 00000000000..b0c6ecdb479 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/source.c @@ -0,0 +1,223 @@ +/* + * 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 (!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; + } + return sf; +} + + +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 (strcmp (filename, fname) == 0) + { + break; + } + } + return sf; +} + + +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 */ + } + 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 (!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 (sle) + { + strcpy (fname, sle->path); + strcat (fname, "/"); + if (name_only) + { + strcat (fname, name_only); + } + else + { + strcat (fname, sf->name); + } + sle = sle->next; + } + else + { + if (errno == ENOENT) + { + fprintf (stderr, "%s: could not locate `%s'\n", + whoami, sf->name); + } + else + { + perror (sf->name); + } + return 0; + } + } + + 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; + } + + strcpy (fname, filename); + strcat (fname, EXT_ANNO); + ofp = fopen (fname, "w"); + if (!ofp) + { + perror (fname); + return 0; + } + } + + /* + * 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 (first_output) + { + first_output = FALSE; + } + else + { + fprintf (ofp, "\f\n"); + } + fprintf (ofp, "*** File %s:\n", sf->name); + } + + 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; + } + new_line = (buf[i] == '\n'); + fputc (buf[i], ofp); + } + } + free (annotation); + return ofp; +} diff --git a/gnu/usr.bin/binutils/gprof/source.h b/gnu/usr.bin/binutils/gprof/source.h new file mode 100644 index 00000000000..ebdbb3b91b2 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/source.h @@ -0,0 +1,55 @@ +#ifndef source_h +#define source_h + +#include <stdio.h> +#include "gprof.h" +#include "search_list.h" + +typedef struct source_file + { + struct source_file *next; + const char *name; /* name of source file */ + int ncalls; /* # of "calls" to this file */ + int num_lines; /* # of lines in file */ + int nalloced; /* number of lines allocated */ + void **line; /* usage-dependent per-line data */ + } +Source_File; + +/* + * Options: + */ +extern bool create_annotation_files; /* create annotated output files? */ + +/* + * List of directories to search for source files: + */ +extern Search_List src_search_list; + +/* + * Chain of source-file descriptors: + */ +extern Source_File *first_src_file; + +/* + * Returns pointer to source file descriptor for PATH/FILENAME. + */ +extern Source_File *source_file_lookup_path PARAMS ((const char *path)); +extern Source_File *source_file_lookup_name PARAMS ((const char *filename)); + +/* + * Read source file SF output annotated source. The annotation is at + * MAX_WIDTH characters wide and for each source-line an annotation is + * obtained by invoking function ANNOTE. ARG is an argument passed to + * ANNOTE that is left uninterpreted by annotate_source(). + * + * Returns a pointer to the output file (which maybe stdout) such + * that summary statistics can be printed. If the returned file + * is not stdout, it should be closed when done with it. + */ +extern FILE *annotate_source PARAMS ((Source_File * sf, int max_width, + void (*annote) (char *b, int w, int l, + void *arg), + void *arg)); + +#endif /* source_h */ diff --git a/gnu/usr.bin/binutils/gprof/sparc.c b/gnu/usr.bin/binutils/gprof/sparc.c new file mode 100644 index 00000000000..b12420ae80a --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/sparc.c @@ -0,0 +1,84 @@ +/* + * 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" + + +void +find_call (parent, p_lowpc, p_highpc) + Sym *parent; + bfd_vma p_lowpc; + bfd_vma p_highpc; +{ + 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) + { + p_lowpc = s_lowpc; + } + if (p_highpc > s_highpc) + { + p_highpc = s_highpc; + } + 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; + } + } + /* + * Something funny going on. + */ + DBG (CALLDEBUG, printf ("\tbut it's a botch\n")); + } + } +} diff --git a/gnu/usr.bin/binutils/gprof/sparc.h b/gnu/usr.bin/binutils/gprof/sparc.h new file mode 100644 index 00000000000..48e5d230aa7 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/sparc.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. + * + * @(#)tahoe.h 1.4 (Berkeley) 6/1/90 + */ + + /* + * opcode of the `callf' instruction + */ +#define CALL (0xc0000000) + /* + * offset (in bytes) of the code from the entry address of a routine. + * (see asgnsamples for use and explanation.) + */ +#define OFFSET_TO_CODE 0 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) diff --git a/gnu/usr.bin/binutils/gprof/sym_ids.c b/gnu/usr.bin/binutils/gprof/sym_ids.c new file mode 100644 index 00000000000..9c809b64cc4 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/sym_ids.c @@ -0,0 +1,372 @@ +#include <ctype.h> + +#include "libiberty.h" +#include "cg_arcs.h" +#include "sym_ids.h" + +struct sym_id + { + struct sym_id *next; + char *spec; /* parsing modifies this */ + Table_Id which_table; + bool has_right; + struct match + { + int prev_index; /* index of prev match */ + Sym *prev_match; /* previous match */ + Sym *first_match; /* chain of all matches */ + Sym sym; + } + left, right; + } + *id_list; + +Sym_Table syms[NUM_TABLES]; + +#ifdef DEBUG +const char *table_name[] = +{ + "INCL_GRAPH", "EXCL_GRAPH", + "INCL_ARCS", "EXCL_ARCS", + "INCL_FLAT", "EXCL_FLAT", + "INCL_TIME", "EXCL_TIME", + "INCL_ANNO", "EXCL_ANNO", + "INCL_EXEC", "EXCL_EXEC" +}; +#endif /* DEBUG */ + +/* + * This is the table in which we keep all the syms that match + * the right half of an arc id. It is NOT sorted according + * to the addresses, because it is accessed only through + * the left half's CHILDREN pointers (so it's crucial not + * to reorder this table once pointers into it exist). + */ +static Sym_Table right_ids; + +static Source_File non_existent_file = +{ + 0, "<non-existent-file>" +}; + + +void +DEFUN (sym_id_add, (spec, which_table), + const char *spec AND Table_Id which_table) +{ + struct sym_id *id; + int len = strlen (spec); + + id = (struct sym_id *) xmalloc (sizeof (*id) + len + 1); + memset (id, 0, sizeof (*id)); + + id->spec = (char *) id + sizeof (*id); + strcpy (id->spec, spec); + id->which_table = which_table; + + id->next = id_list; + id_list = id; +} + + +/* + * 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; + } + } + spec = colon + 1; + if (strlen (spec)) + { + if (isdigit (spec[0])) + { + sym->line_num = atoi (spec); + } + else + { + sym->name = spec; + } + } + } + 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; + } + } + else if (isdigit (*spec)) + { + sym->line_num = atoi (spec); + } + else if (strlen (spec)) + { + sym->name = 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; + } + parse_spec (id->spec, &id->left.sym); + +#ifdef DEBUG + if (debug_level & 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 (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 ("*"); + } + } + printf ("\n"); + } +#endif +} + + +/* + * 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); +} + + +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]; + } + ++tab->len; + } + + /* extend match to include this symbol: */ + if (second_pass) + { + tab->base[m->prev_index].end_addr = sym->end_addr; + } + m->prev_match = sym; +} + + +/* + * 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); + } + + /* 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 (id->has_right && match (&id->right.sym, sym)) + { + extend_match (&id->right, sym, &right_ids, FALSE); + } + } + } + + /* 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 (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; + } + + /* 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 (id->has_right && match (&id->right.sym, sym)) + { + extend_match (&id->right, sym, &right_ids, TRUE); + } + } + } + + /* 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); + } + } + } + } + + /* 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); + } +} + + +/* + * 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; + } + } + return FALSE; +} diff --git a/gnu/usr.bin/binutils/gprof/sym_ids.h b/gnu/usr.bin/binutils/gprof/sym_ids.h new file mode 100644 index 00000000000..90963381b22 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/sym_ids.h @@ -0,0 +1,25 @@ +#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/gnu/usr.bin/binutils/gprof/symtab.c b/gnu/usr.bin/binutils/gprof/symtab.c new file mode 100644 index 00000000000..6fd48c3c8f5 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/symtab.c @@ -0,0 +1,265 @@ +#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; +} + + +/* + * 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 (left->is_func != right->is_func) + { + return right->is_func - left->is_func; + } + + return left->is_static - right->is_static; +} + + +void +DEFUN (symtab_finalize, (tab), Sym_Table * tab) +{ + Sym *src, *dst; + bfd_vma prev_addr; + + if (!tab->len) + { + return; + } + + /* + * 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)); + } + } + else + { + if (dst > tab->base && dst[-1].end_addr == 0) + { + dst[-1].end_addr = src->addr - 1; + } + + /* 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 (tab->len > 0 && dst[-1].end_addr == 0) + { + dst[-1].end_addr = core_text_sect->vma + core_text_sect->_raw_size - 1; + } + + 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); + } + ); +} + + +#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 (sym[mid].addr > address) + { + high = mid; + } + else + { + low = mid + 1; + } + } + fprintf (stderr, "[sym_lookup] binary search fails???\n"); + return 0; +} + +#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; + } + + 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 (sym[mid].addr > address) + { + high = mid; + } + else + { + low = mid + 1; + } + } + 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]; + } + } + return 0; +} diff --git a/gnu/usr.bin/binutils/gprof/symtab.h b/gnu/usr.bin/binutils/gprof/symtab.h new file mode 100644 index 00000000000..07c7751ed6d --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/symtab.h @@ -0,0 +1,100 @@ +#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/gnu/usr.bin/binutils/gprof/tahoe.c b/gnu/usr.bin/binutils/gprof/tahoe.c new file mode 100644 index 00000000000..967ea019574 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/tahoe.c @@ -0,0 +1,319 @@ +/* + * 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 "time_host.h" + +/* + * A symbol to be the child of indirect callf: + */ +Sym indirectchild; + + +operandenum +operandmode (modep) + unsigned char *modep; +{ + long usesreg = ((long) *modep) & 0xf; + + switch (((long) *modep) >> 4) + { + case 0: + case 1: + case 2: + case 3: + return literal; + case 4: + return indexed; + case 5: + return reg; + case 6: + return regdef; + case 7: + return autodec; + case 8: + return usesreg != 0xe ? autoinc : immediate; + case 9: + return usesreg != PC ? autoincdef : absolute; + case 10: + return usesreg != PC ? bytedisp : byterel; + case 11: + return usesreg != PC ? bytedispdef : bytereldef; + case 12: + return usesreg != PC ? worddisp : wordrel; + case 13: + return usesreg != PC ? worddispdef : wordreldef; + case 14: + return usesreg != PC ? longdisp : longrel; + case 15: + return usesreg != PC ? longdispdef : longreldef; + } + /* NOTREACHED */ +} + +char * +operandname (mode) + operandenum mode; +{ + + switch (mode) + { + case literal: + return "literal"; + case indexed: + return "indexed"; + case reg: + return "register"; + case regdef: + return "register deferred"; + case autodec: + return "autodecrement"; + case autoinc: + return "autoincrement"; + case autoincdef: + return "autoincrement deferred"; + case bytedisp: + return "byte displacement"; + case bytedispdef: + return "byte displacement deferred"; + case byterel: + return "byte relative"; + case bytereldef: + return "byte relative deferred"; + case worddisp: + return "word displacement"; + case worddispdef: + return "word displacement deferred"; + case wordrel: + return "word relative"; + case wordreldef: + return "word relative deferred"; + case immediate: + return "immediate"; + case absolute: + return "absolute"; + case longdisp: + return "long displacement"; + case longdispdef: + return "long displacement deferred"; + case longrel: + return "long relative"; + case longreldef: + return "long relative deferred"; + } + /* NOTREACHED */ +} + +long +operandlength (modep) + unsigned char *modep; +{ + + switch (operandmode (modep)) + { + case literal: + case reg: + case regdef: + case autodec: + case autoinc: + case autoincdef: + return 1; + case bytedisp: + case bytedispdef: + case byterel: + case bytereldef: + return 2; + case worddisp: + case worddispdef: + case wordrel: + case wordreldef: + return 3; + case immediate: + case absolute: + case longdisp: + case longdispdef: + case longrel: + case longreldef: + return 5; + case indexed: + return 1 + operandlength (modep + 1); + } + /* NOTREACHED */ +} + +bfd_vma +reladdr (modep) + char *modep; +{ + operandenum mode = operandmode (modep); + char *cp; + short *sp; + long *lp; + int i; + long value = 0; + + cp = modep; + ++cp; /* skip over the mode */ + switch (mode) + { + default: + fprintf (stderr, "[reladdr] not relative address\n"); + return (bfd_vma) modep; + case byterel: + return (bfd_vma) (cp + sizeof *cp + *cp); + case wordrel: + for (i = 0; i < sizeof *sp; i++) + value = (value << 8) + (cp[i] & 0xff); + return (bfd_vma) (cp + sizeof *sp + value); + case longrel: + for (i = 0; i < sizeof *lp; i++) + value = (value << 8) + (cp[i] & 0xff); + return (bfd_vma) (cp + sizeof *lp + value); + } +} + +find_call (parent, p_lowpc, p_highpc) + Sym *parent; + bfd_vma p_lowpc; + bfd_vma p_highpc; +{ + unsigned char *instructp; + long length; + Sym *child; + operandenum mode; + operandenum firstmode; + bfd_vma destpc; + static bool inited = FALSE; + + if (!inited) + { + inited = TRUE; + sym_init (&indirectchild); + indirectchild.cg.prop.fract = 1.0; + indirectchild.cg.cyc.head = &indirectchild; + } + + if (textspace == 0) + { + return; + } + if (p_lowpc < s_lowpc) + { + p_lowpc = s_lowpc; + } + if (p_highpc > s_highpc) + { + p_highpc = s_highpc; + } + 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) + { + length = 1; + if (*instructp == CALLF) + { + /* + * maybe a callf, better check it out. + * skip the count of the number of arguments. + */ + 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); + 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: + case longdispdef: + case bytereldef: + case wordreldef: + case longreldef: + /* + * indirect call: call through pointer + * either *d(r) as a parameter or local + * (r) as a return value + * *f as a global pointer + * [are there others that we miss?, + * e.g. arrays of pointers to functions???] + */ + arc_add (parent, &indirectchild, (long) 0); + length += operandlength (instructp + length); + continue; + case byterel: + case wordrel: + case longrel: + /* + * regular pc relative addressing + * check that this is the address of + * a function. + */ + 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 + */ + arc_add (parent, child, (long) 0); + length += operandlength (instructp + length); + continue; + } + goto botched; + } + /* + * else: + * it looked like a callf, + * but it wasn't to anywhere. + */ + goto botched; + default: + botched: + /* + * something funny going on. + */ + DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n")); + length = 1; + continue; + } + } + } +} diff --git a/gnu/usr.bin/binutils/gprof/tahoe.h b/gnu/usr.bin/binutils/gprof/tahoe.h new file mode 100644 index 00000000000..4d153a44f54 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/tahoe.h @@ -0,0 +1,46 @@ +/* + * 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. + * + * @(#)tahoe.h 1.4 (Berkeley) 6/1/90 + */ + + /* + * opcode of the `callf' instruction + */ +#define CALLF 0xfe + + /* + * offset (in bytes) of the code from the entry address of a routine. + * (see asgnsamples for use and explanation.) + */ +#define OFFSET_TO_CODE 2 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) + + /* + * register for pc relative addressing + */ +#define PC 0xf + +enum opermodes + { + literal, indexed, reg, regdef, autodec, autoinc, autoincdef, + bytedisp, bytedispdef, worddisp, worddispdef, longdisp, longdispdef, + immediate, absolute, byterel, bytereldef, wordrel, wordreldef, + longrel, longreldef + }; +typedef enum opermodes operandenum; diff --git a/gnu/usr.bin/binutils/gprof/utils.c b/gnu/usr.bin/binutils/gprof/utils.c new file mode 100644 index 00000000000..2402fe4ee62 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/utils.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +#include <demangle.h> +#include "gprof.h" +#include "cg_arcs.h" +#include "symtab.h" + + +/* + * Print name of symbol. Return number of characters printed. + */ +int +DEFUN (print_name_only, (self), Sym * self) +{ + const char *name = self->name; + const char *filename; + char *demangled = 0; + char buf[PATH_MAX]; + int size = 0; + + if (name) + { + if (!bsd_style_output) + { + if (name[0] == '_' && name[1] && discard_underscores) + { + name++; + } + demangled = cplus_demangle (name, DMGL_ANSI | DMGL_PARAMS); + if (demangled) + { + name = demangled; + } + } + 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; + } + } + sprintf (buf, " (%s:%d)", filename, self->line_num); + printf (buf); + size += strlen (buf); + } + if (demangled) + { + free (demangled); + } + DBG (DFNDEBUG, printf ("{%d} ", self->cg.top_order)); + DBG (PROPDEBUG, printf ("%4.0f%% ", 100.0 * self->cg.prop.fract)); + } + return size; +} + + +void +DEFUN (print_name, (self), Sym * self) +{ + print_name_only (self); + + if (self->cg.cyc.num != 0) + { + printf (" <cycle %d>", self->cg.cyc.num); + } + if (self->cg.index != 0) + { + if (self->cg.print_flag) + { + printf (" [%d]", self->cg.index); + } + else + { + printf (" (%d)", self->cg.index); + } + } +} diff --git a/gnu/usr.bin/binutils/gprof/utils.h b/gnu/usr.bin/binutils/gprof/utils.h new file mode 100644 index 00000000000..27fb9c67349 --- /dev/null +++ b/gnu/usr.bin/binutils/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/gnu/usr.bin/binutils/gprof/vax.c b/gnu/usr.bin/binutils/gprof/vax.c new file mode 100644 index 00000000000..9d7fa835d00 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/vax.c @@ -0,0 +1,323 @@ +/* + * 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" +#include "vax.h" + +/* + * A symbol to be the child of indirect calls: + */ +Sym indirectchild; + + +static operandenum +operandmode (modep) + struct modebyte *modep; +{ + long usesreg = modep->regfield; + + switch (modep->modefield) + { + case 0: + case 1: + case 2: + case 3: + return literal; + case 4: + return indexed; + case 5: + return reg; + case 6: + return regdef; + case 7: + return autodec; + case 8: + return usesreg != PC ? autoinc : immediate; + case 9: + return usesreg != PC ? autoincdef : absolute; + case 10: + return usesreg != PC ? bytedisp : byterel; + case 11: + return usesreg != PC ? bytedispdef : bytereldef; + case 12: + return usesreg != PC ? worddisp : wordrel; + case 13: + return usesreg != PC ? worddispdef : wordreldef; + case 14: + return usesreg != PC ? longdisp : longrel; + case 15: + return usesreg != PC ? longdispdef : longreldef; + } + /* NOTREACHED */ +} + +static char * +operandname (mode) + operandenum mode; +{ + + switch (mode) + { + case literal: + return "literal"; + case indexed: + return "indexed"; + case reg: + return "register"; + case regdef: + return "register deferred"; + case autodec: + return "autodecrement"; + case autoinc: + return "autoincrement"; + case autoincdef: + return "autoincrement deferred"; + case bytedisp: + return "byte displacement"; + case bytedispdef: + return "byte displacement deferred"; + case byterel: + return "byte relative"; + case bytereldef: + return "byte relative deferred"; + case worddisp: + return "word displacement"; + case worddispdef: + return "word displacement deferred"; + case wordrel: + return "word relative"; + case wordreldef: + return "word relative deferred"; + case immediate: + return "immediate"; + case absolute: + return "absolute"; + case longdisp: + return "long displacement"; + case longdispdef: + return "long displacement deferred"; + case longrel: + return "long relative"; + case longreldef: + return "long relative deferred"; + } + /* NOTREACHED */ +} + +static long +operandlength (modep) + struct modebyte *modep; +{ + + switch (operandmode (modep)) + { + case literal: + case reg: + case regdef: + case autodec: + case autoinc: + case autoincdef: + return 1; + case bytedisp: + case bytedispdef: + case byterel: + case bytereldef: + return 2; + case worddisp: + case worddispdef: + case wordrel: + case wordreldef: + return 3; + case immediate: + case absolute: + case longdisp: + case longdispdef: + case longrel: + case longreldef: + return 5; + case indexed: + return 1 + operandlength ((struct modebyte *) ((char *) modep) + 1); + } + /* NOTREACHED */ +} + +static bfd_vma +reladdr (modep) + struct modebyte *modep; +{ + operandenum mode = operandmode (modep); + char *cp; + short *sp; + long *lp; + + cp = (char *) modep; + ++cp; /* skip over the mode */ + switch (mode) + { + default: + fprintf (stderr, "[reladdr] not relative address\n"); + return (bfd_vma) modep; + case byterel: + return (bfd_vma) (cp + sizeof *cp + *cp); + case wordrel: + sp = (short *) cp; + return (bfd_vma) (cp + sizeof *sp + *sp); + case longrel: + lp = (long *) cp; + return (bfd_vma) (cp + sizeof *lp + *lp); + } +} + + +void +find_call (parent, p_lowpc, p_highpc) + Sym *parent; + bfd_vma p_lowpc; + bfd_vma p_highpc; +{ + unsigned char *instructp; + long length; + Sym *child; + operandenum mode; + operandenum firstmode; + bfd_vma destpc; + static bool inited = FALSE; + + if (!inited) + { + inited = TRUE; + sym_init (&indirectchild); + indirectchild.cg.prop.fract = 1.0; + indirectchild.cg.cyc.head = &indirectchild; + } + + if (core_text_space == 0) + { + return; + } + if (p_lowpc < s_lowpc) + { + p_lowpc = s_lowpc; + } + if (p_highpc > s_highpc) + { + p_highpc = s_highpc; + } + DBG (CALLDEBUG, printf ("[findcall] %s: 0x%lx to 0x%lx\n", + parent->name, p_lowpc, p_highpc)); + for (instructp = (unsigned char *) core_text_space + p_lowpc; + instructp < (unsigned char *) core_text_space + p_highpc; + instructp += length) + { + length = 1; + if (*instructp == CALLS) + { + /* + * maybe a calls, better check it out. + * skip the count of the number of arguments. + */ + 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)); + 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: + case longdispdef: + case bytereldef: + case wordreldef: + case longreldef: + /* + * indirect call: call through pointer + * either *d(r) as a parameter or local + * (r) as a return value + * *f as a global pointer + * [are there others that we miss?, + * e.g. arrays of pointers to functions???] + */ + arc_add (parent, &indirectchild, (long) 0); + length += operandlength ( + (struct modebyte *) (instructp + length)); + continue; + case byterel: + case wordrel: + case longrel: + /* + * regular pc relative addressing + * check that this is the address of + * a function. + */ + 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 + */ + arc_add (parent, child, (long) 0); + length += operandlength ((struct modebyte *) + (instructp + length)); + continue; + } + goto botched; + } + /* + * else: + * it looked like a calls, + * but it wasn't to anywhere. + */ + goto botched; + default: + botched: + /* + * something funny going on. + */ + DBG (CALLDEBUG, printf ("[findcall]\tbut it's a botch\n")); + length = 1; + continue; + } + } + } +} diff --git a/gnu/usr.bin/binutils/gprof/vax.h b/gnu/usr.bin/binutils/gprof/vax.h new file mode 100644 index 00000000000..69ec9c26ee9 --- /dev/null +++ b/gnu/usr.bin/binutils/gprof/vax.h @@ -0,0 +1,52 @@ +/* + * 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. + * + * @(#)vax.h 5.4 (Berkeley) 6/1/90 + */ + + /* + * opcode of the `calls' instruction + */ +#define CALLS 0xfb + + /* + * offset (in bytes) of the code from the entry address of a routine. + * (see asgnsamples for use and explanation.) + */ +#define OFFSET_TO_CODE 2 +#define UNITS_TO_CODE (OFFSET_TO_CODE / sizeof(UNIT)) + + /* + * register for pc relative addressing + */ +#define PC 0xf + +enum opermodes + { + literal, indexed, reg, regdef, autodec, autoinc, autoincdef, + bytedisp, bytedispdef, worddisp, worddispdef, longdisp, longdispdef, + immediate, absolute, byterel, bytereldef, wordrel, wordreldef, + longrel, longreldef + }; +typedef enum opermodes operandenum; + +struct modebyte + { + unsigned int regfield:4; + unsigned int modefield:4; + }; |