Blame sysdeps/mach/hurd/profil.c

Packit 6c4009
/* Low-level statistical profiling support function.  Mach/Hurd version.
Packit 6c4009
   Copyright (C) 1995-2018 Free Software Foundation, Inc.
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 <sys/types.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <hurd.h>
Packit 6c4009
#include <mach/mach4.h>
Packit 6c4009
#include <mach/pc_sample.h>
Packit 6c4009
#include <cthreads.h>
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <libc-internal.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
#define MAX_PC_SAMPLES	512	/* XXX ought to be exported in kernel hdr */
Packit 6c4009
Packit 6c4009
static thread_t profile_thread = MACH_PORT_NULL;
Packit 6c4009
static u_short *samples;
Packit 6c4009
static size_t maxsamples;
Packit 6c4009
static size_t pc_offset;
Packit 6c4009
static size_t sample_scale;
Packit 6c4009
static sampled_pc_seqno_t seqno;
Packit 6c4009
static spin_lock_t lock = SPIN_LOCK_INITIALIZER;
Packit 6c4009
static mach_msg_timeout_t collector_timeout; /* ms between collections.  */
Packit 6c4009
static int profile_tick;
Packit 6c4009
Packit 6c4009
/* Reply port used by profiler thread */
Packit 6c4009
static mach_port_t profil_reply_port = MACH_PORT_NULL;
Packit 6c4009
Packit 6c4009
/* Forwards */
Packit 6c4009
static kern_return_t profil_task_get_sampled_pcs (mach_port_t,
Packit 6c4009
						  sampled_pc_seqno_t *,
Packit 6c4009
						  sampled_pc_array_t,
Packit 6c4009
						  mach_msg_type_number_t *);
Packit 6c4009
static void fetch_samples (void);
Packit 6c4009
static void profile_waiter (void);
Packit 6c4009
Packit 6c4009
/* Enable statistical profiling, writing samples of the PC into at most
Packit 6c4009
   SIZE bytes of SAMPLE_BUFFER; every processor clock tick while profiling
Packit 6c4009
   is enabled, the system examines the user PC and increments
Packit 6c4009
   SAMPLE_BUFFER[((PC - OFFSET) / 2) * SCALE / 65536].  If SCALE is zero,
Packit 6c4009
   disable profiling.  Returns zero on success, -1 on error.  */
