Blame sysdeps/posix/sprofil.c

Packit Service 82fcde
/* Copyright (C) 2001-2018 Free Software Foundation, Inc.
Packit Service 82fcde
   Contributed by David Mosberger-Tang <davidm@hpl.hp.com>.
Packit Service 82fcde
   This file is part of the GNU C Library.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is free software; you can redistribute it and/or
Packit Service 82fcde
   modify it under the terms of the GNU Lesser General Public
Packit Service 82fcde
   License as published by the Free Software Foundation; either
Packit Service 82fcde
   version 2.1 of the License, or (at your option) any later version.
Packit Service 82fcde
Packit Service 82fcde
   The GNU C Library is distributed in the hope that it will be useful,
Packit Service 82fcde
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 82fcde
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 82fcde
   Lesser General Public License for more details.
Packit Service 82fcde
Packit Service 82fcde
   You should have received a copy of the GNU Lesser General Public
Packit Service 82fcde
   License along with the GNU C Library; if not, see
Packit Service 82fcde
   <http://www.gnu.org/licenses/>.  */
Packit Service 82fcde
Packit Service 82fcde
#include <assert.h>
Packit Service 82fcde
#include <signal.h>
Packit Service 82fcde
#include <stdlib.h>
Packit Service 82fcde
#include <string.h>
Packit Service 82fcde
#include <unistd.h>
Packit Service 82fcde
#include <sigsetops.h>
Packit Service 82fcde
Packit Service 82fcde
#include <sys/time.h>
Packit Service 82fcde
#include <sys/profil.h>
Packit Service 82fcde
Packit Service 82fcde
#ifndef SIGPROF
Packit Service 82fcde
# include <gmon/sprofil.c>
Packit Service 82fcde
#else
Packit Service 82fcde
Packit Service 82fcde
#include <libc-internal.h>
Packit Service 82fcde
Packit Service 82fcde
struct region
Packit Service 82fcde
  {
Packit Service 82fcde
    size_t offset;
Packit Service 82fcde
    size_t nsamples;
Packit Service 82fcde
    unsigned int scale;
Packit Service 82fcde
    union
Packit Service 82fcde
      {
Packit Service 82fcde
	void *vp;
Packit Service 82fcde
	unsigned short *us;
Packit Service 82fcde
	unsigned int *ui;
Packit Service 82fcde
      }
Packit Service 82fcde
    sample;
Packit Service 82fcde
    size_t start;
Packit Service 82fcde
    size_t end;
Packit Service 82fcde
  };
Packit Service 82fcde
Packit Service 82fcde
struct prof_info
Packit Service 82fcde
  {
Packit Service 82fcde
    unsigned int num_regions;
Packit Service 82fcde
    struct region *region;
Packit Service 82fcde
    struct region *last, *overflow;
Packit Service 82fcde
    struct itimerval saved_timer;
Packit Service 82fcde
    struct sigaction saved_action;
Packit Service 82fcde
  };
Packit Service 82fcde
Packit Service 82fcde
static unsigned int overflow_counter;
Packit Service 82fcde
Packit Service 82fcde
static struct region default_overflow_region =
Packit Service 82fcde
  {
Packit Service 82fcde
    .offset	= 0,
Packit Service 82fcde
    .nsamples	= 1,
Packit Service 82fcde
    .scale	= 2,
Packit Service 82fcde
    .sample	= { &overflow_counter },
Packit Service 82fcde
    .start	= 0,
Packit Service 82fcde
    .end	= ~(size_t) 0
Packit Service 82fcde
  };
Packit Service 82fcde
Packit Service 82fcde
static struct prof_info prof_info;
Packit Service 82fcde
Packit Service 82fcde
static unsigned long int
Packit Service 82fcde
pc_to_index (size_t pc, size_t offset, unsigned int scale, int prof_uint)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t i = (pc - offset) / (prof_uint ? sizeof (int) : sizeof (short));
Packit Service 82fcde
Packit Service 82fcde
  if (sizeof (unsigned long long int) > sizeof (size_t))
Packit Service 82fcde
    return (unsigned long long int) i * scale / 65536;
Packit Service 82fcde
  else
Packit Service 82fcde
    return i / 65536 * scale + i % 65536 * scale / 65536;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static inline size_t
Packit Service 82fcde
index_to_pc (unsigned long int n, size_t offset, unsigned int scale,
Packit Service 82fcde
	     int prof_uint)
