Index: head/usr.bin/gprof/sparc64.c =================================================================== --- head/usr.bin/gprof/sparc64.c (revision 130775) +++ head/usr.bin/gprof/sparc64.c (nonexistent) @@ -1,14 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -#include "gprof.h" - -/* - * gprof -c isn't currently supported... - */ -findcall( parentp , p_lowpc , p_highpc ) - nltype *parentp; - unsigned long p_lowpc; - unsigned long p_highpc; -{ -} Property changes on: head/usr.bin/gprof/sparc64.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/usr.bin/gprof/alpha.c =================================================================== --- head/usr.bin/gprof/alpha.c (revision 130775) +++ head/usr.bin/gprof/alpha.c (nonexistent) @@ -1,14 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -#include "gprof.h" - -/* - * gprof -c isn't currently supported... - */ -findcall( parentp , p_lowpc , p_highpc ) - nltype *parentp; - unsigned long p_lowpc; - unsigned long p_highpc; -{ -} Property changes on: head/usr.bin/gprof/alpha.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/usr.bin/gprof/ia64.c =================================================================== --- head/usr.bin/gprof/ia64.c (revision 130775) +++ head/usr.bin/gprof/ia64.c (nonexistent) @@ -1,14 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -#include "gprof.h" - -/* - * gprof -c isn't currently supported... - */ -findcall( parentp , p_lowpc , p_highpc ) - nltype *parentp; - unsigned long p_lowpc; - unsigned long p_highpc; -{ -} Property changes on: head/usr.bin/gprof/ia64.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/usr.bin/gprof/i386.c =================================================================== --- head/usr.bin/gprof/i386.c (revision 130775) +++ head/usr.bin/gprof/i386.c (nonexistent) @@ -1,15 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -#include "gprof.h" - -/* - * gprof -c isn't currently supported... - */ -void -findcall( parentp , p_lowpc , p_highpc ) - nltype *parentp; - unsigned long p_lowpc; - unsigned long p_highpc; -{ -} Property changes on: head/usr.bin/gprof/i386.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/usr.bin/gprof/amd64.c =================================================================== --- head/usr.bin/gprof/amd64.c (revision 130775) +++ head/usr.bin/gprof/amd64.c (nonexistent) @@ -1,15 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -#include "gprof.h" - -/* - * gprof -c isn't currently supported... - */ -void -findcall( parentp , p_lowpc , p_highpc ) - nltype *parentp; - unsigned long p_lowpc; - unsigned long p_highpc; -{ -} Property changes on: head/usr.bin/gprof/amd64.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/usr.bin/gprof/arm.c =================================================================== --- head/usr.bin/gprof/arm.c (revision 130775) +++ head/usr.bin/gprof/arm.c (nonexistent) @@ -1,15 +0,0 @@ -#include -__FBSDID("$FreeBSD$"); - -#include "gprof.h" - -/* - * gprof -c isn't currently supported... - */ -void -findcall( parentp , p_lowpc , p_highpc ) - nltype *parentp; - unsigned long p_lowpc; - unsigned long p_highpc; -{ -} Property changes on: head/usr.bin/gprof/arm.c ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/usr.bin/gprof/Makefile =================================================================== --- head/usr.bin/gprof/Makefile (revision 130775) +++ head/usr.bin/gprof/Makefile (revision 130776) @@ -1,10 +1,10 @@ # @(#)Makefile 8.1 (Berkeley) 6/29/93 # $FreeBSD$ PROG= gprof -SRCS= gprof.c aout.c arcs.c dfn.c elf.c lookup.c ${MACHINE_ARCH}.c hertz.c \ +SRCS= gprof.c aout.c arcs.c dfn.c elf.c lookup.c hertz.c \ printgprof.c printlist.c kernel.c FILES= gprof.flat gprof.callg FILESDIR= ${SHAREDIR}/misc .include Index: head/usr.bin/gprof/aout.c =================================================================== --- head/usr.bin/gprof/aout.c (revision 130775) +++ head/usr.bin/gprof/aout.c (revision 130776) @@ -1,236 +1,233 @@ /*- * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 /* From: */ #ifndef lint static char sccsid[] = "@(#)gprof.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include "gprof.h" static void getstrtab(FILE *, const char *); static void getsymtab(FILE *, const char *); static void gettextspace(FILE *); static bool funcsymbol(struct nlist *); static char *strtab; /* string table in core */ static long ssiz; /* size of the string table */ static struct exec xbuf; /* exec header of a.out */ /* Things which get -E excluded by default. */ static char *excludes[] = { "mcount", "__mcleanup", NULL }; /* * Set up string and symbol tables from a.out. * and optionally the text space. * On return symbol table is sorted by value. * * Returns 0 on success, -1 on failure. */ int aout_getnfile(const char *filename, char ***defaultEs) { FILE *nfile; int valcmp(); nfile = fopen( filename ,"r"); if (nfile == NULL) err( 1 , "%s", filename ); fread(&xbuf, 1, sizeof(xbuf), nfile); if (N_BADMAG(xbuf)) { fclose(nfile); return -1; } getstrtab(nfile, filename); getsymtab(nfile, filename); gettextspace( nfile ); fclose(nfile); # ifdef DEBUG if ( debug & AOUTDEBUG ) { register int j; for (j = 0; j < nname; j++){ printf("[getnfile] 0X%08lx\t%s\n", nl[j].value, nl[j].name); } } # endif /* DEBUG */ *defaultEs = excludes; return 0; } static void getstrtab(FILE *nfile, const char *filename) { fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0); if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) errx( 1 , "%s: no string table (old format?)" , filename ); strtab = calloc(ssiz, 1); if (strtab == NULL) errx( 1 , "%s: no room for %ld bytes of string table", filename , ssiz); if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) errx( 1 , "%s: error reading string table" , filename ); } /* * Read in symbol table */ static void getsymtab(FILE *nfile, const char *filename) { register long i; int askfor; struct nlist nbuf; /* pass1 - count symbols */ fseek(nfile, (long)N_SYMOFF(xbuf), 0); nname = 0; for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { fread(&nbuf, sizeof(nbuf), 1, nfile); if ( ! funcsymbol( &nbuf ) ) { continue; } nname++; } if (nname == 0) errx( 1 , "%s: no symbols" , filename ); askfor = nname + 1; nl = (nltype *) calloc( askfor , sizeof(nltype) ); if (nl == 0) errx( 1 , "no room for %d bytes of symbol table" , askfor * sizeof(nltype) ); /* pass2 - read symbols */ fseek(nfile, (long)N_SYMOFF(xbuf), 0); npe = nl; nname = 0; for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) { fread(&nbuf, sizeof(nbuf), 1, nfile); if ( ! funcsymbol( &nbuf ) ) { # ifdef DEBUG if ( debug & AOUTDEBUG ) { printf( "[getsymtab] rejecting: 0x%x %s\n" , nbuf.n_type , strtab + nbuf.n_un.n_strx ); } # endif /* DEBUG */ continue; } npe->value = nbuf.n_value; npe->name = strtab+nbuf.n_un.n_strx; # ifdef DEBUG if ( debug & AOUTDEBUG ) { printf( "[getsymtab] %d %s 0x%08lx\n" , nname , npe -> name , npe -> value ); } # endif /* DEBUG */ npe++; nname++; } npe->value = -1; } /* * read in the text space of an a.out file */ static void gettextspace(FILE *nfile) { - if ( cflag == 0 ) { - return; - } textspace = (u_char *) malloc( xbuf.a_text ); if ( textspace == 0 ) { warnx("no room for %lu bytes of text space: can't do -c" , xbuf.a_text ); return; } (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 ); if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) { warnx("couldn't read text space: can't do -c"); free( textspace ); textspace = 0; return; } } static bool funcsymbol(struct nlist *nlistp) { char *name, c; /* * must be a text symbol, * and static text symbols don't qualify if aflag set. */ if ( ! ( ( nlistp -> n_type == ( N_TEXT | N_EXT ) ) || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) { return FALSE; } /* * name must start with an underscore if uflag is set. * can't have any `funny' characters in name, * where `funny' means `.' (.o file names) * need to make an exception for sparc .mul & co. * perhaps we should just drop this code entirely... */ name = strtab + nlistp -> n_un.n_strx; if ( uflag && *name != '_' ) return FALSE; #ifdef sparc if ( *name == '.' ) { char *p = name + 1; if ( *p == 'u' ) p++; if ( strcmp ( p, "mul" ) == 0 || strcmp ( p, "div" ) == 0 || strcmp ( p, "rem" ) == 0 ) return TRUE; } #endif while ( (c = *name++) ) { if ( c == '.' ) { return FALSE; } } return TRUE; } Index: head/usr.bin/gprof/arcs.c =================================================================== --- head/usr.bin/gprof/arcs.c (revision 130775) +++ head/usr.bin/gprof/arcs.c (revision 130776) @@ -1,968 +1,965 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static char sccsid[] = "@(#)arcs.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include "gprof.h" #ifdef DEBUG int visited; int viable; int newcycle; int oldcycle; #endif /* DEBUG */ /* * add (or just increment) an arc */ void addarc( parentp , childp , count ) nltype *parentp; nltype *childp; long count; { arctype *arcp; # ifdef DEBUG if ( debug & TALLYDEBUG ) { printf( "[addarc] %ld arcs from %s to %s\n" , count , parentp -> name , childp -> name ); } # endif /* DEBUG */ arcp = arclookup( parentp , childp ); if ( arcp != 0 ) { /* * a hit: just increment the count. */ # ifdef DEBUG if ( debug & TALLYDEBUG ) { printf( "[tally] hit %ld += %ld\n" , arcp -> arc_count , count ); } # endif /* DEBUG */ arcp -> arc_count += count; return; } arcp = (arctype *)calloc( 1 , sizeof *arcp ); if (arcp == NULL) errx( 1 , "malloc failed" ); arcp -> arc_parentp = parentp; arcp -> arc_childp = childp; arcp -> arc_count = count; /* * prepend this child to the children of this parent */ arcp -> arc_childlist = parentp -> children; parentp -> children = arcp; /* * prepend this parent to the parents of this child */ arcp -> arc_parentlist = childp -> parents; childp -> parents = arcp; } /* * the code below topologically sorts the graph (collapsing cycles), * and propagates time bottom up and flags top down. */ /* * the topologically sorted name list pointers */ nltype **topsortnlp; int topcmp( npp1 , npp2 ) nltype **npp1; nltype **npp2; { return (*npp1) -> toporder - (*npp2) -> toporder; } nltype ** doarcs() { nltype *parentp, **timesortnlp; arctype *arcp; long index; long pass; /* * initialize various things: * zero out child times. * count self-recursive calls. * indicate that nothing is on cycles. */ for ( parentp = nl ; parentp < npe ; parentp++ ) { parentp -> childtime = 0.0; arcp = arclookup( parentp , parentp ); if ( arcp != 0 ) { parentp -> ncall -= arcp -> arc_count; parentp -> selfcalls = arcp -> arc_count; } else { parentp -> selfcalls = 0; } parentp -> npropcall = parentp -> ncall; parentp -> propfraction = 0.0; parentp -> propself = 0.0; parentp -> propchild = 0.0; parentp -> printflag = FALSE; parentp -> toporder = DFN_NAN; parentp -> cycleno = 0; parentp -> cyclehead = parentp; parentp -> cnext = 0; - if ( cflag ) { - findcall( parentp , parentp -> value , (parentp+1) -> value ); - } } for ( pass = 1 ; ; pass++ ) { /* * topologically order things * if any node is unnumbered, * number it and any of its descendents. */ for ( dfn_init() , parentp = nl ; parentp < npe ; parentp++ ) { if ( parentp -> toporder == DFN_NAN ) { dfn( parentp ); } } /* * link together nodes on the same cycle */ cyclelink(); /* * if no cycles to break up, proceed */ if ( ! Cflag ) break; /* * analyze cycles to determine breakup */ # ifdef DEBUG if ( debug & BREAKCYCLE ) { printf("[doarcs] pass %ld, cycle(s) %d\n" , pass , ncycle ); } # endif /* DEBUG */ if ( pass == 1 ) { printf( "\n\n%s %s\n%s %d:\n" , "The following arcs were deleted" , "from the propagation calculation" , "to reduce the maximum cycle size to", cyclethreshold ); } if ( cycleanalyze() ) break; free ( cyclenl ); ncycle = 0; for ( parentp = nl ; parentp < npe ; parentp++ ) { parentp -> toporder = DFN_NAN; parentp -> cycleno = 0; parentp -> cyclehead = parentp; parentp -> cnext = 0; } } if ( pass > 1 ) { printf( "\f\n" ); } else { printf( "\tNone\n\n" ); } /* * Sort the symbol table in reverse topological order */ topsortnlp = (nltype **) calloc( nname , sizeof(nltype *) ); if ( topsortnlp == (nltype **) 0 ) errx( 1 , "[doarcs] ran out of memory for topo sorting" ); for ( index = 0 ; index < nname ; index += 1 ) { topsortnlp[ index ] = &nl[ index ]; } qsort( topsortnlp , nname , sizeof(nltype *) , topcmp ); # ifdef DEBUG if ( debug & DFNDEBUG ) { printf( "[doarcs] topological sort listing\n" ); for ( index = 0 ; index < nname ; index += 1 ) { printf( "[doarcs] " ); printf( "%d:" , topsortnlp[ index ] -> toporder ); printname( topsortnlp[ index ] ); printf( "\n" ); } } # endif /* DEBUG */ /* * starting from the topological top, * propagate print flags to children. * also, calculate propagation fractions. * this happens before time propagation * since time propagation uses the fractions. */ doflags(); /* * starting from the topological bottom, * propagate children times up to parents. */ dotime(); /* * Now, sort by propself + propchild. * sorting both the regular function names * and cycle headers. */ timesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) ); if ( timesortnlp == (nltype **) 0 ) errx( 1 , "ran out of memory for sorting" ); for ( index = 0 ; index < nname ; index++ ) { timesortnlp[index] = &nl[index]; } for ( index = 1 ; index <= ncycle ; index++ ) { timesortnlp[nname+index-1] = &cyclenl[index]; } qsort( timesortnlp , nname + ncycle , sizeof(nltype *) , totalcmp ); for ( index = 0 ; index < nname + ncycle ; index++ ) { timesortnlp[ index ] -> index = index + 1; } return( timesortnlp ); } void dotime() { int index; cycletime(); for ( index = 0 ; index < nname ; index += 1 ) { timepropagate( topsortnlp[ index ] ); } } void timepropagate( parentp ) nltype *parentp; { arctype *arcp; nltype *childp; double share; double propshare; if ( parentp -> propfraction == 0.0 ) { return; } /* * gather time from children of this parent. */ for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) { childp = arcp -> arc_childp; if ( arcp -> arc_flags & DEADARC ) { continue; } if ( arcp -> arc_count == 0 ) { continue; } if ( childp == parentp ) { continue; } if ( childp -> propfraction == 0.0 ) { continue; } if ( childp -> cyclehead != childp ) { if ( parentp -> cycleno == childp -> cycleno ) { continue; } if ( parentp -> toporder <= childp -> toporder ) { fprintf( stderr , "[propagate] toporder botches\n" ); } childp = childp -> cyclehead; } else { if ( parentp -> toporder <= childp -> toporder ) { fprintf( stderr , "[propagate] toporder botches\n" ); continue; } } if ( childp -> npropcall == 0 ) { continue; } /* * distribute time for this arc */ arcp -> arc_time = childp -> time * ( ( (double) arcp -> arc_count ) / ( (double) childp -> npropcall ) ); arcp -> arc_childtime = childp -> childtime * ( ( (double) arcp -> arc_count ) / ( (double) childp -> npropcall ) ); share = arcp -> arc_time + arcp -> arc_childtime; parentp -> childtime += share; /* * ( 1 - propfraction ) gets lost along the way */ propshare = parentp -> propfraction * share; /* * fix things for printing */ parentp -> propchild += propshare; arcp -> arc_time *= parentp -> propfraction; arcp -> arc_childtime *= parentp -> propfraction; /* * add this share to the parent's cycle header, if any. */ if ( parentp -> cyclehead != parentp ) { parentp -> cyclehead -> childtime += share; parentp -> cyclehead -> propchild += propshare; } # ifdef DEBUG if ( debug & PROPDEBUG ) { printf( "[dotime] child \t" ); printname( childp ); printf( " with %f %f %ld/%ld\n" , childp -> time , childp -> childtime , arcp -> arc_count , childp -> npropcall ); printf( "[dotime] parent\t" ); printname( parentp ); printf( "\n[dotime] share %f\n" , share ); } # endif /* DEBUG */ } } void cyclelink() { register nltype *nlp; register nltype *cyclenlp; int cycle; nltype *memberp; arctype *arcp; /* * Count the number of cycles, and initialize the cycle lists */ ncycle = 0; for ( nlp = nl ; nlp < npe ; nlp++ ) { /* * this is how you find unattached cycles */ if ( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) { ncycle += 1; } } /* * cyclenl is indexed by cycle number: * i.e. it is origin 1, not origin 0. */ cyclenl = (nltype *) calloc( ncycle + 1 , sizeof( nltype ) ); if ( cyclenl == 0 ) errx( 1 , "no room for %d bytes of cycle headers" , ( ncycle + 1 ) * sizeof( nltype ) ); /* * now link cycles to true cycleheads, * number them, accumulate the data for the cycle */ cycle = 0; for ( nlp = nl ; nlp < npe ; nlp++ ) { if ( !( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) ) { continue; } cycle += 1; cyclenlp = &cyclenl[cycle]; cyclenlp -> name = 0; /* the name */ cyclenlp -> value = 0; /* the pc entry point */ cyclenlp -> time = 0.0; /* ticks in this routine */ cyclenlp -> childtime = 0.0; /* cumulative ticks in children */ cyclenlp -> ncall = 0; /* how many times called */ cyclenlp -> selfcalls = 0; /* how many calls to self */ cyclenlp -> propfraction = 0.0; /* what % of time propagates */ cyclenlp -> propself = 0.0; /* how much self time propagates */ cyclenlp -> propchild = 0.0; /* how much child time propagates */ cyclenlp -> printflag = TRUE; /* should this be printed? */ cyclenlp -> index = 0; /* index in the graph list */ cyclenlp -> toporder = DFN_NAN; /* graph call chain top-sort order */ cyclenlp -> cycleno = cycle; /* internal number of cycle on */ cyclenlp -> cyclehead = cyclenlp; /* pointer to head of cycle */ cyclenlp -> cnext = nlp; /* pointer to next member of cycle */ cyclenlp -> parents = 0; /* list of caller arcs */ cyclenlp -> children = 0; /* list of callee arcs */ # ifdef DEBUG if ( debug & CYCLEDEBUG ) { printf( "[cyclelink] " ); printname( nlp ); printf( " is the head of cycle %d\n" , cycle ); } # endif /* DEBUG */ /* * link members to cycle header */ for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) { memberp -> cycleno = cycle; memberp -> cyclehead = cyclenlp; } /* * count calls from outside the cycle * and those among cycle members */ for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) { for ( arcp=memberp->parents ; arcp ; arcp=arcp->arc_parentlist ) { if ( arcp -> arc_parentp == memberp ) { continue; } if ( arcp -> arc_parentp -> cycleno == cycle ) { cyclenlp -> selfcalls += arcp -> arc_count; } else { cyclenlp -> npropcall += arcp -> arc_count; } } } } } /* * analyze cycles to determine breakup */ bool cycleanalyze() { arctype **cyclestack; arctype **stkp; arctype **arcpp; arctype **endlist; arctype *arcp; nltype *nlp; cltype *clp; bool ret; bool done; int size; int cycleno; /* * calculate the size of the cycle, and find nodes that * exit the cycle as they are desirable targets to cut * some of their parents */ for ( done = TRUE , cycleno = 1 ; cycleno <= ncycle ; cycleno++ ) { size = 0; for (nlp = cyclenl[ cycleno ] . cnext; nlp; nlp = nlp -> cnext) { size += 1; nlp -> parentcnt = 0; nlp -> flags &= ~HASCYCLEXIT; for ( arcp = nlp -> parents; arcp; arcp = arcp -> arc_parentlist ) { nlp -> parentcnt += 1; if ( arcp -> arc_parentp -> cycleno != cycleno ) nlp -> flags |= HASCYCLEXIT; } } if ( size <= cyclethreshold ) continue; done = FALSE; cyclestack = (arctype **) calloc( size + 1 , sizeof( arctype *) ); if ( cyclestack == 0 ) errx( 1, "no room for %d bytes of cycle stack" , ( size + 1 ) * sizeof( arctype * ) ); # ifdef DEBUG if ( debug & BREAKCYCLE ) { printf( "[cycleanalyze] starting cycle %d of %d, size %d\n" , cycleno , ncycle , size ); } # endif /* DEBUG */ for ( nlp = cyclenl[ cycleno ] . cnext ; nlp ; nlp = nlp -> cnext ) { stkp = &cyclestack[0]; nlp -> flags |= CYCLEHEAD; ret = descend ( nlp , cyclestack , stkp ); nlp -> flags &= ~CYCLEHEAD; if ( ret == FALSE ) break; } free( cyclestack ); if ( cyclecnt > 0 ) { compresslist(); for ( clp = cyclehead ; clp ; ) { endlist = &clp -> list[ clp -> size ]; for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) (*arcpp) -> arc_cyclecnt--; cyclecnt--; clp = clp -> next; free( clp ); } cyclehead = 0; } } # ifdef DEBUG if ( debug & BREAKCYCLE ) { printf("%s visited %d, viable %d, newcycle %d, oldcycle %d\n", "[doarcs]" , visited , viable , newcycle , oldcycle); } # endif /* DEBUG */ return( done ); } bool descend( node , stkstart , stkp ) nltype *node; arctype **stkstart; arctype **stkp; { arctype *arcp; bool ret; for ( arcp = node -> children ; arcp ; arcp = arcp -> arc_childlist ) { # ifdef DEBUG visited++; # endif /* DEBUG */ if ( arcp -> arc_childp -> cycleno != node -> cycleno || ( arcp -> arc_childp -> flags & VISITED ) || ( arcp -> arc_flags & DEADARC ) ) continue; # ifdef DEBUG viable++; # endif /* DEBUG */ *stkp = arcp; if ( arcp -> arc_childp -> flags & CYCLEHEAD ) { if ( addcycle( stkstart , stkp ) == FALSE ) return( FALSE ); continue; } arcp -> arc_childp -> flags |= VISITED; ret = descend( arcp -> arc_childp , stkstart , stkp + 1 ); arcp -> arc_childp -> flags &= ~VISITED; if ( ret == FALSE ) return( FALSE ); } return( TRUE ); } bool addcycle( stkstart , stkend ) arctype **stkstart; arctype **stkend; { arctype **arcpp; arctype **stkloc; arctype **stkp; arctype **endlist; arctype *minarc; arctype *arcp; cltype *clp; int size; size = stkend - stkstart + 1; if ( size <= 1 ) return( TRUE ); for ( arcpp = stkstart , minarc = *arcpp ; arcpp <= stkend ; arcpp++ ) { if ( *arcpp > minarc ) continue; minarc = *arcpp; stkloc = arcpp; } for ( clp = cyclehead ; clp ; clp = clp -> next ) { if ( clp -> size != size ) continue; stkp = stkloc; endlist = &clp -> list[ size ]; for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) { if ( *stkp++ != *arcpp ) break; if ( stkp > stkend ) stkp = stkstart; } if ( arcpp == endlist ) { # ifdef DEBUG oldcycle++; # endif /* DEBUG */ return( TRUE ); } } clp = (cltype *) calloc( 1 , sizeof ( cltype ) + ( size - 1 ) * sizeof( arctype * ) ); if ( clp == 0 ) { warnx( "no room for %d bytes of subcycle storage" , sizeof ( cltype ) + ( size - 1 ) * sizeof( arctype * ) ); return( FALSE ); } stkp = stkloc; endlist = &clp -> list[ size ]; for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) { arcp = *arcpp = *stkp++; if ( stkp > stkend ) stkp = stkstart; arcp -> arc_cyclecnt++; if ( ( arcp -> arc_flags & ONLIST ) == 0 ) { arcp -> arc_flags |= ONLIST; arcp -> arc_next = archead; archead = arcp; } } clp -> size = size; clp -> next = cyclehead; cyclehead = clp; # ifdef DEBUG newcycle++; if ( debug & SUBCYCLELIST ) { printsubcycle( clp ); } # endif /* DEBUG */ cyclecnt++; if ( cyclecnt >= CYCLEMAX ) return( FALSE ); return( TRUE ); } void compresslist() { cltype *clp; cltype **prev; arctype **arcpp; arctype **endlist; arctype *arcp; arctype *maxarcp; arctype *maxexitarcp; arctype *maxwithparentarcp; arctype *maxnoparentarcp; int maxexitcnt; int maxwithparentcnt; int maxnoparentcnt; # ifdef DEBUG const char *type; # endif /* DEBUG */ maxexitcnt = 0; maxwithparentcnt = 0; maxnoparentcnt = 0; for ( endlist = &archead , arcp = archead ; arcp ; ) { if ( arcp -> arc_cyclecnt == 0 ) { arcp -> arc_flags &= ~ONLIST; *endlist = arcp -> arc_next; arcp -> arc_next = 0; arcp = *endlist; continue; } if ( arcp -> arc_childp -> flags & HASCYCLEXIT ) { if ( arcp -> arc_cyclecnt > maxexitcnt || ( arcp -> arc_cyclecnt == maxexitcnt && arcp -> arc_cyclecnt < maxexitarcp -> arc_count ) ) { maxexitcnt = arcp -> arc_cyclecnt; maxexitarcp = arcp; } } else if ( arcp -> arc_childp -> parentcnt > 1 ) { if ( arcp -> arc_cyclecnt > maxwithparentcnt || ( arcp -> arc_cyclecnt == maxwithparentcnt && arcp -> arc_cyclecnt < maxwithparentarcp -> arc_count ) ) { maxwithparentcnt = arcp -> arc_cyclecnt; maxwithparentarcp = arcp; } } else { if ( arcp -> arc_cyclecnt > maxnoparentcnt || ( arcp -> arc_cyclecnt == maxnoparentcnt && arcp -> arc_cyclecnt < maxnoparentarcp -> arc_count ) ) { maxnoparentcnt = arcp -> arc_cyclecnt; maxnoparentarcp = arcp; } } endlist = &arcp -> arc_next; arcp = arcp -> arc_next; } if ( maxexitcnt > 0 ) { /* * first choice is edge leading to node with out-of-cycle parent */ maxarcp = maxexitarcp; # ifdef DEBUG type = "exit"; # endif /* DEBUG */ } else if ( maxwithparentcnt > 0 ) { /* * second choice is edge leading to node with at least one * other in-cycle parent */ maxarcp = maxwithparentarcp; # ifdef DEBUG type = "internal"; # endif /* DEBUG */ } else { /* * last choice is edge leading to node with only this arc as * a parent (as it will now be orphaned) */ maxarcp = maxnoparentarcp; # ifdef DEBUG type = "orphan"; # endif /* DEBUG */ } maxarcp -> arc_flags |= DEADARC; maxarcp -> arc_childp -> parentcnt -= 1; maxarcp -> arc_childp -> npropcall -= maxarcp -> arc_count; # ifdef DEBUG if ( debug & BREAKCYCLE ) { printf( "%s delete %s arc: %s (%ld) -> %s from %u cycle(s)\n" , "[compresslist]" , type , maxarcp -> arc_parentp -> name , maxarcp -> arc_count , maxarcp -> arc_childp -> name , maxarcp -> arc_cyclecnt ); } # endif /* DEBUG */ printf( "\t%s to %s with %ld calls\n" , maxarcp -> arc_parentp -> name , maxarcp -> arc_childp -> name , maxarcp -> arc_count ); prev = &cyclehead; for ( clp = cyclehead ; clp ; ) { endlist = &clp -> list[ clp -> size ]; for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) if ( (*arcpp) -> arc_flags & DEADARC ) break; if ( arcpp == endlist ) { prev = &clp -> next; clp = clp -> next; continue; } for ( arcpp = clp -> list ; arcpp < endlist ; arcpp++ ) (*arcpp) -> arc_cyclecnt--; cyclecnt--; *prev = clp -> next; clp = clp -> next; free( clp ); } } #ifdef DEBUG void printsubcycle( clp ) cltype *clp; { arctype **arcpp; arctype **endlist; arcpp = clp -> list; printf( "%s \n" , (*arcpp) -> arc_parentp -> name , (*arcpp) -> arc_parentp -> cycleno ) ; for ( endlist = &clp -> list[ clp -> size ]; arcpp < endlist ; arcpp++ ) printf( "\t(%ld) -> %s\n" , (*arcpp) -> arc_count , (*arcpp) -> arc_childp -> name ) ; } #endif /* DEBUG */ void cycletime() { int cycle; nltype *cyclenlp; nltype *childp; for ( cycle = 1 ; cycle <= ncycle ; cycle += 1 ) { cyclenlp = &cyclenl[ cycle ]; for ( childp = cyclenlp -> cnext ; childp ; childp = childp -> cnext ) { if ( childp -> propfraction == 0.0 ) { /* * all members have the same propfraction except those * that were excluded with -E */ continue; } cyclenlp -> time += childp -> time; } cyclenlp -> propself = cyclenlp -> propfraction * cyclenlp -> time; } } /* * in one top to bottom pass over the topologically sorted namelist * propagate: * printflag as the union of parents' printflags * propfraction as the sum of fractional parents' propfractions * and while we're here, sum time for functions. */ void doflags() { int index; nltype *childp; nltype *oldhead; oldhead = 0; for ( index = nname-1 ; index >= 0 ; index -= 1 ) { childp = topsortnlp[ index ]; /* * if we haven't done this function or cycle, * inherit things from parent. * this way, we are linear in the number of arcs * since we do all members of a cycle (and the cycle itself) * as we hit the first member of the cycle. */ if ( childp -> cyclehead != oldhead ) { oldhead = childp -> cyclehead; inheritflags( childp ); } # ifdef DEBUG if ( debug & PROPDEBUG ) { printf( "[doflags] " ); printname( childp ); printf( " inherits printflag %d and propfraction %f\n" , childp -> printflag , childp -> propfraction ); } # endif /* DEBUG */ if ( ! childp -> printflag ) { /* * printflag is off * it gets turned on by * being on -f list, * or there not being any -f list and not being on -e list. */ if ( onlist( flist , childp -> name ) || ( !fflag && !onlist( elist , childp -> name ) ) ) { childp -> printflag = TRUE; } } else { /* * this function has printing parents: * maybe someone wants to shut it up * by putting it on -e list. (but favor -f over -e) */ if ( ( !onlist( flist , childp -> name ) ) && onlist( elist , childp -> name ) ) { childp -> printflag = FALSE; } } if ( childp -> propfraction == 0.0 ) { /* * no parents to pass time to. * collect time from children if * its on -F list, * or there isn't any -F list and its not on -E list. */ if ( onlist( Flist , childp -> name ) || ( !Fflag && !onlist( Elist , childp -> name ) ) ) { childp -> propfraction = 1.0; } } else { /* * it has parents to pass time to, * but maybe someone wants to shut it up * by putting it on -E list. (but favor -F over -E) */ if ( !onlist( Flist , childp -> name ) && onlist( Elist , childp -> name ) ) { childp -> propfraction = 0.0; } } childp -> propself = childp -> time * childp -> propfraction; printtime += childp -> propself; # ifdef DEBUG if ( debug & PROPDEBUG ) { printf( "[doflags] " ); printname( childp ); printf( " ends up with printflag %d and propfraction %f\n" , childp -> printflag , childp -> propfraction ); printf( "time %f propself %f printtime %f\n" , childp -> time , childp -> propself , printtime ); } # endif /* DEBUG */ } } /* * check if any parent of this child * (or outside parents of this cycle) * have their print flags on and set the * print flag of the child (cycle) appropriately. * similarly, deal with propagation fractions from parents. */ void inheritflags( childp ) nltype *childp; { nltype *headp; arctype *arcp; nltype *parentp; nltype *memp; headp = childp -> cyclehead; if ( childp == headp ) { /* * just a regular child, check its parents */ childp -> printflag = FALSE; childp -> propfraction = 0.0; for (arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist) { parentp = arcp -> arc_parentp; if ( childp == parentp ) { continue; } childp -> printflag |= parentp -> printflag; /* * if the child was never actually called * (e.g. this arc is static (and all others are, too)) * no time propagates along this arc. */ if ( arcp -> arc_flags & DEADARC ) { continue; } if ( childp -> npropcall ) { childp -> propfraction += parentp -> propfraction * ( ( (double) arcp -> arc_count ) / ( (double) childp -> npropcall ) ); } } } else { /* * its a member of a cycle, look at all parents from * outside the cycle */ headp -> printflag = FALSE; headp -> propfraction = 0.0; for ( memp = headp -> cnext ; memp ; memp = memp -> cnext ) { for (arcp = memp->parents ; arcp ; arcp = arcp->arc_parentlist) { if ( arcp -> arc_parentp -> cyclehead == headp ) { continue; } parentp = arcp -> arc_parentp; headp -> printflag |= parentp -> printflag; /* * if the cycle was never actually called * (e.g. this arc is static (and all others are, too)) * no time propagates along this arc. */ if ( arcp -> arc_flags & DEADARC ) { continue; } if ( headp -> npropcall ) { headp -> propfraction += parentp -> propfraction * ( ( (double) arcp -> arc_count ) / ( (double) headp -> npropcall ) ); } } } for ( memp = headp ; memp ; memp = memp -> cnext ) { memp -> printflag = headp -> printflag; memp -> propfraction = headp -> propfraction; } } } Index: head/usr.bin/gprof/gprof.1 =================================================================== --- head/usr.bin/gprof/gprof.1 (revision 130775) +++ head/usr.bin/gprof/gprof.1 (revision 130776) @@ -1,329 +1,323 @@ .\" Copyright (c) 1983, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)gprof.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" .Dd June 6, 1993 .Dt GPROF 1 .Os .Sh NAME .Nm gprof .Nd display call graph profile data .Sh SYNOPSIS .Nm .Op Fl abcKlLsuz .Op Fl C Ar count .Op Fl e Ar name .Op Fl E Ar name .Op Fl f Ar name .Op Fl F Ar name .Op Fl k Ar fromname toname .Op Ar a.out Op Ar a.out.gmon ... .Sh DESCRIPTION The .Nm utility produces an execution profile of C, Pascal, or Fortran77 programs. The effect of called routines is incorporated in the profile of each caller. The profile data is taken from the call graph profile file which is created by programs that are compiled with the .Fl pg option of .Xr cc 1 , .Xr pc 1 , and .Xr f77 1 . The .Fl pg option also links in versions of the library routines that are compiled for profiling. By convention these libraries have their name suffixed with .Pa _p , i.e. the profiled version of .Pa libc.a is .Pa libc_p.a and if you specify libraries directly to the compiler or linker you can use .Fl l Ns Ar c_p instead of .Fl l Ns Ar c . Read the given object file (the default is .Pa a.out) and establishes the relation between its symbol table and the call graph profile. The default graph profile file name is the name of the executable with the suffix .Pa .gmon appended. If more than one profile file is specified, the .Nm output shows the sum of the profile information in the given profile files. .Pp The .Nm utility calculates the amount of time spent in each routine. Next, these times are propagated along the edges of the call graph. Cycles are discovered, and calls into a cycle are made to share the time of the cycle. The first listing shows the functions sorted according to the time they represent including the time of their call graph descendants. Below each function entry is shown its (direct) call graph children, and how their times are propagated to this function. A similar display above the function shows how this function's time and the time of its descendants is propagated to its (direct) call graph parents. .Pp Cycles are also shown, with an entry for the cycle as a whole and a listing of the members of the cycle and their contributions to the time and call counts of the cycle. .Pp Second, a flat profile is given, similar to that provided by .Xr prof 1 . This listing gives the total execution times, the call counts, the time in msec or usec the call spent in the routine itself, and the time in msec or usec the call spent in the routine itself including its descendants. .Pp Finally, an index of the function names is provided. .Pp The following options are available: .Bl -tag -width indent .It Fl a Suppress the printing of statically declared functions. If this option is given, all relevant information about the static function (e.g., time samples, calls to other functions, calls from other functions) belongs to the function loaded just before the static function in the .Pa a.out file. .It Fl b Suppress the printing of a description of each field in the profile. -.It Fl c -The static call graph of the program is discovered by a heuristic -that examines the text space of the object file. -Static-only parents or children are shown -with call counts of 0. -This option is not supported on some architectures. .It Fl C Ar count Find a minimal set of arcs that can be broken to eliminate all cycles with .Ar count or more members. Caution: the algorithm used to break cycles is exponential, so using this option may cause .Nm to run for a very long time. .It Fl e Ar name Suppress the printing of the graph profile entry for routine .Ar name and all its descendants (unless they have other ancestors that aren't suppressed). More than one .Fl e option may be given. Only one .Ar name may be given with each .Fl e option. .It Fl E Ar name Suppress the printing of the graph profile entry for routine .Ar name (and its descendants) as .Fl e , above, and also excludes the time spent in .Ar name (and its descendants) from the total and percentage time computations. (For example, .Fl E .Ar mcount .Fl E .Ar mcleanup is the default.) .It Fl f Ar name Print the graph profile entry of only the specified routine .Ar name and its descendants. More than one .Fl f option may be given. Only one .Ar name may be given with each .Fl f option. .It Fl F Ar name Print the graph profile entry of only the routine .Ar name and its descendants (as .Fl f , above) and also uses only the times of the printed routines in total time and percentage computations. More than one .Fl F option may be given. Only one .Ar name may be given with each .Fl F option. The .Fl F option overrides the .Fl E option. .It Fl k Ar fromname Ar toname Will delete any arcs from routine .Ar fromname to routine .Ar toname . This can be used to break undesired cycles. More than one .Fl k option may be given. Only one pair of routine names may be given with each .Fl k option. .It Fl K Gather information about symbols from the currently-running kernel using the .Xr sysctl 3 and .Xr kldsym 2 interfaces. This forces the .Pa a.out argument to be ignored, and allows for symbols in .Xr kld 4 modules to be used. .It Fl l Suppress the printing of the call-graph profile. .It Fl L Suppress the printing of the flat profile. .It Fl s A profile file .Pa gmon.sum is produced that represents the sum of the profile information in all the specified profile files. This summary profile file may be given to later executions of gprof (probably also with a .Fl s ) to accumulate profile data across several runs of an .Pa a.out file. .It Fl u Suppress the printing of functions whose names are not visible to C programs. For the ELF object format, this means names that contain the .Ql .\& character. For the a.out object format, it means names that do not begin with a .Ql _ character. All relevant information about such functions belongs to the (non-suppressed) function with the next lowest address. This is useful for eliminating "functions" that are just labels inside other functions. .It Fl z Display routines that have zero usage (as shown by call counts and accumulated time). This is useful with the .Fl c option for discovering which routines were never called. .El .Sh FILES .Bl -tag -width a.out.gmon -compact .It Pa a.out The namelist and text space. .It Pa a.out.gmon Dynamic call graph and profile. .It Pa gmon.sum Summarized dynamic call graph and profile. .El .Sh SEE ALSO .Xr cc 1 , .Xr profil 2 , .Xr clocks 7 .\" .Xr monitor 3 , .\" .Xr prof 1 .Rs .%T "An Execution Profiler for Modular Programs" .%A S. Graham .%A P. Kessler .%A M. McKusick .%J "Software - Practice and Experience" .%V 13 .%P pp. 671-685 .%D 1983 .Re .Rs .%T "gprof: A Call Graph Execution Profiler" .%A S. Graham .%A P. Kessler .%A M. McKusick .%J "Proceedings of the SIGPLAN '82 Symposium on Compiler Construction, SIGPLAN Notices" .%V 17 .%N 6 .%P pp. 120-126 .%D June 1982 .Re .Sh HISTORY The .Nm profiler appeared in .Bx 4.2 . .Sh BUGS The granularity of the sampling is shown, but remains statistical at best. We assume that the time for each execution of a function can be expressed by the total time for the function divided by the number of times the function is called. Thus the time propagated along the call graph arcs to the function's parents is directly proportional to the number of times that arc is traversed. .Pp Parents that are not themselves profiled will have the time of their profiled children propagated to them, but they will appear to be spontaneously invoked in the call graph listing, and will not have their time propagated further. Similarly, signal catchers, even though profiled, will appear to be spontaneous (although for more obscure reasons). Any profiled children of signal catchers should have their times propagated properly, unless the signal catcher was invoked during the execution of the profiling routine, in which case all is lost. .Pp The profiled program must call .Xr exit 3 or return normally for the profiling information to be saved in the graph profile file. Index: head/usr.bin/gprof/gprof.c =================================================================== --- head/usr.bin/gprof/gprof.c (revision 130775) +++ head/usr.bin/gprof/gprof.c (revision 130776) @@ -1,617 +1,610 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1983, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)gprof.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "gprof.h" static int valcmp(const void *, const void *); static struct gmonhdr gmonhdr; static int lflag; static int Lflag; int main(argc, argv) int argc; char **argv; { char **sp; nltype **timesortnlp; char **defaultEs; --argc; argv++; debug = 0; bflag = TRUE; while ( *argv != 0 && **argv == '-' ) { (*argv)++; switch ( **argv ) { case 'a': aflag = TRUE; break; case 'b': bflag = FALSE; break; case 'C': Cflag = TRUE; cyclethreshold = atoi( *++argv ); break; - case 'c': -#if 0 - cflag = TRUE; -#else - errx(1, "-c isn't supported on this architecture yet"); -#endif - break; case 'd': dflag = TRUE; setlinebuf(stdout); debug |= atoi( *++argv ); debug |= ANYDEBUG; # ifdef DEBUG printf("[main] debug = %d\n", debug); # else /* not DEBUG */ printf("gprof: -d ignored\n"); # endif /* DEBUG */ break; case 'E': ++argv; addlist( Elist , *argv ); Eflag = TRUE; addlist( elist , *argv ); eflag = TRUE; break; case 'e': addlist( elist , *++argv ); eflag = TRUE; break; case 'F': ++argv; addlist( Flist , *argv ); Fflag = TRUE; addlist( flist , *argv ); fflag = TRUE; break; case 'f': addlist( flist , *++argv ); fflag = TRUE; break; case 'k': addlist( kfromlist , *++argv ); addlist( ktolist , *++argv ); kflag = TRUE; break; case 'K': Kflag = TRUE; break; case 'l': lflag = 1; Lflag = 0; break; case 'L': Lflag = 1; lflag = 0; break; case 's': sflag = TRUE; break; case 'u': uflag = TRUE; break; case 'z': zflag = TRUE; break; } argv++; } if ( *argv != 0 ) { a_outname = *argv; argv++; } else { a_outname = A_OUTNAME; } if ( *argv != 0 ) { gmonname = *argv; argv++; } else { gmonname = (char *) malloc(strlen(a_outname)+6); strcpy(gmonname, a_outname); strcat(gmonname, ".gmon"); } /* * get information from the executable file. */ if ((Kflag && kernel_getnfile(a_outname, &defaultEs) == -1) || (elf_getnfile(a_outname, &defaultEs) == -1 && aout_getnfile(a_outname, &defaultEs) == -1)) errx(1, "%s: bad format", a_outname); /* * sort symbol table. */ qsort(nl, nname, sizeof(nltype), valcmp); /* * turn off default functions */ for ( sp = defaultEs ; *sp ; sp++ ) { Eflag = TRUE; addlist( Elist , *sp ); eflag = TRUE; addlist( elist , *sp ); } /* * get information about mon.out file(s). */ do { getpfile( gmonname ); if ( *argv != 0 ) { gmonname = *argv; } } while ( *argv++ != 0 ); /* * how many ticks per second? * if we can't tell, report time in ticks. */ if (hz == 0) { hz = 1; fprintf(stderr, "time is in ticks, not seconds\n"); } /* * dump out a gmon.sum file if requested */ if ( sflag ) { dumpsum( GMONSUM ); } /* * assign samples to procedures */ asgnsamples(); /* * assemble the dynamic profile */ timesortnlp = doarcs(); /* * print the dynamic profile */ if(!lflag) { printgprof( timesortnlp ); } /* * print the flat profile */ if(!Lflag) { printprof(); } /* * print the index */ printindex(); exit(0); } /* * information from a gmon.out file is in two parts: * an array of sampling hits within pc ranges, * and the arcs. */ void getpfile(filename) char *filename; { FILE *pfile; FILE *openpfile(); struct rawarc arc; pfile = openpfile(filename); readsamples(pfile); /* * the rest of the file consists of * a bunch of tuples. */ while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) { # ifdef DEBUG if ( debug & SAMPLEDEBUG ) { printf( "[getpfile] frompc 0x%lx selfpc 0x%lx count %ld\n" , arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); } # endif /* DEBUG */ /* * add this arc */ tally( &arc ); } fclose(pfile); } FILE * openpfile(filename) char *filename; { struct gmonhdr tmp; FILE *pfile; int size; int rate; if((pfile = fopen(filename, "r")) == NULL) err(1, "%s", filename); fread(&tmp, sizeof(struct gmonhdr), 1, pfile); if ( s_highpc != 0 && ( tmp.lpc != gmonhdr.lpc || tmp.hpc != gmonhdr.hpc || tmp.ncnt != gmonhdr.ncnt ) ) errx(1, "%s: incompatible with first gmon file", filename); gmonhdr = tmp; if ( gmonhdr.version == GMONVERSION ) { rate = gmonhdr.profrate; size = sizeof(struct gmonhdr); } else { fseek(pfile, sizeof(struct ophdr), SEEK_SET); size = sizeof(struct ophdr); gmonhdr.profrate = rate = hertz(); gmonhdr.version = GMONVERSION; } if (hz == 0) { hz = rate; } else if (hz != rate) errx(0, "%s: profile clock rate (%d) %s (%ld) in first gmon file", filename, rate, "incompatible with clock rate", hz); if ( gmonhdr.histcounter_type == 0 ) { /* Historical case. The type was u_short (2 bytes in practice). */ histcounter_type = 16; histcounter_size = 2; } else { histcounter_type = gmonhdr.histcounter_type; histcounter_size = abs(histcounter_type) / CHAR_BIT; } s_lowpc = (unsigned long) gmonhdr.lpc; s_highpc = (unsigned long) gmonhdr.hpc; lowpc = (unsigned long)gmonhdr.lpc / HISTORICAL_SCALE_2; highpc = (unsigned long)gmonhdr.hpc / HISTORICAL_SCALE_2; sampbytes = gmonhdr.ncnt - size; nsamples = sampbytes / histcounter_size; # ifdef DEBUG if ( debug & SAMPLEDEBUG ) { printf( "[openpfile] hdr.lpc 0x%lx hdr.hpc 0x%lx hdr.ncnt %d\n", gmonhdr.lpc , gmonhdr.hpc , gmonhdr.ncnt ); printf( "[openpfile] s_lowpc 0x%lx s_highpc 0x%lx\n" , s_lowpc , s_highpc ); printf( "[openpfile] lowpc 0x%lx highpc 0x%lx\n" , lowpc , highpc ); printf( "[openpfile] sampbytes %d nsamples %d\n" , sampbytes , nsamples ); printf( "[openpfile] sample rate %ld\n" , hz ); } # endif /* DEBUG */ return(pfile); } void tally( rawp ) struct rawarc *rawp; { nltype *parentp; nltype *childp; parentp = nllookup( rawp -> raw_frompc ); childp = nllookup( rawp -> raw_selfpc ); if ( parentp == 0 || childp == 0 ) return; if ( kflag && onlist( kfromlist , parentp -> name ) && onlist( ktolist , childp -> name ) ) { return; } childp -> ncall += rawp -> raw_count; # ifdef DEBUG if ( debug & TALLYDEBUG ) { printf( "[tally] arc from %s to %s traversed %ld times\n" , parentp -> name , childp -> name , rawp -> raw_count ); } # endif /* DEBUG */ addarc( parentp , childp , rawp -> raw_count ); } /* * dump out the gmon.sum file */ void dumpsum( sumfile ) char *sumfile; { register nltype *nlp; register arctype *arcp; struct rawarc arc; FILE *sfile; if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) err( 1 , "%s" , sumfile ); /* * dump the header; use the last header read in */ if ( fwrite( &gmonhdr , sizeof gmonhdr , 1 , sfile ) != 1 ) err( 1 , "%s" , sumfile ); /* * dump the samples */ if (fwrite(samples, histcounter_size, nsamples, sfile) != nsamples) err( 1 , "%s" , sumfile ); /* * dump the normalized raw arc information */ for ( nlp = nl ; nlp < npe ; nlp++ ) { for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) { arc.raw_frompc = arcp -> arc_parentp -> value; arc.raw_selfpc = arcp -> arc_childp -> value; arc.raw_count = arcp -> arc_count; if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) err( 1 , "%s" , sumfile ); # ifdef DEBUG if ( debug & SAMPLEDEBUG ) { printf( "[dumpsum] frompc 0x%lx selfpc 0x%lx count %ld\n" , arc.raw_frompc , arc.raw_selfpc , arc.raw_count ); } # endif /* DEBUG */ } } fclose( sfile ); } static int valcmp(v1, v2) const void *v1; const void *v2; { const nltype *p1 = (const nltype *)v1; const nltype *p2 = (const nltype *)v2; if ( p1 -> value < p2 -> value ) { return LESSTHAN; } if ( p1 -> value > p2 -> value ) { return GREATERTHAN; } return EQUALTO; } void readsamples(pfile) FILE *pfile; { register i; intmax_t sample; if (samples == 0) { samples = (double *) calloc(nsamples, sizeof(double)); if (samples == 0) errx(0, "no room for %d sample pc's", nsamples); } for (i = 0; i < nsamples; i++) { fread(&sample, histcounter_size, 1, pfile); if (feof(pfile)) break; switch ( histcounter_type ) { case -8: samples[i] += *(int8_t *)&sample; break; case 8: samples[i] += *(u_int8_t *)&sample; break; case -16: samples[i] += *(int16_t *)&sample; break; case 16: samples[i] += *(u_int16_t *)&sample; break; case -32: samples[i] += *(int32_t *)&sample; break; case 32: samples[i] += *(u_int32_t *)&sample; break; case -64: samples[i] += *(int64_t *)&sample; break; case 64: samples[i] += *(u_int64_t *)&sample; break; default: err(1, "unsupported histogram counter type %d", histcounter_type); } } if (i != nsamples) errx(1, "unexpected EOF after reading %d/%d samples", --i , nsamples ); } /* * Assign samples to the procedures to which they belong. * * There are three cases as to where pcl and pch can be * with respect to the routine entry addresses svalue0 and svalue1 * as shown in the following diagram. overlap computes the * distance between the arrows, the fraction of the sample * that is to be credited to the routine which starts at svalue0. * * svalue0 svalue1 * | | * v v * * +-----------------------------------------------+ * | | * | ->| |<- ->| |<- ->| |<- | * | | | | | | * +---------+ +---------+ +---------+ * * ^ ^ ^ ^ ^ ^ * | | | | | | * pcl pch pcl pch pcl pch * * For the vax we assert that samples will never fall in the first * two bytes of any routine, since that is the entry mask, * thus we give call alignentries() to adjust the entry points if * the entry mask falls in one bucket but the code for the routine * doesn't start until the next bucket. In conjunction with the * alignment of routine addresses, this should allow us to have * only one sample for every four bytes of text space and never * have any overlap (the two end cases, above). */ void asgnsamples() { register int j; double ccnt; double time; unsigned long pcl, pch; register int i; unsigned long overlap; unsigned long svalue0, svalue1; /* read samples and assign to namelist symbols */ scale = highpc - lowpc; scale /= nsamples; alignentries(); for (i = 0, j = 1; i < nsamples; i++) { ccnt = samples[i]; if (ccnt == 0) continue; pcl = lowpc + (unsigned long)(scale * i); pch = lowpc + (unsigned long)(scale * (i + 1)); time = ccnt; # ifdef DEBUG if ( debug & SAMPLEDEBUG ) { printf( "[asgnsamples] pcl 0x%lx pch 0x%lx ccnt %.0f\n" , pcl , pch , ccnt ); } # endif /* DEBUG */ totime += time; for (j = j - 1; j < nname; j++) { svalue0 = nl[j].svalue; svalue1 = nl[j+1].svalue; /* * if high end of tick is below entry address, * go for next tick. */ if (pch < svalue0) break; /* * if low end of tick into next routine, * go for next routine. */ if (pcl >= svalue1) continue; overlap = min(pch, svalue1) - max(pcl, svalue0); if (overlap > 0) { # ifdef DEBUG if (debug & SAMPLEDEBUG) { printf("[asgnsamples] (0x%lx->0x%lx-0x%lx) %s gets %f ticks %lu overlap\n", nl[j].value / HISTORICAL_SCALE_2, svalue0, svalue1, nl[j].name, overlap * time / scale, overlap); } # endif /* DEBUG */ nl[j].time += overlap * time / scale; } } } # ifdef DEBUG if (debug & SAMPLEDEBUG) { printf("[asgnsamples] totime %f\n", totime); } # endif /* DEBUG */ } unsigned long min(a, b) unsigned long a,b; { if (ab) return(a); return(b); } /* * calculate scaled entry point addresses (to save time in asgnsamples), * and possibly push the scaled entry points over the entry mask, * if it turns out that the entry point is in one bucket and the code * for a routine is in the next bucket. */ void alignentries() { register struct nl *nlp; unsigned long bucket_of_entry; unsigned long bucket_of_code; for (nlp = nl; nlp < npe; nlp++) { nlp -> svalue = nlp -> value / HISTORICAL_SCALE_2; bucket_of_entry = (nlp->svalue - lowpc) / scale; bucket_of_code = (nlp->svalue + OFFSET_OF_CODE / HISTORICAL_SCALE_2 - lowpc) / scale; if (bucket_of_entry < bucket_of_code) { # ifdef DEBUG if (debug & SAMPLEDEBUG) { printf("[alignentries] pushing svalue 0x%lx to 0x%lx\n", nlp->svalue, nlp->svalue + OFFSET_OF_CODE / HISTORICAL_SCALE_2); } # endif /* DEBUG */ nlp->svalue += OFFSET_OF_CODE / HISTORICAL_SCALE_2; } } } Index: head/usr.bin/gprof/gprof.h =================================================================== --- head/usr.bin/gprof/gprof.h (revision 130775) +++ head/usr.bin/gprof/gprof.h (revision 130776) @@ -1,346 +1,342 @@ /* * Copyright (c) 1983, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)gprof.h 8.1 (Berkeley) 6/6/93 * $FreeBSD$ */ #include #include #include #include #include #if __alpha__ # include "alpha.h" #endif #if __amd64__ # include "amd64.h" #endif #if __arm__ # include "arm.h" #endif #if __i386__ # include "i386.h" #endif #if __ia64__ # include "ia64.h" #endif #if __powerpc__ # include "powerpc.h" #endif #if __sparc64__ # include "sparc64.h" #endif /* * booleans */ typedef int bool; #define FALSE 0 #define TRUE 1 /* * Historical scale factor in profil(2)'s algorithm for converting * pc addresses to bucket numbers. This now just complicates the * scaling and makes bucket:pc densities of more than 1/2 useless. */ #define HISTORICAL_SCALE_2 2 /* * ticks per second */ long hz; size_t histcounter_size; int histcounter_type; char *a_outname; #define A_OUTNAME "a.out" char *gmonname; #define GMONSUM "gmon.sum" /* * a constructed arc, * with pointers to the namelist entry of the parent and the child, * a count of how many times this arc was traversed, * and pointers to the next parent of this child and * the next child of this parent. */ struct arcstruct { struct nl *arc_parentp; /* pointer to parent's nl entry */ struct nl *arc_childp; /* pointer to child's nl entry */ long arc_count; /* num calls from parent to child */ double arc_time; /* time inherited along arc */ double arc_childtime; /* childtime inherited along arc */ struct arcstruct *arc_parentlist; /* parents-of-this-child list */ struct arcstruct *arc_childlist; /* children-of-this-parent list */ struct arcstruct *arc_next; /* list of arcs on cycle */ unsigned short arc_cyclecnt; /* num cycles involved in */ unsigned short arc_flags; /* see below */ }; typedef struct arcstruct arctype; /* * arc flags */ #define DEADARC 0x01 /* time should not propagate across the arc */ #define ONLIST 0x02 /* arc is on list of arcs in cycles */ /* * The symbol table; * for each external in the specified file we gather * its address, the number of calls and compute its share of CPU time. */ struct nl { const char *name; /* the name */ unsigned long value; /* the pc entry point */ unsigned long svalue; /* entry point aligned to histograms */ double time; /* ticks in this routine */ double childtime; /* cumulative ticks in children */ long ncall; /* how many times called */ long npropcall; /* times called by live arcs */ long selfcalls; /* how many calls to self */ double propfraction; /* what % of time propagates */ double propself; /* how much self time propagates */ double propchild; /* how much child time propagates */ short printflag; /* should this be printed? */ short flags; /* see below */ int index; /* index in the graph list */ int toporder; /* graph call chain top-sort order */ int cycleno; /* internal number of cycle on */ int parentcnt; /* number of live parent arcs */ struct nl *cyclehead; /* pointer to head of cycle */ struct nl *cnext; /* pointer to next member of cycle */ arctype *parents; /* list of caller arcs */ arctype *children; /* list of callee arcs */ }; typedef struct nl nltype; nltype *nl; /* the whole namelist */ nltype *npe; /* the virtual end of the namelist */ int nname; /* the number of function names */ #define HASCYCLEXIT 0x08 /* node has arc exiting from cycle */ #define CYCLEHEAD 0x10 /* node marked as head of a cycle */ #define VISITED 0x20 /* node visited during a cycle */ /* * The cycle list. * for each subcycle within an identified cycle, we gather * its size and the list of included arcs. */ struct cl { int size; /* length of cycle */ struct cl *next; /* next member of list */ arctype *list[1]; /* list of arcs in cycle */ /* actually longer */ }; typedef struct cl cltype; arctype *archead; /* the head of arcs in current cycle list */ cltype *cyclehead; /* the head of the list */ int cyclecnt; /* the number of cycles found */ #define CYCLEMAX 100 /* maximum cycles before cutting one of them */ /* * flag which marks a nl entry as topologically ``busy'' * flag which marks a nl entry as topologically ``not_numbered'' */ #define DFN_BUSY -1 #define DFN_NAN 0 /* * namelist entries for cycle headers. * the number of discovered cycles. */ nltype *cyclenl; /* cycle header namelist */ int ncycle; /* number of cycles discovered */ /* * The header on the gmon.out file. * gmon.out consists of a struct phdr (defined in gmon.h) * and then an array of ncnt samples representing the * discretized program counter values. * * Backward compatible old style header */ struct ophdr { u_short *lpc; u_short *hpc; int ncnt; }; int debug; /* * Each discretized pc sample has * a count of the number of samples in its range */ double *samples; unsigned long s_lowpc; /* lowpc from the profile file */ unsigned long s_highpc; /* highpc from the profile file */ unsigned long lowpc, highpc; /* range profiled, in historical units */ unsigned sampbytes; /* number of bytes of samples */ int nsamples; /* number of samples */ double actime; /* accumulated time thus far for putprofline */ double totime; /* total time for all routines */ double printtime; /* total of time being printed */ double scale; /* scale factor converting samples to pc values: each sample covers scale bytes */ unsigned char *textspace; /* text space of a.out in core */ int cyclethreshold; /* with -C, minimum cycle size to ignore */ /* * option flags, from a to z. */ bool aflag; /* suppress static functions */ bool bflag; /* blurbs, too */ -bool cflag; /* discovered call graph, too */ bool Cflag; /* find cut-set to eliminate cycles */ bool dflag; /* debugging options */ bool eflag; /* specific functions excluded */ bool Eflag; /* functions excluded with time */ bool fflag; /* specific functions requested */ bool Fflag; /* functions requested with time */ bool kflag; /* arcs to be deleted */ bool Kflag; /* use the running kernel for symbols */ bool sflag; /* sum multiple gmon.out files */ bool uflag; /* suppress symbols hidden from C */ bool zflag; /* zero time/called functions, too */ /* * structure for various string lists */ struct stringlist { struct stringlist *next; char *string; }; struct stringlist *elist; struct stringlist *Elist; struct stringlist *flist; struct stringlist *Flist; struct stringlist *kfromlist; struct stringlist *ktolist; /* * function declarations */ void addarc(nltype *, nltype *, long); bool addcycle(arctype **, arctype **); void addlist(struct stringlist *, char *); void alignentries(void); int aout_getnfile(const char *, char ***); int arccmp(); arctype *arclookup(); void asgnsamples(void); void compresslist(void); bool cycleanalyze(void); void cyclelink(void); void cycletime(void); bool descend(nltype *, arctype **, arctype **); void dfn(nltype *); bool dfn_busy(); void dfn_findcycle(nltype *); void dfn_init(void); bool dfn_numbered(); void dfn_post_visit(nltype *); void dfn_pre_visit(nltype *); void dfn_self_cycle(nltype *); nltype **doarcs(); void doflags(void); void dotime(void); void dumpsum(char *); int elf_getnfile(const char *, char ***); -/* - findcalls(); -*/ void flatprofheader(void); void flatprofline(nltype *); void getpfile(char *); /* gprofheader(); gprofline(); */ void inheritflags(nltype *); int kernel_getnfile(const char *, char ***); /* main(); */ unsigned long max(); int membercmp(); unsigned long min(); nltype *nllookup(); bool onlist(struct stringlist *, const char *); FILE *openpfile(); long operandlength(); operandenum operandmode(); char *operandname(); void printblurb(char *); void printchildren(nltype *); void printcycle(nltype *); void printgprof(nltype **); void printindex(void); void printmembers(nltype *); void printname(nltype *); void printparents(nltype *); void printprof(void); void printsubcycle(cltype *); void readsamples(FILE *); unsigned long reladdr(); void sortchildren(nltype *); void sortmembers(nltype *); void sortparents(nltype *); void tally(struct rawarc *); void timepropagate(nltype *); int totalcmp(); #define LESSTHAN -1 #define EQUALTO 0 #define GREATERTHAN 1 #define DFNDEBUG 1 #define CYCLEDEBUG 2 #define ARCDEBUG 4 #define TALLYDEBUG 8 #define TIMEDEBUG 16 #define SAMPLEDEBUG 32 #define AOUTDEBUG 64 #define CALLDEBUG 128 #define LOOKUPDEBUG 256 #define PROPDEBUG 512 #define BREAKCYCLE 1024 #define SUBCYCLELIST 2048 #define ANYDEBUG 4096