xref: /optee_os/lib/libutee/arch/arm/gprof/gprof.c (revision 1bb929836182ecb96d2d9d268daa807c67596396)
1 // SPDX-License-Identifier: (BSD-2-Clause AND BSD-3-Clause)
2 /*
3  * Copyright (c) 2016, Linaro Limited
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * Portions of this file are adapted from glibc:
31  *   gmon/gmon.c
32  *   gmon/mcount.c
33  *
34  *-
35  * Copyright (c) 1983, 1992, 1993, 2011
36  *	The Regents of the University of California.  All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  * 4. Neither the name of the University nor the names of its contributors
47  *    may be used to endorse or promote products derived from this software
48  *    without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60  * SUCH DAMAGE.
61  */
62 
63 #include <assert.h>
64 #include <compiler.h>
65 #include <inttypes.h>
66 #include <malloc.h>
67 #include <stdint.h>
68 #include <string.h>
69 #include <tee_api_private.h>
70 #include <trace.h>
71 #include <user_ta_header.h>
72 #include <utee_types.h>
73 #include "gmon.h"
74 #include "gmon_out.h"
75 #include "gprof_pta.h"
76 
77 /* Defined by the linker script */
78 extern uint8_t __gprof_buf_end[];
79 extern uint8_t __gprof_buf_start[];
80 
81 static bool ta_instrumented(void)
82 {
83 	return (__gprof_buf_end != __gprof_buf_start);
84 }
85 
86 static void *gprof_alloc(size_t len)
87 {
88 	if (len > (size_t)(__gprof_buf_end - __gprof_buf_start))
89 		return NULL;
90 	return __gprof_buf_start;
91 }
92 
93 static struct gmonparam _gmonparam = { GMON_PROF_OFF };
94 
95 static uint32_t _gprof_file_id; /* File id returned by tee-supplicant */
96 
97 static int _gprof_s_scale;
98 #define SCALE_1_TO_1 0x10000L
99 
100 /* Adjust PC so that gprof can locate it in the TA ELF file */
101 static unsigned long __noprof adjust_pc(unsigned long pc)
102 {
103 	return pc - (unsigned long)__text_start + sizeof(struct ta_head);
104 }
105 
106 void __utee_gprof_init(void)
107 {
108 	unsigned long lowpc;
109 	unsigned long highpc;
110 	struct gmonparam *p = &_gmonparam;
111 	size_t bufsize;
112 	TEE_Result res;
113 	char *cp;
114 
115 	if (!ta_instrumented())
116 		return;
117 
118 	lowpc = adjust_pc((unsigned long)__text_start);
119 	highpc = adjust_pc((unsigned long)__text_end);
120 
121 	/*
122 	 * Round lowpc and highpc to multiples of the density we're using
123 	 * so the rest of the scaling (here and in gprof) stays in ints.
124 	 */
125 	p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER));
126 	p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER));
127 	p->textsize = p->highpc - p->lowpc;
128 	p->kcountsize = ROUNDUP(p->textsize / HISTFRACTION, sizeof(*p->froms));
129 	p->hashfraction = HASHFRACTION;
130 	p->log_hashfraction = -1;
131 	/*
132 	 * The following test must be kept in sync with the corresponding
133 	 * test in __mcount_internal
134 	 */
135 	if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) {
136 		/*
137 		 * If HASHFRACTION is a power of two, mcount can use shifting
138 		 * instead of integer division. Precompute shift amount.
139 		 */
140 		p->log_hashfraction = __builtin_ffs(p->hashfraction *
141 						    sizeof(*p->froms)) - 1;
142 	}
143 	p->fromssize = p->textsize / HASHFRACTION;
144 	p->tolimit = p->textsize * ARCDENSITY / 100;
145 	if (p->tolimit < MINARCS)
146 		p->tolimit = MINARCS;
147 	else if (p->tolimit > MAXARCS)
148 		p->tolimit = MAXARCS;
149 	p->tossize = p->tolimit * sizeof(struct tostruct);
150 
151 	bufsize = p->kcountsize + p->fromssize + p->tossize;
152 
153 	IMSG("gprof: initializing");
154 	DMSG("TA text size: %zu, gprof buffer size: %zu",
155 	     __text_end - __text_start, bufsize);
156 
157 	cp = gprof_alloc(bufsize);
158 	if (!cp) {
159 		EMSG("gprof: could not allocate profiling buffer");
160 		p->tos = NULL;
161 		p->state = GMON_PROF_ERROR;
162 		return;
163 	}
164 
165 	p->tos = (struct tostruct *)cp;
166 	cp += p->tossize;
167 	p->kcount = (HISTCOUNTER *)cp;
168 	cp += p->kcountsize;
169 	p->froms = (ARCINDEX *)cp;
170 
171 	p->tos[0].link = 0;
172 
173 	if (p->kcountsize < p->textsize)
174 		_gprof_s_scale = ((float)p->kcountsize / p->textsize) *
175 				  SCALE_1_TO_1;
176 	else
177 		_gprof_s_scale = SCALE_1_TO_1;
178 
179 	res = __pta_gprof_pc_sampling_start(p->kcount, p->kcountsize,
180 					    p->lowpc +
181 					    ((unsigned long)__text_start -
182 						sizeof(struct ta_head)),
183 					    _gprof_s_scale);
184 	if (res != TEE_SUCCESS)
185 		EMSG("gprof: could not start PC sampling (0x%08x)", res);
186 
187 	p->state = GMON_PROF_ON;
188 }
189 
190 static void _gprof_write_buf(void *buf, size_t size)
191 {
192 	TEE_Result res;
193 
194 	res = __pta_gprof_send(buf, size, &_gprof_file_id);
195 	if (res != TEE_SUCCESS)
196 		EMSG("gprof: could not send gprof data (0x%08x)", res);
197 }
198 
199 static void _gprof_write_header(void)
200 {
201 	struct gmon_hdr ghdr;
202 	size_t size = sizeof(struct gmon_hdr);
203 
204 	memcpy(&ghdr.cookie[0], GMON_MAGIC, sizeof(ghdr.cookie));
205 	ghdr.version = GMON_VERSION;
206 	memset(ghdr.spare, '\0', sizeof(ghdr.spare));
207 
208 	_gprof_write_buf(&ghdr, size);
209 }
210 
211 static void _gprof_write_hist(void)
212 {
213 	struct out_record {
214 		uint8_t tag;
215 		struct gmon_hist_hdr hist_hdr;
216 	} __packed out = {
217 		.tag = GMON_TAG_TIME_HIST,
218 		.hist_hdr = {
219 			.low_pc = _gmonparam.lowpc,
220 			.high_pc = _gmonparam.highpc,
221 			.hist_size = _gmonparam.kcountsize/sizeof(HISTCOUNTER),
222 			.prof_rate = _gmonparam.prof_rate,
223 			.dimen = "seconds",
224 			.dimen_abbrev = 's',
225 		}
226 	};
227 
228 	_gprof_write_buf(&out, sizeof(out));
229 	_gprof_write_buf(_gmonparam.kcount, _gmonparam.kcountsize);
230 }
231 
232 static void _gprof_write_call_graph(void)
233 {
234 #define NARCS_PER_WRITE 16
235 	struct out_record {
236 		uint8_t tag;
237 		uint8_t data[sizeof(struct gmon_cg_arc_record)];
238 	} out[NARCS_PER_WRITE];
239 	struct gmon_cg_arc_record arc;
240 	ARCINDEX from_index, to_index;
241 	unsigned long from_len;
242 	unsigned long frompc;
243 	int nfilled = 0;
244 
245 	from_len = _gmonparam.fromssize / sizeof(*_gmonparam.froms);
246 
247 	for (from_index = 0; from_index < from_len; ++from_index) {
248 
249 		if (_gmonparam.froms[from_index] == 0)
250 			continue;
251 
252 		frompc = _gmonparam.lowpc;
253 		frompc += (from_index * _gmonparam.hashfraction
254 			   * sizeof(*_gmonparam.froms));
255 		for (to_index = _gmonparam.froms[from_index];
256 		     to_index != 0;
257 		     to_index = _gmonparam.tos[to_index].link) {
258 
259 			arc.from_pc = frompc;
260 			arc.self_pc = _gmonparam.tos[to_index].selfpc;
261 			arc.count = _gmonparam.tos[to_index].count;
262 
263 			out[nfilled].tag = GMON_TAG_CG_ARC;
264 			memcpy(out[nfilled].data, &arc, sizeof(arc));
265 
266 			if (++nfilled == NARCS_PER_WRITE) {
267 				_gprof_write_buf(out, sizeof(out));
268 				nfilled = 0;
269 			}
270 		}
271 	}
272 	if (nfilled > 0)
273 		_gprof_write_buf(out, nfilled * sizeof(out[0]));
274 }
275 
276 /* Stop profiling and send profile data in gmon.out format to Normal World */
277 void __utee_gprof_fini(void)
278 {
279 	TEE_Result res;
280 
281 	if (_gmonparam.state != GMON_PROF_ON)
282 		return;
283 
284 	/* Stop call graph tracing */
285 	_gmonparam.state = GMON_PROF_OFF_EXITING;
286 
287 	/* Stop TA sampling */
288 	res = __pta_gprof_pc_sampling_stop(&_gmonparam.prof_rate);
289 
290 	_gprof_write_header();
291 	if (res == TEE_SUCCESS)
292 		_gprof_write_hist();
293 	_gprof_write_call_graph();
294 
295 	__pta_gprof_fini();
296 }
297 
298 /*
299  * Called from the assembly stub (_mcount or __gnu_mcount_nc).
300  *
301  * __mcount_internal updates data structures that represent traversals of the
302  * program's call graph edges.  frompc and selfpc are the return
303  * address and function address that represents the given call graph edge.
304  */
305 void __noprof __mcount_internal(unsigned long frompc, unsigned long selfpc)
306 {
307 	ARCINDEX *frompcindex;
308 	struct tostruct *top, *prevtop;
309 	struct gmonparam *p;
310 	ARCINDEX toindex;
311 	int i;
312 
313 	p = &_gmonparam;
314 
315 	/*
316 	 * Check that we are profiling and that we aren't recursively invoked.
317 	 */
318 	if (p->state != GMON_PROF_ON)
319 		return;
320 	p->state = GMON_PROF_BUSY;
321 
322 	frompc = adjust_pc(frompc);
323 	selfpc = adjust_pc(selfpc);
324 
325 	/* Check that frompcindex is a reasonable pc value. */
326 	frompc -= p->lowpc;
327 	if (frompc > p->textsize)
328 		goto done;
329 
330 	/* Note: keep in sync. with the initialization function above */
331 	if ((HASHFRACTION & (HASHFRACTION - 1)) == 0) {
332 		/* Avoid integer divide if possible */
333 		i = frompc >> p->log_hashfraction;
334 	} else {
335 		i = frompc / (p->hashfraction * sizeof(*p->froms));
336 	}
337 	frompcindex = &p->froms[i];
338 	toindex = *frompcindex;
339 	if (toindex == 0) {
340 		/* First time traversing this arc */
341 		toindex = ++p->tos[0].link;
342 		if (toindex >= p->tolimit) {
343 			/* Halt further profiling */
344 			goto overflow;
345 		}
346 
347 		*frompcindex = toindex;
348 		top = &p->tos[toindex];
349 		top->selfpc = selfpc;
350 		top->count = 1;
351 		top->link = 0;
352 		goto done;
353 	}
354 	top = &p->tos[toindex];
355 	if (top->selfpc == selfpc) {
356 		/* Arc at front of chain; usual case */
357 		top->count++;
358 		goto done;
359 	}
360 	/*
361 	 * Have to go looking down chain for it.
362 	 * top points to what we are looking at,
363 	 * prevtop points to previous top.
364 	 * we know it is not at the head of the chain.
365 	 */
366 	for (;;) {
367 		if (top->link == 0) {
368 			/*
369 			 * top is end of the chain and none of the chain
370 			 * had top->selfpc == selfpc.
371 			 * so we allocate a new tostruct
372 			 * and link it to the head of the chain.
373 			 */
374 			toindex = ++p->tos[0].link;
375 			if (toindex >= p->tolimit)
376 				goto overflow;
377 
378 			top = &p->tos[toindex];
379 			top->selfpc = selfpc;
380 			top->count = 1;
381 			top->link = *frompcindex;
382 			*frompcindex = toindex;
383 			goto done;
384 		}
385 		/*
386 		 * Otherwise, check the next arc on the chain.
387 		 */
388 		prevtop = top;
389 		top = &p->tos[top->link];
390 		if (top->selfpc == selfpc) {
391 			/*
392 			 * There it is. Increment its count, move it to the
393 			 * head of the chain.
394 			 */
395 			top->count++;
396 			toindex = prevtop->link;
397 			prevtop->link = top->link;
398 			top->link = *frompcindex;
399 			*frompcindex = toindex;
400 			goto done;
401 		}
402 	}
403 done:
404 	p->state = GMON_PROF_ON;
405 	return;
406 overflow:
407 	p->state = GMON_PROF_ERROR;
408 }
409