]> git.saurik.com Git - apple/system_cmds.git/blob - lsmp.tproj/task_details.c
system_cmds-735.20.1.tar.gz
[apple/system_cmds.git] / lsmp.tproj / task_details.c
1 /*
2 * Copyright (c) 2002-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <unistd.h>
24 #include <mach/mach.h>
25 #include <mach/mach_error.h>
26 #include <mach_debug/ipc_info.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <libproc.h>
30 #include <assert.h>
31
32 #include "common.h"
33
34 #pragma mark kobject to name hash table implementation
35
36 #if (K2N_TABLE_SIZE & (K2N_TABLE_SIZE - 1) != 0)
37 #error K2N_TABLE_SIZE must be a power of two
38 #endif
39
40 #define K2N_TABLE_MASK (K2N_TABLE_SIZE - 1)
41
42 static uint32_t k2n_hash(natural_t kobject) {
43 return (uint64_t)kobject * 2654435761 >> 32;
44 }
45
46 struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natural_t kobject) {
47 while (node) {
48 if (kobject == node->kobject)
49 return node;
50
51 node = node->next;
52 }
53
54 return NULL;
55 }
56
57 struct k2n_table_node *k2n_table_lookup(struct k2n_table_node **table, natural_t kobject) {
58 uint32_t hv;
59 struct k2n_table_node *node;
60
61 hv = k2n_hash(kobject);
62
63 node = table[hv & K2N_TABLE_MASK];
64
65 return k2n_table_lookup_next(node, kobject);
66 }
67
68 static void k2n_table_enter(struct k2n_table_node **table, natural_t kobject, ipc_info_name_t *info_name) {
69 uint32_t hv;
70 struct k2n_table_node *node;
71
72 hv = k2n_hash(kobject);
73
74 node = malloc(sizeof (struct k2n_table_node));
75 assert(node);
76
77 node->kobject = kobject;
78 node->info_name = info_name;
79
80 node->next = table[hv & K2N_TABLE_MASK];
81 table[hv & K2N_TABLE_MASK] = node;
82 }
83
84 #pragma mark -
85
86 static my_per_task_info_t NOT_FOUND_TASK_INFO = {
87 .task = NULL,
88 .task_kobject = NULL,
89 .pid = -1,
90 .info = {0,0,0,0,0,0},
91 .table = NULL,
92 .tableCount = 0,
93 .tree = NULL,
94 .treeCount = 0,
95 .valid = FALSE,
96 .k2ntable = {0},
97 .processName = "Unknown",
98 .exceptionInfo = {0},
99 .threadInfos = NULL,
100 .threadCount = 0,
101 .threadExceptionInfos = NULL
102 };
103
104 char * get_task_name_by_pid(pid_t pid);
105
106 static void proc_pid_to_name(int pid, char *pname){
107 if (0 == proc_name(pid, pname, PROC_NAME_LEN)) {
108 strcpy(pname, "Unknown Process");
109 }
110 }
111
112 static my_per_task_info_t *global_taskinfo = NULL;
113 static uint32_t global_taskcount = 0;
114
115 my_per_task_info_t * allocate_taskinfo_memory(uint32_t taskCount)
116 {
117 my_per_task_info_t * retval = malloc(taskCount * sizeof(my_per_task_info_t));
118 if (retval) {
119 bzero((void *)retval, taskCount * sizeof(my_per_task_info_t));
120 }
121 global_taskcount = taskCount;
122 global_taskinfo = retval;
123 return retval;
124 }
125
126 void deallocate_taskinfo_memory(my_per_task_info_t *data){
127 if (data) {
128 free(data);
129 global_taskinfo = NULL;
130 global_taskcount = 0;
131 }
132 }
133
134 kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_t target_task)
135 {
136 int i;
137 kern_return_t ret = KERN_SUCCESS;
138 unsigned int kotype = 0;
139 vm_offset_t kobject = (vm_offset_t)0;
140
141 taskinfo->task = target_task;
142 pid_for_task(target_task, &taskinfo->pid);
143
144 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);
145
146 if (ret != KERN_SUCCESS) {
147 fprintf(stderr, "task_get_exception_ports() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
148 taskinfo->pid = 0;
149 }
150
151 /* collect threads port as well */
152 taskinfo->threadCount = 0;
153 thread_act_array_t threadPorts;
154 ret = task_threads(taskinfo->task, &threadPorts, &taskinfo->threadCount);
155
156 if (ret != KERN_SUCCESS) {
157 fprintf(stderr, "task_threads() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
158 taskinfo->threadCount = 0;
159 } else {
160 /* collect the thread information */
161 taskinfo->threadInfos = (struct my_per_thread_info *)malloc(sizeof(struct my_per_thread_info) * taskinfo->threadCount);
162 bzero(taskinfo->threadInfos, sizeof(struct my_per_thread_info) * taskinfo->threadCount);
163
164 /* now collect exception ports for each of those threads as well */
165 taskinfo->threadExceptionInfos = (struct exc_port_info *) malloc(sizeof(struct exc_port_info) * taskinfo->threadCount);
166 boolean_t found_exception = false;
167 for (int i = 0; i < taskinfo->threadCount; i++) {
168 unsigned th_kobject = 0;
169 unsigned th_kotype = 0;
170 ipc_voucher_t th_voucher = IPC_VOUCHER_NULL;
171 thread_identifier_info_data_t th_info;
172 mach_msg_type_number_t th_info_count = THREAD_IDENTIFIER_INFO_COUNT;
173 struct exc_port_info *excinfo = &(taskinfo->threadExceptionInfos[i]);
174
175 ret = thread_get_exception_ports(threadPorts[i], EXC_MASK_ALL, excinfo->masks, &excinfo->count, excinfo->ports, excinfo->behaviors, excinfo->flavors);
176 if (ret != KERN_SUCCESS){
177 fprintf(stderr, "thread_get_exception_ports() failed: pid: %d thread: %d error %s\n", taskinfo->pid, threadPorts[i], mach_error_string(ret));
178 }
179
180 if (excinfo->count != 0) {
181 found_exception = true;
182 }
183
184 taskinfo->threadInfos[i].thread = threadPorts[i];
185
186 if (KERN_SUCCESS == mach_port_kernel_object(mach_task_self(), threadPorts[i], &th_kotype, &th_kobject)) {
187 taskinfo->threadInfos[i].th_kobject = th_kobject;
188 if (KERN_SUCCESS == thread_info(threadPorts[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&th_info, &th_info_count)) {
189 taskinfo->threadInfos[i].th_id = th_info.thread_id;
190 }
191 }
192
193 if (KERN_SUCCESS == thread_get_mach_voucher(threadPorts[i], 0, &th_voucher) && th_voucher != IPC_VOUCHER_NULL) {
194 char *detail = copy_voucher_detail(mach_task_self(), th_voucher);
195 taskinfo->threadInfos[i].voucher_detail = strndup(detail, VOUCHER_DETAIL_MAXLEN);
196 free(detail);
197
198 mach_port_deallocate(mach_task_self(), th_voucher);
199 }
200
201 mach_port_deallocate(mach_task_self(), threadPorts[i]);
202 threadPorts[i] = MACH_PORT_NULL;
203 }
204
205 if (found_exception == false) {
206 free(taskinfo->threadExceptionInfos);
207 taskinfo->threadExceptionInfos = NULL;
208 }
209
210 }
211
212 vm_deallocate(mach_task_self(), threadPorts, taskinfo->threadCount * sizeof(thread_act_t));
213 threadPorts = NULL;
214
215 ret = mach_port_space_info(target_task, &taskinfo->info, &taskinfo->table, &taskinfo->tableCount, &taskinfo->tree, &taskinfo->treeCount);
216
217 if (ret != KERN_SUCCESS) {
218 fprintf(stderr, "mach_port_space_info() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
219 taskinfo->pid = 0;
220 return ret;
221 }
222
223 bzero(taskinfo->k2ntable, K2N_TABLE_SIZE * sizeof (struct k2n_table_node *));
224 for (i = 0; i < taskinfo->tableCount; i++) {
225 k2n_table_enter(taskinfo->k2ntable, taskinfo->table[i].iin_object, &taskinfo->table[i]);
226 }
227
228 proc_pid_to_name(taskinfo->pid, taskinfo->processName);
229
230 ret = mach_port_kernel_object(mach_task_self(), taskinfo->task, &kotype, (unsigned *)&kobject);
231
232 if (ret == KERN_SUCCESS && kotype == IKOT_TASK) {
233 taskinfo->task_kobject = kobject;
234 taskinfo->valid = TRUE;
235 }
236
237 return ret;
238 }
239
240 void get_exc_behavior_string(exception_behavior_t b, char *out_string, size_t len)
241 {
242 out_string[0]='\0';
243
244 if (b & MACH_EXCEPTION_CODES)
245 strncat(out_string, "MACH +", len);
246 switch (b & ~MACH_EXCEPTION_CODES) {
247 case EXCEPTION_DEFAULT:
248 strncat(out_string, " DEFAULT", len);
249 break;
250 case EXCEPTION_STATE:
251 strncat(out_string, " STATE", len);
252 break;
253 case EXCEPTION_STATE_IDENTITY:
254 strncat(out_string, " IDENTITY", len);
255 break;
256 default:
257 strncat(out_string, " UNKNOWN", len);
258 }
259 }
260
261 void get_exc_mask_string(exception_mask_t m, char *out_string, size_t len)
262 {
263 out_string[0]='\0';
264
265 if (m & (1<<EXC_BAD_ACCESS))
266 strncat(out_string, " BAD_ACCESS", len);
267 if (m & (1<<EXC_BAD_INSTRUCTION))
268 strncat(out_string," BAD_INSTRUCTION", len);
269 if (m & (1<<EXC_ARITHMETIC))
270 strncat(out_string," ARITHMETIC", len);
271 if (m & (1<<EXC_EMULATION))
272 strncat(out_string," EMULATION", len);
273 if (m & (1<<EXC_SOFTWARE))
274 strncat(out_string," SOFTWARE", len);
275 if (m & (1<<EXC_BREAKPOINT))
276 strncat(out_string," BREAKPOINT", len);
277 if (m & (1<<EXC_SYSCALL))
278 strncat(out_string," SYSCALL", len);
279 if (m & (1<<EXC_MACH_SYSCALL))
280 strncat(out_string," MACH_SYSCALL", len);
281 if (m & (1<<EXC_RPC_ALERT))
282 strncat(out_string," RPC_ALERT", len);
283 if (m & (1<<EXC_CRASH))
284 strncat(out_string," CRASH", len);
285 if (m & (1<<EXC_RESOURCE))
286 strncat(out_string," RESOURCE", len);
287 if (m & (1<<EXC_GUARD))
288 strncat(out_string," GUARD", len);
289 }
290
291 kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo)
292 {
293
294 char behavior_string[30];
295 char mask_string[200];
296
297 boolean_t header_required = TRUE;
298 for (int i = 0; i < taskinfo->exceptionInfo.count; i++) {
299 if (taskinfo->exceptionInfo.ports[i] != MACH_PORT_NULL) {
300 if (header_required) {
301
302 printf(" exc_port flavor <behaviors> mask \n");
303 header_required = FALSE;
304 }
305 get_exc_behavior_string(taskinfo->exceptionInfo.behaviors[i], behavior_string, sizeof(behavior_string));
306 get_exc_mask_string(taskinfo->exceptionInfo.masks[i], mask_string, 200);
307 printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo->exceptionInfo.ports[i], taskinfo->exceptionInfo.flavors[i], behavior_string, mask_string);
308 }
309
310 }
311
312 return KERN_SUCCESS;
313 }
314
315
316 kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo)
317 {
318 kern_return_t kret = KERN_SUCCESS;
319 mach_msg_type_number_t threadcount = taskinfo->threadCount;
320 boolean_t header_required = TRUE;
321 boolean_t newline_required = TRUE;
322 struct my_per_thread_info * info = NULL;
323
324 for (int i = 0; i < threadcount; i++) {
325 info = &taskinfo->threadInfos[i];
326 if (header_required) {
327 printf("Thread_KObject Thread-ID Port Description.");
328 header_required = FALSE;
329 }
330
331 if (newline_required) {
332 printf("\n");
333 }
334 newline_required = TRUE;
335
336 if (info->th_kobject != 0) {
337 /* TODO: Should print tid and stuff */
338 printf("0x%08x ", info->th_kobject);
339 printf("0x%llx ", info->th_id);
340 }
341
342 if (info->voucher_detail != NULL) {
343 printf("%s\n", info->voucher_detail);
344 }
345
346 /* print the thread exception ports also */
347 if (taskinfo->threadExceptionInfos != NULL)
348 {
349
350 struct exc_port_info *excinfo = &taskinfo->threadExceptionInfos[i];
351 char behavior_string[30];
352 char mask_string[200];
353
354 if (excinfo->count > 0) {
355 boolean_t header_required = TRUE;
356 for (int i = 0; i < excinfo->count; i++) {
357 if (excinfo->ports[i] != MACH_PORT_NULL) {
358 if (header_required) {
359 printf("\n exc_port flavor <behaviors> mask -> name owner\n");
360 header_required = FALSE;
361 }
362 get_exc_behavior_string(excinfo->behaviors[i], behavior_string, sizeof(behavior_string));
363 get_exc_mask_string(excinfo->masks[i], mask_string, sizeof(mask_string));
364 printf(" 0x%08x 0x%03x <%s> %s " , excinfo->ports[i], excinfo->flavors[i], behavior_string, mask_string);
365
366 ipc_info_name_t actual_sendinfo;
367 if (KERN_SUCCESS == get_ipc_info_from_lsmp_spaceinfo(excinfo->ports[i], &actual_sendinfo)) {
368 my_per_task_info_t *recv_holder_taskinfo;
369 mach_port_name_t recv_name = MACH_PORT_NULL;
370 if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(&actual_sendinfo, &recv_holder_taskinfo, &recv_name)) {
371 printf(" -> 0x%08x 0x%08x (%d) %s\n",
372 recv_name,
373 actual_sendinfo.iin_object,
374 recv_holder_taskinfo->pid,
375 recv_holder_taskinfo->processName);
376 }
377
378 } else {
379 fprintf(stderr, "failed to find");
380 }
381
382 printf("\n");
383
384 }
385
386 }
387 }
388
389 }
390
391 }
392 printf("\n");
393 return kret;
394 }
395
396 char * get_task_name_by_pid(pid_t pid) {
397 char * retval = "Unknown";
398 for (int i = 0; i < global_taskcount; i++) {
399 if (pid == global_taskinfo[i].pid) {
400 return global_taskinfo[i].processName;
401 }
402 }
403 return retval;
404 }
405
406 my_per_task_info_t * get_taskinfo_by_kobject(natural_t kobj) {
407 my_per_task_info_t *retval = &NOT_FOUND_TASK_INFO;
408 for (int j = 0; j < global_taskcount; j++) {
409 if (global_taskinfo[j].task_kobject == kobj) {
410 retval = &global_taskinfo[j];
411 break;
412 }
413 }
414 return retval;
415 }
416
417 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)
418 {
419 *out_taskinfo = &NOT_FOUND_TASK_INFO;
420 struct k2n_table_node *k2nnode;
421
422 for (int j = 0; j < global_taskcount; j++) {
423 if ((k2nnode = k2n_table_lookup(global_taskinfo[j].k2ntable, sendright->iin_object))) {
424 assert(k2nnode->info_name->iin_object == sendright->iin_object);
425
426 if (k2nnode->info_name->iin_type & MACH_PORT_TYPE_RECEIVE) {
427 *out_taskinfo = &global_taskinfo[j];
428 *out_recv_info = k2nnode->info_name->iin_name;
429 return KERN_SUCCESS;
430 }
431 }
432 }
433
434 return KERN_FAILURE;
435 }
436
437 kern_return_t get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name, ipc_info_name_t *out_sendright){
438 kern_return_t retval = KERN_FAILURE;
439 bzero(out_sendright, sizeof(ipc_info_name_t));
440 my_per_task_info_t *mytaskinfo = NULL;
441 for (int i = global_taskcount - 1; i >= 0; i--){
442 if (global_taskinfo[i].task == mach_task_self()){
443 mytaskinfo = &global_taskinfo[i];
444 break;
445 }
446 }
447 if (mytaskinfo) {
448 for (int k = 0; k < mytaskinfo->tableCount; k++) {
449 if (port_name == mytaskinfo->table[k].iin_name){
450 bcopy(&mytaskinfo->table[k], out_sendright, sizeof(ipc_info_name_t));
451 retval = KERN_SUCCESS;
452 break;
453 }
454 }
455 }
456 return retval;
457
458 }