1  /* Copyright (C) 2001-2023 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 */