Packit Service 82fcde
{
Packit Service 82fcde
  size_t pc, bin_size = (prof_uint ? sizeof (int) : sizeof (short));
Packit Service 82fcde
Packit Service 82fcde
  if (sizeof (unsigned long long int) > sizeof (size_t))
Packit Service 82fcde
    pc = offset + (unsigned long long int) n * bin_size * 65536ull / scale;
Packit Service 82fcde
  else
Packit Service 82fcde
    pc = (offset + n * bin_size / scale * 65536
Packit Service 82fcde
	  + n * bin_size % scale * 65536 / scale);
Packit Service 82fcde
Packit Service 82fcde
  if (pc_to_index (pc, offset, scale, prof_uint) < n)
Packit Service 82fcde
    /* Adjust for rounding error.  */
Packit Service 82fcde
    ++pc;
Packit Service 82fcde
Packit Service 82fcde
  assert (pc_to_index (pc - 1, offset, scale, prof_uint) < n
Packit Service 82fcde
	  && pc_to_index (pc, offset, scale, prof_uint) >= n);
Packit Service 82fcde
Packit Service 82fcde
  return pc;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static void
Packit Service 82fcde
profil_count (void *pcp, int prof_uint)
Packit Service 82fcde
{
Packit Service 82fcde
  struct region *region, *r = prof_info.last;
Packit Service 82fcde
  size_t lo, hi, mid, pc = (unsigned long int) pcp;
Packit Service 82fcde
  unsigned long int i;
Packit Service 82fcde
Packit Service 82fcde
  /* Fast path: pc is in same region as before.  */
Packit Service 82fcde
  if (pc >= r->start && pc < r->end)
Packit Service 82fcde
    region = r;
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Slow path: do a binary search for the right region.  */
Packit Service 82fcde
      lo = 0; hi = prof_info.num_regions - 1;
Packit Service 82fcde
      while (lo <= hi)
Packit Service 82fcde
	{
Packit Service 82fcde
	  mid = (lo + hi) / 2;
Packit Service 82fcde
Packit Service 82fcde
	  r = prof_info.region + mid;
Packit Service 82fcde
	  if (pc >= r->start && pc < r->end)
Packit Service 82fcde
	    {
Packit Service 82fcde
	      prof_info.last = r;
Packit Service 82fcde
	      region = r;
Packit Service 82fcde
	      break;
Packit Service 82fcde
	    }
Packit Service 82fcde
Packit Service 82fcde
	  if (pc < r->start)
Packit Service 82fcde
	    hi = mid - 1;
Packit Service 82fcde
	  else
Packit Service 82fcde
	    lo = mid + 1;
Packit Service 82fcde
	}
Packit Service 82fcde
Packit Service 82fcde
      /* No matching region: increment overflow count.  There is no point
Packit Service 82fcde
	 in updating the cache here, as it won't hit anyhow.  */
Packit Service 82fcde
      region = prof_info.overflow;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  i = pc_to_index (pc, region->offset, region->scale, prof_uint);
Packit Service 82fcde
  if (i < r->nsamples)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (prof_uint)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (r->sample.ui[i] < (unsigned int) ~0)
Packit Service 82fcde
	    ++r->sample.ui[i];
Packit Service 82fcde
	}
Packit Service 82fcde
      else
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (r->sample.us[i] < (unsigned short) ~0)
Packit Service 82fcde
	    ++r->sample.us[i];
Packit Service 82fcde
	}
Packit Service 82fcde
    }
Packit Service 82fcde
  else
