aboutsummaryrefslogtreecommitdiffstats
path: root/system_cmds/lsmp.tproj
diff options
context:
space:
mode:
Diffstat (limited to 'system_cmds/lsmp.tproj')
-rw-r--r--system_cmds/lsmp.tproj/common.h204
-rw-r--r--system_cmds/lsmp.tproj/entitlements.plist10
-rw-r--r--system_cmds/lsmp.tproj/json.h119
-rw-r--r--system_cmds/lsmp.tproj/lsmp.161
-rw-r--r--system_cmds/lsmp.tproj/lsmp.c239
-rw-r--r--system_cmds/lsmp.tproj/port_details.c746
-rw-r--r--system_cmds/lsmp.tproj/task_details.c507
7 files changed, 1886 insertions, 0 deletions
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 <mach/mach.h>
+#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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>task_for_pid-allow</key>
+ <true/>
+ <key>com.apple.system-task-ports</key>
+ <true/>
+</dict>
+</plist>
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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#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 <pid>
+Show mach port usage for <pid>. 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 <path>
+Save output as JSON to <path>.
+.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 <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 <TargetConditionals.h>
+#include <errno.h>
+#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 <pid> [-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 <pid> : print all mach ports for process id <pid>. \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 <path> : save output as JSON to <path>.\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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libproc.h>
+#include <assert.h>
+#include <mach/mach.h>
+//#include <mach/mach_port.h.h>
+#include <mach/mach_voucher.h>
+#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 <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;
+
+}