/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "gprof.h" /* * a namelist entry to be the child of indirect calls */ nltype indirectchild = { "(*)", /* the name */ &modules, /* module [-c only for prog txtspace] */ (pctype)0, /* the pc entry point */ (pctype)0, /* aligned entry point */ (unsigned long)0, /* function size */ (unsigned char)0, /* symbol information */ (size_t)0, /* ticks in this routine */ (double)0.0, /* ticks in this routine (as double) */ (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 */ (unsigned long)0 /* number of callers */ }; void findcalls(nltype *parentp, pctype p_lowpc, pctype p_highpc) { unsigned long instructp; sztype length; nltype *childp; pctype destpc; if (textspace == 0) { return; } if (p_lowpc > s_highpc) return; if (p_highpc < s_lowpc) 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%llx to 0x%llx\n", parentp->name, p_lowpc, p_highpc); } #endif /* DEBUG */ length = 4; for (instructp = (uintptr_t)textspace + p_lowpc - TORIGIN; instructp < (uintptr_t)textspace + p_highpc - TORIGIN; instructp += length) { switch (OP(instructp)) { case CALL: /* * May be a call, better check it out. */ #ifdef DEBUG if (debug & CALLSDEBUG) { printf("[findcalls]\t0x%x:call\n", PC_VAL(instructp)); } #endif /* DEBUG */ destpc = (DISP30(instructp) << 2) + PC_VAL(instructp); break; case FMT3_0x10: if (OP3(instructp) != JMPL) continue; #ifdef DEBUG if (debug & CALLSDEBUG) printf("[findcalls]\t0x%x:jmpl", PC_VAL(instructp)); #endif /* DEBUG */ if (RD(instructp) == R_G0) { #ifdef DEBUG if (debug & CALLSDEBUG) { switch (RS1(instructp)) { case R_O7: printf("\tprobably a RETL\n"); break; case R_I7: printf("\tprobably a RET\n"); break; default: printf(", but not a call: " "linked to g0\n"); } } #endif /* DEBUG */ continue; } #ifdef DEBUG if (debug & CALLSDEBUG) { printf("\toperands are DST = R%d,\tSRC = R%d", RD(instructp), RS1(instructp)); } #endif /* DEBUG */ if (IMMED(instructp)) { #ifdef DEBUG if (debug & CALLSDEBUG) { if (SIMM13(instructp) < 0) { printf(" - 0x%x\n", -(SIMM13(instructp))); } else { printf(" + 0x%x\n", SIMM13(instructp)); } } #endif /* DEBUG */ switch (RS1(instructp)) { case R_G0: /* * absolute address, simm 13 */ destpc = SIMM13(instructp); break; default: /* * indirect call */ addarc(parentp, &indirectchild, 0); continue; } } else { /* * two register sources, all cases are indirect */ #ifdef DEBUG if (debug & CALLSDEBUG) { printf(" + R%d\n", RS2(instructp)); } #endif /* DEBUG */ addarc(parentp, &indirectchild, 0); continue; } break; default: continue; } /* * Check that the destination is the address of * a function; this allows us to differentiate * real calls from someone trying to get the PC, * e.g. position independent switches. */ if (destpc >= s_lowpc && destpc <= s_highpc) { childp = nllookup(&modules, destpc, NULL); #ifdef DEBUG if (debug & CALLSDEBUG) { printf("[findcalls]\tdestpc 0x%llx", destpc); printf(" childp->name %s", childp->name); printf(" childp->value 0x%llx\n", childp->value); } #endif /* DEBUG */ if (childp->value == destpc) { /* * a hit */ addarc(parentp, childp, 0); continue; } } /* * else: * it looked like a call, * but it wasn't to anywhere. */ #ifdef DEBUG if (debug & CALLSDEBUG) { printf("[findcalls]\tbut it's a switch or a botch\n"); } #endif /* DEBUG */ continue; } }