Packit Service 82fcde
    {
Packit Service 82fcde
      if (prof_uint)
Packit Service 82fcde
	++prof_info.overflow->sample.ui[0];
Packit Service 82fcde
      else
Packit Service 82fcde
	++prof_info.overflow->sample.us[0];
Packit Service 82fcde
    }
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static inline void
Packit Service 82fcde
profil_count_ushort (void *pcp)
Packit Service 82fcde
{
Packit Service 82fcde
  profil_count (pcp, 0);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static inline void
Packit Service 82fcde
profil_count_uint (void *pcp)
Packit Service 82fcde
{
Packit Service 82fcde
  profil_count (pcp, 1);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Get the machine-dependent definition of `__profil_counter', the signal
Packit Service 82fcde
   handler for SIGPROF.  It calls `profil_count' (above) with the PC of the
Packit Service 82fcde
   interrupted code.  */
Packit Service 82fcde
#define __profil_counter	__profil_counter_ushort
Packit Service 82fcde
#define profil_count(pc)	profil_count (pc, 0)
Packit Service 82fcde
#include <profil-counter.h>
Packit Service 82fcde
Packit Service 82fcde
#undef __profil_counter
Packit Service 82fcde
#undef profil_count
Packit Service 82fcde
Packit Service 82fcde
#define __profil_counter	__profil_counter_uint
Packit Service 82fcde
#define profil_count(pc)	profil_count (pc, 1)
Packit Service 82fcde
#include <profil-counter.h>
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
insert (int i, unsigned long int start, unsigned long int end, struct prof *p,
Packit Service 82fcde
	int prof_uint)
Packit Service 82fcde
{
Packit Service 82fcde
  struct region *r;
Packit Service 82fcde
  size_t to_copy;
Packit Service 82fcde
Packit Service 82fcde
  if (start >= end)
Packit Service 82fcde
    return 0;		/* don't bother with empty regions */
Packit Service 82fcde
Packit Service 82fcde
  if (prof_info.num_regions == 0)
Packit Service 82fcde
    r = malloc (sizeof (*r));
Packit Service 82fcde
  else
Packit Service 82fcde
    r = realloc (prof_info.region, (prof_info.num_regions + 1) * sizeof (*r));
Packit Service 82fcde
  if (r == NULL)
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  to_copy = prof_info.num_regions - i;
Packit Service 82fcde
  if (to_copy > 0)
Packit Service 82fcde
    memmove (r + i + 1, r + i, to_copy * sizeof (*r));
Packit Service 82fcde
Packit Service 82fcde
  r[i].offset = p->pr_off;
Packit Service 82fcde
  r[i].nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
Packit Service 82fcde
  r[i].scale = p->pr_scale;
Packit Service 82fcde
  r[i].sample.vp = p->pr_base;
Packit Service 82fcde
  r[i].start = start;
Packit Service 82fcde
  r[i].end = end;
Packit Service 82fcde
Packit Service 82fcde
  prof_info.region = r;
Packit Service 82fcde
  ++prof_info.num_regions;
Packit Service 82fcde
Packit Service 82fcde
  if (p->pr_off == 0 && p->pr_scale == 2)
Packit Service 82fcde
    prof_info.overflow = r;
Packit Service 82fcde
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
/* Add a new profiling region.  If the new region overlaps with
Packit Service 82fcde
   existing ones, this may add multiple subregions so that the final
Packit Service 82fcde
   data structure is free of overlaps.  The absence of overlaps makes
Packit Service 82fcde
   it possible to use a binary search in profil_count().  Note that
Packit Service 82fcde
   this function depends on new regions being presented in DECREASING
Packit Service 82fcde
   ORDER of starting address.  */
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
add_region (struct prof *p, int prof_uint)
Packit Service 82fcde
{
Packit Service 82fcde
  unsigned long int nsamples;
Packit Service 82fcde
  size_t start, end;
Packit Service 82fcde
  unsigned int i;
Packit Service 82fcde
Packit Service 82fcde
  if (p->pr_scale < 2)
Packit Service 82fcde
    return 0;
Packit Service 82fcde
Packit Service 82fcde
  nsamples = p->pr_size / (prof_uint ? sizeof (int) : sizeof (short));
Packit Service 82fcde
Packit Service 82fcde
  start = p->pr_off;
Packit Service 82fcde
  end = index_to_pc (nsamples, p->pr_off, p->pr_scale, prof_uint);
Packit Service 82fcde
Packit Service 82fcde
  /* Merge with existing regions.  */
Packit Service 82fcde
  for (i = 0; i < prof_info.num_regions; ++i)
Packit Service 82fcde
    {
Packit Service 82fcde
      if (start < prof_info.region[i].start)
Packit Service 82fcde
	{
Packit Service 82fcde
	  if (end < prof_info.region[i].start)
Packit Service 82fcde
	    break;
Packit Service 82fcde
	  else if (insert (i, start, prof_info.region[i].start, p, prof_uint)
Packit Service 82fcde
		   < 0)
Packit Service 82fcde
	    return -1;
Packit Service 82fcde
	}
Packit Service 82fcde
      start = prof_info.region[i].end;
Packit Service 82fcde
    }
Packit Service 82fcde
  return insert (i, start, end, p, prof_uint);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
static int
Packit Service 82fcde
pcmp (const void *left, const void *right)
Packit Service 82fcde
{
Packit Service 82fcde
  struct prof *l = *(struct prof **) left;
Packit Service 82fcde
  struct prof *r = *(struct prof **) right;
Packit Service 82fcde
Packit Service 82fcde
  if (l->pr_off < r->pr_off)
Packit Service 82fcde
    return 1;
Packit Service 82fcde
  else if (l->pr_off > r->pr_off)
Packit Service 82fcde
    return -1;
Packit Service 82fcde
  return 0;
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
int
Packit Service 82fcde
__sprofil (struct prof *profp, int profcnt, struct timeval *tvp,
Packit Service 82fcde
	   unsigned int flags)
Packit Service 82fcde
{
Packit Service 82fcde
  struct prof *p[profcnt];
Packit Service 82fcde
  struct itimerval timer;
Packit Service 82fcde
  struct sigaction act;
Packit Service 82fcde
  int i;
Packit Service 82fcde
Packit Service 82fcde
  if (tvp != NULL)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Return profiling period.  */
Packit Service 82fcde
      unsigned long int t = 1000000 / __profile_frequency ();
Packit Service 82fcde
      tvp->tv_sec  = t / 1000000;
Packit Service 82fcde
      tvp->tv_usec = t % 1000000;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  if (prof_info.num_regions > 0)
Packit Service 82fcde
    {
Packit Service 82fcde
      /* Disable profiling.  */
Packit Service 82fcde
      if (__setitimer (ITIMER_PROF, &prof_info.saved_timer, NULL) < 0)
Packit Service 82fcde
	return -1;
Packit Service 82fcde
Packit Service 82fcde
      if (__sigaction (SIGPROF, &prof_info.saved_action, NULL) < 0)
Packit Service 82fcde
	return -1;
Packit Service 82fcde
Packit Service 82fcde
      free (prof_info.region);
Packit Service 82fcde
      return 0;
Packit Service 82fcde
    }
Packit Service 82fcde
Packit Service 82fcde
  prof_info.num_regions = 0;
Packit Service 82fcde
  prof_info.region = NULL;
Packit Service 82fcde
  prof_info.overflow = &default_overflow_region;
Packit Service 82fcde
Packit Service 82fcde
  for (i = 0; i < profcnt; ++i)
Packit Service 82fcde
    p[i] = profp + i;
Packit Service 82fcde
Packit Service 82fcde
  /* Sort in order of decreasing starting address: */
Packit Service 82fcde
  qsort (p, profcnt, sizeof (p[0]), pcmp);
Packit Service 82fcde
Packit Service 82fcde
  /* Add regions in order of decreasing starting address: */
Packit Service 82fcde
  for (i = 0; i < profcnt; ++i)
Packit Service 82fcde
    if (add_region (p[i], (flags & PROF_UINT) != 0) < 0)
Packit Service 82fcde
      {
Packit Service 82fcde
	free (prof_info.region);
Packit Service 82fcde
	prof_info.num_regions = 0;
Packit Service 82fcde
	prof_info.region = NULL;
Packit Service 82fcde
	return -1;
Packit Service 82fcde
      }
Packit Service 82fcde
Packit Service 82fcde
  if (prof_info.num_regions == 0)
Packit Service 82fcde
    return 0;
Packit Service 82fcde
Packit Service 82fcde
  prof_info.last = prof_info.region;
Packit Service 82fcde
Packit Service 82fcde
  /* Install SIGPROF handler.  */
Packit Service 82fcde
  if (flags & PROF_UINT)
Packit Service 82fcde
    act.sa_handler = (sighandler_t) &__profil_counter_uint;
Packit Service 82fcde
  else
Packit Service 82fcde
    act.sa_handler = (sighandler_t) &__profil_counter_ushort;
Packit Service 82fcde
  act.sa_flags = SA_RESTART;
Packit Service 82fcde
  __sigfillset (&act.sa_mask);
Packit Service 82fcde
  if (__sigaction (SIGPROF, &act, &prof_info.saved_action) < 0)
Packit Service 82fcde
    return -1;
Packit Service 82fcde
Packit Service 82fcde
  /* Setup profiling timer.  */
Packit Service 82fcde
  timer.it_value.tv_sec  = 0;
Packit Service 82fcde
  timer.it_value.tv_usec = 1;
Packit Service 82fcde
  timer.it_interval = timer.it_value;
Packit Service 82fcde
  return __setitimer (ITIMER_PROF, &timer, &prof_info.saved_timer);
Packit Service 82fcde
}
Packit Service 82fcde
Packit Service 82fcde
weak_alias (__sprofil, sprofil)
Packit Service 82fcde
Packit Service 82fcde
#endif /* SIGPROF */