2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
34 * Mach Operating System
35 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
36 * All Rights Reserved.
38 * Permission to use, copy, modify and distribute this software and its
39 * documentation is hereby granted, provided that both the copyright
40 * notice and this permission notice appear in all copies of the
41 * software, derivative works or modified versions, and any portions
42 * thereof, and that both notices appear in supporting documentation.
44 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
45 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
46 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
48 * Carnegie Mellon requests users of this software to return to
50 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
51 * School of Computer Science
52 * Carnegie Mellon University
53 * Pittsburgh PA 15213-3890
55 * any improvements or extensions that they make and grant Carnegie Mellon
56 * the rights to redistribute these changes.
61 #include <mach_prof.h>
63 #include <mach/task_server.h>
64 #include <mach/thread_act_server.h>
67 #include <kern/thread.h>
68 #include <kern/queue.h>
69 #include <kern/profile.h>
70 #include <kern/sched_prim.h>
72 #include <kern/misc_protos.h>
73 #include <ipc/ipc_space.h>
74 #include <machine/machparam.h>
75 #include <mach/prof.h>
77 thread_t profile_thread_id
= THREAD_NULL
;
78 int profile_sample_count
= 0; /* Provided for looking at from kdb. */
79 extern kern_return_t
task_suspend(task_t task
); /* ack */
82 prof_data_t
pbuf_alloc(void);
85 void profile_thread(void);
86 void send_last_sample_buf(
90 *****************************************************************************
91 * profile_thread is the profile/trace kernel support thread. It is started
92 * by a server/user request through task_sample, or thread_sample. The profile
93 * thread dequeues messages and sends them to the receive_prof thread, in the
94 * server, via the send_samples and send_notices mig interface functions. If
95 * there are no messages in the queue profile thread blocks until wakened by
96 * profile (called in from mach_clock), or last_sample (called by thread/task_
105 queue_entry_t prof_queue_entry
;
110 /* Initialise the queue header for the prof_queue */
111 mpqueue_init(&prof_queue
);
115 /* Dequeue the first buffer. */
117 mpdequeue_head(&prof_queue
, &prof_queue_entry
);
120 if ((buf_entry
= (buffer_t
) prof_queue_entry
) == NULLPBUF
) {
121 assert_wait((event_t
) profile_thread
, THREAD_UNINT
);
122 thread_block(THREAD_CONTINUE_NULL
);
123 if (current_thread()->wait_result
!= THREAD_AWAKENED
)
129 pbuf
= buf_entry
->p_prof
;
130 kr
= send_samples(pbuf
->prof_port
, (void *)buf_entry
->p_zone
,
131 (mach_msg_type_number_t
)buf_entry
->p_index
);
132 profile_sample_count
+= buf_entry
->p_index
;
133 if (kr
!= KERN_SUCCESS
)
134 printf("send_samples(%x, %x, %d) error %x\n",
135 pbuf
->prof_port
, buf_entry
->p_zone
, buf_entry
->p_index
, kr
);
136 dropped
= buf_entry
->p_dropped
;
138 printf("kernel: profile dropped %d sample%s\n", dropped
,
139 dropped
== 1 ? "" : "s");
140 buf_entry
->p_dropped
= 0;
143 /* Indicate you've finished the dirty job */
144 buf_entry
->p_full
= FALSE
;
145 if (buf_entry
->p_wakeme
)
146 thread_wakeup((event_t
) &buf_entry
->p_wakeme
);
150 /* The profile thread has been signalled to exit. Any threads waiting
151 for the last buffer of samples to be acknowledged should be woken
153 profile_thread_id
= THREAD_NULL
;
156 mpdequeue_head(&prof_queue
, &prof_queue_entry
);
158 if ((buf_entry
= (buffer_t
) prof_queue_entry
) == NULLPBUF
)
160 if (buf_entry
->p_wakeme
)
161 thread_wakeup((event_t
) &buf_entry
->p_wakeme
);
166 panic("profile_thread(): halt_self");
171 *****************************************************************************
172 * send_last_sample is the drain mechanism to allow partial profiled buffers
173 * to be sent to the receive_prof thread in the server.
174 *****************************************************************************
178 send_last_sample_buf(prof_data_t pbuf
)
183 if (pbuf
== NULLPROFDATA
)
186 /* Ask for the sending of the last PC buffer.
187 * Make a request to the profile_thread by inserting
188 * the buffer in the send queue, and wake it up.
189 * The last buffer must be inserted at the head of the
190 * send queue, so the profile_thread handles it immediatly.
192 buf_entry
= pbuf
->prof_area
+ pbuf
->prof_index
;
193 buf_entry
->p_prof
= pbuf
;
196 Watch out in case profile thread exits while we are about to
200 if (profile_thread_id
== THREAD_NULL
)
203 buf_entry
->p_wakeme
= 1;
204 mpenqueue_tail(&prof_queue
, &buf_entry
->p_list
);
205 thread_wakeup((event_t
) profile_thread
);
206 assert_wait((event_t
) &buf_entry
->p_wakeme
, THREAD_ABORTSAFE
);
208 thread_block(THREAD_CONTINUE_NULL
);
214 *****************************************************************************
215 * add clock tick parameters to profile/trace buffers. Called from the mach_
216 * clock heritz_tick function. DCI version stores thread, sp, and pc values
217 * into the profile/trace buffers. MACH_PROF version just stores pc values.
218 *****************************************************************************
222 profile(natural_t pc
,
225 natural_t inout_val
= pc
;
228 if (pbuf
== NULLPROFDATA
)
231 /* Inserts the PC value in the buffer of the thread */
232 set_pbuf_value(pbuf
, &inout_val
);
233 switch((int)inout_val
) {
235 if (profile_thread_id
== THREAD_NULL
) {
236 reset_pbuf_area(pbuf
);
240 /* Normal case, value successfully inserted */
244 * The value we have just inserted caused the
245 * buffer to be full, and ready to be sent.
246 * If profile_thread_id is null, the profile
247 * thread has been killed. Since this generally
248 * happens only when the O/S server task of which
249 * it is a part is killed, it is not a great loss
250 * to throw away the data.
252 if (profile_thread_id
== THREAD_NULL
) {
253 reset_pbuf_area(pbuf
);
257 buf_entry
= (buffer_t
) &pbuf
->prof_area
[pbuf
->prof_index
];
258 buf_entry
->p_prof
= pbuf
;
259 mpenqueue_tail(&prof_queue
, &buf_entry
->p_list
);
261 /* Switch to another buffer */
262 reset_pbuf_area(pbuf
);
264 /* Wake up the profile thread */
265 if (profile_thread_id
!= THREAD_NULL
)
266 thread_wakeup((event_t
) profile_thread
);
270 printf("profile : unexpected case\n");
275 *****************************************************************************
276 * pbuf_alloc creates a profile/trace buffer and assoc. zones for storing
278 *****************************************************************************
284 register prof_data_t pbuf
;
286 register natural_t
*zone
;
288 pbuf
= (prof_data_t
)kalloc(sizeof(struct prof_data
));
290 return(NULLPROFDATA
);
291 pbuf
->prof_port
= MACH_PORT_NULL
;
292 for (i
=0; i
< NB_PROF_BUFFER
; i
++) {
293 zone
= (natural_t
*)kalloc(SIZE_PROF_BUFFER
*sizeof(natural_t
));
297 kfree((vm_offset_t
)pbuf
->prof_area
[i
].p_zone
,
298 SIZE_PROF_BUFFER
*sizeof(natural_t
));
299 kfree((vm_offset_t
)pbuf
, sizeof(struct prof_data
));
300 return(NULLPROFDATA
);
302 pbuf
->prof_area
[i
].p_zone
= zone
;
303 pbuf
->prof_area
[i
].p_full
= FALSE
;
305 pbuf
->prof_port
= MACH_PORT_NULL
;
310 *****************************************************************************
311 * pbuf_free free memory allocated for storing profile/trace items. Called
312 * when a task is no longer profiled/traced. Pbuf_free tears down the memory
313 * alloced in pbuf_alloc. It does not check to see if the structures are valid
314 * since it is only called by functions in this file.
315 *****************************************************************************
324 ipc_port_release_send(pbuf
->prof_port
);
326 for(i
=0; i
< NB_PROF_BUFFER
; i
++)
327 kfree((vm_offset_t
)pbuf
->prof_area
[i
].p_zone
,
328 SIZE_PROF_BUFFER
*sizeof(natural_t
));
329 kfree((vm_offset_t
)pbuf
, sizeof(struct prof_data
));
332 #endif /* MACH_PROF */
335 *****************************************************************************
336 * Thread_sample is used by MACH_PROF to profile a single thread, and is only
338 *****************************************************************************
344 __unused thread_t thread
,
345 __unused ipc_port_t reply
)
356 * This routine is called every time that a new thread has made
357 * a request for the sampling service. We must keep track of the
358 * correspondance between its identity (thread) and the port
359 * we are going to use as a reply port to send out the samples resulting
360 * from its execution.
365 if (reply
!= MACH_PORT_NULL
) {
366 if (thread
->profiled
) /* yuck! */
367 return KERN_INVALID_ARGUMENT
;
368 /* Start profiling this activation, do the initialization. */
370 if ((thread
->profil_buffer
= pbuf
) == NULLPROFDATA
) {
371 printf("thread_sample: cannot allocate pbuf\n");
372 return KERN_RESOURCE_SHORTAGE
;
375 if (!set_pbuf_nb(pbuf
, NB_PROF_BUFFER
-1)) {
376 printf("mach_sample_thread: cannot set pbuf_nb\n");
379 reset_pbuf_area(pbuf
);
381 pbuf
->prof_port
= reply
;
382 thread
->profiled
= TRUE
;
383 thread
->profiled_own
= TRUE
;
384 if (profile_thread_id
== THREAD_NULL
)
385 profile_thread_id
= kernel_thread(kernel_task
, profile_thread
);
387 if (!thread
->profiled
)
388 return(KERN_INVALID_ARGUMENT
);
390 thread
->profiled
= FALSE
;
391 /* do not stop sampling if thread is not profiled by its own */
393 if (!thread
->profiled_own
)
396 thread
->profiled_own
= FALSE
;
398 send_last_sample_buf(thread
->profil_buffer
);
399 pbuf_free(thread
->profil_buffer
);
400 thread
->profil_buffer
= NULLPROFDATA
;
404 #endif /* MACH_PROF */
407 *****************************************************************************
408 * Task_sample is used to profile/trace tasks - all thread within a task using
409 * a common profile buffer to collect items generated by the hertz_tick. For
410 * each task profiled a profile buffer is created that associates a reply port
411 * (used to send the data to a server thread), task (used for throttling), and
412 * a zone area (used to store profiled/traced items).
413 *****************************************************************************
419 __unused task_t task
,
420 __unused ipc_port_t reply
)
430 prof_data_t pbuf
=task
->profil_buffer
;
432 boolean_t turnon
= (reply
!= MACH_PORT_NULL
);
434 if (task
== TASK_NULL
)
435 return KERN_INVALID_ARGUMENT
;
436 if (turnon
) /* Do we want to profile this task? */
438 pbuf
= pbuf_alloc(); /* allocate a profile buffer */
440 if (task
->task_profiled
) { /* if it is already profiled return so */
442 if (pbuf
!= NULLPROFDATA
)
444 return(KERN_INVALID_ARGUMENT
);
446 if (pbuf
== NULLPROFDATA
) {
448 return KERN_RESOURCE_SHORTAGE
; /* can't allocate a buffer, quit */
450 task
->profil_buffer
= pbuf
;
452 if (!set_pbuf_nb(pbuf
, NB_PROF_BUFFER
-1)) {
457 reset_pbuf_area(pbuf
);
458 pbuf
->prof_port
= reply
; /* assoc. buffer with reply port */
459 } else { /* We want to stop profiling/tracing */
461 if (!task
->task_profiled
) { /* but this task is not being profiled */
463 return(KERN_INVALID_ARGUMENT
);
468 * turnon = FALSE && task_profile = TRUE ||
469 * turnon = TRUE && task_profile = FALSE
472 if (turnon
!= task
->task_profiled
) {
476 if (turnon
&& profile_thread_id
== THREAD_NULL
) /* 1st time thru? */
477 profile_thread_id
= /* then start profile thread. */
478 kernel_thread(kernel_task
, profile_thread
);
479 task
->task_profiled
= turnon
;
480 actual
= task
->thread_count
;
481 for (i
= 0, thread
= (thread_t
)queue_first(&task
->threads
);
483 i
++, thread
= (thread_t
)queue_next(&thr_act
->task_threads
)) {
484 if (!thread
->profiled_own
) {
485 threadt
->profiled
= turnon
;
487 threadt
->profil_buffer
= task
->profil_buffer
;
488 thread
->profiled
= TRUE
;
490 thread
->profiled
= FALSE
;
491 thread
->profil_buffer
= NULLPROFDATA
;
495 if (!turnon
) { /* drain buffers and clean-up */
496 send_last_sample_buf(task
->profil_buffer
);
497 pbuf_free(task
->profil_buffer
);
498 task
->profil_buffer
= NULLPROFDATA
;
505 #endif /* MACH_PROF */