xref: /illumos-gate/usr/src/cmd/sgs/gprof/common/calls.c (revision e4d060fb4c00d44cd578713eb9a921f594b733b8)
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
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