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
findcalls(nltype * parentp,pctype p_lowpc,pctype p_highpc)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