Back to index

glibc  2.9
profil.c
Go to the documentation of this file.
00001 /* Low-level statistical profiling support function.  Mach/Hurd version.
00002    Copyright (C) 1995, 1996, 1997, 2000, 2002, 2005 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <sys/types.h>
00021 #include <unistd.h>
00022 #include <errno.h>
00023 #include <hurd.h>
00024 #include <mach/mach4.h>
00025 #include <mach/pc_sample.h>
00026 #include <cthreads.h>
00027 #include <assert.h>
00028 #include <libc-internal.h>
00029 
00030 
00031 #define MAX_PC_SAMPLES      512    /* XXX ought to be exported in kernel hdr */
00032 
00033 static thread_t profile_thread = MACH_PORT_NULL;
00034 static u_short *samples;
00035 static size_t maxsamples;
00036 static size_t pc_offset;
00037 static size_t sample_scale;
00038 static sampled_pc_seqno_t seqno;
00039 static spin_lock_t lock = SPIN_LOCK_INITIALIZER;
00040 static mach_msg_timeout_t collector_timeout; /* ms between collections.  */
00041 static int profile_tick;
00042 
00043 /* Reply port used by profiler thread */
00044 static mach_port_t profil_reply_port;
00045 
00046 /* Forwards */
00047 static kern_return_t profil_task_get_sampled_pcs (mach_port_t,
00048                                             sampled_pc_seqno_t *,
00049                                             sampled_pc_array_t,
00050                                             mach_msg_type_number_t *);
00051 static void fetch_samples (void);
00052 static void profile_waiter (void);
00053 
00054 /* Enable statistical profiling, writing samples of the PC into at most
00055    SIZE bytes of SAMPLE_BUFFER; every processor clock tick while profiling
00056    is enabled, the system examines the user PC and increments
00057    SAMPLE_BUFFER[((PC - OFFSET) / 2) * SCALE / 65536].  If SCALE is zero,
00058    disable profiling.  Returns zero on success, -1 on error.  */
00059 
00060 static error_t
00061 update_waiter (u_short *sample_buffer, size_t size, size_t offset, u_int scale)
00062 {
00063   error_t err;
00064 
00065   if (profile_thread == MACH_PORT_NULL)
00066     {
00067       /* Set up the profiling collector thread.  */
00068       err = __thread_create (__mach_task_self (), &profile_thread);
00069       if (! err)
00070        err = __mach_setup_thread (__mach_task_self (), profile_thread,
00071                                &profile_waiter, NULL, NULL);
00072     }
00073   else
00074     err = 0;
00075 
00076   if (! err)
00077     {
00078       err = __task_enable_pc_sampling (__mach_task_self (), &profile_tick,
00079                                    SAMPLED_PC_PERIODIC);
00080       if (!err && sample_scale == 0)
00081        /* Profiling was not turned on, so the collector thread was
00082           suspended.  Resume it.  */
00083        err = __thread_resume (profile_thread);
00084       if (! err)
00085        {
00086          samples = sample_buffer;
00087          maxsamples = size / sizeof *sample_buffer;
00088          pc_offset = offset;
00089          sample_scale = scale;
00090          /* Calculate a good period for the collector thread.  From TICK
00091             and the kernel buffer size we get the length of time it takes
00092             to fill the buffer; translate that to milliseconds for
00093             mach_msg, and chop it in half for general lag factor.  */
00094          collector_timeout = MAX_PC_SAMPLES * profile_tick / 1000 / 2;
00095        }
00096     }
00097 
00098   return err;
00099 }
00100 
00101 int
00102 __profile_frequency (void)
00103 {
00104   return profile_tick;
00105 }
00106 libc_hidden_def (__profile_frequency)
00107 
00108 int
00109 __profil (u_short *sample_buffer, size_t size, size_t offset, u_int scale)
00110 {
00111   error_t err;
00112 
00113   __spin_lock (&lock);
00114 
00115   if (scale == 0)
00116     {
00117       /* Disable profiling.  */
00118       int count;
00119 
00120       if (profile_thread != MACH_PORT_NULL)
00121        __thread_suspend (profile_thread);
00122 
00123       /* Fetch the last set of samples */
00124       if (sample_scale)
00125        fetch_samples ();
00126 
00127       err = __task_disable_pc_sampling (__mach_task_self (), &count);
00128       sample_scale = 0;
00129       seqno = 0;
00130     }
00131   else
00132     err = update_waiter (sample_buffer, size, offset, scale);
00133 
00134   __spin_unlock (&lock);
00135 
00136   return err ? __hurd_fail (err) : 0;
00137 }
00138 weak_alias (__profil, profil)
00139 
00140 /* Fetch PC samples.  This function must be very careful not to depend
00141    on Hurd threadvar variables.  We arrange that by using a special
00142    stub arranged for at the end of this file. */
00143 static void
00144 fetch_samples (void)
00145 {
00146   sampled_pc_t pc_samples[MAX_PC_SAMPLES];
00147   mach_msg_type_number_t nsamples, i;
00148   error_t err;
00149 
00150   nsamples = MAX_PC_SAMPLES;
00151 
00152   err = profil_task_get_sampled_pcs (__mach_task_self (), &seqno,
00153                                  pc_samples, &nsamples);
00154   if (err)
00155     {
00156       static error_t special_profil_failure;
00157       static volatile int a, b, c;
00158 
00159       special_profil_failure = err;
00160       a = 1;
00161       b = 0;
00162       while (1)
00163        c = a / b;
00164     }
00165 
00166   for (i = 0; i < nsamples; ++i)
00167     {
00168       /* Do arithmetic in long long to avoid overflow problems. */
00169       long long pc_difference = pc_samples[i].pc - pc_offset;
00170       size_t idx = ((pc_difference / 2) * sample_scale) / 65536;
00171       if (idx < maxsamples)
00172        ++samples[idx];
00173     }
00174 }
00175 
00176 
00177 /* This function must be very careful not to depend on Hurd threadvar
00178    variables.  We arrange that by using special stubs arranged for at the
00179    end of this file. */
00180 static void
00181 profile_waiter (void)
00182 {
00183   mach_msg_header_t msg;
00184   mach_port_t timeout_reply_port;
00185 
00186   profil_reply_port = __mach_reply_port ();
00187   timeout_reply_port = __mach_reply_port ();
00188 
00189   while (1)
00190     {
00191       __spin_lock (&lock);
00192 
00193       fetch_samples ();
00194 
00195       __spin_unlock (&lock);
00196 
00197       __mach_msg (&msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof msg,
00198                 timeout_reply_port, collector_timeout, MACH_PORT_NULL);
00199     }
00200 }
00201 
00202 /* Fork interaction */
00203 
00204 /* Before fork, lock the interlock so that we are in a clean state. */
00205 static void
00206 fork_profil_prepare (void)
00207 {
00208   __spin_lock (&lock);
00209 }
00210 text_set_element (_hurd_fork_prepare_hook, fork_profil_prepare);
00211 
00212 /* In the parent, unlock the interlock once fork is complete. */
00213 static void
00214 fork_profil_parent (void)
00215 {
00216   __spin_unlock (&lock);
00217 }
00218 text_set_element (_hurd_fork_parent_hook, fork_profil_parent);
00219 
00220 /* In the childs, unlock the interlock, and start a profiling thread up
00221    if necessary. */
00222 static void
00223 fork_profil_child (void)
00224 {
00225   u_short *sb;
00226   size_t n, o, ss;
00227   error_t err;
00228 
00229   __spin_unlock (&lock);
00230 
00231   if (profile_thread != MACH_PORT_NULL)
00232     {
00233       __mach_port_deallocate (__mach_task_self (), profile_thread);
00234       profile_thread = MACH_PORT_NULL;
00235     }
00236 
00237   sb = samples;
00238   samples = NULL;
00239   n = maxsamples;
00240   maxsamples = 0;
00241   o = pc_offset;
00242   pc_offset = 0;
00243   ss = sample_scale;
00244   sample_scale = 0;
00245 
00246   if (ss != 0)
00247     {
00248       err = update_waiter (sb, n * sizeof *sb, o, ss);
00249       assert_perror (err);
00250     }
00251 }
00252 text_set_element (_hurd_fork_child_hook, fork_profil_child);
00253 
00254 
00255 
00256 
00257 /* Special RPC stubs for profile_waiter are made by including the normal
00258    source code, with special CPP state to prevent it from doing the
00259    usual thing. */
00260 
00261 /* Include these first; then our #define's will take full effect, not
00262    being overridden. */
00263 #include <mach/mig_support.h>
00264 
00265 /* This need not do anything; it is always associated with errors, which
00266    are fatal in profile_waiter anyhow. */
00267 #define __mig_put_reply_port(foo)
00268 
00269 /* Use our static variable instead of the usual threadvar mechanism for
00270    this. */
00271 #define __mig_get_reply_port() profil_reply_port
00272 
00273 /* Make the functions show up as static */
00274 #define mig_external static
00275 
00276 /* Turn off the attempt to generate ld aliasing records. */
00277 #undef weak_alias
00278 #define weak_alias(a,b)
00279 
00280 /* And change their names to avoid confusing disasters. */
00281 #define __vm_deallocate_rpc profil_vm_deallocate
00282 #define __task_get_sampled_pcs profil_task_get_sampled_pcs
00283 
00284 /* And include the source code */
00285 #include <../mach/RPC_task_get_sampled_pcs.c>