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 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 #include "gprof.h"
30
31 /*
32 * a namelist entry to be the child of indirect calls
33 */
34 nltype indirectchild = {
35 "(*)", /* the name */
36 &modules, /* module [-c only for prog txtspace] */
37 (pctype)0, /* the pc entry point */
38 (pctype)0, /* aligned entry point */
39 (unsigned long)0, /* function size */
40 (unsigned char)0, /* symbol information */
41 (size_t)0, /* ticks in this routine */
42 (double)0.0, /* ticks in this routine (as double) */
43 (double)0.0, /* cumulative ticks in children */
44 (long)0, /* how many times called */
45 (long)0, /* how many calls to self */
46 (double)1.0, /* propagation fraction */
47 (double)0.0, /* self propagation time */
48 (double)0.0, /* child propagation time */
49 (bool)0, /* print flag */
50 (int)0, /* index in the graph list */
51 (int)0, /* graph call chain top-sort order */
52 (int)0, /* internal number of cycle on */
53 (struct nl *)&indirectchild, /* pointer to head of cycle */
54 (struct nl *)0, /* pointer to next member of cycle */
55 (arctype *)0, /* list of caller arcs */
56 (arctype *)0, /* list of callee arcs */
57 (unsigned long)0 /* number of callers */
58 };
59
60 void
findcalls(nltype * parentp,pctype p_lowpc,pctype p_highpc)61 findcalls(nltype *parentp, pctype p_lowpc, pctype p_highpc)
62 {
63 unsigned long instructp;
64 sztype length;
65 nltype *childp;
66 pctype destpc;
67
68 if (textspace == 0) {
69 return;
70 }
71 if (p_lowpc > s_highpc)
72 return;
73 if (p_highpc < s_lowpc)
74 return;
75 if (p_lowpc < s_lowpc)
76 p_lowpc = s_lowpc;
77 if (p_highpc > s_highpc)
78 p_highpc = s_highpc;
79
80 #ifdef DEBUG
81 if (debug & CALLSDEBUG) {
82 printf("[findcalls] %s: 0x%llx to 0x%llx\n",
83 parentp->name, p_lowpc, p_highpc);
84 }
85 #endif /* DEBUG */
86
87 length = 4;
88 for (instructp = (uintptr_t)textspace + p_lowpc - TORIGIN;
89 instructp < (uintptr_t)textspace + p_highpc - TORIGIN;
90 instructp += length) {
91
92 switch (OP(instructp)) {
93 case CALL:
94 /*
95 * May be a call, better check it out.
96 */
97 #ifdef DEBUG
98 if (debug & CALLSDEBUG) {
99 printf("[findcalls]\t0x%x:call\n",
100 PC_VAL(instructp));
101 }
102 #endif /* DEBUG */
103 destpc = (DISP30(instructp) << 2) + PC_VAL(instructp);
104 break;
105
106 case FMT3_0x10:
107 if (OP3(instructp) != JMPL)
108 continue;
109
110 #ifdef DEBUG
111 if (debug & CALLSDEBUG)
112 printf("[findcalls]\t0x%x:jmpl",
113 PC_VAL(instructp));
114 #endif /* DEBUG */
115 if (RD(instructp) == R_G0) {
116 #ifdef DEBUG
117 if (debug & CALLSDEBUG) {
118 switch (RS1(instructp)) {
119 case R_O7:
120 printf("\tprobably a RETL\n");
121 break;
122 case R_I7:
123 printf("\tprobably a RET\n");
124 break;
125 default:
126 printf(", but not a call: "
127 "linked to g0\n");
128 }
129 }
130 #endif /* DEBUG */
131 continue;
132 }
133 #ifdef DEBUG
134 if (debug & CALLSDEBUG) {
135 printf("\toperands are DST = R%d,\tSRC = R%d",
136 RD(instructp), RS1(instructp));
137 }
138 #endif /* DEBUG */
139 if (IMMED(instructp)) {
140 #ifdef DEBUG
141 if (debug & CALLSDEBUG) {
142 if (SIMM13(instructp) < 0) {
143 printf(" - 0x%x\n",
144 -(SIMM13(instructp)));
145 } else {
146 printf(" + 0x%x\n",
147 SIMM13(instructp));
148 }
149 }
150 #endif /* DEBUG */
151 switch (RS1(instructp)) {
152 case R_G0:
153 /*
154 * absolute address, simm 13
155 */
156 destpc = SIMM13(instructp);
157 break;
158 default:
159 /*
160 * indirect call
161 */
162 addarc(parentp, &indirectchild, 0);
163 continue;
164 }
165 } else {
166 /*
167 * two register sources, all cases are indirect
168 */
169 #ifdef DEBUG
170 if (debug & CALLSDEBUG) {
171 printf(" + R%d\n", RS2(instructp));
172 }
173 #endif /* DEBUG */
174 addarc(parentp, &indirectchild, 0);
175 continue;
176 }
177 break;
178 default:
179 continue;
180 }
181
182 /*
183 * Check that the destination is the address of
184 * a function; this allows us to differentiate
185 * real calls from someone trying to get the PC,
186 * e.g. position independent switches.
187 */
188 if (destpc >= s_lowpc && destpc <= s_highpc) {
189
190 childp = nllookup(&modules, destpc, NULL);
191 #ifdef DEBUG
192 if (debug & CALLSDEBUG) {
193 printf("[findcalls]\tdestpc 0x%llx", destpc);
194 printf(" childp->name %s", childp->name);
195 printf(" childp->value 0x%llx\n",
196 childp->value);
197 }
198 #endif /* DEBUG */
199 if (childp->value == destpc) {
200 /*
201 * a hit
202 */
203 addarc(parentp, childp, 0);
204 continue;
205 }
206 }
207 /*
208 * else:
209 * it looked like a call,
210 * but it wasn't to anywhere.
211 */
212 #ifdef DEBUG
213 if (debug & CALLSDEBUG) {
214 printf("[findcalls]\tbut it's a switch or a botch\n");
215 }
216 #endif /* DEBUG */
217 continue;
218 }
219 }
220