| 1 | /* Copyright (C) 2001-2020 Free Software Foundation, Inc. | 
|---|
| 2 | Contributed by David Mosberger-Tang <davidm@hpl.hp.com>. | 
|---|
| 3 | This file is part of the GNU C Library. | 
|---|
| 4 |  | 
|---|
| 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. | 
|---|
| 9 |  | 
|---|
| 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. | 
|---|
| 14 |  | 
|---|
| 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 | <https://www.gnu.org/licenses/>.  */ | 
|---|
| 18 |  | 
|---|
| 19 | #include <assert.h> | 
|---|
| 20 | #include <signal.h> | 
|---|
| 21 | #include <stdlib.h> | 
|---|
| 22 | #include <string.h> | 
|---|
| 23 | #include <unistd.h> | 
|---|
| 24 | #include <sigsetops.h> | 
|---|
| 25 |  | 
|---|
| 26 | #include <sys/time.h> | 
|---|
| 27 | #include <sys/profil.h> | 
|---|
| 28 |  | 
|---|
| 29 | #ifndef SIGPROF | 
|---|
| 30 | # include <gmon/sprofil.c> | 
|---|
| 31 | #else | 
|---|
| 32 |  | 
|---|
| 33 | #include <libc-internal.h> | 
|---|
| 34 |  | 
|---|
| 35 | struct region | 
|---|
| 36 | { | 
|---|
| 37 | size_t offset; | 
|---|
| 38 | size_t nsamples; | 
|---|
| 39 | unsigned int scale; | 
|---|
| 40 | union | 
|---|
| 41 | { | 
|---|
| 42 | void *vp; | 
|---|
| 43 | unsigned short *us; | 
|---|
| 44 | unsigned int *ui; | 
|---|
| 45 | } | 
|---|
| 46 | sample; | 
|---|
| 47 | size_t start; | 
|---|
| 48 | size_t end; | 
|---|
| 49 | }; | 
|---|
| 50 |  | 
|---|
| 51 | struct prof_info | 
|---|
| 52 | { | 
|---|
| 53 | unsigned int num_regions; | 
|---|
| 54 | struct region *region; | 
|---|
| 55 | struct region *last, *overflow; | 
|---|
| 56 | struct itimerval saved_timer; | 
|---|
| 57 | struct sigaction saved_action; | 
|---|
| 58 | }; | 
|---|
| 59 |  | 
|---|
| 60 | static unsigned int overflow_counter; | 
|---|
| 61 |  | 
|---|
| 62 | static struct region default_overflow_region = | 
|---|
| 63 | { | 
|---|
| 64 | .offset	= 0, | 
|---|
| 65 | .nsamples	= 1, | 
|---|
| 66 | .scale	= 2, | 
|---|
| 67 | .sample	= { &overflow_counter }, | 
|---|
| 68 | .start	= 0, | 
|---|
| 69 | .end	= ~(size_t) 0 | 
|---|
| 70 | }; | 
|---|
| 71 |  | 
|---|
| 72 | static struct prof_info prof_info; | 
|---|
| 73 |  | 
|---|
| 74 | static unsigned long int | 
|---|
| 75 | pc_to_index (size_t pc, size_t offset, unsigned int scale, int prof_uint) | 
|---|
| 76 | { | 
|---|
| 77 | size_t i = (pc - offset) / (prof_uint ? sizeof (int) : sizeof (short)); | 
|---|
| 78 |  | 
|---|
| 79 | if (sizeof (unsigned long long int) > sizeof (size_t)) | 
|---|
| 80 | return (unsigned long long int) i * scale / 65536; | 
|---|
| 81 | else | 
|---|
| 82 | return i / 65536 * scale + i % 65536 * scale / 65536; | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | static inline size_t | 
|---|
| 86 | index_to_pc (unsigned long int n, size_t offset, unsigned int scale, | 
|---|
| 87 | int prof_uint) | 
|---|
| 88 | { | 
|---|
| 89 | size_t pc, bin_size = (prof_uint ? sizeof (int) : sizeof (short)); | 
|---|
| 90 |  | 
|---|
| 91 | if (sizeof (unsigned long long int) > sizeof (size_t)) | 
|---|
| 92 | pc = offset + (unsigned long long int) n * bin_size * 65536ull / scale; | 
|---|
| 93 | else | 
|---|
| 94 | pc = (offset + n * bin_size / scale * 65536 | 
|---|
| 95 | + n * bin_size % scale * 65536 / scale); | 
|---|
| 96 |  | 
|---|
| 97 | if (pc_to_index (pc, offset, scale, prof_uint) < n) | 
|---|
| 98 | /* Adjust for rounding error.  */ | 
|---|
| 99 | ++pc; | 
|---|
| 100 |  | 
|---|
| 101 | assert (pc_to_index (pc - 1, offset, scale, prof_uint) < n | 
|---|
| 102 | && pc_to_index (pc, offset, scale, prof_uint) >= n); | 
|---|
| 103 |  | 
|---|
| 104 | return pc; | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | static void | 
|---|
| 108 | profil_count (uintptr_t pcp, int prof_uint) | 
|---|
| 109 | { | 
|---|
| 110 | struct region *region, *r = prof_info.last; | 
|---|
| 111 | size_t lo, hi, mid, pc = pcp; | 
|---|
| 112 | unsigned long int i; | 
|---|
| 113 |  | 
|---|
| 114 | /* Fast path: pc is in same region as before.  */ | 
|---|
| 115 | if (pc >= r->start && pc < r->end) | 
|---|
| 116 | region = r; | 
|---|
| 117 | else | 
|---|
| 118 | { | 
|---|
| 119 | /* Slow path: do a binary search for the right region.  */ | 
|---|
| 120 | lo = 0; hi = prof_info.num_regions - 1; | 
|---|
| 121 | while (lo <= hi) | 
|---|
| 122 | { | 
|---|
| 123 | mid = (lo + hi) / 2; | 
|---|
| 124 |  | 
|---|
| 125 | r = prof_info.region + mid; | 
|---|
| 126 | if (pc >= r->start && pc < r->end) | 
|---|
| 127 | { | 
|---|
| 128 | prof_info.last = r; | 
|---|
| 129 | region = r; | 
|---|
| 130 | break; | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | if (pc < r->start) | 
|---|
| 134 | hi = mid - 1; | 
|---|
| 135 | else | 
|---|
| 136 | lo = mid + 1; | 
|---|
| 137 | } | 
|---|
| 138 |  | 
|---|
| 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; | 
|---|
| 142 | } | 
|---|
| 143 |  | 
|---|
| 144 | i = pc_to_index (pc, region->offset, region->scale, prof_uint); | 
|---|
| 145 | if (i < r->nsamples) | 
|---|
| 146 | { | 
|---|
| 147 | if (prof_uint) | 
|---|
| 148 | { | 
|---|
| 149 | if (r->sample.ui[i] < (unsigned int) ~0) | 
|---|
| 150 | ++r->sample.ui[i]; | 
|---|
| 151 | } | 
|---|
| 152 | else | 
|---|
| 153 | { | 
|---|
| 154 | if (r->sample.us[i] < (unsigned short) ~0) | 
|---|
| 155 | ++r->sample.us[i]; | 
|---|
| 156 | } | 
|---|
| 157 | } | 
|---|
| 158 | else | 
|---|
| 159 | { | 
|---|
| 160 | if (prof_uint) | 
|---|
| 161 | ++prof_info.overflow->sample.ui[0]; | 
|---|
| 162 | else | 
|---|
| 163 | ++prof_info.overflow->sample.us[0]; | 
|---|
| 164 | } | 
|---|
| 165 | } | 
|---|
| 166 |  | 
|---|
| 167 | static inline void | 
|---|
| 168 | profil_count_ushort (uintptr_t pcp) | 
|---|
| 169 | { | 
|---|
| 170 | profil_count (pcp, 0); | 
|---|
| 171 | } | 
|---|
| 172 |  | 
|---|
| 173 | static inline void | 
|---|
| 174 | profil_count_uint (uintptr_t pcp) | 
|---|
| 175 | { | 
|---|
| 176 | profil_count (pcp, 1); | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 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 | 
|---|
| 181 | interrupted code.  */ | 
|---|
| 182 | #define __profil_counter	__profil_counter_ushort | 
|---|
| 183 | #define profil_count(pc)	profil_count (pc, 0) | 
|---|
| 184 | #include <profil-counter.h> | 
|---|
| 185 |  | 
|---|
| 186 | #undef __profil_counter | 
|---|
| 187 | #undef profil_count | 
|---|
| 188 |  | 
|---|
| 189 | #define __profil_counter	__profil_counter_uint | 
|---|
| 190 | #define profil_count(pc)	profil_count (pc, 1) | 
|---|
| 191 | #include <profil-counter.h> | 
|---|
| 192 |  | 
|---|
| 193 | static int | 
|---|
| 194 | insert (int i, unsigned long int start, unsigned long int end, struct prof *p, | 
|---|
| 195 | int prof_uint) | 
|---|
| 196 | { | 
|---|
| 197 | struct region *r; | 
|---|
| 198 | size_t to_copy; | 
|---|
| 199 |  | 
|---|
| 200 | if (start >= end) | 
|---|
| 201 | return 0;		/* don't bother with empty regions */ | 
|---|
| 202 |  | 
|---|
| 203 | if (prof_info.num_regions == 0) | 
|---|
| 204 | r = malloc (sizeof (*r)); | 
|---|
| 205 | else | 
|---|
| 206 | r = realloc (prof_info.region, (prof_info.num_regions + 1) * sizeof (*r)); | 
|---|
| 207 | if (r == NULL) | 
|---|
| 208 | return -1; | 
|---|
| 209 |  | 
|---|
| 210 | to_copy = prof_info.num_regions - i; | 
|---|
| 211 | if (to_copy > 0) | 
|---|
| 212 | memmove (r + i + 1, r + i, to_copy * sizeof (*r)); | 
|---|
| 213 |  | 
|---|
| 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; | 
|---|
| 218 | r[i].start = start; | 
|---|
| 219 | r[i].end = end; | 
|---|
| 220 |  | 
|---|
| 221 | prof_info.region = r; | 
|---|
| 222 | ++prof_info.num_regions; | 
|---|
| 223 |  | 
|---|
| 224 | if (p->pr_off == 0 && p->pr_scale == 2) | 
|---|
| 225 | prof_info.overflow = r; | 
|---|
| 226 |  | 
|---|
| 227 | return 0; | 
|---|
| 228 | } | 
|---|
| 229 |  | 
|---|
| 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.  */ | 
|---|
| 236 |  | 
|---|
| 237 | static int | 
|---|
| 238 | add_region (struct prof *p, int prof_uint) | 
|---|
| 239 | { | 
|---|
| 240 | unsigned long int nsamples; | 
|---|
| 241 | size_t start, end; | 
|---|
| 242 | unsigned int i; | 
|---|
| 243 |  | 
|---|
| 244 | if (p->pr_scale < 2) | 
|---|
| 245 | return 0; | 
|---|
| 246 |  | 
|---|
| 247 | nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short)); | 
|---|
| 248 |  | 
|---|
| 249 | start = p->pr_off; | 
|---|
| 250 | end = index_to_pc (nsamples, p->pr_off, p->pr_scale, prof_uint); | 
|---|
| 251 |  | 
|---|
| 252 | /* Merge with existing regions.  */ | 
|---|
| 253 | for (i = 0; i < prof_info.num_regions; ++i) | 
|---|
| 254 | { | 
|---|
| 255 | if (start < prof_info.region[i].start) | 
|---|
| 256 | { | 
|---|
| 257 | if (end < prof_info.region[i].start) | 
|---|
| 258 | break; | 
|---|
| 259 | else if (insert (i, start, prof_info.region[i].start, p, prof_uint) | 
|---|
| 260 | < 0) | 
|---|
| 261 | return -1; | 
|---|
| 262 | } | 
|---|
| 263 | start = prof_info.region[i].end; | 
|---|
| 264 | } | 
|---|
| 265 | return insert (i, start, end, p, prof_uint); | 
|---|
| 266 | } | 
|---|
| 267 |  | 
|---|
| 268 | static int | 
|---|
| 269 | pcmp (const void *left, const void *right) | 
|---|
| 270 | { | 
|---|
| 271 | struct prof *l = *(struct prof **) left; | 
|---|
| 272 | struct prof *r = *(struct prof **) right; | 
|---|
| 273 |  | 
|---|
| 274 | if (l->pr_off < r->pr_off) | 
|---|
| 275 | return 1; | 
|---|
| 276 | else if (l->pr_off > r->pr_off) | 
|---|
| 277 | return -1; | 
|---|
| 278 | return 0; | 
|---|
| 279 | } | 
|---|
| 280 |  | 
|---|
| 281 | int | 
|---|
| 282 | __sprofil (struct prof *profp, int profcnt, struct timeval *tvp, | 
|---|
| 283 | unsigned int flags) | 
|---|
| 284 | { | 
|---|
| 285 | struct prof *p[profcnt]; | 
|---|
| 286 | struct itimerval timer; | 
|---|
| 287 | struct sigaction act; | 
|---|
| 288 | int i; | 
|---|
| 289 |  | 
|---|
| 290 | if (tvp != NULL) | 
|---|
| 291 | { | 
|---|
| 292 | /* Return profiling period.  */ | 
|---|
| 293 | unsigned long int t = 1000000 / __profile_frequency (); | 
|---|
| 294 | tvp->tv_sec  = t / 1000000; | 
|---|
| 295 | tvp->tv_usec = t % 1000000; | 
|---|
| 296 | } | 
|---|
| 297 |  | 
|---|
| 298 | if (prof_info.num_regions > 0) | 
|---|
| 299 | { | 
|---|
| 300 | /* Disable profiling.  */ | 
|---|
| 301 | if (__setitimer (ITIMER_PROF, &prof_info.saved_timer, NULL) < 0) | 
|---|
| 302 | return -1; | 
|---|
| 303 |  | 
|---|
| 304 | if (__sigaction (SIGPROF, &prof_info.saved_action, NULL) < 0) | 
|---|
| 305 | return -1; | 
|---|
| 306 |  | 
|---|
| 307 | free (prof_info.region); | 
|---|
| 308 | return 0; | 
|---|
| 309 | } | 
|---|
| 310 |  | 
|---|
| 311 | prof_info.num_regions = 0; | 
|---|
| 312 | prof_info.region = NULL; | 
|---|
| 313 | prof_info.overflow = &default_overflow_region; | 
|---|
| 314 |  | 
|---|
| 315 | for (i = 0; i < profcnt; ++i) | 
|---|
| 316 | p[i] = profp + i; | 
|---|
| 317 |  | 
|---|
| 318 | /* Sort in order of decreasing starting address: */ | 
|---|
| 319 | qsort (p, profcnt, sizeof (p[0]), pcmp); | 
|---|
| 320 |  | 
|---|
| 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) | 
|---|
| 324 | { | 
|---|
| 325 | free (prof_info.region); | 
|---|
| 326 | prof_info.num_regions = 0; | 
|---|
| 327 | prof_info.region = NULL; | 
|---|
| 328 | return -1; | 
|---|
| 329 | } | 
|---|
| 330 |  | 
|---|
| 331 | if (prof_info.num_regions == 0) | 
|---|
| 332 | return 0; | 
|---|
| 333 |  | 
|---|
| 334 | prof_info.last = prof_info.region; | 
|---|
| 335 |  | 
|---|
| 336 | /* Install SIGPROF handler.  */ | 
|---|
| 337 | #ifdef SA_SIGINFO | 
|---|
| 338 | act.sa_sigaction= flags & PROF_UINT | 
|---|
| 339 | ? __profil_counter_uint | 
|---|
| 340 | : __profil_counter_ushort; | 
|---|
| 341 | act.sa_flags = SA_SIGINFO; | 
|---|
| 342 | #else | 
|---|
| 343 | act.sa_handler = flags & PROF_UINT | 
|---|
| 344 | ? (sighandler_t) __profil_counter_uint | 
|---|
| 345 | : (sighandler_t) __profil_counter_ushort; | 
|---|
| 346 | act.sa_flags = 0; | 
|---|
| 347 | #endif | 
|---|
| 348 | act.sa_flags |= SA_RESTART; | 
|---|
| 349 | __sigfillset (&act.sa_mask); | 
|---|
| 350 | if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0) | 
|---|
| 351 | return -1; | 
|---|
| 352 |  | 
|---|
| 353 | /* Setup profiling timer.  */ | 
|---|
| 354 | timer.it_value.tv_sec  = 0; | 
|---|
| 355 | timer.it_value.tv_usec = 1; | 
|---|
| 356 | timer.it_interval = timer.it_value; | 
|---|
| 357 | return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer); | 
|---|
| 358 | } | 
|---|
| 359 |  | 
|---|
| 360 | weak_alias (__sprofil, sprofil) | 
|---|
| 361 |  | 
|---|
| 362 | #endif /* SIGPROF */ | 
|---|
| 363 |  | 
|---|