summaryrefslogtreecommitdiff
path: root/kern/profile.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /kern/profile.c
Initial source
Diffstat (limited to 'kern/profile.c')
-rw-r--r--kern/profile.c413
1 files changed, 413 insertions, 0 deletions
diff --git a/kern/profile.c b/kern/profile.c
new file mode 100644
index 0000000..7513934
--- /dev/null
+++ b/kern/profile.c
@@ -0,0 +1,413 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University.
+ * Copyright (c) 1993,1994 The University of Utah and
+ * the Computer Systems Laboratory (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF
+ * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY
+ * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF
+ * THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Copyright 1991 by Open Software Foundation,
+ * Grenoble, FRANCE
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OSF or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.
+ *
+ * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+ * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if 0
+
+#include <kern/thread.h>
+#include <kern/queue.h>
+#include <mach/profil.h>
+#include <kern/sched_prim.h>
+#include <ipc/ipc_space.h>
+
+extern vm_map_t kernel_map; /* can be discarded, defined in <vm/vm_kern.h> */
+
+thread_t profile_thread_id = THREAD_NULL;
+
+
+void profile_thread()
+{
+ struct message {
+ mach_msg_header_t head;
+ mach_msg_type_t type;
+ int arg[SIZE_PROF_BUFFER+1];
+ } msg;
+
+ register spl_t s;
+ buf_to_send_t buf_entry;
+ queue_entry_t prof_queue_entry;
+ prof_data_t pbuf;
+ simple_lock_t lock;
+ msg_return_t mr;
+ int j;
+
+ /* Initialise the queue header for the prof_queue */
+ mpqueue_init(&prof_queue);
+
+ /* Template initialisation of header and type structures */
+ msg.head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ msg.head.msgh_size = sizeof(msg);
+ msg.head.msgh_local_port = MACH_PORT_NULL;
+ msg.head.msgh_kind = MACH_MSGH_KIND_NORMAL;
+ msg.head.msgh_id = 666666;
+
+ msg.type.msgt_name = MACH_MSG_TYPE_INTEGER_32;
+ msg.type.msgt_size = 32;
+ msg.type.msgt_number = SIZE_PROF_BUFFER+1;
+ msg.type.msgt_inline = TRUE;
+ msg.type.msgt_longform = FALSE;
+ msg.type.msgt_deallocate = FALSE;
+ msg.type.msgt_unused = 0;
+
+ while (TRUE) {
+
+ /* Dequeue the first buffer. */
+ s = splsched();
+ mpdequeue_head(&prof_queue, &prof_queue_entry);
+ splx(s);
+
+ if ((buf_entry = (buf_to_send_t) prof_queue_entry) == NULLBTS)
+ {
+ thread_sleep((event_t) profile_thread, lock, TRUE);
+ if (current_thread()->wait_result != THREAD_AWAKENED)
+ break;
+ }
+ else {
+ task_t curr_task;
+ thread_t curr_th;
+ register int *sample;
+ int curr_buf;
+ int imax;
+
+ curr_th = (thread_t) buf_entry->thread;
+ curr_buf = (int) buf_entry->number;
+ pbuf = curr_th->profil_buffer;
+
+ /* Set the remote port */
+ msg.head.msgh_remote_port = (mach_port_t) pbuf->prof_port;
+
+
+ sample = pbuf->prof_area[curr_buf].p_zone;
+ imax = pbuf->prof_area[curr_buf].p_index;
+ for(j=0 ;j<imax; j++,sample++)
+ msg.arg[j] = *sample;
+
+ /* Let hardclock() know you've finished the dirty job */
+ pbuf->prof_area[curr_buf].p_full = FALSE;
+
+ /*
+ * Store the number of samples actually sent
+ * as the last element of the array.
+ */
+ msg.arg[SIZE_PROF_BUFFER] = imax;
+
+ mr = mach_msg(&(msg.head), MACH_SEND_MSG,
+ sizeof(struct message), 0,
+ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+
+ if (mr != MACH_MSG_SUCCESS) {
+printf("profile_thread: mach_msg failed returned %x\n",(int)mr);
+ }
+
+ if (buf_entry->wakeme)
+ thread_wakeup((event_t) &buf_entry->wakeme);
+ kmem_free(kernel_map, (buf_to_send_t) buf_entry,
+ sizeof(struct buf_to_send));
+
+ }
+
+ }
+ /* The profile thread has been signalled to exit. There may still
+ be sample data queued for us, which we must now throw away.
+ Once we set profile_thread_id to null, hardclock() will stop
+ queueing any additional samples, so we do not need to alter
+ the interrupt level. */
+ profile_thread_id = THREAD_NULL;
+ while (1) {
+ mpdequeue_head(&prof_queue, &prof_queue_entry);
+ if ((buf_entry = (buf_to_send_t) prof_queue_entry) == NULLBTS)
+ break;
+ if (buf_entry->wakeme)
+ thread_wakeup((event_t) &buf_entry->wakeme);
+ kmem_free(kernel_map, (buf_to_send_t) buf_entry,
+ sizeof(struct buf_to_send));
+ }
+
+ thread_halt_self();
+}
+
+
+
+#include <mach/message.h>
+
+void
+send_last_sample_buf(th)
+thread_t th;
+{
+ register spl_t s;
+ buf_to_send_t buf_entry;
+ vm_offset_t vm_buf_entry;
+
+ if (th->profil_buffer == NULLPBUF)
+ return;
+
+ /* Ask for the sending of the last PC buffer.
+ * Make a request to the profile_thread by inserting
+ * the buffer in the send queue, and wake it up.
+ * The last buffer must be inserted at the head of the
+ * send queue, so the profile_thread handles it immediatly.
+ */
+ if (kmem_alloc( kernel_map, &vm_buf_entry,
+ sizeof(struct buf_to_send)) != KERN_SUCCESS)
+ return;
+ buf_entry = (buf_to_send_t) vm_buf_entry;
+ buf_entry->thread = (int *) th;
+ buf_entry->number = th->profil_buffer->prof_index;
+
+ /* Watch out in case profile thread exits while we are about to
+ queue data for it. */
+ s = splsched();
+ if (profile_thread_id != THREAD_NULL) {
+ simple_lock_t lock;
+ buf_entry->wakeme = 1;
+ mpenqueue_tail( &prof_queue, &(buf_entry->list));
+ thread_wakeup((event_t) profile_thread);
+ assert_wait((event_t) &buf_entry->wakeme, TRUE);
+ splx(s);
+ thread_block((void (*)()) 0);
+ } else {
+ splx(s);
+ kmem_free(kernel_map, vm_buf_entry, sizeof(struct buf_to_send));
+ }
+}
+
+/*
+ * Profile current thread
+ */
+
+profile(pc) {
+
+ /* Find out which thread has been interrupted. */
+ thread_t it_thread = current_thread();
+ int inout_val = pc;
+ buf_to_send_t buf_entry;
+ vm_offset_t vm_buf_entry;
+ int *val;
+ /*
+ * Test if the current thread is to be sampled
+ */
+ if (it_thread->thread_profiled) {
+ /* Inserts the PC value in the buffer of the thread */
+ set_pbuf_value(it_thread->profil_buffer, &inout_val);
+ switch(inout_val) {
+ case 0:
+ if (profile_thread_id == THREAD_NULL) {
+ reset_pbuf_area(it_thread->profil_buffer);
+ } else printf("ERROR : hardclock : full buffer unsent\n");
+ break;
+ case 1:
+ /* Normal case, value successfully inserted */
+ break;
+ case 2 :
+ /*
+ * The value we have just inserted caused the
+ * buffer to be full, and ready to be sent.
+ * If profile_thread_id is null, the profile
+ * thread has been killed. Since this generally
+ * happens only when the O/S server task of which
+ * it is a part is killed, it is not a great loss
+ * to throw away the data.
+ */
+ if (profile_thread_id == THREAD_NULL ||
+ kmem_alloc(kernel_map,
+ &vm_buf_entry ,
+ sizeof(struct buf_to_send)) !=
+ KERN_SUCCESS) {
+ reset_pbuf_area(it_thread->profil_buffer);
+ break;
+ }
+ buf_entry = (buf_to_send_t) vm_buf_entry;
+ buf_entry->thread = (int *)it_thread;
+ buf_entry->number =
+ (it_thread->profil_buffer)->prof_index;
+ mpenqueue_tail(&prof_queue, &(buf_entry->list));
+
+ /* Switch to another buffer */
+ reset_pbuf_area(it_thread->profil_buffer);
+
+ /* Wake up the profile thread */
+ if (profile_thread_id != THREAD_NULL)
+ thread_wakeup((event_t) profile_thread);
+ break;
+
+ default:
+ printf("ERROR: profile : unexpected case\n");
+ }
+ }
+}
+
+
+/* The task parameter in this and the subsequent routine is needed for
+ MiG, even though it is not used in the function itself. */
+
+kern_return_t
+mach_sample_thread (task, reply, cur_thread)
+ipc_space_t task;
+ipc_object_t reply;
+thread_t cur_thread;
+{
+/*
+ * This routine is called every time that a new thread has made
+ * a request for the sampling service. We must keep track of the
+ * correspondance between it's identity (cur_thread) and the port
+ * we are going to use as a reply port to send out the samples resulting
+ * from its execution.
+ */
+ prof_data_t pbuf;
+ vm_offset_t vmpbuf;
+
+ if (reply != MACH_PORT_NULL) {
+ if (cur_thread->thread_profiled && cur_thread->thread_profiled_own) {
+ if (reply == cur_thread->profil_buffer->prof_port)
+ return KERN_SUCCESS;
+ mach_sample_thread(MACH_PORT_NULL, cur_thread);
+ }
+ /* Start profiling this thread , do the initialization. */
+ alloc_pbuf_area(pbuf, vmpbuf);
+ if ((cur_thread->profil_buffer = pbuf) == NULLPBUF) {
+printf("ERROR:mach_sample_thread:cannot allocate pbuf\n");
+ return KERN_RESOURCE_SHORTAGE;
+ } else {
+ if (!set_pbuf_nb(pbuf, NB_PROF_BUFFER-1)) {
+printf("ERROR:mach_sample_thread:cannot set pbuf_nb\n");
+ return KERN_FAILURE;
+ }
+ reset_pbuf_area(pbuf);
+ }
+
+ pbuf->prof_port = reply;
+ cur_thread->thread_profiled = TRUE;
+ cur_thread->thread_profiled_own = TRUE;
+ if (profile_thread_id == THREAD_NULL)
+ profile_thread_id = kernel_thread(current_task(), profile_thread);
+ } else {
+ if (!cur_thread->thread_profiled_own)
+ cur_thread->thread_profiled = FALSE;
+ if (!cur_thread->thread_profiled)
+ return KERN_SUCCESS;
+
+ send_last_sample_buf(cur_thread);
+
+ /* Stop profiling this thread, do the cleanup. */
+
+ cur_thread->thread_profiled_own = FALSE;
+ cur_thread->thread_profiled = FALSE;
+ dealloc_pbuf_area(cur_thread->profil_buffer);
+ cur_thread->profil_buffer = NULLPBUF;
+ }
+
+ return KERN_SUCCESS;
+}
+
+kern_return_t
+mach_sample_task (task, reply, cur_task)
+ipc_space_t task;
+ipc_object_t reply;
+task_t cur_task;
+{
+ prof_data_t pbuf=cur_task->profil_buffer;
+ vm_offset_t vmpbuf;
+ int turnon = (reply != MACH_PORT_NULL);
+
+ if (turnon) {
+ if (cur_task->task_profiled) {
+ if (cur_task->profil_buffer->prof_port == reply)
+ return KERN_SUCCESS;
+ (void) mach_sample_task(task, MACH_PORT_NULL, cur_task);
+ }
+ if (pbuf == NULLPBUF) {
+ alloc_pbuf_area(pbuf, vmpbuf);
+ if (pbuf == NULLPBUF) {
+ return KERN_RESOURCE_SHORTAGE;
+ }
+ cur_task->profil_buffer = pbuf;
+ }
+ if (!set_pbuf_nb(pbuf, NB_PROF_BUFFER-1)) {
+ return KERN_FAILURE;
+ }
+ reset_pbuf_area(pbuf);
+ pbuf->prof_port = reply;
+ }
+
+ if (turnon != cur_task->task_profiled) {
+ int actual,i,sentone;
+ thread_t thread;
+
+ if (turnon && profile_thread_id == THREAD_NULL)
+ profile_thread_id =
+ kernel_thread(current_task(), profile_thread);
+ cur_task->task_profiled = turnon;
+ actual = cur_task->thread_count;
+ sentone = 0;
+ for (i=0, thread=(thread_t) queue_first(&cur_task->thread_list);
+ i < actual;
+ i++, thread=(thread_t) queue_next(&thread->thread_list)) {
+ if (!thread->thread_profiled_own) {
+ thread->thread_profiled = turnon;
+ if (turnon)
+ thread->profil_buffer = cur_task->profil_buffer;
+ else if (!sentone) {
+ send_last_sample_buf(thread);
+ sentone = 1;
+ }
+ }
+ }
+ if (!turnon) {
+ dealloc_pbuf_area(pbuf);
+ cur_task->profil_buffer = NULLPBUF;
+ }
+ }
+
+ return KERN_SUCCESS;
+}
+
+#endif 0