(root)/
glibc-2.38/
sysdeps/
mach/
hurd/
profil.c
       1  /* Low-level statistical profiling support function.  Mach/Hurd version.
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       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     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <sys/types.h>
      20  #include <unistd.h>
      21  #include <errno.h>
      22  #include <hurd.h>
      23  #include <mach/mach4.h>
      24  #include <mach/pc_sample.h>
      25  #include <mach/setup-thread.h>
      26  #include <lock-intern.h>
      27  #include <assert.h>
      28  #include <libc-internal.h>
      29  
      30  
      31  #define MAX_PC_SAMPLES	512	/* XXX ought to be exported in kernel hdr */
      32  
      33  static thread_t profile_thread = MACH_PORT_NULL;
      34  static u_short *samples;
      35  static size_t maxsamples;
      36  static size_t pc_offset;
      37  static size_t sample_scale;
      38  static sampled_pc_seqno_t seqno;
      39  static spin_lock_t lock = SPIN_LOCK_INITIALIZER;
      40  static mach_msg_timeout_t collector_timeout; /* ms between collections.  */
      41  static int profile_tick;
      42  
      43  /* Reply port used by profiler thread */
      44  static mach_port_t profil_reply_port = MACH_PORT_NULL;
      45  
      46  /* Forwards */
      47  static kern_return_t profil_task_get_sampled_pcs (mach_port_t,
      48  						  sampled_pc_seqno_t *,
      49  						  sampled_pc_array_t,
      50  						  mach_msg_type_number_t *);
      51  static void fetch_samples (void);
      52  static void profile_waiter (void);
      53  
      54  /* Enable statistical profiling, writing samples of the PC into at most
      55     SIZE bytes of SAMPLE_BUFFER; every processor clock tick while profiling
      56     is enabled, the system examines the user PC and increments
      57     SAMPLE_BUFFER[((PC - OFFSET) / 2) * SCALE / 65536].  If SCALE is zero,
      58     disable profiling.  Returns zero on success, -1 on error.  */
      59  
      60  static error_t
      61  update_waiter (u_short *sample_buffer, size_t size, size_t offset, u_int scale)
      62  {
      63    error_t err;
      64  
      65    if (profile_thread == MACH_PORT_NULL)
      66      {
      67        if (profil_reply_port == MACH_PORT_NULL)
      68  	profil_reply_port = __mach_reply_port ();
      69        /* Set up the profiling collector thread.  */
      70        err = __thread_create (__mach_task_self (), &profile_thread);
      71        if (! err)
      72  	err = __mach_setup_thread_call (__mach_task_self (), profile_thread,
      73  					&profile_waiter, NULL, NULL);
      74        if (! err)
      75  	err = __mach_setup_tls(profile_thread);
      76      }
      77    else
      78      err = 0;
      79  
      80    if (! err)
      81      {
      82        err = __task_enable_pc_sampling (__mach_task_self (), &profile_tick,
      83  				       SAMPLED_PC_PERIODIC);
      84        if (!err && sample_scale == 0)
      85  	/* Profiling was not turned on, so the collector thread was
      86  	   suspended.  Resume it.  */
      87  	err = __thread_resume (profile_thread);
      88        if (! err)
      89  	{
      90  	  samples = sample_buffer;
      91  	  maxsamples = size / sizeof *sample_buffer;
      92  	  pc_offset = offset;
      93  	  sample_scale = scale;
      94  	  /* Calculate a good period for the collector thread.  From TICK
      95  	     and the kernel buffer size we get the length of time it takes
      96  	     to fill the buffer; translate that to milliseconds for
      97  	     mach_msg, and chop it in half for general lag factor.  */
      98  	  collector_timeout = MAX_PC_SAMPLES * profile_tick / 1000 / 2;
      99  	}
     100      }
     101  
     102    return err;
     103  }
     104  
     105  int
     106  __profile_frequency (void)
     107  {
     108    return 1000000 / profile_tick;
     109  }
     110  libc_hidden_def (__profile_frequency)
     111  
     112  int
     113  __profil (u_short *sample_buffer, size_t size, size_t offset, u_int scale)
     114  {
     115    error_t err;
     116  
     117    __spin_lock (&lock);
     118  
     119    if (scale == 0)
     120      {
     121        /* Disable profiling.  */
     122        int count;
     123  
     124        if (profile_thread != MACH_PORT_NULL)
     125  	__thread_suspend (profile_thread);
     126  
     127        /* Fetch the last set of samples */
     128        if (sample_scale)
     129  	fetch_samples ();
     130  
     131        err = __task_disable_pc_sampling (__mach_task_self (), &count);
     132        sample_scale = 0;
     133        seqno = 0;
     134      }
     135    else
     136      err = update_waiter (sample_buffer, size, offset, scale);
     137  
     138    __spin_unlock (&lock);
     139  
     140    return err ? __hurd_fail (err) : 0;
     141  }
     142  weak_alias (__profil, profil)
     143  
     144  static volatile error_t special_profil_failure;
     145  
     146  /* Fetch PC samples.  This function must be very careful not to depend
     147     on Hurd TLS variables.  We arrange that by using a special
     148     stub arranged for at the end of this file. */
     149  static void
     150  fetch_samples (void)
     151  {
     152    sampled_pc_t pc_samples[MAX_PC_SAMPLES];
     153    mach_msg_type_number_t nsamples, i;
     154    error_t err;
     155  
     156    nsamples = MAX_PC_SAMPLES;
     157  
     158    err = profil_task_get_sampled_pcs (__mach_task_self (), &seqno,
     159  				     pc_samples, &nsamples);
     160    if (err)
     161      {
     162        static volatile int a, b;
     163  
     164        special_profil_failure = err;
     165        a = 1;
     166        b = 0;
     167        while (1)
     168  	a = a / b;
     169      }
     170  
     171    for (i = 0; i < nsamples; ++i)
     172      {
     173        /* Do arithmetic in long long to avoid overflow problems. */
     174        long long pc_difference = pc_samples[i].pc - pc_offset;
     175        size_t idx = ((pc_difference / 2) * sample_scale) / 65536;
     176        if (idx < maxsamples)
     177  	++samples[idx];
     178      }
     179  }
     180  
     181  
     182  /* This function must be very careful not to depend on Hurd TLS
     183     variables.  We arrange that by using special stubs arranged for at the
     184     end of this file. */
     185  static void
     186  profile_waiter (void)
     187  {
     188    mach_msg_header_t msg;
     189    mach_port_t timeout_reply_port;
     190  
     191    timeout_reply_port = __mach_reply_port ();
     192  
     193    while (1)
     194      {
     195        __spin_lock (&lock);
     196  
     197        fetch_samples ();
     198  
     199        __spin_unlock (&lock);
     200  
     201        __mach_msg (&msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof msg,
     202  		  timeout_reply_port, collector_timeout, MACH_PORT_NULL);
     203      }
     204  }
     205  
     206  /* Fork interaction */
     207  
     208  /* Before fork, lock the interlock so that we are in a clean state. */
     209  static void
     210  fork_profil_prepare (void)
     211  {
     212    __spin_lock (&lock);
     213  }
     214  text_set_element (_hurd_fork_prepare_hook, fork_profil_prepare);
     215  
     216  /* In the parent, unlock the interlock once fork is complete. */
     217  static void
     218  fork_profil_parent (void)
     219  {
     220    __spin_unlock (&lock);
     221  }
     222  text_set_element (_hurd_fork_parent_hook, fork_profil_parent);
     223  
     224  /* In the child, unlock the interlock, and start a profiling thread up
     225     if necessary. */
     226  static void
     227  fork_profil_child (void)
     228  {
     229    u_short *sb;
     230    size_t n, o, ss;
     231    error_t err;
     232  
     233    __spin_unlock (&lock);
     234  
     235    if (profile_thread != MACH_PORT_NULL)
     236      {
     237        __mach_port_deallocate (__mach_task_self (), profile_thread);
     238        profile_thread = MACH_PORT_NULL;
     239      }
     240  
     241    sb = samples;
     242    samples = NULL;
     243    n = maxsamples;
     244    maxsamples = 0;
     245    o = pc_offset;
     246    pc_offset = 0;
     247    ss = sample_scale;
     248    sample_scale = 0;
     249  
     250    if (ss != 0)
     251      {
     252        err = update_waiter (sb, n * sizeof *sb, o, ss);
     253        assert_perror (err);
     254      }
     255  }
     256  text_set_element (_hurd_fork_child_hook, fork_profil_child);
     257  
     258  
     259  
     260  
     261  /* Special RPC stubs for profile_waiter are made by including the normal
     262     source code, with special CPP state to prevent it from doing the
     263     usual thing. */
     264  
     265  /* Include these first; then our #define's will take full effect, not
     266     being overridden. */
     267  #include <mach/mig_support.h>
     268  
     269  /* This need not do anything; it is always associated with errors, which
     270     are fatal in profile_waiter anyhow. */
     271  #define __mig_put_reply_port(foo)
     272  
     273  /* Use our static variable instead of the usual TLS mechanism for
     274     this. */
     275  #define __mig_get_reply_port() profil_reply_port
     276  
     277  /* Make the functions show up as static */
     278  #define mig_external static
     279  
     280  /* Turn off the attempt to generate ld aliasing records. */
     281  #undef weak_alias
     282  #define weak_alias(a,b)
     283  
     284  /* And change their names to avoid confusing disasters. */
     285  #define __vm_deallocate_rpc profil_vm_deallocate
     286  #define __task_get_sampled_pcs profil_task_get_sampled_pcs
     287  
     288  /* And include the source code */
     289  #include <../mach/RPC_task_get_sampled_pcs.c>