Blame sysdeps/posix/sprofil.c

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