1 /* Copyright (C) 2001-2022 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
pc_to_index(size_t pc,size_t offset,unsigned int scale,int prof_uint)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
index_to_pc(unsigned long int n,size_t offset,unsigned int scale,int prof_uint)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
profil_count(uintptr_t pcp,int prof_uint)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
insert(int i,unsigned long int start,unsigned long int end,struct prof * p,int prof_uint)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
add_region(struct prof * p,int prof_uint)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
pcmp(const void * left,const void * right)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
__sprofil(struct prof * profp,int profcnt,struct timeval * tvp,unsigned int flags)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 */
350