|
|
1.1 root 1: /*
2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
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.
11: *
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
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22: /*
23: * @OSF_COPYRIGHT@
24: */
25: /*
26: * Mach Operating System
27: * Copyright (c) 1991,1990,1989 Carnegie Mellon University
28: * All Rights Reserved.
29: *
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.
35: *
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.
39: *
40: * Carnegie Mellon requests users of this software to return to
41: *
42: * Software Distribution Coordinator or [email protected]
43: * School of Computer Science
44: * Carnegie Mellon University
45: * Pittsburgh PA 15213-3890
46: *
47: * any improvements or extensions that they make and grant Carnegie Mellon
48: * the rights to redistribute these changes.
49: */
50:
51: /*
52: */
53: #include <mach_prof.h>
54:
55: #include <mach/task_server.h>
56: #include <mach/thread_act_server.h>
57:
58: #if MACH_PROF
59: #include <cpus.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>
65: #include <kern/spl.h>
66: #include <kern/misc_protos.h>
67: #include <ipc/ipc_space.h>
68: #include <machine/machparam.h>
69: #include <mach/prof.h>
70:
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 */
74:
75: /* Forwards */
76: prof_data_t pbuf_alloc(void);
77: void pbuf_free(
78: prof_data_t pbuf);
79: void profile_thread(void);
80: void send_last_sample_buf(
81: prof_data_t pbuf);
82:
83: /*
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_
91: * sample).
92: */
93:
94: void
95: profile_thread(void)
96: {
97: spl_t s;
98: buffer_t buf_entry;
99: queue_entry_t prof_queue_entry;
100: prof_data_t pbuf;
101: kern_return_t kr;
102: int j;
103:
104: thread_swappable(current_act(), FALSE);
105:
106: /* Initialise the queue header for the prof_queue */
107: mpqueue_init(&prof_queue);
108:
109: while (TRUE) {
110:
111: /* Dequeue the first buffer. */
112: s = splsched();
113: mpdequeue_head(&prof_queue, &prof_queue_entry);
114: splx(s);
115:
116: if ((buf_entry = (buffer_t) prof_queue_entry) == NULLPBUF) {
117: assert_wait((event_t) profile_thread, THREAD_UNINT);
118: thread_block((void (*)(void)) 0);
119: if (current_thread()->wait_result != THREAD_AWAKENED)
120: break;
121: } else
122: {
123: int dropped;
124:
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;
133: if (dropped > 0) {
134: printf("kernel: profile dropped %d sample%s\n", dropped,
135: dropped == 1 ? "" : "s");
136: buf_entry->p_dropped = 0;
137: }
138:
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);
143: }
144:
145: }
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
148: up now. */
149: profile_thread_id = THREAD_NULL;
150: while (1) {
151: s = splsched();
152: mpdequeue_head(&prof_queue, &prof_queue_entry);
153: splx(s);
154: if ((buf_entry = (buffer_t) prof_queue_entry) == NULLPBUF)
155: break;
156: if (buf_entry->p_wakeme)
157: thread_wakeup((event_t) &buf_entry->p_wakeme);
158: }
159: #if 0 /* XXXXX */
160: thread_halt_self();
161: #else
162: panic("profile_thread(): halt_self");
163: #endif /* XXXXX */
164: }
165:
166: /*
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: *****************************************************************************
171: */
172:
173: void
174: send_last_sample_buf(prof_data_t pbuf)
175: {
176: spl_t s;
177: buffer_t buf_entry;
178:
179: if (pbuf == NULLPROFDATA)
180: return;
181:
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.
187: */
188: buf_entry = pbuf->prof_area + pbuf->prof_index;
189: buf_entry->p_prof = pbuf;
190:
191: /*
192: Watch out in case profile thread exits while we are about to
193: queue data for it.
194: */
195: s = splsched();
196: if (profile_thread_id == THREAD_NULL)
197: splx(s);
198: else {
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);
203: splx(s);
204: thread_block((void (*)(void)) 0);
205: }
206: }
207:
208:
209: /*
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: *****************************************************************************
215: */
216:
217: void
218: profile(natural_t pc,
219: prof_data_t pbuf)
220: {
221: natural_t inout_val = pc;
222: buffer_t buf_entry;
223:
224: if (pbuf == NULLPROFDATA)
225: return;
226:
227: /* Inserts the PC value in the buffer of the thread */
228: set_pbuf_value(pbuf, &inout_val);
229: switch((int)inout_val) {
230: case 0:
231: if (profile_thread_id == THREAD_NULL) {
232: reset_pbuf_area(pbuf);
233: }
234: break;
235: case 1:
236: /* Normal case, value successfully inserted */
237: break;
238: case 2 :
239: /*
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.
247: */
248: if (profile_thread_id == THREAD_NULL) {
249: reset_pbuf_area(pbuf);
250: break;
251: }
252:
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);
256:
257: /* Switch to another buffer */
258: reset_pbuf_area(pbuf);
259:
260: /* Wake up the profile thread */
261: if (profile_thread_id != THREAD_NULL)
262: thread_wakeup((event_t) profile_thread);
263: break;
264:
265: default:
266: printf("profile : unexpected case\n");
267: }
268: }
269:
270: /*
271: *****************************************************************************
272: * pbuf_alloc creates a profile/trace buffer and assoc. zones for storing
273: * profiled items.
274: *****************************************************************************
275: */
276:
277: prof_data_t
278: pbuf_alloc(void)
279: {
280: register prof_data_t pbuf;
281: register int i;
282: register natural_t *zone;
283:
284: pbuf = (prof_data_t)kalloc(sizeof(struct prof_data));
285: if (!pbuf)
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));
290: if (!zone) {
291: i--;
292: while (i--)
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);
297: }
298: pbuf->prof_area[i].p_zone = zone;
299: pbuf->prof_area[i].p_full = FALSE;
300: }
301: pbuf->prof_port = MACH_PORT_NULL;
302: return(pbuf);
303: }
304:
305: /*
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: *****************************************************************************
312: */
313: void
314: pbuf_free(
315: prof_data_t pbuf)
316: {
317: register int i;
318:
319: if (pbuf->prof_port)
320: ipc_port_release_send(pbuf->prof_port);
321:
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));
326: }
327:
328: #endif /* MACH_PROF */
329:
330: /*
331: *****************************************************************************
332: * Thread_sample is used by MACH_PROF to profile a single thread, and is only
333: * stub in DCI.
334: *****************************************************************************
335: */
336:
337: kern_return_t
338: thread_sample(
339: thread_act_t thr_act,
340: ipc_port_t reply)
341: {
342: /*
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.
348: */
349: #if !MACH_PROF
350: return KERN_FAILURE;
351: #else
352: prof_data_t pbuf;
353: vm_offset_t vmpbuf;
354:
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. */
359: pbuf = pbuf_alloc();
360: if ((thr_act->profil_buffer = pbuf) == NULLPROFDATA) {
361: printf("thread_sample: cannot allocate pbuf\n");
362: return KERN_RESOURCE_SHORTAGE;
363: }
364: else {
365: if (!set_pbuf_nb(pbuf, NB_PROF_BUFFER-1)) {
366: printf("mach_sample_thread: cannot set pbuf_nb\n");
367: return KERN_FAILURE;
368: }
369: reset_pbuf_area(pbuf);
370: }
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);
376: } else {
377: if (!thr_act->act_profiled)
378: return(KERN_INVALID_ARGUMENT);
379:
380: thr_act->act_profiled = FALSE;
381: /* do not stop sampling if thread is not profiled by its own */
382:
383: if (!thr_act->act_profiled_own)
384: return KERN_SUCCESS;
385: else
386: thr_act->act_profiled_own = FALSE;
387:
388: send_last_sample_buf(thr_act->profil_buffer);
389: pbuf_free(thr_act->profil_buffer);
390: thr_act->profil_buffer = NULLPROFDATA;
391: }
392: return KERN_SUCCESS;
393: #endif /* MACH_PROF */
394: }
395:
396: /*
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: *****************************************************************************
404: */
405:
406: kern_return_t
407: task_sample(
408: task_t task,
409: ipc_port_t reply)
410: {
411: #if !MACH_PROF
412: return KERN_FAILURE;
413: #else
414: prof_data_t pbuf=task->profil_buffer;
415: vm_offset_t vmpbuf;
416: boolean_t turnon = (reply != MACH_PORT_NULL);
417:
418: if (task == TASK_NULL)
419: return KERN_INVALID_ARGUMENT;
420: if (turnon) /* Do we want to profile this task? */
421: {
422: pbuf = pbuf_alloc(); /* allocate a profile buffer */
423: task_lock(task);
424: if (task->task_profiled) { /* if it is already profiled return so */
425: task_unlock(task);
426: if (pbuf != NULLPROFDATA)
427: pbuf_free(pbuf);
428: return(KERN_INVALID_ARGUMENT);
429: }
430: if (pbuf == NULLPROFDATA) {
431: task_unlock(task);
432: return KERN_RESOURCE_SHORTAGE; /* can't allocate a buffer, quit */
433: }
434: task->profil_buffer = pbuf;
435:
436: if (!set_pbuf_nb(pbuf, NB_PROF_BUFFER-1)) {
437: pbuf_free(pbuf);
438: task_unlock(task);
439: return KERN_FAILURE;
440: }
441: reset_pbuf_area(pbuf);
442: pbuf->prof_port = reply; /* assoc. buffer with reply port */
443: } else { /* We want to stop profiling/tracing */
444: task_lock(task);
445: if (!task->task_profiled) { /* but this task is not being profiled */
446: task_unlock(task);
447: return(KERN_INVALID_ARGUMENT);
448: }
449: }
450:
451: /*
452: * turnon = FALSE && task_profile = TRUE ||
453: * turnon = TRUE && task_profile = FALSE
454: */
455:
456: if (turnon != task->task_profiled) {
457: int actual, i;
458: thread_act_t thr_act;
459:
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);
466: i < actual;
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;
470: if (turnon) {
471: thr_act->profil_buffer = task->profil_buffer;
472: thr_act->act_profiled = TRUE;
473: } else {
474: thr_act->act_profiled = FALSE;
475: thr_act->profil_buffer = NULLPROFDATA;
476: }
477: }
478: }
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;
483: }
484: }
485:
486: task_unlock(task);
487: return KERN_SUCCESS;
488: #endif /* MACH_PROF */
489: }
490:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.