1 /* Copyright (C) 2001-2013 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, see
17 <http://www.gnu.org/licenses/>. */
26 #include <sys/profil.h>
29 # include <gmon/sprofil.c>
32 #include <libc-internal.h>
52 unsigned int num_regions
;
53 struct region
*region
;
54 struct region
*last
, *overflow
;
55 struct itimerval saved_timer
;
56 struct sigaction saved_action
;
59 static unsigned int overflow_counter
;
61 static struct region default_overflow_region
=
66 .sample
= { &overflow_counter
},
71 static struct prof_info prof_info
;
73 static unsigned long int
74 pc_to_index (size_t pc
, size_t offset
, unsigned int scale
, int prof_uint
)
76 size_t i
= (pc
- offset
) / (prof_uint
? sizeof (int) : sizeof (short));
78 if (sizeof (unsigned long long int) > sizeof (size_t))
79 return (unsigned long long int) i
* scale
/ 65536;
81 return i
/ 65536 * scale
+ i
% 65536 * scale
/ 65536;
85 index_to_pc (unsigned long int n
, size_t offset
, unsigned int scale
,
88 size_t pc
, bin_size
= (prof_uint
? sizeof (int) : sizeof (short));
90 if (sizeof (unsigned long long int) > sizeof (size_t))
91 pc
= offset
+ (unsigned long long int) n
* bin_size
* 65536ull / scale
;
93 pc
= (offset
+ n
* bin_size
/ scale
* 65536
94 + n
* bin_size
% scale
* 65536 / scale
);
96 if (pc_to_index (pc
, offset
, scale
, prof_uint
) < n
)
97 /* Adjust for rounding error. */
100 assert (pc_to_index (pc
- 1, offset
, scale
, prof_uint
) < n
101 && pc_to_index (pc
, offset
, scale
, prof_uint
) >= n
);
107 profil_count (void *pcp
, int prof_uint
)
109 struct region
*region
, *r
= prof_info
.last
;
110 size_t lo
, hi
, mid
, pc
= (unsigned long int) pcp
;
113 /* Fast path: pc is in same region as before. */
114 if (pc
>= r
->start
&& pc
< r
->end
)
118 /* Slow path: do a binary search for the right region. */
119 lo
= 0; hi
= prof_info
.num_regions
- 1;
124 r
= prof_info
.region
+ mid
;
125 if (pc
>= r
->start
&& pc
< r
->end
)
138 /* No matching region: increment overflow count. There is no point
139 in updating the cache here, as it won't hit anyhow. */
140 region
= prof_info
.overflow
;
143 i
= pc_to_index (pc
, region
->offset
, region
->scale
, prof_uint
);
148 if (r
->sample
.ui
[i
] < (unsigned int) ~0)
153 if (r
->sample
.us
[i
] < (unsigned short) ~0)
160 ++prof_info
.overflow
->sample
.ui
[0];
162 ++prof_info
.overflow
->sample
.us
[0];
167 profil_count_ushort (void *pcp
)
169 profil_count (pcp
, 0);
173 profil_count_uint (void *pcp
)
175 profil_count (pcp
, 1);
178 /* Get the machine-dependent definition of `profil_counter', the signal
179 handler for SIGPROF. It calls `profil_count' (above) with the PC of the
181 #define profil_counter profil_counter_ushort
182 #define profil_count(pc) profil_count (pc, 0)
183 #include <profil-counter.h>
185 #undef profil_counter
188 #define profil_counter profil_counter_uint
189 #define profil_count(pc) profil_count (pc, 1)
190 #include <profil-counter.h>
193 insert (int i
, unsigned long int start
, unsigned long int end
, struct prof
*p
,
200 return 0; /* don't bother with empty regions */
202 if (prof_info
.num_regions
== 0)
203 r
= malloc (sizeof (*r
));
205 r
= realloc (prof_info
.region
, (prof_info
.num_regions
+ 1) * sizeof (*r
));
209 to_copy
= prof_info
.num_regions
- i
;
211 memmove (r
+ i
+ 1, r
+ i
, to_copy
* sizeof (*r
));
213 r
[i
].offset
= p
->pr_off
;
214 r
[i
].nsamples
= p
->pr_size
/ (prof_uint
? sizeof (int) : sizeof (short));
215 r
[i
].scale
= p
->pr_scale
;
216 r
[i
].sample
.vp
= p
->pr_base
;
220 prof_info
.region
= r
;
221 ++prof_info
.num_regions
;
223 if (p
->pr_off
== 0 && p
->pr_scale
== 2)
224 prof_info
.overflow
= r
;
229 /* Add a new profiling region. If the new region overlaps with
230 existing ones, this may add multiple subregions so that the final
231 data structure is free of overlaps. The absence of overlaps makes
232 it possible to use a binary search in profil_count(). Note that
233 this function depends on new regions being presented in DECREASING
234 ORDER of starting address. */
237 add_region (struct prof
*p
, int prof_uint
)
239 unsigned long int nsamples
;
246 nsamples
= p
->pr_size
/ (prof_uint
? sizeof (int) : sizeof (short));
249 end
= index_to_pc (nsamples
, p
->pr_off
, p
->pr_scale
, prof_uint
);
251 /* Merge with existing regions. */
252 for (i
= 0; i
< prof_info
.num_regions
; ++i
)
254 if (start
< prof_info
.region
[i
].start
)
256 if (end
< prof_info
.region
[i
].start
)
258 else if (insert (i
, start
, prof_info
.region
[i
].start
, p
, prof_uint
)
262 start
= prof_info
.region
[i
].end
;
264 return insert (i
, start
, end
, p
, prof_uint
);
268 pcmp (const void *left
, const void *right
)
270 struct prof
*l
= *(struct prof
**) left
;
271 struct prof
*r
= *(struct prof
**) right
;
273 if (l
->pr_off
< r
->pr_off
)
275 else if (l
->pr_off
> r
->pr_off
)
281 __sprofil (struct prof
*profp
, int profcnt
, struct timeval
*tvp
,
284 struct prof
*p
[profcnt
];
285 struct itimerval timer
;
286 struct sigaction act
;
291 /* Return profiling period. */
292 unsigned long int t
= 1000000 / __profile_frequency ();
293 tvp
->tv_sec
= t
/ 1000000;
294 tvp
->tv_usec
= t
% 1000000;
297 if (prof_info
.num_regions
> 0)
299 /* Disable profiling. */
300 if (__setitimer (ITIMER_PROF
, &prof_info
.saved_timer
, NULL
) < 0)
303 if (__sigaction (SIGPROF
, &prof_info
.saved_action
, NULL
) < 0)
306 free (prof_info
.region
);
310 prof_info
.num_regions
= 0;
311 prof_info
.region
= NULL
;
312 prof_info
.overflow
= &default_overflow_region
;
314 for (i
= 0; i
< profcnt
; ++i
)
317 /* Sort in order of decreasing starting address: */
318 qsort (p
, profcnt
, sizeof (p
[0]), pcmp
);
320 /* Add regions in order of decreasing starting address: */
321 for (i
= 0; i
< profcnt
; ++i
)
322 if (add_region (p
[i
], (flags
& PROF_UINT
) != 0) < 0)
324 free (prof_info
.region
);
325 prof_info
.num_regions
= 0;
326 prof_info
.region
= NULL
;
330 if (prof_info
.num_regions
== 0)
333 prof_info
.last
= prof_info
.region
;
335 /* Install SIGPROF handler. */
336 if (flags
& PROF_UINT
)
337 act
.sa_handler
= (sighandler_t
) &profil_counter_uint
;
339 act
.sa_handler
= (sighandler_t
) &profil_counter_ushort
;
340 act
.sa_flags
= SA_RESTART
;
341 __sigfillset (&act
.sa_mask
);
342 if (__sigaction (SIGPROF
, &act
, &prof_info
.saved_action
) < 0)
345 /* Setup profiling timer. */
346 timer
.it_value
.tv_sec
= 0;
347 timer
.it_value
.tv_usec
= 1;
348 timer
.it_interval
= timer
.it_value
;
349 return __setitimer (ITIMER_PROF
, &timer
, &prof_info
.saved_timer
);
352 weak_alias (__sprofil
, sprofil
)