From 5fd83771641d15c418f747bd343ba6738d3875f7 Mon Sep 17 00:00:00 2001 From: Cameron Katri Date: Sun, 9 May 2021 14:20:58 -0400 Subject: 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 --- system_cmds/lsmp.tproj/common.h | 204 ++++++++ system_cmds/lsmp.tproj/entitlements.plist | 10 + system_cmds/lsmp.tproj/json.h | 119 +++++ system_cmds/lsmp.tproj/lsmp.1 | 61 +++ system_cmds/lsmp.tproj/lsmp.c | 239 ++++++++++ system_cmds/lsmp.tproj/port_details.c | 746 ++++++++++++++++++++++++++++++ system_cmds/lsmp.tproj/task_details.c | 507 ++++++++++++++++++++ 7 files changed, 1886 insertions(+) create mode 100644 system_cmds/lsmp.tproj/common.h create mode 100644 system_cmds/lsmp.tproj/entitlements.plist create mode 100644 system_cmds/lsmp.tproj/json.h create mode 100644 system_cmds/lsmp.tproj/lsmp.1 create mode 100644 system_cmds/lsmp.tproj/lsmp.c create mode 100644 system_cmds/lsmp.tproj/port_details.c create mode 100644 system_cmds/lsmp.tproj/task_details.c (limited to 'system_cmds/lsmp.tproj') diff --git a/system_cmds/lsmp.tproj/common.h b/system_cmds/lsmp.tproj/common.h new file mode 100644 index 0000000..9a47012 --- /dev/null +++ b/system_cmds/lsmp.tproj/common.h @@ -0,0 +1,204 @@ +/* + * 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@ + */ + + +#ifndef system_cmds_common_h +#define system_cmds_common_h + +#include +#include "json.h" + +#define PROC_NAME_LEN 100 +#define BUFSTR_LEN 30 +#define VOUCHER_DETAIL_MAXLEN 1024 + +/* common struct to hold all configurations, static and args based */ +struct prog_configs { + boolean_t show_all_tasks; + boolean_t show_voucher_details; + boolean_t verbose; + int voucher_detail_length; + pid_t pid; /* if user focusing only one pid */ + JSON_t json_output; +}; + +extern struct prog_configs lsmp_config; + +/* exception port information */ +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]; +}; + +/* private structure to hold thread specific information */ +struct my_per_thread_info { + mach_port_t thread; + uint32_t th_kobject; + uint64_t th_id; + char * voucher_detail; +}; + +/* kobject to name hash table declarations */ +#define K2N_TABLE_SIZE 256 + +struct k2n_table_node { + natural_t kobject; /* kobject referred to by the name -- the key into the table */ + ipc_info_name_t *info_name; /* info about the name that refers to the key kobject -- value of the table */ + struct k2n_table_node *next; +}; + +struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natural_t kobject); +struct k2n_table_node *k2n_table_lookup(struct k2n_table_node **table, natural_t kobject); + +/* private structure to wrap up per-task info */ +typedef struct my_per_task_info { + task_t task; + pid_t pid; + vm_address_t task_kobject; + ipc_info_space_t info; + ipc_info_name_array_t table; + mach_msg_type_number_t tableCount; + ipc_info_tree_name_array_t tree; + mach_msg_type_number_t treeCount; + boolean_t valid; /* TRUE if all data is accurately collected */ + struct k2n_table_node *k2ntable[K2N_TABLE_SIZE]; + char processName[PROC_NAME_LEN]; + struct exc_port_info exceptionInfo; + struct my_per_thread_info * threadInfos; /* dynamically allocated in collect_per_task_info */ + unsigned int threadCount; + struct exc_port_info *threadExceptionInfos; /* this is 2 dimensional array with threadCount X struct exc_port_info of ports */ +} my_per_task_info_t; + + +/* + * WARNING - these types are copied from xnu/osfmk/kern/ipc_kobject.h + * Need to stay in sync to print accurate results. + */ +#define IKOT_NONE 0 +#define IKOT_THREAD_CONTROL 1 +#define IKOT_TASK_CONTROL 2 +#define IKOT_HOST 3 +#define IKOT_HOST_PRIV 4 +#define IKOT_PROCESSOR 5 +#define IKOT_PSET 6 +#define IKOT_PSET_NAME 7 +#define IKOT_TIMER 8 +#define IKOT_PAGING_REQUEST 9 +#define IKOT_MIG 10 +#define IKOT_MEMORY_OBJECT 11 +#define IKOT_XMM_PAGER 12 +#define IKOT_XMM_KERNEL 13 +#define IKOT_XMM_REPLY 14 +#define IKOT_UND_REPLY 15 +#define IKOT_HOST_NOTIFY 16 +#define IKOT_HOST_SECURITY 17 +#define IKOT_LEDGER 18 +#define IKOT_MASTER_DEVICE 19 +#define IKOT_TASK_NAME 20 +#define IKOT_SUBSYSTEM 21 +#define IKOT_IO_DONE_QUEUE 22 +#define IKOT_SEMAPHORE 23 +#define IKOT_LOCK_SET 24 +#define IKOT_CLOCK 25 +#define IKOT_CLOCK_CTRL 26 +#define IKOT_IOKIT_IDENT 27 +#define IKOT_NAMED_ENTRY 28 +#define IKOT_IOKIT_CONNECT 29 +#define IKOT_IOKIT_OBJECT 30 +#define IKOT_UPL 31 +#define IKOT_MEM_OBJ_CONTROL 32 +#define IKOT_AU_SESSIONPORT 33 +#define IKOT_FILEPORT 34 +#define IKOT_LABELH 35 +#define IKOT_TASK_RESUME 36 +#define IKOT_VOUCHER 37 +#define IKOT_VOUCHER_ATTR_CONTROL 38 +#define IKOT_WORK_INTERVAL 39 +#define IKOT_UX_HANDLER 40 +#define IKOT_UEXT_OBJECT 41 +#define IKOT_ARCADE_REG 42 +#define IKOT_EVENTLINK 43 +#define IKOT_TASK_INSPECT 44 +#define IKOT_TASK_READ 45 +#define IKOT_THREAD_INSPECT 46 +#define IKOT_THREAD_READ 47 +#define IKOT_SUID_CRED 48 +#define IKOT_HYPERVISOR 49 + +#define IKOT_UNKNOWN 50 /* magic catchall */ +#define IKOT_MAX_TYPE (IKOT_UNKNOWN+1) /* # of IKOT_ types */ + + +#define PORT_FLAG_TO_INDEX(flag) ( __builtin_ctz(flag) ) /* count trailing zeros */ +#define INDEX_TO_PORT_FLAG(idx) ( 1 << idx ) +typedef struct port_status_flag_info { + natural_t flag; /* MACH_PORT_STATUS_FLAG_* */ + const char *compact_name; /* Single character name for compact representation */ + const char *name; /* human readable long name */ +} port_status_flag_info_t; + +/* + * list of names for possible MACH_PORT_STATUS_FLAG_* + * indexed by PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_*) + */ +extern const port_status_flag_info_t port_status_flags[]; + +#define _SHOW_PORT_STATUS_FLAG(flags, flag) \ + (flags & flag) ? port_status_flags[PORT_FLAG_TO_INDEX(flag)].compact_name : "-" +#define SHOW_PORT_STATUS_FLAGS(flags) \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_TEMPOWNER), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_GUARDED), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_STRICT_GUARD), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_IMP_DONATION), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_REVIVE), \ + _SHOW_PORT_STATUS_FLAG(flags, MACH_PORT_STATUS_FLAG_TASKPTR) + + +uint32_t show_recipe_detail(mach_voucher_attr_recipe_t recipe, char * voucher_outstr, uint32_t maxlen, JSON_t json); +char *copy_voucher_detail(mach_port_t task, mach_port_name_t voucher, JSON_t json); + +/* mach port related functions */ +const char * kobject_name(natural_t kotype); +void get_receive_port_context(task_t taskp, mach_port_name_t portname, mach_port_context_t *context); +int get_recieve_port_status(task_t taskp, mach_port_name_t portname, mach_port_info_ext_t *info); +void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, JSON_t json); + +/* task and thread related helper functions */ +kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_t target_task); +my_per_task_info_t * allocate_taskinfo_memory(uint32_t taskCount); +void deallocate_taskinfo_memory(my_per_task_info_t *data); +kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo, JSON_t json); +kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo, JSON_t json); +my_per_task_info_t * get_taskinfo_by_kobject(natural_t kobj); + +void get_exc_behavior_string(exception_behavior_t b, char *out_string, size_t len); +void get_exc_mask_string(exception_mask_t m, char *out_string, size_t len); +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); +kern_return_t get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name, ipc_info_name_t *out_sendright); + +/* basic util functions */ +uint32_t print_hex_data(char *outstr, uint32_t maxlen, char *prefix, char *desc, void *addr, int len); + +#endif diff --git a/system_cmds/lsmp.tproj/entitlements.plist b/system_cmds/lsmp.tproj/entitlements.plist new file mode 100644 index 0000000..b7b4e6c --- /dev/null +++ b/system_cmds/lsmp.tproj/entitlements.plist @@ -0,0 +1,10 @@ + + + + + task_for_pid-allow + + com.apple.system-task-ports + + + diff --git a/system_cmds/lsmp.tproj/json.h b/system_cmds/lsmp.tproj/json.h new file mode 100644 index 0000000..2a87a8b --- /dev/null +++ b/system_cmds/lsmp.tproj/json.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2017 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@ + */ + +/* + * Provides a stream-based API for generating JSON output + * + * Handles tedious tasks like worrying about comma placement (and avoiding trailing commas). + * Assumes strings are already escaped (if necessary) and does no error checking (thus it + * may produce invalid JSON when used improperly). + * + * As a convenience, when the provided `json` stream is NULL (i.e. it was never initialized + * by `JSON_OPEN`) these APIs will do nothing. + * + * Example usage: + * + * JSON_t json = JSON_OPEN("/path/to/output.json") + * JSON_OBJECT_BEGIN(json); // root object + * + * JSON_OBJECT_SET(json, version, %.1f, 1.0); + * JSON_OBJECT_SET_BOOL(json, has_fruit, 1); + * + * // Note the required quotes for strings (formatted or not) + * char *mystr = "hello"; + * JSON_OBJECT_SET(json, formatted_string, "%s", mystr); + * JSON_OBJECT_SET(json, literal_string, "my literal string"); + * + * JSON_KEY(json, fruit_array); + * JSON_ARRAY_BEGIN(json); // fruit_array + * JSON_ARRAY_APPEND(json, "my literal string"); + * JSON_ARRAY_APPEND(json, "<0x%08llx>", 0xface); + * JSON_ARRAY_APPEND(json, %d, 3); + * JSON_ARRAY_END(json); // fruit_array + * + * JSON_OBJECT_END(json); // root object + * JSON_CLOSE(json); + */ + +#ifndef _JSON_H_ +#define _JSON_H_ + +#include +#include +#include + +#define _JSON_IF(json, code) \ + if (json != NULL) { \ + code; \ + } +#define _JSON_COMMA(json) \ + if (json->require_comma) { \ + fprintf(json->stream, ","); \ + } + +struct _JSON { + FILE* stream; + bool require_comma; +}; +typedef struct _JSON * JSON_t; + +#pragma mark Open/Close +/* Return a new JSON_t stream */ +static inline JSON_t JSON_OPEN(const char *path) { + JSON_t p = malloc(sizeof(struct _JSON)); + p->stream = fopen(path, "w+"); + p->require_comma = false; + return p; +} + +/* Close an existing JSON stream, removing trailing commas */ +#define JSON_CLOSE(json) _JSON_IF(json, fclose(json->stream); free(json)) + +#pragma mark Keys/Values +/* Output the `key` half of a key/value pair */ +#define JSON_KEY(json, key) _JSON_IF(json, _JSON_COMMA(json); fprintf(json->stream, "\"" #key "\":"); json->require_comma = false) +/* Output the `value` half of a key/value pair */ +#define JSON_VALUE(json, format, ...) _JSON_IF(json, fprintf(json->stream, #format, ##__VA_ARGS__); json->require_comma = true) + +#define _JSON_BEGIN(json, character) _JSON_COMMA(json); fprintf(json->stream, #character); json->require_comma = false; +#define _JSON_END(json, character) fprintf(json->stream, #character); json->require_comma = true; +#define _JSON_BOOL(val) ( val ? "true" : "false" ) + +#pragma mark Objects +/* Start a new JSON object */ +#define JSON_OBJECT_BEGIN(json) _JSON_IF(json, _JSON_BEGIN(json, {)) +/* Set a value in the current JSON object */ +#define JSON_OBJECT_SET(json, key, format, ...) _JSON_IF(json, JSON_KEY(json, key); JSON_VALUE(json, format, ##__VA_ARGS__)) +/* Set a boolean in the current JSON object */ +#define JSON_OBJECT_SET_BOOL(json, key, value) JSON_OBJECT_SET(json, key, %s, _JSON_BOOL(value)) +/* End the current JSON object */ +#define JSON_OBJECT_END(json) _JSON_IF(json, _JSON_END(json, })) + +#pragma mark Arrays +/* Start a new JSON array */ +#define JSON_ARRAY_BEGIN(json) _JSON_IF(json, _JSON_BEGIN(json, [)) +/* Append a value to the current JSON array */ +#define JSON_ARRAY_APPEND(json, format, ...) _JSON_IF(json, _JSON_COMMA(json); JSON_VALUE(json, format, ##__VA_ARGS__)) +/* End the current JSON array */ +#define JSON_ARRAY_END(json) _JSON_IF(json, _JSON_END(json, ])) + +#endif /* _JSON_H_ */ diff --git a/system_cmds/lsmp.tproj/lsmp.1 b/system_cmds/lsmp.tproj/lsmp.1 new file mode 100644 index 0000000..d4c70e9 --- /dev/null +++ b/system_cmds/lsmp.tproj/lsmp.1 @@ -0,0 +1,61 @@ +.\" Copyright (c) 2012, Apple Inc. All rights reserved. +.\" +.Dd Jul 24, 2012 +.Dt LSMP 1 +.Os "Mac OS X" +.Sh NAME +.Nm lsmp +.Nd Display mach port information for processes on the system +.Sh SYNOPSIS +.Nm lsmp +.Fl h +.Pp +.Nm lsmp +.Ar -p +Show mach port usage for . Run with root privileges to see detailed info about port destinations etc. +.Pp +.Nm lsmp +.Ar -v +Show information in detail for Kernel object based ports. Including thread ports and special ports attached to it. +.Pp +.Nm lsmp +.Ar -a +Show mach port usage for all tasks in the system +.Pp +.Nm lsmp +.Ar -j +Save output as JSON to . +.Sh DESCRIPTION +The +.Nm lsmp + command prints information about every active right in a task's port space, giving a view into the inter-process communication behavior of that task. +.P +.nf +Following is an explanation of each symbol and values from the output. +name : Task unique name for a port. A "-" signifies that this is a member of a port-set +ipc-object : A unique identifier for a kernel object. A "+" sign implies that this entry is expanded from above ipc-object. +rights : Rights corresponding to this name. Possible values are recv, send, send-once and port-set. +flags : Flags indicating port status. + T : Port has tempowner set + G : Port is guarded + S : Port has strict guarding restrictions + I : Port has importance donation flag set + R : Port is marked reviving + P : Port has task pointer set +boost : Importance boost count +reqs : Notifications armed on this port. + D : Dead name notification + N : No sender notification + P : Port Destroy requests +recv : Number of recv rights for this name. +send : Number of send rights stored at this name. This does NOT reflect the total number of send rights for this recv right. +sonce : Number of outstanding send-once rights for this receive right. +oref : Do send rights exist somewhere for this receive right? +qlimit : Queue limit for this port. If orefs column shows -> then it indicates the queue limit on the destination port. And a <- indicates this port right is destined to receive messages from process referred in identifier column. +msgcount : Number of messages enqueued on this port. See qlimit for -> and <- explanations. +context : Mach port context value. +identifier : A unique identifier for a kernel object or task's name for this right. This field is described by the type column. +.fi +.Sh SEE ALSO +.Xr ddt 1 +.Xr top 1 diff --git a/system_cmds/lsmp.tproj/lsmp.c b/system_cmds/lsmp.tproj/lsmp.c new file mode 100644 index 0000000..71a7c68 --- /dev/null +++ b/system_cmds/lsmp.tproj/lsmp.c @@ -0,0 +1,239 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "json.h" + +#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) +#define TASK_FOR_PID_USAGE_MESG "\nPlease check your boot-args to ensure you have access to task_for_pid()." +#else +#define TASK_FOR_PID_USAGE_MESG "" +#endif + +struct prog_configs lsmp_config = { + .show_all_tasks = FALSE, + .show_voucher_details = FALSE, + .verbose = FALSE, + .pid = 0, + .json_output = NULL, +}; + +static void print_usage(char *progname) { + fprintf(stderr, "Usage: %s -p [-a|-v|-h] \n", "lsmp"); + fprintf(stderr, "Lists information about mach ports. Please see man page for description of each column.\n"); + fprintf(stderr, "\t-p : print all mach ports for process id . \n"); + fprintf(stderr, "\t-a : print all mach ports for all processeses. \n"); + fprintf(stderr, "\t-v : print verbose details for kernel objects.\n"); + fprintf(stderr, "\t-j : save output as JSON to .\n"); + fprintf(stderr, "\t-h : print this help.\n"); + exit(1); +} + +static void print_task_info(my_per_task_info_t *taskinfo, mach_msg_type_number_t taskCount, my_per_task_info_t *psettaskinfo, boolean_t verbose, JSON_t json) { + printf("Process (%d) : %s\n", taskinfo->pid, taskinfo->processName); + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, pid, %d, taskinfo->pid); + JSON_OBJECT_SET(json, name, "%s", taskinfo->processName); + show_task_mach_ports(taskinfo, taskCount, psettaskinfo, json); + print_task_exception_info(taskinfo, json); + if (verbose) { + printf("\n"); + print_task_threads_special_ports(taskinfo, json); + } + JSON_OBJECT_END(json); +} + +int main(int argc, char *argv[]) { + kern_return_t ret; + task_t aTask; + my_per_task_info_t *taskinfo = NULL; + task_array_t tasks; + char *progname = "lsmp"; + int i, option = 0; + lsmp_config.voucher_detail_length = 128; /* default values for config */ + my_per_task_info_t *psettaskinfo; + mach_msg_type_number_t taskCount; + + while((option = getopt(argc, argv, "hvalp:j:")) != -1) { + switch(option) { + case 'a': + /* user asked for info on all processes */ + lsmp_config.pid = 0; + lsmp_config.show_all_tasks = 1; + break; + + case 'l': + /* for compatibility with sysdiagnose's usage of -all */ + lsmp_config.voucher_detail_length = 1024; + /* Fall through to 'v' */ + + case 'v': + lsmp_config.show_voucher_details = TRUE; + lsmp_config.verbose = TRUE; + break; + + case 'p': + lsmp_config.pid = atoi(optarg); + if (lsmp_config.pid == 0) { + fprintf(stderr, "Unknown pid: %s\n", optarg); + exit(1); + } + break; + + case 'j': + lsmp_config.json_output = JSON_OPEN(optarg); + if (lsmp_config.json_output == NULL) { + fprintf(stderr, "Unable to open \"%s\": %s\n", optarg, strerror(errno)); + exit(1); + } + break; + + default: + fprintf(stderr, "Unknown argument. \n"); + /* Fall through to 'h' */ + + case 'h': + print_usage(progname); + break; + + } + } + argc -= optind; + argv += optind; + + /* if privileged, get the info for all tasks so we can match ports up */ + if (geteuid() == 0) { + processor_set_name_array_t psets; + mach_msg_type_number_t psetCount; + mach_port_t pset_priv; + + ret = host_processor_sets(mach_host_self(), &psets, &psetCount); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "host_processor_sets() failed: %s\n", mach_error_string(ret)); + exit(1); + } + if (psetCount != 1) { + fprintf(stderr, "Assertion Failure: pset count greater than one (%d)\n", psetCount); + exit(1); + } + + /* convert the processor-set-name port to a privileged port */ + ret = host_processor_set_priv(mach_host_self(), psets[0], &pset_priv); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "host_processor_set_priv() failed: %s\n", mach_error_string(ret)); + exit(1); + } + mach_port_deallocate(mach_task_self(), psets[0]); + vm_deallocate(mach_task_self(), (vm_address_t)psets, (vm_size_t)psetCount * sizeof(mach_port_t)); + + /* convert the processor-set-priv to a list of tasks for the processor set */ + ret = processor_set_tasks(pset_priv, &tasks, &taskCount); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "processor_set_tasks() failed: %s\n", mach_error_string(ret)); + exit(1); + } + mach_port_deallocate(mach_task_self(), pset_priv); + + /* swap my current instances port to be last to collect all threads and exception port info */ + int myTaskPosition = -1; + for (int i = 0; i < taskCount; i++) { + if (tasks[i] == mach_task_self()){ + myTaskPosition = i; + break; + } + } + if (myTaskPosition >= 0) { + mach_port_t swap_holder = MACH_PORT_NULL; + swap_holder = tasks[taskCount - 1]; + tasks[taskCount - 1] = tasks[myTaskPosition]; + tasks[myTaskPosition] = swap_holder; + } + + } + else + { + fprintf(stderr, "warning: should run as root for best output (cross-ref to other tasks' ports).\n"); + /* just the one process */ + ret = task_for_pid(mach_task_self(), lsmp_config.pid, &aTask); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "task_for_pid() failed: %s %s\n", mach_error_string(ret), TASK_FOR_PID_USAGE_MESG); + exit(1); + } + taskCount = 1; + tasks = &aTask; + } + + /* convert each task to structure of pointer for the task info */ + psettaskinfo = allocate_taskinfo_memory(taskCount); + + for (i = 0; i < taskCount; i++) { + ret = collect_per_task_info(&psettaskinfo[i], tasks[i]); + if (ret != KERN_SUCCESS) { + printf("Ignoring failure of mach_port_space_info() for task %d for '-all'\n", tasks[i]); + continue; + } + + if (psettaskinfo[i].pid == lsmp_config.pid) + taskinfo = &psettaskinfo[i]; + } + + JSON_OBJECT_BEGIN(lsmp_config.json_output); + JSON_OBJECT_SET(lsmp_config.json_output, version, "%.1f", 1.0); + JSON_KEY(lsmp_config.json_output, processes); + JSON_ARRAY_BEGIN(lsmp_config.json_output); + + if (lsmp_config.show_all_tasks == FALSE) { + if (taskinfo == NULL) { + fprintf(stderr, "Failed to find task ipc information for pid %d\n", lsmp_config.pid); + exit(1); + } + print_task_info(taskinfo, taskCount, psettaskinfo, TRUE, lsmp_config.json_output); + } else { + for (i=0; i < taskCount; i++) { + if (psettaskinfo[i].valid != TRUE) + continue; + print_task_info(&psettaskinfo[i], taskCount, psettaskinfo, lsmp_config.verbose, lsmp_config.json_output); + printf("\n\n"); + } + } + + JSON_ARRAY_END(lsmp_config.json_output); + JSON_OBJECT_END(lsmp_config.json_output); + + if (taskCount > 1) { + vm_deallocate(mach_task_self(), (vm_address_t)tasks, (vm_size_t)taskCount * sizeof(mach_port_t)); + } + + deallocate_taskinfo_memory(psettaskinfo); + + JSON_CLOSE(lsmp_config.json_output); + + return(0); +} diff --git a/system_cmds/lsmp.tproj/port_details.c b/system_cmds/lsmp.tproj/port_details.c new file mode 100644 index 0000000..fb888e4 --- /dev/null +++ b/system_cmds/lsmp.tproj/port_details.c @@ -0,0 +1,746 @@ +/* + * 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 +#include +#include +#include +#include +#include +//#include +#include +#include "common.h" +#include "json.h" + +const char * kobject_name(natural_t kotype) +{ + switch (kotype) { + case IKOT_NONE: return "message-queue"; + case IKOT_THREAD_CONTROL: return "THREAD-CONTROL"; + case IKOT_TASK_CONTROL: return "TASK-CONTROL"; + case IKOT_HOST: return "HOST"; + case IKOT_HOST_PRIV: return "HOST-PRIV"; + case IKOT_PROCESSOR: return "PROCESSOR"; + case IKOT_PSET: return "PROCESSOR-SET"; + case IKOT_PSET_NAME: return "PROCESSOR-SET-NAME"; + case IKOT_TIMER: return "TIMER"; + case IKOT_PAGING_REQUEST: return "PAGER-REQUEST"; + case IKOT_MIG: return "MIG"; + case IKOT_MEMORY_OBJECT: return "MEMORY-OBJECT"; + case IKOT_XMM_PAGER: return "XMM-PAGER"; + case IKOT_XMM_KERNEL: return "XMM-KERNEL"; + case IKOT_XMM_REPLY: return "XMM-REPLY"; + case IKOT_UND_REPLY: return "UND-REPLY"; + case IKOT_HOST_NOTIFY: return "message-queue"; + case IKOT_HOST_SECURITY: return "HOST-SECURITY"; + case IKOT_LEDGER: return "LEDGER"; + case IKOT_MASTER_DEVICE: return "MASTER-DEVICE"; + case IKOT_TASK_NAME: return "TASK-NAME"; + case IKOT_SUBSYSTEM: return "SUBSYSTEM"; + case IKOT_IO_DONE_QUEUE: return "IO-QUEUE-DONE"; + case IKOT_SEMAPHORE: return "SEMAPHORE"; + case IKOT_LOCK_SET: return "LOCK-SET"; + case IKOT_CLOCK: return "CLOCK"; + case IKOT_CLOCK_CTRL: return "CLOCK-CONTROL"; + case IKOT_IOKIT_IDENT: return "IOKIT-IDENT"; + case IKOT_NAMED_ENTRY: return "NAMED-MEMORY"; + case IKOT_IOKIT_CONNECT: return "IOKIT-CONNECT"; + case IKOT_IOKIT_OBJECT: return "IOKIT-OBJECT"; + case IKOT_UPL: return "UPL"; + case IKOT_MEM_OBJ_CONTROL: return "XMM-CONTROL"; + case IKOT_AU_SESSIONPORT: return "SESSIONPORT"; + case IKOT_FILEPORT: return "FILEPORT"; + case IKOT_LABELH: return "MACF-LABEL"; + case IKOT_TASK_RESUME: return "TASK_RESUME"; + case IKOT_VOUCHER: return "VOUCHER"; + case IKOT_VOUCHER_ATTR_CONTROL: return "VOUCHER_ATTR_CONTROL"; + case IKOT_WORK_INTERVAL: return "WORK_INTERVAL"; + case IKOT_UX_HANDLER: return "UX_HANDLER"; + case IKOT_UEXT_OBJECT: return "UEXT_OBJECT"; + case IKOT_ARCADE_REG: return "ARCADE_REG"; + case IKOT_EVENTLINK: return "EVENTLINK"; + case IKOT_TASK_INSPECT: return "TASK-INSPECT"; + case IKOT_TASK_READ: return "TASK-READ"; + case IKOT_THREAD_INSPECT: return "THREAD-INSPECT"; + case IKOT_THREAD_READ: return "THREAD-READ"; + case IKOT_SUID_CRED: return "SUID_CRED"; + case IKOT_HYPERVISOR: return "HYPERVISOR"; + case IKOT_UNKNOWN: + default: return "UNKNOWN"; + } +} + +const port_status_flag_info_t port_status_flags[] = { + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_TEMPOWNER)] = { + .flag = MACH_PORT_STATUS_FLAG_TEMPOWNER, + .compact_name = "T", + .name = "tempowner", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_GUARDED)] = { + .flag = MACH_PORT_STATUS_FLAG_GUARDED, + .compact_name = "G", + .name = "guarded", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_STRICT_GUARD)] = { + .flag = MACH_PORT_STATUS_FLAG_STRICT_GUARD, + .compact_name = "S", + .name = "strict guard", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_IMP_DONATION)] = { + .flag = MACH_PORT_STATUS_FLAG_IMP_DONATION, + .compact_name = "I", + .name = "imp donation", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_REVIVE)] = { + .flag = MACH_PORT_STATUS_FLAG_REVIVE, + .compact_name = "R", + .name = "revive", + }, + [PORT_FLAG_TO_INDEX(MACH_PORT_STATUS_FLAG_TASKPTR)] = { + .flag = MACH_PORT_STATUS_FLAG_TASKPTR, + .compact_name = "P", + .name = "taskptr", + }, + {0}, +}; + +#define VOUCHER_DETAIL_PREFIX " " + +static const unsigned int voucher_contents_size = 8192; +static uint8_t voucher_contents[voucher_contents_size]; + +typedef struct { + int total; + int sendcount; + int receivecount; + int sendoncecount; + int portsetcount; + int deadcount; + int dncount; + int vouchercount; +} task_table_entry_counts; + +typedef task_table_entry_counts * task_table_entry_counts_t; + +static void show_task_table_entry(ipc_info_name_t *entry, my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, task_table_entry_counts_t counts, JSON_t json); + +static uint32_t safesize (int len){ + return (len > 0) ? len : 0; +} + +uint32_t show_recipe_detail(mach_voucher_attr_recipe_t recipe, char *voucher_outstr, uint32_t maxlen, JSON_t json) { + JSON_OBJECT_BEGIN(json); + + uint32_t len = 0; + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "Key: %u, ", recipe->key); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Command: %u, ", recipe->command); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Previous voucher: 0x%x, ", recipe->previous_voucher); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), "Content size: %u\n", recipe->content_size); + + JSON_OBJECT_SET(json, key, %u, recipe->key); + JSON_OBJECT_SET(json, command, %u, recipe->command); + JSON_OBJECT_SET(json, previous_voucher, "0x%x", recipe->previous_voucher); + JSON_OBJECT_SET(json, content_size, %u, recipe->content_size); + + switch (recipe->key) { + case MACH_VOUCHER_ATTR_KEY_IMPORTANCE: + // content may not be valid JSON, exclude + // JSON_OBJECT_SET(json, importance_info, "%s", (char *)recipe->content); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "IMPORTANCE INFO: %s", (char *)recipe->content); + break; + case MACH_VOUCHER_ATTR_KEY_BANK: + // content may not be valid JSON, exclude + // JSON_OBJECT_SET(json, resource_accounting_info, "%s", (char *)recipe->content); + len += snprintf(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX "RESOURCE ACCOUNTING INFO: %s", (char *)recipe->content); + break; + default: + len += print_hex_data(&voucher_outstr[len], safesize(maxlen - len), VOUCHER_DETAIL_PREFIX, "Recipe Contents", (void *)recipe->content, MIN(recipe->content_size, lsmp_config.voucher_detail_length)); + break; + } + + if (len + 1 < maxlen && voucher_outstr[len - 1] != '\n') { + voucher_outstr[len++] = '\n'; + voucher_outstr[len] = '\0'; + } + JSON_OBJECT_END(json); // recipe + return len; +} + + +char * copy_voucher_detail(mach_port_t task, mach_port_name_t voucher, JSON_t json) { + unsigned int recipe_size = voucher_contents_size; + kern_return_t kr = KERN_SUCCESS; + bzero((void *)&voucher_contents[0], sizeof(voucher_contents)); + unsigned v_kobject = 0; + unsigned v_kotype = 0; + uint32_t detail_maxlen = VOUCHER_DETAIL_MAXLEN; + char * voucher_outstr = (char *)malloc(detail_maxlen); + voucher_outstr[0] = '\0'; + uint32_t plen = 0; + + kr = mach_port_kernel_object( task, + voucher, + &v_kotype, (unsigned *)&v_kobject); + if (kr == KERN_SUCCESS && v_kotype == IKOT_VOUCHER ) { + + kr = mach_voucher_debug_info(task, voucher, + (mach_voucher_attr_raw_recipe_array_t)&voucher_contents[0], + &recipe_size); + if (kr != KERN_SUCCESS && kr != KERN_NOT_SUPPORTED) { + plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x Failed to get contents %s\n", v_kobject, mach_error_string(kr)); + return voucher_outstr; + } + + if (recipe_size == 0) { + plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x has no contents\n", v_kobject); + return voucher_outstr; + } + + plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Voucher: 0x%x\n", v_kobject); + unsigned int used_size = 0; + mach_voucher_attr_recipe_t recipe = NULL; + while (recipe_size > used_size) { + recipe = (mach_voucher_attr_recipe_t)&voucher_contents[used_size]; + if (recipe->key) { + plen += show_recipe_detail(recipe, &voucher_outstr[plen], safesize(detail_maxlen - plen), json); + } + used_size += sizeof(mach_voucher_attr_recipe_data_t) + recipe->content_size; + } + } else { + plen += snprintf(&voucher_outstr[plen], safesize(detail_maxlen - plen), VOUCHER_DETAIL_PREFIX "Invalid voucher: 0x%x\n", voucher); + } + + return voucher_outstr; +} + +void get_receive_port_context(task_t taskp, mach_port_name_t portname, mach_port_context_t *context) { + if (context == NULL) { + return; + } + + kern_return_t ret; + ret = mach_port_get_context(taskp, portname, context); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "mach_port_get_context(0x%08x) failed: %s\n", + portname, + mach_error_string(ret)); + *context = (mach_port_context_t)0; + } + return; +} + +int get_recieve_port_status(task_t taskp, mach_port_name_t portname, mach_port_info_ext_t *info){ + if (info == NULL) { + return -1; + } + mach_msg_type_number_t statusCnt; + kern_return_t ret; + statusCnt = MACH_PORT_INFO_EXT_COUNT; + ret = mach_port_get_attributes(taskp, + portname, + MACH_PORT_INFO_EXT, + (mach_port_info_t)info, + &statusCnt); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "mach_port_get_attributes(0x%08x) failed: %s\n", + portname, + mach_error_string(ret)); + return -1; + } + + return 0; +} + +void show_task_mach_ports(my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, JSON_t json) +{ + int i; + task_table_entry_counts counts = {0}; + + counts.total = taskinfo->tableCount + taskinfo->treeCount; + + JSON_KEY(json, ports) + JSON_ARRAY_BEGIN(json); + + printf(" name ipc-object rights flags boost reqs recv send sonce oref qlimit msgcount context identifier type\n"); + printf("--------- ---------- ---------- -------- ----- ---- ----- ----- ----- ---- ------ -------- ------------------ ----------- ------------\n"); + for (i = 0; i < taskinfo->tableCount; i++) { + show_task_table_entry(&taskinfo->table[i], taskinfo, taskCount, allTaskInfos, &counts, json); + } + + JSON_ARRAY_END(json); // ports + JSON_OBJECT_SET(json, total, %d, counts.total); + JSON_OBJECT_SET(json, send_rights, %d, counts.sendcount); + JSON_OBJECT_SET(json, receive_rights, %d, counts.receivecount); + JSON_OBJECT_SET(json, send_once_rights, %d, counts.sendoncecount); + JSON_OBJECT_SET(json, port_sets, %d, counts.portsetcount); + JSON_OBJECT_SET(json, dead_names, %d, counts.deadcount); + JSON_OBJECT_SET(json, dead_name requests, %d, counts.dncount); + JSON_OBJECT_SET(json, vouchers, %d, counts.vouchercount); + + printf("\n"); + printf("total = %d\n", counts.total); + printf("SEND = %d\n", counts.sendcount); + printf("RECEIVE = %d\n", counts.receivecount); + printf("SEND_ONCE = %d\n", counts.sendoncecount); + printf("PORT_SET = %d\n", counts.portsetcount); + printf("DEAD_NAME = %d\n", counts.deadcount); + printf("DNREQUEST = %d\n", counts.dncount); + printf("VOUCHERS = %d\n", counts.vouchercount); +} + +static void show_task_table_entry(ipc_info_name_t *entry, my_per_task_info_t *taskinfo, uint32_t taskCount, my_per_task_info_t *allTaskInfos, task_table_entry_counts_t counts, JSON_t json) { + int j, k, port_status_flag_idx; + kern_return_t ret; + boolean_t send = FALSE; + boolean_t dnreq = FALSE; + int sendrights = 0; + unsigned int kotype = 0; + vm_offset_t kobject = (vm_offset_t)0; + kobject_description_t desc; + mach_vm_address_t kaddr; + + /* skip empty slots in the table */ + if ((entry->iin_type & MACH_PORT_TYPE_ALL_RIGHTS) == 0) { + counts->total--; + return; + } + + if (entry->iin_type == MACH_PORT_TYPE_PORT_SET) { + mach_port_name_array_t members; + mach_msg_type_number_t membersCnt; + + ret = mach_port_get_set_status(taskinfo->task, + entry->iin_name, + &members, &membersCnt); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "mach_port_get_set_status(0x%08x) failed: %s\n", + entry->iin_name, + mach_error_string(ret)); + return; + } + + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, type, "port set"); + JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object); + + JSON_KEY(json, members); + JSON_ARRAY_BEGIN(json); + + printf("0x%08x 0x%08x port-set -------- --- 1 %d members\n", + entry->iin_name, + entry->iin_object, + membersCnt); + /* get some info for each portset member */ + for (j = 0; j < membersCnt; j++) { + for (k = 0; k < taskinfo->tableCount; k++) { + if (taskinfo->table[k].iin_name == members[j]) { + mach_port_info_ext_t info; + mach_port_status_t port_status; + mach_port_context_t port_context = (mach_port_context_t)0; + if (0 != get_recieve_port_status(taskinfo->task, taskinfo->table[k].iin_name, &info)) { + bzero((void *)&info, sizeof(info)); + } + port_status = info.mpie_status; + get_receive_port_context(taskinfo->task, taskinfo->table[k].iin_name, &port_context); + + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", taskinfo->table[k].iin_object); + + JSON_KEY(json, rights); + JSON_ARRAY_BEGIN(json); + JSON_ARRAY_APPEND(json, "recv"); + if (taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) { + JSON_ARRAY_APPEND(json, "send"); + } + JSON_ARRAY_END(json); // rights + + JSON_KEY(json, port_status_flags); + JSON_ARRAY_BEGIN(json); + port_status_flag_idx = 0; + while (0 != port_status_flags[port_status_flag_idx++].flag) { + if (port_status.mps_flags & INDEX_TO_PORT_FLAG(port_status_flag_idx)) { + JSON_ARRAY_APPEND(json, "%s", port_status_flags[port_status_flag_idx].name); + } + } + JSON_ARRAY_END(json); // port status flags + JSON_OBJECT_SET(json, boosts, %d, info.mpie_boost_cnt); + + JSON_KEY(json, notifications); + JSON_ARRAY_BEGIN(json); + if (taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) { + JSON_ARRAY_APPEND(json, "dead name"); + } + if (port_status.mps_nsrequest) { + JSON_ARRAY_APPEND(json, "no sender"); + } + if (port_status.mps_nsrequest) { + JSON_ARRAY_APPEND(json, "port destroy request"); + } + JSON_ARRAY_END(json); // notifications + + JSON_OBJECT_SET(json, recv_rights, %d, 1); + JSON_OBJECT_SET(json, send_rights, %d, taskinfo->table[k].iin_urefs); + JSON_OBJECT_SET(json, send_once_rights, %d, port_status.mps_sorights); + JSON_OBJECT_SET_BOOL(json, oref, port_status.mps_srights); + JSON_OBJECT_SET(json, queue_limit, %d, port_status.mps_qlimit); + JSON_OBJECT_SET(json, msg_count, %d, port_status.mps_msgcount); + JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)port_context); + JSON_OBJECT_SET(json, identifier, "0x%08x", taskinfo->table[k].iin_name); + JSON_OBJECT_SET(json, pid, %d, taskinfo->pid); + JSON_OBJECT_SET(json, process, "%s", taskinfo->processName); + JSON_OBJECT_END(json); // member + + printf(" - 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx 0x%08x (%d) %s\n", + taskinfo->table[k].iin_object, + (taskinfo->table[k].iin_type & MACH_PORT_TYPE_SEND) ? "recv,send ":"recv ", + SHOW_PORT_STATUS_FLAGS(port_status.mps_flags), + info.mpie_boost_cnt, + (taskinfo->table[k].iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-", + (port_status.mps_nsrequest) ? "N" : "-", + (port_status.mps_pdrequest) ? "P" : "-", + 1, + taskinfo->table[k].iin_urefs, + port_status.mps_sorights, + (port_status.mps_srights) ? "Y" : "N", + port_status.mps_qlimit, + port_status.mps_msgcount, + (uint64_t)port_context, + taskinfo->table[k].iin_name, + taskinfo->pid, + taskinfo->processName); + break; + } + } + } + + JSON_ARRAY_END(json); // members + JSON_OBJECT_END(json); // port-set + + ret = vm_deallocate(mach_task_self(), (vm_address_t)members, + membersCnt * sizeof(mach_port_name_t)); + if (ret != KERN_SUCCESS) { + fprintf(stderr, "vm_deallocate() failed: %s\n", + mach_error_string(ret)); + exit(1); + } + counts->portsetcount++; + return; + } + + if (entry->iin_type & MACH_PORT_TYPE_SEND) { + send = TRUE; + sendrights = entry->iin_urefs; + counts->sendcount++; + } + + if (entry->iin_type & MACH_PORT_TYPE_DNREQUEST) { + dnreq = TRUE; + counts->dncount++; + } + + if (entry->iin_type & MACH_PORT_TYPE_RECEIVE) { + mach_port_status_t status; + mach_port_info_ext_t info; + mach_port_context_t context = (mach_port_context_t)0; + struct k2n_table_node *k2nnode; + ret = get_recieve_port_status(taskinfo->task, entry->iin_name, &info); + get_receive_port_context(taskinfo->task, entry->iin_name, &context); + /* its ok to fail in fetching attributes */ + if (ret < 0) { + return; + } + status = info.mpie_status; + + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, type, "port"); + JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object); + + JSON_KEY(json, rights); + JSON_ARRAY_BEGIN(json); + JSON_ARRAY_APPEND(json, "recv"); + if (send) JSON_ARRAY_APPEND(json, "send"); + JSON_ARRAY_END(json); // rights + + JSON_KEY(json, port_status_flags); + JSON_ARRAY_BEGIN(json); + port_status_flag_idx = 0; + while (0 != port_status_flags[port_status_flag_idx++].flag) { + if (status.mps_flags & INDEX_TO_PORT_FLAG(port_status_flag_idx)) { + JSON_ARRAY_APPEND(json, "%s", port_status_flags[port_status_flag_idx].name); + } + } + JSON_ARRAY_END(json); // port status flags + JSON_OBJECT_SET(json, boosts, %d, info.mpie_boost_cnt); + + JSON_KEY(json, notifications); + JSON_ARRAY_BEGIN(json); + if (dnreq) { + JSON_ARRAY_APPEND(json, "dead name"); + } + if (status.mps_nsrequest) { + JSON_ARRAY_APPEND(json, "no sender"); + } + if (status.mps_nsrequest) { + JSON_ARRAY_APPEND(json, "port destroy request"); + } + JSON_ARRAY_END(json); // notifications + + JSON_OBJECT_SET(json, recv_rights, %d, 1); + JSON_OBJECT_SET(json, send_rights, %d, sendrights); + JSON_OBJECT_SET(json, send_once_rights, %d, status.mps_sorights); + JSON_OBJECT_SET_BOOL(json, oref, status.mps_srights); + JSON_OBJECT_SET(json, queue_limit, %d, status.mps_qlimit); + JSON_OBJECT_SET(json, msg_count, %d, status.mps_msgcount); + JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)context); + JSON_OBJECT_END(json); // port + + printf("0x%08x 0x%08x %s --%s%s%s%s%s%s %5d %s%s%s %5d %5.0d %5.0d %s %6d %8d 0x%016llx \n", + entry->iin_name, + entry->iin_object, + (send) ? "recv,send ":"recv ", + SHOW_PORT_STATUS_FLAGS(status.mps_flags), + info.mpie_boost_cnt, + (dnreq) ? "D":"-", + (status.mps_nsrequest) ? "N":"-", + (status.mps_pdrequest) ? "P":"-", + 1, + sendrights, + status.mps_sorights, + (status.mps_srights) ? "Y":"N", + status.mps_qlimit, + status.mps_msgcount, + (uint64_t)context); + counts->receivecount++; + + /* show other rights (in this and other tasks) for the port */ + for (j = 0; j < taskCount; j++) { + if (allTaskInfos[j].valid == FALSE) + continue; + + k2nnode = k2n_table_lookup(allTaskInfos[j].k2ntable, entry->iin_object); + + while (k2nnode) { + if (k2nnode->info_name != entry) { + assert(k2nnode->info_name->iin_object == entry->iin_object); + + printf(" + %s -------- %s%s%s %5d <- 0x%08x (%d) %s\n", + (k2nnode->info_name->iin_type & MACH_PORT_TYPE_SEND_ONCE) ? + "send-once " : "send ", + (k2nnode->info_name->iin_type & MACH_PORT_TYPE_DNREQUEST) ? "D" : "-", + "-", + "-", + k2nnode->info_name->iin_urefs, + k2nnode->info_name->iin_name, + allTaskInfos[j].pid, + allTaskInfos[j].processName); + } + + k2nnode = k2n_table_lookup_next(k2nnode, entry->iin_object); + } + } + return; + } + else if (entry->iin_type & MACH_PORT_TYPE_DEAD_NAME) + { + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, type, "dead name"); + JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object); + JSON_OBJECT_SET(json, send_rights, %d, entry->iin_urefs); + JSON_OBJECT_END(json); // dead name + + printf("0x%08x 0x%08x dead-name -------- --- %5d \n", + entry->iin_name, + entry->iin_object, + entry->iin_urefs); + counts->deadcount++; + return; + } + + if (entry->iin_type & MACH_PORT_TYPE_SEND_ONCE) { + counts->sendoncecount++; + } + + JSON_OBJECT_BEGIN(json); + JSON_OBJECT_SET(json, name, "0x%08x", entry->iin_name); + JSON_OBJECT_SET(json, ipc-object, "0x%08x", entry->iin_object); + + JSON_KEY(json, rights); + JSON_ARRAY_BEGIN(json); + JSON_ARRAY_APPEND(json, "%s", (send) ? "send":"send once"); + JSON_ARRAY_END(json); //rights + + JSON_KEY(json, notifications); + JSON_ARRAY_BEGIN(json); + if (dnreq) JSON_ARRAY_APPEND(json, "dead name"); + JSON_ARRAY_END(json); // notifications + + JSON_OBJECT_SET(json, send_rights, %d, (send) ? sendrights : 0); + + printf("0x%08x 0x%08x %s -------- %s%s%s %5.0d ", + entry->iin_name, + entry->iin_object, + (send) ? "send ":"send-once ", + (dnreq) ? "D":"-", + "-", + "-", + (send) ? sendrights : 0); + + /* converting to kobjects is not always supported */ + + desc[0] = '\0'; + ret = mach_port_kobject_description(taskinfo->task, + entry->iin_name, + &kotype, &kaddr, + desc); + if (KERN_SUCCESS == ret) { + kobject = (unsigned) kaddr; + } else { + ret = mach_port_kernel_object(taskinfo->task, + entry->iin_name, + &kotype, (unsigned *)&kobject); + } + + if (ret == KERN_SUCCESS && kotype != 0) { + JSON_OBJECT_SET(json, identifier, "0x%08x", (natural_t)kobject); + JSON_OBJECT_SET(json, type, "%s", kobject_name(kotype)); + if (desc[0]) { + JSON_OBJECT_SET(json, description, "%s", desc); + printf(" 0x%08x %s %s", (natural_t)kobject, kobject_name(kotype), desc); + } else { + printf(" 0x%08x %s", (natural_t)kobject, kobject_name(kotype)); + } + if ((kotype == IKOT_TASK_RESUME) || (kotype == IKOT_TASK_CONTROL) || (kotype == IKOT_TASK_NAME)) { + if (taskinfo->task_kobject == kobject) { + /* neat little optimization since in most cases tasks have themselves in their ipc space */ + JSON_OBJECT_SET(json, pid, %d, taskinfo->pid); + JSON_OBJECT_SET(json, process, "%s", taskinfo->processName); + printf(" SELF (%d) %s", taskinfo->pid, taskinfo->processName); + } else { + my_per_task_info_t * _found_task = get_taskinfo_by_kobject((natural_t)kobject); + JSON_OBJECT_SET(json, pid, %d, _found_task->pid); + JSON_OBJECT_SET(json, process, "%s", _found_task->processName); + printf(" (%d) %s", _found_task->pid, _found_task->processName); + } + } + + if (kotype == IKOT_THREAD_CONTROL) { + for (int i = 0; i < taskinfo->threadCount; i++) { + if (taskinfo->threadInfos[i].th_kobject == kobject) { + printf(" (%#llx)", taskinfo->threadInfos[i].th_id); + break; + } + } + } + + printf("\n"); + if (kotype == IKOT_VOUCHER) { + counts->vouchercount++; + if (lsmp_config.show_voucher_details) { + JSON_KEY(json, recipes); + JSON_ARRAY_BEGIN(json); + char * detail = copy_voucher_detail(taskinfo->task, entry->iin_name, json); + JSON_ARRAY_END(json); // recipes + printf("%s\n", detail); + free(detail); + } + } + JSON_OBJECT_END(json); // kobject + return; + } + + /* not kobject - find the receive right holder */ + 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(entry, &recv_holder_taskinfo, &recv_name)) { + mach_port_status_t port_status; + mach_port_info_ext_t info; + mach_port_context_t port_context = (mach_port_context_t)0; + if (0 != get_recieve_port_status(recv_holder_taskinfo->task, recv_name, &info)) { + bzero((void *)&port_status, sizeof(port_status)); + } + port_status = info.mpie_status; + get_receive_port_context(recv_holder_taskinfo->task, recv_name, &port_context); + + JSON_OBJECT_SET(json, queue_limit, %d, port_status.mps_qlimit); + JSON_OBJECT_SET(json, msg_count, %d, port_status.mps_msgcount); + JSON_OBJECT_SET(json, context, "0x%016llx", (uint64_t)port_context); + JSON_OBJECT_SET(json, identifier, "0x%08x", recv_name); + JSON_OBJECT_SET(json, pid, %d, recv_holder_taskinfo->pid); + JSON_OBJECT_SET(json, process, "%s", recv_holder_taskinfo->processName); + + printf(" -> %6d %8d 0x%016llx 0x%08x (%d) %s\n", + port_status.mps_qlimit, + port_status.mps_msgcount, + (uint64_t)port_context, + recv_name, + recv_holder_taskinfo->pid, + recv_holder_taskinfo->processName); + + } else { + JSON_OBJECT_SET(json, identifier, "0x%08x", 0); + JSON_OBJECT_SET(json, pid, %d, -1); + JSON_OBJECT_SET(json, process, "unknown"); + printf(" 0x00000000 (-) Unknown Process\n"); + } + + JSON_OBJECT_END(json); // non-kobject +} + +uint32_t print_hex_data(char *outstr, uint32_t maxlen, char *prefix, char *desc, void *addr, int len) { + int i; + unsigned char buff[17]; + unsigned char *pc = addr; + uint32_t plen = 0; + + if (desc != NULL) + plen += snprintf(&outstr[plen], safesize(maxlen - plen), "%s%s:\n", prefix, desc); + + for (i = 0; i < len; i++) { + + if ((i % 16) == 0) { + if (i != 0) + plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %s\n", buff); + + plen += snprintf(&outstr[plen], safesize(maxlen - plen), "%s %04x ", prefix, i); + } + + plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %02x", pc[i]); + + if ((pc[i] < 0x20) || (pc[i] > 0x7e)) + buff[i % 16] = '.'; + else + buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + + while ((i % 16) != 0) { + plen += snprintf(&outstr[plen], safesize(maxlen - plen), " "); + i++; + } + + plen += snprintf(&outstr[plen], safesize(maxlen - plen), " %s\n", buff); + + return plen; +} 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 +#include +#include +#include +#include +#include +#include +#include + +#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<exceptionInfo.count; i++) { + if (taskinfo->exceptionInfo.ports[i] != MACH_PORT_NULL) { + if (header_required) { + + printf(" exc_port flavor 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 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; + +} -- cgit v1.2.3-56-ge451