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