/* * 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 #include #include #include #include extern vm_map_t kernel_map; /* can be discarded, defined in */ 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 ;jprof_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 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 immediately. */ 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(thread_no_continuation); } 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 */