2 * Copyright (c) 2002-2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
24 #include <mach/mach.h>
25 #include <mach/mach_error.h>
26 #include <mach_debug/ipc_info.h>
34 #pragma mark kobject to name hash table implementation
36 #if (K2N_TABLE_SIZE & (K2N_TABLE_SIZE - 1) != 0)
37 #error K2N_TABLE_SIZE must be a power of two
40 #define K2N_TABLE_MASK (K2N_TABLE_SIZE - 1)
42 static uint32_t k2n_hash(natural_t kobject
) {
43 return (uint64_t)kobject
* 2654435761 >> 32;
46 static struct k2n_table_node
*k2n_table_lookup_next_internal(struct k2n_table_node
*node
, natural_t kobject
) {
48 if (kobject
== node
->kobject
)
57 struct k2n_table_node
*k2n_table_lookup_next(struct k2n_table_node
*node
, natural_t kobject
) {
61 return k2n_table_lookup_next_internal(node
->next
, kobject
);
64 struct k2n_table_node
*k2n_table_lookup(struct k2n_table_node
**table
, natural_t kobject
) {
66 struct k2n_table_node
*node
;
68 hv
= k2n_hash(kobject
);
70 node
= table
[hv
& K2N_TABLE_MASK
];
72 return k2n_table_lookup_next_internal(node
, kobject
);
75 static void k2n_table_enter(struct k2n_table_node
**table
, natural_t kobject
, ipc_info_name_t
*info_name
) {
77 struct k2n_table_node
*node
;
79 hv
= k2n_hash(kobject
);
81 node
= malloc(sizeof (struct k2n_table_node
));
84 node
->kobject
= kobject
;
85 node
->info_name
= info_name
;
86 assert(kobject
== info_name
->iin_object
);
88 node
->next
= table
[hv
& K2N_TABLE_MASK
];
89 table
[hv
& K2N_TABLE_MASK
] = node
;
94 static my_per_task_info_t NOT_FOUND_TASK_INFO
= {
98 .info
= {0,0,0,0,0,0},
105 .processName
= "Unknown",
106 .exceptionInfo
= {0},
109 .threadExceptionInfos
= NULL
112 char * get_task_name_by_pid(pid_t pid
);
114 static void proc_pid_to_name(int pid
, char *pname
){
115 if (0 == proc_name(pid
, pname
, PROC_NAME_LEN
)) {
116 strcpy(pname
, "Unknown Process");
120 static my_per_task_info_t
*global_taskinfo
= NULL
;
121 static uint32_t global_taskcount
= 0;
123 my_per_task_info_t
* allocate_taskinfo_memory(uint32_t taskCount
)
125 my_per_task_info_t
* retval
= malloc(taskCount
* sizeof(my_per_task_info_t
));
127 bzero((void *)retval
, taskCount
* sizeof(my_per_task_info_t
));
129 global_taskcount
= taskCount
;
130 global_taskinfo
= retval
;
134 void deallocate_taskinfo_memory(my_per_task_info_t
*data
){
137 global_taskinfo
= NULL
;
138 global_taskcount
= 0;
142 kern_return_t
collect_per_task_info(my_per_task_info_t
*taskinfo
, task_read_t target_task
)
145 kern_return_t ret
= KERN_SUCCESS
;
146 unsigned int kotype
= 0;
147 vm_offset_t kobject
= (vm_offset_t
)0;
149 taskinfo
->task
= target_task
;
150 pid_for_task(target_task
, &taskinfo
->pid
);
152 ret
= task_get_exception_ports_info(taskinfo
->task
, EXC_MASK_ALL
, taskinfo
->exceptionInfo
.masks
, &taskinfo
->exceptionInfo
.count
, taskinfo
->exceptionInfo
.ports_info
, taskinfo
->exceptionInfo
.behaviors
, taskinfo
->exceptionInfo
.flavors
);
154 if (ret
!= KERN_SUCCESS
) {
155 fprintf(stderr
, "task_get_exception_ports_info() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
159 /* collect threads port as well */
160 taskinfo
->threadCount
= 0;
161 thread_act_array_t threadPorts
;
162 ret
= task_threads(taskinfo
->task
, &threadPorts
, &taskinfo
->threadCount
);
164 if (ret
!= KERN_SUCCESS
) {
165 fprintf(stderr
, "task_threads() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
166 taskinfo
->threadCount
= 0;
168 /* collect the thread information */
169 taskinfo
->threadInfos
= (struct my_per_thread_info
*)malloc(sizeof(struct my_per_thread_info
) * taskinfo
->threadCount
);
170 bzero(taskinfo
->threadInfos
, sizeof(struct my_per_thread_info
) * taskinfo
->threadCount
);
172 /* now collect exception ports for each of those threads as well */
173 taskinfo
->threadExceptionInfos
= (struct exc_port_info
*) malloc(sizeof(struct exc_port_info
) * taskinfo
->threadCount
);
174 boolean_t found_exception
= false;
175 for (int i
= 0; i
< taskinfo
->threadCount
; i
++) {
176 unsigned th_kobject
= 0;
177 unsigned th_kotype
= 0;
178 ipc_voucher_t th_voucher
= IPC_VOUCHER_NULL
;
179 thread_identifier_info_data_t th_info
;
180 mach_msg_type_number_t th_info_count
= THREAD_IDENTIFIER_INFO_COUNT
;
181 struct exc_port_info
*excinfo
= &(taskinfo
->threadExceptionInfos
[i
]);
183 ret
= thread_get_exception_ports_info(threadPorts
[i
], EXC_MASK_ALL
, excinfo
->masks
, &excinfo
->count
, excinfo
->ports_info
, excinfo
->behaviors
, excinfo
->flavors
);
184 if (ret
!= KERN_SUCCESS
){
185 fprintf(stderr
, "thread_get_exception_ports_info() failed: pid: %d thread: %d error %s\n", taskinfo
->pid
, threadPorts
[i
], mach_error_string(ret
));
188 if (excinfo
->count
!= 0) {
189 found_exception
= true;
192 taskinfo
->threadInfos
[i
].thread
= threadPorts
[i
];
194 if (KERN_SUCCESS
== mach_port_kernel_object(mach_task_self(), threadPorts
[i
], &th_kotype
, &th_kobject
)) {
195 taskinfo
->threadInfos
[i
].th_kobject
= th_kobject
;
196 if (KERN_SUCCESS
== thread_info(threadPorts
[i
], THREAD_IDENTIFIER_INFO
, (thread_info_t
)&th_info
, &th_info_count
)) {
197 taskinfo
->threadInfos
[i
].th_id
= th_info
.thread_id
;
201 if (KERN_SUCCESS
== thread_get_mach_voucher(threadPorts
[i
], 0, &th_voucher
) && th_voucher
!= IPC_VOUCHER_NULL
) {
202 char *detail
= copy_voucher_detail(mach_task_self(), th_voucher
, NULL
);
203 taskinfo
->threadInfos
[i
].voucher_detail
= strndup(detail
, VOUCHER_DETAIL_MAXLEN
);
206 mach_port_deallocate(mach_task_self(), th_voucher
);
209 mach_port_deallocate(mach_task_self(), threadPorts
[i
]);
210 threadPorts
[i
] = MACH_PORT_NULL
;
213 if (found_exception
== false) {
214 free(taskinfo
->threadExceptionInfos
);
215 taskinfo
->threadExceptionInfos
= NULL
;
220 vm_deallocate(mach_task_self(), threadPorts
, taskinfo
->threadCount
* sizeof(thread_act_t
));
223 ret
= mach_port_space_info(target_task
, &taskinfo
->info
, &taskinfo
->table
, &taskinfo
->tableCount
, &taskinfo
->tree
, &taskinfo
->treeCount
);
225 if (ret
!= KERN_SUCCESS
) {
226 fprintf(stderr
, "mach_port_space_info() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
231 bzero(taskinfo
->k2ntable
, K2N_TABLE_SIZE
* sizeof (struct k2n_table_node
*));
232 for (i
= 0; i
< taskinfo
->tableCount
; i
++) {
233 k2n_table_enter(taskinfo
->k2ntable
, taskinfo
->table
[i
].iin_object
, &taskinfo
->table
[i
]);
236 proc_pid_to_name(taskinfo
->pid
, taskinfo
->processName
);
238 ret
= mach_port_kernel_object(mach_task_self(), taskinfo
->task
, &kotype
, (unsigned *)&kobject
);
240 /* Now that we are using read ports, kotype should be checked against IKOT_TASK_READ */
241 if (ret
== KERN_SUCCESS
&& kotype
== IKOT_TASK_READ
) {
242 taskinfo
->task_kobject
= kobject
;
243 taskinfo
->valid
= TRUE
;
249 void get_exc_behavior_string(exception_behavior_t b
, char *out_string
, size_t len
)
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
);
259 case EXCEPTION_STATE
:
260 strlcat(out_string
, " STATE", len
);
262 case EXCEPTION_STATE_IDENTITY
:
263 strlcat(out_string
, " IDENTITY", len
);
266 strlcat(out_string
, " UNKNOWN", len
);
270 void get_exc_mask_string(exception_mask_t m
, char *out_string
, size_t len
)
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
);
300 kern_return_t
print_task_exception_info(my_per_task_info_t
*taskinfo
, JSON_t json
)
303 char behavior_string
[30];
304 char mask_string
[200];
306 JSON_KEY(json
, exception_ports
);
307 JSON_ARRAY_BEGIN(json
);
309 boolean_t header_required
= TRUE
;
310 for (int i
= 0; i
< taskinfo
->exceptionInfo
.count
; i
++) {
311 if (taskinfo
->exceptionInfo
.ports_info
[i
].iip_port_object
!= 0) {
312 my_per_task_info_t
* _found_task
;
314 if (header_required
) {
316 printf(" exc_port_object receiver_task flavor <behaviors> mask \n");
317 header_required
= FALSE
;
319 get_exc_behavior_string(taskinfo
->exceptionInfo
.behaviors
[i
], behavior_string
, sizeof(behavior_string
));
320 get_exc_mask_string(taskinfo
->exceptionInfo
.masks
[i
], mask_string
, sizeof(mask_string
));
322 JSON_OBJECT_BEGIN(json
);
323 JSON_OBJECT_SET(json
, port_object
, "0x%08x", taskinfo
->exceptionInfo
.ports_info
[i
].iip_port_object
);
324 JSON_OBJECT_SET(json
, receiver_object
, "0x%08x", taskinfo
->exceptionInfo
.ports_info
[i
].iip_receiver_object
);
325 JSON_OBJECT_SET(json
, flavor
, "0x%03x", taskinfo
->exceptionInfo
.flavors
[i
]);
326 JSON_OBJECT_SET(json
, behavior
, "%s", behavior_string
);
327 JSON_OBJECT_SET(json
, mask
, "%s", mask_string
);
328 JSON_OBJECT_END(json
); // exception port
330 _found_task
= get_taskinfo_by_kobject((natural_t
)taskinfo
->exceptionInfo
.ports_info
[i
].iip_receiver_object
);
332 printf(" 0x%08x (%d) %s 0x%03x <%s> %s \n",
333 taskinfo
->exceptionInfo
.ports_info
[i
].iip_port_object
,
335 _found_task
->processName
,
336 taskinfo
->exceptionInfo
.flavors
[i
], behavior_string
, mask_string
);
341 JSON_ARRAY_END(json
); // exception ports
347 kern_return_t
print_task_threads_special_ports(my_per_task_info_t
*taskinfo
, JSON_t json
)
349 kern_return_t kret
= KERN_SUCCESS
;
350 mach_msg_type_number_t threadcount
= taskinfo
->threadCount
;
351 boolean_t header_required
= TRUE
;
352 boolean_t newline_required
= TRUE
;
353 struct my_per_thread_info
* info
= NULL
;
355 JSON_KEY(json
, threads
);
356 JSON_ARRAY_BEGIN(json
);
358 for (int i
= 0; i
< threadcount
; i
++) {
359 JSON_OBJECT_BEGIN(json
);
361 info
= &taskinfo
->threadInfos
[i
];
362 if (header_required
) {
363 printf("Thread_KObject Thread-ID Port Description.");
364 header_required
= FALSE
;
367 if (newline_required
) {
370 newline_required
= TRUE
;
372 if (info
->th_kobject
!= 0) {
373 /* TODO: Should print tid and stuff */
374 printf("0x%08x ", info
->th_kobject
);
375 printf("0x%llx ", info
->th_id
);
377 JSON_OBJECT_SET(json
, kobject
, "0x%08x", info
->th_kobject
);
378 JSON_OBJECT_SET(json
, tid
, "0x%llx", info
->th_id
);
381 if (info
->voucher_detail
!= NULL
) {
382 /* TODO: include voucher detail in JSON */
383 printf("%s\n", info
->voucher_detail
);
386 JSON_KEY(json
, exception_ports
);
387 JSON_ARRAY_BEGIN(json
);
389 /* print the thread exception ports also */
390 if (taskinfo
->threadExceptionInfos
!= NULL
)
393 struct exc_port_info
*excinfo
= &taskinfo
->threadExceptionInfos
[i
];
394 char behavior_string
[30];
395 char mask_string
[200];
397 if (excinfo
->count
> 0) {
398 boolean_t header_required
= TRUE
;
399 for (int i
= 0; i
< excinfo
->count
; i
++) {
400 JSON_OBJECT_BEGIN(json
);
402 if (excinfo
->ports_info
[i
].iip_port_object
!= 0) {
403 if (header_required
) {
404 printf("\n exc_port_object exc_port_receiver flavor <behaviors> mask -> name owner\n");
405 header_required
= FALSE
;
407 get_exc_behavior_string(excinfo
->behaviors
[i
], behavior_string
, sizeof(behavior_string
));
408 get_exc_mask_string(excinfo
->masks
[i
], mask_string
, sizeof(mask_string
));
410 JSON_OBJECT_SET(json
, port_object
, "0x%08x", excinfo
->ports_info
[i
].iip_port_object
);
411 JSON_OBJECT_SET(json
, receiver_object
, "0x%08x", excinfo
->ports_info
[i
].iip_receiver_object
);
412 JSON_OBJECT_SET(json
, flavor
, "0x%03x", excinfo
->flavors
[i
]);
413 JSON_OBJECT_SET(json
, behavior
, "%s", behavior_string
);
414 JSON_OBJECT_SET(json
, mask
, "%s", mask_string
);
416 printf(" 0x%08x 0x%08x 0x%03x <%s> %s " , excinfo
->ports_info
[i
].iip_port_object
, excinfo
->ports_info
[i
].iip_receiver_object
, excinfo
->flavors
[i
], behavior_string
, mask_string
);
418 my_per_task_info_t
*recv_holder_taskinfo
;
419 mach_port_name_t recv_name
= MACH_PORT_NULL
;
420 if (KERN_SUCCESS
== get_taskinfo_of_receiver_by_send_right_info(excinfo
->ports_info
[i
], &recv_holder_taskinfo
, &recv_name
)) {
421 JSON_OBJECT_SET(json
, name
, "0x%08x", recv_name
);
422 JSON_OBJECT_SET(json
, ipc
-object
, "0x%08x", excinfo
->ports_info
[i
].iip_port_object
);
423 JSON_OBJECT_SET(json
, pid
, %d
, recv_holder_taskinfo
->pid
);
424 JSON_OBJECT_SET(json
, process
, "%s", recv_holder_taskinfo
->processName
);
426 printf(" -> 0x%08x 0x%08x (%d) %s\n",
428 excinfo
->ports_info
[i
].iip_port_object
,
429 recv_holder_taskinfo
->pid
,
430 recv_holder_taskinfo
->processName
);
432 fprintf(stderr
, "failed to find");
438 JSON_OBJECT_END(json
); // exception port
442 JSON_ARRAY_END(json
); // exception ports
443 JSON_OBJECT_END(json
); // thread
446 JSON_ARRAY_END(json
); // threads
451 char * get_task_name_by_pid(pid_t pid
) {
452 char * retval
= "Unknown";
453 for (int i
= 0; i
< global_taskcount
; i
++) {
454 if (pid
== global_taskinfo
[i
].pid
) {
455 return global_taskinfo
[i
].processName
;
461 my_per_task_info_t
* get_taskinfo_by_kobject(natural_t kobj
) {
462 my_per_task_info_t
*retval
= &NOT_FOUND_TASK_INFO
;
463 for (int j
= 0; j
< global_taskcount
; j
++) {
464 if (global_taskinfo
[j
].task_kobject
== kobj
) {
465 retval
= &global_taskinfo
[j
];
472 static kern_return_t
_get_taskinfo_of_receiver_by_send_right(natural_t kobject
, my_per_task_info_t
**out_taskinfo
, mach_port_name_t
*out_recv_info
)
474 *out_taskinfo
= &NOT_FOUND_TASK_INFO
;
475 struct k2n_table_node
*k2nnode
;
477 for (unsigned int j
= 0; j
< global_taskcount
; j
++) {
478 if ((k2nnode
= k2n_table_lookup(global_taskinfo
[j
].k2ntable
, kobject
))) {
479 assert(k2nnode
->info_name
->iin_object
== kobject
);
481 if (k2nnode
->info_name
->iin_type
& MACH_PORT_TYPE_RECEIVE
) {
482 *out_taskinfo
= &global_taskinfo
[j
];
483 *out_recv_info
= k2nnode
->info_name
->iin_name
;
492 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
)
494 return _get_taskinfo_of_receiver_by_send_right(sendright
.iin_object
, out_taskinfo
, out_recv_info
);
497 kern_return_t
get_taskinfo_of_receiver_by_send_right_info(ipc_info_port_t sendright_info
, my_per_task_info_t
**out_taskinfo
, mach_port_name_t
*out_recv_info
)
499 return _get_taskinfo_of_receiver_by_send_right(sendright_info
.iip_port_object
, out_taskinfo
, out_recv_info
);