Packit 6c4009
Packit 6c4009
static error_t
Packit 6c4009
update_waiter (u_short *sample_buffer, size_t size, size_t offset, u_int scale)
Packit 6c4009
{
Packit 6c4009
  error_t err;
Packit 6c4009
Packit 6c4009
  if (profile_thread == MACH_PORT_NULL)
Packit 6c4009
    {
Packit 6c4009
      if (profil_reply_port == MACH_PORT_NULL)
Packit 6c4009
	profil_reply_port = __mach_reply_port ();
Packit 6c4009
      /* Set up the profiling collector thread.  */
Packit 6c4009
      err = __thread_create (__mach_task_self (), &profile_thread);
Packit 6c4009
      if (! err)
Packit 6c4009
	err = __mach_setup_thread (__mach_task_self (), profile_thread,
Packit 6c4009
				   &profile_waiter, NULL, NULL);
Packit 6c4009
      if (! err)
Packit 6c4009
	err = __mach_setup_tls(profile_thread);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    err = 0;
Packit 6c4009
Packit 6c4009
  if (! err)
Packit 6c4009
    {
Packit 6c4009
      err = __task_enable_pc_sampling (__mach_task_self (), &profile_tick,
Packit 6c4009
				       SAMPLED_PC_PERIODIC);
Packit 6c4009
      if (!err && sample_scale == 0)
Packit 6c4009
	/* Profiling was not turned on, so the collector thread was
Packit 6c4009
	   suspended.  Resume it.  */
Packit 6c4009
	err = __thread_resume (profile_thread);
Packit 6c4009
      if (! err)
Packit 6c4009
	{
Packit 6c4009
	  samples = sample_buffer;
Packit 6c4009
	  maxsamples = size / sizeof *sample_buffer;
Packit 6c4009
	  pc_offset = offset;
Packit 6c4009
	  sample_scale = scale;
Packit 6c4009
	  /* Calculate a good period for the collector thread.  From TICK
Packit 6c4009
	     and the kernel buffer size we get the length of time it takes
Packit 6c4009
	     to fill the buffer; translate that to milliseconds for
Packit 6c4009
	     mach_msg, and chop it in half for general lag factor.  */
Packit 6c4009
	  collector_timeout = MAX_PC_SAMPLES * profile_tick / 1000 / 2;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return err;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__profile_frequency (void)
Packit 6c4009
{
Packit 6c4009
  return 1000000 / profile_tick;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__profile_frequency)
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__profil (u_short *sample_buffer, size_t size, size_t offset, u_int scale)
Packit 6c4009
{
Packit 6c4009
  error_t err;
Packit 6c4009
Packit 6c4009
  __spin_lock (&lock);
Packit 6c4009
Packit 6c4009
  if (scale == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Disable profiling.  */
Packit 6c4009
      int count;
Packit 6c4009
Packit 6c4009
      if (profile_thread != MACH_PORT_NULL)
Packit 6c4009
	__thread_suspend (profile_thread);
Packit 6c4009
Packit 6c4009
      /* Fetch the last set of samples */
Packit 6c4009
      if (sample_scale)
Packit 6c4009
	fetch_samples ();
Packit 6c4009
Packit 6c4009
      err = __task_disable_pc_sampling (__mach_task_self (), &count);
Packit 6c4009
      sample_scale = 0;
Packit 6c4009
      seqno = 0;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    err = update_waiter (sample_buffer, size, offset, scale);
Packit 6c4009
Packit 6c4009
  __spin_unlock (&lock);
Packit 6c4009
Packit 6c4009
  return err ? __hurd_fail (err) : 0;
Packit 6c4009
}
Packit 6c4009
weak_alias (__profil, profil)
Packit 6c4009
Packit 6c4009
static volatile error_t special_profil_failure;
Packit 6c4009
Packit 6c4009
/* Fetch PC samples.  This function must be very careful not to depend
Packit 6c4009
   on Hurd TLS variables.  We arrange that by using a special
Packit 6c4009
   stub arranged for at the end of this file. */
Packit 6c4009
static void
Packit 6c4009
fetch_samples (void)
Packit 6c4009
{
Packit 6c4009
  sampled_pc_t pc_samples[MAX_PC_SAMPLES];
Packit 6c4009
  mach_msg_type_number_t nsamples, i;
Packit 6c4009
  error_t err;
Packit 6c4009
Packit 6c4009
  nsamples = MAX_PC_SAMPLES;
Packit 6c4009
Packit 6c4009
  err = profil_task_get_sampled_pcs (__mach_task_self (), &seqno,
Packit 6c4009
				     pc_samples, &nsamples);
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      static volatile int a, b;
Packit 6c4009
Packit 6c4009
      special_profil_failure = err;
Packit 6c4009
      a = 1;
Packit 6c4009
      b = 0;
Packit 6c4009
      while (1)
Packit 6c4009
	a = a / b;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  for (i = 0; i < nsamples; ++i)
Packit 6c4009
    {
Packit 6c4009
      /* Do arithmetic in long long to avoid overflow problems. */
Packit 6c4009
      long long pc_difference = pc_samples[i].pc - pc_offset;
Packit 6c4009
      size_t idx = ((pc_difference / 2) * sample_scale) / 65536;
Packit 6c4009
      if (idx < maxsamples)
Packit 6c4009
	++samples[idx];
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* This function must be very careful not to depend on Hurd TLS
Packit 6c4009
   variables.  We arrange that by using special stubs arranged for at the
Packit 6c4009
   end of this file. */
Packit 6c4009
static void
Packit 6c4009
profile_waiter (void)
Packit 6c4009
{
Packit 6c4009
  mach_msg_header_t msg;
Packit 6c4009
  mach_port_t timeout_reply_port;
Packit 6c4009
Packit 6c4009
  timeout_reply_port = __mach_reply_port ();
Packit 6c4009
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit 6c4009
      __spin_lock (&lock);
Packit 6c4009
Packit 6c4009
      fetch_samples ();
Packit 6c4009
Packit 6c4009
      __spin_unlock (&lock);
Packit 6c4009
Packit 6c4009
      __mach_msg (&msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof msg,
Packit 6c4009
		  timeout_reply_port, collector_timeout, MACH_PORT_NULL);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009

Packit 6c4009
/* Fork interaction */
Packit 6c4009
Packit 6c4009
/* Before fork, lock the interlock so that we are in a clean state. */
Packit 6c4009
static void
Packit 6c4009
fork_profil_prepare (void)
Packit 6c4009
{
Packit 6c4009
  __spin_lock (&lock);
Packit 6c4009
}
Packit 6c4009
text_set_element (_hurd_fork_prepare_hook, fork_profil_prepare);
Packit 6c4009
Packit 6c4009
/* In the parent, unlock the interlock once fork is complete. */
Packit 6c4009
static void
Packit 6c4009
fork_profil_parent (void)
Packit 6c4009
{
Packit 6c4009
  __spin_unlock (&lock);
Packit 6c4009
}
Packit 6c4009
text_set_element (_hurd_fork_parent_hook, fork_profil_parent);
Packit 6c4009
Packit 6c4009
/* In the child, unlock the interlock, and start a profiling thread up
Packit 6c4009
   if necessary. */
Packit 6c4009
static void
Packit 6c4009
fork_profil_child (void)
Packit 6c4009
{
Packit 6c4009
  u_short *sb;
Packit 6c4009
  size_t n, o, ss;
Packit 6c4009
  error_t err;
Packit 6c4009
Packit 6c4009
  __spin_unlock (&lock);
Packit 6c4009
Packit 6c4009
  if (profile_thread != MACH_PORT_NULL)
Packit 6c4009
    {
Packit 6c4009
      __mach_port_deallocate (__mach_task_self (), profile_thread);
Packit 6c4009
      profile_thread = MACH_PORT_NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  sb = samples;
Packit 6c4009
  samples = NULL;
Packit 6c4009
  n = maxsamples;
Packit 6c4009
  maxsamples = 0;
Packit 6c4009
  o = pc_offset;
Packit 6c4009
  pc_offset = 0;
Packit 6c4009
  ss = sample_scale;
Packit 6c4009
  sample_scale = 0;
Packit 6c4009
Packit 6c4009
  if (ss != 0)
Packit 6c4009
    {
Packit 6c4009
      err = update_waiter (sb, n * sizeof *sb, o, ss);
Packit 6c4009
      assert_perror (err);
Packit 6c4009
    }
Packit 6c4009
}
Packit 6c4009
text_set_element (_hurd_fork_child_hook, fork_profil_child);
Packit 6c4009
Packit 6c4009
Packit 6c4009

Packit 6c4009
Packit 6c4009
/* Special RPC stubs for profile_waiter are made by including the normal
Packit 6c4009
   source code, with special CPP state to prevent it from doing the
Packit 6c4009
   usual thing. */
Packit 6c4009
Packit 6c4009
/* Include these first; then our #define's will take full effect, not
Packit 6c4009
   being overridden. */
Packit 6c4009
#include <mach/mig_support.h>
Packit 6c4009
Packit 6c4009
/* This need not do anything; it is always associated with errors, which
Packit 6c4009
   are fatal in profile_waiter anyhow. */
Packit 6c4009
#define __mig_put_reply_port(foo)
Packit 6c4009
Packit 6c4009
/* Use our static variable instead of the usual TLS mechanism for
Packit 6c4009
   this. */
Packit 6c4009
#define __mig_get_reply_port() profil_reply_port
Packit 6c4009
Packit 6c4009
/* Make the functions show up as static */
Packit 6c4009
#define mig_external static
Packit 6c4009
Packit 6c4009
/* Turn off the attempt to generate ld aliasing records. */
Packit 6c4009
#undef weak_alias
Packit 6c4009
#define weak_alias(a,b)
Packit 6c4009
Packit 6c4009
/* And change their names to avoid confusing disasters. */
Packit 6c4009
#define __vm_deallocate_rpc profil_vm_deallocate
Packit 6c4009
#define __task_get_sampled_pcs profil_task_get_sampled_pcs
Packit 6c4009
Packit 6c4009
/* And include the source code */
Packit 6c4009
#include <../mach/RPC_task_get_sampled_pcs.c>