/* Low-level statistical profiling support function. Mach/Hurd version. Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #define MAX_PC_SAMPLES 512 /* XXX ought to be exported in kernel hdr */ static thread_t profile_thread = MACH_PORT_NULL; static u_short *samples; static size_t maxsamples; static size_t pc_offset; static size_t sample_scale; static sampled_pc_seqno_t seqno; static struct mutex lock = MUTEX_INITIALIZER; static mach_msg_timeout_t collector_timeout; /* ms between collections. */ static int profile_tick; /* Enable statistical profiling, writing samples of the PC into at most SIZE bytes of SAMPLE_BUFFER; every processor clock tick while profiling is enabled, the system examines the user PC and increments SAMPLE_BUFFER[((PC - OFFSET) / 2) * SCALE / 65536]. If SCALE is zero, disable profiling. Returns zero on success, -1 on error. */ static error_t update_waiter (u_short *sample_buffer, size_t size, size_t offset, u_int scale) { error_t err; if (profile_thread == MACH_PORT_NULL) { /* Set up the profiling collector thread. */ static void profile_waiter (void); err = __thread_create (__mach_task_self (), &profile_thread); if (! err) err = __mach_setup_thread (__mach_task_self (), profile_thread, &profile_waiter, NULL, NULL); } else err = 0; if (! err) { err = __task_enable_pc_sampling (__mach_task_self (), &profile_tick, SAMPLED_PC_PERIODIC); if (!err && sample_scale == 0) /* Profiling was not turned on, so the collector thread was suspended. Resume it. */ err = __thread_resume (profile_thread); if (! err) { samples = sample_buffer; maxsamples = size / sizeof *sample_buffer; pc_offset = offset; sample_scale = scale; /* Calculate a good period for the collector thread. From TICK and the kernel buffer size we get the length of time it takes to fill the buffer; translate that to milliseconds for mach_msg, and chop it in half for general lag factor. */ collector_timeout = MAX_PC_SAMPLES * profile_tick / 1000 / 2; } } return err; } int __profile_frequency () { return profile_tick; } int profil (u_short *sample_buffer, size_t size, size_t offset, u_int scale) { error_t err; __mutex_lock (&lock); if (scale == 0) { /* Disable profiling. */ int count; __thread_suspend (profile_thread); err = __task_disable_pc_sampling (__mach_task_self (), &count); sample_scale = 0; seqno = 0; } else err = update_waiter (sample_buffer, size, offset, scale); __mutex_unlock (&lock); return err ? __hurd_fail (err) : 0; } static void profile_waiter (void) { sampled_pc_t pc_samples[MAX_PC_SAMPLES]; mach_msg_type_number_t nsamples, i; mach_port_t rcv = __mach_reply_port (); mach_msg_header_t msg; error_t err; while (1) { __mutex_lock (&lock); nsamples = sizeof pc_samples / sizeof pc_samples[0]; err = __task_get_sampled_pcs (__mach_task_self (), &seqno, pc_samples, &nsamples); assert_perror (err); for (i = 0; i < nsamples; ++i) { size_t idx = (((pc_samples[i].pc - pc_offset) / 2) * sample_scale / 65536); if (idx < maxsamples) ++samples[idx]; } __vm_deallocate (__mach_task_self (), (vm_address_t) pc_samples, nsamples * sizeof *pc_samples); __mutex_unlock (&lock); __mach_msg (&msg, MACH_RCV_MSG|MACH_RCV_TIMEOUT, 0, sizeof msg, rcv, collector_timeout, MACH_PORT_NULL); } } data_set_element (_hurd_fork_locks, lock); static void fork_profil (void) { u_short *sb; size_t n, o, ss; error_t err; if (profile_thread != MACH_PORT_NULL) { __mach_port_deallocate (__mach_task_self (), profile_thread); profile_thread = MACH_PORT_NULL; } sb = samples; samples = NULL; n = maxsamples; maxsamples = 0; o = pc_offset; pc_offset = 0; ss = sample_scale; sample_scale = 0; if (ss != 0) { err = update_waiter (sb, n * sizeof *sb, o, ss); assert_perror (err); } } text_set_element (_hurd_fork_child_hook, fork_profil);