| 1 | /* Copyright (C) 2001-2019 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 |    <http://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 (void *pcp, int prof_uint) | 
| 109 | { | 
| 110 |   struct region *region, *r = prof_info.last; | 
| 111 |   size_t lo, hi, mid, pc = (unsigned long int) 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 (void *pcp) | 
| 169 | { | 
| 170 |   profil_count (pcp, 0); | 
| 171 | } | 
| 172 |  | 
| 173 | static inline void | 
| 174 | profil_count_uint (void *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 |   if (flags & PROF_UINT) | 
| 338 |     act.sa_handler = (sighandler_t) &__profil_counter_uint; | 
| 339 |   else | 
| 340 |     act.sa_handler = (sighandler_t) &__profil_counter_ushort; | 
| 341 |   act.sa_flags = SA_RESTART; | 
| 342 |   __sigfillset (&act.sa_mask); | 
| 343 |   if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0) | 
| 344 |     return -1; | 
| 345 |  | 
| 346 |   /* Setup profiling timer.  */ | 
| 347 |   timer.it_value.tv_sec  = 0; | 
| 348 |   timer.it_value.tv_usec = 1; | 
| 349 |   timer.it_interval = timer.it_value; | 
| 350 |   return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer); | 
| 351 | } | 
| 352 |  | 
| 353 | weak_alias (__sprofil, sprofil) | 
| 354 |  | 
| 355 | #endif /* SIGPROF */ | 
| 356 |  |