1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include "gprof.h" 28 29 /* 30 * a namelist entry to be the child of indirect calls 31 */ 32 nltype indirectchild = { 33 "(*)", /* the name */ 34 &modules, /* module [-c only for prog txtspace] */ 35 (pctype)0, /* the pc entry point */ 36 (pctype)0, /* aligned entry point */ 37 (unsigned long)0, /* function size */ 38 (unsigned char)0, /* symbol information */ 39 (size_t)0, /* ticks in this routine */ 40 (double)0.0, /* ticks in this routine (as double) */ 41 (double)0.0, /* cumulative ticks in children */ 42 (long)0, /* how many times called */ 43 (long)0, /* how many calls to self */ 44 (double)1.0, /* propagation fraction */ 45 (double)0.0, /* self propagation time */ 46 (double)0.0, /* child propagation time */ 47 (bool)0, /* print flag */ 48 (int)0, /* index in the graph list */ 49 (int)0, /* graph call chain top-sort order */ 50 (int)0, /* internal number of cycle on */ 51 (struct nl *)&indirectchild, /* pointer to head of cycle */ 52 (struct nl *)0, /* pointer to next member of cycle */ 53 (arctype *)0, /* list of caller arcs */ 54 (arctype *)0, /* list of callee arcs */ 55 (unsigned long)0 /* number of callers */ 56 }; 57 58 void 59 findcalls(nltype *parentp, pctype p_lowpc, pctype p_highpc) 60 { 61 unsigned long instructp; 62 sztype length; 63 nltype *childp; 64 pctype destpc; 65 66 if (textspace == 0) { 67 return; 68 } 69 if (p_lowpc > s_highpc) 70 return; 71 if (p_highpc < s_lowpc) 72 return; 73 if (p_lowpc < s_lowpc) 74 p_lowpc = s_lowpc; 75 if (p_highpc > s_highpc) 76 p_highpc = s_highpc; 77 78 #ifdef DEBUG 79 if (debug & CALLSDEBUG) { 80 printf("[findcalls] %s: 0x%llx to 0x%llx\n", 81 parentp->name, p_lowpc, p_highpc); 82 } 83 #endif /* DEBUG */ 84 85 length = 4; 86 for (instructp = (uintptr_t)textspace + p_lowpc - TORIGIN; 87 instructp < (uintptr_t)textspace + p_highpc - TORIGIN; 88 instructp += length) { 89 90 switch (OP(instructp)) { 91 case CALL: 92 /* 93 * May be a call, better check it out. 94 */ 95 #ifdef DEBUG 96 if (debug & CALLSDEBUG) { 97 printf("[findcalls]\t0x%x:call\n", 98 PC_VAL(instructp)); 99 } 100 #endif /* DEBUG */ 101 destpc = (DISP30(instructp) << 2) + PC_VAL(instructp); 102 break; 103 104 case FMT3_0x10: 105 if (OP3(instructp) != JMPL) 106 continue; 107 108 #ifdef DEBUG 109 if (debug & CALLSDEBUG) 110 printf("[findcalls]\t0x%x:jmpl", 111 PC_VAL(instructp)); 112 #endif /* DEBUG */ 113 if (RD(instructp) == R_G0) { 114 #ifdef DEBUG 115 if (debug & CALLSDEBUG) { 116 switch (RS1(instructp)) { 117 case R_O7: 118 printf("\tprobably a RETL\n"); 119 break; 120 case R_I7: 121 printf("\tprobably a RET\n"); 122 break; 123 default: 124 printf(", but not a call: " 125 "linked to g0\n"); 126 } 127 } 128 #endif /* DEBUG */ 129 continue; 130 } 131 #ifdef DEBUG 132 if (debug & CALLSDEBUG) { 133 printf("\toperands are DST = R%d,\tSRC = R%d", 134 RD(instructp), RS1(instructp)); 135 } 136 #endif /* DEBUG */ 137 if (IMMED(instructp)) { 138 #ifdef DEBUG 139 if (debug & CALLSDEBUG) { 140 if (SIMM13(instructp) < 0) { 141 printf(" - 0x%x\n", 142 -(SIMM13(instructp))); 143 } else { 144 printf(" + 0x%x\n", 145 SIMM13(instructp)); 146 } 147 } 148 #endif /* DEBUG */ 149 switch (RS1(instructp)) { 150 case R_G0: 151 /* 152 * absolute address, simm 13 153 */ 154 destpc = SIMM13(instructp); 155 break; 156 default: 157 /* 158 * indirect call 159 */ 160 addarc(parentp, &indirectchild, 0); 161 continue; 162 } 163 } else { 164 /* 165 * two register sources, all cases are indirect 166 */ 167 #ifdef DEBUG 168 if (debug & CALLSDEBUG) { 169 printf(" + R%d\n", RS2(instructp)); 170 } 171 #endif /* DEBUG */ 172 addarc(parentp, &indirectchild, 0); 173 continue; 174 } 175 break; 176 default: 177 continue; 178 } 179 180 /* 181 * Check that the destination is the address of 182 * a function; this allows us to differentiate 183 * real calls from someone trying to get the PC, 184 * e.g. position independent switches. 185 */ 186 if (destpc >= s_lowpc && destpc <= s_highpc) { 187 188 childp = nllookup(&modules, destpc, NULL); 189 #ifdef DEBUG 190 if (debug & CALLSDEBUG) { 191 printf("[findcalls]\tdestpc 0x%llx", destpc); 192 printf(" childp->name %s", childp->name); 193 printf(" childp->value 0x%llx\n", 194 childp->value); 195 } 196 #endif /* DEBUG */ 197 if (childp->value == destpc) { 198 /* 199 * a hit 200 */ 201 addarc(parentp, childp, 0); 202 continue; 203 } 204 } 205 /* 206 * else: 207 * it looked like a call, 208 * but it wasn't to anywhere. 209 */ 210 #ifdef DEBUG 211 if (debug & CALLSDEBUG) { 212 printf("[findcalls]\tbut it's a switch or a botch\n"); 213 } 214 #endif /* DEBUG */ 215 continue; 216 } 217 } 218