#include <mach_prof.h>
#include <mach/task_server.h>
#include <mach/thread_act_server.h>
#if MACH_PROF
#include <kern/thread.h>
#include <kern/queue.h>
#include <kern/profile.h>
#include <kern/sched_prim.h>
#include <kern/spl.h>
#include <kern/misc_protos.h>
#include <ipc/ipc_space.h>
#include <machine/machparam.h>
#include <mach/prof.h>
thread_t profile_thread_id = THREAD_NULL;
int profile_sample_count = 0;
extern kern_return_t task_suspend(task_t task);
prof_data_t pbuf_alloc(void);
void pbuf_free(
prof_data_t pbuf);
void profile_thread(void);
void send_last_sample_buf(
prof_data_t pbuf);
void
profile_thread(void)
{
spl_t s;
buffer_t buf_entry;
queue_entry_t prof_queue_entry;
prof_data_t pbuf;
kern_return_t kr;
int j;
mpqueue_init(&prof_queue);
while (TRUE) {
s = splsched();
mpdequeue_head(&prof_queue, &prof_queue_entry);
splx(s);
if ((buf_entry = (buffer_t) prof_queue_entry) == NULLPBUF) {
assert_wait((event_t) profile_thread, THREAD_UNINT);
thread_block(THREAD_CONTINUE_NULL);
if (current_thread()->wait_result != THREAD_AWAKENED)
break;
} else
{
int dropped;
pbuf = buf_entry->p_prof;
kr = send_samples(pbuf->prof_port, (void *)buf_entry->p_zone,
(mach_msg_type_number_t)buf_entry->p_index);
profile_sample_count += buf_entry->p_index;
if (kr != KERN_SUCCESS)
printf("send_samples(%x, %x, %d) error %x\n",
pbuf->prof_port, buf_entry->p_zone, buf_entry->p_index, kr);
dropped = buf_entry->p_dropped;
if (dropped > 0) {
printf("kernel: profile dropped %d sample%s\n", dropped,
dropped == 1 ? "" : "s");
buf_entry->p_dropped = 0;
}
buf_entry->p_full = FALSE;
if (buf_entry->p_wakeme)
thread_wakeup((event_t) &buf_entry->p_wakeme);
}
}
profile_thread_id = THREAD_NULL;
while (1) {
s = splsched();
mpdequeue_head(&prof_queue, &prof_queue_entry);
splx(s);
if ((buf_entry = (buffer_t) prof_queue_entry) == NULLPBUF)
break;
if (buf_entry->p_wakeme)
thread_wakeup((event_t) &buf_entry->p_wakeme);
}
#if 0
thread_halt_self();
#else
panic("profile_thread(): halt_self");
#endif
}
void
send_last_sample_buf(prof_data_t pbuf)
{
spl_t s;
buffer_t buf_entry;
if (pbuf == NULLPROFDATA)
return;
buf_entry = pbuf->prof_area + pbuf->prof_index;
buf_entry->p_prof = pbuf;
s = splsched();
if (profile_thread_id == THREAD_NULL)
splx(s);
else {
buf_entry->p_wakeme = 1;
mpenqueue_tail(&prof_queue, &buf_entry->p_list);
thread_wakeup((event_t) profile_thread);
assert_wait((event_t) &buf_entry->p_wakeme, THREAD_ABORTSAFE);
splx(s);
thread_block(THREAD_CONTINUE_NULL);
}
}
void
profile(natural_t pc,
prof_data_t pbuf)
{
natural_t inout_val = pc;
buffer_t buf_entry;
if (pbuf == NULLPROFDATA)
return;
set_pbuf_value(pbuf, &inout_val);
switch((int)inout_val) {
case 0:
if (profile_thread_id == THREAD_NULL) {
reset_pbuf_area(pbuf);
}
break;
case 1:
break;
case 2 :
if (profile_thread_id == THREAD_NULL) {
reset_pbuf_area(pbuf);
break;
}
buf_entry = (buffer_t) &pbuf->prof_area[pbuf->prof_index];
buf_entry->p_prof = pbuf;
mpenqueue_tail(&prof_queue, &buf_entry->p_list);
reset_pbuf_area(pbuf);
if (profile_thread_id != THREAD_NULL)
thread_wakeup((event_t) profile_thread);
break;
default:
printf("profile : unexpected case\n");
}
}
prof_data_t
pbuf_alloc(void)
{
register prof_data_t pbuf;
register int i;
register natural_t *zone;
pbuf = (prof_data_t)kalloc(sizeof(struct prof_data));
if (!pbuf)
return(NULLPROFDATA);
pbuf->prof_port = MACH_PORT_NULL;
for (i=0; i< NB_PROF_BUFFER; i++) {
zone = (natural_t *)kalloc(SIZE_PROF_BUFFER*sizeof(natural_t));
if (!zone) {
i--;
while (i--)
kfree((vm_offset_t)pbuf->prof_area[i].p_zone,
SIZE_PROF_BUFFER*sizeof(natural_t));
kfree((vm_offset_t)pbuf, sizeof(struct prof_data));
return(NULLPROFDATA);
}
pbuf->prof_area[i].p_zone = zone;
pbuf->prof_area[i].p_full = FALSE;
}
pbuf->prof_port = MACH_PORT_NULL;
return(pbuf);
}
void
pbuf_free(
prof_data_t pbuf)
{
register int i;
if (pbuf->prof_port)
ipc_port_release_send(pbuf->prof_port);
for(i=0; i < NB_PROF_BUFFER ; i++)
kfree((vm_offset_t)pbuf->prof_area[i].p_zone,
SIZE_PROF_BUFFER*sizeof(natural_t));
kfree((vm_offset_t)pbuf, sizeof(struct prof_data));
}
#endif
#if !MACH_PROF
kern_return_t
thread_sample(
__unused thread_t thread,
__unused ipc_port_t reply)
{
return KERN_FAILURE;
}
#else
kern_return_t
thread_sample(
thread_t thread,
ipc_port_t reply)
{
prof_data_t pbuf;
vm_offset_t vmpbuf;
if (reply != MACH_PORT_NULL) {
if (thread->profiled)
return KERN_INVALID_ARGUMENT;
pbuf = pbuf_alloc();
if ((thread->profil_buffer = pbuf) == NULLPROFDATA) {
printf("thread_sample: cannot allocate pbuf\n");
return KERN_RESOURCE_SHORTAGE;
}
else {
if (!set_pbuf_nb(pbuf, NB_PROF_BUFFER-1)) {
printf("mach_sample_thread: cannot set pbuf_nb\n");
return KERN_FAILURE;
}
reset_pbuf_area(pbuf);
}
pbuf->prof_port = reply;
thread->profiled = TRUE;
thread->profiled_own = TRUE;
if (profile_thread_id == THREAD_NULL)
profile_thread_id = kernel_thread(kernel_task, profile_thread);
} else {
if (!thread->profiled)
return(KERN_INVALID_ARGUMENT);
thread->profiled = FALSE;
if (!thread->profiled_own)
return KERN_SUCCESS;
else
thread->profiled_own = FALSE;
send_last_sample_buf(thread->profil_buffer);
pbuf_free(thread->profil_buffer);
thread->profil_buffer = NULLPROFDATA;
}
return KERN_SUCCESS;
}
#endif
#if !MACH_PROF
kern_return_t
task_sample(
__unused task_t task,
__unused ipc_port_t reply)
{
return KERN_FAILURE;
}
#else
kern_return_t
task_sample(
task_t task,
ipc_port_t reply)
{
prof_data_t pbuf=task->profil_buffer;
vm_offset_t vmpbuf;
boolean_t turnon = (reply != MACH_PORT_NULL);
if (task == TASK_NULL)
return KERN_INVALID_ARGUMENT;
if (turnon)
{
pbuf = pbuf_alloc();
task_lock(task);
if (task->task_profiled) {
task_unlock(task);
if (pbuf != NULLPROFDATA)
pbuf_free(pbuf);
return(KERN_INVALID_ARGUMENT);
}
if (pbuf == NULLPROFDATA) {
task_unlock(task);
return KERN_RESOURCE_SHORTAGE;
}
task->profil_buffer = pbuf;
if (!set_pbuf_nb(pbuf, NB_PROF_BUFFER-1)) {
pbuf_free(pbuf);
task_unlock(task);
return KERN_FAILURE;
}
reset_pbuf_area(pbuf);
pbuf->prof_port = reply;
} else {
task_lock(task);
if (!task->task_profiled) {
task_unlock(task);
return(KERN_INVALID_ARGUMENT);
}
}
if (turnon != task->task_profiled) {
int actual, i;
thread_t thread;
if (turnon && profile_thread_id == THREAD_NULL)
profile_thread_id =
kernel_thread(kernel_task, profile_thread);
task->task_profiled = turnon;
actual = task->thread_count;
for (i = 0, thread = (thread_t)queue_first(&task->threads);
i < actual;
i++, thread = (thread_t)queue_next(&thr_act->task_threads)) {
if (!thread->profiled_own) {
threadt->profiled = turnon;
if (turnon) {
threadt->profil_buffer = task->profil_buffer;
thread->profiled = TRUE;
} else {
thread->profiled = FALSE;
thread->profil_buffer = NULLPROFDATA;
}
}
}
if (!turnon) {
send_last_sample_buf(task->profil_buffer);
pbuf_free(task->profil_buffer);
task->profil_buffer = NULLPROFDATA;
}
}
task_unlock(task);
return KERN_SUCCESS;
}
#endif