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