aboutsummaryrefslogtreecommitdiffstats
path: root/system_cmds/lsmp.tproj/task_details.c
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
committerCameron Katri <me@cameronkatri.com>2021-05-09 14:20:58 -0400
commit5fd83771641d15c418f747bd343ba6738d3875f7 (patch)
tree5abf0f78f680d9837dbd93d4d4c3933bb7509599 /system_cmds/lsmp.tproj/task_details.c
downloadapple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.gz
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.tar.zst
apple_cmds-5fd83771641d15c418f747bd343ba6738d3875f7.zip
Import macOS userland
adv_cmds-176 basic_cmds-55 bootstrap_cmds-116.100.1 developer_cmds-66 diskdev_cmds-667.40.1 doc_cmds-53.60.1 file_cmds-321.40.3 mail_cmds-35 misc_cmds-34 network_cmds-606.40.1 patch_cmds-17 remote_cmds-63 shell_cmds-216.60.1 system_cmds-880.60.2 text_cmds-106
Diffstat (limited to 'system_cmds/lsmp.tproj/task_details.c')
-rw-r--r--system_cmds/lsmp.tproj/task_details.c507
1 files changed, 507 insertions, 0 deletions
diff --git a/system_cmds/lsmp.tproj/task_details.c b/system_cmds/lsmp.tproj/task_details.c
new file mode 100644
index 0000000..bf9ef51
--- /dev/null
+++ b/system_cmds/lsmp.tproj/task_details.c
@@ -0,0 +1,507 @@
+/*
+ * Copyright (c) 2002-2016 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public Source License Version 1.1 (the
+ * "License"). You may not use this file except in compliance with the
+ * License. Please obtain a copy of the License at
+ * http://www.apple.com/publicsource and read it before using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
+ * License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+#include <unistd.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach_debug/ipc_info.h>
+#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,
+ .pid = -1,
+ .info = {0,0,0,0,0,0},
+ .table = NULL,
+ .tableCount = 0,
+ .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);
+
+static void proc_pid_to_name(int pid, char *pname){
+ if (0 == proc_name(pid, pname, PROC_NAME_LEN)) {
+ strcpy(pname, "Unknown Process");
+ }
+}
+
+static my_per_task_info_t *global_taskinfo = NULL;
+static uint32_t global_taskcount = 0;
+
+my_per_task_info_t * allocate_taskinfo_memory(uint32_t taskCount)
+{
+ my_per_task_info_t * retval = malloc(taskCount * sizeof(my_per_task_info_t));
+ if (retval) {
+ bzero((void *)retval, taskCount * sizeof(my_per_task_info_t));
+ }
+ global_taskcount = taskCount;
+ global_taskinfo = retval;
+ return retval;
+}
+
+void deallocate_taskinfo_memory(my_per_task_info_t *data){
+ if (data) {
+ free(data);
+ global_taskinfo = NULL;
+ global_taskcount = 0;
+ }
+}
+
+kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_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 = task_get_exception_ports(taskinfo->task, EXC_MASK_ALL, taskinfo->exceptionInfo.masks, &taskinfo->exceptionInfo.count, taskinfo->exceptionInfo.ports, taskinfo->exceptionInfo.behaviors, taskinfo->exceptionInfo.flavors);
+
+ if (ret != KERN_SUCCESS) {
+ fprintf(stderr, "task_get_exception_ports() 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(threadPorts[i], EXC_MASK_ALL, excinfo->masks, &excinfo->count, excinfo->ports, excinfo->behaviors, excinfo->flavors);
+ if (ret != KERN_SUCCESS){
+ fprintf(stderr, "thread_get_exception_ports() 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_CONTROL) {
+ taskinfo->task_kobject = kobject;
+ taskinfo->valid = TRUE;
+ }
+
+ return ret;
+}
+
+void get_exc_behavior_string(exception_behavior_t b, char *out_string, size_t len)
+{
+ out_string[0]='\0';
+
+ if (b & MACH_EXCEPTION_CODES)
+ strlcat(out_string, "MACH +", len);
+ switch (b & ~MACH_EXCEPTION_CODES) {
+ case EXCEPTION_DEFAULT:
+ strlcat(out_string, " DEFAULT", len);
+ break;
+ case EXCEPTION_STATE:
+ strlcat(out_string, " STATE", len);
+ break;
+ case EXCEPTION_STATE_IDENTITY:
+ strlcat(out_string, " IDENTITY", len);
+ break;
+ default:
+ 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))
+ strlcat(out_string, " BAD_ACCESS", len);
+ if (m & (1<<EXC_BAD_INSTRUCTION))
+ strlcat(out_string," BAD_INSTRUCTION", len);
+ if (m & (1<<EXC_ARITHMETIC))
+ strlcat(out_string," ARITHMETIC", len);
+ if (m & (1<<EXC_EMULATION))
+ strlcat(out_string," EMULATION", len);
+ if (m & (1<<EXC_SOFTWARE))
+ strlcat(out_string," SOFTWARE", len);
+ if (m & (1<<EXC_BREAKPOINT))
+ strlcat(out_string," BREAKPOINT", len);
+ if (m & (1<<EXC_SYSCALL))
+ strlcat(out_string," SYSCALL", len);
+ if (m & (1<<EXC_MACH_SYSCALL))
+ strlcat(out_string," MACH_SYSCALL", len);
+ if (m & (1<<EXC_RPC_ALERT))
+ strlcat(out_string," RPC_ALERT", len);
+ if (m & (1<<EXC_CRASH))
+ strlcat(out_string," CRASH", len);
+ if (m & (1<<EXC_RESOURCE))
+ strlcat(out_string," RESOURCE", len);
+ if (m & (1<<EXC_GUARD))
+ strlcat(out_string," GUARD", len);
+}
+
+kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo, JSON_t json)
+{
+
+ char behavior_string[30];
+ char mask_string[200];
+
+ JSON_KEY(json, exception_ports);
+ JSON_ARRAY_BEGIN(json);
+
+ boolean_t header_required = TRUE;
+ for (int i = 0; i < taskinfo->exceptionInfo.count; i++) {
+ if (taskinfo->exceptionInfo.ports[i] != MACH_PORT_NULL) {
+ if (header_required) {
+
+ printf(" exc_port flavor <behaviors> mask \n");
+ header_required = FALSE;
+ }
+ 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, "0x%08x", taskinfo->exceptionInfo.ports[i]);
+ 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
+
+ printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo->exceptionInfo.ports[i], taskinfo->exceptionInfo.flavors[i], behavior_string, mask_string);
+ }
+
+ }
+
+ JSON_ARRAY_END(json); // exception ports
+
+ return KERN_SUCCESS;
+}
+
+
+kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo, JSON_t json)
+{
+ kern_return_t kret = KERN_SUCCESS;
+ mach_msg_type_number_t threadcount = taskinfo->threadCount;
+ boolean_t header_required = TRUE;
+ boolean_t newline_required = TRUE;
+ 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("Thread_KObject Thread-ID Port Description.");
+ header_required = FALSE;
+ }
+
+ if (newline_required) {
+ printf("\n");
+ }
+ newline_required = TRUE;
+
+ if (info->th_kobject != 0) {
+ /* TODO: Should print tid and stuff */
+ 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);
+ }
+
+ 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[i] != MACH_PORT_NULL) {
+ if (header_required) {
+ printf("\n exc_port 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, "0x%08x", excinfo->ports[i]);
+ 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%03x <%s> %s " , excinfo->ports[i], excinfo->flavors[i], behavior_string, mask_string);
+
+ ipc_info_name_t actual_sendinfo;
+ if (KERN_SUCCESS == get_ipc_info_from_lsmp_spaceinfo(excinfo->ports[i], &actual_sendinfo)) {
+ 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(&actual_sendinfo, &recv_holder_taskinfo, &recv_name)) {
+
+ JSON_OBJECT_SET(json, name, "0x%08x", recv_name);
+ JSON_OBJECT_SET(json, ipc-object, "0x%08x", actual_sendinfo.iin_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,
+ actual_sendinfo.iin_object,
+ recv_holder_taskinfo->pid,
+ recv_holder_taskinfo->processName);
+ }
+
+ } else {
+ fprintf(stderr, "failed to find");
+ }
+
+ printf("\n");
+
+ }
+ JSON_OBJECT_END(json); // exception port
+ }
+ }
+ }
+ JSON_ARRAY_END(json); // exception ports
+ JSON_OBJECT_END(json); // thread
+ }
+
+ JSON_ARRAY_END(json); // threads
+ printf("\n");
+ return kret;
+}
+
+char * get_task_name_by_pid(pid_t pid) {
+ char * retval = "Unknown";
+ for (int i = 0; i < global_taskcount; i++) {
+ if (pid == global_taskinfo[i].pid) {
+ return global_taskinfo[i].processName;
+ }
+ }
+ return retval;
+}
+
+my_per_task_info_t * get_taskinfo_by_kobject(natural_t kobj) {
+ my_per_task_info_t *retval = &NOT_FOUND_TASK_INFO;
+ for (int j = 0; j < global_taskcount; j++) {
+ if (global_taskinfo[j].task_kobject == kobj) {
+ retval = &global_taskinfo[j];
+ break;
+ }
+ }
+ 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)
+{
+ *out_taskinfo = &NOT_FOUND_TASK_INFO;
+ struct k2n_table_node *k2nnode;
+
+ for (int j = 0; j < global_taskcount; j++) {
+ if ((k2nnode = k2n_table_lookup(global_taskinfo[j].k2ntable, sendright->iin_object))) {
+ assert(k2nnode->info_name->iin_object == sendright->iin_object);
+
+ if (k2nnode->info_name->iin_type & MACH_PORT_TYPE_RECEIVE) {
+ *out_taskinfo = &global_taskinfo[j];
+ *out_recv_info = k2nnode->info_name->iin_name;
+ return KERN_SUCCESS;
+ }
+ }
+ }
+
+ return KERN_FAILURE;
+}
+
+kern_return_t get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name, ipc_info_name_t *out_sendright){
+ kern_return_t retval = KERN_FAILURE;
+ bzero(out_sendright, sizeof(ipc_info_name_t));
+ my_per_task_info_t *mytaskinfo = NULL;
+ for (int i = global_taskcount - 1; i >= 0; i--){
+ if (global_taskinfo[i].task == mach_task_self()){
+ mytaskinfo = &global_taskinfo[i];
+ break;
+ }
+ }
+ if (mytaskinfo) {
+ for (int k = 0; k < mytaskinfo->tableCount; k++) {
+ if (port_name == mytaskinfo->table[k].iin_name){
+ bcopy(&mytaskinfo->table[k], out_sendright, sizeof(ipc_info_name_t));
+ retval = KERN_SUCCESS;
+ break;
+ }
+ }
+ }
+ return retval;
+
+}