cctools-port/cctools/gprof/calls.c
Thomas Pöchtrager 9d10513d86 merge cctools-855
2014-05-08 19:55:29 +02:00

482 lines
12 KiB
C

/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1983, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "gprof.h"
#ifdef vax
#include "vax.h"
#endif
#ifdef m68k
#include "m68k.h"
#endif
/*
* A namelist entry to be the child of indirect calls.
*/
static nltype indirectchild = {
"(*)" , /* the name */
(uint64_t) 0 , /* the pc entry point */
(uint64_t) 0 , /* entry point aligned to histogram */
(double) 0.0 , /* ticks in this routine */
(double) 0.0 , /* cumulative ticks in children */
(uint32_t) 0 , /* order called */
(int32_t) 0 , /* how many times called */
(int32_t) 0 , /* how many calls to self */
(double) 1.0 , /* propagation fraction */
(double) 0.0 , /* self propagation time */
(double) 0.0 , /* child propagation time */
(enum 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 */
};
#ifdef vax
operandenum
operandmode( modep )
struct modebyte *modep;
{
uint32_t 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 */
}
static
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 */
}
static
uint32_t
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 */
}
static
uint32_t
reladdr( modep )
struct modebyte *modep;
{
operandenum mode = operandmode( modep );
char *cp;
short *sp;
int32_t *lp;
cp = (char *) modep;
cp += 1; /* skip over the mode */
switch ( mode ) {
default:
fprintf( stderr , "[reladdr] not relative address\n" );
return (uint32_t) modep;
case byterel:
return (uint32_t) ( cp + sizeof *cp + *cp );
case wordrel:
sp = (short *) cp;
return (uint32_t) ( cp + sizeof *sp + *sp );
case longrel:
lp = (int32_t *) cp;
return (uint32_t) ( cp + sizeof *lp + *lp );
}
}
findcalls( parentp , p_lowpc , p_highpc )
nltype *parentp;
uint32_t p_lowpc;
uint32_t p_highpc;
{
unsigned char *instructp;
int32_t length;
nltype *childp;
operandenum mode;
operandenum firstmode;
uint32_t 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 & CALLSDEBUG ) {
printf( "[findcalls] %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 & CALLSDEBUG ) {
printf( "[findcalls]\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 & CALLSDEBUG ) {
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 , (uint32_t) 0 , 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) )
- (uint32_t) textspace;
if ( destpc >= s_lowpc && destpc <= s_highpc ) {
childp = nllookup( destpc );
# ifdef DEBUG
if ( debug & CALLSDEBUG ) {
printf( "[findcalls]\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 , (uint32_t) 0 , 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 & CALLSDEBUG ) {
printf( "[findcalls]\tbut it's a botch\n" );
}
# endif /* DEBUG */
length = 1;
continue;
}
}
}
}
#endif /* vax */
#ifdef m68k
void
findcalls(
nltype *parentp,
uint32_t p_lowpc,
uint32_t p_highpc)
{
unsigned short *instructp;
int32_t length;
nltype *childp;
uint32_t destpc;
if(textspace == NULL){
return;
}
if(p_lowpc < sample_sets->s_lowpc){
p_lowpc = sample_sets->s_lowpc;
}
if(p_highpc > sample_sets->s_highpc){
p_highpc = sample_sets->s_highpc;
}
#ifdef DEBUG
if(debug & CALLSDEBUG){
printf("[findcalls] %s: 0x%x to 0x%x\n", parentp->name,
(unsigned int)p_lowpc, (unsigned int)p_highpc);
}
#endif
for(instructp = (unsigned short *)(textspace + p_lowpc);
instructp < (unsigned short *)(textspace + p_highpc);
instructp += length){
length = 1; /* 1 word */
if((*instructp & BSR_MASK) == BSR_OP){
/* bsr instruction */
short disp;
if(*instructp & 0xff)
disp = (char)(*instructp & 0xff);
else{
length = 2; /* 2 words */
disp = instructp[1];
}
destpc = (uint32_t)instructp + disp + 2 -
(uint32_t)textspace;
gotdestpc:
if(destpc >= sample_sets->s_lowpc &&
destpc <= sample_sets->s_highpc){
childp = nllookup(destpc);
#ifdef DEBUG
if(debug & CALLSDEBUG){
printf("[findcalls]\tdestpc 0x%x",
(unsigned int)destpc);
printf("childp->name %s", childp->name);
printf("childp->value 0x%x\n",
(unsigned int)childp->value );
}
#endif
if(childp->value == destpc){
/*
* a hit
*/
addarc(parentp, childp, 0, 0);
continue;
}
goto botched;
}
}
else if((*instructp & JBSR_MASK) == JBSR_OP){
/* jbsr instruction */
if((*instructp & 0x38) != 0x38) {
indirect:
addarc(parentp, &indirectchild, 0, 0);
continue;
}
switch(*instructp & 7) {
case 0: /* Abs.W */
destpc = instructp[1];
length = 2;
goto gotdestpc;
case 1: /* Abs.L */
destpc = (instructp[1] << 16) + (instructp[2] & 0xffff);
length = 3;
goto gotdestpc;
default:
goto indirect;
}
}
else
continue;
botched:
/*
* something funny going on.
*/
#ifdef DEBUG
if(debug & CALLSDEBUG){
printf("[findcalls]\tbut it's a botch\n");
}
#endif
length = 1;
}
}
#endif /* m68k */