2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
26 * Mach Operating System
27 * Copyright (c) 1991,1990,1989 Carnegie Mellon University
28 * All Rights Reserved.
30 * Permission to use, copy, modify and distribute this software and its
31 * documentation is hereby granted, provided that both the copyright
32 * notice and this permission notice appear in all copies of the
33 * software, derivative works or modified versions, and any portions
34 * thereof, and that both notices appear in supporting documentation.
36 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
37 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
38 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
40 * Carnegie Mellon requests users of this software to return to
42 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
43 * School of Computer Science
44 * Carnegie Mellon University
45 * Pittsburgh PA 15213-3890
47 * any improvements or extensions that they make and grant Carnegie Mellon
48 * the rights to redistribute these changes.
53 #include <mach_prof.h>
55 #include <mach/task_server.h>
56 #include <mach/thread_act_server.h>
60 #include <kern/thread.h>
61 #include <kern/thread_swap.h>
62 #include <kern/queue.h>
63 #include <kern/profile.h>
64 #include <kern/sched_prim.h>
66 #include <kern/misc_protos.h>
67 #include <ipc/ipc_space.h>
68 #include <machine/machparam.h>
69 #include <mach/prof.h>
71 thread_t profile_thread_id
= THREAD_NULL
;
72 int profile_sample_count
= 0; /* Provided for looking at from kdb. */
73 extern kern_return_t
task_suspend(task_t task
); /* ack */
76 prof_data_t
pbuf_alloc(void);
79 void profile_thread(void);
80 void send_last_sample_buf(
84 *****************************************************************************
85 * profile_thread is the profile/trace kernel support thread. It is started
86 * by a server/user request through task_sample, or thread_sample. The profile
87 * thread dequeues messages and sends them to the receive_prof thread, in the
88 * server, via the send_samples and send_notices mig interface functions. If
89 * there are no messages in the queue profile thread blocks until wakened by
90 * profile (called in from mach_clock), or last_sample (called by thread/task_
99 queue_entry_t prof_queue_entry
;
104 thread_swappable(current_act(), FALSE
);
106 /* Initialise the queue header for the prof_queue */
107 mpqueue_init(&prof_queue
);
111 /* Dequeue the first buffer. */
113 mpdequeue_head(&prof_queue
, &prof_queue_entry
);
116 if ((buf_entry
= (buffer_t
) prof_queue_entry
) == NULLPBUF
) {
117 assert_wait((event_t
) profile_thread
, THREAD_UNINT
);
118 thread_block(THREAD_CONTINUE_NULL
);
119 if (current_thread()->wait_result
!= THREAD_AWAKENED
)
125 pbuf
= buf_entry
->p_prof
;
126 kr
= send_samples(pbuf
->prof_port
, (void *)buf_entry
->p_zone
,
127 (mach_msg_type_number_t
)buf_entry
->p_index
);
128 profile_sample_count
+= buf_entry
->p_index
;
129 if (kr
!= KERN_SUCCESS
)
130 printf("send_samples(%x, %x, %d) error %x\n",
131 pbuf
->prof_port
, buf_entry
->p_zone
, buf_entry
->p_index
, kr
);
132 dropped
= buf_entry
->p_dropped
;
134 printf("kernel: profile dropped %d sample%s\n", dropped
,
135 dropped
== 1 ? "" : "s");
136 buf_entry
->p_dropped
= 0;
139 /* Indicate you've finished the dirty job */
140 buf_entry
->p_full
= FALSE
;
141 if (buf_entry
->p_wakeme
)
142 thread_wakeup((event_t
) &buf_entry
->p_wakeme
);
146 /* The profile thread has been signalled to exit. Any threads waiting
147 for the last buffer of samples to be acknowledged should be woken
149 profile_thread_id
= THREAD_NULL
;
152 mpdequeue_head(&prof_queue
, &prof_queue_entry
);
154 if ((buf_entry
= (buffer_t
) prof_queue_entry
) == NULLPBUF
)
156 if (buf_entry
->p_wakeme
)
157 thread_wakeup((event_t
) &buf_entry
->p_wakeme
);
162 panic("profile_thread(): halt_self");
167 *****************************************************************************
168 * send_last_sample is the drain mechanism to allow partial profiled buffers
169 * to be sent to the receive_prof thread in the server.
170 *****************************************************************************
174 send_last_sample_buf(prof_data_t pbuf
)
179 if (pbuf
== NULLPROFDATA
)
182 /* Ask for the sending of the last PC buffer.
183 * Make a request to the profile_thread by inserting
184 * the buffer in the send queue, and wake it up.
185 * The last buffer must be inserted at the head of the
186 * send queue, so the profile_thread handles it immediatly.
188 buf_entry
= pbuf
->prof_area
+ pbuf
->prof_index
;
189 buf_entry
->p_prof
= pbuf
;
192 Watch out in case profile thread exits while we are about to
196 if (profile_thread_id
== THREAD_NULL
)
199 buf_entry
->p_wakeme
= 1;
200 mpenqueue_tail(&prof_queue
, &buf_entry
->p_list
);
201 thread_wakeup((event_t
) profile_thread
);
202 assert_wait((event_t
) &buf_entry
->p_wakeme
, THREAD_ABORTSAFE
);
204 thread_block(THREAD_CONTINUE_NULL
);
210 *****************************************************************************
211 * add clock tick parameters to profile/trace buffers. Called from the mach_
212 * clock heritz_tick function. DCI version stores thread, sp, and pc values
213 * into the profile/trace buffers. MACH_PROF version just stores pc values.
214 *****************************************************************************
218 profile(natural_t pc
,
221 natural_t inout_val
= pc
;
224 if (pbuf
== NULLPROFDATA
)
227 /* Inserts the PC value in the buffer of the thread */
228 set_pbuf_value(pbuf
, &inout_val
);
229 switch((int)inout_val
) {
231 if (profile_thread_id
== THREAD_NULL
) {
232 reset_pbuf_area(pbuf
);
236 /* Normal case, value successfully inserted */
240 * The value we have just inserted caused the
241 * buffer to be full, and ready to be sent.
242 * If profile_thread_id is null, the profile
243 * thread has been killed. Since this generally
244 * happens only when the O/S server task of which
245 * it is a part is killed, it is not a great loss
246 * to throw away the data.
248 if (profile_thread_id
== THREAD_NULL
) {
249 reset_pbuf_area(pbuf
);
253 buf_entry
= (buffer_t
) &pbuf
->prof_area
[pbuf
->prof_index
];
254 buf_entry
->p_prof
= pbuf
;
255 mpenqueue_tail(&prof_queue
, &buf_entry
->p_list
);
257 /* Switch to another buffer */
258 reset_pbuf_area(pbuf
);
260 /* Wake up the profile thread */
261 if (profile_thread_id
!= THREAD_NULL
)
262 thread_wakeup((event_t
) profile_thread
);
266 printf("profile : unexpected case\n");
271 *****************************************************************************
272 * pbuf_alloc creates a profile/trace buffer and assoc. zones for storing
274 *****************************************************************************
280 register prof_data_t pbuf
;
282 register natural_t
*zone
;
284 pbuf
= (prof_data_t
)kalloc(sizeof(struct prof_data
));
286 return(NULLPROFDATA
);
287 pbuf
->prof_port
= MACH_PORT_NULL
;
288 for (i
=0; i
< NB_PROF_BUFFER
; i
++) {
289 zone
= (natural_t
*)kalloc(SIZE_PROF_BUFFER
*sizeof(natural_t
));
293 kfree((vm_offset_t
)pbuf
->prof_area
[i
].p_zone
,
294 SIZE_PROF_BUFFER
*sizeof(natural_t
));
295 kfree((vm_offset_t
)pbuf
, sizeof(struct prof_data
));
296 return(NULLPROFDATA
);
298 pbuf
->prof_area
[i
].p_zone
= zone
;
299 pbuf
->prof_area
[i
].p_full
= FALSE
;
301 pbuf
->prof_port
= MACH_PORT_NULL
;
306 *****************************************************************************
307 * pbuf_free free memory allocated for storing profile/trace items. Called
308 * when a task is no longer profiled/traced. Pbuf_free tears down the memory
309 * alloced in pbuf_alloc. It does not check to see if the structures are valid
310 * since it is only called by functions in this file.
311 *****************************************************************************
320 ipc_port_release_send(pbuf
->prof_port
);
322 for(i
=0; i
< NB_PROF_BUFFER
; i
++)
323 kfree((vm_offset_t
)pbuf
->prof_area
[i
].p_zone
,
324 SIZE_PROF_BUFFER
*sizeof(natural_t
));
325 kfree((vm_offset_t
)pbuf
, sizeof(struct prof_data
));
328 #endif /* MACH_PROF */
331 *****************************************************************************
332 * Thread_sample is used by MACH_PROF to profile a single thread, and is only
334 *****************************************************************************
339 thread_act_t thr_act
,
343 * This routine is called every time that a new thread has made
344 * a request for the sampling service. We must keep track of the
345 * correspondance between its identity (thread) and the port
346 * we are going to use as a reply port to send out the samples resulting
347 * from its execution.
355 if (reply
!= MACH_PORT_NULL
) {
356 if (thr_act
->act_profiled
) /* yuck! */
357 return KERN_INVALID_ARGUMENT
;
358 /* Start profiling this activation, do the initialization. */
360 if ((thr_act
->profil_buffer
= pbuf
) == NULLPROFDATA
) {
361 printf("thread_sample: cannot allocate pbuf\n");
362 return KERN_RESOURCE_SHORTAGE
;
365 if (!set_pbuf_nb(pbuf
, NB_PROF_BUFFER
-1)) {
366 printf("mach_sample_thread: cannot set pbuf_nb\n");
369 reset_pbuf_area(pbuf
);
371 pbuf
->prof_port
= reply
;
372 thr_act
->act_profiled
= TRUE
;
373 thr_act
->act_profiled_own
= TRUE
;
374 if (profile_thread_id
== THREAD_NULL
)
375 profile_thread_id
= kernel_thread(kernel_task
, profile_thread
);
377 if (!thr_act
->act_profiled
)
378 return(KERN_INVALID_ARGUMENT
);
380 thr_act
->act_profiled
= FALSE
;
381 /* do not stop sampling if thread is not profiled by its own */
383 if (!thr_act
->act_profiled_own
)
386 thr_act
->act_profiled_own
= FALSE
;
388 send_last_sample_buf(thr_act
->profil_buffer
);
389 pbuf_free(thr_act
->profil_buffer
);
390 thr_act
->profil_buffer
= NULLPROFDATA
;
393 #endif /* MACH_PROF */
397 *****************************************************************************
398 * Task_sample is used to profile/trace tasks - all thread within a task using
399 * a common profile buffer to collect items generated by the hertz_tick. For
400 * each task profiled a profile buffer is created that associates a reply port
401 * (used to send the data to a server thread), task (used for throttling), and
402 * a zone area (used to store profiled/traced items).
403 *****************************************************************************
414 prof_data_t pbuf
=task
->profil_buffer
;
416 boolean_t turnon
= (reply
!= MACH_PORT_NULL
);
418 if (task
== TASK_NULL
)
419 return KERN_INVALID_ARGUMENT
;
420 if (turnon
) /* Do we want to profile this task? */
422 pbuf
= pbuf_alloc(); /* allocate a profile buffer */
424 if (task
->task_profiled
) { /* if it is already profiled return so */
426 if (pbuf
!= NULLPROFDATA
)
428 return(KERN_INVALID_ARGUMENT
);
430 if (pbuf
== NULLPROFDATA
) {
432 return KERN_RESOURCE_SHORTAGE
; /* can't allocate a buffer, quit */
434 task
->profil_buffer
= pbuf
;
436 if (!set_pbuf_nb(pbuf
, NB_PROF_BUFFER
-1)) {
441 reset_pbuf_area(pbuf
);
442 pbuf
->prof_port
= reply
; /* assoc. buffer with reply port */
443 } else { /* We want to stop profiling/tracing */
445 if (!task
->task_profiled
) { /* but this task is not being profiled */
447 return(KERN_INVALID_ARGUMENT
);
452 * turnon = FALSE && task_profile = TRUE ||
453 * turnon = TRUE && task_profile = FALSE
456 if (turnon
!= task
->task_profiled
) {
458 thread_act_t thr_act
;
460 if (turnon
&& profile_thread_id
== THREAD_NULL
) /* 1st time thru? */
461 profile_thread_id
= /* then start profile thread. */
462 kernel_thread(kernel_task
, profile_thread
);
463 task
->task_profiled
= turnon
;
464 actual
= task
->thr_act_count
;
465 for (i
= 0, thr_act
= (thread_act_t
)queue_first(&task
->thr_acts
);
467 i
++, thr_act
= (thread_act_t
)queue_next(&thr_act
->thr_acts
)) {
468 if (!thr_act
->act_profiled_own
) {
469 thr_act
->act_profiled
= turnon
;
471 thr_act
->profil_buffer
= task
->profil_buffer
;
472 thr_act
->act_profiled
= TRUE
;
474 thr_act
->act_profiled
= FALSE
;
475 thr_act
->profil_buffer
= NULLPROFDATA
;
479 if (!turnon
) { /* drain buffers and clean-up */
480 send_last_sample_buf(task
->profil_buffer
);
481 pbuf_free(task
->profil_buffer
);
482 task
->profil_buffer
= NULLPROFDATA
;
488 #endif /* MACH_PROF */