From 3d6c6501891ab37f334c209e065ad0e7a2cd5a74 Mon Sep 17 00:00:00 2001 From: Sean Eric Fagan Date: Thu, 18 Jul 1991 20:19:17 +0000 Subject: [PATCH] Initial revision --- gprof/Makefile | 23 ++ gprof/arcs.c | 567 ++++++++++++++++++++++++++++++++++++ gprof/dfn.c | 302 +++++++++++++++++++ gprof/gmon.h | 105 +++++++ gprof/gprof.1 | 264 +++++++++++++++++ gprof/gprof.c | 691 ++++++++++++++++++++++++++++++++++++++++++++ gprof/gprof.callg | 108 +++++++ gprof/gprof.flat | 32 +++ gprof/gprof.h | 284 ++++++++++++++++++ gprof/hertz.c | 45 +++ gprof/lookup.c | 96 +++++++ gprof/pathnames.h | 24 ++ gprof/printgprof.c | 704 +++++++++++++++++++++++++++++++++++++++++++++ gprof/printlist.c | 77 +++++ gprof/sparc.c | 131 +++++++++ gprof/sparc.h | 32 +++ gprof/t.c | 12 + gprof/tahoe.c | 335 +++++++++++++++++++++ gprof/tahoe.h | 45 +++ gprof/vax.c | 333 +++++++++++++++++++++ gprof/vax.h | 51 ++++ 21 files changed, 4261 insertions(+) create mode 100755 gprof/Makefile create mode 100644 gprof/arcs.c create mode 100644 gprof/dfn.c create mode 100644 gprof/gmon.h create mode 100644 gprof/gprof.1 create mode 100644 gprof/gprof.c create mode 100644 gprof/gprof.callg create mode 100644 gprof/gprof.flat create mode 100644 gprof/gprof.h create mode 100644 gprof/hertz.c create mode 100644 gprof/lookup.c create mode 100755 gprof/pathnames.h create mode 100644 gprof/printgprof.c create mode 100644 gprof/printlist.c create mode 100644 gprof/sparc.c create mode 100644 gprof/sparc.h create mode 100755 gprof/t.c create mode 100644 gprof/tahoe.c create mode 100644 gprof/tahoe.h create mode 100644 gprof/vax.c create mode 100644 gprof/vax.h diff --git a/gprof/Makefile b/gprof/Makefile new file mode 100755 index 00000000000..9c14d68eca1 --- /dev/null +++ b/gprof/Makefile @@ -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 +$(PROG): $(OBJS) + $(CC) $(CFLAGS) $(OBJS) -o $(PROG) $(LIBS) diff --git a/gprof/arcs.c b/gprof/arcs.c new file mode 100644 index 00000000000..8cb6dad518b --- /dev/null +++ b/gprof/arcs.c @@ -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; + } + } +} diff --git a/gprof/dfn.c b/gprof/dfn.c new file mode 100644 index 00000000000..1d464beb276 --- /dev/null +++ b/gprof/dfn.c @@ -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 +#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; +} diff --git a/gprof/gmon.h b/gprof/gmon.h new file mode 100644 index 00000000000..c89a721972b --- /dev/null +++ b/gprof/gmon.h @@ -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)) diff --git a/gprof/gprof.1 b/gprof/gprof.1 new file mode 100644 index 00000000000..286a312008a --- /dev/null +++ b/gprof/gprof.1 @@ -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. diff --git a/gprof/gprof.c b/gprof/gprof.c new file mode 100644 index 00000000000..c1f0d08d6ed --- /dev/null +++ b/gprof/gprof.c @@ -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 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 (ab) + return(a); + return(b); +} + + /* + * calculate scaled entry point addresses (to save time in asgnsamples), + * and possibly push the scaled entry points over the entry mask, + * if it turns out that the entry point is in one bucket and the code + * for a routine is in the next bucket. + */ +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); +} diff --git a/gprof/gprof.callg b/gprof/gprof.callg new file mode 100644 index 00000000000..533c96ca439 --- /dev/null +++ b/gprof/gprof.callg @@ -0,0 +1,108 @@ + + + +call graph profile: + The sum of self and descendents is the major sort + for this listing. + + function entries: + +index the index of the function in the call graph + listing, as an aid to locating it (see below). + +%time the percentage of the total time of the program + accounted for by this function and its + descendents. + +self the number of seconds spent in this function + itself. + +descendents + the number of seconds spent in the descendents of + this function on behalf of this function. + +called the number of times this function is called (other + than recursive calls). + +self the number of times this function calls itself + recursively. + +name the name of the function, with an indication of + its membership in a cycle, if any. + +index the index of the function in the call graph + listing, as an aid to locating it. + + + + parent listings: + +self* the number of seconds of this function's self time + which is due to calls from this parent. + +descendents* + the number of seconds of this function's + descendent time which is due to calls from this + parent. + +called** the number of times this function is called by + this parent. This is the numerator of the + fraction which divides up the function's time to + its parents. + +total* the number of times this function was called by + all of its parents. This is the denominator of + the propagation fraction. + +parents the name of this parent, with an indication of the + parent's membership in a cycle, if any. + +index the index of this parent in the call graph + listing, as an aid in locating it. + + + + children listings: + +self* the number of seconds of this child's self time + which is due to being called by this function. + +descendent* + the number of seconds of this child's descendent's + time which is due to being called by this + function. + +called** the number of times this child is called by this + function. This is the numerator of the + propagation fraction for this child. + +total* the number of times this child is called by all + functions. This is the denominator of the + propagation fraction. + +children the name of this child, and an indication of its + membership in a cycle, if any. + +index the index of this child in the call graph listing, + as an aid to locating it. + + + + * these fields are omitted for parents (or + children) in the same cycle as the function. If + the function (or child) is a member of a cycle, + the propagated times and propagation denominator + represent the self time and descendent time of the + cycle as a whole. + + ** static-only parents and children are indicated + by a call count of 0. + + + + cycle listings: + the cycle as a whole is listed with the same + fields as a function entry. Below it are listed + the members of the cycle, and their contributions + to the time and call counts of the cycle. + diff --git a/gprof/gprof.flat b/gprof/gprof.flat new file mode 100644 index 00000000000..60999a35f2f --- /dev/null +++ b/gprof/gprof.flat @@ -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. + diff --git a/gprof/gprof.h b/gprof/gprof.h new file mode 100644 index 00000000000..6edec28f98e --- /dev/null +++ b/gprof/gprof.h @@ -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 +#include +#include +#include +#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 diff --git a/gprof/hertz.c b/gprof/hertz.c new file mode 100644 index 00000000000..1c2927f673e --- /dev/null +++ b/gprof/hertz.c @@ -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 + + /* + * 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); +} diff --git a/gprof/lookup.c b/gprof/lookup.c new file mode 100644 index 00000000000..405f3205027 --- /dev/null +++ b/gprof/lookup.c @@ -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; +} diff --git a/gprof/pathnames.h b/gprof/pathnames.h new file mode 100755 index 00000000000..729325cbe82 --- /dev/null +++ b/gprof/pathnames.h @@ -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" + diff --git a/gprof/printgprof.c b/gprof/printgprof.c new file mode 100644 index 00000000000..ce788522bd9 --- /dev/null +++ b/gprof/printgprof.c @@ -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 \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( " " , 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( " \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 , "" , nlp -> cycleno ); + printf( "%-19.19s" , peterbuffer ); + } + } + printf( "\n" ); + } + cfree( namesortnlp ); +} diff --git a/gprof/printlist.c b/gprof/printlist.c new file mode 100644 index 00000000000..8e952f05d2b --- /dev/null +++ b/gprof/printlist.c @@ -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; +} diff --git a/gprof/sparc.c b/gprof/sparc.c new file mode 100644 index 00000000000..0bd64510279 --- /dev/null +++ b/gprof/sparc.c @@ -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; + } + } + } diff --git a/gprof/sparc.h b/gprof/sparc.h new file mode 100644 index 00000000000..0287b88fc69 --- /dev/null +++ b/gprof/sparc.h @@ -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)) + diff --git a/gprof/t.c b/gprof/t.c new file mode 100755 index 00000000000..62272b4b1fc --- /dev/null +++ b/gprof/t.c @@ -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); +} diff --git a/gprof/tahoe.c b/gprof/tahoe.c new file mode 100644 index 00000000000..924d95d06e1 --- /dev/null +++ b/gprof/tahoe.c @@ -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; + } + } + } +} diff --git a/gprof/tahoe.h b/gprof/tahoe.h new file mode 100644 index 00000000000..55c1d32e47e --- /dev/null +++ b/gprof/tahoe.h @@ -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; diff --git a/gprof/vax.c b/gprof/vax.c new file mode 100644 index 00000000000..220b7582ad1 --- /dev/null +++ b/gprof/vax.c @@ -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; + } + } + } +} diff --git a/gprof/vax.h b/gprof/vax.h new file mode 100644 index 00000000000..3e45167520e --- /dev/null +++ b/gprof/vax.h @@ -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; +}; + -- 2.30.2