packaing: switch off build_html
[platform/upstream/glibc.git] / sysdeps / posix / sprofil.c
1 /* Copyright (C) 2001-2024 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17
18 #include <assert.h>
19 #include <signal.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <sigsetops.h>
24
25 #include <sys/time.h>
26 #include <sys/profil.h>
27
28 #ifndef SIGPROF
29 # include <gmon/sprofil.c>
30 #else
31
32 #include <libc-internal.h>
33
34 struct region
35   {
36     size_t offset;
37     size_t nsamples;
38     unsigned int scale;
39     union
40       {
41         void *vp;
42         unsigned short *us;
43         unsigned int *ui;
44       }
45     sample;
46     size_t start;
47     size_t end;
48   };
49
50 struct prof_info
51   {
52     unsigned int num_regions;
53     struct region *region;
54     struct region *last, *overflow;
55     struct itimerval saved_timer;
56     struct sigaction saved_action;
57   };
58
59 static unsigned int overflow_counter;
60
61 static struct region default_overflow_region =
62   {
63     .offset     = 0,
64     .nsamples   = 1,
65     .scale      = 2,
66     .sample     = { &overflow_counter },
67     .start      = 0,
68     .end        = ~(size_t) 0
69   };
70
71 static struct prof_info prof_info;
72
73 static unsigned long int
74 pc_to_index (size_t pc, size_t offset, unsigned int scale, int prof_uint)
75 {
76   size_t i = (pc - offset) / (prof_uint ? sizeof (int) : sizeof (short));
77
78   if (sizeof (unsigned long long int) > sizeof (size_t))
79     return (unsigned long long int) i * scale / 65536;
80   else
81     return i / 65536 * scale + i % 65536 * scale / 65536;
82 }
83
84 static inline size_t
85 index_to_pc (unsigned long int n, size_t offset, unsigned int scale,
86              int prof_uint)
87 {
88   size_t pc, bin_size = (prof_uint ? sizeof (int) : sizeof (short));
89
90   if (sizeof (unsigned long long int) > sizeof (size_t))
91     pc = offset + (unsigned long long int) n * bin_size * 65536ull / scale;
92   else
93     pc = (offset + n * bin_size / scale * 65536
94           + n * bin_size % scale * 65536 / scale);
95
96   if (pc_to_index (pc, offset, scale, prof_uint) < n)
97     /* Adjust for rounding error.  */
98     ++pc;
99
100   assert (pc_to_index (pc - 1, offset, scale, prof_uint) < n
101           && pc_to_index (pc, offset, scale, prof_uint) >= n);
102
103   return pc;
104 }
105
106 static void
107 profil_count (uintptr_t pcp, int prof_uint)
108 {
109   struct region *region, *r = prof_info.last;
110   size_t lo, hi, mid, pc = pcp;
111   unsigned long int i;
112
113   /* Fast path: pc is in same region as before.  */
114   if (pc >= r->start && pc < r->end)
115     region = r;
116   else
117     {
118       /* Slow path: do a binary search for the right region.  */
119       lo = 0; hi = prof_info.num_regions - 1;
120       while (lo <= hi)
121         {
122           mid = (lo + hi) / 2;
123
124           r = prof_info.region + mid;
125           if (pc >= r->start && pc < r->end)
126             {
127               prof_info.last = r;
128               region = r;
129               break;
130             }
131
132           if (pc < r->start)
133             hi = mid - 1;
134           else
135             lo = mid + 1;
136         }
137
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;
141     }
142
143   i = pc_to_index (pc, region->offset, region->scale, prof_uint);
144   if (i < r->nsamples)
145     {
146       if (prof_uint)
147         {
148           if (r->sample.ui[i] < (unsigned int) ~0)
149             ++r->sample.ui[i];
150         }
151       else
152         {
153           if (r->sample.us[i] < (unsigned short) ~0)
154             ++r->sample.us[i];
155         }
156     }
157   else
158     {
159       if (prof_uint)
160         ++prof_info.overflow->sample.ui[0];
161       else
162         ++prof_info.overflow->sample.us[0];
163     }
164 }
165
166 /* Get the machine-dependent definition of `__profil_counter', the signal
167    handler for SIGPROF.  It calls `profil_count' (above) with the PC of the
168    interrupted code.  */
169 #define __profil_counter        __profil_counter_ushort
170 #define profil_count(pc)        profil_count (pc, 0)
171 #include <profil-counter.h>
172
173 #undef __profil_counter
174 #undef profil_count
175
176 #define __profil_counter        __profil_counter_uint
177 #define profil_count(pc)        profil_count (pc, 1)
178 #include <profil-counter.h>
179
180 static int
181 insert (int i, unsigned long int start, unsigned long int end, struct prof *p,
182         int prof_uint)
183 {
184   struct region *r;
185   size_t to_copy;
186
187   if (start >= end)
188     return 0;           /* don't bother with empty regions */
189
190   if (prof_info.num_regions == 0)
191     r = malloc (sizeof (*r));
192   else
193     r = realloc (prof_info.region, (prof_info.num_regions + 1) * sizeof (*r));
194   if (r == NULL)
195     return -1;
196
197   to_copy = prof_info.num_regions - i;
198   if (to_copy > 0)
199     memmove (r + i + 1, r + i, to_copy * sizeof (*r));
200
201   r[i].offset = p->pr_off;
202   r[i].nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
203   r[i].scale = p->pr_scale;
204   r[i].sample.vp = p->pr_base;
205   r[i].start = start;
206   r[i].end = end;
207
208   prof_info.region = r;
209   ++prof_info.num_regions;
210
211   if (p->pr_off == 0 && p->pr_scale == 2)
212     prof_info.overflow = r;
213
214   return 0;
215 }
216
217 /* Add a new profiling region.  If the new region overlaps with
218    existing ones, this may add multiple subregions so that the final
219    data structure is free of overlaps.  The absence of overlaps makes
220    it possible to use a binary search in profil_count().  Note that
221    this function depends on new regions being presented in DECREASING
222    ORDER of starting address.  */
223
224 static int
225 add_region (struct prof *p, int prof_uint)
226 {
227   unsigned long int nsamples;
228   size_t start, end;
229   unsigned int i;
230
231   if (p->pr_scale < 2)
232     return 0;
233
234   nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
235
236   start = p->pr_off;
237   end = index_to_pc (nsamples, p->pr_off, p->pr_scale, prof_uint);
238
239   /* Merge with existing regions.  */
240   for (i = 0; i < prof_info.num_regions; ++i)
241     {
242       if (start < prof_info.region[i].start)
243         {
244           if (end < prof_info.region[i].start)
245             break;
246           else if (insert (i, start, prof_info.region[i].start, p, prof_uint)
247                    < 0)
248             return -1;
249         }
250       start = prof_info.region[i].end;
251     }
252   return insert (i, start, end, p, prof_uint);
253 }
254
255 static int
256 pcmp (const void *left, const void *right)
257 {
258   struct prof *l = *(struct prof **) left;
259   struct prof *r = *(struct prof **) right;
260
261   if (l->pr_off < r->pr_off)
262     return 1;
263   else if (l->pr_off > r->pr_off)
264     return -1;
265   return 0;
266 }
267
268 int
269 __sprofil (struct prof *profp, int profcnt, struct timeval *tvp,
270            unsigned int flags)
271 {
272   struct prof *p[profcnt];
273   struct itimerval timer;
274   struct sigaction act;
275   int i;
276
277   if (tvp != NULL)
278     {
279       /* Return profiling period.  */
280       unsigned long int t = 1000000 / __profile_frequency ();
281       tvp->tv_sec  = t / 1000000;
282       tvp->tv_usec = t % 1000000;
283     }
284
285   if (prof_info.num_regions > 0)
286     {
287       /* Disable profiling.  */
288       if (__setitimer (ITIMER_PROF, &prof_info.saved_timer, NULL) < 0)
289         return -1;
290
291       if (__sigaction (SIGPROF, &prof_info.saved_action, NULL) < 0)
292         return -1;
293
294       free (prof_info.region);
295       return 0;
296     }
297
298   prof_info.num_regions = 0;
299   prof_info.region = NULL;
300   prof_info.overflow = &default_overflow_region;
301
302   for (i = 0; i < profcnt; ++i)
303     p[i] = profp + i;
304
305   /* Sort in order of decreasing starting address: */
306   qsort (p, profcnt, sizeof (p[0]), pcmp);
307
308   /* Add regions in order of decreasing starting address: */
309   for (i = 0; i < profcnt; ++i)
310     if (add_region (p[i], (flags & PROF_UINT) != 0) < 0)
311       {
312         free (prof_info.region);
313         prof_info.num_regions = 0;
314         prof_info.region = NULL;
315         return -1;
316       }
317
318   if (prof_info.num_regions == 0)
319     return 0;
320
321   prof_info.last = prof_info.region;
322
323   /* Install SIGPROF handler.  */
324 #ifdef SA_SIGINFO
325   act.sa_sigaction= flags & PROF_UINT
326                     ? __profil_counter_uint
327                     : __profil_counter_ushort;
328   act.sa_flags = SA_SIGINFO;
329 #else
330   act.sa_handler = flags & PROF_UINT
331                    ? (sighandler_t) __profil_counter_uint
332                    : (sighandler_t) __profil_counter_ushort;
333   act.sa_flags = 0;
334 #endif
335   act.sa_flags |= SA_RESTART;
336   __sigfillset (&act.sa_mask);
337   if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0)
338     return -1;
339
340   /* Setup profiling timer.  */
341   timer.it_value.tv_sec  = 0;
342   timer.it_value.tv_usec = 1;
343   timer.it_interval = timer.it_value;
344   return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer);
345 }
346
347 weak_alias (__sprofil, sprofil)
348
349 #endif /* SIGPROF */