]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/lsmp.tproj/task_details.c
Merge branch 'apple'
[apple_cmds.git] / system_cmds / 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 typedef char *kobject_description_t[512];
25 #include <mach/mach.h>
26 #include <mach/mach_error.h>
27 #include <mach_debug/ipc_info.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <libproc.h>
31 #include <assert.h>
32
33 #include "common.h"
34
35 #pragma mark kobject to name hash table implementation
36
37 #if (K2N_TABLE_SIZE & (K2N_TABLE_SIZE - 1) != 0)
38 #error K2N_TABLE_SIZE must be a power of two
39 #endif
40
41 #define K2N_TABLE_MASK (K2N_TABLE_SIZE - 1)
42
43 static uint32_t k2n_hash(natural_t kobject) {
44 return (uint64_t)kobject * 2654435761 >> 32;
45 }
46
47 static struct k2n_table_node *k2n_table_lookup_next_internal(struct k2n_table_node *node, natural_t kobject) {
48 while (node) {
49 if (kobject == node->kobject)
50 return node;
51
52 node = node->next;
53 }
54
55 return NULL;
56 }
57
58 struct k2n_table_node *k2n_table_lookup_next(struct k2n_table_node *node, natural_t kobject) {
59 if (!node) {
60 return NULL;
61 }
62 return k2n_table_lookup_next_internal(node->next, kobject);
63 }
64
65 struct k2n_table_node *k2n_table_lookup(struct k2n_table_node **table, natural_t kobject) {
66 uint32_t hv;
67 struct k2n_table_node *node;
68
69 hv = k2n_hash(kobject);
70
71 node = table[hv & K2N_TABLE_MASK];
72
73 return k2n_table_lookup_next_internal(node, kobject);
74 }
75
76 static void k2n_table_enter(struct k2n_table_node **table, natural_t kobject, ipc_info_name_t *info_name) {
77 uint32_t hv;
78 struct k2n_table_node *node;
79
80 hv = k2n_hash(kobject);
81
82 node = malloc(sizeof (struct k2n_table_node));
83 assert(node);
84
85 node->kobject = kobject;
86 node->info_name = info_name;
87 assert(kobject == info_name->iin_object);
88
89 node->next = table[hv & K2N_TABLE_MASK];
90 table[hv & K2N_TABLE_MASK] = node;
91 }
92
93 #pragma mark -
94
95 static my_per_task_info_t NOT_FOUND_TASK_INFO = {
96 .task = NULL,
97 .task_kobject = NULL,
98 .pid = -1,
99 .info = {0,0,0,0,0,0},
100 .table = NULL,
101 .tableCount = 0,
102 .tree = NULL,
103 .treeCount = 0,
104 .valid = FALSE,
105 .k2ntable = {0},
106 .processName = "Unknown",
107 .exceptionInfo = {0},
108 .threadInfos = NULL,
109 .threadCount = 0,
110 .threadExceptionInfos = NULL
111 };
112
113 char * get_task_name_by_pid(pid_t pid);
114
115 static void proc_pid_to_name(int pid, char *pname){
116 if (0 == proc_name(pid, pname, PROC_NAME_LEN)) {
117 strcpy(pname, "Unknown Process");
118 }
119 }
120
121 static my_per_task_info_t *global_taskinfo = NULL;
122 static uint32_t global_taskcount = 0;
123
124 my_per_task_info_t * allocate_taskinfo_memory(uint32_t taskCount)
125 {
126 my_per_task_info_t * retval = malloc(taskCount * sizeof(my_per_task_info_t));
127 if (retval) {
128 bzero((void *)retval, taskCount * sizeof(my_per_task_info_t));
129 }
130 global_taskcount = taskCount;
131 global_taskinfo = retval;
132 return retval;
133 }
134
135 void deallocate_taskinfo_memory(my_per_task_info_t *data){
136 if (data) {
137 free(data);
138 global_taskinfo = NULL;
139 global_taskcount = 0;
140 }
141 }
142
143 kern_return_t collect_per_task_info(my_per_task_info_t *taskinfo, task_t target_task)
144 {
145 int i;
146 kern_return_t ret = KERN_SUCCESS;
147 unsigned int kotype = 0;
148 vm_offset_t kobject = (vm_offset_t)0;
149
150 taskinfo->task = target_task;
151 pid_for_task(target_task, &taskinfo->pid);
152
153 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);
154
155 if (ret != KERN_SUCCESS) {
156 fprintf(stderr, "task_get_exception_ports() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
157 taskinfo->pid = 0;
158 }
159
160 /* collect threads port as well */
161 taskinfo->threadCount = 0;
162 thread_act_array_t threadPorts;
163 ret = task_threads(taskinfo->task, &threadPorts, &taskinfo->threadCount);
164
165 if (ret != KERN_SUCCESS) {
166 fprintf(stderr, "task_threads() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
167 taskinfo->threadCount = 0;
168 } else {
169 /* collect the thread information */
170 taskinfo->threadInfos = (struct my_per_thread_info *)malloc(sizeof(struct my_per_thread_info) * taskinfo->threadCount);
171 bzero(taskinfo->threadInfos, sizeof(struct my_per_thread_info) * taskinfo->threadCount);
172
173 /* now collect exception ports for each of those threads as well */
174 taskinfo->threadExceptionInfos = (struct exc_port_info *) malloc(sizeof(struct exc_port_info) * taskinfo->threadCount);
175 boolean_t found_exception = false;
176 for (int i = 0; i < taskinfo->threadCount; i++) {
177 unsigned th_kobject = 0;
178 unsigned th_kotype = 0;
179 ipc_voucher_t th_voucher = IPC_VOUCHER_NULL;
180 thread_identifier_info_data_t th_info;
181 mach_msg_type_number_t th_info_count = THREAD_IDENTIFIER_INFO_COUNT;
182 struct exc_port_info *excinfo = &(taskinfo->threadExceptionInfos[i]);
183
184 ret = thread_get_exception_ports(threadPorts[i], EXC_MASK_ALL, excinfo->masks, &excinfo->count, excinfo->ports, excinfo->behaviors, excinfo->flavors);
185 if (ret != KERN_SUCCESS){
186 fprintf(stderr, "thread_get_exception_ports() failed: pid: %d thread: %d error %s\n", taskinfo->pid, threadPorts[i], mach_error_string(ret));
187 }
188
189 if (excinfo->count != 0) {
190 found_exception = true;
191 }
192
193 taskinfo->threadInfos[i].thread = threadPorts[i];
194
195 if (KERN_SUCCESS == mach_port_kernel_object(mach_task_self(), threadPorts[i], &th_kotype, &th_kobject)) {
196 taskinfo->threadInfos[i].th_kobject = th_kobject;
197 if (KERN_SUCCESS == thread_info(threadPorts[i], THREAD_IDENTIFIER_INFO, (thread_info_t)&th_info, &th_info_count)) {
198 taskinfo->threadInfos[i].th_id = th_info.thread_id;
199 }
200 }
201
202 if (KERN_SUCCESS == thread_get_mach_voucher(threadPorts[i], 0, &th_voucher) && th_voucher != IPC_VOUCHER_NULL) {
203 char *detail = copy_voucher_detail(mach_task_self(), th_voucher, NULL);
204 taskinfo->threadInfos[i].voucher_detail = strndup(detail, VOUCHER_DETAIL_MAXLEN);
205 free(detail);
206
207 mach_port_deallocate(mach_task_self(), th_voucher);
208 }
209
210 mach_port_deallocate(mach_task_self(), threadPorts[i]);
211 threadPorts[i] = MACH_PORT_NULL;
212 }
213
214 if (found_exception == false) {
215 free(taskinfo->threadExceptionInfos);
216 taskinfo->threadExceptionInfos = NULL;
217 }
218
219 }
220
221 vm_deallocate(mach_task_self(), threadPorts, taskinfo->threadCount * sizeof(thread_act_t));
222 threadPorts = NULL;
223
224 ret = mach_port_space_info(target_task, &taskinfo->info, &taskinfo->table, &taskinfo->tableCount, &taskinfo->tree, &taskinfo->treeCount);
225
226 if (ret != KERN_SUCCESS) {
227 fprintf(stderr, "mach_port_space_info() failed: pid:%d error: %s\n",taskinfo->pid, mach_error_string(ret));
228 taskinfo->pid = 0;
229 return ret;
230 }
231
232 bzero(taskinfo->k2ntable, K2N_TABLE_SIZE * sizeof (struct k2n_table_node *));
233 for (i = 0; i < taskinfo->tableCount; i++) {
234 k2n_table_enter(taskinfo->k2ntable, taskinfo->table[i].iin_object, &taskinfo->table[i]);
235 }
236
237 proc_pid_to_name(taskinfo->pid, taskinfo->processName);
238
239 ret = mach_port_kernel_object(mach_task_self(), taskinfo->task, &kotype, (unsigned *)&kobject);
240
241 if (ret == KERN_SUCCESS && kotype == IKOT_TASK_CONTROL) {
242 taskinfo->task_kobject = kobject;
243 taskinfo->valid = TRUE;
244 }
245
246 return ret;
247 }
248
249 void get_exc_behavior_string(exception_behavior_t b, char *out_string, size_t len)
250 {
251 out_string[0]='\0';
252
253 if (b & MACH_EXCEPTION_CODES)
254 strlcat(out_string, "MACH +", len);
255 switch (b & ~MACH_EXCEPTION_CODES) {
256 case EXCEPTION_DEFAULT:
257 strlcat(out_string, " DEFAULT", len);
258 break;
259 case EXCEPTION_STATE:
260 strlcat(out_string, " STATE", len);
261 break;
262 case EXCEPTION_STATE_IDENTITY:
263 strlcat(out_string, " IDENTITY", len);
264 break;
265 default:
266 strlcat(out_string, " UNKNOWN", len);
267 }
268 }
269
270 void get_exc_mask_string(exception_mask_t m, char *out_string, size_t len)
271 {
272 out_string[0]='\0';
273
274 if (m & (1<<EXC_BAD_ACCESS))
275 strlcat(out_string, " BAD_ACCESS", len);
276 if (m & (1<<EXC_BAD_INSTRUCTION))
277 strlcat(out_string," BAD_INSTRUCTION", len);
278 if (m & (1<<EXC_ARITHMETIC))
279 strlcat(out_string," ARITHMETIC", len);
280 if (m & (1<<EXC_EMULATION))
281 strlcat(out_string," EMULATION", len);
282 if (m & (1<<EXC_SOFTWARE))
283 strlcat(out_string," SOFTWARE", len);
284 if (m & (1<<EXC_BREAKPOINT))
285 strlcat(out_string," BREAKPOINT", len);
286 if (m & (1<<EXC_SYSCALL))
287 strlcat(out_string," SYSCALL", len);
288 if (m & (1<<EXC_MACH_SYSCALL))
289 strlcat(out_string," MACH_SYSCALL", len);
290 if (m & (1<<EXC_RPC_ALERT))
291 strlcat(out_string," RPC_ALERT", len);
292 if (m & (1<<EXC_CRASH))
293 strlcat(out_string," CRASH", len);
294 if (m & (1<<EXC_RESOURCE))
295 strlcat(out_string," RESOURCE", len);
296 if (m & (1<<EXC_GUARD))
297 strlcat(out_string," GUARD", len);
298 }
299
300 kern_return_t print_task_exception_info(my_per_task_info_t *taskinfo, JSON_t json)
301 {
302
303 char behavior_string[30];
304 char mask_string[200];
305
306 JSON_KEY(json, exception_ports);
307 JSON_ARRAY_BEGIN(json);
308
309 boolean_t header_required = TRUE;
310 for (int i = 0; i < taskinfo->exceptionInfo.count; i++) {
311 if (taskinfo->exceptionInfo.ports[i] != MACH_PORT_NULL) {
312 if (header_required) {
313
314 printf(" exc_port flavor <behaviors> mask \n");
315 header_required = FALSE;
316 }
317 get_exc_behavior_string(taskinfo->exceptionInfo.behaviors[i], behavior_string, sizeof(behavior_string));
318 get_exc_mask_string(taskinfo->exceptionInfo.masks[i], mask_string, sizeof(mask_string));
319
320 JSON_OBJECT_BEGIN(json);
321 JSON_OBJECT_SET(json, port, "0x%08x", taskinfo->exceptionInfo.ports[i]);
322 JSON_OBJECT_SET(json, flavor, "0x%03x", taskinfo->exceptionInfo.flavors[i]);
323 JSON_OBJECT_SET(json, behavior, "%s", behavior_string);
324 JSON_OBJECT_SET(json, mask, "%s", mask_string);
325 JSON_OBJECT_END(json); // exception port
326
327 printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo->exceptionInfo.ports[i], taskinfo->exceptionInfo.flavors[i], behavior_string, mask_string);
328 }
329
330 }
331
332 JSON_ARRAY_END(json); // exception ports
333
334 return KERN_SUCCESS;
335 }
336
337
338 kern_return_t print_task_threads_special_ports(my_per_task_info_t *taskinfo, JSON_t json)
339 {
340 kern_return_t kret = KERN_SUCCESS;
341 mach_msg_type_number_t threadcount = taskinfo->threadCount;
342 boolean_t header_required = TRUE;
343 boolean_t newline_required = TRUE;
344 struct my_per_thread_info * info = NULL;
345
346 JSON_KEY(json, threads);
347 JSON_ARRAY_BEGIN(json);
348
349 for (int i = 0; i < threadcount; i++) {
350 JSON_OBJECT_BEGIN(json);
351
352 info = &taskinfo->threadInfos[i];
353 if (header_required) {
354 printf("Thread_KObject Thread-ID Port Description.");
355 header_required = FALSE;
356 }
357
358 if (newline_required) {
359 printf("\n");
360 }
361 newline_required = TRUE;
362
363 if (info->th_kobject != 0) {
364 /* TODO: Should print tid and stuff */
365 printf("0x%08x ", info->th_kobject);
366 printf("0x%llx ", info->th_id);
367
368 JSON_OBJECT_SET(json, kobject, "0x%08x", info->th_kobject);
369 JSON_OBJECT_SET(json, tid, "0x%llx", info->th_id);
370 }
371
372 if (info->voucher_detail != NULL) {
373 /* TODO: include voucher detail in JSON */
374 printf("%s\n", info->voucher_detail);
375 }
376
377 JSON_KEY(json, exception_ports);
378 JSON_ARRAY_BEGIN(json);
379
380 /* print the thread exception ports also */
381 if (taskinfo->threadExceptionInfos != NULL)
382 {
383
384 struct exc_port_info *excinfo = &taskinfo->threadExceptionInfos[i];
385 char behavior_string[30];
386 char mask_string[200];
387
388 if (excinfo->count > 0) {
389 boolean_t header_required = TRUE;
390 for (int i = 0; i < excinfo->count; i++) {
391 JSON_OBJECT_BEGIN(json);
392
393 if (excinfo->ports[i] != MACH_PORT_NULL) {
394 if (header_required) {
395 printf("\n exc_port flavor <behaviors> mask -> name owner\n");
396 header_required = FALSE;
397 }
398 get_exc_behavior_string(excinfo->behaviors[i], behavior_string, sizeof(behavior_string));
399 get_exc_mask_string(excinfo->masks[i], mask_string, sizeof(mask_string));
400
401 JSON_OBJECT_SET(json, port, "0x%08x", excinfo->ports[i]);
402 JSON_OBJECT_SET(json, flavor, "0x%03x", excinfo->flavors[i]);
403 JSON_OBJECT_SET(json, behavior, "%s", behavior_string);
404 JSON_OBJECT_SET(json, mask, "%s", mask_string);
405
406 printf(" 0x%08x 0x%03x <%s> %s " , excinfo->ports[i], excinfo->flavors[i], behavior_string, mask_string);
407
408 ipc_info_name_t actual_sendinfo;
409 if (KERN_SUCCESS == get_ipc_info_from_lsmp_spaceinfo(excinfo->ports[i], &actual_sendinfo)) {
410 my_per_task_info_t *recv_holder_taskinfo;
411 mach_port_name_t recv_name = MACH_PORT_NULL;
412 if (KERN_SUCCESS == get_taskinfo_of_receiver_by_send_right(&actual_sendinfo, &recv_holder_taskinfo, &recv_name)) {
413
414 JSON_OBJECT_SET(json, name, "0x%08x", recv_name);
415 JSON_OBJECT_SET(json, ipc-object, "0x%08x", actual_sendinfo.iin_object);
416 JSON_OBJECT_SET(json, pid, %d, recv_holder_taskinfo->pid);
417 JSON_OBJECT_SET(json, process, "%s", recv_holder_taskinfo->processName);
418
419 printf(" -> 0x%08x 0x%08x (%d) %s\n",
420 recv_name,
421 actual_sendinfo.iin_object,
422 recv_holder_taskinfo->pid,
423 recv_holder_taskinfo->processName);
424 }
425
426 } else {
427 fprintf(stderr, "failed to find");
428 }
429
430 printf("\n");
431
432 }
433 JSON_OBJECT_END(json); // exception port
434 }
435 }
436 }
437 JSON_ARRAY_END(json); // exception ports
438 JSON_OBJECT_END(json); // thread
439 }
440
441 JSON_ARRAY_END(json); // threads
442 printf("\n");
443 return kret;
444 }
445
446 char * get_task_name_by_pid(pid_t pid) {
447 char * retval = "Unknown";
448 for (int i = 0; i < global_taskcount; i++) {
449 if (pid == global_taskinfo[i].pid) {
450 return global_taskinfo[i].processName;
451 }
452 }
453 return retval;
454 }
455
456 my_per_task_info_t * get_taskinfo_by_kobject(natural_t kobj) {
457 my_per_task_info_t *retval = &NOT_FOUND_TASK_INFO;
458 for (int j = 0; j < global_taskcount; j++) {
459 if (global_taskinfo[j].task_kobject == kobj) {
460 retval = &global_taskinfo[j];
461 break;
462 }
463 }
464 return retval;
465 }
466
467 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)
468 {
469 *out_taskinfo = &NOT_FOUND_TASK_INFO;
470 struct k2n_table_node *k2nnode;
471
472 for (int j = 0; j < global_taskcount; j++) {
473 if ((k2nnode = k2n_table_lookup(global_taskinfo[j].k2ntable, sendright->iin_object))) {
474 assert(k2nnode->info_name->iin_object == sendright->iin_object);
475
476 if (k2nnode->info_name->iin_type & MACH_PORT_TYPE_RECEIVE) {
477 *out_taskinfo = &global_taskinfo[j];
478 *out_recv_info = k2nnode->info_name->iin_name;
479 return KERN_SUCCESS;
480 }
481 }
482 }
483
484 return KERN_FAILURE;
485 }
486
487 kern_return_t get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name, ipc_info_name_t *out_sendright){
488 kern_return_t retval = KERN_FAILURE;
489 bzero(out_sendright, sizeof(ipc_info_name_t));
490 my_per_task_info_t *mytaskinfo = NULL;
491 for (int i = global_taskcount - 1; i >= 0; i--){
492 if (global_taskinfo[i].task == mach_task_self()){
493 mytaskinfo = &global_taskinfo[i];
494 break;
495 }
496 }
497 if (mytaskinfo) {
498 for (int k = 0; k < mytaskinfo->tableCount; k++) {
499 if (port_name == mytaskinfo->table[k].iin_name){
500 bcopy(&mytaskinfo->table[k], out_sendright, sizeof(ipc_info_name_t));
501 retval = KERN_SUCCESS;
502 break;
503 }
504 }
505 }
506 return retval;
507
508 }