1 /* Copyright (C) 2001-2021 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 static inline void
profil_count_ushort(uintptr_t pcp)167 profil_count_ushort (uintptr_t pcp)
168 {
169   profil_count (pcp, 0);
170 }
171 
172 static inline void
profil_count_uint(uintptr_t pcp)173 profil_count_uint (uintptr_t pcp)
174 {
175   profil_count (pcp, 1);
176 }
177 
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
180    interrupted code.  */
181 #define __profil_counter	__profil_counter_ushort
182 #define profil_count(pc)	profil_count (pc, 0)
183 #include <profil-counter.h>
184 
185 #undef __profil_counter
186 #undef profil_count
187 
188 #define __profil_counter	__profil_counter_uint
189 #define profil_count(pc)	profil_count (pc, 1)
190 #include <profil-counter.h>
191 
192 static int
insert(int i,unsigned long int start,unsigned long int end,struct prof * p,int prof_uint)193 insert (int i, unsigned long int start, unsigned long int end, struct prof *p,
194 	int prof_uint)
195 {
196   struct region *r;
197   size_t to_copy;
198 
199   if (start >= end)
200     return 0;		/* don't bother with empty regions */
201 
202   if (prof_info.num_regions == 0)
203     r = malloc (sizeof (*r));
204   else
205     r = realloc (prof_info.region, (prof_info.num_regions + 1) * sizeof (*r));
206   if (r == NULL)
207     return -1;
208 
209   to_copy = prof_info.num_regions - i;
210   if (to_copy > 0)
211     memmove (r + i + 1, r + i, to_copy * sizeof (*r));
212 
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;
217   r[i].start = start;
218   r[i].end = end;
219 
220   prof_info.region = r;
221   ++prof_info.num_regions;
222 
223   if (p->pr_off == 0 && p->pr_scale == 2)
224     prof_info.overflow = r;
225 
226   return 0;
227 }
228 
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.  */
235 
236 static int
add_region(struct prof * p,int prof_uint)237 add_region (struct prof *p, int prof_uint)
238 {
239   unsigned long int nsamples;
240   size_t start, end;
241   unsigned int i;
242 
243   if (p->pr_scale < 2)
244     return 0;
245 
246   nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
247 
248   start = p->pr_off;
249   end = index_to_pc (nsamples, p->pr_off, p->pr_scale, prof_uint);
250 
251   /* Merge with existing regions.  */
252   for (i = 0; i < prof_info.num_regions; ++i)
253     {
254       if (start < prof_info.region[i].start)
255 	{
256 	  if (end < prof_info.region[i].start)
257 	    break;
258 	  else if (insert (i, start, prof_info.region[i].start, p, prof_uint)
259 		   < 0)
260 	    return -1;
261 	}
262       start = prof_info.region[i].end;
263     }
264   return insert (i, start, end, p, prof_uint);
265 }
266 
267 static int
pcmp(const void * left,const void * right)268 pcmp (const void *left, const void *right)
269 {
270   struct prof *l = *(struct prof **) left;
271   struct prof *r = *(struct prof **) right;
272 
273   if (l->pr_off < r->pr_off)
274     return 1;
275   else if (l->pr_off > r->pr_off)
276     return -1;
277   return 0;
278 }
279 
280 int
__sprofil(struct prof * profp,int profcnt,struct timeval * tvp,unsigned int flags)281 __sprofil (struct prof *profp, int profcnt, struct timeval *tvp,
282 	   unsigned int flags)
283 {
284   struct prof *p[profcnt];
285   struct itimerval timer;
286   struct sigaction act;
287   int i;
288 
289   if (tvp != NULL)
290     {
291       /* Return profiling period.  */
292       unsigned long int t = 1000000 / __profile_frequency ();
293       tvp->tv_sec  = t / 1000000;
294       tvp->tv_usec = t % 1000000;
295     }
296 
297   if (prof_info.num_regions > 0)
298     {
299       /* Disable profiling.  */
300       if (__setitimer (ITIMER_PROF, &prof_info.saved_timer, NULL) < 0)
301 	return -1;
302 
303       if (__sigaction (SIGPROF, &prof_info.saved_action, NULL) < 0)
304 	return -1;
305 
306       free (prof_info.region);
307       return 0;
308     }
309 
310   prof_info.num_regions = 0;
311   prof_info.region = NULL;
312   prof_info.overflow = &default_overflow_region;
313 
314   for (i = 0; i < profcnt; ++i)
315     p[i] = profp + i;
316 
317   /* Sort in order of decreasing starting address: */
318   qsort (p, profcnt, sizeof (p[0]), pcmp);
319 
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)
323       {
324 	free (prof_info.region);
325 	prof_info.num_regions = 0;
326 	prof_info.region = NULL;
327 	return -1;
328       }
329 
330   if (prof_info.num_regions == 0)
331     return 0;
332 
333   prof_info.last = prof_info.region;
334 
335   /* Install SIGPROF handler.  */
336 #ifdef SA_SIGINFO
337   act.sa_sigaction= flags & PROF_UINT
338 		    ? __profil_counter_uint
339 		    : __profil_counter_ushort;
340   act.sa_flags = SA_SIGINFO;
341 #else
342   act.sa_handler = flags & PROF_UINT
343 		   ? (sighandler_t) __profil_counter_uint
344 		   : (sighandler_t) __profil_counter_ushort;
345   act.sa_flags = 0;
346 #endif
347   act.sa_flags |= SA_RESTART;
348   __sigfillset (&act.sa_mask);
349   if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0)
350     return -1;
351 
352   /* Setup profiling timer.  */
353   timer.it_value.tv_sec  = 0;
354   timer.it_value.tv_usec = 1;
355   timer.it_interval = timer.it_value;
356   return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer);
357 }
358 
359 weak_alias (__sprofil, sprofil)
360 
361 #endif /* SIGPROF */
362