diff options
Diffstat (limited to 'binutils-1.9/gprof.c')
| -rw-r--r-- | binutils-1.9/gprof.c | 2737 |
1 files changed, 2737 insertions, 0 deletions
diff --git a/binutils-1.9/gprof.c b/binutils-1.9/gprof.c new file mode 100644 index 0000000..e3a7a73 --- /dev/null +++ b/binutils-1.9/gprof.c @@ -0,0 +1,2737 @@ +/* `gprof', analyze gmon.out and print a profile. + Copyright (C) 1988 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* GNU gprof was written mainly by Jay Fenlason. */ + +#include <assert.h> +#include "getopt.h" +#ifdef __STDC__ +#include "stddef.h" +#include "stdarg.h" +#ifdef __GNUC__ +#define alloca __builtin_alloca +#endif +#else /* no __STDC__ */ +#ifdef sparc +#include "alloca.h" +#endif /* sparc */ +#include "varargs.h" +#define const +typedef unsigned int size_t; +#endif /* no __STDC__ */ + +#undef NULL +#include <stdio.h> +#undef NULL +#define NULL 0 + +#if !defined(A_OUT) && !defined(MACH_O) +#define A_OUT +#endif + +#ifdef A_OUT +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include <a.out.h> +#endif +#endif + +#ifdef MACH_O +#ifndef A_OUT +#include <nlist.h> +#ifndef N_TEXT +#define N_TEXT 0x04 +#endif +#endif +#include <sys/loader.h> +#endif + +#include "gmon.h" +/* #include <nlist.h> */ + +#ifdef USG +#define index strchr +#define bzero(s, n) (memset((s), 0, (n))) +#define bcopy(from, to, n) (memcpy ((to), (from), (n))) +#endif + +/* Special macros designed to remove some __STDC__ ugliness from my source + files. Instead, I use these (which may be just as ugly). Instead of using +extern foo(); + or +extern foo(int,double); + I use +extern foo FUN2(int, double); + which is expanded to the right thing depending on whether you're using an + ANSI cc or not. + + Also, instead of saying +type +foo(x,y) +int x; +double y; + or +type +foo(int x, double y) + I use +type +foo FUN2(int, x, double, y) +Which is also expanded into the right thing. . . + */ + +#ifdef __STDC__ +#define var_start(x,y) va_start(x,y) + +/* These macros expand into ANSI prototypes */ +#define FUN0() (void) +#define EXT0() (void) + +#define FUN1(t1,a1) (t1 a1) +#define EXT1(t1) (t1) +#define FUN1N(t1,a1) (t1 a1, ...) +#define EXT1N(t1) (t1, ...) + +#define FUN2(t1,a1,t2,a2) (t1 a1,t2 a2) +#define EXT2(t1, t2) (t1, t2) +#define FUN2N(t1,a1,t2,a2) (t1 a1,t2 a2, ...) +#define EXT2N(t1, t2) (t1, t2, ...) + +#define FUN3(t1,a1,t2,a2,t3,a3) (t1 a1, t2 a2, t3 a3) +#define EXT3(t1, t2, t3) (t1, t2, t3) +#define FUN3N(t1,a1,t2,a2,t3,a3)(t1 a1, t2 a2, t3 a3, ...) +#define EXT3N(t1, t2, t3) (t1, t2, t3, ...) + +#define FUN4(t1,a1,t2,a2,t3,a3,t4,a4) (t1 a1, t2 a2, t3 a3, t4 a4) +#define EXT4(t1, t2, t3, t4) (t1, t2, t3, t4) + +#define FUN5(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5) +#define EXT5(t1, t2, t3, t4, t5) (t1, t2, t3, t4, t5) + +#define FUN6(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6) +#define EXT6(t1, t2, t3, t4, t5, t6) (t1, t2, t3, t4, t5, t6) + +#define FUN7(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7) +#define EXT7(t1, t2, t3, t4, t5, t6, t7) (t1, t2, t3, t4, t5, t6, t7) + +#define FUN8(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8) +#define EXT8(t1, t2, t3, t4, t5, t6, t7, t8) (t1, t2, t3, t4, t5, t6, t7, t8) + +#define FUN9(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9) +#define EXT9(t1, t2, t3, t4, t5, t6, t7, t8, t9) (t1, t2, t3, t4, t5, t6, t7, t8, t9) + +#define FUN10(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9, t10 a10) +#define EXT10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) + +#define FUN11(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9, t10 a10, t11 a11) +#define EXT11(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + +#define FUN12(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11,t12,a12) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9, t10 a10, t11 a11, t12 a12) +#define EXT12(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) + +#define FUN13(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11,t12,a12,t13,a13) (t1 a1, t2 a2, t3 a3, t4 a4, t5 a5, t6 a6, t7 a7, t8 a8, t9 a9, t10 a10, t11 a11, t12 a12, t13 a13) +#define EXT13(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) + + +#else +/* Non-ANSI */ +#define var_start(x,y) va_start(x) + +/* These macros expand into old-style function definitions */ + +#define FUN0() () +#define EXT0() () + +#define FUN1(t1,a1) (a1) t1 a1; +#define EXT1(t1) () +#define FUN1N(t1,a1) (a1,va_alist) t1 a1; va_dcl +#define EXT1N(t1) () + +#define FUN2(t1,a1,t2,a2) (a1, a2) t1 a1; t2 a2; +#define EXT2(t1, t2) () +#define FUN2N(t1,a1,t2,a2,va_alist) (a1, a2) t1 a1; t2 a2; va_dcl +#define EXT2N(t1, t2) () + +#define FUN3(t1,a1,t2,a2,t3,a3) (a1, a2, a3) t1 a1; t2 a2; t3 a3; +#define EXT3(t1, t2, t3) () +#define FUN3N(t1,a1,t2,a2,t3,a3) (a1, a2, a3, va_alist) t1 a1; t2 a2; t3 a3; va_dcl +#define EXT3N(t1, t2, t3) () + +#define FUN4(t1,a1,t2,a2,t3,a3,t4,a4) (a1, a2, a3, a4) t1 a1; t2 a2; t3 a3; t4 a4; +#define EXT4(t1, t2, t3, t4) () + +#define FUN5(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5) (a1, a2, a3, a4, a5) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; +#define EXT5(t1, t2, t3, t4, t5) () + +#define FUN6(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6) (a1, a2, a3, a4, a5, a6) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; +#define EXT6(t1, t2, t3, t4, t5, t6) () + +#define FUN7(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7) (a1, a2, a3, a4, a5, a6, a7) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; +#define EXT7(t1, t2, t3, t4, t5, t6, t7) () + +#define FUN8(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8) (a1, a2, a3, a4, a5, a6, a7, a8) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; +#define EXT8(t1, t2, t3, t4, t5, t6, t7, t8) () + +#define FUN9(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9) (a1, a2, a3, a4, a5, a6, a7, a8, a9) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; +#define EXT9(t1, t2, t3, t4, t5, t6, t7, t8, t9) () + +#define FUN10(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10) (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; t10 a10; +#define EXT10(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) () + +#define FUN11(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11) (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; t10 a10; t11 a11; +#define EXT11(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) () + +#define FUN12(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11,t12,a12) (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; t10 a10; t11 a11; t12 a12; +#define EXT12(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) () + +#define FUN13(t1,a1,t2,a2,t3,a3,t4,a4,t5,a5,t6,a6,t7,a7,t8,a8,t9,a9,t10,a10,t11,a11,t12,a12,t13,a13) (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) t1 a1; t2 a2; t3 a3; t4 a4; t5 a5; t6 a6; t7 a7; t8 a8; t9 a9; t10 a10; t11 a11; t12 a12; t13 a13; +#define EXT13(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) () + + +#endif + +#ifdef VPRINTF_MISSING +/* The following will work for some systems. */ +#define vfprintf(stream, format, ap) _doprnt (format, ap, stream) +int +vsprintf FUN3(char *, into, char *, s, va_list, ap) +{ + int ret; + FILE f; + + f._cnt = 32767; + /* taking address and dereferencing deals with the fact that + f._ptr can be either a char * or an unsigned char *. */ + *(char **)&f._ptr = into; + f._flag = _IOWRT+_IOSTRG; + ret = _doprnt(s, ap, &f); + *f._ptr = 0; + return (ret); +} +#endif + +/* Names or default names for various files. */ +#define OBJ_NAM "a.out" +#define MON_NAM "gmon.out" +#define SUM_NAM "gmon.sum" + +/* Debugging stuff */ + +#define DEBUG + +/* A mask of flags defined below. */ +long debug=0; + +/* Print obnoxious msgs about the a.out file, and the amusing values therein */ +#define DB_AFILE (1<<0) /* a.out file */ + +/* Describe in detail the rending and tearing of the gmon.out file */ +#define DB_GFILE (1<<1) /* gmon.out file */ + +/* Describe in detail the writing out of the gmon.sum file */ +#define DB_SUM (1<<2) /* gmon.sum file */ + +/* Print neeto messages as we deal with -e -E -f and -F options */ +#define DB_OPT (1<<3) /* Options */ + +/* Print msgs whenever the ring buffer is used */ +/* This probably doesn't work since the ring buffer code was + moved into the utilities file */ +#define DB_RB (1<<4) /* Ring buffer */ + +/* Print msgs about cycles */ +#define DB_CYC (1<<5) + +/* Print msgs about assigning the histogram entries to functions */ +#define DB_HISTO (1<<6) + +/* Print obnoxious msgs here and there as we do our stuff */ +#define DB_MISC (1<<31) /* misc stuff */ + +#ifdef DEBUG +#define PRINT_OBNOXIOUS_DEBUG_MESSAGE(x, msg) if (debug&x) dbgprintf msg +#else +#define PRINT_OBNOXIOUS_DEBUG_MESSAGE(x, msg) +#endif + +/* LessThan EQual GreaterThan */ +/* These get returned by the various comparison functions */ +#define LT (-1) +#define EQ (0) +#define GT (1) + +/* Note since *ALL* non-zero values are true, do *NOT* say + if (x==TRUE) + These values are here only for saying + return TRUE; + and things like that. */ + +#define TRUE (1) +#define FALSE (0) + +/* The floating point datatype to use for storing + propagated info. Float should be big enough */ +#define FLOAT double + +/* Used when creating a gmon.sum file */ +/* #define FUDGE_FACTOR (sizeof (CHUNK)) */ +#define FUDGE_FACTOR 0 + +/* Description of one symbol in gprof's internal symbol table. + There is one of these for each function and one for each cycle. + + NAME is the name of the function or cycle. + VALUE is the address of the start of the function. + + CALLS and CALLED are chains of edges for calls into and out of + this function. These constitute the call graph. + + NCALLS is the total number + of times this function called other functions, + NCALLED is the total number of times this function was called. + Note that if NCALLED is zero, but NCALLS is nozero, this function + must have been started magically. It happens. (Signals, etc.) + + RECURSIVE is the total number of times this function was called + recursively. + + HISTO is the total number of histogram counts for this function. + + SUB_HISTO is the total number of histogram counts for this function + plus time propagated from the children. + + CYCNUM is the number of the cycle this function is in, + or the number of this cycle is this entry is for a cycle. + -1 means the function isn't in any cycles. 0 means the + cycle-ness of the function hasn't been determined yet. + + NUMINDEX is the index number assigned to this function + for the call graph. + + If FLAG is non-zero, this function is in the process of being + saved from the oblivion caused by the -f or -F options. */ + +struct mesym { + char *name; + unsigned long value; + + struct symlist *calls; + unsigned ncalls; + struct symlist *called; + unsigned ncalled; + + unsigned recursive; + + unsigned histo; + FLOAT sub_histo; + int cycnum; + int numindex; + int flag; +}; + +/* Vector of `struct mesym' for all functions, + sorted in ascending order by VALUE field for binary search. */ +struct mesym *syms; +int nsym=0; /* Length of vector */ + +/* Vector of `struct mesym' for all cycles so far identified. */ +struct mesym **cycles; +int number_of_cycles = 0; /* Length of vector */ + +/* Structure for an edges of the call graph. + + Each edge--each pair of functions A and B such that A called B-- + is represented by one of these structures. It appears in + A's `calls' chain and in B's `called' chain. + + Meanwhile, the structure describes its meaning with the + SYM_FROM and SYM_TO fields, which point to the symbol entries + for A and B. + + Basically, if you got here from a mesym's calls + pointer, SYM_FROM points back to that symbol, NEXT_FROM is irrevelant, + SYM_TO points to the symbol it called, and NEXT_TO points to the + next one in the list. + + If you got here from a mesym's called pointer, SYM_FROM points to + the symbol that called it, NEXT_FROM points to the next one in the + list, SYM_TO points back to the symbol, and NEXT_TO is irrelevant. + + NCALLS is the number of times SYM_FROM called SYM_TO, + PROP_TIME is the amount of time in SYM_TO itself propagated to SYM_FROM, + CHILD_TIME is the amount of time + propagated from SYM_TO's children to SYM_FROM. */ + +struct symlist { + struct mesym *sym_from; + struct symlist *next_from; + struct mesym *sym_to; + struct symlist *next_to; + + unsigned ncalls; + FLOAT prop_time; + FLOAT child_time; +}; + +/* argv[0], here for the sake of error messages. */ +char *myname = 0; + +/* Name of the executable file. */ +char *exec_file_name; + +/* The string table of the executable file. + Each symbol entry in the file contains an index in the string table. + When the symbols are in core, we relocate them to point to their names, + which remain inside the string table. */ +char *strs; + +/* Header of the first gmon.out file we read. This is used to (try to) + make sure all the rest of the gmon.out files agree with the first one + (and the executable.) */ +struct gm_header hdr; + +/* Number of clock ticks per second. Read from the kernel's memory, + this number tells us how long an interval a single stab in the + histogram represents. */ +long ticks; + +/* This is an array of pointers to symbols. These are the functions that + we'll have to print out in the flat graph. */ +struct mesym **flat_profile_functions; +int number_of_flat_profile_functions = 0; /* Size of vector */ + +/* This is an array of pointers to symbols. These are the functions that + we should mention in the call tree. */ +struct mesym **functions_in_call_tree; +int number_of_functions_in_call_tree= 0; +struct mesym **f_end; + +/* Number of slots in the histogram. */ +int nhist; + +/* Total number of counts in the histogram. */ +unsigned long tothist = 0; + +/* Pointer to the histogram itself. */ +unsigned CHUNK *histo; /* Stabs */ + +/* Now that I've re-written the ring_buffer code, we need this */ +void *ring_buffer; + +/* Record the specified options. */ + +/* If the -a option is given, static (private) functions are not read into + the symbol table. This means that time spent in them, calls to them, etc, + are instead added to whatever function was loaded next to it in the a.out + file. This is compatable with UN*X gprof. (Bleh.) The right thing to do + is keep track of time spent in static functions, and forget about it, + instead of adding it to another hapless function. Rms disagrees with me. + I think its evil to add histogram time to a function simply because it + happend to be loaded in memory just before a function we don't want to print. */ +int no_locals = 0; /* -a flag */ + +/* The -b option tells gprof to not print out the obnoxious blurbs telling + what all the fields of the output mean. This is useful if you've seen + the blurbs a gzillion times and you only want to look at your numbers. */ +int no_blurbs = 0; /* -b flag */ + +/* If the -s option is given, gprof will write out a 'sum file' gmon.sum + which is a gmon.out file that contains the profile info from all the + gmon.out files that gprof read in. */ +int write_sum_file = 0; /* -s option */ + +/* The -z option tells gprof to include functions with zero usage (never called, + and used no time) in the output. Usually, such functions are considered + boring, and aren't printed. */ +int print_zeros = 0; /* -z option */ + +/* The -e option takes a function name, and suppress the printing of that + function and its descendents from the call graph profile. (If its + descendents are called from elswhere, well. . .) (Currently, they're + printed, and the -e'd function is shown under the list of parents so + you can see where the child's time is disappearing to. . .) + + The -E option takes a function name, and not only -e's it, but + removes the time spent in the function from the total time. (Its as if + that function never existed (unless it calls something that is also + called from somewhere else, in which case. . . (Currently it works + as described for -e above.)) + + -f prints only the call tree for its argument. + + -F is like -f, but it uses only the time in the function and its + descendents for computing total time. + */ + +enum option_type { SMALL_E, BIG_E, SMALL_F, BIG_F, + /* Like BIG_E except don't print a warning if the + function doesn't exist. */ + REMOVE_TIME_IF_THERE }; + +struct filter { + char *name; + enum option_type type; +}; + +/* Vector with an element for each -e, -E, -f or -F option specified. */ +struct filter *filters; +/* Length of the vector. */ +int nfilters; + +/* These values are stored in the flag field of a struct mesym so we can know + what we're doing to that function */ + +/* This one is being saved by a -f or -F flag */ +#define SAVE_ME 01 +/* This one is being killed by a -e or -E flag */ +#define KILL_ME 02 + +/* Blurbs that are copied verbatim into the output file + to explain the data in the tables. + + If your compiler can't stand this, split this up into a vector + of strings, and print them one after another. */ + +char *first_blurb = "\n\ + The above table shows how much time was spent directly in each\n\ + function. The table was sorted by the amount of time the computer\n\ + spent in each function.\n\n\ + % time This is the percentage of the total execution time\n\ + the program spent in this function. These should all add\n\ + up to 100%.\n\n\ + seconds This is the total number of seconds the computer spent\n\ + executing this function.\n\n\ + cumsec This is the cumulative total number of seconds the\n\ + computer spent executing this functions, plus the time spent\n\ + in all the functions above this one in this table.\n\n\ + calls This is the total number of times the function was called.\n\ + If there isn't a number here, the function wasn't compiled with\n\ + the profiler enabled, and further information about this function\n\ + is limited. In particular, all information about where this \n\ + function was called from has been lost.\n\n\ + function This is the name of the function.\n\ +\f"; + +char *second_blurb = "\n\ + This table describes the call tree of the program, and was sorted by\n\ + the total amount of time spent in each function and its children.\n\n\ + Each entry in this table consists of several lines. The line with the\n\ + index number at the left hand margin lists the current function.\n\ + The lines above it list the functions that called this function,\n\ + and the lines below it list the functions this one called.\n\ + This line lists:\n\ + index A unique number given to each element of the table.\n\ + Index numbers are sorted numerically.\n\ + The index number is printed next to every function name so\n\ + it is easier to look up where the function in the table.\n\n\ + % time This is the percentage of the `total' time that was spent\n\ + in this function and its children. Note that due to\n\ + different viewpoints, functions excluded by options, etc,\n\ + these numbers will NOT add up to 100%.\n\n\ + self This is the total amount of time spent in this function.\n\n\ + children This is the total amount of time propagated into this\n\ + function by its children.\n\n\ + called This is the number of times the function was called.\n\ + If the function called itself recursively, the number\n\ + only includes non-recursive calls, and is followed by\n\ + a `+' and the number of recursive calls.\n\n\ + name The name of the current function. The index number is\n\ + printed after it. If the function is a member of a\n\ + cycle, the cycle number is printed between the\n\ + function's name and the index number.\n\n\n\ + For the function's parents, the fields have the following meanings:\n\n\ + self This is the amount of time that was propagated directly\n\ + from the function into this parent.\n\n\ + children This is the amount of time that was propagated from\n\ + the function's children into this parent.\n\n\ + called This is the number of times this parent called the\n\ + function `/' the total number of times the function\n\ + was called. Recursive calls to the function are not\n\ + included in the number after the `/'.\n\n\ + name This is the name of the parent. The parent's index\n\ + number is printed after it. If the parent is a\n\ + member of a cycle, the cycle number is printed between\n\ + the name and the index number.\n\n\ + If the parents of the function cannot be determined, the word\n\ + `<spontaneous>' is printed in the `name' field, and all the other\n\ + fields are blank.\n\n\ + For the function's children, the fields have the following meanings:\n\n\ + self This is the amount of time that was propagated directly\n\ + from the child into the function.\n\n\ + children This is the amount of time that was propagated from the\n\ + child's children to the function.\n\n\ + called This is the number of times the function called\n\ + this child `/' the total number of times the child\n\ + was called. Recursive calls by the child are not\n\ + listed in the number after the `/'.\n\n\ + name This is the name of the child. The child's index\n\ + number is printed after it. If the child is a\n\ + member of a cycle, the cycle number is printed\n\ + between the name and the index number.\n\n\ + If there are any cycles (circles) in the call graph, there is an\n\ + entry for the cycle-as-a-whole. This entry shows who called the\n\ + cycle (as parents) and the members of the cycle (as children.)\n\ + The `+' recursive calls entry shows the number of function calls that\n\ + were internal to the cycle, and the calls entry for each member shows,\n\ + for that member, how many times it was called from other members of\n\ + the cycle.\n\n"; + + +/* Prototypes for all the functions. */ + +int main EXT2(int, char **); +int read_header_info EXT6 (char *, FILE *, long int *, unsigned int *, + long int *, unsigned int *); +void print_flat_profile EXT0(); +void print_call_graph EXT0(); +void write_summary EXT0(); +void filter_graph EXT0(); +FLOAT convert_and_round EXT1(FLOAT); +void add_to_lists EXT3(struct mesym *, struct mesym *, unsigned); +void delete_from_lists EXT2(struct mesym*, struct mesym *); +void flushfuns EXT0(); +void findcycles EXT0(); +void kill_children EXT2(struct mesym *, int); +void save_the_children EXT2(struct mesym *, int); +void remove_from_call_tree EXT1(struct mesym **); +struct mesym **find_funp_from_name EXT1(char *); +struct mesym **find_funp_from_pointer EXT1(struct mesym *); +void read_syms EXT2(FILE *, int); +struct mesym *val_to_sym EXT1(unsigned long); +int badsym EXT1(struct nlist *); +int symcmp EXT2(const void *, const void *); +int timecmp EXT2(const void *, const void *); +int callcmp EXT2(const void *, const void *); +int treetimecmp EXT2(const void *, const void *); +int listcmp EXT2(const void *, const void *); +void readgm EXT1(char *); +void print_blurb EXT1(char *blurb); +long get_ticks EXT0(); +void add_filter EXT2(char *name, int flag); +void print_sorted_list EXT3(int, int, struct symlist *); + +void fatal EXT1N(char *); +void fatal_io EXT2(char *, char *); + +FILE *ck_fopen EXT2(char *, char *); +void ck_fseek EXT3(FILE *, long, int); +void ck_fread EXT4(void *, size_t, size_t, FILE *); +void ck_fwrite EXT4(void *, size_t, size_t, FILE *); +void ck_fclose EXT1(FILE *); + +void *ck_malloc EXT1(size_t); +void *ck_calloc EXT2(size_t, size_t); +void *ck_realloc EXT2(void *, size_t); + +char *mk_sprintf EXT1N(char *); + +void *init_ring_buffer EXT0(); +void push_ring_buffer EXT2(void *, void *); +void *pop_ring_buffer EXT1(void *); +int ring_buffer_isnt_empty EXT1(void *); +void flush_ring_buffer EXT1(void *); + +/* C++ demangler stuff. */ +char *cplus_demangle EXT1(char *); +void fprint_name EXT2(FILE *, char *); + +void fatal EXT1N(char *); + +void *malloc EXT1(size_t); +void *realloc EXT2(void *,size_t); +void free EXT1(void *); + + +/* Since we don't have prototypes for the system funs, we add them + ourselves. . . */ +int atoi EXT1(const char *); +long atol EXT1(const char *); +double atof EXT1(const char *); + +#ifndef NeXT /* NeXT has a bug in their include files. */ +void qsort EXT4(void *, size_t, size_t, int (*)(const void *, const void *)); +#endif + +void exit EXT1(int); + +int strcmp EXT2(const char *, const char *); +char *index EXT2(const char *, int); +int printf EXT1N(const char *); +int fprintf EXT2N(FILE *, const char *); +/* char *sprintf EXT2N(const char *, const char *); */ + +int puts EXT1(const char *); +int fputs EXT2(const char *, FILE *); + +int fputc EXT2(int, FILE *); +size_t fread EXT4(void *, size_t, size_t, FILE *); + +int nlist EXT2(const char *, struct nlist *); + +#ifndef VPRINTF_MISSING +int vfprintf EXT3(FILE *, const char *, va_list); +#endif + +void dbgprintf EXT1N(char *); +void dumpsyms EXT0(); +void dumpfuns EXT0(); + + +/* And now, the program. */ + +int +main FUN2(int, ac, char **,av) +{ + FILE *fp; + int argc; + char **argv; + int c; + long int syms_offset, strs_offset; + unsigned int syms_size, strs_size; + + extern char *optarg; + extern int optind, opterr; + + static struct option long_options[] = + { + {"no-static", 0, &no_locals, 1}, + {"brief", 0, &no_blurbs, 1}, + {"no-prof", 1, 0, 'e'}, + {"no-time", 1, 0, 'E'}, + {"only-prof", 1, 0, 'f'}, + {"only-time", 1, 0, 'F'}, + {"sum", 0, &write_sum_file, 1}, + {"zeros", 0, &print_zeros, 1}, + {NULL, 0, NULL, 0} + }; + + char *name = '\0'; + int ind; + + argc=ac; + argv=av; + + myname= argv[0]; + + /* Omit profiling internal functions from call graph. */ +#ifdef sparc + +#else + add_filter ("mcount", BIG_E); +#endif + /* add_filter ("mcleanup", BIG_E); Seems to have dissappeared? */ + + /* GCC output doesn't have this function. */ + add_filter ("moncontrol", REMOVE_TIME_IF_THERE); + + ring_buffer=init_ring_buffer (); + + while ((c = getopt_long (argc, argv, "abcdD:e:E:f:F:svz", long_options, + &ind)) !=EOF) { + if (c == 0 && long_options[ind].flag == 0) + c = long_options[ind].val; + switch (c) { + case 0 : + break; + case 'a': + no_locals= TRUE; + break; + case 'b': + no_blurbs = TRUE; + break; + case 'c': + fatal ("The -c option is not supported"); + case 'd': + debug= -1; + break; + case 'D': + debug=atoi (optarg); + break; + case 's': + write_sum_file= TRUE; + break; + case 'z': + print_zeros = TRUE; + break; + case 'e': + add_filter (optarg, SMALL_E); + break; + case 'E': + add_filter (optarg, BIG_E); + break; + case 'f': + add_filter (optarg, SMALL_F); + break; + case 'F': + add_filter (optarg, BIG_F); + break; + default: + fprintf (stderr, "\ +Usage: %s [-absz] [-e func] [-E func] [-f func] [-F func]\n\ + [+no-static] [+brief] [+no-prof func]\n\ + [+no-time func] [+only-prof func]\n\ + [+only-time func] [+sum] [+zeros] executable gmon.out...\n", + myname); + exit (1); + } + } + if (optind<argc) { + exec_file_name=argv[optind]; + optind++; + } + if (!exec_file_name) + exec_file_name=OBJ_NAM; + + /* Open the a.out file, and read in selected portions */ + fp=ck_fopen (exec_file_name, "r"); + + /* Make sure its really an a.out file. If it isn't yell and scream + and stamp our feet. */ + if (!read_header_info(exec_file_name, fp, &syms_offset, &syms_size, &strs_offset, &strs_size)) + fatal ("`%s' is not an executable file", exec_file_name); + + /* Read in the string table. */ + ck_fseek (fp, strs_offset + sizeof strs_size, 0); + strs=(char *)ck_malloc (strs_size); + ck_fread ((void *)(strs+sizeof (long)), sizeof (char), strs_size-sizeof (long), fp); + + /* Read the symbols, and put the interesting ones (sorted) in SYMS. */ + ck_fseek (fp, syms_offset, 0); + read_syms (fp, syms_size / sizeof (struct nlist)); + + ck_fclose (fp); + /* We are done with the a.out file */ + + /* Read in the gmon.out files; accumulate all histogram data in HISTO + and put all number-of-calls figures into the call graph. */ + if (optind>=argc) readgm (MON_NAM); + else { + while (optind<argc) { + readgm (argv[optind]); + optind++; + } + } + + /* If a summary output file is wanted, + we can do it straightaway since we have merged the data. */ + + if (write_sum_file) { + write_summary (); + exit (0); + } + + /* Find out how many clock ticks/second our machine puts out */ + ticks=get_ticks (); + + /* Assign the ticks in the histogram to the functions they represent */ + { + int n; + unsigned long pos; + struct mesym *sym; + int incr = (hdr.high - hdr.low) / (nhist - 1); + + if ((hdr.high - hdr.low) != 4 * (nhist - 1)) + abort (); + + for (n=0, pos=hdr.low, sym= &syms[0]; n<nhist; n++, pos+=incr) { + if (histo[n]) { + + /* We've found the right symbol when *sym<=pos && *(sym+1)>pos */ + /* This means POS lies between sym and the one after it */ + + while ((sym+1)->value<=pos) + sym++; + if ((sym+1)->value<(pos+incr)) + { + /* Wow! On the edge. Split the time between the symbols */ + sym->histo+=(histo[n]+1)/2; + (sym+1)->histo+=(histo[n])/2; + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_HISTO, ("%05lx %02d-> %s (%d) %s (%d)\n", pos, histo[n], sym->name, sym->histo, (sym+1)->name, (sym+1)->histo)); + } else { + sym->histo+=histo[n]; + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_HISTO, ("%05lx %02d-> %s (%d)\n", pos, histo[n], sym->name, sym->histo)); + } + } + } + } + + /* Avoid division by zero if there wasn't any time collected! */ + if (tothist==0) + tothist=1; + + print_flat_profile (); + + print_call_graph (); + + if (debug&DB_AFILE) + dumpsyms (); + + if (debug&DB_AFILE) + dumpfuns (); + exit (0); +} + +/* Read various information from the header of an object file. + Return 0 for failure or 1 for success. */ + +int +read_header_info (name, fp, syms_offset, syms_size, strs_offset, strs_size) + char *name; + FILE *fp; + long int *syms_offset; + unsigned int *syms_size; + long int *strs_offset; + unsigned int *strs_size; +{ +#ifdef A_OUT + { + struct exec hdr; + + ck_fseek (fp, 0L, 0); +#ifdef HEADER_SEEK + HEADER_SEEK (fp); +#endif + + if (fread ((char *) &hdr, sizeof hdr, 1, fp) == 1 && !N_BADMAG(hdr)) + { + *syms_offset = N_SYMOFF (hdr); + *syms_size = hdr.a_syms; + *strs_offset = N_STROFF (hdr); + ck_fseek (fp, N_STROFF (hdr), 0); + if (fread ((char *) strs_size, sizeof *strs_size, 1, fp) != 1) + fatal ("error reading string table of `%s'", name); + return 1; + } + } +#endif + +#ifdef MACH_O + { + struct mach_header mach_header; + struct load_command *load_command; + struct symtab_command *symtab_command; + char *hdrbuf; + int cmd, symtab_seen; + + ck_fseek (fp, 0L, 0); + if (fread ((char *) &mach_header, sizeof mach_header, 1, fp) == 1 + && mach_header.magic == MH_MAGIC) + { + hdrbuf = ck_malloc (mach_header.sizeofcmds); + if (fread (hdrbuf, mach_header.sizeofcmds, 1, fp) != 1) + fatal ("failure reading load commands of file `%s'", name); + load_command = (struct load_command *) hdrbuf; + symtab_seen = 0; + for (cmd = 0; cmd < mach_header.ncmds; ++cmd) + { + if (load_command->cmd == LC_SYMTAB) + { + symtab_seen = 1; + symtab_command = (struct symtab_command *) load_command; + *syms_offset = symtab_command->symoff; + *syms_size = symtab_command->nsyms * sizeof (struct nlist); + *strs_offset = symtab_command->stroff; + *strs_size = symtab_command->strsize; + } + load_command = (struct load_command *) ((char *) load_command + load_command->cmdsize); + } + free (hdrbuf); + if (!symtab_seen) + *syms_offset = *syms_size = *strs_offset = *strs_size = 0; + return 1; + } + } +#endif + + return 0; +} + +/* Output a summary gmon file containing all our accumulated + histogram and call-graph data. */ + +void +write_summary FUN0() +{ + struct mesym *p; + struct symlist *t; + struct gm_call call_tmp; + FILE *fp; + + fp=ck_fopen (SUM_NAM, "w"); + hdr.nbytes=sizeof (struct gm_header)+nhist*sizeof (CHUNK); + ck_fwrite ((void *)&hdr, sizeof (hdr), 1, fp); + ck_fwrite ((void *)histo, sizeof (unsigned CHUNK), nhist, fp); + /* for (p= &syms[nsym]; --p>=&syms[0];) { */ + if (nsym) { + p= &syms[nsym-1]; + do { + for (t=p->calls; t; t=t->next_to) { + /* Since we've forgotten exactly where in FROM we + were called from, we fake it. Since this is only + gonna be fed back into gprof, it doesn't matter */ + call_tmp.from=(p->value+FUDGE_FACTOR)-hdr.low; + call_tmp.to=(t->sym_to->value+FUDGE_FACTOR)-hdr.low; + call_tmp.ncalls=t->ncalls; + ck_fwrite ((void *)&call_tmp, sizeof (call_tmp), 1, fp); + } + } while (p-->&syms[0]); + } + ck_fclose (fp); +} + +/* Print the flat profile from the symbol table information. */ + +void +print_flat_profile FUN0() +{ + int n; + struct mesym **funp; + + /* Scan the symbol table and discover how many functions either had time + spent in them, or had a non-zero call count */ + for (n=0; n<nsym; n++) { + if (syms[n].histo || syms[n].ncalled || print_zeros) + number_of_flat_profile_functions++; + } + /* Collect all the interesting functions */ + flat_profile_functions + =(struct mesym **)ck_calloc (number_of_flat_profile_functions, sizeof (struct mesym *)); + for (n=0, funp=flat_profile_functions; n<nsym; n++) { + if (syms[n].histo || syms[n].ncalled || print_zeros) { + *funp= &syms[n]; + funp++; + } + } + + /* Sort them */ + qsort (flat_profile_functions, number_of_flat_profile_functions, + sizeof (struct mesym *), timecmp); + + /* And print them out */ + + if (no_blurbs) + printf ("\t\tFlat profile\n\n"); + else + printf ("\tFlat profile (explanation follows)\n\n"); + + if (no_blurbs) + printf ("\t\tCall graph is on the following page.\n\n"); + else + printf ("\tCall graph is on the following page.\n\n"); + + printf ("Each sample counts as %g seconds.\n\n", 1.0/ticks); + + puts ("% time seconds cumsec calls function"); + + for (n=0; n<number_of_flat_profile_functions; n++) { + unsigned histo; + static cumhist = 0; + + histo=flat_profile_functions[n]->histo; + cumhist+=histo; + printf ("%6.2f ", (FLOAT)(100.0*histo)/(FLOAT)tothist); + printf ("%8.2f ", ((FLOAT)histo)/(FLOAT)ticks); + printf ("%8.2f ", ((FLOAT)cumhist)/(FLOAT)ticks); + if (flat_profile_functions[n]->ncalled) + printf (" %5d ", flat_profile_functions[n]->ncalled); + else fputs (" ", stdout); + fprint_name (stdout, flat_profile_functions[n]->name); + fputs ("\n", stdout); + } + + /* RMS says we should print the blurb last, which makes sense to me */ + print_blurb (first_blurb); +} + +/* Compute the call graph and print it. */ + +void +print_call_graph FUN0() +{ + int n; + int index; + struct mesym **funp; + struct mesym **f; + + /* Count the functions that appear in the call tree. */ + for (n=0; n<nsym; n++) { + /* If a function calls something else, or is called by + something else, its in the call tree. . . */ + if (syms[n].ncalls || syms[n].ncalled) + number_of_functions_in_call_tree++; + } + + if (number_of_functions_in_call_tree == 0) { + printf ("\t\tNo call graph information available.\n"); + return; + } + + /* Allocate a vector of these functions. */ + functions_in_call_tree + =(struct mesym **)ck_calloc (number_of_functions_in_call_tree, sizeof (struct mesym *)); + for (n=0, funp=functions_in_call_tree; n<nsym; n++) { + if (syms[n].ncalls || syms[n].ncalled) { + *funp= &syms[n]; + funp++; + } + } + + /* Put all the leaf nodes at the end of the call tree */ + qsort (functions_in_call_tree, number_of_functions_in_call_tree, sizeof (struct mesym *), callcmp); + + /* Root nodes are now all at the head, and can be easily found + 'cuz they have call-counts of zero (Never been called, but + calls something else; that's spontaneous in my book.) */ + /* Ordinary nodes are in the middle, (were called, and + called others. Leaf nodes are at the end. */ + + /* Our mission, should we choose to accept it, is to detect + circles in the call graph. */ + /* We do this by keeping a pointer into the call tree called f_end. This + points to the end of the functions that we don't know if they are leaf + nodes or not. When we know something is a leaf node, we move it down + past f_end */ + + f= &functions_in_call_tree[number_of_functions_in_call_tree-1]; + do { + if ((*f)->ncalls!=0) + break; + (*f)->cycnum= -1; + + /* Note the neeto side effect here! Is there a better way to do this? */ + } while (f-- > &functions_in_call_tree[0]); + + /* Note that functions that only call themselves (and nobody else) don't + get marked above. Doesn't matter; they get marked soon. */ + if (f== &functions_in_call_tree[0]) { + (*f)->cycnum= -1; + } else { + int found; + + f_end=f; + + /* Eliminate recursive calls */ + do { + struct symlist *t, *u; + + for (t= (*f)->calls; t; t=t->next_to) { + if (t->sym_to!= (*f)) + continue; + (*f)->recursive+=t->ncalls; + + (*f)->ncalls-=t->ncalls; + (*f)->ncalled-=t->ncalls; + + /* Delete from linked list */ + if (t==(*f)->calls) + (*f)->calls=t->next_to; + else { + for (u=(*f)->calls; u->next_to!=t; u=u->next_to) + ; + u->next_to=t->next_to; + } + + /* Find and delete from called list too */ + if (t==(*f)->called) + (*f)->called=t->next_from; + else { + for (u=(*f)->called; u->next_from!=t; u=u->next_from) + ; + u->next_from=t->next_from; + } + free (t); + break; + } + } while (f--!=&functions_in_call_tree[0]); + + number_of_cycles = 0; + + /* Either there weren't any cycles, or all the cycles live + between f_end and functions_in_call_tree */ + + /* Mark all functions that are not in any cycle. */ + flushfuns (); + + + /* If we haven't got them all, find the cycle (s). */ + if (f_end != &functions_in_call_tree[0]) + findcycles (); + } + + /* Add entries for the cycles to the vector of all call-graph nodes. */ + + functions_in_call_tree + =ck_realloc (functions_in_call_tree, + (number_of_cycles+number_of_functions_in_call_tree)*sizeof (struct mesym *)); + for (n=0; n<number_of_cycles; n++) + functions_in_call_tree[number_of_functions_in_call_tree++]= cycles[n]; + + /* Now discard the functions that the filters say should not appear. */ + + filter_graph (); + + /* So by now, the only functions left are the ones we want to print */ + qsort (functions_in_call_tree, number_of_functions_in_call_tree, sizeof (struct mesym *), treetimecmp); + + /* Assign each function its index number. */ + for (n=0; n<number_of_functions_in_call_tree; n++) + functions_in_call_tree[n]->numindex=n+1; + + if (no_blurbs) + printf ("\t\t\tCall graph\n\n"); + else + printf ("\t\t Call graph (explanation follows)\n\n"); + + puts ("index % time self children called name"); + + /* Loop over entries. */ + + for (index = 0; index <number_of_functions_in_call_tree; index++) { + struct mesym *current = functions_in_call_tree[index]; + char tmpstr[40]; /* Can an int have more than 40 digits? I hope not */ + + /* Print out all the things that called this */ + if (current->ncalled==0 && current->called==0) + printf (" <spontaneous>\n"); + else { + print_sorted_list (current->ncalled, current->cycnum, current->called); + } + + + sprintf (tmpstr, "[%d]", current->numindex); + printf ("%-6s %6.2f %7.2f %7.2f ", + tmpstr, + (FLOAT)(100.0*(current->sub_histo+current->histo))/(FLOAT)tothist, + convert_and_round ((FLOAT)current->histo), + convert_and_round ((FLOAT)current->sub_histo)); + + if (current->recursive) { + printf ("%4d+%-4d ", + current->ncalled, + current->recursive); + fprint_name (stdout, current->name); + } else { + printf ("%4d ", current->ncalled); + fprint_name (stdout, current->name); + } + + if (current->cycnum>0 && current->name[0]!='<') + printf (" <cycle %d>", current->cycnum); + + printf (" [%d]\n", current->numindex); + + + /* Now print out the children */ + + if (current->name[0]=='<') + print_sorted_list (-2, current->cycnum, current->calls); + else + print_sorted_list (-1, current->cycnum, current->calls); + + + printf ("----------------------------------------\n"); + } + + print_blurb (second_blurb); +} + +/* Scan the call tree for virtual leaf nodes */ +/* If we find any, propagate time into them from their children, + mark them as being leaf nodes. Then repeat the process. When + we drop out of here, either we've flushed the entire tree, or + we've found a cycle. */ + +void +flushfuns FUN0() +{ + int found; + struct mesym **f; + + do { + found=0; + f=f_end; + do { + struct symlist *t; + + assert (f>=functions_in_call_tree); + + for (t=(*f)->calls; t; t=t->next_to) + if (t->sym_to->cycnum==0) + break; + + /* We've found an virtual leaf node. We shold + propagate time into the node, and move it + past f_end, since we aren't interested in + it anymore */ + if (!t) { + found++; + + /* If its a member of a cycle, cycnum is positive, and + time propagation has already been dealt with. */ + if ((*f)->cycnum==0) { + for (t=(*f)->calls; t; t=t->next_to) { + struct mesym *symP; + + if (t->sym_to->name[0]=='<') + continue; + if (t->sym_to->cycnum==-1) + symP= t->sym_to; + else + symP= cycles[t->sym_to->cycnum-1]; + + t->prop_time= (FLOAT)t->ncalls*(FLOAT)symP->histo/(FLOAT)symP->ncalled; + t->child_time=(FLOAT)t->ncalls* symP->sub_histo /(FLOAT)symP->ncalled; + (*f)->sub_histo+=t->prop_time+t->child_time; + } + (*f)->cycnum= -1; + } + + /* move this node past f_end, since we don't care + about it anymore */ + if (f_end!=functions_in_call_tree) { + /* move this function to the end */ + if (f!=f_end) { + struct mesym *tmp; + + tmp= *f; + *f= *f_end; + *f_end=tmp; + } + --f_end; + } + assert (f_end>=functions_in_call_tree); + } + } while (f-->&functions_in_call_tree[0]); + } while (found && f_end>&functions_in_call_tree[0]); +} + +void +findcycles FUN0() +{ + struct mesym **f; + struct mesym *ptr; + struct symlist *tmp; + struct cy { + struct mesym *memb1; + struct mesym *memb2; + struct mesym **others; + int width; + int deepest; + int shallowest; + }; + struct cy *cy; + int ncy = 0; + int n; + + int bigdepth = 0; + int curdepth; + struct mesym *current_cycle_pointer; + struct mesym *cursym; + + int tree_depth; + + for (f= &functions_in_call_tree[0]; f<=f_end; f++) { + if ((*f)->ncalled==0) { + push_ring_buffer (ring_buffer, *f); + (*f)->flag=0; + } + } + push_ring_buffer (ring_buffer, (void *)0); + + tree_depth = 1; + for (;;) { + ptr=pop_ring_buffer (ring_buffer); + if (!ptr) { + if (ring_buffer_isnt_empty (ring_buffer)) { + push_ring_buffer (ring_buffer, (void *)0); + tree_depth ++; + continue; + } else { + break; + } + } else if (ptr->flag==1) { + fprintf (stderr, "Ignoring call to spont function "); + fprint_name (stderr, ptr->name); + fprintf (stderr, "\n"); + } else if (ptr->flag!=0) { + /* Save upward edge */ + /* printf ("Upward edge detected in function %s\n", ptr->name); */ + if (!ncy) { + cy=ck_malloc (sizeof (struct cy)); + ncy=1; + } else { + ncy++; + cy=ck_realloc (cy, ncy*sizeof (struct cy)); + } + cy[ncy-1].memb1=ptr; + cy[ncy-1].memb2=0; + } else { + ptr->flag=tree_depth; + for (tmp=ptr->calls; tmp; tmp=tmp->next_to) + if (tmp->sym_to->cycnum!=-1) + push_ring_buffer (ring_buffer, tmp->sym_to); + } + } + if (!ncy) + return; + + for (n=0; n<ncy; n++) { + struct symlist *s; + struct symlist *u; + + + cursym=cy[n].memb1; + if (cursym->cycnum) + continue; + + number_of_cycles++; + /* for (s=cursym->called; s; s=s->next_from) { + if (s->sym_from->flag>=cursym->flag) + break; + } + if (!s) + abort (); */ + + if (!cycles) + cycles=(struct mesym **)ck_malloc (sizeof (struct mesym *)); + else + cycles=(struct mesym **)ck_realloc ((void *)cycles, + number_of_cycles*sizeof (struct mesym *)); + + current_cycle_pointer = (struct mesym *)ck_malloc (sizeof (struct mesym)); + cycles[number_of_cycles-1] = current_cycle_pointer; + bzero (current_cycle_pointer, sizeof *current_cycle_pointer); + current_cycle_pointer->name=mk_sprintf ("<cycle %d as a whole>", number_of_cycles); + current_cycle_pointer->value= (unsigned long)-1; + current_cycle_pointer->cycnum=number_of_cycles; + + cursym=cy[n].memb1; + + push_ring_buffer (ring_buffer, cursym); + + while (ring_buffer_isnt_empty (ring_buffer)) { + cursym=pop_ring_buffer (ring_buffer); + if (cursym->cycnum==number_of_cycles) + continue; + if (cursym->cycnum!=0) + abort (); + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_CYC, ("adding %s (%d) to cycle", cursym->name, cursym->histo)); + current_cycle_pointer->histo+=cursym->histo; + cursym->cycnum=number_of_cycles; + add_to_lists (current_cycle_pointer, cursym, 0); + + /* Now queue the subroutines of this function to be scanned + eventually. */ + + for (u=cursym->calls; u; u=u->next_to) + if (u->sym_to->cycnum==0) { + push_ring_buffer (ring_buffer, (void *)(u->sym_to)); + } else if (u->sym_to->name[0] == '<') { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_CYC, ("Cycle calls %s (%d)", u->sym_to->name, u->ncalls)); + add_to_lists (current_cycle_pointer, u->sym_to, u->ncalls); + } + } + + /* The cycle's list of children now contains all the members of the cycle. + Occasionally a function creeps in that doesn't belong in the cycle. + Find and remove them. */ + + { + struct symlist *v; + int found; + + do { + found = 0; + for (u=current_cycle_pointer->calls; u; u=u->next_to) { + for (v=u->sym_to->called; v; v=v->next_from) + if (v->sym_from->name[0]!='<' && v->sym_from->cycnum==number_of_cycles) + break; + if (!v) { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_CYC, ("%s not really in cycle", u->sym_to->name)); + /* This 'cycle member' wasn't called by any member of the cycle. + thus, it isn't a cycle member. */ + u->sym_to->cycnum=0; + delete_from_lists (current_cycle_pointer, u->sym_to); + found++; + } + } + } while (found); + } + /* The cycle's lists of callers and children are now correct. + Propagate the time through the cycle. */ + + + /* Now we propagate time INTO the + cycle from its children. We could do this in flushfuns, + but its easier to do here, since doing it here guarentees + it only happens once per cycle */ + + for (u=current_cycle_pointer->calls; u; u=u->next_to) { + struct mesym *symP; + struct symlist *v; + + /* We have to remember to not propagate time that's already here. . . */ + if (u->sym_to->cycnum!=number_of_cycles) { + current_cycle_pointer->ncalls+=u->ncalls; + + if (u->sym_to->cycnum==-1) + symP= u->sym_to; + else /* Propagate time FROM another cycle here */ + symP= cycles[u->sym_to->cycnum-1]; + u->prop_time= (FLOAT)u->ncalls*(FLOAT)symP->histo/(FLOAT)symP->ncalled; + u->child_time=(FLOAT)u->ncalls* symP->sub_histo /(FLOAT)symP->ncalled; + current_cycle_pointer->sub_histo+=u->prop_time+u->child_time; + for (v=u->sym_to->called; v; v=v->next_from) { + if (v->sym_from->cycnum==number_of_cycles) { + v->prop_time= (FLOAT)v->ncalls*(FLOAT)symP->histo/(FLOAT)symP->ncalled; + v->child_time=(FLOAT)v->ncalls* symP->sub_histo /(FLOAT)symP->ncalled; + } + } + } else { + u->prop_time= u->sym_to->histo; + u->child_time=u->sym_to->sub_histo; + + for (v=u->sym_to->calls; v; v=v->next_to) { + if (v->sym_to->cycnum==number_of_cycles) { + add_to_lists (current_cycle_pointer, v->sym_to, v->ncalls); + current_cycle_pointer->recursive+=v->ncalls; + } else { + symP = v->sym_to; + v->prop_time= (FLOAT)v->ncalls*(FLOAT)symP->histo/(FLOAT)symP->ncalled; + v->child_time=(FLOAT)v->ncalls* symP->sub_histo /(FLOAT)symP->ncalled; + } + } + + for (v=u->sym_to->called; v; v=v->next_from) { + assert (v->sym_from->cycnum==0 || v->sym_from->cycnum==number_of_cycles); + if (v->sym_from->cycnum==0) { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_CYC, ("Cycle called by %s (%d)", v->sym_from->name, v->ncalls)); + add_to_lists (v->sym_from, current_cycle_pointer, v->ncalls); + } + } + } + } + + + /* Sum all calls into the cycle, from each caller not in the cycle, + to get the number of calls into the cycle. */ + for (u=current_cycle_pointer->called; u; u=u->next_from) { + if (u->sym_from->cycnum!=number_of_cycles) + current_cycle_pointer->ncalled+=u->ncalls; + } + + /* Loop over functions in this cycle. */ + for (u=current_cycle_pointer->calls; u; u=u->next_to) { + struct symlist *v; + + if (u->sym_to->cycnum!=number_of_cycles) + continue; + + /* U->sym_to is a function in this cycle. */ + + /* Find all calls to this function from functions outside the cycle + and add remove them from the count of calls "from the cycle" to this function. */ + + for (v=u->sym_to->called; v; v=v->next_from) { + if (v->sym_from != current_cycle_pointer + && v->sym_from->cycnum==number_of_cycles) + u->sym_to->ncalled-=v->ncalls; + } + } + + /* Compute amounts of time to propagate out of the cycle + to the callers-in. */ + + for (u=current_cycle_pointer->called; u; u=u->next_from) + if (u->sym_from->cycnum != number_of_cycles) { + struct symlist *v; + + u->prop_time= ((FLOAT)u->ncalls*(FLOAT)current_cycle_pointer->histo /(FLOAT)current_cycle_pointer->ncalled); + u->child_time=((FLOAT)u->ncalls* current_cycle_pointer->sub_histo /(FLOAT)current_cycle_pointer->ncalled); + for (v=u->sym_from->calls; v; v=v->next_to) { + if (v->sym_to->cycnum==number_of_cycles) { + v->prop_time= ((FLOAT)v->ncalls*(FLOAT)current_cycle_pointer->histo /(FLOAT)current_cycle_pointer->ncalled); + v->child_time=((FLOAT)v->ncalls* current_cycle_pointer->sub_histo /(FLOAT)current_cycle_pointer->ncalled); + } + } + } + + flushfuns (); + } +} + + +/* Remove from the call tree all nodes that are rejected by + the -e, -E, -f and -F filters that were specified. + Their nodes are removed from functions_in_call_tree + and their edges are deleted from the lists they are in. */ + +void +filter_graph FUN0() +{ + int n; + int the_bomb_is_falling = 0; + + for (n=0; n<nfilters; n++) { + struct mesym **call_tree_pointer; + + call_tree_pointer=find_funp_from_name (filters[n].name); + /* Couldn't find it? Skip it! */ + if (!call_tree_pointer) { + /* It may have taken time, although it + isn't in the call tree. Seek and + destroy! */ + if (filters[n].type==BIG_E + || filters[n].type == REMOVE_TIME_IF_THERE) { + struct mesym *p; + + for (p=syms; p< &syms[nsym]; p++) { + if (!strcmp (p->name, filters[n].name)) { + tothist-=p->histo; + break; + } + } + if (p==&syms[nsym] && filters[n].type != REMOVE_TIME_IF_THERE) { + fprintf (stderr, "Warning: couldn't find function "); + fprint_name (stderr, filters[n].name); + fprintf (stderr, "\n"); + } + } else { + fprintf (stderr, "Warning: "); + fprint_name (stderr, filters[n].name); + fprintf (stderr, " is not in the call tree.\n"); + } + continue; + } + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Option %d on %s", filters[n].type, (*call_tree_pointer)->name)); + + switch (filters[n].type) { + case SMALL_E: + kill_children (*call_tree_pointer, FALSE); + break; + case BIG_E: + case REMOVE_TIME_IF_THERE: + kill_children (*call_tree_pointer, TRUE); + break; + case SMALL_F: + if (!the_bomb_is_falling) + the_bomb_is_falling = 1; + save_the_children (*call_tree_pointer, FALSE); + break; + case BIG_F: + if (!the_bomb_is_falling) { + the_bomb_is_falling = 1; + tothist=0; + } + save_the_children (*call_tree_pointer, TRUE); + break; + default: + abort (); + } + } + + /* If we had -e or -E filters, now delete everything that + was not marked to be saved. */ + + if (the_bomb_is_falling) { + struct mesym **call_tree_pointer; + + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("And now we drop the bomb.")); + call_tree_pointer = + &functions_in_call_tree[number_of_functions_in_call_tree-1]; + do { + if (((*call_tree_pointer)->flag&SAVE_ME)==0) + remove_from_call_tree (call_tree_pointer); + } while (call_tree_pointer-->&functions_in_call_tree[0]); + } +} + +/* Collect a list of parents/children, sort them, and print them */ +/* If CALLED > 0, we print parents, + and the value of CALLED is the total number of times this fn was called. + If CALLED == -2 we print children. + This is used for printing the children of an entire cycle. + If CALLED == -1, we print children and if their cycle number is the + same as CYCLE, we print abbreviated information. + This is used for printing the children of an ordinary function. */ +/* LIST is the list of parents/children we want to print */ + +void +print_sorted_list FUN3(int, called, int, cycle, struct symlist *, list) +{ + static struct symlist **sbuf; + static nsbuf, sizsbuf; + struct symlist **sbp, *t; + struct mesym *symP; + + if (!sizsbuf) { + sbuf=(struct symlist **)ck_calloc (30, sizeof (struct symlist *)); + nsbuf=0; + sizsbuf=30; + } + + /* Extract all the symbols in LIST as a vector, + but ignore any which represent entire cycles. */ + + t=list; + for (sbp= &sbuf[0]; t;) { + symP = (called>=0) ? t->sym_from : t->sym_to; + if (symP && symP->name[0] != '<') { + *sbp=t; + sbp++; + nsbuf++; + if (nsbuf==sizsbuf) { + sizsbuf*=2; + sbuf=(struct symlist **)ck_realloc ((void *)sbuf, sizsbuf*sizeof (struct symlist *)); + sbp= &sbuf[nsbuf]; + } + } else { + symP++; + + } + + if (called>=0) t=t->next_from; + else t=t->next_to; + } + + /* Sort the vector. */ + qsort (sbuf, nsbuf, sizeof (struct symlist *), listcmp); + + /* Print the elements of the vector. */ + for (sbp= &sbuf[0]; nsbuf>0; nsbuf--, sbp++) { + t= *sbp; + if (called>=0) symP=t->sym_from; + else symP=t->sym_to; + + if (cycle>0 && cycle==symP->cycnum) { + if (called==-2) { + printf (" %7.2f %7.2f %4d ", + convert_and_round ((FLOAT)(symP->histo)), + convert_and_round ((FLOAT)(symP->sub_histo)), + t->ncalls); + fprint_name (stdout, symP->name); + } else { + /* For things in the same cycle, we only want to print + the number of calls */ + printf ("%30s %4d ", "", t->ncalls); + fprint_name (stdout, symP->name); + } + } else { + printf (" %7.2f %7.2f %4d/%-4d ", + convert_and_round (t->prop_time), + convert_and_round (t->child_time), + t->ncalls, + (called>=0) ? called : symP->ncalled); + fprint_name (stdout, symP->name); + } + + if (symP->cycnum>0 && symP->name[0]!='<') + printf (" <cycle %d>", symP->cycnum); + + if (symP->numindex) + printf (" [%d]\n", symP->numindex); + else + printf (" [not printed]\n"); + } +} + +/* Compare two symbols for which should come first among + the callers or subroutines in a single call-graph entry. */ + +int +listcmp FUN2(const void *, a, const void *, b) +{ + struct symlist *aa, *bb; + FLOAT n; + + aa= *(struct symlist **)a; + bb= *(struct symlist **)b; + + n=(bb->prop_time + bb->child_time - aa->prop_time - aa->child_time); + + if (n<0) return -1; + if (n>0) return 1; + return 0; +} + +/* Convert a number (IN) from the histogram into seconds, rounding to the + nearest 100th of a second. */ +FLOAT +convert_and_round FUN1(FLOAT, in) +{ + long int inter; + + inter=((in/(FLOAT)(ticks))+.005)*100.0; + return ((FLOAT)(inter)/100.0); +} + +/* Given ncalls from fromP to toP, add a symlist element telling about it */ +void +add_to_lists FUN3(struct mesym *, fromP, struct mesym *, toP, unsigned, ncalls) +{ + struct symlist *tmp; + + for (tmp=fromP->calls; tmp; tmp=tmp->next_to) + if (tmp->sym_to==toP) { + tmp->ncalls+=ncalls; + break; + } + if (!tmp) { + tmp=(struct symlist *)ck_malloc (sizeof (struct symlist)); + tmp->sym_from=fromP; + tmp->next_from=toP->called; + tmp->sym_to=toP; + tmp->next_to=fromP->calls; + tmp->ncalls=ncalls; + tmp->prop_time = -1; + tmp->child_time = -1; + + fromP->calls=tmp; + toP->called=tmp; + } +} + + +/* The reverse of add_to_lists. Forget that fromP ever called toP */ +void +delete_from_lists FUN2(struct mesym *, fromP, struct mesym *, toP) +{ + struct symlist *die, *tmp, *old; + + if (fromP->calls->sym_to==toP) { + die=fromP->calls; + fromP->calls=fromP->calls->next_to; + } else { + old=0; + for (tmp=fromP->calls; tmp; tmp=tmp->next_to) { + if (tmp->sym_to==toP) { + die=tmp; + old->next_to=tmp->next_to; + } + old=tmp; + } + } + + if (toP->called->sym_from==fromP) { + die=toP->called; + toP->called=toP->called->next_from; + } else { + for (tmp=toP->called; tmp; tmp=tmp->next_from) { + old=0; + if (tmp->sym_from==fromP) { + die=tmp; + old->next_from=tmp->next_from; + } + old=tmp; + } + } + free (die); +} + + +/* Implement the -e or -E option by deleting a certain function + and all its descendents from the call graph. + If FLUSHFLAG is set, remove its histogram time from the total, too. */ + +void +kill_children FUN2(struct mesym *, p, int, flushflag) +{ + struct symlist *t; + + push_ring_buffer (ring_buffer, p); + while (ring_buffer_isnt_empty (ring_buffer)) { + p=pop_ring_buffer (ring_buffer); + if (flushflag) + tothist-=p->histo; + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Flushing %s (%d)", p->name, flushflag ? p->histo : 0)); + p->flag|=KILL_ME; + remove_from_call_tree (find_funp_from_pointer (p)); + for (t=p->calls; t; t=t->next_to) { + if (t->sym_to->ncalled==t->ncalls + || (p->cycnum>0 && t->sym_to->cycnum==p->cycnum)) { + push_ring_buffer (ring_buffer, t->sym_to); + } else if (t->sym_to->cycnum>0 && t->sym_to->cycnum!=p->cycnum) { + if (cycles[t->sym_to->cycnum-1]->ncalled==t->ncalls) { + push_ring_buffer (ring_buffer, cycles[t->sym_to->cycnum-1]); + } + } else { + struct symlist *ztmp; + + for (ztmp=t->sym_to->called; ztmp; ztmp=ztmp->next_from) { + if ((ztmp->sym_from->flag&KILL_ME)==0) + break; + } + if (!ztmp) + push_ring_buffer (ring_buffer, t->sym_to); + else { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Not flushing %s. . . More parents", t->sym_to->name)); + /* Do we want to deal with removing FRACTIONS of time from + functions who were called from different places? If so, + code goes here. (F'rinstance, if half of the calls to + function X were made from here, cut its stored time in half */ + } + } + } + } +} + +/* This is the opposite of kill_children (). + -f or -F has been specified, so all call-graph nodes EXCEPT + the descendents of specified functions will be killed. + Find all these descendents and mark them to be saved + by setting the `flag' fields nonzero. + + If TIMEFLAG is set, the histogram-total has + already been nuked, and we should add our histogram time to + it in an attempt at reconstruction. . . */ + +void +save_the_children FUN2(struct mesym *, p, int, timeflag) +{ + struct symlist *t; + + push_ring_buffer (ring_buffer, p); + while (ring_buffer_isnt_empty (ring_buffer)) { + p=pop_ring_buffer (ring_buffer); + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Saving %s (%d)", p->name, p->histo)); + p->flag|=SAVE_ME; + if (p->cycnum>0) + cycles[p->cycnum-1]->flag|=SAVE_ME; + if (timeflag) + tothist+=p->histo; + for (t=p->calls; t; t=t->next_to) { + if ((t->sym_to->flag&SAVE_ME)==0) + push_ring_buffer (ring_buffer, t->sym_to); + } + } +} + +/* The bomb is falling, and PT has just been hit by severe doses of + radiation. Remove it from the call-tree vector */ +void +remove_from_call_tree FUN1(struct mesym **, pt) +{ + if (!pt) { + /* Just quietly returning allows us to remove cycles from + the call tree easily. */ + /* panic ("Internal Error: trying to remove null from call tree"); */ + return; + } + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_OPT, ("Removing %ss from the tree", (*pt)->name)); + *pt=functions_in_call_tree[number_of_functions_in_call_tree-1]; + number_of_functions_in_call_tree--; +} + +/* We want to play with a member of the call tree, but we + only know its name. Try to find the funp. This is slow, + natch, since it uses linear search, but it doesn't get called much */ + +/* Should be some way to scan rest of symbols so we could delete + mcount/mcleanup histogram ticks. Should be way to disable warning? */ +struct mesym ** +find_funp_from_name FUN1(char *, name) +{ + struct mesym **ret; + + ret= &functions_in_call_tree[number_of_functions_in_call_tree-1]; + do { + if (strcmp ((*ret)->name, name)==0) + return ret; + } while (ret-->&functions_in_call_tree[0]); + return 0; +} + +/* We have the mesym, but we need to know where it lives in the call-tree + This is almost as slow as find_funp_from_name (). */ +struct mesym ** +find_funp_from_pointer FUN1(struct mesym *, p) +{ + struct mesym **ret; + + ret= &functions_in_call_tree[number_of_functions_in_call_tree-1]; + do { + if (*ret==p) + return ret; + } while (ret-->&functions_in_call_tree[0]); + /* fprintf (stderr, "Warning: Can't find %s in the call tree.\n", p->name); */ + return 0; +} + +/* Read symbols from a.out file open on FP. There are N of them. Allocate a + vector to store the interesting ones in, and sort it numerically. */ + +void +read_syms FUN2(FILE *, fp, int, n) +{ + struct nlist *tmpsyms; + struct nlist *sym; + int i; +#ifdef __STDC__ + char buf[n]; +#else + char *buf; + char *alloca (); + + buf=alloca (n); +#endif + /* Read the entire symbol table. */ + tmpsyms=(struct nlist *)ck_malloc (n*sizeof (struct nlist)); + ck_fread (tmpsyms, sizeof (struct nlist), n, fp); + + bzero (buf, n); + + /* Count the useful symbols. + Also relocate their name-fields to be C string pointers. */ + for (sym= &tmpsyms[0], i=0; sym<&tmpsyms[n]; sym++) { + sym->n_un.n_name= strs+sym->n_un.n_strx; + if (!badsym (sym)) + buf[sym-tmpsyms] = 1, i++; + } + + /* Allocate permanent space and copy useful symbols into it. */ + nsym=i; + syms=(struct mesym *)ck_calloc (nsym, sizeof (struct mesym)); + + for (sym= &tmpsyms[0], i=0; sym<&tmpsyms[n]; sym++) { + if (!badsym (sym)) { + syms[i].name=sym->n_un.n_name; + +#ifndef nounderscore + /* Remove the initial _ from the symbol name */ + if (*(syms[i].name)=='_') + syms[i].name++; +#endif + syms[i].value=sym->n_value; + i++; + } + else if (buf[sym-tmpsyms]) + abort (); + } + + if (i != nsym) + abort (); + + /* Put symbols in numeric order. */ + qsort (syms, nsym, sizeof (struct mesym), symcmp); + free ((void *)tmpsyms); +} + +/* Return the symbol which has the largest value less than VAL. + Since the symbol vector is sorted by value, this is done + with a binary search. */ + +struct mesym * +val_to_sym FUN1(unsigned long, val) +{ + struct mesym *m; + int gap=nsym/4; + + m= &syms[nsym/2]; + for (;;) { + if (m->value>val) { + m-=gap; + gap/=2; + } else if ((m+1)->value<val) { + m+=gap; + gap/=2; + } else + break; + if (m<&syms[0] || m>=&syms[nsym]) + abort (); + if (gap<1) + gap=1; + } + return m; +} + +/* Return TRUE if the nlist-entry SYM describes a symbol + that gprof should pay attention to. */ + +int +badsym FUN1(struct nlist *, sym) +{ +#ifndef N_SECT + if ((sym->n_type & ~N_EXT) != N_TEXT) + return TRUE; +#else + if ((sym->n_type & ~N_EXT) != N_TEXT && (sym->n_type & ~N_EXT) != N_SECT) + return TRUE; +#endif + if (no_locals && !(sym->n_type&N_EXT)) + return TRUE; + /* Filenames or pascal labels should be ignored */ + if (index (sym->n_un.n_name, '.') || index (sym->n_un.n_name, '$')) + return TRUE; + return FALSE; +} + +/* This is used to qsort () the symbol table into numerical order. */ + +int +symcmp FUN2(const void *, a, const void *, b) +{ + struct mesym *aa, *bb; + + aa=(struct mesym *)a; + bb=(struct mesym *)b; + return aa->value - bb->value; +} + +/* This is used to sort the vector for the flat profile. + if they used different amounts of time, the one with + the most time goes first. If they used the same amount, + the one that was called most goes first. If they were + called the same #, they are sorted alphabetically */ + +int +timecmp FUN2(const void *, a, const void *, b) +{ + struct mesym *aa, *bb; + int n; + + aa= *(struct mesym **)a; + bb= *(struct mesym **)b; + n = bb->histo - aa->histo; + if (n==0) n=bb->ncalled - aa->ncalled; + if (n==0) n=strcmp (aa->name, bb->name); + return n; +} + +/* This is used to sort the functions_in_call_tree[] vector + so the leaf nodes are at the end, and the root nodes + are at the beginning. This bit about useless nodes could + probably be taken out now, since they shouldn't make it + into the vector in the first place */ + +int +callcmp FUN2(const void *, a, const void *, b) +{ + struct mesym *aa, *bb; + + aa= *(struct mesym **)a; + bb= *(struct mesym **)b; + + /* Send useless symbols to the end */ + if (aa->ncalls==0 && aa->ncalled==0) { + if (bb->ncalls==0 && bb->ncalled==0) + return EQ; + return GT; + } + if (bb->ncalled==0 && bb->ncalls==0) + return LT; + + /* send root nodes to the front; */ + /* Functions that were called 0 times + are spontaneous */ + + if (aa->ncalled==0) { + if (bb->ncalled==0) + return EQ; + return LT; + } + if (bb->ncalled==0) + return GT; + + /* Send leaf nodes to the end */ + if (aa->ncalls==0) { + if (bb->ncalls==0) + return EQ; + return GT; + } + if (bb->ncalls==0) + return LT; + + /* And keep the rest the same */ + return EQ; +} + +/* This is used to sort functions_in_call_tree[] before printing. + To print, we want + A: the ones with the most time first + a: (If one was never called, put it first, 'cuz it + was invoked by GOD, else the one called the most + # of times first) + 1: the one with the lower name (strcmp ()) first + */ + +int +treetimecmp FUN2(const void *, a, const void *, b) +{ + struct mesym *aa, *bb; + int n; + + aa= *(struct mesym **)a; + bb= *(struct mesym **)b; + n = (bb->histo+bb->sub_histo) - (aa->histo+aa->sub_histo); + if (n==0) { + + /* Just to be bizarre: If one was never called, + put it first, else put the one who was called + the most first. Confused yet? */ + if (aa->ncalled==0) + return LT; + if (bb->ncalled==0) + return GT; + n=bb->ncalled - aa->ncalled; + if (n==0) + n=strcmp (aa->name, bb->name); + } + return n; +} + +/* Read in a gmon.out file named NAME and deal with the stuff inside it. */ + +void +readgm FUN1(char *, name) +{ + FILE *fp; + struct gm_header tmp; + unsigned CHUNK *p; + int n; + struct gm_call calltmp; + + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_GFILE, ("gmon from `%s'", name)); + + fp=ck_fopen (name, "r"); + + /* Read in the gmon file header and check that histogram is + compatible with the other gmon files already read. */ + + ck_fread ((void *)&tmp, sizeof (tmp), 1, fp); + if (hdr.low) { + if (hdr.low!=tmp.low || hdr.high!=tmp.high) + fatal ("file `%s' is incompatable with previous gmon.out file", name); + } else + hdr=tmp; + + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_GFILE, ("lowpc=%ld hipc=%ld nbytes= %ld", hdr.low, hdr.high, hdr.nbytes)); + + /* Allocate the total histogram if we haven't yet done so. */ + + if (!histo) { + nhist=(hdr.nbytes-sizeof (struct gm_header))/sizeof (CHUNK); + histo=(unsigned CHUNK *)ck_calloc (nhist, sizeof (unsigned CHUNK)); + } + + /* Read this file's histogram and merge it into the total one. */ + + p=(unsigned CHUNK *)ck_malloc (nhist*sizeof (unsigned CHUNK)); + ck_fread ((void *)p, sizeof (unsigned CHUNK), nhist, fp); + for (n=0; n<nhist; n++) { + tothist+=p[n]; + histo[n]+=p[n]; + if (debug&DB_GFILE) { + static ncol; + + if (n==0) ncol=0; + if (histo[n]) { + fprintf (stderr, "s[%05d]=%-3d", n, histo[n]); + if (ncol++%10==9) fputc ('\n', stderr); + } + } + } + free ((void *)p); + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_GFILE, ("")); + + /* We've read in the histogram, now read in the function call data. + Loop, reading one call-graph edge from the file + and recording the edge in the graph. */ + + while (fread ((void *)&calltmp, sizeof (calltmp), 1, fp)==1) { + struct symlist *tmp; + struct mesym *s_fm, *s_to; + + /* Find the calling and called functions's symbol entries. */ + + s_fm=val_to_sym (calltmp.from); + s_to=val_to_sym (calltmp.to); + + if (!s_fm || !s_to) { + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_GFILE, ("unknown call %#lx to %#lx %d times.", calltmp.from, calltmp.to, calltmp.ncalls)); + continue; + } + + /* Updated total numbers of calls from this caller and to this callee. */ + + s_to->ncalled+=calltmp.ncalls; + s_fm->ncalls+=calltmp.ncalls; + + /* Add these calls to the edge between them. */ + + add_to_lists (s_fm, s_to, calltmp.ncalls); + + PRINT_OBNOXIOUS_DEBUG_MESSAGE + (DB_GFILE, ("call %s (%#lx) to %s (%#lx) %ld times", s_fm->name, + calltmp.from, s_to->name, calltmp.to, calltmp.ncalls)); + } + + ck_fclose (fp); +} + +/* Print one of those obnoxious and vaguely informative blurbs that we know + and love so well. Used to be, we'd print them out of a file, but nowaday + the blurb is encoded direcly into the program. Makes printing them + much faster. Also means bozo syswiz can't accidentally delete/move/etc the + blurb file on us. */ + +void +print_blurb FUN1(char *, blurb) +{ + if (! no_blurbs) + fputs (blurb, stdout); +} + + +/* Find the number of clock ticks/second by reading the kernel's memory. + This means that if /dev/kmem isn't readable, this program will have to run + set[ug]id to someone who can read the kernel. setgid is probably best. */ + +long +get_ticks FUN0() +{ + static struct nlist nl[2]; + long ret; + FILE *fp; + +#ifdef USG +#include <sys/types.h> +#include <sys/param.h> + ret = HZ; +#else + nl[0].n_un.n_name="_hz"; + nlist ("/vmunix", &nl[0]); + if (nl[0].n_type==0) + fatal ("Can't find `%s' in namelist of /vmunix", nl[0].n_un.n_name); + fp=ck_fopen ("/dev/kmem", "r"); + ck_fseek (fp, (long)nl[0].n_value, 0); + ck_fread ((void *)&ret, sizeof (ret), 1, fp); + fclose (fp); /* This really should be ck_fclose, but on some systems, + closing devices returns -1, errno=NOT_OWNER, which + really screws things up. . . */ +#endif + PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_MISC, ("get_ticks ()=%ld", ret)); + return ret; +} + + +/* Record one -e, -E, -f or -F option in `filters', and check for conflicts. + All these options are recorded there for processing later + once the call-graph has been constructed. */ + +void +add_filter FUN2(char *, name, int, type) +{ + if (!filters) { + filters=(struct filter *)ck_malloc (sizeof (struct filter)); + nfilters=1; + } else { + int n; + + for (n=0; n<nfilters; n++) { + if (filters[n].name==name || !strcmp (filters[n].name, name)) + fatal ("Conflicting options for function `%s'", name); + } + nfilters++; + filters=(struct filter *)ck_realloc ((void *)filters, sizeof (struct filter)*nfilters); + } + filters[nfilters-1].name=name; + filters[nfilters-1].type=type; +} + +/* Data base associating stdio streams with the file names that are open. */ + +struct file_to_name { + FILE *stream; /* A stdio stream */ + char *name; /* The file name (malloc'd specially for this list) */ + struct file_to_name *next; +}; + +struct file_to_name *files_to_names; + +/* Given a stdio stream, look it up in the data base and return + the file name. */ + +char * +stream_name FUN1(FILE *, stream) +{ + struct file_to_name *tail; + + for (tail = files_to_names; tail; tail = tail->next) + if (tail->stream == stream) + return tail->name; + + return "unknown file"; +} + +/* Open a file like `fopen', but report a fatal error if it fails; + if it succeeds, record the stream and filename in the data base of such. */ + +FILE * +ck_fopen FUN2(char *, name, char *, mode) +{ + FILE *stream; + int n; + struct file_to_name *new; + + stream=fopen (name, mode); + if (stream==(FILE *)0) + fatal_io ("Couldn't open", name); + + new = ck_malloc (sizeof (struct file_to_name)); + new->name = ck_malloc (strlen (name) + 1); + strcpy (new->name, name); + new->stream = stream; + new->next = files_to_names; + files_to_names = new; + return stream; +} + +/* Interfaces to various functions of stdio + which use the stream/filename database to print an error message + if the function gets an error. */ + +void +ck_fseek FUN3(FILE *, stream, long, i, int, w) +{ + if (fseek (stream, i, w)==-1) + fatal_io ("Couldn't lseek", stream_name (stream)); +} + +void +ck_fread FUN4(void *, ptr, size_t, size, size_t, nmemb, FILE *, stream) +{ + if (fread (ptr, size, nmemb, stream)!=nmemb) + fatal_io ("Couldn't read", stream_name (stream)); +} + +void +ck_fwrite FUN4(void *, ptr, size_t, size, size_t, nmemb, FILE *, stream) +{ + if (fwrite (ptr, size, nmemb, stream)!=nmemb) + fatal_io ("Couldn't write", stream_name (stream)); +} + +void +ck_fclose FUN1(FILE *, stream) +{ + struct file_to_name *tail; + + if(stream->_flag & _IOWRT) + fflush (stream); + if (ferror (stream)) + fatal ("I/O error on `%s'", stream_name (stream)); + if (fclose (stream)==EOF) + fatal_io ("Couldn't close", stream_name (stream)); + + if (files_to_names && files_to_names->stream == stream) + files_to_names = files_to_names->next; + else + for (tail = files_to_names; tail->next; tail = tail->next) + if (tail->next->stream == stream) + { + struct file_to_name *loser = tail->next; + tail->next = tail->next->next; + free (loser->name); + free (loser); + return; + } +} + +/* Report a fatal error doing I/O, and exit. + PROBLEM is "Couldn't whatever" and NAME is the file name. */ + +void +fatal_io FUN2(char *, problem, char *, name) +{ + fprintf (stderr, "%s: %s %s:", myname, problem, name); + perror (0); + exit (1); +} + +/* Report a fatal error and exit. Arguments like `printf'. */ + +void +fatal FUN1N(char *, s) +{ + va_list iggy; + + var_start (iggy, s); + fprintf (stderr, "%s: ", myname); + vfprintf (stderr, s, iggy); + /* _doprnt (s, iggy, stderr); */ + putc ('\n', stderr); + va_end (iggy); + + exit (1); +} + +/* Memory allocation functions. */ + +/* This function is called just like `printf' + except that the output is put into a new string + allocated with `malloc'. + Returns the address of the string. The caller must free the string. */ + +char * +mk_sprintf FUN1N(char *, str) +{ + char tmpbuf[2048]; + char *ret; + va_list iggy; + + var_start (iggy, str); + vsprintf (tmpbuf, str, iggy); + va_end (iggy); + + ret=ck_malloc (strlen (tmpbuf)+1); + strcpy (ret, tmpbuf); + return ret; +} + +/* Encapsulations of `malloc', `calloc' and `realloc' + that cause fatal errors if there is not enough memory. */ + +void * +ck_malloc FUN1(size_t, size) +{ + void *ret; + void *malloc (); + + ret=malloc (size); + if (ret==(void *)0) + fatal ("Virtual memory exhausted"); + return ret; +} + +void * +ck_calloc FUN2(size_t, nmemb, size_t, size) +{ + void *ret; + void *calloc (); + + ret=calloc (nmemb, size); + if (ret==(void *)0) + fatal ("Virtual memory exhausted"); + return ret; +} + +void * +ck_realloc FUN2(void *, ptr, size_t, size) +{ + void *ret; + void *realloc (); + + ret=realloc (ptr, size); + if (ret==(void *)0) + fatal ("Virtual memory exhausted"); + return ret; +} + +/* Implement an expandable fifo buffer of pointers + (we don't care what they point to) + which is used for conducting breadth-first tree walks in the call graph. + + The fifo is implemented as a ring buffer. */ + +struct ring_buf +{ + void **buf; + int size; + void **push_to_here; + void **pop_frm_here; +}; + +void * +init_ring_buffer FUN0() +{ + struct ring_buf *ret; + + ret=ck_malloc (sizeof (struct ring_buf)); + ret->size=40; + ret->buf=(void **)ck_calloc (ret->size, sizeof (void **)); + ret->push_to_here=ret->buf; + ret->pop_frm_here=ret->buf; + return ret; +} + +void +push_ring_buffer FUN2(void *, b, void *, n) +{ + struct ring_buf *buf; + + buf=(struct ring_buf *)b; + if (buf->push_to_here+1==buf->pop_frm_here + || (buf->pop_frm_here==buf->buf + && buf->push_to_here==buf->buf+(buf->size-1))) { + int f, t, from_num; + + f=buf->pop_frm_here-buf->buf; + t=buf->push_to_here-buf->buf; + from_num=buf->size-f; + + buf->size*=2; + buf->buf=ck_realloc ((void *)buf->buf, buf->size*sizeof (void **)); + if (t==0) { + buf->push_to_here=buf->buf+f+from_num; + buf->pop_frm_here=buf->buf+f; + } else if (t>f) { + buf->push_to_here=buf->buf+t; + buf->pop_frm_here=buf->buf+f; + } else { + buf->push_to_here=buf->buf+t; + buf->pop_frm_here=buf->buf+(buf->size-from_num); + if (from_num) + bcopy (buf->buf+f, + buf->pop_frm_here, + from_num*sizeof (void **)); + } + } + *(buf->push_to_here)=n; + buf->push_to_here++; + if (buf->push_to_here==buf->buf+buf->size) + buf->push_to_here=buf->buf; +} + +void * +pop_ring_buffer FUN1(void *, b) +{ + struct ring_buf *buf; + void *ret; + + buf=(struct ring_buf *)b; + ret= *(buf->pop_frm_here); + buf->pop_frm_here++; + if (buf->pop_frm_here==buf->buf+buf->size) + buf->pop_frm_here=buf->buf; + return ret; +} + +int +ring_buffer_isnt_empty FUN1(void *, b) +{ + struct ring_buf *buf; + + buf=(struct ring_buf *)b; + return buf->pop_frm_here!=buf->push_to_here; +} + +void +flush_ring_buffer FUN1(void *, b) +{ + struct ring_buf *buf; + + buf=(struct ring_buf *)b; + free (buf->buf); + free (buf); +} + + +/* Functions to print debugging messages. */ + +/* Like vprintf but print an extra newline at the end. */ + +void +dbgprintf FUN1N(char *, s) +{ + va_list iggy; + + var_start (iggy, s); + vfprintf (stderr, s, iggy); + putc ('\n', stderr); + va_end (iggy); +} + +/* Print out the entire symbol table */ + +void +dumpsyms FUN0() +{ + struct mesym *np, *endp; + struct symlist *sy; + int n; + char buf[80]; + + n=0; + for (np=syms, endp= &syms[nsym]; np<endp; np++) { + char *demangled = cplus_demangle (np->name); + char *name = demangled == NULL ? np->name : demangled; + + sprintf (buf, "%-15s %#6lx %d %2d.%2d[%d]", + name, np->value, np->histo, np->ncalled, np->ncalls, np->cycnum); + if (demangled != NULL) + free (demangled); + + if (n==0) { + printf ("%-35s", buf); + n++; + } else { + printf ("%s\n", buf); + n=0; + } + } + putchar ('\n'); + if (n==0) putchar ('\n'); +} + +/* Print out the call tree vector */ +void +dumpfuns FUN0() +{ + struct mesym *np; + struct symlist *sy; + int n; + + for (n=0; n<number_of_functions_in_call_tree; n++) { + np=functions_in_call_tree[n]; + fprint_name (stdout, np->name); + printf ("{%d}[%d] ", np->cycnum, np->ncalls); + /* for (sy=np->called; sy; sy=sy->next) + printf ("%s{%d}(%d) ", sy->sym->name, sy->sym->cycnum, sy->ncalls); + putchar ('\n'); */ + } +} + +/* JF someone put this stuff in the file before all the prototyes et all. + I moved them here where they belong */ + +/* Like malloc but abort if out of memory. */ +void * +xmalloc FUN1(size_t, size) +{ + return ck_malloc(size); +} + +/* Like realloc but abort if out of memory. */ +void * +xrealloc FUN2(void *, p, size_t, size) +{ + return ck_realloc(p,size); +} + +/* Print NAME on STREAM, demangling if necessary. */ +void +fprint_name FUN2(FILE *, stream, char *, name) +{ + char *demangled = cplus_demangle (name); + if (demangled == NULL) + fputs (name, stream); + else + { + fputs (demangled, stream); + free (demangled); + } +} |
