Initial revision
This commit is contained in:
parent
1b2d0bf260
commit
3d6c650189
23
gprof/Makefile
Executable file
23
gprof/Makefile
Executable file
@ -0,0 +1,23 @@
|
||||
# @(#)Makefile 5.17 (Berkeley) 5/11/90
|
||||
|
||||
CC= gcc
|
||||
MACHINE= sparc
|
||||
PROG= gprof
|
||||
SRCS= gprof.c arcs.c dfn.c lookup.c ${MACHINE}.c hertz.c \
|
||||
printgprof.c printlist.c
|
||||
#CFLAGS+=-I${.CURDIR}/../../lib/csu.${MACHINE}
|
||||
CFLAGS= -I. -O -g -DMACHINE_H=\"${MACHINE}.h\"
|
||||
|
||||
OBJS= gprof.o arcs.o dfn.o lookup.o ${MACHINE}.o hertz.o \
|
||||
printgprof.o printlist.o
|
||||
|
||||
all: ${PROG}
|
||||
|
||||
beforeinstall:
|
||||
install -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
|
||||
${.CURDIR}/gprof.flat ${.CURDIR}/gprof.callg \
|
||||
${DESTDIR}/usr/share/misc
|
||||
|
||||
#.include <bsd.prog.mk>
|
||||
$(PROG): $(OBJS)
|
||||
$(CC) $(CFLAGS) $(OBJS) -o $(PROG) $(LIBS)
|
567
gprof/arcs.c
Normal file
567
gprof/arcs.c
Normal file
@ -0,0 +1,567 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)arcs.c 5.6 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* add (or just increment) an arc
|
||||
*/
|
||||
addarc( parentp , childp , count )
|
||||
nltype *parentp;
|
||||
nltype *childp;
|
||||
long count;
|
||||
{
|
||||
arctype *calloc();
|
||||
arctype *arcp;
|
||||
|
||||
# ifdef DEBUG
|
||||
if ( debug & TALLYDEBUG ) {
|
||||
printf( "[addarc] %d arcs from %s to %s\n" ,
|
||||
count , parentp -> name , childp -> name );
|
||||
}
|
||||
# endif DEBUG
|
||||
arcp = arclookup( parentp , childp );
|
||||
if ( arcp != 0 ) {
|
||||
/*
|
||||
* a hit: just increment the count.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & TALLYDEBUG ) {
|
||||
printf( "[tally] hit %d += %d\n" ,
|
||||
arcp -> arc_count , count );
|
||||
}
|
||||
# endif DEBUG
|
||||
arcp -> arc_count += count;
|
||||
return;
|
||||
}
|
||||
arcp = calloc( 1 , sizeof *arcp );
|
||||
arcp -> arc_parentp = parentp;
|
||||
arcp -> arc_childp = childp;
|
||||
arcp -> arc_count = count;
|
||||
/*
|
||||
* prepend this child to the children of this parent
|
||||
*/
|
||||
arcp -> arc_childlist = parentp -> children;
|
||||
parentp -> children = arcp;
|
||||
/*
|
||||
* prepend this parent to the parents of this child
|
||||
*/
|
||||
arcp -> arc_parentlist = childp -> parents;
|
||||
childp -> parents = arcp;
|
||||
}
|
||||
|
||||
/*
|
||||
* the code below topologically sorts the graph (collapsing cycles),
|
||||
* and propagates time bottom up and flags top down.
|
||||
*/
|
||||
|
||||
/*
|
||||
* the topologically sorted name list pointers
|
||||
*/
|
||||
nltype **topsortnlp;
|
||||
|
||||
topcmp( npp1 , npp2 )
|
||||
nltype **npp1;
|
||||
nltype **npp2;
|
||||
{
|
||||
return (*npp1) -> toporder - (*npp2) -> toporder;
|
||||
}
|
||||
|
||||
nltype **
|
||||
doarcs()
|
||||
{
|
||||
nltype *parentp, **timesortnlp;
|
||||
arctype *arcp;
|
||||
long index;
|
||||
|
||||
/*
|
||||
* initialize various things:
|
||||
* zero out child times.
|
||||
* count self-recursive calls.
|
||||
* indicate that nothing is on cycles.
|
||||
*/
|
||||
for ( parentp = nl ; parentp < npe ; parentp++ ) {
|
||||
parentp -> childtime = 0.0;
|
||||
arcp = arclookup( parentp , parentp );
|
||||
if ( arcp != 0 ) {
|
||||
parentp -> ncall -= arcp -> arc_count;
|
||||
parentp -> selfcalls = arcp -> arc_count;
|
||||
} else {
|
||||
parentp -> selfcalls = 0;
|
||||
}
|
||||
parentp -> propfraction = 0.0;
|
||||
parentp -> propself = 0.0;
|
||||
parentp -> propchild = 0.0;
|
||||
parentp -> printflag = FALSE;
|
||||
parentp -> toporder = DFN_NAN;
|
||||
parentp -> cycleno = 0;
|
||||
parentp -> cyclehead = parentp;
|
||||
parentp -> cnext = 0;
|
||||
if ( cflag ) {
|
||||
findcall( parentp , parentp -> value , (parentp+1) -> value );
|
||||
}
|
||||
}
|
||||
/*
|
||||
* topologically order things
|
||||
* if any node is unnumbered,
|
||||
* number it and any of its descendents.
|
||||
*/
|
||||
for ( parentp = nl ; parentp < npe ; parentp++ ) {
|
||||
if ( parentp -> toporder == DFN_NAN ) {
|
||||
dfn( parentp );
|
||||
}
|
||||
}
|
||||
/*
|
||||
* link together nodes on the same cycle
|
||||
*/
|
||||
cyclelink();
|
||||
/*
|
||||
* Sort the symbol table in reverse topological order
|
||||
*/
|
||||
topsortnlp = (nltype **) calloc( nname , sizeof(nltype *) );
|
||||
if ( topsortnlp == (nltype **) 0 ) {
|
||||
fprintf( stderr , "[doarcs] ran out of memory for topo sorting\n" );
|
||||
}
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
topsortnlp[ index ] = &nl[ index ];
|
||||
}
|
||||
qsort( topsortnlp , nname , sizeof(nltype *) , topcmp );
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[doarcs] topological sort listing\n" );
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
printf( "[doarcs] " );
|
||||
printf( "%d:" , topsortnlp[ index ] -> toporder );
|
||||
printname( topsortnlp[ index ] );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* starting from the topological top,
|
||||
* propagate print flags to children.
|
||||
* also, calculate propagation fractions.
|
||||
* this happens before time propagation
|
||||
* since time propagation uses the fractions.
|
||||
*/
|
||||
doflags();
|
||||
/*
|
||||
* starting from the topological bottom,
|
||||
* propogate children times up to parents.
|
||||
*/
|
||||
dotime();
|
||||
/*
|
||||
* Now, sort by propself + propchild.
|
||||
* sorting both the regular function names
|
||||
* and cycle headers.
|
||||
*/
|
||||
timesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) );
|
||||
if ( timesortnlp == (nltype **) 0 ) {
|
||||
fprintf( stderr , "%s: ran out of memory for sorting\n" , whoami );
|
||||
}
|
||||
for ( index = 0 ; index < nname ; index++ ) {
|
||||
timesortnlp[index] = &nl[index];
|
||||
}
|
||||
for ( index = 1 ; index <= ncycle ; index++ ) {
|
||||
timesortnlp[nname+index-1] = &cyclenl[index];
|
||||
}
|
||||
qsort( timesortnlp , nname + ncycle , sizeof(nltype *) , totalcmp );
|
||||
for ( index = 0 ; index < nname + ncycle ; index++ ) {
|
||||
timesortnlp[ index ] -> index = index + 1;
|
||||
}
|
||||
return( timesortnlp );
|
||||
}
|
||||
|
||||
dotime()
|
||||
{
|
||||
int index;
|
||||
|
||||
cycletime();
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
timepropagate( topsortnlp[ index ] );
|
||||
}
|
||||
}
|
||||
|
||||
timepropagate( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
arctype *arcp;
|
||||
nltype *childp;
|
||||
double share;
|
||||
double propshare;
|
||||
|
||||
if ( parentp -> propfraction == 0.0 ) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* gather time from children of this parent.
|
||||
*/
|
||||
for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
|
||||
childp = arcp -> arc_childp;
|
||||
if ( arcp -> arc_count == 0 ) {
|
||||
continue;
|
||||
}
|
||||
if ( childp == parentp ) {
|
||||
continue;
|
||||
}
|
||||
if ( childp -> propfraction == 0.0 ) {
|
||||
continue;
|
||||
}
|
||||
if ( childp -> cyclehead != childp ) {
|
||||
if ( parentp -> cycleno == childp -> cycleno ) {
|
||||
continue;
|
||||
}
|
||||
if ( parentp -> toporder <= childp -> toporder ) {
|
||||
fprintf( stderr , "[propagate] toporder botches\n" );
|
||||
}
|
||||
childp = childp -> cyclehead;
|
||||
} else {
|
||||
if ( parentp -> toporder <= childp -> toporder ) {
|
||||
fprintf( stderr , "[propagate] toporder botches\n" );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ( childp -> ncall == 0 ) {
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* distribute time for this arc
|
||||
*/
|
||||
arcp -> arc_time = childp -> time
|
||||
* ( ( (double) arcp -> arc_count ) /
|
||||
( (double) childp -> ncall ) );
|
||||
arcp -> arc_childtime = childp -> childtime
|
||||
* ( ( (double) arcp -> arc_count ) /
|
||||
( (double) childp -> ncall ) );
|
||||
share = arcp -> arc_time + arcp -> arc_childtime;
|
||||
parentp -> childtime += share;
|
||||
/*
|
||||
* ( 1 - propfraction ) gets lost along the way
|
||||
*/
|
||||
propshare = parentp -> propfraction * share;
|
||||
/*
|
||||
* fix things for printing
|
||||
*/
|
||||
parentp -> propchild += propshare;
|
||||
arcp -> arc_time *= parentp -> propfraction;
|
||||
arcp -> arc_childtime *= parentp -> propfraction;
|
||||
/*
|
||||
* add this share to the parent's cycle header, if any.
|
||||
*/
|
||||
if ( parentp -> cyclehead != parentp ) {
|
||||
parentp -> cyclehead -> childtime += share;
|
||||
parentp -> cyclehead -> propchild += propshare;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & PROPDEBUG ) {
|
||||
printf( "[dotime] child \t" );
|
||||
printname( childp );
|
||||
printf( " with %f %f %d/%d\n" ,
|
||||
childp -> time , childp -> childtime ,
|
||||
arcp -> arc_count , childp -> ncall );
|
||||
printf( "[dotime] parent\t" );
|
||||
printname( parentp );
|
||||
printf( "\n[dotime] share %f\n" , share );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
cyclelink()
|
||||
{
|
||||
register nltype *nlp;
|
||||
register nltype *cyclenlp;
|
||||
int cycle;
|
||||
nltype *memberp;
|
||||
arctype *arcp;
|
||||
|
||||
/*
|
||||
* Count the number of cycles, and initialze the cycle lists
|
||||
*/
|
||||
ncycle = 0;
|
||||
for ( nlp = nl ; nlp < npe ; nlp++ ) {
|
||||
/*
|
||||
* this is how you find unattached cycles
|
||||
*/
|
||||
if ( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) {
|
||||
ncycle += 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* cyclenl is indexed by cycle number:
|
||||
* i.e. it is origin 1, not origin 0.
|
||||
*/
|
||||
cyclenl = (nltype *) calloc( ncycle + 1 , sizeof( nltype ) );
|
||||
if ( cyclenl == 0 ) {
|
||||
fprintf( stderr , "%s: No room for %d bytes of cycle headers\n" ,
|
||||
whoami , ( ncycle + 1 ) * sizeof( nltype ) );
|
||||
done();
|
||||
}
|
||||
/*
|
||||
* now link cycles to true cycleheads,
|
||||
* number them, accumulate the data for the cycle
|
||||
*/
|
||||
cycle = 0;
|
||||
for ( nlp = nl ; nlp < npe ; nlp++ ) {
|
||||
if ( !( nlp -> cyclehead == nlp && nlp -> cnext != 0 ) ) {
|
||||
continue;
|
||||
}
|
||||
cycle += 1;
|
||||
cyclenlp = &cyclenl[cycle];
|
||||
cyclenlp -> name = 0; /* the name */
|
||||
cyclenlp -> value = 0; /* the pc entry point */
|
||||
cyclenlp -> time = 0.0; /* ticks in this routine */
|
||||
cyclenlp -> childtime = 0.0; /* cumulative ticks in children */
|
||||
cyclenlp -> ncall = 0; /* how many times called */
|
||||
cyclenlp -> selfcalls = 0; /* how many calls to self */
|
||||
cyclenlp -> propfraction = 0.0; /* what % of time propagates */
|
||||
cyclenlp -> propself = 0.0; /* how much self time propagates */
|
||||
cyclenlp -> propchild = 0.0; /* how much child time propagates */
|
||||
cyclenlp -> printflag = TRUE; /* should this be printed? */
|
||||
cyclenlp -> index = 0; /* index in the graph list */
|
||||
cyclenlp -> toporder = DFN_NAN; /* graph call chain top-sort order */
|
||||
cyclenlp -> cycleno = cycle; /* internal number of cycle on */
|
||||
cyclenlp -> cyclehead = cyclenlp; /* pointer to head of cycle */
|
||||
cyclenlp -> cnext = nlp; /* pointer to next member of cycle */
|
||||
cyclenlp -> parents = 0; /* list of caller arcs */
|
||||
cyclenlp -> children = 0; /* list of callee arcs */
|
||||
# ifdef DEBUG
|
||||
if ( debug & CYCLEDEBUG ) {
|
||||
printf( "[cyclelink] " );
|
||||
printname( nlp );
|
||||
printf( " is the head of cycle %d\n" , cycle );
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* link members to cycle header
|
||||
*/
|
||||
for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) {
|
||||
memberp -> cycleno = cycle;
|
||||
memberp -> cyclehead = cyclenlp;
|
||||
}
|
||||
/*
|
||||
* count calls from outside the cycle
|
||||
* and those among cycle members
|
||||
*/
|
||||
for ( memberp = nlp ; memberp ; memberp = memberp -> cnext ) {
|
||||
for ( arcp=memberp->parents ; arcp ; arcp=arcp->arc_parentlist ) {
|
||||
if ( arcp -> arc_parentp == memberp ) {
|
||||
continue;
|
||||
}
|
||||
if ( arcp -> arc_parentp -> cycleno == cycle ) {
|
||||
cyclenlp -> selfcalls += arcp -> arc_count;
|
||||
} else {
|
||||
cyclenlp -> ncall += arcp -> arc_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cycletime()
|
||||
{
|
||||
int cycle;
|
||||
nltype *cyclenlp;
|
||||
nltype *childp;
|
||||
|
||||
for ( cycle = 1 ; cycle <= ncycle ; cycle += 1 ) {
|
||||
cyclenlp = &cyclenl[ cycle ];
|
||||
for ( childp = cyclenlp -> cnext ; childp ; childp = childp -> cnext ) {
|
||||
if ( childp -> propfraction == 0.0 ) {
|
||||
/*
|
||||
* all members have the same propfraction except those
|
||||
* that were excluded with -E
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
cyclenlp -> time += childp -> time;
|
||||
}
|
||||
cyclenlp -> propself = cyclenlp -> propfraction * cyclenlp -> time;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* in one top to bottom pass over the topologically sorted namelist
|
||||
* propagate:
|
||||
* printflag as the union of parents' printflags
|
||||
* propfraction as the sum of fractional parents' propfractions
|
||||
* and while we're here, sum time for functions.
|
||||
*/
|
||||
doflags()
|
||||
{
|
||||
int index;
|
||||
nltype *childp;
|
||||
nltype *oldhead;
|
||||
|
||||
oldhead = 0;
|
||||
for ( index = nname-1 ; index >= 0 ; index -= 1 ) {
|
||||
childp = topsortnlp[ index ];
|
||||
/*
|
||||
* if we haven't done this function or cycle,
|
||||
* inherit things from parent.
|
||||
* this way, we are linear in the number of arcs
|
||||
* since we do all members of a cycle (and the cycle itself)
|
||||
* as we hit the first member of the cycle.
|
||||
*/
|
||||
if ( childp -> cyclehead != oldhead ) {
|
||||
oldhead = childp -> cyclehead;
|
||||
inheritflags( childp );
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & PROPDEBUG ) {
|
||||
printf( "[doflags] " );
|
||||
printname( childp );
|
||||
printf( " inherits printflag %d and propfraction %f\n" ,
|
||||
childp -> printflag , childp -> propfraction );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( ! childp -> printflag ) {
|
||||
/*
|
||||
* printflag is off
|
||||
* it gets turned on by
|
||||
* being on -f list,
|
||||
* or there not being any -f list and not being on -e list.
|
||||
*/
|
||||
if ( onlist( flist , childp -> name )
|
||||
|| ( !fflag && !onlist( elist , childp -> name ) ) ) {
|
||||
childp -> printflag = TRUE;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* this function has printing parents:
|
||||
* maybe someone wants to shut it up
|
||||
* by putting it on -e list. (but favor -f over -e)
|
||||
*/
|
||||
if ( ( !onlist( flist , childp -> name ) )
|
||||
&& onlist( elist , childp -> name ) ) {
|
||||
childp -> printflag = FALSE;
|
||||
}
|
||||
}
|
||||
if ( childp -> propfraction == 0.0 ) {
|
||||
/*
|
||||
* no parents to pass time to.
|
||||
* collect time from children if
|
||||
* its on -F list,
|
||||
* or there isn't any -F list and its not on -E list.
|
||||
*/
|
||||
if ( onlist( Flist , childp -> name )
|
||||
|| ( !Fflag && !onlist( Elist , childp -> name ) ) ) {
|
||||
childp -> propfraction = 1.0;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* it has parents to pass time to,
|
||||
* but maybe someone wants to shut it up
|
||||
* by puttting it on -E list. (but favor -F over -E)
|
||||
*/
|
||||
if ( !onlist( Flist , childp -> name )
|
||||
&& onlist( Elist , childp -> name ) ) {
|
||||
childp -> propfraction = 0.0;
|
||||
}
|
||||
}
|
||||
childp -> propself = childp -> time * childp -> propfraction;
|
||||
printtime += childp -> propself;
|
||||
# ifdef DEBUG
|
||||
if ( debug & PROPDEBUG ) {
|
||||
printf( "[doflags] " );
|
||||
printname( childp );
|
||||
printf( " ends up with printflag %d and propfraction %f\n" ,
|
||||
childp -> printflag , childp -> propfraction );
|
||||
printf( "time %f propself %f printtime %f\n" ,
|
||||
childp -> time , childp -> propself , printtime );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* check if any parent of this child
|
||||
* (or outside parents of this cycle)
|
||||
* have their print flags on and set the
|
||||
* print flag of the child (cycle) appropriately.
|
||||
* similarly, deal with propagation fractions from parents.
|
||||
*/
|
||||
inheritflags( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
nltype *headp;
|
||||
arctype *arcp;
|
||||
nltype *parentp;
|
||||
nltype *memp;
|
||||
|
||||
headp = childp -> cyclehead;
|
||||
if ( childp == headp ) {
|
||||
/*
|
||||
* just a regular child, check its parents
|
||||
*/
|
||||
childp -> printflag = FALSE;
|
||||
childp -> propfraction = 0.0;
|
||||
for (arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist) {
|
||||
parentp = arcp -> arc_parentp;
|
||||
if ( childp == parentp ) {
|
||||
continue;
|
||||
}
|
||||
childp -> printflag |= parentp -> printflag;
|
||||
/*
|
||||
* if the child was never actually called
|
||||
* (e.g. this arc is static (and all others are, too))
|
||||
* no time propagates along this arc.
|
||||
*/
|
||||
if ( childp -> ncall ) {
|
||||
childp -> propfraction += parentp -> propfraction
|
||||
* ( ( (double) arcp -> arc_count )
|
||||
/ ( (double) childp -> ncall ) );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* its a member of a cycle, look at all parents from
|
||||
* outside the cycle
|
||||
*/
|
||||
headp -> printflag = FALSE;
|
||||
headp -> propfraction = 0.0;
|
||||
for ( memp = headp -> cnext ; memp ; memp = memp -> cnext ) {
|
||||
for (arcp = memp->parents ; arcp ; arcp = arcp->arc_parentlist) {
|
||||
if ( arcp -> arc_parentp -> cyclehead == headp ) {
|
||||
continue;
|
||||
}
|
||||
parentp = arcp -> arc_parentp;
|
||||
headp -> printflag |= parentp -> printflag;
|
||||
/*
|
||||
* if the cycle was never actually called
|
||||
* (e.g. this arc is static (and all others are, too))
|
||||
* no time propagates along this arc.
|
||||
*/
|
||||
if ( headp -> ncall ) {
|
||||
headp -> propfraction += parentp -> propfraction
|
||||
* ( ( (double) arcp -> arc_count )
|
||||
/ ( (double) headp -> ncall ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
for ( memp = headp ; memp ; memp = memp -> cnext ) {
|
||||
memp -> printflag = headp -> printflag;
|
||||
memp -> propfraction = headp -> propfraction;
|
||||
}
|
||||
}
|
||||
}
|
302
gprof/dfn.c
Normal file
302
gprof/dfn.c
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)dfn.c 5.4 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <stdio.h>
|
||||
#include "gprof.h"
|
||||
|
||||
#define DFN_DEPTH 100
|
||||
struct dfnstruct {
|
||||
nltype *nlentryp;
|
||||
int cycletop;
|
||||
};
|
||||
typedef struct dfnstruct dfntype;
|
||||
|
||||
dfntype dfn_stack[ DFN_DEPTH ];
|
||||
int dfn_depth = 0;
|
||||
|
||||
int dfn_counter = DFN_NAN;
|
||||
|
||||
/*
|
||||
* given this parent, depth first number its children.
|
||||
*/
|
||||
dfn( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
arctype *arcp;
|
||||
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn] dfn(" );
|
||||
printname( parentp );
|
||||
printf( ")\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* if we're already numbered, no need to look any furthur.
|
||||
*/
|
||||
if ( dfn_numbered( parentp ) ) {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* if we're already busy, must be a cycle
|
||||
*/
|
||||
if ( dfn_busy( parentp ) ) {
|
||||
dfn_findcycle( parentp );
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* visit yourself before your children
|
||||
*/
|
||||
dfn_pre_visit( parentp );
|
||||
/*
|
||||
* visit children
|
||||
*/
|
||||
for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
|
||||
dfn( arcp -> arc_childp );
|
||||
}
|
||||
/*
|
||||
* visit yourself after your children
|
||||
*/
|
||||
dfn_post_visit( parentp );
|
||||
}
|
||||
|
||||
/*
|
||||
* push a parent onto the stack and mark it busy
|
||||
*/
|
||||
dfn_pre_visit( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
|
||||
dfn_depth += 1;
|
||||
if ( dfn_depth >= DFN_DEPTH ) {
|
||||
fprintf( stderr , "[dfn] out of my depth (dfn_stack overflow)\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
dfn_stack[ dfn_depth ].nlentryp = parentp;
|
||||
dfn_stack[ dfn_depth ].cycletop = dfn_depth;
|
||||
parentp -> toporder = DFN_BUSY;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_pre_visit]\t\t%d:" , dfn_depth );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
|
||||
/*
|
||||
* are we already numbered?
|
||||
*/
|
||||
bool
|
||||
dfn_numbered( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
|
||||
return ( childp -> toporder != DFN_NAN && childp -> toporder != DFN_BUSY );
|
||||
}
|
||||
|
||||
/*
|
||||
* are we already busy?
|
||||
*/
|
||||
bool
|
||||
dfn_busy( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
|
||||
if ( childp -> toporder == DFN_NAN ) {
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* MISSING: an explanation
|
||||
*/
|
||||
dfn_findcycle( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
int cycletop;
|
||||
nltype *cycleheadp;
|
||||
nltype *tailp;
|
||||
int index;
|
||||
|
||||
for ( cycletop = dfn_depth ; cycletop > 0 ; cycletop -= 1 ) {
|
||||
cycleheadp = dfn_stack[ cycletop ].nlentryp;
|
||||
if ( childp == cycleheadp ) {
|
||||
break;
|
||||
}
|
||||
if ( childp -> cyclehead != childp &&
|
||||
childp -> cyclehead == cycleheadp ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( cycletop <= 0 ) {
|
||||
fprintf( stderr , "[dfn_findcycle] couldn't find head of cycle\n" );
|
||||
exit( 1 );
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] dfn_depth %d cycletop %d " ,
|
||||
dfn_depth , cycletop );
|
||||
printname( cycleheadp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( cycletop == dfn_depth ) {
|
||||
/*
|
||||
* this is previous function, e.g. this calls itself
|
||||
* sort of boring
|
||||
*/
|
||||
dfn_self_cycle( childp );
|
||||
} else {
|
||||
/*
|
||||
* glom intervening functions that aren't already
|
||||
* glommed into this cycle.
|
||||
* things have been glommed when their cyclehead field
|
||||
* points to the head of the cycle they are glommed into.
|
||||
*/
|
||||
for ( tailp = cycleheadp ; tailp -> cnext ; tailp = tailp -> cnext ) {
|
||||
/* void: chase down to tail of things already glommed */
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] tail " );
|
||||
printname( tailp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
/*
|
||||
* if what we think is the top of the cycle
|
||||
* has a cyclehead field, then it's not really the
|
||||
* head of the cycle, which is really what we want
|
||||
*/
|
||||
if ( cycleheadp -> cyclehead != cycleheadp ) {
|
||||
cycleheadp = cycleheadp -> cyclehead;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] new cyclehead " );
|
||||
printname( cycleheadp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
for ( index = cycletop + 1 ; index <= dfn_depth ; index += 1 ) {
|
||||
childp = dfn_stack[ index ].nlentryp;
|
||||
if ( childp -> cyclehead == childp ) {
|
||||
/*
|
||||
* not yet glommed anywhere, glom it
|
||||
* and fix any children it has glommed
|
||||
*/
|
||||
tailp -> cnext = childp;
|
||||
childp -> cyclehead = cycleheadp;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] glomming " );
|
||||
printname( childp );
|
||||
printf( " onto " );
|
||||
printname( cycleheadp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( tailp = childp ; tailp->cnext ; tailp = tailp->cnext ) {
|
||||
tailp -> cnext -> cyclehead = cycleheadp;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_findcycle] and its tail " );
|
||||
printname( tailp -> cnext );
|
||||
printf( " onto " );
|
||||
printname( cycleheadp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
} else if ( childp -> cyclehead != cycleheadp /* firewall */ ) {
|
||||
fprintf( stderr ,
|
||||
"[dfn_busy] glommed, but not to cyclehead\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* deal with self-cycles
|
||||
* for lint: ARGSUSED
|
||||
*/
|
||||
dfn_self_cycle( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
/*
|
||||
* since we are taking out self-cycles elsewhere
|
||||
* no need for the special case, here.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_self_cycle] " );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
|
||||
/*
|
||||
* visit a node after all its children
|
||||
* [MISSING: an explanation]
|
||||
* and pop it off the stack
|
||||
*/
|
||||
dfn_post_visit( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
nltype *memberp;
|
||||
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_post_visit]\t%d: " , dfn_depth );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* number functions and things in their cycles
|
||||
* unless the function is itself part of a cycle
|
||||
*/
|
||||
if ( parentp -> cyclehead == parentp ) {
|
||||
dfn_counter += 1;
|
||||
for ( memberp = parentp ; memberp ; memberp = memberp -> cnext ) {
|
||||
memberp -> toporder = dfn_counter;
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_post_visit]\t\tmember " );
|
||||
printname( memberp );
|
||||
printf( " -> toporder = %d\n" , dfn_counter );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
} else {
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "[dfn_post_visit]\t\tis part of a cycle\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
dfn_depth -= 1;
|
||||
}
|
105
gprof/gmon.h
Normal file
105
gprof/gmon.h
Normal file
@ -0,0 +1,105 @@
|
||||
/*-
|
||||
* Copyright (c) 1991 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. All advertising materials mentioning features or use of this software
|
||||
* must display the following acknowledgement:
|
||||
* This product includes software developed by the University of
|
||||
* California, Berkeley and its contributors.
|
||||
* 4. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)gmon.h 5.2 (Berkeley) 5/6/91
|
||||
*/
|
||||
|
||||
struct phdr {
|
||||
char *lpc;
|
||||
char *hpc;
|
||||
int ncnt;
|
||||
};
|
||||
|
||||
/*
|
||||
* histogram counters are unsigned shorts (according to the kernel).
|
||||
*/
|
||||
#define HISTCOUNTER unsigned short
|
||||
|
||||
/*
|
||||
* fraction of text space to allocate for histogram counters
|
||||
* here, 1/2
|
||||
*/
|
||||
#define HISTFRACTION 2
|
||||
|
||||
/*
|
||||
* Fraction of text space to allocate for from hash buckets.
|
||||
* The value of HASHFRACTION is based on the minimum number of bytes
|
||||
* of separation between two subroutine call points in the object code.
|
||||
* Given MIN_SUBR_SEPARATION bytes of separation the value of
|
||||
* HASHFRACTION is calculated as:
|
||||
*
|
||||
* HASHFRACTION = MIN_SUBR_SEPARATION / (2 * sizeof(short) - 1);
|
||||
*
|
||||
* For the VAX, the shortest two call sequence is:
|
||||
*
|
||||
* calls $0,(r0)
|
||||
* calls $0,(r0)
|
||||
*
|
||||
* which is separated by only three bytes, thus HASHFRACTION is
|
||||
* calculated as:
|
||||
*
|
||||
* HASHFRACTION = 3 / (2 * 2 - 1) = 1
|
||||
*
|
||||
* Note that the division above rounds down, thus if MIN_SUBR_FRACTION
|
||||
* is less than three, this algorithm will not work!
|
||||
*/
|
||||
#define HASHFRACTION 1
|
||||
|
||||
/*
|
||||
* percent of text space to allocate for tostructs
|
||||
* with a minimum.
|
||||
*/
|
||||
#define ARCDENSITY 2
|
||||
#define MINARCS 50
|
||||
|
||||
struct tostruct {
|
||||
char *selfpc;
|
||||
long count;
|
||||
unsigned short link;
|
||||
};
|
||||
|
||||
/*
|
||||
* a raw arc,
|
||||
* with pointers to the calling site and the called site
|
||||
* and a count.
|
||||
*/
|
||||
struct rawarc {
|
||||
unsigned long raw_frompc;
|
||||
unsigned long raw_selfpc;
|
||||
long raw_count;
|
||||
};
|
||||
|
||||
/*
|
||||
* general rounding functions.
|
||||
*/
|
||||
#define ROUNDDOWN(x,y) (((x)/(y))*(y))
|
||||
#define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
|
264
gprof/gprof.1
Normal file
264
gprof/gprof.1
Normal file
@ -0,0 +1,264 @@
|
||||
.\" Copyright (c) 1983, 1990 The Regents of the University of California.
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms are permitted provided
|
||||
.\" that: (1) source distributions retain this entire copyright notice and
|
||||
.\" comment, and (2) distributions including binaries display the following
|
||||
.\" acknowledgement: ``This product includes software developed by the
|
||||
.\" University of California, Berkeley and its contributors'' in the
|
||||
.\" documentation or other materials provided with the distribution and in
|
||||
.\" all advertising materials mentioning features or use of this software.
|
||||
.\" Neither the name of the University nor the names of its contributors may
|
||||
.\" be used to endorse or promote products derived from this software without
|
||||
.\" specific prior written permission.
|
||||
.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.\"
|
||||
.\" @(#)gprof.1 6.6 (Berkeley) 7/24/90
|
||||
.\"
|
||||
.Dd July 24, 1990
|
||||
.Dt GPROF 1
|
||||
.Os BSD 4.2
|
||||
.Sh NAME
|
||||
.Nm gprof
|
||||
.Nd display call graph profile data
|
||||
.Sh SYNOPSIS
|
||||
.Nm gprof
|
||||
.Op options
|
||||
.Op Ar a.out Op Ar gmon.out ...
|
||||
.Sh DESCRIPTION
|
||||
.Nm gprof
|
||||
produces an execution profile of C, Pascal, or Fortran77 programs.
|
||||
The effect of called routines is incorporated in the profile of each caller.
|
||||
The profile data is taken from the call graph profile file
|
||||
.Pf \&( Pa gmon.out
|
||||
default) 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.
|
||||
.Nm Gprof
|
||||
reads the given object file (the default is
|
||||
.Pa a.out)
|
||||
and establishes the relation between it's symbol table
|
||||
and the call graph profile from
|
||||
.Pa gmon.out .
|
||||
If more than one profile file is specified,
|
||||
the
|
||||
.Nm gprof
|
||||
output shows the sum of the profile information in the given profile files.
|
||||
.Pp
|
||||
.Nm Gprof
|
||||
calculates the amount of time spent in each routine.
|
||||
Next, these times are propagated along the edges of the call graph.
|
||||
Cycles are discovered, and calls into a cycle are made to share the time
|
||||
of the cycle.
|
||||
The first listing shows the functions
|
||||
sorted according to the time they represent
|
||||
including the time of their call graph descendents.
|
||||
Below each function entry is shown its (direct) call graph children,
|
||||
and how their times are propagated to this function.
|
||||
A similar display above the function shows how this function's time and the
|
||||
time of its descendents is propagated to its (direct) call graph parents.
|
||||
.Pp
|
||||
Cycles are also shown, with an entry for the cycle as a whole and
|
||||
a listing of the members of the cycle and their contributions to the
|
||||
time and call counts of the cycle.
|
||||
.Pp
|
||||
Second, a flat profile is given,
|
||||
similar to that provided by
|
||||
.Xr prof 1 .
|
||||
This listing gives the total execution times, the call counts,
|
||||
the time in milleseconds the call spent in the routine itself, and
|
||||
the time in milleseconds the call spent in the routine itself including
|
||||
its descendents.
|
||||
.Pp
|
||||
Finally, an index of the function names is provided.
|
||||
.Pp
|
||||
The following options are available:
|
||||
.Tw Fl
|
||||
.Tp Fl a
|
||||
suppresses the printing of statically declared functions.
|
||||
If this option is given, all relevant information about the static function
|
||||
(e.g., time samples, calls to other functions, calls from other functions)
|
||||
belongs to the function loaded just before the static function in the
|
||||
.Pa a.out
|
||||
file.
|
||||
.Tp Fl b
|
||||
suppresses the printing of a description of each field in the profile.
|
||||
.Tp 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.
|
||||
.Tc Fl e
|
||||
.Ws
|
||||
.Ar name
|
||||
.Cx
|
||||
suppresses 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.
|
||||
.Tc Fl E
|
||||
.Ws
|
||||
.Ar name
|
||||
.Cx
|
||||
suppresses 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.)
|
||||
.Tc Fl f
|
||||
.Ws
|
||||
.Ar name
|
||||
.Cx
|
||||
prints 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.
|
||||
.Tc Fl F
|
||||
.Ws
|
||||
.Ar name
|
||||
.Cx
|
||||
prints 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.
|
||||
.Tc Fl k
|
||||
.Ws
|
||||
.Ar fromname
|
||||
.Ws
|
||||
.Ar toname
|
||||
.Cx
|
||||
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.
|
||||
.Tp 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.
|
||||
.Tp Fl z
|
||||
displays 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.
|
||||
.Tp
|
||||
.Sh FILES
|
||||
.Dw gmon.sum
|
||||
.Di L
|
||||
.Dp Pa a.out
|
||||
the namelist and text space.
|
||||
.Dp Pa gmon.out
|
||||
dynamic call graph and profile.
|
||||
.Dp Pa gmon.sum
|
||||
summarized dynamic call graph and profile.
|
||||
.Dp
|
||||
.Sh SEE ALSO
|
||||
.Xr monitor 3 ,
|
||||
.Xr profil 2 ,
|
||||
.Xr cc 1 ,
|
||||
.Xr prof 1
|
||||
.br
|
||||
.Em An Execution Profiler for Modular Programs ,
|
||||
by
|
||||
S. Graham, P. Kessler, M. McKusick;
|
||||
Software - Practice and Experience,
|
||||
Vol. 13, pp. 671-685, 1983.
|
||||
.br
|
||||
.Em gprof: A Call Graph Execution Profiler ,
|
||||
by S. Graham, P. Kessler, M. McKusick;
|
||||
Proceedings of the SIGPLAN '82 Symposium on Compiler Construction,
|
||||
SIGPLAN Notices, Vol. 17, No 6, pp. 120-126, June 1982.
|
||||
.Sh HISTORY
|
||||
.Nm Gprof
|
||||
appeared in 4.2 BSD.
|
||||
.Sh BUGS
|
||||
The granularity of the sampling is shown, but remains
|
||||
statistical at best.
|
||||
We assume that the time for each execution of a function
|
||||
can be expressed by the total time for the function divided
|
||||
by the number of times the function is called.
|
||||
Thus the time propagated along the call graph arcs to the function's
|
||||
parents is directly proportional to the number of times that
|
||||
arc is traversed.
|
||||
.Pp
|
||||
Parents that are not themselves profiled will have the time of
|
||||
their profiled children propagated to them, but they will appear
|
||||
to be spontaneously invoked in the call graph listing, and will
|
||||
not have their time propagated further.
|
||||
Similarly, signal catchers, even though profiled, will appear
|
||||
to be spontaneous (although for more obscure reasons).
|
||||
Any profiled children of signal catchers should have their times
|
||||
propagated properly, unless the signal catcher was invoked during
|
||||
the execution of the profiling routine, in which case all is lost.
|
||||
.Pp
|
||||
The profiled program must call
|
||||
.Xr exit 2
|
||||
or return normally for the profiling information to be saved
|
||||
in the
|
||||
.Pa gmon.out
|
||||
file.
|
691
gprof/gprof.c
Normal file
691
gprof/gprof.c
Normal file
@ -0,0 +1,691 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
char copyright[] =
|
||||
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
|
||||
All rights reserved.\n";
|
||||
#endif /* not lint */
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)gprof.c 5.6 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
char *whoami = "gprof";
|
||||
|
||||
/*
|
||||
* things which get -E excluded by default.
|
||||
*/
|
||||
char *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
|
||||
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char **argv;
|
||||
{
|
||||
char **sp;
|
||||
nltype **timesortnlp;
|
||||
|
||||
--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;
|
||||
break;
|
||||
case 'd':
|
||||
dflag = TRUE;
|
||||
(*argv)++;
|
||||
debug |= atoi( *argv );
|
||||
debug |= ANYDEBUG;
|
||||
# ifdef DEBUG
|
||||
printf("[main] debug = %d\n", debug);
|
||||
# else not DEBUG
|
||||
printf("%s: -d ignored\n", whoami);
|
||||
# endif DEBUG
|
||||
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 's':
|
||||
sflag = 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 = GMONNAME;
|
||||
}
|
||||
/*
|
||||
* turn off default functions
|
||||
*/
|
||||
for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
|
||||
Eflag = TRUE;
|
||||
addlist( Elist , *sp );
|
||||
eflag = TRUE;
|
||||
addlist( elist , *sp );
|
||||
}
|
||||
/*
|
||||
* how many ticks per second?
|
||||
* if we can't tell, report time in ticks.
|
||||
*/
|
||||
hz = hertz();
|
||||
if (hz == 0) {
|
||||
hz = 1;
|
||||
fprintf(stderr, "time is in ticks, not seconds\n");
|
||||
}
|
||||
/*
|
||||
* get information about a.out file.
|
||||
*/
|
||||
getnfile();
|
||||
/*
|
||||
* get information about mon.out file(s).
|
||||
*/
|
||||
do {
|
||||
getpfile( gmonname );
|
||||
if ( *argv != 0 ) {
|
||||
gmonname = *argv;
|
||||
}
|
||||
} while ( *argv++ != 0 );
|
||||
/*
|
||||
* dump out a gmon.sum file if requested
|
||||
*/
|
||||
if ( sflag ) {
|
||||
dumpsum( GMONSUM );
|
||||
}
|
||||
/*
|
||||
* assign samples to procedures
|
||||
*/
|
||||
asgnsamples();
|
||||
/*
|
||||
* assemble the dynamic profile
|
||||
*/
|
||||
timesortnlp = doarcs();
|
||||
/*
|
||||
* print the dynamic profile
|
||||
*/
|
||||
printgprof( timesortnlp );
|
||||
/*
|
||||
* print the flat profile
|
||||
*/
|
||||
printprof();
|
||||
/*
|
||||
* print the index
|
||||
*/
|
||||
printindex();
|
||||
done();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up string and symbol tables from a.out.
|
||||
* and optionally the text space.
|
||||
* On return symbol table is sorted by value.
|
||||
*/
|
||||
getnfile()
|
||||
{
|
||||
FILE *nfile;
|
||||
int valcmp();
|
||||
|
||||
nfile = fopen( a_outname ,"r");
|
||||
if (nfile == NULL) {
|
||||
perror( a_outname );
|
||||
done();
|
||||
}
|
||||
fread(&xbuf, 1, sizeof(xbuf), nfile);
|
||||
if (N_BADMAG(xbuf)) {
|
||||
fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname );
|
||||
done();
|
||||
}
|
||||
getstrtab(nfile);
|
||||
getsymtab(nfile);
|
||||
gettextspace( nfile );
|
||||
qsort(nl, nname, sizeof(nltype), valcmp);
|
||||
fclose(nfile);
|
||||
# ifdef DEBUG
|
||||
if ( debug & AOUTDEBUG ) {
|
||||
register int j;
|
||||
|
||||
for (j = 0; j < nname; j++){
|
||||
printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
|
||||
}
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
|
||||
getstrtab(nfile)
|
||||
FILE *nfile;
|
||||
{
|
||||
|
||||
fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
|
||||
if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
|
||||
fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
|
||||
whoami , a_outname );
|
||||
done();
|
||||
}
|
||||
strtab = (char *)calloc(ssiz, 1);
|
||||
if (strtab == NULL) {
|
||||
fprintf(stderr, "%s: %s: no room for %d bytes of string table",
|
||||
whoami , a_outname , ssiz);
|
||||
done();
|
||||
}
|
||||
if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
|
||||
fprintf(stderr, "%s: %s: error reading string table\n",
|
||||
whoami , a_outname );
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in symbol table
|
||||
*/
|
||||
getsymtab(nfile)
|
||||
FILE *nfile;
|
||||
{
|
||||
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) {
|
||||
fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
|
||||
done();
|
||||
}
|
||||
askfor = nname + 1;
|
||||
nl = (nltype *) calloc( askfor , sizeof(nltype) );
|
||||
if (nl == 0) {
|
||||
fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
|
||||
whoami, askfor * sizeof(nltype) );
|
||||
done();
|
||||
}
|
||||
|
||||
/* pass2 - read symbols */
|
||||
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%08x\n" ,
|
||||
nname , npe -> name , npe -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
npe++;
|
||||
nname++;
|
||||
}
|
||||
npe->value = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* read in the text space of an a.out file
|
||||
*/
|
||||
gettextspace( nfile )
|
||||
FILE *nfile;
|
||||
{
|
||||
char *malloc();
|
||||
|
||||
if ( cflag == 0 ) {
|
||||
return;
|
||||
}
|
||||
textspace = (u_char *) malloc( xbuf.a_text );
|
||||
if ( textspace == 0 ) {
|
||||
fprintf( stderr , "%s: ran out room for %d bytes of text space: " ,
|
||||
whoami , xbuf.a_text );
|
||||
fprintf( stderr , "can't do -c\n" );
|
||||
return;
|
||||
}
|
||||
(void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
|
||||
if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
|
||||
fprintf( stderr , "%s: couldn't read text space: " , whoami );
|
||||
fprintf( stderr , "can't do -c\n" );
|
||||
free( textspace );
|
||||
textspace = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* information from a gmon.out file is in two parts:
|
||||
* an array of sampling hits within pc ranges,
|
||||
* and the arcs.
|
||||
*/
|
||||
getpfile(filename)
|
||||
char *filename;
|
||||
{
|
||||
FILE *pfile;
|
||||
FILE *openpfile();
|
||||
struct rawarc arc;
|
||||
|
||||
pfile = openpfile(filename);
|
||||
readsamples(pfile);
|
||||
/*
|
||||
* the rest of the file consists of
|
||||
* a bunch of <from,self,count> tuples.
|
||||
*/
|
||||
while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & SAMPLEDEBUG ) {
|
||||
printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
|
||||
arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
|
||||
}
|
||||
# endif DEBUG
|
||||
/*
|
||||
* add this arc
|
||||
*/
|
||||
tally( &arc );
|
||||
}
|
||||
fclose(pfile);
|
||||
}
|
||||
|
||||
FILE *
|
||||
openpfile(filename)
|
||||
char *filename;
|
||||
{
|
||||
struct hdr tmp;
|
||||
FILE *pfile;
|
||||
|
||||
if((pfile = fopen(filename, "r")) == NULL) {
|
||||
perror(filename);
|
||||
done();
|
||||
}
|
||||
fread(&tmp, sizeof(struct hdr), 1, pfile);
|
||||
if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
|
||||
tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
|
||||
fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
|
||||
done();
|
||||
}
|
||||
h = tmp;
|
||||
s_lowpc = (unsigned long) h.lowpc;
|
||||
s_highpc = (unsigned long) h.highpc;
|
||||
lowpc = (unsigned long)h.lowpc / sizeof(UNIT);
|
||||
highpc = (unsigned long)h.highpc / sizeof(UNIT);
|
||||
sampbytes = h.ncnt - sizeof(struct hdr);
|
||||
nsamples = sampbytes / sizeof (UNIT);
|
||||
# ifdef DEBUG
|
||||
if ( debug & SAMPLEDEBUG ) {
|
||||
printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
|
||||
h.lowpc , h.highpc , h.ncnt );
|
||||
printf( "[openpfile] s_lowpc 0x%x s_highpc 0x%x\n" ,
|
||||
s_lowpc , s_highpc );
|
||||
printf( "[openpfile] lowpc 0x%x highpc 0x%x\n" ,
|
||||
lowpc , highpc );
|
||||
printf( "[openpfile] sampbytes %d nsamples %d\n" ,
|
||||
sampbytes , nsamples );
|
||||
}
|
||||
# endif DEBUG
|
||||
return(pfile);
|
||||
}
|
||||
|
||||
tally( rawp )
|
||||
struct rawarc *rawp;
|
||||
{
|
||||
nltype *parentp;
|
||||
nltype *childp;
|
||||
|
||||
parentp = nllookup( rawp -> raw_frompc );
|
||||
childp = nllookup( rawp -> raw_selfpc );
|
||||
if ( kflag
|
||||
&& onlist( kfromlist , parentp -> name )
|
||||
&& onlist( ktolist , childp -> name ) ) {
|
||||
return;
|
||||
}
|
||||
childp -> ncall += rawp -> raw_count;
|
||||
# ifdef DEBUG
|
||||
if ( debug & TALLYDEBUG ) {
|
||||
printf( "[tally] arc from %s to %s traversed %d times\n" ,
|
||||
parentp -> name , childp -> name , rawp -> raw_count );
|
||||
}
|
||||
# endif DEBUG
|
||||
addarc( parentp , childp , rawp -> raw_count );
|
||||
}
|
||||
|
||||
/*
|
||||
* dump out the gmon.sum file
|
||||
*/
|
||||
dumpsum( sumfile )
|
||||
char *sumfile;
|
||||
{
|
||||
register nltype *nlp;
|
||||
register arctype *arcp;
|
||||
struct rawarc arc;
|
||||
FILE *sfile;
|
||||
|
||||
if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
|
||||
perror( sumfile );
|
||||
done();
|
||||
}
|
||||
/*
|
||||
* dump the header; use the last header read in
|
||||
*/
|
||||
if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
|
||||
perror( sumfile );
|
||||
done();
|
||||
}
|
||||
/*
|
||||
* dump the samples
|
||||
*/
|
||||
if (fwrite(samples, sizeof (UNIT), nsamples, sfile) != nsamples) {
|
||||
perror( sumfile );
|
||||
done();
|
||||
}
|
||||
/*
|
||||
* 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 ) {
|
||||
perror( sumfile );
|
||||
done();
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & SAMPLEDEBUG ) {
|
||||
printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
|
||||
arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
}
|
||||
fclose( sfile );
|
||||
}
|
||||
|
||||
valcmp(p1, p2)
|
||||
nltype *p1, *p2;
|
||||
{
|
||||
if ( p1 -> value < p2 -> value ) {
|
||||
return LESSTHAN;
|
||||
}
|
||||
if ( p1 -> value > p2 -> value ) {
|
||||
return GREATERTHAN;
|
||||
}
|
||||
return EQUALTO;
|
||||
}
|
||||
|
||||
readsamples(pfile)
|
||||
FILE *pfile;
|
||||
{
|
||||
register i;
|
||||
UNIT sample;
|
||||
|
||||
if (samples == 0) {
|
||||
samples = (UNIT *) calloc(sampbytes, sizeof (UNIT));
|
||||
if (samples == 0) {
|
||||
fprintf( stderr , "%s: No room for %d sample pc's\n",
|
||||
whoami , sampbytes / sizeof (UNIT));
|
||||
done();
|
||||
}
|
||||
}
|
||||
for (i = 0; i < nsamples; i++) {
|
||||
fread(&sample, sizeof (UNIT), 1, pfile);
|
||||
if (feof(pfile))
|
||||
break;
|
||||
samples[i] += sample;
|
||||
}
|
||||
if (i != nsamples) {
|
||||
fprintf(stderr,
|
||||
"%s: unexpected EOF after reading %d/%d samples\n",
|
||||
whoami , --i , nsamples );
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign samples to the procedures to which they belong.
|
||||
*
|
||||
* There are three cases as to where pcl and pch can be
|
||||
* with respect to the routine entry addresses svalue0 and svalue1
|
||||
* as shown in the following diagram. overlap computes the
|
||||
* distance between the arrows, the fraction of the sample
|
||||
* that is to be credited to the routine which starts at svalue0.
|
||||
*
|
||||
* svalue0 svalue1
|
||||
* | |
|
||||
* v v
|
||||
*
|
||||
* +-----------------------------------------------+
|
||||
* | |
|
||||
* | ->| |<- ->| |<- ->| |<- |
|
||||
* | | | | | |
|
||||
* +---------+ +---------+ +---------+
|
||||
*
|
||||
* ^ ^ ^ ^ ^ ^
|
||||
* | | | | | |
|
||||
* pcl pch pcl pch pcl pch
|
||||
*
|
||||
* For the vax we assert that samples will never fall in the first
|
||||
* two bytes of any routine, since that is the entry mask,
|
||||
* thus we give call alignentries() to adjust the entry points if
|
||||
* the entry mask falls in one bucket but the code for the routine
|
||||
* doesn't start until the next bucket. In conjunction with the
|
||||
* alignment of routine addresses, this should allow us to have
|
||||
* only one sample for every four bytes of text space and never
|
||||
* have any overlap (the two end cases, above).
|
||||
*/
|
||||
asgnsamples()
|
||||
{
|
||||
register int j;
|
||||
UNIT 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 + scale * i;
|
||||
pch = lowpc + scale * (i + 1);
|
||||
time = ccnt;
|
||||
# ifdef DEBUG
|
||||
if ( debug & SAMPLEDEBUG ) {
|
||||
printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
|
||||
pcl , pch , ccnt );
|
||||
}
|
||||
# endif DEBUG
|
||||
totime += time;
|
||||
for (j = j - 1; j < nname; j++) {
|
||||
svalue0 = nl[j].svalue;
|
||||
svalue1 = nl[j+1].svalue;
|
||||
/*
|
||||
* if high end of tick is below entry address,
|
||||
* go for next tick.
|
||||
*/
|
||||
if (pch < svalue0)
|
||||
break;
|
||||
/*
|
||||
* if low end of tick into next routine,
|
||||
* go for next routine.
|
||||
*/
|
||||
if (pcl >= svalue1)
|
||||
continue;
|
||||
overlap = min(pch, svalue1) - max(pcl, svalue0);
|
||||
if (overlap > 0) {
|
||||
# ifdef DEBUG
|
||||
if (debug & SAMPLEDEBUG) {
|
||||
printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
|
||||
nl[j].value/sizeof(UNIT), svalue0, svalue1,
|
||||
nl[j].name,
|
||||
overlap * time / scale, overlap);
|
||||
}
|
||||
# endif DEBUG
|
||||
nl[j].time += overlap * time / scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if (debug & SAMPLEDEBUG) {
|
||||
printf("[asgnsamples] totime %f\n", totime);
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
|
||||
|
||||
unsigned long
|
||||
min(a, b)
|
||||
unsigned long a,b;
|
||||
{
|
||||
if (a<b)
|
||||
return(a);
|
||||
return(b);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
max(a, b)
|
||||
unsigned long a,b;
|
||||
{
|
||||
if (a>b)
|
||||
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.
|
||||
*/
|
||||
alignentries()
|
||||
{
|
||||
register struct nl *nlp;
|
||||
unsigned long bucket_of_entry;
|
||||
unsigned long bucket_of_code;
|
||||
|
||||
for (nlp = nl; nlp < npe; nlp++) {
|
||||
nlp -> svalue = nlp -> value / sizeof(UNIT);
|
||||
bucket_of_entry = (nlp->svalue - lowpc) / scale;
|
||||
bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
|
||||
if (bucket_of_entry < bucket_of_code) {
|
||||
# ifdef DEBUG
|
||||
if (debug & SAMPLEDEBUG) {
|
||||
printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
|
||||
nlp->svalue, nlp->svalue + UNITS_TO_CODE);
|
||||
}
|
||||
# endif DEBUG
|
||||
nlp->svalue += UNITS_TO_CODE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
funcsymbol( nlistp )
|
||||
struct nlist *nlistp;
|
||||
{
|
||||
extern char *strtab; /* string table from a.out */
|
||||
extern int aflag; /* if static functions aren't desired */
|
||||
char *name;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
/*
|
||||
* can't have any `funny' characters in name,
|
||||
* where `funny' includes `.', .o file names
|
||||
* and `$', pascal labels.
|
||||
*/
|
||||
for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) {
|
||||
if ( *name == '.' || *name == '$' ) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
done()
|
||||
{
|
||||
|
||||
exit(0);
|
||||
}
|
108
gprof/gprof.callg
Normal file
108
gprof/gprof.callg
Normal file
@ -0,0 +1,108 @@
|
||||
|
||||
|
||||
|
||||
call graph profile:
|
||||
The sum of self and descendents is the major sort
|
||||
for this listing.
|
||||
|
||||
function entries:
|
||||
|
||||
index the index of the function in the call graph
|
||||
listing, as an aid to locating it (see below).
|
||||
|
||||
%time the percentage of the total time of the program
|
||||
accounted for by this function and its
|
||||
descendents.
|
||||
|
||||
self the number of seconds spent in this function
|
||||
itself.
|
||||
|
||||
descendents
|
||||
the number of seconds spent in the descendents of
|
||||
this function on behalf of this function.
|
||||
|
||||
called the number of times this function is called (other
|
||||
than recursive calls).
|
||||
|
||||
self the number of times this function calls itself
|
||||
recursively.
|
||||
|
||||
name the name of the function, with an indication of
|
||||
its membership in a cycle, if any.
|
||||
|
||||
index the index of the function in the call graph
|
||||
listing, as an aid to locating it.
|
||||
|
||||
|
||||
|
||||
parent listings:
|
||||
|
||||
self* the number of seconds of this function's self time
|
||||
which is due to calls from this parent.
|
||||
|
||||
descendents*
|
||||
the number of seconds of this function's
|
||||
descendent time which is due to calls from this
|
||||
parent.
|
||||
|
||||
called** the number of times this function is called by
|
||||
this parent. This is the numerator of the
|
||||
fraction which divides up the function's time to
|
||||
its parents.
|
||||
|
||||
total* the number of times this function was called by
|
||||
all of its parents. This is the denominator of
|
||||
the propagation fraction.
|
||||
|
||||
parents the name of this parent, with an indication of the
|
||||
parent's membership in a cycle, if any.
|
||||
|
||||
index the index of this parent in the call graph
|
||||
listing, as an aid in locating it.
|
||||
|
||||
|
||||
|
||||
children listings:
|
||||
|
||||
self* the number of seconds of this child's self time
|
||||
which is due to being called by this function.
|
||||
|
||||
descendent*
|
||||
the number of seconds of this child's descendent's
|
||||
time which is due to being called by this
|
||||
function.
|
||||
|
||||
called** the number of times this child is called by this
|
||||
function. This is the numerator of the
|
||||
propagation fraction for this child.
|
||||
|
||||
total* the number of times this child is called by all
|
||||
functions. This is the denominator of the
|
||||
propagation fraction.
|
||||
|
||||
children the name of this child, and an indication of its
|
||||
membership in a cycle, if any.
|
||||
|
||||
index the index of this child in the call graph listing,
|
||||
as an aid to locating it.
|
||||
|
||||
|
||||
|
||||
* these fields are omitted for parents (or
|
||||
children) in the same cycle as the function. If
|
||||
the function (or child) is a member of a cycle,
|
||||
the propagated times and propagation denominator
|
||||
represent the self time and descendent time of the
|
||||
cycle as a whole.
|
||||
|
||||
** static-only parents and children are indicated
|
||||
by a call count of 0.
|
||||
|
||||
|
||||
|
||||
cycle listings:
|
||||
the cycle as a whole is listed with the same
|
||||
fields as a function entry. Below it are listed
|
||||
the members of the cycle, and their contributions
|
||||
to the time and call counts of the cycle.
|
||||
|
32
gprof/gprof.flat
Normal file
32
gprof/gprof.flat
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
|
||||
|
||||
flat profile:
|
||||
|
||||
% the percentage of the total running time of the
|
||||
time program used by this function.
|
||||
|
||||
cumulative a running sum of the number of seconds accounted
|
||||
seconds for by this function and those listed above it.
|
||||
|
||||
self the number of seconds accounted for by this
|
||||
seconds function alone. This is the major sort for this
|
||||
listing.
|
||||
|
||||
calls the number of times this function was invoked, if
|
||||
this function is profiled, else blank.
|
||||
|
||||
self the average number of milliseconds spent in this
|
||||
ms/call function per call, if this function is profiled,
|
||||
else blank.
|
||||
|
||||
total the average number of milliseconds spent in this
|
||||
ms/call function and its descendents per call, if this
|
||||
function is profiled, else blank.
|
||||
|
||||
name the name of the function. This is the minor sort
|
||||
for this listing. The index shows the location of
|
||||
the function in the gprof listing. If the index is
|
||||
in parenthesis it shows where it would appear in
|
||||
the gprof listing if it were to be printed.
|
||||
|
284
gprof/gprof.h
Normal file
284
gprof/gprof.h
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#)gprof.h 5.9 (Berkeley) 6/1/90
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <a.out.h>
|
||||
#include <stdio.h>
|
||||
#include "gmon.h"
|
||||
|
||||
#ifdef MACHINE_H
|
||||
# include MACHINE_H
|
||||
#else
|
||||
# if vax
|
||||
# include "vax.h"
|
||||
# endif
|
||||
# if sun
|
||||
# include "sun.h"
|
||||
# endif
|
||||
# if tahoe
|
||||
# include "tahoe.h"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* who am i, for error messages.
|
||||
*/
|
||||
char *whoami;
|
||||
|
||||
/*
|
||||
* booleans
|
||||
*/
|
||||
typedef int bool;
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
|
||||
/*
|
||||
* ticks per second
|
||||
*/
|
||||
long hz;
|
||||
|
||||
typedef u_short UNIT; /* unit of profiling */
|
||||
char *a_outname;
|
||||
#define A_OUTNAME "a.out"
|
||||
|
||||
char *gmonname;
|
||||
#define GMONNAME "gmon.out"
|
||||
#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; /* how calls from parent to child */
|
||||
double arc_time; /* time inherited along arc */
|
||||
double arc_childtime; /* childtime inherited along arc */
|
||||
struct arcstruct *arc_parentlist; /* parents-of-this-child list */
|
||||
struct arcstruct *arc_childlist; /* children-of-this-parent list */
|
||||
};
|
||||
typedef struct arcstruct arctype;
|
||||
|
||||
/*
|
||||
* The symbol table;
|
||||
* for each external in the specified file we gather
|
||||
* its address, the number of calls and compute its share of cpu time.
|
||||
*/
|
||||
struct nl {
|
||||
char *name; /* the name */
|
||||
unsigned long value; /* the pc entry point */
|
||||
unsigned long svalue; /* entry point aligned to histograms */
|
||||
double time; /* ticks in this routine */
|
||||
double childtime; /* cumulative ticks in children */
|
||||
long ncall; /* how many times called */
|
||||
long selfcalls; /* how many calls to self */
|
||||
double propfraction; /* what % of time propagates */
|
||||
double propself; /* how much self time propagates */
|
||||
double propchild; /* how much child time propagates */
|
||||
bool printflag; /* should this be printed? */
|
||||
int index; /* index in the graph list */
|
||||
int toporder; /* graph call chain top-sort order */
|
||||
int cycleno; /* internal number of cycle on */
|
||||
struct nl *cyclehead; /* pointer to head of cycle */
|
||||
struct nl *cnext; /* pointer to next member of cycle */
|
||||
arctype *parents; /* list of caller arcs */
|
||||
arctype *children; /* list of callee arcs */
|
||||
};
|
||||
typedef struct nl nltype;
|
||||
|
||||
nltype *nl; /* the whole namelist */
|
||||
nltype *npe; /* the virtual end of the namelist */
|
||||
int nname; /* the number of function names */
|
||||
|
||||
/*
|
||||
* flag which marks a nl entry as topologically ``busy''
|
||||
* flag which marks a nl entry as topologically ``not_numbered''
|
||||
*/
|
||||
#define DFN_BUSY -1
|
||||
#define DFN_NAN 0
|
||||
|
||||
/*
|
||||
* namelist entries for cycle headers.
|
||||
* the number of discovered cycles.
|
||||
*/
|
||||
nltype *cyclenl; /* cycle header namelist */
|
||||
int ncycle; /* number of cycles discovered */
|
||||
|
||||
/*
|
||||
* The header on the gmon.out file.
|
||||
* gmon.out consists of one of these headers,
|
||||
* and then an array of ncnt samples
|
||||
* representing the discretized program counter values.
|
||||
* this should be a struct phdr, but since everything is done
|
||||
* as UNITs, this is in UNITs too.
|
||||
*/
|
||||
struct hdr {
|
||||
UNIT *lowpc;
|
||||
UNIT *highpc;
|
||||
int ncnt;
|
||||
};
|
||||
|
||||
struct hdr h;
|
||||
|
||||
int debug;
|
||||
|
||||
/*
|
||||
* Each discretized pc sample has
|
||||
* a count of the number of samples in its range
|
||||
*/
|
||||
UNIT *samples;
|
||||
|
||||
unsigned long s_lowpc; /* lowpc from the profile file */
|
||||
unsigned long s_highpc; /* highpc from the profile file */
|
||||
unsigned lowpc, highpc; /* range profiled, in UNIT's */
|
||||
unsigned sampbytes; /* number of bytes of samples */
|
||||
int nsamples; /* number of samples */
|
||||
double actime; /* accumulated time thus far for putprofline */
|
||||
double totime; /* total time for all routines */
|
||||
double printtime; /* total of time being printed */
|
||||
double scale; /* scale factor converting samples to pc
|
||||
values: each sample covers scale bytes */
|
||||
char *strtab; /* string table in core */
|
||||
off_t ssiz; /* size of the string table */
|
||||
struct exec xbuf; /* exec header of a.out */
|
||||
unsigned char *textspace; /* text space of a.out in core */
|
||||
|
||||
/*
|
||||
* option flags, from a to z.
|
||||
*/
|
||||
bool aflag; /* suppress static functions */
|
||||
bool bflag; /* blurbs, too */
|
||||
bool cflag; /* discovered call graph, too */
|
||||
bool dflag; /* debugging options */
|
||||
bool eflag; /* specific functions excluded */
|
||||
bool Eflag; /* functions excluded with time */
|
||||
bool fflag; /* specific functions requested */
|
||||
bool Fflag; /* functions requested with time */
|
||||
bool kflag; /* arcs to be deleted */
|
||||
bool sflag; /* sum multiple gmon.out files */
|
||||
bool zflag; /* zero time/called functions, too */
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
/*
|
||||
addarc();
|
||||
*/
|
||||
int arccmp();
|
||||
arctype *arclookup();
|
||||
/*
|
||||
asgnsamples();
|
||||
printblurb();
|
||||
cyclelink();
|
||||
dfn();
|
||||
*/
|
||||
bool dfn_busy();
|
||||
/*
|
||||
dfn_findcycle();
|
||||
*/
|
||||
bool dfn_numbered();
|
||||
/*
|
||||
dfn_post_visit();
|
||||
dfn_pre_visit();
|
||||
dfn_self_cycle();
|
||||
*/
|
||||
nltype **doarcs();
|
||||
/*
|
||||
done();
|
||||
findcalls();
|
||||
flatprofheader();
|
||||
flatprofline();
|
||||
*/
|
||||
bool funcsymbol();
|
||||
/*
|
||||
getnfile();
|
||||
getpfile();
|
||||
getstrtab();
|
||||
getsymtab();
|
||||
gettextspace();
|
||||
gprofheader();
|
||||
gprofline();
|
||||
main();
|
||||
*/
|
||||
unsigned long max();
|
||||
int membercmp();
|
||||
unsigned long min();
|
||||
nltype *nllookup();
|
||||
FILE *openpfile();
|
||||
/*
|
||||
printchildren();
|
||||
printcycle();
|
||||
printgprof();
|
||||
printmembers();
|
||||
printname();
|
||||
printparents();
|
||||
printprof();
|
||||
readsamples();
|
||||
*/
|
||||
unsigned long reladdr();
|
||||
/*
|
||||
sortchildren();
|
||||
sortmembers();
|
||||
sortparents();
|
||||
tally();
|
||||
timecmp();
|
||||
topcmp();
|
||||
*/
|
||||
int totalcmp();
|
||||
/*
|
||||
valcmp();
|
||||
*/
|
||||
|
||||
#define LESSTHAN -1
|
||||
#define EQUALTO 0
|
||||
#define GREATERTHAN 1
|
||||
|
||||
#define DFNDEBUG 1
|
||||
#define CYCLEDEBUG 2
|
||||
#define ARCDEBUG 4
|
||||
#define TALLYDEBUG 8
|
||||
#define TIMEDEBUG 16
|
||||
#define SAMPLEDEBUG 32
|
||||
#define AOUTDEBUG 64
|
||||
#define CALLDEBUG 128
|
||||
#define LOOKUPDEBUG 256
|
||||
#define PROPDEBUG 512
|
||||
#define ANYDEBUG 1024
|
45
gprof/hertz.c
Normal file
45
gprof/hertz.c
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)hertz.c 5.4 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
/*
|
||||
* discover the tick frequency of the machine
|
||||
* if something goes wrong, we return 0, an impossible hertz.
|
||||
*/
|
||||
#define HZ_WRONG 0
|
||||
|
||||
hertz()
|
||||
{
|
||||
struct itimerval tim;
|
||||
|
||||
tim.it_interval.tv_sec = 0;
|
||||
tim.it_interval.tv_usec = 1;
|
||||
tim.it_value.tv_sec = 0;
|
||||
tim.it_value.tv_usec = 0;
|
||||
setitimer(ITIMER_REAL, &tim, 0);
|
||||
setitimer(ITIMER_REAL, 0, &tim);
|
||||
if (tim.it_interval.tv_usec < 2)
|
||||
return(HZ_WRONG);
|
||||
return (1000000 / tim.it_interval.tv_usec);
|
||||
}
|
96
gprof/lookup.c
Normal file
96
gprof/lookup.c
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)lookup.c 5.4 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* look up an address in a sorted-by-address namelist
|
||||
* this deals with misses by mapping them to the next lower
|
||||
* entry point.
|
||||
*/
|
||||
nltype *
|
||||
nllookup( address )
|
||||
unsigned long address;
|
||||
{
|
||||
register long low;
|
||||
register long middle;
|
||||
register long high;
|
||||
# ifdef DEBUG
|
||||
register int probes;
|
||||
|
||||
probes = 0;
|
||||
# endif DEBUG
|
||||
for ( low = 0 , high = nname - 1 ; low != high ; ) {
|
||||
# ifdef DEBUG
|
||||
probes += 1;
|
||||
# endif DEBUG
|
||||
middle = ( high + low ) >> 1;
|
||||
if ( nl[ middle ].value <= address && nl[ middle+1 ].value > address ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & LOOKUPDEBUG ) {
|
||||
printf( "[nllookup] %d (%d) probes\n" , probes , nname-1 );
|
||||
}
|
||||
# endif DEBUG
|
||||
return &nl[ middle ];
|
||||
}
|
||||
if ( nl[ middle ].value > address ) {
|
||||
high = middle;
|
||||
} else {
|
||||
low = middle + 1;
|
||||
}
|
||||
}
|
||||
fprintf( stderr , "[nllookup] binary search fails???\n" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
arctype *
|
||||
arclookup( parentp , childp )
|
||||
nltype *parentp;
|
||||
nltype *childp;
|
||||
{
|
||||
arctype *arcp;
|
||||
|
||||
if ( parentp == 0 || childp == 0 ) {
|
||||
fprintf( "[arclookup] parentp == 0 || childp == 0\n" );
|
||||
return 0;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & LOOKUPDEBUG ) {
|
||||
printf( "[arclookup] parent %s child %s\n" ,
|
||||
parentp -> name , childp -> name );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & LOOKUPDEBUG ) {
|
||||
printf( "[arclookup]\t arc_parent %s arc_child %s\n" ,
|
||||
arcp -> arc_parentp -> name ,
|
||||
arcp -> arc_childp -> name );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( arcp -> arc_childp == childp ) {
|
||||
return arcp;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
24
gprof/pathnames.h
Executable file
24
gprof/pathnames.h
Executable file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) 1989 The Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#)pathnames.h 5.2 (Berkeley) 6/1/90
|
||||
*/
|
||||
|
||||
#define _PATH_FLAT_BLURB "/usr/share/misc/gprof.flat"
|
||||
#define _PATH_CALLG_BLURB "/usr/share/misc/gprof.callg"
|
||||
|
704
gprof/printgprof.c
Normal file
704
gprof/printgprof.c
Normal file
@ -0,0 +1,704 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)printgprof.c 5.7 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
#include "pathnames.h"
|
||||
|
||||
printprof()
|
||||
{
|
||||
register nltype *np;
|
||||
nltype **sortednlp;
|
||||
int index, timecmp();
|
||||
|
||||
actime = 0.0;
|
||||
printf( "\f\n" );
|
||||
flatprofheader();
|
||||
/*
|
||||
* Sort the symbol table in by time
|
||||
*/
|
||||
sortednlp = (nltype **) calloc( nname , sizeof(nltype *) );
|
||||
if ( sortednlp == (nltype **) 0 ) {
|
||||
fprintf( stderr , "[printprof] ran out of memory for time sorting\n" );
|
||||
}
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
sortednlp[ index ] = &nl[ index ];
|
||||
}
|
||||
qsort( sortednlp , nname , sizeof(nltype *) , timecmp );
|
||||
for ( index = 0 ; index < nname ; index += 1 ) {
|
||||
np = sortednlp[ index ];
|
||||
flatprofline( np );
|
||||
}
|
||||
actime = 0.0;
|
||||
cfree( sortednlp );
|
||||
}
|
||||
|
||||
timecmp( npp1 , npp2 )
|
||||
nltype **npp1, **npp2;
|
||||
{
|
||||
double timediff;
|
||||
long calldiff;
|
||||
|
||||
timediff = (*npp2) -> time - (*npp1) -> time;
|
||||
if ( timediff > 0.0 )
|
||||
return 1 ;
|
||||
if ( timediff < 0.0 )
|
||||
return -1;
|
||||
calldiff = (*npp2) -> ncall - (*npp1) -> ncall;
|
||||
if ( calldiff > 0 )
|
||||
return 1;
|
||||
if ( calldiff < 0 )
|
||||
return -1;
|
||||
return( strcmp( (*npp1) -> name , (*npp2) -> name ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* header for flatprofline
|
||||
*/
|
||||
flatprofheader()
|
||||
{
|
||||
|
||||
if ( bflag ) {
|
||||
printblurb( _PATH_FLAT_BLURB );
|
||||
}
|
||||
printf( "\ngranularity: each sample hit covers %d byte(s)" ,
|
||||
(long) scale * sizeof(UNIT) );
|
||||
if ( totime > 0.0 ) {
|
||||
printf( " for %.2f%% of %.2f seconds\n\n" ,
|
||||
100.0/totime , totime / hz );
|
||||
} else {
|
||||
printf( " no time accumulated\n\n" );
|
||||
/*
|
||||
* this doesn't hurt sinc eall the numerators will be zero.
|
||||
*/
|
||||
totime = 1.0;
|
||||
}
|
||||
printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n" ,
|
||||
"% " , "cumulative" , "self " , "" , "self " , "total " , "" );
|
||||
printf( "%5.5s %10.10s %8.8s %8.8s %8.8s %8.8s %-8.8s\n" ,
|
||||
"time" , "seconds " , "seconds" , "calls" ,
|
||||
"ms/call" , "ms/call" , "name" );
|
||||
}
|
||||
|
||||
flatprofline( np )
|
||||
register nltype *np;
|
||||
{
|
||||
|
||||
if ( zflag == 0 && np -> ncall == 0 && np -> time == 0 ) {
|
||||
return;
|
||||
}
|
||||
actime += np -> time;
|
||||
printf( "%5.1f %10.2f %8.2f" ,
|
||||
100 * np -> time / totime , actime / hz , np -> time / hz );
|
||||
if ( np -> ncall != 0 ) {
|
||||
printf( " %8d %8.2f %8.2f " , np -> ncall ,
|
||||
1000 * np -> time / hz / np -> ncall ,
|
||||
1000 * ( np -> time + np -> childtime ) / hz / np -> ncall );
|
||||
} else {
|
||||
printf( " %8.8s %8.8s %8.8s " , "" , "" , "" );
|
||||
}
|
||||
printname( np );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
gprofheader()
|
||||
{
|
||||
|
||||
if ( bflag ) {
|
||||
printblurb( _PATH_CALLG_BLURB );
|
||||
}
|
||||
printf( "\ngranularity: each sample hit covers %d byte(s)" ,
|
||||
(long) scale * sizeof(UNIT) );
|
||||
if ( printtime > 0.0 ) {
|
||||
printf( " for %.2f%% of %.2f seconds\n\n" ,
|
||||
100.0/printtime , printtime / hz );
|
||||
} else {
|
||||
printf( " no time propagated\n\n" );
|
||||
/*
|
||||
* this doesn't hurt, since all the numerators will be 0.0
|
||||
*/
|
||||
printtime = 1.0;
|
||||
}
|
||||
printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n" ,
|
||||
"" , "" , "" , "" , "called" , "total" , "parents");
|
||||
printf( "%-6.6s %5.5s %7.7s %11.11s %7.7s+%-7.7s %-8.8s\t%5.5s\n" ,
|
||||
"index" , "%time" , "self" , "descendents" ,
|
||||
"called" , "self" , "name" , "index" );
|
||||
printf( "%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n" ,
|
||||
"" , "" , "" , "" , "called" , "total" , "children");
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
gprofline( np )
|
||||
register nltype *np;
|
||||
{
|
||||
char kirkbuffer[ BUFSIZ ];
|
||||
|
||||
sprintf( kirkbuffer , "[%d]" , np -> index );
|
||||
printf( "%-6.6s %5.1f %7.2f %11.2f" ,
|
||||
kirkbuffer ,
|
||||
100 * ( np -> propself + np -> propchild ) / printtime ,
|
||||
np -> propself / hz ,
|
||||
np -> propchild / hz );
|
||||
if ( ( np -> ncall + np -> selfcalls ) != 0 ) {
|
||||
printf( " %7d" , np -> ncall );
|
||||
if ( np -> selfcalls != 0 ) {
|
||||
printf( "+%-7d " , np -> selfcalls );
|
||||
} else {
|
||||
printf( " %7.7s " , "" );
|
||||
}
|
||||
} else {
|
||||
printf( " %7.7s %7.7s " , "" , "" );
|
||||
}
|
||||
printname( np );
|
||||
printf( "\n" );
|
||||
}
|
||||
|
||||
printgprof(timesortnlp)
|
||||
nltype **timesortnlp;
|
||||
{
|
||||
int index;
|
||||
nltype *parentp;
|
||||
|
||||
/*
|
||||
* Print out the structured profiling list
|
||||
*/
|
||||
gprofheader();
|
||||
for ( index = 0 ; index < nname + ncycle ; index ++ ) {
|
||||
parentp = timesortnlp[ index ];
|
||||
if ( zflag == 0 &&
|
||||
parentp -> ncall == 0 &&
|
||||
parentp -> selfcalls == 0 &&
|
||||
parentp -> propself == 0 &&
|
||||
parentp -> propchild == 0 ) {
|
||||
continue;
|
||||
}
|
||||
if ( ! parentp -> printflag ) {
|
||||
continue;
|
||||
}
|
||||
if ( parentp -> name == 0 && parentp -> cycleno != 0 ) {
|
||||
/*
|
||||
* cycle header
|
||||
*/
|
||||
printcycle( parentp );
|
||||
printmembers( parentp );
|
||||
} else {
|
||||
printparents( parentp );
|
||||
gprofline( parentp );
|
||||
printchildren( parentp );
|
||||
}
|
||||
printf( "\n" );
|
||||
printf( "-----------------------------------------------\n" );
|
||||
printf( "\n" );
|
||||
}
|
||||
cfree( timesortnlp );
|
||||
}
|
||||
|
||||
/*
|
||||
* sort by decreasing propagated time
|
||||
* if times are equal, but one is a cycle header,
|
||||
* say that's first (e.g. less, i.e. -1).
|
||||
* if one's name doesn't have an underscore and the other does,
|
||||
* say the one is first.
|
||||
* all else being equal, sort by names.
|
||||
*/
|
||||
int
|
||||
totalcmp( npp1 , npp2 )
|
||||
nltype **npp1;
|
||||
nltype **npp2;
|
||||
{
|
||||
register nltype *np1 = *npp1;
|
||||
register nltype *np2 = *npp2;
|
||||
double diff;
|
||||
|
||||
diff = ( np1 -> propself + np1 -> propchild )
|
||||
- ( np2 -> propself + np2 -> propchild );
|
||||
if ( diff < 0.0 )
|
||||
return 1;
|
||||
if ( diff > 0.0 )
|
||||
return -1;
|
||||
if ( np1 -> name == 0 && np1 -> cycleno != 0 )
|
||||
return -1;
|
||||
if ( np2 -> name == 0 && np2 -> cycleno != 0 )
|
||||
return 1;
|
||||
if ( np1 -> name == 0 )
|
||||
return -1;
|
||||
if ( np2 -> name == 0 )
|
||||
return 1;
|
||||
if ( *(np1 -> name) != '_' && *(np2 -> name) == '_' )
|
||||
return -1;
|
||||
if ( *(np1 -> name) == '_' && *(np2 -> name) != '_' )
|
||||
return 1;
|
||||
if ( np1 -> ncall > np2 -> ncall )
|
||||
return -1;
|
||||
if ( np1 -> ncall < np2 -> ncall )
|
||||
return 1;
|
||||
return strcmp( np1 -> name , np2 -> name );
|
||||
}
|
||||
|
||||
printparents( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
nltype *parentp;
|
||||
arctype *arcp;
|
||||
nltype *cycleheadp;
|
||||
|
||||
if ( childp -> cyclehead != 0 ) {
|
||||
cycleheadp = childp -> cyclehead;
|
||||
} else {
|
||||
cycleheadp = childp;
|
||||
}
|
||||
if ( childp -> parents == 0 ) {
|
||||
printf( "%6.6s %5.5s %7.7s %11.11s %7.7s %7.7s <spontaneous>\n" ,
|
||||
"" , "" , "" , "" , "" , "" );
|
||||
return;
|
||||
}
|
||||
sortparents( childp );
|
||||
for ( arcp = childp -> parents ; arcp ; arcp = arcp -> arc_parentlist ) {
|
||||
parentp = arcp -> arc_parentp;
|
||||
if ( childp == parentp ||
|
||||
( childp->cycleno != 0 && parentp->cycleno == childp->cycleno ) ) {
|
||||
/*
|
||||
* selfcall or call among siblings
|
||||
*/
|
||||
printf( "%6.6s %5.5s %7.7s %11.11s %7d %7.7s " ,
|
||||
"" , "" , "" , "" ,
|
||||
arcp -> arc_count , "" );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
} else {
|
||||
/*
|
||||
* regular parent of child
|
||||
*/
|
||||
printf( "%6.6s %5.5s %7.2f %11.2f %7d/%-7d " ,
|
||||
"" , "" ,
|
||||
arcp -> arc_time / hz , arcp -> arc_childtime / hz ,
|
||||
arcp -> arc_count , cycleheadp -> ncall );
|
||||
printname( parentp );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printchildren( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
nltype *childp;
|
||||
arctype *arcp;
|
||||
|
||||
sortchildren( parentp );
|
||||
arcp = parentp -> children;
|
||||
for ( arcp = parentp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
|
||||
childp = arcp -> arc_childp;
|
||||
if ( childp == parentp ||
|
||||
( childp->cycleno != 0 && childp->cycleno == parentp->cycleno ) ) {
|
||||
/*
|
||||
* self call or call to sibling
|
||||
*/
|
||||
printf( "%6.6s %5.5s %7.7s %11.11s %7d %7.7s " ,
|
||||
"" , "" , "" , "" , arcp -> arc_count , "" );
|
||||
printname( childp );
|
||||
printf( "\n" );
|
||||
} else {
|
||||
/*
|
||||
* regular child of parent
|
||||
*/
|
||||
printf( "%6.6s %5.5s %7.2f %11.2f %7d/%-7d " ,
|
||||
"" , "" ,
|
||||
arcp -> arc_time / hz , arcp -> arc_childtime / hz ,
|
||||
arcp -> arc_count , childp -> cyclehead -> ncall );
|
||||
printname( childp );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printname( selfp )
|
||||
nltype *selfp;
|
||||
{
|
||||
|
||||
if ( selfp -> name != 0 ) {
|
||||
printf( "%s" , selfp -> name );
|
||||
# ifdef DEBUG
|
||||
if ( debug & DFNDEBUG ) {
|
||||
printf( "{%d} " , selfp -> toporder );
|
||||
}
|
||||
if ( debug & PROPDEBUG ) {
|
||||
printf( "%5.2f%% " , selfp -> propfraction );
|
||||
}
|
||||
# endif DEBUG
|
||||
}
|
||||
if ( selfp -> cycleno != 0 ) {
|
||||
printf( " <cycle %d>" , selfp -> cycleno );
|
||||
}
|
||||
if ( selfp -> index != 0 ) {
|
||||
if ( selfp -> printflag ) {
|
||||
printf( " [%d]" , selfp -> index );
|
||||
} else {
|
||||
printf( " (%d)" , selfp -> index );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sortchildren( parentp )
|
||||
nltype *parentp;
|
||||
{
|
||||
arctype *arcp;
|
||||
arctype *detachedp;
|
||||
arctype sorted;
|
||||
arctype *prevp;
|
||||
|
||||
/*
|
||||
* unlink children from parent,
|
||||
* then insertion sort back on to sorted's children.
|
||||
* *arcp the arc you have detached and are inserting.
|
||||
* *detachedp the rest of the arcs to be sorted.
|
||||
* sorted arc list onto which you insertion sort.
|
||||
* *prevp arc before the arc you are comparing.
|
||||
*/
|
||||
sorted.arc_childlist = 0;
|
||||
for ( (arcp = parentp -> children)&&(detachedp = arcp -> arc_childlist);
|
||||
arcp ;
|
||||
(arcp = detachedp)&&(detachedp = detachedp -> arc_childlist)) {
|
||||
/*
|
||||
* consider *arcp as disconnected
|
||||
* insert it into sorted
|
||||
*/
|
||||
for ( prevp = &sorted ;
|
||||
prevp -> arc_childlist ;
|
||||
prevp = prevp -> arc_childlist ) {
|
||||
if ( arccmp( arcp , prevp -> arc_childlist ) != LESSTHAN ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
arcp -> arc_childlist = prevp -> arc_childlist;
|
||||
prevp -> arc_childlist = arcp;
|
||||
}
|
||||
/*
|
||||
* reattach sorted children to parent
|
||||
*/
|
||||
parentp -> children = sorted.arc_childlist;
|
||||
}
|
||||
|
||||
sortparents( childp )
|
||||
nltype *childp;
|
||||
{
|
||||
arctype *arcp;
|
||||
arctype *detachedp;
|
||||
arctype sorted;
|
||||
arctype *prevp;
|
||||
|
||||
/*
|
||||
* unlink parents from child,
|
||||
* then insertion sort back on to sorted's parents.
|
||||
* *arcp the arc you have detached and are inserting.
|
||||
* *detachedp the rest of the arcs to be sorted.
|
||||
* sorted arc list onto which you insertion sort.
|
||||
* *prevp arc before the arc you are comparing.
|
||||
*/
|
||||
sorted.arc_parentlist = 0;
|
||||
for ( (arcp = childp -> parents)&&(detachedp = arcp -> arc_parentlist);
|
||||
arcp ;
|
||||
(arcp = detachedp)&&(detachedp = detachedp -> arc_parentlist)) {
|
||||
/*
|
||||
* consider *arcp as disconnected
|
||||
* insert it into sorted
|
||||
*/
|
||||
for ( prevp = &sorted ;
|
||||
prevp -> arc_parentlist ;
|
||||
prevp = prevp -> arc_parentlist ) {
|
||||
if ( arccmp( arcp , prevp -> arc_parentlist ) != GREATERTHAN ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
arcp -> arc_parentlist = prevp -> arc_parentlist;
|
||||
prevp -> arc_parentlist = arcp;
|
||||
}
|
||||
/*
|
||||
* reattach sorted arcs to child
|
||||
*/
|
||||
childp -> parents = sorted.arc_parentlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* print a cycle header
|
||||
*/
|
||||
printcycle( cyclep )
|
||||
nltype *cyclep;
|
||||
{
|
||||
char kirkbuffer[ BUFSIZ ];
|
||||
|
||||
sprintf( kirkbuffer , "[%d]" , cyclep -> index );
|
||||
printf( "%-6.6s %5.1f %7.2f %11.2f %7d" ,
|
||||
kirkbuffer ,
|
||||
100 * ( cyclep -> propself + cyclep -> propchild ) / printtime ,
|
||||
cyclep -> propself / hz ,
|
||||
cyclep -> propchild / hz ,
|
||||
cyclep -> ncall );
|
||||
if ( cyclep -> selfcalls != 0 ) {
|
||||
printf( "+%-7d" , cyclep -> selfcalls );
|
||||
} else {
|
||||
printf( " %7.7s" , "" );
|
||||
}
|
||||
printf( " <cycle %d as a whole>\t[%d]\n" ,
|
||||
cyclep -> cycleno , cyclep -> index );
|
||||
}
|
||||
|
||||
/*
|
||||
* print the members of a cycle
|
||||
*/
|
||||
printmembers( cyclep )
|
||||
nltype *cyclep;
|
||||
{
|
||||
nltype *memberp;
|
||||
|
||||
sortmembers( cyclep );
|
||||
for ( memberp = cyclep -> cnext ; memberp ; memberp = memberp -> cnext ) {
|
||||
printf( "%6.6s %5.5s %7.2f %11.2f %7d" ,
|
||||
"" , "" , memberp -> propself / hz , memberp -> propchild / hz ,
|
||||
memberp -> ncall );
|
||||
if ( memberp -> selfcalls != 0 ) {
|
||||
printf( "+%-7d" , memberp -> selfcalls );
|
||||
} else {
|
||||
printf( " %7.7s" , "" );
|
||||
}
|
||||
printf( " " );
|
||||
printname( memberp );
|
||||
printf( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sort members of a cycle
|
||||
*/
|
||||
sortmembers( cyclep )
|
||||
nltype *cyclep;
|
||||
{
|
||||
nltype *todo;
|
||||
nltype *doing;
|
||||
nltype *prev;
|
||||
|
||||
/*
|
||||
* detach cycle members from cyclehead,
|
||||
* and insertion sort them back on.
|
||||
*/
|
||||
todo = cyclep -> cnext;
|
||||
cyclep -> cnext = 0;
|
||||
for ( (doing = todo)&&(todo = doing -> cnext);
|
||||
doing ;
|
||||
(doing = todo )&&(todo = doing -> cnext )){
|
||||
for ( prev = cyclep ; prev -> cnext ; prev = prev -> cnext ) {
|
||||
if ( membercmp( doing , prev -> cnext ) == GREATERTHAN ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
doing -> cnext = prev -> cnext;
|
||||
prev -> cnext = doing;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* major sort is on propself + propchild,
|
||||
* next is sort on ncalls + selfcalls.
|
||||
*/
|
||||
int
|
||||
membercmp( this , that )
|
||||
nltype *this;
|
||||
nltype *that;
|
||||
{
|
||||
double thistime = this -> propself + this -> propchild;
|
||||
double thattime = that -> propself + that -> propchild;
|
||||
long thiscalls = this -> ncall + this -> selfcalls;
|
||||
long thatcalls = that -> ncall + that -> selfcalls;
|
||||
|
||||
if ( thistime > thattime ) {
|
||||
return GREATERTHAN;
|
||||
}
|
||||
if ( thistime < thattime ) {
|
||||
return LESSTHAN;
|
||||
}
|
||||
if ( thiscalls > thatcalls ) {
|
||||
return GREATERTHAN;
|
||||
}
|
||||
if ( thiscalls < thatcalls ) {
|
||||
return LESSTHAN;
|
||||
}
|
||||
return EQUALTO;
|
||||
}
|
||||
/*
|
||||
* compare two arcs to/from the same child/parent.
|
||||
* - if one arc is a self arc, it's least.
|
||||
* - if one arc is within a cycle, it's less than.
|
||||
* - if both arcs are within a cycle, compare arc counts.
|
||||
* - if neither arc is within a cycle, compare with
|
||||
* arc_time + arc_childtime as major key
|
||||
* arc count as minor key
|
||||
*/
|
||||
int
|
||||
arccmp( thisp , thatp )
|
||||
arctype *thisp;
|
||||
arctype *thatp;
|
||||
{
|
||||
nltype *thisparentp = thisp -> arc_parentp;
|
||||
nltype *thischildp = thisp -> arc_childp;
|
||||
nltype *thatparentp = thatp -> arc_parentp;
|
||||
nltype *thatchildp = thatp -> arc_childp;
|
||||
double thistime;
|
||||
double thattime;
|
||||
|
||||
# ifdef DEBUG
|
||||
if ( debug & TIMEDEBUG ) {
|
||||
printf( "[arccmp] " );
|
||||
printname( thisparentp );
|
||||
printf( " calls " );
|
||||
printname ( thischildp );
|
||||
printf( " %f + %f %d/%d\n" ,
|
||||
thisp -> arc_time , thisp -> arc_childtime ,
|
||||
thisp -> arc_count , thischildp -> ncall );
|
||||
printf( "[arccmp] " );
|
||||
printname( thatparentp );
|
||||
printf( " calls " );
|
||||
printname( thatchildp );
|
||||
printf( " %f + %f %d/%d\n" ,
|
||||
thatp -> arc_time , thatp -> arc_childtime ,
|
||||
thatp -> arc_count , thatchildp -> ncall );
|
||||
printf( "\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( thisparentp == thischildp ) {
|
||||
/* this is a self call */
|
||||
return LESSTHAN;
|
||||
}
|
||||
if ( thatparentp == thatchildp ) {
|
||||
/* that is a self call */
|
||||
return GREATERTHAN;
|
||||
}
|
||||
if ( thisparentp -> cycleno != 0 && thischildp -> cycleno != 0 &&
|
||||
thisparentp -> cycleno == thischildp -> cycleno ) {
|
||||
/* this is a call within a cycle */
|
||||
if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 &&
|
||||
thatparentp -> cycleno == thatchildp -> cycleno ) {
|
||||
/* that is a call within the cycle, too */
|
||||
if ( thisp -> arc_count < thatp -> arc_count ) {
|
||||
return LESSTHAN;
|
||||
}
|
||||
if ( thisp -> arc_count > thatp -> arc_count ) {
|
||||
return GREATERTHAN;
|
||||
}
|
||||
return EQUALTO;
|
||||
} else {
|
||||
/* that isn't a call within the cycle */
|
||||
return LESSTHAN;
|
||||
}
|
||||
} else {
|
||||
/* this isn't a call within a cycle */
|
||||
if ( thatparentp -> cycleno != 0 && thatchildp -> cycleno != 0 &&
|
||||
thatparentp -> cycleno == thatchildp -> cycleno ) {
|
||||
/* that is a call within a cycle */
|
||||
return GREATERTHAN;
|
||||
} else {
|
||||
/* neither is a call within a cycle */
|
||||
thistime = thisp -> arc_time + thisp -> arc_childtime;
|
||||
thattime = thatp -> arc_time + thatp -> arc_childtime;
|
||||
if ( thistime < thattime )
|
||||
return LESSTHAN;
|
||||
if ( thistime > thattime )
|
||||
return GREATERTHAN;
|
||||
if ( thisp -> arc_count < thatp -> arc_count )
|
||||
return LESSTHAN;
|
||||
if ( thisp -> arc_count > thatp -> arc_count )
|
||||
return GREATERTHAN;
|
||||
return EQUALTO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printblurb( blurbname )
|
||||
char *blurbname;
|
||||
{
|
||||
FILE *blurbfile;
|
||||
int input;
|
||||
|
||||
blurbfile = fopen( blurbname , "r" );
|
||||
if ( blurbfile == NULL ) {
|
||||
perror( blurbname );
|
||||
return;
|
||||
}
|
||||
while ( ( input = getc( blurbfile ) ) != EOF ) {
|
||||
putchar( input );
|
||||
}
|
||||
fclose( blurbfile );
|
||||
}
|
||||
|
||||
int
|
||||
namecmp( npp1 , npp2 )
|
||||
nltype **npp1, **npp2;
|
||||
{
|
||||
return( strcmp( (*npp1) -> name , (*npp2) -> name ) );
|
||||
}
|
||||
|
||||
printindex()
|
||||
{
|
||||
nltype **namesortnlp;
|
||||
register nltype *nlp;
|
||||
int index, nnames, todo, i, j;
|
||||
char peterbuffer[ BUFSIZ ];
|
||||
|
||||
/*
|
||||
* Now, sort regular function name alphbetically
|
||||
* to create an index.
|
||||
*/
|
||||
namesortnlp = (nltype **) calloc( nname + ncycle , sizeof(nltype *) );
|
||||
if ( namesortnlp == (nltype **) 0 ) {
|
||||
fprintf( stderr , "%s: ran out of memory for sorting\n" , whoami );
|
||||
}
|
||||
for ( index = 0 , nnames = 0 ; index < nname ; index++ ) {
|
||||
if ( zflag == 0 && nl[index].ncall == 0 && nl[index].time == 0 )
|
||||
continue;
|
||||
namesortnlp[nnames++] = &nl[index];
|
||||
}
|
||||
qsort( namesortnlp , nnames , sizeof(nltype *) , namecmp );
|
||||
for ( index = 1 , todo = nnames ; index <= ncycle ; index++ ) {
|
||||
namesortnlp[todo++] = &cyclenl[index];
|
||||
}
|
||||
printf( "\f\nIndex by function name\n\n" );
|
||||
index = ( todo + 2 ) / 3;
|
||||
for ( i = 0; i < index ; i++ ) {
|
||||
for ( j = i; j < todo ; j += index ) {
|
||||
nlp = namesortnlp[ j ];
|
||||
if ( nlp -> printflag ) {
|
||||
sprintf( peterbuffer , "[%d]" , nlp -> index );
|
||||
} else {
|
||||
sprintf( peterbuffer , "(%d)" , nlp -> index );
|
||||
}
|
||||
if ( j < nnames ) {
|
||||
printf( "%6.6s %-19.19s" , peterbuffer , nlp -> name );
|
||||
} else {
|
||||
printf( "%6.6s " , peterbuffer );
|
||||
sprintf( peterbuffer , "<cycle %d>" , nlp -> cycleno );
|
||||
printf( "%-19.19s" , peterbuffer );
|
||||
}
|
||||
}
|
||||
printf( "\n" );
|
||||
}
|
||||
cfree( namesortnlp );
|
||||
}
|
77
gprof/printlist.c
Normal file
77
gprof/printlist.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)printlist.c 5.5 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* these are the lists of names:
|
||||
* there is the list head and then the listname
|
||||
* is a pointer to the list head
|
||||
* (for ease of passing to stringlist functions).
|
||||
*/
|
||||
struct stringlist kfromhead = { 0 , 0 };
|
||||
struct stringlist *kfromlist = &kfromhead;
|
||||
struct stringlist ktohead = { 0 , 0 };
|
||||
struct stringlist *ktolist = &ktohead;
|
||||
struct stringlist fhead = { 0 , 0 };
|
||||
struct stringlist *flist = &fhead;
|
||||
struct stringlist Fhead = { 0 , 0 };
|
||||
struct stringlist *Flist = &Fhead;
|
||||
struct stringlist ehead = { 0 , 0 };
|
||||
struct stringlist *elist = &ehead;
|
||||
struct stringlist Ehead = { 0 , 0 };
|
||||
struct stringlist *Elist = &Ehead;
|
||||
|
||||
addlist( listp , funcname )
|
||||
struct stringlist *listp;
|
||||
char *funcname;
|
||||
{
|
||||
struct stringlist *slp;
|
||||
|
||||
slp = (struct stringlist *) malloc( sizeof(struct stringlist));
|
||||
if ( slp == (struct stringlist *) 0 ) {
|
||||
fprintf( stderr, "gprof: ran out room for printlist\n" );
|
||||
done();
|
||||
}
|
||||
slp -> next = listp -> next;
|
||||
slp -> string = funcname;
|
||||
listp -> next = slp;
|
||||
}
|
||||
|
||||
bool
|
||||
onlist( listp , funcname )
|
||||
struct stringlist *listp;
|
||||
char *funcname;
|
||||
{
|
||||
struct stringlist *slp;
|
||||
|
||||
for ( slp = listp -> next ; slp ; slp = slp -> next ) {
|
||||
if ( ! strcmp( slp -> string , funcname ) ) {
|
||||
return TRUE;
|
||||
}
|
||||
if ( funcname[0] == '_' && ! strcmp( slp -> string , &funcname[1] ) ) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
131
gprof/sparc.c
Normal file
131
gprof/sparc.c
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)tahoe.c 1.5 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* a namelist entry to be the child of indirect callf
|
||||
*/
|
||||
nltype indirectchild = {
|
||||
"(*)" , /* the name */
|
||||
(unsigned long) 0 , /* the pc entry point */
|
||||
(unsigned long) 0 , /* entry point aligned to histogram */
|
||||
(double) 0.0 , /* ticks in this routine */
|
||||
(double) 0.0 , /* cumulative ticks in children */
|
||||
(long) 0 , /* how many times called */
|
||||
(long) 0 , /* how many calls to self */
|
||||
(double) 1.0 , /* propagation fraction */
|
||||
(double) 0.0 , /* self propagation time */
|
||||
(double) 0.0 , /* child propagation time */
|
||||
(bool) 0 , /* print flag */
|
||||
(int) 0 , /* index in the graph list */
|
||||
(int) 0 , /* graph call chain top-sort order */
|
||||
(int) 0 , /* internal number of cycle on */
|
||||
(struct nl *) &indirectchild , /* pointer to head of cycle */
|
||||
(struct nl *) 0 , /* pointer to next member of cycle */
|
||||
(arctype *) 0 , /* list of caller arcs */
|
||||
(arctype *) 0 /* list of callee arcs */
|
||||
};
|
||||
|
||||
findcall( parentp , p_lowpc , p_highpc )
|
||||
nltype *parentp;
|
||||
unsigned long p_lowpc;
|
||||
unsigned long p_highpc;
|
||||
{
|
||||
unsigned char *instructp;
|
||||
long length;
|
||||
nltype *childp;
|
||||
unsigned long destpc;
|
||||
|
||||
if ( textspace == 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( p_lowpc < s_lowpc ) {
|
||||
p_lowpc = s_lowpc;
|
||||
}
|
||||
if ( p_highpc > s_highpc ) {
|
||||
p_highpc = s_highpc;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall] %s: 0x%x to 0x%x\n" ,
|
||||
parentp -> name , p_lowpc , p_highpc );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( instructp = textspace + p_lowpc ;
|
||||
instructp < textspace + p_highpc ;
|
||||
instructp += length ) {
|
||||
length = 1;
|
||||
if ( (*instructp & CALL) ) {
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\t0x%x:callf" , instructp - textspace );
|
||||
}
|
||||
# endif DEBUG
|
||||
length += 4; /* constant length in a SPARC */
|
||||
/*
|
||||
* regular pc relative addressing
|
||||
* check that this is the address of
|
||||
* a function.
|
||||
*/
|
||||
destpc = ( (unsigned long)instructp + (*(long *)instructp & ~CALL) )
|
||||
- (unsigned long) textspace;
|
||||
if ( destpc >= s_lowpc && destpc <= s_highpc ) {
|
||||
childp = nllookup( destpc );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tdestpc 0x%x" , destpc );
|
||||
printf( " childp->name %s" , childp -> name );
|
||||
printf( " childp->value 0x%x\n" ,
|
||||
childp -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( childp -> value == destpc ) {
|
||||
/*
|
||||
* a hit
|
||||
*/
|
||||
addarc( parentp , childp , (long) 0 );
|
||||
length += 4; /* constant lengths */
|
||||
continue;
|
||||
}
|
||||
goto botched;
|
||||
}
|
||||
/*
|
||||
* else:
|
||||
* it looked like a callf,
|
||||
* but it wasn't to anywhere.
|
||||
*/
|
||||
botched:
|
||||
/*
|
||||
* something funny going on.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tbut it's a botch\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
length = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
32
gprof/sparc.h
Normal file
32
gprof/sparc.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#)tahoe.h 1.4 (Berkeley) 6/1/90
|
||||
*/
|
||||
|
||||
/*
|
||||
* opcode of the `callf' instruction
|
||||
*/
|
||||
#define CALL (0xc0000000)
|
||||
/*
|
||||
* offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see asgnsamples for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_OF_CODE 0
|
||||
#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
|
||||
|
12
gprof/t.c
Executable file
12
gprof/t.c
Executable file
@ -0,0 +1,12 @@
|
||||
void
|
||||
foo(int x) {
|
||||
if (x&3)
|
||||
foo (x-1);
|
||||
}
|
||||
|
||||
main() {
|
||||
int i;
|
||||
|
||||
for (i=0; i< 1024; i++)
|
||||
foo(i);
|
||||
}
|
335
gprof/tahoe.c
Normal file
335
gprof/tahoe.c
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)tahoe.c 1.5 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* a namelist entry to be the child of indirect callf
|
||||
*/
|
||||
nltype indirectchild = {
|
||||
"(*)" , /* the name */
|
||||
(unsigned long) 0 , /* the pc entry point */
|
||||
(unsigned long) 0 , /* entry point aligned to histogram */
|
||||
(double) 0.0 , /* ticks in this routine */
|
||||
(double) 0.0 , /* cumulative ticks in children */
|
||||
(long) 0 , /* how many times called */
|
||||
(long) 0 , /* how many calls to self */
|
||||
(double) 1.0 , /* propagation fraction */
|
||||
(double) 0.0 , /* self propagation time */
|
||||
(double) 0.0 , /* child propagation time */
|
||||
(bool) 0 , /* print flag */
|
||||
(int) 0 , /* index in the graph list */
|
||||
(int) 0 , /* graph call chain top-sort order */
|
||||
(int) 0 , /* internal number of cycle on */
|
||||
(struct nl *) &indirectchild , /* pointer to head of cycle */
|
||||
(struct nl *) 0 , /* pointer to next member of cycle */
|
||||
(arctype *) 0 , /* list of caller arcs */
|
||||
(arctype *) 0 /* list of callee arcs */
|
||||
};
|
||||
|
||||
operandenum
|
||||
operandmode( modep )
|
||||
unsigned char *modep;
|
||||
{
|
||||
long usesreg = ((long)*modep) & 0xf;
|
||||
|
||||
switch ( ((long)*modep) >> 4 ) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
return literal;
|
||||
case 4:
|
||||
return indexed;
|
||||
case 5:
|
||||
return reg;
|
||||
case 6:
|
||||
return regdef;
|
||||
case 7:
|
||||
return autodec;
|
||||
case 8:
|
||||
return ( usesreg != 0xe ? autoinc : immediate );
|
||||
case 9:
|
||||
return ( usesreg != PC ? autoincdef : absolute );
|
||||
case 10:
|
||||
return ( usesreg != PC ? bytedisp : byterel );
|
||||
case 11:
|
||||
return ( usesreg != PC ? bytedispdef : bytereldef );
|
||||
case 12:
|
||||
return ( usesreg != PC ? worddisp : wordrel );
|
||||
case 13:
|
||||
return ( usesreg != PC ? worddispdef : wordreldef );
|
||||
case 14:
|
||||
return ( usesreg != PC ? longdisp : longrel );
|
||||
case 15:
|
||||
return ( usesreg != PC ? longdispdef : longreldef );
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
char *
|
||||
operandname( mode )
|
||||
operandenum mode;
|
||||
{
|
||||
|
||||
switch ( mode ) {
|
||||
case literal:
|
||||
return "literal";
|
||||
case indexed:
|
||||
return "indexed";
|
||||
case reg:
|
||||
return "register";
|
||||
case regdef:
|
||||
return "register deferred";
|
||||
case autodec:
|
||||
return "autodecrement";
|
||||
case autoinc:
|
||||
return "autoincrement";
|
||||
case autoincdef:
|
||||
return "autoincrement deferred";
|
||||
case bytedisp:
|
||||
return "byte displacement";
|
||||
case bytedispdef:
|
||||
return "byte displacement deferred";
|
||||
case byterel:
|
||||
return "byte relative";
|
||||
case bytereldef:
|
||||
return "byte relative deferred";
|
||||
case worddisp:
|
||||
return "word displacement";
|
||||
case worddispdef:
|
||||
return "word displacement deferred";
|
||||
case wordrel:
|
||||
return "word relative";
|
||||
case wordreldef:
|
||||
return "word relative deferred";
|
||||
case immediate:
|
||||
return "immediate";
|
||||
case absolute:
|
||||
return "absolute";
|
||||
case longdisp:
|
||||
return "long displacement";
|
||||
case longdispdef:
|
||||
return "long displacement deferred";
|
||||
case longrel:
|
||||
return "long relative";
|
||||
case longreldef:
|
||||
return "long relative deferred";
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
long
|
||||
operandlength( modep )
|
||||
unsigned char *modep;
|
||||
{
|
||||
|
||||
switch ( operandmode( modep ) ) {
|
||||
case literal:
|
||||
case reg:
|
||||
case regdef:
|
||||
case autodec:
|
||||
case autoinc:
|
||||
case autoincdef:
|
||||
return 1;
|
||||
case bytedisp:
|
||||
case bytedispdef:
|
||||
case byterel:
|
||||
case bytereldef:
|
||||
return 2;
|
||||
case worddisp:
|
||||
case worddispdef:
|
||||
case wordrel:
|
||||
case wordreldef:
|
||||
return 3;
|
||||
case immediate:
|
||||
case absolute:
|
||||
case longdisp:
|
||||
case longdispdef:
|
||||
case longrel:
|
||||
case longreldef:
|
||||
return 5;
|
||||
case indexed:
|
||||
return 1+operandlength( modep + 1 );
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
unsigned long
|
||||
reladdr( modep )
|
||||
char *modep;
|
||||
{
|
||||
operandenum mode = operandmode( modep );
|
||||
char *cp;
|
||||
short *sp;
|
||||
long *lp;
|
||||
int i;
|
||||
long value = 0;
|
||||
|
||||
cp = modep;
|
||||
cp += 1; /* skip over the mode */
|
||||
switch ( mode ) {
|
||||
default:
|
||||
fprintf( stderr , "[reladdr] not relative address\n" );
|
||||
return (unsigned long) modep;
|
||||
case byterel:
|
||||
return (unsigned long) ( cp + sizeof *cp + *cp );
|
||||
case wordrel:
|
||||
for (i = 0; i < sizeof *sp; i++)
|
||||
value = (value << 8) + (cp[i] & 0xff);
|
||||
return (unsigned long) ( cp + sizeof *sp + value );
|
||||
case longrel:
|
||||
for (i = 0; i < sizeof *lp; i++)
|
||||
value = (value << 8) + (cp[i] & 0xff);
|
||||
return (unsigned long) ( cp + sizeof *lp + value );
|
||||
}
|
||||
}
|
||||
|
||||
findcall( parentp , p_lowpc , p_highpc )
|
||||
nltype *parentp;
|
||||
unsigned long p_lowpc;
|
||||
unsigned long p_highpc;
|
||||
{
|
||||
unsigned char *instructp;
|
||||
long length;
|
||||
nltype *childp;
|
||||
operandenum mode;
|
||||
operandenum firstmode;
|
||||
unsigned long destpc;
|
||||
|
||||
if ( textspace == 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( p_lowpc < s_lowpc ) {
|
||||
p_lowpc = s_lowpc;
|
||||
}
|
||||
if ( p_highpc > s_highpc ) {
|
||||
p_highpc = s_highpc;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall] %s: 0x%x to 0x%x\n" ,
|
||||
parentp -> name , p_lowpc , p_highpc );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( instructp = textspace + p_lowpc ;
|
||||
instructp < textspace + p_highpc ;
|
||||
instructp += length ) {
|
||||
length = 1;
|
||||
if ( *instructp == CALLF ) {
|
||||
/*
|
||||
* maybe a callf, better check it out.
|
||||
* skip the count of the number of arguments.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\t0x%x:callf" , instructp - textspace );
|
||||
}
|
||||
# endif DEBUG
|
||||
firstmode = operandmode( instructp+length );
|
||||
switch ( firstmode ) {
|
||||
case literal:
|
||||
case immediate:
|
||||
break;
|
||||
default:
|
||||
goto botched;
|
||||
}
|
||||
length += operandlength( instructp+length );
|
||||
mode = operandmode( instructp + length );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "\tfirst operand is %s", operandname( firstmode ) );
|
||||
printf( "\tsecond operand is %s\n" , operandname( mode ) );
|
||||
}
|
||||
# endif DEBUG
|
||||
switch ( mode ) {
|
||||
case regdef:
|
||||
case bytedispdef:
|
||||
case worddispdef:
|
||||
case longdispdef:
|
||||
case bytereldef:
|
||||
case wordreldef:
|
||||
case longreldef:
|
||||
/*
|
||||
* indirect call: call through pointer
|
||||
* either *d(r) as a parameter or local
|
||||
* (r) as a return value
|
||||
* *f as a global pointer
|
||||
* [are there others that we miss?,
|
||||
* e.g. arrays of pointers to functions???]
|
||||
*/
|
||||
addarc( parentp , &indirectchild , (long) 0 );
|
||||
length += operandlength( instructp + length );
|
||||
continue;
|
||||
case byterel:
|
||||
case wordrel:
|
||||
case longrel:
|
||||
/*
|
||||
* regular pc relative addressing
|
||||
* check that this is the address of
|
||||
* a function.
|
||||
*/
|
||||
destpc = reladdr( instructp+length )
|
||||
- (unsigned long) textspace;
|
||||
if ( destpc >= s_lowpc && destpc <= s_highpc ) {
|
||||
childp = nllookup( destpc );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tdestpc 0x%x" , destpc );
|
||||
printf( " childp->name %s" , childp -> name );
|
||||
printf( " childp->value 0x%x\n" ,
|
||||
childp -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( childp -> value == destpc ) {
|
||||
/*
|
||||
* a hit
|
||||
*/
|
||||
addarc( parentp , childp , (long) 0 );
|
||||
length += operandlength( instructp + length );
|
||||
continue;
|
||||
}
|
||||
goto botched;
|
||||
}
|
||||
/*
|
||||
* else:
|
||||
* it looked like a callf,
|
||||
* but it wasn't to anywhere.
|
||||
*/
|
||||
goto botched;
|
||||
default:
|
||||
botched:
|
||||
/*
|
||||
* something funny going on.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tbut it's a botch\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
length = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
gprof/tahoe.h
Normal file
45
gprof/tahoe.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#)tahoe.h 1.4 (Berkeley) 6/1/90
|
||||
*/
|
||||
|
||||
/*
|
||||
* opcode of the `callf' instruction
|
||||
*/
|
||||
#define CALLF 0xfe
|
||||
|
||||
/*
|
||||
* offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see asgnsamples for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_OF_CODE 2
|
||||
#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
|
||||
|
||||
/*
|
||||
* register for pc relative addressing
|
||||
*/
|
||||
#define PC 0xf
|
||||
|
||||
enum opermodes {
|
||||
literal, indexed, reg, regdef, autodec, autoinc, autoincdef,
|
||||
bytedisp, bytedispdef, worddisp, worddispdef, longdisp, longdispdef,
|
||||
immediate, absolute, byterel, bytereldef, wordrel, wordreldef,
|
||||
longrel, longreldef
|
||||
};
|
||||
typedef enum opermodes operandenum;
|
333
gprof/vax.c
Normal file
333
gprof/vax.c
Normal file
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static char sccsid[] = "@(#)vax.c 5.6 (Berkeley) 6/1/90";
|
||||
#endif /* not lint */
|
||||
|
||||
#include "gprof.h"
|
||||
|
||||
/*
|
||||
* a namelist entry to be the child of indirect calls
|
||||
*/
|
||||
nltype indirectchild = {
|
||||
"(*)" , /* the name */
|
||||
(unsigned long) 0 , /* the pc entry point */
|
||||
(unsigned long) 0 , /* entry point aligned to histogram */
|
||||
(double) 0.0 , /* ticks in this routine */
|
||||
(double) 0.0 , /* cumulative ticks in children */
|
||||
(long) 0 , /* how many times called */
|
||||
(long) 0 , /* how many calls to self */
|
||||
(double) 1.0 , /* propagation fraction */
|
||||
(double) 0.0 , /* self propagation time */
|
||||
(double) 0.0 , /* child propagation time */
|
||||
(bool) 0 , /* print flag */
|
||||
(int) 0 , /* index in the graph list */
|
||||
(int) 0 , /* graph call chain top-sort order */
|
||||
(int) 0 , /* internal number of cycle on */
|
||||
(struct nl *) &indirectchild , /* pointer to head of cycle */
|
||||
(struct nl *) 0 , /* pointer to next member of cycle */
|
||||
(arctype *) 0 , /* list of caller arcs */
|
||||
(arctype *) 0 /* list of callee arcs */
|
||||
};
|
||||
|
||||
operandenum
|
||||
operandmode( modep )
|
||||
struct modebyte *modep;
|
||||
{
|
||||
long usesreg = modep -> regfield;
|
||||
|
||||
switch ( modep -> modefield ) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
return literal;
|
||||
case 4:
|
||||
return indexed;
|
||||
case 5:
|
||||
return reg;
|
||||
case 6:
|
||||
return regdef;
|
||||
case 7:
|
||||
return autodec;
|
||||
case 8:
|
||||
return ( usesreg != PC ? autoinc : immediate );
|
||||
case 9:
|
||||
return ( usesreg != PC ? autoincdef : absolute );
|
||||
case 10:
|
||||
return ( usesreg != PC ? bytedisp : byterel );
|
||||
case 11:
|
||||
return ( usesreg != PC ? bytedispdef : bytereldef );
|
||||
case 12:
|
||||
return ( usesreg != PC ? worddisp : wordrel );
|
||||
case 13:
|
||||
return ( usesreg != PC ? worddispdef : wordreldef );
|
||||
case 14:
|
||||
return ( usesreg != PC ? longdisp : longrel );
|
||||
case 15:
|
||||
return ( usesreg != PC ? longdispdef : longreldef );
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
char *
|
||||
operandname( mode )
|
||||
operandenum mode;
|
||||
{
|
||||
|
||||
switch ( mode ) {
|
||||
case literal:
|
||||
return "literal";
|
||||
case indexed:
|
||||
return "indexed";
|
||||
case reg:
|
||||
return "register";
|
||||
case regdef:
|
||||
return "register deferred";
|
||||
case autodec:
|
||||
return "autodecrement";
|
||||
case autoinc:
|
||||
return "autoincrement";
|
||||
case autoincdef:
|
||||
return "autoincrement deferred";
|
||||
case bytedisp:
|
||||
return "byte displacement";
|
||||
case bytedispdef:
|
||||
return "byte displacement deferred";
|
||||
case byterel:
|
||||
return "byte relative";
|
||||
case bytereldef:
|
||||
return "byte relative deferred";
|
||||
case worddisp:
|
||||
return "word displacement";
|
||||
case worddispdef:
|
||||
return "word displacement deferred";
|
||||
case wordrel:
|
||||
return "word relative";
|
||||
case wordreldef:
|
||||
return "word relative deferred";
|
||||
case immediate:
|
||||
return "immediate";
|
||||
case absolute:
|
||||
return "absolute";
|
||||
case longdisp:
|
||||
return "long displacement";
|
||||
case longdispdef:
|
||||
return "long displacement deferred";
|
||||
case longrel:
|
||||
return "long relative";
|
||||
case longreldef:
|
||||
return "long relative deferred";
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
long
|
||||
operandlength( modep )
|
||||
struct modebyte *modep;
|
||||
{
|
||||
|
||||
switch ( operandmode( modep ) ) {
|
||||
case literal:
|
||||
case reg:
|
||||
case regdef:
|
||||
case autodec:
|
||||
case autoinc:
|
||||
case autoincdef:
|
||||
return 1;
|
||||
case bytedisp:
|
||||
case bytedispdef:
|
||||
case byterel:
|
||||
case bytereldef:
|
||||
return 2;
|
||||
case worddisp:
|
||||
case worddispdef:
|
||||
case wordrel:
|
||||
case wordreldef:
|
||||
return 3;
|
||||
case immediate:
|
||||
case absolute:
|
||||
case longdisp:
|
||||
case longdispdef:
|
||||
case longrel:
|
||||
case longreldef:
|
||||
return 5;
|
||||
case indexed:
|
||||
return 1+operandlength( (struct modebyte *) ((char *) modep) + 1 );
|
||||
}
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
unsigned long
|
||||
reladdr( modep )
|
||||
struct modebyte *modep;
|
||||
{
|
||||
operandenum mode = operandmode( modep );
|
||||
char *cp;
|
||||
short *sp;
|
||||
long *lp;
|
||||
|
||||
cp = (char *) modep;
|
||||
cp += 1; /* skip over the mode */
|
||||
switch ( mode ) {
|
||||
default:
|
||||
fprintf( stderr , "[reladdr] not relative address\n" );
|
||||
return (unsigned long) modep;
|
||||
case byterel:
|
||||
return (unsigned long) ( cp + sizeof *cp + *cp );
|
||||
case wordrel:
|
||||
sp = (short *) cp;
|
||||
return (unsigned long) ( cp + sizeof *sp + *sp );
|
||||
case longrel:
|
||||
lp = (long *) cp;
|
||||
return (unsigned long) ( cp + sizeof *lp + *lp );
|
||||
}
|
||||
}
|
||||
|
||||
findcall( parentp , p_lowpc , p_highpc )
|
||||
nltype *parentp;
|
||||
unsigned long p_lowpc;
|
||||
unsigned long p_highpc;
|
||||
{
|
||||
unsigned char *instructp;
|
||||
long length;
|
||||
nltype *childp;
|
||||
operandenum mode;
|
||||
operandenum firstmode;
|
||||
unsigned long destpc;
|
||||
|
||||
if ( textspace == 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( p_lowpc < s_lowpc ) {
|
||||
p_lowpc = s_lowpc;
|
||||
}
|
||||
if ( p_highpc > s_highpc ) {
|
||||
p_highpc = s_highpc;
|
||||
}
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall] %s: 0x%x to 0x%x\n" ,
|
||||
parentp -> name , p_lowpc , p_highpc );
|
||||
}
|
||||
# endif DEBUG
|
||||
for ( instructp = textspace + p_lowpc ;
|
||||
instructp < textspace + p_highpc ;
|
||||
instructp += length ) {
|
||||
length = 1;
|
||||
if ( *instructp == CALLS ) {
|
||||
/*
|
||||
* maybe a calls, better check it out.
|
||||
* skip the count of the number of arguments.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\t0x%x:calls" , instructp - textspace );
|
||||
}
|
||||
# endif DEBUG
|
||||
firstmode = operandmode( (struct modebyte *) (instructp+length) );
|
||||
switch ( firstmode ) {
|
||||
case literal:
|
||||
case immediate:
|
||||
break;
|
||||
default:
|
||||
goto botched;
|
||||
}
|
||||
length += operandlength( (struct modebyte *) (instructp+length) );
|
||||
mode = operandmode( (struct modebyte *) ( instructp + length ) );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "\tfirst operand is %s", operandname( firstmode ) );
|
||||
printf( "\tsecond operand is %s\n" , operandname( mode ) );
|
||||
}
|
||||
# endif DEBUG
|
||||
switch ( mode ) {
|
||||
case regdef:
|
||||
case bytedispdef:
|
||||
case worddispdef:
|
||||
case longdispdef:
|
||||
case bytereldef:
|
||||
case wordreldef:
|
||||
case longreldef:
|
||||
/*
|
||||
* indirect call: call through pointer
|
||||
* either *d(r) as a parameter or local
|
||||
* (r) as a return value
|
||||
* *f as a global pointer
|
||||
* [are there others that we miss?,
|
||||
* e.g. arrays of pointers to functions???]
|
||||
*/
|
||||
addarc( parentp , &indirectchild , (long) 0 );
|
||||
length += operandlength(
|
||||
(struct modebyte *) ( instructp + length ) );
|
||||
continue;
|
||||
case byterel:
|
||||
case wordrel:
|
||||
case longrel:
|
||||
/*
|
||||
* regular pc relative addressing
|
||||
* check that this is the address of
|
||||
* a function.
|
||||
*/
|
||||
destpc = reladdr( (struct modebyte *) (instructp+length) )
|
||||
- (unsigned long) textspace;
|
||||
if ( destpc >= s_lowpc && destpc <= s_highpc ) {
|
||||
childp = nllookup( destpc );
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tdestpc 0x%x" , destpc );
|
||||
printf( " childp->name %s" , childp -> name );
|
||||
printf( " childp->value 0x%x\n" ,
|
||||
childp -> value );
|
||||
}
|
||||
# endif DEBUG
|
||||
if ( childp -> value == destpc ) {
|
||||
/*
|
||||
* a hit
|
||||
*/
|
||||
addarc( parentp , childp , (long) 0 );
|
||||
length += operandlength( (struct modebyte *)
|
||||
( instructp + length ) );
|
||||
continue;
|
||||
}
|
||||
goto botched;
|
||||
}
|
||||
/*
|
||||
* else:
|
||||
* it looked like a calls,
|
||||
* but it wasn't to anywhere.
|
||||
*/
|
||||
goto botched;
|
||||
default:
|
||||
botched:
|
||||
/*
|
||||
* something funny going on.
|
||||
*/
|
||||
# ifdef DEBUG
|
||||
if ( debug & CALLDEBUG ) {
|
||||
printf( "[findcall]\tbut it's a botch\n" );
|
||||
}
|
||||
# endif DEBUG
|
||||
length = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
gprof/vax.h
Normal file
51
gprof/vax.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (c) 1983 Regents of the University of California.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms are permitted
|
||||
* provided that: (1) source distributions retain this entire copyright
|
||||
* notice and comment, and (2) distributions including binaries display
|
||||
* the following acknowledgement: ``This product includes software
|
||||
* developed by the University of California, Berkeley and its contributors''
|
||||
* in the documentation or other materials provided with the distribution
|
||||
* and in all advertising materials mentioning features or use of this
|
||||
* software. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* @(#)vax.h 5.4 (Berkeley) 6/1/90
|
||||
*/
|
||||
|
||||
/*
|
||||
* opcode of the `calls' instruction
|
||||
*/
|
||||
#define CALLS 0xfb
|
||||
|
||||
/*
|
||||
* offset (in bytes) of the code from the entry address of a routine.
|
||||
* (see asgnsamples for use and explanation.)
|
||||
*/
|
||||
#define OFFSET_OF_CODE 2
|
||||
#define UNITS_TO_CODE (OFFSET_OF_CODE / sizeof(UNIT))
|
||||
|
||||
/*
|
||||
* register for pc relative addressing
|
||||
*/
|
||||
#define PC 0xf
|
||||
|
||||
enum opermodes {
|
||||
literal, indexed, reg, regdef, autodec, autoinc, autoincdef,
|
||||
bytedisp, bytedispdef, worddisp, worddispdef, longdisp, longdispdef,
|
||||
immediate, absolute, byterel, bytereldef, wordrel, wordreldef,
|
||||
longrel, longreldef
|
||||
};
|
||||
typedef enum opermodes operandenum;
|
||||
|
||||
struct modebyte {
|
||||
unsigned int regfield:4;
|
||||
unsigned int modefield:4;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user