1 /* Copyright (C) 2001, 2002, 2003 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
27 #include <sys/profil.h>
30 # include <sysdeps/generic/sprofil.c>
33 #include <libc-internal.h>
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
=
67 .sample
= { &overflow_counter
},
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;
82 return i
/ 65536 * scale
+ i
% 65536 * scale
/ 65536;
86 index_to_pc (unsigned long int n
, size_t offset
, unsigned int scale
,
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
;
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. */
101 assert (pc_to_index (pc
- 1, offset
, scale
, prof_uint
) < n
102 && pc_to_index (pc
, offset
, scale
, prof_uint
) >= n
);
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
;
114 /* Fast path: pc is in same region as before. */
115 if (pc
>= r
->start
&& pc
< r
->end
)
119 /* Slow path: do a binary search for the right region. */
120 lo
= 0; hi
= prof_info
.num_regions
- 1;
125 r
= prof_info
.region
+ mid
;
126 if (pc
>= r
->start
&& pc
< r
->end
)
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
);
149 if (r
->sample
.ui
[i
] < (unsigned int) ~0)
154 if (r
->sample
.us
[i
] < (unsigned short) ~0)
161 ++prof_info
.overflow
->sample
.ui
[0];
163 ++prof_info
.overflow
->sample
.us
[0];
168 profil_count_ushort (void *pcp
)
170 profil_count (pcp
, 0);
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
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
189 #define profil_counter profil_counter_uint
190 #define profil_count(pc) profil_count (pc, 1)
191 #include "profil-counter.h"
194 insert (int i
, unsigned long int start
, unsigned long int end
, struct prof
*p
,
201 return 0; /* don't bother with empty regions */
203 if (prof_info
.num_regions
== 0)
204 r
= malloc (sizeof (*r
));
206 r
= realloc (prof_info
.region
, (prof_info
.num_regions
+ 1) * sizeof (*r
));
210 to_copy
= prof_info
.num_regions
- i
;
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
;
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
;
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. */
238 add_region (struct prof
*p
, int prof_uint
)
240 unsigned long int nsamples
;
247 nsamples
= p
->pr_size
/ (prof_uint
? sizeof (int) : sizeof (short));
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
)
259 else if (insert (i
, start
, prof_info
.region
[i
].start
, p
, prof_uint
)
263 start
= prof_info
.region
[i
].end
;
265 return insert (i
, start
, end
, p
, prof_uint
);
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
)
276 else if (l
->pr_off
> r
->pr_off
)
282 __sprofil (struct prof
*profp
, int profcnt
, struct timeval
*tvp
,
285 struct prof
*p
[profcnt
];
286 struct itimerval timer
;
287 struct sigaction act
;
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)
304 if (__sigaction (SIGPROF
, &prof_info
.saved_action
, NULL
) < 0)
307 free (prof_info
.region
);
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
)
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 if (prof_info
.region
)
326 free (prof_info
.region
);
327 prof_info
.num_regions
= 0;
328 prof_info
.region
= NULL
;
332 if (prof_info
.num_regions
== 0)
335 prof_info
.last
= prof_info
.region
;
337 /* Install SIGPROF handler. */
338 if (flags
& PROF_UINT
)
339 act
.sa_handler
= (sighandler_t
) &profil_counter_uint
;
341 act
.sa_handler
= (sighandler_t
) &profil_counter_ushort
;
342 act
.sa_flags
= SA_RESTART
;
343 __sigfillset (&act
.sa_mask
);
344 if (__sigaction (SIGPROF
, &act
, &prof_info
.saved_action
) < 0)
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
);
354 weak_alias (__sprofil
, sprofil
)