]>
Commit | Line | Data |
---|---|---|
2826ac7e | 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 | |
2826ac7e | 30 | # include <gmon/sprofil.c> |
4c48b267 UD |
31 | #else |
32 | ||
33 | #include <libc-internal.h> | |
34 | ||
35 | struct 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 | ||
51 | struct 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 | ||
60 | static unsigned int overflow_counter; | |
61 | ||
62 | static 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 | ||
72 | static struct prof_info prof_info; | |
73 | ||
1deb5779 | 74 | static unsigned long int |
4c48b267 UD |
75 | pc_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 | ||
85 | static inline size_t | |
86 | index_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 | 107 | static void |
4c48b267 UD |
108 | profil_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 | ||
167 | static inline void | |
168 | profil_count_ushort (void *pcp) | |
169 | { | |
170 | profil_count (pcp, 0); | |
171 | } | |
172 | ||
173 | static inline void | |
174 | profil_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) | |
8f480b4b | 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) | |
8f480b4b | 191 | #include <profil-counter.h> |
4c48b267 UD |
192 | |
193 | static int | |
194 | insert (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 | ||
237 | static int | |
238 | add_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 | ||
268 | static int | |
269 | pcmp (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 | ||
281 | int | |
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 | ||
354 | weak_alias (__sprofil, sprofil) | |
355 | ||
356 | #endif /* SIGPROF */ |