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_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(taskinfo
->task
, EXC_MASK_ALL
, taskinfo
->exceptionInfo
.masks
, &taskinfo
->exceptionInfo
.count
, taskinfo
->exceptionInfo
.ports
, taskinfo
->exceptionInfo
.behaviors
, taskinfo
->exceptionInfo
.flavors
);
154 if (ret
!= KERN_SUCCESS
) {
155 fprintf(stderr
, "task_get_exception_ports() 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(threadPorts
[i
], EXC_MASK_ALL
, excinfo
->masks
, &excinfo
->count
, excinfo
->ports
, excinfo
->behaviors
, excinfo
->flavors
);
184 if (ret
!= KERN_SUCCESS
){
185 fprintf(stderr
, "thread_get_exception_ports() 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 if (ret
== KERN_SUCCESS
&& kotype
== IKOT_TASK_CONTROL
) {
241 taskinfo
->task_kobject
= kobject
;
242 taskinfo
->valid
= TRUE
;
248 void get_exc_behavior_string(exception_behavior_t b
, char *out_string
, size_t len
)
252 if (b
& MACH_EXCEPTION_CODES
)
253 strlcat(out_string
, "MACH +", len
);
254 switch (b
& ~MACH_EXCEPTION_CODES
) {
255 case EXCEPTION_DEFAULT
:
256 strlcat(out_string
, " DEFAULT", len
);
258 case EXCEPTION_STATE
:
259 strlcat(out_string
, " STATE", len
);
261 case EXCEPTION_STATE_IDENTITY
:
262 strlcat(out_string
, " IDENTITY", len
);
265 strlcat(out_string
, " UNKNOWN", len
);
269 void get_exc_mask_string(exception_mask_t m
, char *out_string
, size_t len
)
273 if (m
& (1<<EXC_BAD_ACCESS
))
274 strlcat(out_string
, " BAD_ACCESS", len
);
275 if (m
& (1<<EXC_BAD_INSTRUCTION
))
276 strlcat(out_string
," BAD_INSTRUCTION", len
);
277 if (m
& (1<<EXC_ARITHMETIC
))
278 strlcat(out_string
," ARITHMETIC", len
);
279 if (m
& (1<<EXC_EMULATION
))
280 strlcat(out_string
," EMULATION", len
);
281 if (m
& (1<<EXC_SOFTWARE
))
282 strlcat(out_string
," SOFTWARE", len
);
283 if (m
& (1<<EXC_BREAKPOINT
))
284 strlcat(out_string
," BREAKPOINT", len
);
285 if (m
& (1<<EXC_SYSCALL
))
286 strlcat(out_string
," SYSCALL", len
);
287 if (m
& (1<<EXC_MACH_SYSCALL
))
288 strlcat(out_string
," MACH_SYSCALL", len
);
289 if (m
& (1<<EXC_RPC_ALERT
))
290 strlcat(out_string
," RPC_ALERT", len
);
291 if (m
& (1<<EXC_CRASH
))
292 strlcat(out_string
," CRASH", len
);
293 if (m
& (1<<EXC_RESOURCE
))
294 strlcat(out_string
," RESOURCE", len
);
295 if (m
& (1<<EXC_GUARD
))
296 strlcat(out_string
," GUARD", len
);
299 kern_return_t
print_task_exception_info(my_per_task_info_t
*taskinfo
, JSON_t json
)
302 char behavior_string
[30];
303 char mask_string
[200];
305 JSON_KEY(json
, exception_ports
);
306 JSON_ARRAY_BEGIN(json
);
308 boolean_t header_required
= TRUE
;
309 for (int i
= 0; i
< taskinfo
->exceptionInfo
.count
; i
++) {
310 if (taskinfo
->exceptionInfo
.ports
[i
] != MACH_PORT_NULL
) {
311 if (header_required
) {
313 printf(" exc_port flavor <behaviors> mask \n");
314 header_required
= FALSE
;
316 get_exc_behavior_string(taskinfo
->exceptionInfo
.behaviors
[i
], behavior_string
, sizeof(behavior_string
));
317 get_exc_mask_string(taskinfo
->exceptionInfo
.masks
[i
], mask_string
, sizeof(mask_string
));
319 JSON_OBJECT_BEGIN(json
);
320 JSON_OBJECT_SET(json
, port
, "0x%08x", taskinfo
->exceptionInfo
.ports
[i
]);
321 JSON_OBJECT_SET(json
, flavor
, "0x%03x", taskinfo
->exceptionInfo
.flavors
[i
]);
322 JSON_OBJECT_SET(json
, behavior
, "%s", behavior_string
);
323 JSON_OBJECT_SET(json
, mask
, "%s", mask_string
);
324 JSON_OBJECT_END(json
); // exception port
326 printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo
->exceptionInfo
.ports
[i
], taskinfo
->exceptionInfo
.flavors
[i
], behavior_string
, mask_string
);
331 JSON_ARRAY_END(json
); // exception ports
337 kern_return_t
print_task_threads_special_ports(my_per_task_info_t
*taskinfo
, JSON_t json
)
339 kern_return_t kret
= KERN_SUCCESS
;
340 mach_msg_type_number_t threadcount
= taskinfo
->threadCount
;
341 boolean_t header_required
= TRUE
;
342 boolean_t newline_required
= TRUE
;
343 struct my_per_thread_info
* info
= NULL
;
345 JSON_KEY(json
, threads
);
346 JSON_ARRAY_BEGIN(json
);
348 for (int i
= 0; i
< threadcount
; i
++) {
349 JSON_OBJECT_BEGIN(json
);
351 info
= &taskinfo
->threadInfos
[i
];
352 if (header_required
) {
353 printf("Thread_KObject Thread-ID Port Description.");
354 header_required
= FALSE
;
357 if (newline_required
) {
360 newline_required
= TRUE
;
362 if (info
->th_kobject
!= 0) {
363 /* TODO: Should print tid and stuff */
364 printf("0x%08x ", info
->th_kobject
);
365 printf("0x%llx ", info
->th_id
);
367 JSON_OBJECT_SET(json
, kobject
, "0x%08x", info
->th_kobject
);
368 JSON_OBJECT_SET(json
, tid
, "0x%llx", info
->th_id
);
371 if (info
->voucher_detail
!= NULL
) {
372 /* TODO: include voucher detail in JSON */
373 printf("%s\n", info
->voucher_detail
);
376 JSON_KEY(json
, exception_ports
);
377 JSON_ARRAY_BEGIN(json
);
379 /* print the thread exception ports also */
380 if (taskinfo
->threadExceptionInfos
!= NULL
)
383 struct exc_port_info
*excinfo
= &taskinfo
->threadExceptionInfos
[i
];
384 char behavior_string
[30];
385 char mask_string
[200];
387 if (excinfo
->count
> 0) {
388 boolean_t header_required
= TRUE
;
389 for (int i
= 0; i
< excinfo
->count
; i
++) {
390 JSON_OBJECT_BEGIN(json
);
392 if (excinfo
->ports
[i
] != MACH_PORT_NULL
) {
393 if (header_required
) {
394 printf("\n exc_port flavor <behaviors> mask -> name owner\n");
395 header_required
= FALSE
;
397 get_exc_behavior_string(excinfo
->behaviors
[i
], behavior_string
, sizeof(behavior_string
));
398 get_exc_mask_string(excinfo
->masks
[i
], mask_string
, sizeof(mask_string
));
400 JSON_OBJECT_SET(json
, port
, "0x%08x", excinfo
->ports
[i
]);
401 JSON_OBJECT_SET(json
, flavor
, "0x%03x", excinfo
->flavors
[i
]);
402 JSON_OBJECT_SET(json
, behavior
, "%s", behavior_string
);
403 JSON_OBJECT_SET(json
, mask
, "%s", mask_string
);
405 printf(" 0x%08x 0x%03x <%s> %s " , excinfo
->ports
[i
], excinfo
->flavors
[i
], behavior_string
, mask_string
);
407 ipc_info_name_t actual_sendinfo
;
408 if (KERN_SUCCESS
== get_ipc_info_from_lsmp_spaceinfo(excinfo
->ports
[i
], &actual_sendinfo
)) {
409 my_per_task_info_t
*recv_holder_taskinfo
;
410 mach_port_name_t recv_name
= MACH_PORT_NULL
;
411 if (KERN_SUCCESS
== get_taskinfo_of_receiver_by_send_right(&actual_sendinfo
, &recv_holder_taskinfo
, &recv_name
)) {
413 JSON_OBJECT_SET(json
, name
, "0x%08x", recv_name
);
414 JSON_OBJECT_SET(json
, ipc
-object
, "0x%08x", actual_sendinfo
.iin_object
);
415 JSON_OBJECT_SET(json
, pid
, %d
, recv_holder_taskinfo
->pid
);
416 JSON_OBJECT_SET(json
, process
, "%s", recv_holder_taskinfo
->processName
);
418 printf(" -> 0x%08x 0x%08x (%d) %s\n",
420 actual_sendinfo
.iin_object
,
421 recv_holder_taskinfo
->pid
,
422 recv_holder_taskinfo
->processName
);
426 fprintf(stderr
, "failed to find");
432 JSON_OBJECT_END(json
); // exception port
436 JSON_ARRAY_END(json
); // exception ports
437 JSON_OBJECT_END(json
); // thread
440 JSON_ARRAY_END(json
); // threads
445 char * get_task_name_by_pid(pid_t pid
) {
446 char * retval
= "Unknown";
447 for (int i
= 0; i
< global_taskcount
; i
++) {
448 if (pid
== global_taskinfo
[i
].pid
) {
449 return global_taskinfo
[i
].processName
;
455 my_per_task_info_t
* get_taskinfo_by_kobject(natural_t kobj
) {
456 my_per_task_info_t
*retval
= &NOT_FOUND_TASK_INFO
;
457 for (int j
= 0; j
< global_taskcount
; j
++) {
458 if (global_taskinfo
[j
].task_kobject
== kobj
) {
459 retval
= &global_taskinfo
[j
];
466 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 *out_taskinfo
= &NOT_FOUND_TASK_INFO
;
469 struct k2n_table_node
*k2nnode
;
471 for (int j
= 0; j
< global_taskcount
; j
++) {
472 if ((k2nnode
= k2n_table_lookup(global_taskinfo
[j
].k2ntable
, sendright
->iin_object
))) {
473 assert(k2nnode
->info_name
->iin_object
== sendright
->iin_object
);
475 if (k2nnode
->info_name
->iin_type
& MACH_PORT_TYPE_RECEIVE
) {
476 *out_taskinfo
= &global_taskinfo
[j
];
477 *out_recv_info
= k2nnode
->info_name
->iin_name
;
486 kern_return_t
get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name
, ipc_info_name_t
*out_sendright
){
487 kern_return_t retval
= KERN_FAILURE
;
488 bzero(out_sendright
, sizeof(ipc_info_name_t
));
489 my_per_task_info_t
*mytaskinfo
= NULL
;
490 for (int i
= global_taskcount
- 1; i
>= 0; i
--){
491 if (global_taskinfo
[i
].task
== mach_task_self()){
492 mytaskinfo
= &global_taskinfo
[i
];
497 for (int k
= 0; k
< mytaskinfo
->tableCount
; k
++) {
498 if (port_name
== mytaskinfo
->table
[k
].iin_name
){
499 bcopy(&mytaskinfo
->table
[k
], out_sendright
, sizeof(ipc_info_name_t
));
500 retval
= KERN_SUCCESS
;