]> git.ipfire.org Git - thirdparty/glibc.git/blame - sysdeps/posix/sprofil.c
2.5-18.1
[thirdparty/glibc.git] / sysdeps / posix / sprofil.c
CommitLineData
0ecb606c 1/* Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
4c48b267 2 Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
4c48b267
UD
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
4c48b267
UD
9
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
41bdb6e2 13 Lesser General Public License for more details.
4c48b267 14
41bdb6e2
AJ
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. */
4c48b267
UD
19
20#include <assert.h>
21#include <signal.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#include <sys/time.h>
27#include <sys/profil.h>
28
29#ifndef SIGPROF
0ecb606c 30# include <gmon/sprofil.c>
4c48b267
UD
31#else
32
33#include <libc-internal.h>
34
35struct region
36 {
37 size_t offset;
38 size_t nsamples;
39 unsigned int scale;
40 union
41 {
42 void *vp;
43 unsigned short *us;
44 unsigned int *ui;
45 }
46 sample;
47 size_t start;
48 size_t end;
49 };
50
51struct prof_info
52 {
53 unsigned int num_regions;
54 struct region *region;
55 struct region *last, *overflow;
56 struct itimerval saved_timer;
57 struct sigaction saved_action;
58 };
59
60static unsigned int overflow_counter;
61
62static struct region default_overflow_region =
63 {
0274d73c
RM
64 .offset = 0,
65 .nsamples = 1,
66 .scale = 2,
67 .sample = { &overflow_counter },
68 .start = 0,
69 .end = ~(size_t) 0
4c48b267
UD
70 };
71
72static struct prof_info prof_info;
73
1deb5779 74static unsigned long int
4c48b267
UD
75pc_to_index (size_t pc, size_t offset, unsigned int scale, int prof_uint)
76{
77 size_t i = (pc - offset) / (prof_uint ? sizeof (int) : sizeof (short));
78
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;
83}
84
85static inline size_t
86index_to_pc (unsigned long int n, size_t offset, unsigned int scale,
87 int prof_uint)
88{
89 size_t pc, bin_size = (prof_uint ? sizeof (int) : sizeof (short));
90
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);
96
97 if (pc_to_index (pc, offset, scale, prof_uint) < n)
98 /* Adjust for rounding error. */
99 ++pc;
100
101 assert (pc_to_index (pc - 1, offset, scale, prof_uint) < n
102 && pc_to_index (pc, offset, scale, prof_uint) >= n);
103
104 return pc;
105}
106
9c7ff11a 107static void
4c48b267
UD
108profil_count (void *pcp, int prof_uint)
109{
110 struct region *region, *r = prof_info.last;
111 size_t lo, hi, mid, pc = (unsigned long int) pcp;
112 unsigned long int i;
113
114 /* Fast path: pc is in same region as before. */
115 if (pc >= r->start && pc < r->end)
116 region = r;
117 else
118 {
119 /* Slow path: do a binary search for the right region. */
120 lo = 0; hi = prof_info.num_regions - 1;
121 while (lo <= hi)
122 {
123 mid = (lo + hi) / 2;
124
125 r = prof_info.region + mid;
126 if (pc >= r->start && pc < r->end)
127 {
128 prof_info.last = r;
129 region = r;
130 break;
131 }
132
133 if (pc < r->start)
134 hi = mid - 1;
135 else
136 lo = mid + 1;
137 }
138
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;
142 }
143
144 i = pc_to_index (pc, region->offset, region->scale, prof_uint);
145 if (i < r->nsamples)
146 {
147 if (prof_uint)
148 {
149 if (r->sample.ui[i] < (unsigned int) ~0)
150 ++r->sample.ui[i];
151 }
152 else
153 {
154 if (r->sample.us[i] < (unsigned short) ~0)
155 ++r->sample.us[i];
156 }
157 }
158 else
159 {
160 if (prof_uint)
161 ++prof_info.overflow->sample.ui[0];
162 else
163 ++prof_info.overflow->sample.us[0];
164 }
165}
166
167static inline void
168profil_count_ushort (void *pcp)
169{
170 profil_count (pcp, 0);
171}
172
173static inline void
174profil_count_uint (void *pcp)
175{
176 profil_count (pcp, 1);
177}
178
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)
0ecb606c 184#include <profil-counter.h>
4c48b267
UD
185
186#undef profil_counter
187#undef profil_count
188
189#define profil_counter profil_counter_uint
190#define profil_count(pc) profil_count (pc, 1)
0ecb606c 191#include <profil-counter.h>
4c48b267
UD
192
193static int
194insert (int i, unsigned long int start, unsigned long int end, struct prof *p,
195 int prof_uint)
196{
197 struct region *r;
198 size_t to_copy;
199
200 if (start >= end)
201 return 0; /* don't bother with empty regions */
202
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;
209
210 to_copy = prof_info.num_regions - i;
211 if (to_copy > 0)
212 memmove (r + i + 1, r + i, to_copy * sizeof (*r));
213
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;
220
221 prof_info.region = r;
222 ++prof_info.num_regions;
223
224 if (p->pr_off == 0 && p->pr_scale == 2)
225 prof_info.overflow = r;
226
227 return 0;
228}
229
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. */
236
237static int
238add_region (struct prof *p, int prof_uint)
239{
240 unsigned long int nsamples;
241 size_t start, end;
57b36a0a 242 unsigned int i;
4c48b267
UD
243
244 if (p->pr_scale < 2)
245 return 0;
246
247 nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
248
249 start = p->pr_off;
250 end = index_to_pc (nsamples, p->pr_off, p->pr_scale, prof_uint);
251
252 /* Merge with existing regions. */
253 for (i = 0; i < prof_info.num_regions; ++i)
254 {
255 if (start < prof_info.region[i].start)
256 {
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;
262 }
263 start = prof_info.region[i].end;
264 }
265 return insert (i, start, end, p, prof_uint);
266}
267
268static int
269pcmp (const void *left, const void *right)
270{
271 struct prof *l = *(struct prof **) left;
272 struct prof *r = *(struct prof **) right;
273
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;
279}
280
281int
282__sprofil (struct prof *profp, int profcnt, struct timeval *tvp,
283 unsigned int flags)
284{
285 struct prof *p[profcnt];
286 struct itimerval timer;
287 struct sigaction act;
288 int i;
289
290 if (tvp != NULL)
291 {
292 /* Return profiling period. */
293 unsigned long int t = 1000000 / __profile_frequency ();
294 tvp->tv_sec = t / 1000000;
295 tvp->tv_usec = t % 1000000;
296 }
297
298 if (prof_info.num_regions > 0)
299 {
300 /* Disable profiling. */
301 if (__setitimer (ITIMER_PROF, &prof_info.saved_timer, NULL) < 0)
302 return -1;
303
304 if (__sigaction (SIGPROF, &prof_info.saved_action, NULL) < 0)
305 return -1;
306
307 free (prof_info.region);
308 return 0;
309 }
310
311 prof_info.num_regions = 0;
312 prof_info.region = NULL;
313 prof_info.overflow = &default_overflow_region;
314
315 for (i = 0; i < profcnt; ++i)
316 p[i] = profp + i;
317
318 /* Sort in order of decreasing starting address: */
319 qsort (p, profcnt, sizeof (p[0]), pcmp);
320
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)
324 {
325 if (prof_info.region)
326 free (prof_info.region);
327 prof_info.num_regions = 0;
328 prof_info.region = NULL;
329 return -1;
330 }
331
332 if (prof_info.num_regions == 0)
333 return 0;
334
335 prof_info.last = prof_info.region;
336
337 /* Install SIGPROF handler. */
338 if (flags & PROF_UINT)
339 act.sa_handler = (sighandler_t) &profil_counter_uint;
340 else
341 act.sa_handler = (sighandler_t) &profil_counter_ushort;
342 act.sa_flags = SA_RESTART;
284128f6 343 __sigfillset (&act.sa_mask);
4c48b267
UD
344 if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0)
345 return -1;
346
347 /* Setup profiling timer. */
348 timer.it_value.tv_sec = 0;
349 timer.it_value.tv_usec = 1;
350 timer.it_interval = timer.it_value;
351 return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer);
352}
353
354weak_alias (__sprofil, sprofil)
355
356#endif /* SIGPROF */