home *** CD-ROM | disk | FTP | other *** search
- /* `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>
- #include <string.h>
-
- #ifdef __GNUC__
- #define alloca __builtin_alloca
- #else /* not __GNUC__ */
- #ifdef sparc
- #include <alloca.h>
- #endif
- #endif /* not __GNUC__ */
-
- #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
-
- #ifdef atarist
- # include <st-out.h>
- #else
- # 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
- #endif
-
- #ifdef HAVE_SETITIMER
- #include <sys/time.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 atarist
- # ifdef VPRINTF_MISSING
- # undef VPRINTF_MISSING
- # endif
- #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;
-
- /* Header of the executable file. */
- #ifdef atarist
- struct aexec exec_header;
- #else
- struct exec exec_header;
- #endif
-
- /* 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;
-
- #ifdef atarist
- /* The maximal length of an symbol name.
- This is initially eight, but will be changed to 22 in the code
- if any GST compatible long symbols are detected in the symbol table.
- NOTE: do not change it manually here */
- int max_atari_sym_length = 8;
- #endif
-
- /* 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 -v option tells gprof to caugh up its version and Patchlevel */
- int version = 0; /* -v 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 **);
- #ifndef atarist
- int read_header_info EXT6 (char *, FILE *, long int *, unsigned int *,
- long int *, unsigned int *);
- #endif
- 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);
- #ifndef atarist
- int badsym EXT1(struct nlist *);
- #else
- int badsym EXT1(struct asym *);
- #endif
- 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 *);
- void print_version EXT0();
-
- /* 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 *);
- #ifdef __STDC__
- size_t fread EXT4(void *, size_t, size_t, FILE *);
- #else
- int fread EXT4(void *, size_t, size_t, FILE *);
- #endif
-
- #ifndef atarist
- int nlist EXT2(const char *, struct nlist *);
- #endif
-
- #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;
- int n;
-
- extern char *optarg;
- extern int optind, opterr;
-
- #ifndef __STDC__
- static
- #endif
- 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},
- {"version", 0, 0, 'v'},
- {NULL, 0, NULL, 0}
- };
-
- char *name = '\0';
- int ind;
- #ifndef atarist
- long int syms_offset;
- unsigned int syms_size;
- long int strs_offset;
- unsigned int strs_size;
- #endif
-
- #ifdef atarist
- _malloczero(1);
- #endif
- 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;
- case 'v':
- version = 1;
- break;
- default:
- print_version();
- fprintf (stderr, "\
- Usage: %s [-absvz] [-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] [+version] [+zeros] executable gmon.out...\n",
- myname);
- exit(1);
- }
- }
- if (optind<argc) {
- exec_file_name=argv[optind];
- optind++;
- }
- if(version) {
- print_version();
- }
- if (!exec_file_name)
- exec_file_name=OBJ_NAM;
-
- /* Open the a.out file, and read in selected portions */
- #ifdef atarist
- fp=ck_fopen (exec_file_name, "rb");
- ck_fread ((void *)&exec_header, sizeof (exec_header), 1, fp);
-
- /* Make sure its really an a.out file. If it isn't yell and scream
- and stamp our feet. */
- if (A_BADMAG (exec_header))
- fatal ("`%s' is not an executable file", exec_file_name);
-
- if(exec_header.a_syms == 0L)
- fatal ("`%s' has no symbols", exec_file_name);
-
- /* Read the symbols, and put the interesting ones (sorted) in SYMS. */
- /* read_syms builds strs too */
- ck_fseek (fp, A_SYMOFF (exec_header), 0);
- read_syms (fp, exec_header.a_syms/sizeof (struct asym));
-
- #else /* ! atarist */
-
- /* 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));
- #endif /* atarist */
-
- 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;
-
- #ifdef atarist
- /* NOTE: The 2 should track HIST_SCALE in lib/gmon.c */
- int incr = (hdr.high - hdr.low) / nhist;
- if ((hdr.high - hdr.low) != (2 * nhist) )
- #else
- int incr = (hdr.high - hdr.low) / (nhist - 1);
- if ((hdr.high - hdr.low) != (4 * (nhist -1)) )
- #endif
- exit (91);
-
- #ifndef atarist /* on the St we dont relocate at 0 */
- for (n=0, pos=hdr.low, sym= &syms[0]; n<nhist; n++, pos+=incr) {
- #else
- for (n=0, pos=0, sym= &syms[0]; n<nhist; n++, pos+=incr) {
- #endif
- 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 ();
- return(0);
- }
-
- #ifndef atarist
- /* 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;
- }
- #endif /* !atarist */
-
- /* 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;
-
- #ifdef atarist
- fp=ck_fopen (SUM_NAM, "wb");
- #else
- fp=ck_fopen (SUM_NAM, "w");
- #endif
- 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 */
- #ifndef atarist
- call_tmp.from=p->value+FUDGE_FACTOR;
- call_tmp.to=t->sym_to->value+FUDGE_FACTOR;
- /* call_tmp.from=(p->value+FUDGE_FACTOR)-hdr.low;
- call_tmp.to=(t->sym_to->value+FUDGE_FACTOR)-hdr.low; */
- #else
- call_tmp.from=(p->value+FUDGE_FACTOR)+hdr.low;
- call_tmp.to=(t->sym_to->value+FUDGE_FACTOR)+hdr.low;
- #endif
- 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
- = (struct mesym **)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)
- exit (92); */
-
- 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)
- exit (93);
- 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++) {
- #ifndef atarist
- if (!strcmp (p->name, filters[n].name)) {
- #else
- if (!strncmp (p->name, filters[n].name, max_atari_sym_length)) {
- #endif
- 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:
- exit (94);
- }
- }
-
- /* 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;
- }
-
- #ifndef atarist /* atarist specific version at end */
-
- /* 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;
- syms[i].value=sym->n_value;
- i++;
- }
- else if (buf[sym-tmpsyms])
- exit (95);
- }
-
- if (i != nsym)
- exit (96);
-
- /* Put symbols in numeric order. */
- qsort (syms, nsym, sizeof (struct mesym), symcmp);
- free ((void *)tmpsyms);
- }
- #endif /* atarist */
-
- /* Return the symbol which has the largest value less than or equal to 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) { /* *********** TEST ********* */
- /* } else if ((m+1)->value<val) { */
- m+=gap;
- gap/=2;
- } else
- break;
- if (m<&syms[0] || m>=&syms[nsym])
- exit (97);
- if (gap<1)
- gap=1;
- }
- return m;
- }
-
- #ifndef atarist
- /* Return TRUE if the nlist-entry SYM describes a symbol
- that gprof should NOT pay attention to. */
-
- int
- badsym FUN1(struct nlist *, sym)
- {
- int local;
- #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
- local = !(sym->n_type&N_EXT);
- if (no_locals && !local)
- return TRUE;
- /* Filenames or pascal labels should be ignored */
- if (local
- && (index (sym->n_un.n_name, '.')
- || index (sym->n_un.n_name, '$')
- #ifndef nounderscore
- || sym->n_un.n_name[0] != '_'
- #endif
- ))
- return TRUE;
- return FALSE;
- }
- #endif
-
- /* 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));
-
- #ifdef atarist
- fp=ck_fopen (name, "rb");
- #else
- fp=ck_fopen (name, "r");
- #endif
-
- /* 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. */
-
- #ifndef atarist /* on the St we dont relocate at zero */
- s_fm=val_to_sym (calltmp.from);
- s_to=val_to_sym (calltmp.to);
- #else
- s_fm=val_to_sym (calltmp.from - hdr.low);
- s_to=val_to_sym (calltmp.to - hdr.low);
- #endif
-
- 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);
- }
-
- #ifdef HAVE_SETITIMER
-
- get_ticks()
- {
- struct itimerval tim;
-
- tim.it_interval.tv_sec = 0;
- tim.it_interval.tv_usec = 1;
- timerclear(&tim.it_value);
- if (setitimer(ITIMER_REAL, &tim, 0) < 0
- || setitimer(ITIMER_REAL, 0, &tim) < 0) {
- fprintf(stderr, "%s: setitimer: ", myname);
- perror((char *)0);
- exit(1);
- }
- return 1000000/tim.it_interval.tv_usec;
- }
-
- #else /* not HAVE_SETITIMER */
-
- /* 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()
- {
- #ifndef atarist
- static struct nlist nl[2];
- #endif
- long ret;
- FILE *fp;
- #ifdef atarist
- ret = 50; /* 200 hz timer / 4, see lib/gmon.c */
- #else
- #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
- #endif
- PRINT_OBNOXIOUS_DEBUG_MESSAGE (DB_MISC, ("get_ticks ()=%ld", ret));
- return ret;
- }
- #endif /* not HAVE_SETITIMER */
-
- /* 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 (101);
- }
-
- /* 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 (102);
- }
-
- /* 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;
-
- if (demangled)
- name = demangled;
- else {
- name = np->name;
- #ifndef nounderscore
- if (*name == '_')
- name++;
- #endif
- }
-
- 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'); */
- }
- }
-
- #ifdef atarist /* atariSt specific versions */
-
- /* Return TRUE if the asym-entry SYM describes a symbol
- that gprof should NOT pay attention to. */
-
- #define ISDRIVE(d) ( \
- (((d) >= 'A') && ((d) <= 'Z')) || \
- (((d) >= 'a') && ((d) <= 'z')) )
- int
- badsym FUN1(struct asym *, sym)
- {
- if(sym->a_type == A_UNDF)
- return TRUE; /* these are the symbols ld (write_atari_sym()) had faked */
- if (!(sym->a_type & A_TEXT))
- return TRUE;
- if (no_locals && !(sym->a_type & A_GLOBL))
- return TRUE;
- if(!strncmp(sym->n_un.ptr, "gcc_comp", 8L))
- return TRUE;
- if(ISDRIVE((sym->n_un.ptr)[0]) && ((sym->n_un.ptr)[1] == ':'))
- return TRUE;
- /* Filenames or pascal labels should be ignored */
- if (index (sym->n_un.ptr, '.') || index (sym->n_un.ptr, '$') ||
- index (sym->n_un.ptr, '/') || index (sym->n_un.ptr, '\\') )
- return TRUE;
- return FALSE;
- }
-
- /* 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 asym *tmpsyms;
- char *s, *p, *q;
- int i,j,is_long;
-
- /* creat space for incore name strings */
- s = strs = ck_malloc(n*25); /* slight overkill */
-
- /* Read the entire symbol table. */
- tmpsyms=(struct asym *)ck_malloc (n*sizeof (struct asym));
- ck_fread (tmpsyms, sizeof (struct asym), n, fp);
-
-
- /* Count the useful symbols.
- Also relocate their name-fields to be C string pointers. */
- for(nsym = 0, i = 0; i < n; i++)
- {
- for(j = 0, p = &(tmpsyms[i].n_un.a_name[0]), q = s;
- (j < 8) && (*p != '\0'); j++)
- *s++ = *p++;
- if ((tmpsyms[i].a_type & A_LNAM) == A_LNAM)
- {
- max_atari_sym_length = 22;
- is_long = TRUE;
- if (i + 1 < n)
- for (j = 0, p = &tmpsyms[i+1].n_un.a_name[0];
- j < 14 && *p != '\0'; j++)
- *s++ = *p++;
- else
- /* there's something wrong; assume short symbol */
- is_long = FALSE;
- }
- else
- is_long = FALSE;
- *s++ = '\0';
- tmpsyms[i].n_un.ptr = q;
- if (badsym (&tmpsyms[i]))
- {
- tmpsyms[i].n_un.ptr = (char *)0;
- }
- else
- {
- nsym++;
- }
- if (is_long)
- /* skip symbol name extension */
- tmpsyms[++i].n_un.ptr = NULL;
- }
-
- /* Allocate permanent space and copy useful symbols into it. */
- syms=(struct mesym *)ck_calloc (nsym, sizeof (struct mesym));
-
- for (i=0, j = 0; i < n; i++) {
- if (tmpsyms[i].n_un.ptr != (char *)0) {
- syms[j].name = tmpsyms[i].n_un.ptr;
- #ifndef nounderscore
- /* Remove the initial _ from the symbol name */
- if (*(syms[j].name)=='_')
- syms[j].name++;
- #endif
- syms[j].value = tmpsyms[i].a_value;
- j++;
- }
- }
-
- if (j != nsym)
- exit (99);
-
- free ((void *)tmpsyms);
-
- /* Put symbols in numeric order. */
- qsort (syms, nsym, sizeof (struct mesym), symcmp);
- }
- #endif /* atarist */
-
- void print_version FUN0()
- {
- #ifdef atarist
- # include "PatchLev.h"
- fprintf(stderr, "GNU gprof for atariST-TOS Patchlevel %s (%s)\n",
- PatchLevel, __DATE__);
- #else
- fprintf(stderr, "GNU gprof\n");
- #endif
- }
-
- /* 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)
- {
- #ifndef nounderscore
- if (*name == '_')
- name++;
- #endif
- fputs (name, stream);
- }
- else
- {
- fputs (demangled, stream);
- free (demangled);
- }
- }
-
- #if 0
- /* let this be dummy for now,
- if you want to throw in the cplusplus name demangler, simple
- delete this function, and link with cplus-dem.o
- */
- char *cplus_demangle FUN1(char *, name)
- {
- return NULL;
- }
- #endif
-