/*
- * Copyright (c) 2002-20014 Apple Inc. All rights reserved.
+ * Copyright (c) 2002-2016 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <stdio.h>
#include <stdlib.h>
#include <libproc.h>
+#include <assert.h>
#include "common.h"
+#pragma mark kobject to name hash table implementation
+
+#if (K2N_TABLE_SIZE & (K2N_TABLE_SIZE - 1) != 0)
+#error K2N_TABLE_SIZE must be a power of two
+#endif
+
+#define K2N_TABLE_MASK (K2N_TABLE_SIZE - 1)
+
+static uint32_t k2n_hash(natural_t kobject) {
+ return (uint64_t)kobject * 2654435761 >> 32;
+}
+
+static struct k2n_table_node *k2n_table_lookup_next_internal(struct k2n_table_node *node, natural_t kobject) {
+ while (node) {
+ if (kobject == node->kobject)
+ return node;
+
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natural_t kobject) {
+ if (!node) {
+ return NULL;
+ }
+ return k2n_table_lookup_next_internal(node->next, kobject);
+}
+
+struct k2n_table_node *k2n_table_lookup(struct k2n_table_node **table, natural_t kobject) {
+ uint32_t hv;
+ struct k2n_table_node *node;
+
+ hv = k2n_hash(kobject);
+
+ node = table[hv & K2N_TABLE_MASK];
+
+ return k2n_table_lookup_next_internal(node, kobject);
+}
+
+static void k2n_table_enter(struct k2n_table_node **table, natural_t kobject, ipc_info_name_t *info_name) {
+ uint32_t hv;
+ struct k2n_table_node *node;
+
+ hv = k2n_hash(kobject);
+
+ node = malloc(sizeof (struct k2n_table_node));
+ assert(node);
+
+ node->kobject = kobject;
+ node->info_name = info_name;
+ assert(kobject == info_name->iin_object);
+
+ node->next = table[hv & K2N_TABLE_MASK];
+ table[hv & K2N_TABLE_MASK] = node;
+}
+
+#pragma mark -
+
static my_per_task_info_t NOT_FOUND_TASK_INFO = {
.task = NULL,
.task_kobject = NULL,
.tree = NULL,
.treeCount = 0,
.valid = FALSE,
+ .k2ntable = {0},
.processName = "Unknown",
+ .exceptionInfo = {0},
+ .threadInfos = NULL,
+ .threadCount = 0,
+ .threadExceptionInfos = NULL
};
char * get_task_name_by_pid(pid_t pid);
}
}
-kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_t target_task)
+kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_read_t target_task)
{
+ int i;
kern_return_t ret = KERN_SUCCESS;
unsigned int kotype = 0;
vm_offset_t kobject = (vm_offset_t)0;
-
+
taskinfo->task = target_task;
pid_for_task(target_task, &taskinfo->pid);
-
- ret = mach_port_space_info(target_task, &taskinfo->info,
- &taskinfo->table, &taskinfo->tableCount,
- &taskinfo->tree, &taskinfo->treeCount);
-
+
+ ret = task_get_exception_ports_info(taskinfo->task, EXC_MASK_ALL, taskinfo->exceptionInfo.masks, &taskinfo->exceptionInfo.count, taskinfo->exceptionInfo.ports_info, taskinfo->exceptionInfo.behaviors, taskinfo->exceptionInfo.flavors);
+
+ if (ret != KERN_SUCCESS) {
+ fprintf(stderr, "task_get_exception_ports_info() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
+ taskinfo->pid = 0;
+ }
+
+ /* collect threads port as well */
+ taskinfo->threadCount = 0;
+ thread_act_array_t threadPorts;
+ ret = task_threads(taskinfo->task, &threadPorts, &taskinfo->threadCount);
+
+ if (ret != KERN_SUCCESS) {
+ fprintf(stderr, "task_threads() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
+ taskinfo->threadCount = 0;
+ } else {
+ /* collect the thread information */
+ taskinfo->threadInfos = (struct my_per_thread_info *)malloc(sizeof(struct my_per_thread_info) * taskinfo->threadCount);
+ bzero(taskinfo->threadInfos, sizeof(struct my_per_thread_info) * taskinfo->threadCount);
+
+ /* now collect exception ports for each of those threads as well */
+ taskinfo->threadExceptionInfos = (struct exc_port_info *) malloc(sizeof(struct exc_port_info) * taskinfo->threadCount);
+ boolean_t found_exception = false;
+ for (int i = 0; i < taskinfo->threadCount; i++) {
+ unsigned th_kobject = 0;
+ unsigned th_kotype = 0;
+ ipc_voucher_t th_voucher = IPC_VOUCHER_NULL;
+ thread_identifier_info_data_t th_info;
+ mach_msg_type_number_t th_info_count = THREAD_IDENTIFIER_INFO_COUNT;
+ struct exc_port_info *excinfo = &(taskinfo->threadExceptionInfos[i]);
+
+ ret = thread_get_exception_ports_info(threadPorts[i], EXC_MASK_ALL, excinfo->masks, &excinfo->count, excinfo->ports_info, excinfo->behaviors, excinfo->flavors);
+ if (ret != KERN_SUCCESS){
+ fprintf(stderr, "thread_get_exception_ports_info() failed: pid: %d thread: %d error %s\n", taskinfo->pid, threadPorts[i], mach_error_string(ret));
+ }
+
+ if (excinfo->count != 0) {
+ found_exception = true;
+ }
+
+ taskinfo->threadInfos[i].thread = threadPorts[i];
+
+ if (KERN_SUCCESS == mach_port_kernel_object(mach_task_self(), threadPorts[i], &th_kotype, &th_kobject)) {
+ taskinfo->threadInfos[i].th_kobject = th_kobject;
+ if (KERN_SUCCESS == thread_info(threadPorts[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&th_info, &th_info_count)) {
+ taskinfo->threadInfos[i].th_id = th_info.thread_id;
+ }
+ }
+
+ if (KERN_SUCCESS == thread_get_mach_voucher(threadPorts[i], 0, &th_voucher) && th_voucher != IPC_VOUCHER_NULL) {
+ char *detail = copy_voucher_detail(mach_task_self(), th_voucher, NULL);
+ taskinfo->threadInfos[i].voucher_detail = strndup(detail, VOUCHER_DETAIL_MAXLEN);
+ free(detail);
+
+ mach_port_deallocate(mach_task_self(), th_voucher);
+ }
+
+ mach_port_deallocate(mach_task_self(), threadPorts[i]);
+ threadPorts[i] = MACH_PORT_NULL;
+ }
+
+ if (found_exception == false) {
+ free(taskinfo->threadExceptionInfos);
+ taskinfo->threadExceptionInfos = NULL;
+ }
+
+ }
+
+ vm_deallocate(mach_task_self(), threadPorts, taskinfo->threadCount * sizeof(thread_act_t));
+ threadPorts = NULL;
+
+ ret = mach_port_space_info(target_task, &taskinfo->info, &taskinfo->table, &taskinfo->tableCount, &taskinfo->tree, &taskinfo->treeCount);
+
if (ret != KERN_SUCCESS) {
fprintf(stderr, "mach_port_space_info() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
taskinfo->pid = 0;
return ret;
}
-
+
+ bzero(taskinfo->k2ntable, K2N_TABLE_SIZE * sizeof (struct k2n_table_node *));
+ for (i = 0; i < taskinfo->tableCount; i++) {
+ k2n_table_enter(taskinfo->k2ntable, taskinfo->table[i].iin_object, &taskinfo->table[i]);
+ }
+
proc_pid_to_name(taskinfo->pid, taskinfo->processName);
+
ret = mach_port_kernel_object(mach_task_self(), taskinfo->task, &kotype, (unsigned *)&kobject);
- if (ret == KERN_SUCCESS && kotype == IKOT_TASK) {
+ /* Now that we are using read ports, kotype should be checked against IKOT_TASK_READ */
+ if (ret == KERN_SUCCESS && kotype == IKOT_TASK_READ) {
taskinfo->task_kobject = kobject;
taskinfo->valid = TRUE;
}
-
+
return ret;
}
-struct exc_port_info {
- mach_msg_type_number_t count;
- mach_port_t ports[EXC_TYPES_COUNT];
- exception_mask_t masks[EXC_TYPES_COUNT];
- exception_behavior_t behaviors[EXC_TYPES_COUNT];
- thread_state_flavor_t flavors[EXC_TYPES_COUNT];
-};
-
void get_exc_behavior_string(exception_behavior_t b, char *out_string, size_t len)
{
out_string[0]='\0';
-
+
if (b & MACH_EXCEPTION_CODES)
- strncat(out_string, "MACH +", len);
+ strlcat(out_string, "MACH +", len);
switch (b & ~MACH_EXCEPTION_CODES) {
case EXCEPTION_DEFAULT:
- strncat(out_string, " DEFAULT", len);
+ strlcat(out_string, " DEFAULT", len);
break;
case EXCEPTION_STATE:
- strncat(out_string, " STATE", len);
+ strlcat(out_string, " STATE", len);
break;
case EXCEPTION_STATE_IDENTITY:
- strncat(out_string, " IDENTITY", len);
+ strlcat(out_string, " IDENTITY", len);
break;
default:
- strncat(out_string, " UNKNOWN", len);
+ strlcat(out_string, " UNKNOWN", len);
}
}
void get_exc_mask_string(exception_mask_t m, char *out_string, size_t len)
{
out_string[0]='\0';
-
+
if (m & (1<<EXC_BAD_ACCESS))
- strncat(out_string, " BAD_ACCESS", len);
+ strlcat(out_string, " BAD_ACCESS", len);
if (m & (1<<EXC_BAD_INSTRUCTION))
- strncat(out_string," BAD_INSTRUCTION", len);
+ strlcat(out_string," BAD_INSTRUCTION", len);
if (m & (1<<EXC_ARITHMETIC))
- strncat(out_string," ARITHMETIC", len);
+ strlcat(out_string," ARITHMETIC", len);
if (m & (1<<EXC_EMULATION))
- strncat(out_string," EMULATION", len);
+ strlcat(out_string," EMULATION", len);
if (m & (1<<EXC_SOFTWARE))
- strncat(out_string," SOFTWARE", len);
+ strlcat(out_string," SOFTWARE", len);
if (m & (1<<EXC_BREAKPOINT))
- strncat(out_string," BREAKPOINT", len);
+ strlcat(out_string," BREAKPOINT", len);
if (m & (1<<EXC_SYSCALL))
- strncat(out_string," SYSCALL", len);
+ strlcat(out_string," SYSCALL", len);
if (m & (1<<EXC_MACH_SYSCALL))
- strncat(out_string," MACH_SYSCALL", len);
+ strlcat(out_string," MACH_SYSCALL", len);
if (m & (1<<EXC_RPC_ALERT))
- strncat(out_string," RPC_ALERT", len);
+ strlcat(out_string," RPC_ALERT", len);
if (m & (1<<EXC_CRASH))
- strncat(out_string," CRASH", len);
+ strlcat(out_string," CRASH", len);
if (m & (1<<EXC_RESOURCE))
- strncat(out_string," RESOURCE", len);
+ strlcat(out_string," RESOURCE", len);
if (m & (1<<EXC_GUARD))
- strncat(out_string," GUARD", len);
-
+ strlcat(out_string," GUARD", len);
}
-kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo)
+kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo, JSON_t json)
{
- kern_return_t kr = KERN_SUCCESS;
- exception_mask_t mask = EXC_MASK_ALL;
- struct exc_port_info excinfo;
+
char behavior_string[30];
char mask_string[200];
-
- kr = task_get_exception_ports(taskinfo->task, mask, excinfo.masks, &excinfo.count, excinfo.ports, excinfo.behaviors, excinfo.flavors);
-
- if (kr != KERN_SUCCESS) {
- fprintf(stderr, "Failed task_get_exception_ports task: %d pid: %d\n", taskinfo->task, taskinfo->pid);
- return kr;
- }
+
+ JSON_KEY(json, exception_ports);
+ JSON_ARRAY_BEGIN(json);
+
boolean_t header_required = TRUE;
- for (int i = 0; i < excinfo.count; i++) {
- if (excinfo.ports[i] != MACH_PORT_NULL) {
+ for (int i = 0; i < taskinfo->exceptionInfo.count; i++) {
+ if (taskinfo->exceptionInfo.ports_info[i].iip_port_object != 0) {
+ my_per_task_info_t * _found_task;
+
if (header_required) {
- printf("port flavor <behaviors> mask \n");
+
+ printf(" exc_port_object receiver_task flavor <behaviors> mask \n");
header_required = FALSE;
}
- get_exc_behavior_string(excinfo.behaviors[i], behavior_string, sizeof(behavior_string));
- get_exc_mask_string(excinfo.masks[i], mask_string, 200);
- printf("0x%08x 0x%03x <%s> %s \n" , excinfo.ports[i], excinfo.flavors[i], behavior_string, mask_string);
- mach_port_deallocate(mach_task_self(), excinfo.ports[i]);
+ get_exc_behavior_string(taskinfo->exceptionInfo.behaviors[i], behavior_string, sizeof(behavior_string));
+ get_exc_mask_string(taskinfo->exceptionInfo.masks[i], mask_string, sizeof(mask_string));
+
+ JSON_OBJECT_BEGIN(json);
+ JSON_OBJECT_SET(json, port_object, "0x%08x", taskinfo->exceptionInfo.ports_info[i].iip_port_object);
+ JSON_OBJECT_SET(json, receiver_object, "0x%08x", taskinfo->exceptionInfo.ports_info[i].iip_receiver_object);
+ JSON_OBJECT_SET(json, flavor, "0x%03x", taskinfo->exceptionInfo.flavors[i]);
+ JSON_OBJECT_SET(json, behavior, "%s", behavior_string);
+ JSON_OBJECT_SET(json, mask, "%s", mask_string);
+ JSON_OBJECT_END(json); // exception port
+
+ _found_task = get_taskinfo_by_kobject((natural_t)taskinfo->exceptionInfo.ports_info[i].iip_receiver_object);
+
+ printf(" 0x%08x (%d) %s 0x%03x <%s> %s \n",
+ taskinfo->exceptionInfo.ports_info[i].iip_port_object,
+ _found_task->pid,
+ _found_task->processName,
+ taskinfo->exceptionInfo.flavors[i], behavior_string, mask_string);
}
-
+
}
-
- return kr;
+
+ JSON_ARRAY_END(json); // exception ports
+
+ return KERN_SUCCESS;
}
-kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
+kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo, JSON_t json)
{
kern_return_t kret = KERN_SUCCESS;
- thread_act_array_t threadlist;
- mach_msg_type_number_t threadcount=0;
- kret = task_threads(taskinfo->task, &threadlist, &threadcount);
+ mach_msg_type_number_t threadcount = taskinfo->threadCount;
boolean_t header_required = TRUE;
boolean_t newline_required = TRUE;
- unsigned th_kobject = 0;
- unsigned th_kotype = 0;
-
+ struct my_per_thread_info * info = NULL;
+
+ JSON_KEY(json, threads);
+ JSON_ARRAY_BEGIN(json);
+
for (int i = 0; i < threadcount; i++) {
+ JSON_OBJECT_BEGIN(json);
+
+ info = &taskinfo->threadInfos[i];
if (header_required) {
- printf("Threads Thread-ID DispatchQ Port Description.");
+ printf("Thread_KObject Thread-ID Port Description.");
header_required = FALSE;
}
-
+
if (newline_required) {
printf("\n");
}
newline_required = TRUE;
- if (KERN_SUCCESS == mach_port_kernel_object(mach_task_self(), threadlist[i], &th_kotype, &th_kobject)) {
+
+ if (info->th_kobject != 0) {
/* TODO: Should print tid and stuff */
- thread_identifier_info_data_t th_info;
- mach_msg_type_number_t th_info_count = THREAD_IDENTIFIER_INFO_COUNT;
- printf("0x%08x ", th_kobject);
- if (KERN_SUCCESS == thread_info(threadlist[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&th_info, &th_info_count)) {
- printf("%16llu 0x%016llx ", th_info.thread_id, th_info.dispatch_qaddr);
- }
- else
- printf(" ");
-
+ printf("0x%08x ", info->th_kobject);
+ printf("0x%llx ", info->th_id);
+
+ JSON_OBJECT_SET(json, kobject, "0x%08x", info->th_kobject);
+ JSON_OBJECT_SET(json, tid, "0x%llx", info->th_id);
}
- ipc_voucher_t th_voucher = IPC_VOUCHER_NULL;
- if (KERN_SUCCESS == thread_get_mach_voucher(threadlist[i], 0, &th_voucher) && th_voucher != IPC_VOUCHER_NULL) {
- show_voucher_detail(mach_task_self(), th_voucher);
- mach_port_deallocate(mach_task_self(), th_voucher);
- newline_required = FALSE;
+ if (info->voucher_detail != NULL) {
+ /* TODO: include voucher detail in JSON */
+ printf("%s\n", info->voucher_detail);
+ }
+
+ JSON_KEY(json, exception_ports);
+ JSON_ARRAY_BEGIN(json);
+
+ /* print the thread exception ports also */
+ if (taskinfo->threadExceptionInfos != NULL)
+ {
+
+ struct exc_port_info *excinfo = &taskinfo->threadExceptionInfos[i];
+ char behavior_string[30];
+ char mask_string[200];
+
+ if (excinfo->count > 0) {
+ boolean_t header_required = TRUE;
+ for (int i = 0; i < excinfo->count; i++) {
+ JSON_OBJECT_BEGIN(json);
+
+ if (excinfo->ports_info[i].iip_port_object != 0) {
+ if (header_required) {
+ printf("\n exc_port_object exc_port_receiver flavor <behaviors> mask -> name owner\n");
+ header_required = FALSE;
+ }
+ get_exc_behavior_string(excinfo->behaviors[i], behavior_string, sizeof(behavior_string));
+ get_exc_mask_string(excinfo->masks[i], mask_string, sizeof(mask_string));
+
+ JSON_OBJECT_SET(json, port_object, "0x%08x", excinfo->ports_info[i].iip_port_object);
+ JSON_OBJECT_SET(json, receiver_object, "0x%08x", excinfo->ports_info[i].iip_receiver_object);
+ JSON_OBJECT_SET(json, flavor, "0x%03x", excinfo->flavors[i]);
+ JSON_OBJECT_SET(json, behavior, "%s", behavior_string);
+ JSON_OBJECT_SET(json, mask, "%s", mask_string);
+
+ printf(" 0x%08x 0x%08x 0x%03x <%s> %s " , excinfo->ports_info[i].iip_port_object, excinfo->ports_info[i].iip_receiver_object, excinfo->flavors[i], behavior_string, mask_string);
+
+ my_per_task_info_t *recv_holder_taskinfo;
+ mach_port_name_t recv_name = MACH_PORT_NULL;
+ if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right_info(excinfo->ports_info[i], &recv_holder_taskinfo, &recv_name)) {
+ JSON_OBJECT_SET(json, name, "0x%08x", recv_name);
+ JSON_OBJECT_SET(json, ipc-object, "0x%08x", excinfo->ports_info[i].iip_port_object);
+ JSON_OBJECT_SET(json, pid, %d, recv_holder_taskinfo->pid);
+ JSON_OBJECT_SET(json, process, "%s", recv_holder_taskinfo->processName);
+
+ printf(" -> 0x%08x 0x%08x (%d) %s\n",
+ recv_name,
+ excinfo->ports_info[i].iip_port_object,
+ recv_holder_taskinfo->pid,
+ recv_holder_taskinfo->processName);
+ } else {
+ fprintf(stderr, "failed to find");
+ }
+
+ printf("\n");
+
+ }
+ JSON_OBJECT_END(json); // exception port
+ }
+ }
}
- mach_port_deallocate(mach_task_self(), threadlist[i]);
+ JSON_ARRAY_END(json); // exception ports
+ JSON_OBJECT_END(json); // thread
}
+
+ JSON_ARRAY_END(json); // threads
+ printf("\n");
return kret;
}
return retval;
}
-kern_return_t get_taskinfo_of_receiver_by_send_right(ipc_info_name_t *sendright, my_per_task_info_t **out_taskinfo, mach_port_name_t *out_recv_info)
+static kern_return_t _get_taskinfo_of_receiver_by_send_right(natural_t kobject, my_per_task_info_t **out_taskinfo, mach_port_name_t *out_recv_info)
{
- kern_return_t retval = KERN_FAILURE;
- boolean_t found = FALSE;
*out_taskinfo = &NOT_FOUND_TASK_INFO;
-
- for (int j = 0; j < global_taskcount && !found; j++) {
- for (int k = 0; k < global_taskinfo[j].tableCount && !found; k++) {
- if ((global_taskinfo[j].table[k].iin_type & MACH_PORT_TYPE_RECEIVE) &&
- global_taskinfo[j].table[k].iin_object == sendright->iin_object ) {
+ struct k2n_table_node *k2nnode;
+
+ for (unsigned int j = 0; j < global_taskcount; j++) {
+ if ((k2nnode = k2n_table_lookup(global_taskinfo[j].k2ntable, kobject))) {
+ assert(k2nnode->info_name->iin_object == kobject);
+
+ if (k2nnode->info_name->iin_type & MACH_PORT_TYPE_RECEIVE) {
*out_taskinfo = &global_taskinfo[j];
- *out_recv_info = global_taskinfo[j].table[k].iin_name;
- found = TRUE;
- retval = KERN_SUCCESS;
+ *out_recv_info = k2nnode->info_name->iin_name;
+ return KERN_SUCCESS;
}
}
}
- return retval;
+
+ return KERN_FAILURE;
}
+kern_return_t get_taskinfo_of_receiver_by_send_right(ipc_info_name_t sendright, my_per_task_info_t **out_taskinfo, mach_port_name_t *out_recv_info)
+{
+ return _get_taskinfo_of_receiver_by_send_right(sendright.iin_object, out_taskinfo, out_recv_info);
+}
+kern_return_t get_taskinfo_of_receiver_by_send_right_info(ipc_info_port_t sendright_info, my_per_task_info_t **out_taskinfo, mach_port_name_t *out_recv_info)
+{
+ return _get_taskinfo_of_receiver_by_send_right(sendright_info.iip_port_object, out_taskinfo, out_recv_info);
+}