2.9
[glibc/nacl-glibc.git] / sysdeps / posix / sprofil.c
blob6ffce4d7838f72f4171b5f6738dd5399dea620b2
1 /* Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
2 Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, write to the Free
17 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307 USA. */
20 #include <assert.h>
21 #include <signal.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
26 #include <sys/time.h>
27 #include <sys/profil.h>
29 #ifndef SIGPROF
30 # include <gmon/sprofil.c>
31 #else
33 #include <libc-internal.h>
35 struct region
37 size_t offset;
38 size_t nsamples;
39 unsigned int scale;
40 union
42 void *vp;
43 unsigned short *us;
44 unsigned int *ui;
46 sample;
47 size_t start;
48 size_t end;
51 struct prof_info
53 unsigned int num_regions;
54 struct region *region;
55 struct region *last, *overflow;
56 struct itimerval saved_timer;
57 struct sigaction saved_action;
60 static unsigned int overflow_counter;
62 static struct region default_overflow_region =
64 .offset = 0,
65 .nsamples = 1,
66 .scale = 2,
67 .sample = { &overflow_counter },
68 .start = 0,
69 .end = ~(size_t) 0
72 static struct prof_info prof_info;
74 static unsigned long int
75 pc_to_index (size_t pc, size_t offset, unsigned int scale, int prof_uint)
77 size_t i = (pc - offset) / (prof_uint ? sizeof (int) : sizeof (short));
79 if (sizeof (unsigned long long int) > sizeof (size_t))
80 return (unsigned long long int) i * scale / 65536;
81 else
82 return i / 65536 * scale + i % 65536 * scale / 65536;
85 static inline size_t
86 index_to_pc (unsigned long int n, size_t offset, unsigned int scale,
87 int prof_uint)
89 size_t pc, bin_size = (prof_uint ? sizeof (int) : sizeof (short));
91 if (sizeof (unsigned long long int) > sizeof (size_t))
92 pc = offset + (unsigned long long int) n * bin_size * 65536ull / scale;
93 else
94 pc = (offset + n * bin_size / scale * 65536
95 + n * bin_size % scale * 65536 / scale);
97 if (pc_to_index (pc, offset, scale, prof_uint) < n)
98 /* Adjust for rounding error. */
99 ++pc;
101 assert (pc_to_index (pc - 1, offset, scale, prof_uint) < n
102 && pc_to_index (pc, offset, scale, prof_uint) >= n);
104 return pc;
107 static void
108 profil_count (void *pcp, int prof_uint)
110 struct region *region, *r = prof_info.last;
111 size_t lo, hi, mid, pc = (unsigned long int) pcp;
112 unsigned long int i;
114 /* Fast path: pc is in same region as before. */
115 if (pc >= r->start && pc < r->end)
116 region = r;
117 else
119 /* Slow path: do a binary search for the right region. */
120 lo = 0; hi = prof_info.num_regions - 1;
121 while (lo <= hi)
123 mid = (lo + hi) / 2;
125 r = prof_info.region + mid;
126 if (pc >= r->start && pc < r->end)
128 prof_info.last = r;
129 region = r;
130 break;
133 if (pc < r->start)
134 hi = mid - 1;
135 else
136 lo = mid + 1;
139 /* No matching region: increment overflow count. There is no point
140 in updating the cache here, as it won't hit anyhow. */
141 region = prof_info.overflow;
144 i = pc_to_index (pc, region->offset, region->scale, prof_uint);
145 if (i < r->nsamples)
147 if (prof_uint)
149 if (r->sample.ui[i] < (unsigned int) ~0)
150 ++r->sample.ui[i];
152 else
154 if (r->sample.us[i] < (unsigned short) ~0)
155 ++r->sample.us[i];
158 else
160 if (prof_uint)
161 ++prof_info.overflow->sample.ui[0];
162 else
163 ++prof_info.overflow->sample.us[0];
167 static inline void
168 profil_count_ushort (void *pcp)
170 profil_count (pcp, 0);
173 static inline void
174 profil_count_uint (void *pcp)
176 profil_count (pcp, 1);
179 /* Get the machine-dependent definition of `profil_counter', the signal
180 handler for SIGPROF. It calls `profil_count' (above) with the PC of the
181 interrupted code. */
182 #define profil_counter profil_counter_ushort
183 #define profil_count(pc) profil_count (pc, 0)
184 #include <profil-counter.h>
186 #undef profil_counter
187 #undef profil_count
189 #define profil_counter profil_counter_uint
190 #define profil_count(pc) profil_count (pc, 1)
191 #include <profil-counter.h>
193 static int
194 insert (int i, unsigned long int start, unsigned long int end, struct prof *p,
195 int prof_uint)
197 struct region *r;
198 size_t to_copy;
200 if (start >= end)
201 return 0; /* don't bother with empty regions */
203 if (prof_info.num_regions == 0)
204 r = malloc (sizeof (*r));
205 else
206 r = realloc (prof_info.region, (prof_info.num_regions + 1) * sizeof (*r));
207 if (r == NULL)
208 return -1;
210 to_copy = prof_info.num_regions - i;
211 if (to_copy > 0)
212 memmove (r + i + 1, r + i, to_copy * sizeof (*r));
214 r[i].offset = p->pr_off;
215 r[i].nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
216 r[i].scale = p->pr_scale;
217 r[i].sample.vp = p->pr_base;
218 r[i].start = start;
219 r[i].end = end;
221 prof_info.region = r;
222 ++prof_info.num_regions;
224 if (p->pr_off == 0 && p->pr_scale == 2)
225 prof_info.overflow = r;
227 return 0;
230 /* Add a new profiling region. If the new region overlaps with
231 existing ones, this may add multiple subregions so that the final
232 data structure is free of overlaps. The absence of overlaps makes
233 it possible to use a binary search in profil_count(). Note that
234 this function depends on new regions being presented in DECREASING
235 ORDER of starting address. */
237 static int
238 add_region (struct prof *p, int prof_uint)
240 unsigned long int nsamples;
241 size_t start, end;
242 unsigned int i;
244 if (p->pr_scale < 2)
245 return 0;
247 nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
249 start = p->pr_off;
250 end = index_to_pc (nsamples, p->pr_off, p->pr_scale, prof_uint);
252 /* Merge with existing regions. */
253 for (i = 0; i < prof_info.num_regions; ++i)
255 if (start < prof_info.region[i].start)
257 if (end < prof_info.region[i].start)
258 break;
259 else if (insert (i, start, prof_info.region[i].start, p, prof_uint)
260 < 0)
261 return -1;
263 start = prof_info.region[i].end;
265 return insert (i, start, end, p, prof_uint);
268 static int
269 pcmp (const void *left, const void *right)
271 struct prof *l = *(struct prof **) left;
272 struct prof *r = *(struct prof **) right;
274 if (l->pr_off < r->pr_off)
275 return 1;
276 else if (l->pr_off > r->pr_off)
277 return -1;
278 return 0;
282 __sprofil (struct prof *profp, int profcnt, struct timeval *tvp,
283 unsigned int flags)
285 struct prof *p[profcnt];
286 struct itimerval timer;
287 struct sigaction act;
288 int i;
290 if (tvp != NULL)
292 /* Return profiling period. */
293 unsigned long int t = 1000000 / __profile_frequency ();
294 tvp->tv_sec = t / 1000000;
295 tvp->tv_usec = t % 1000000;
298 if (prof_info.num_regions > 0)
300 /* Disable profiling. */
301 if (__setitimer (ITIMER_PROF, &prof_info.saved_timer, NULL) < 0)
302 return -1;
304 if (__sigaction (SIGPROF, &prof_info.saved_action, NULL) < 0)
305 return -1;
307 free (prof_info.region);
308 return 0;
311 prof_info.num_regions = 0;
312 prof_info.region = NULL;
313 prof_info.overflow = &default_overflow_region;
315 for (i = 0; i < profcnt; ++i)
316 p[i] = profp + i;
318 /* Sort in order of decreasing starting address: */
319 qsort (p, profcnt, sizeof (p[0]), pcmp);
321 /* Add regions in order of decreasing starting address: */
322 for (i = 0; i < profcnt; ++i)
323 if (add_region (p[i], (flags & PROF_UINT) != 0) < 0)
325 free (prof_info.region);
326 prof_info.num_regions = 0;
327 prof_info.region = NULL;
328 return -1;
331 if (prof_info.num_regions == 0)
332 return 0;
334 prof_info.last = prof_info.region;
336 /* Install SIGPROF handler. */
337 if (flags & PROF_UINT)
338 act.sa_handler = (sighandler_t) &profil_counter_uint;
339 else
340 act.sa_handler = (sighandler_t) &profil_counter_ushort;
341 act.sa_flags = SA_RESTART;
342 __sigfillset (&act.sa_mask);
343 if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0)
344 return -1;
346 /* Setup profiling timer. */
347 timer.it_value.tv_sec = 0;
348 timer.it_value.tv_usec = 1;
349 timer.it_interval = timer.it_value;
350 return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer);
353 weak_alias (__sprofil, sprofil)
355 #endif /* SIGPROF */