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 typedef char *kobject_description_t
[512];
25 #include <mach/mach.h>
26 #include <mach/mach_error.h>
27 #include <mach_debug/ipc_info.h>
35 #pragma mark kobject to name hash table implementation
37 #if (K2N_TABLE_SIZE & (K2N_TABLE_SIZE - 1) != 0)
38 #error K2N_TABLE_SIZE must be a power of two
41 #define K2N_TABLE_MASK (K2N_TABLE_SIZE - 1)
43 static uint32_t k2n_hash(natural_t kobject
) {
44 return (uint64_t)kobject
* 2654435761 >> 32;
47 static struct k2n_table_node
*k2n_table_lookup_next_internal(struct k2n_table_node
*node
, natural_t kobject
) {
49 if (kobject
== node
->kobject
)
58 struct k2n_table_node
*k2n_table_lookup_next(struct k2n_table_node
*node
, natural_t kobject
) {
62 return k2n_table_lookup_next_internal(node
->next
, kobject
);
65 struct k2n_table_node
*k2n_table_lookup(struct k2n_table_node
**table
, natural_t kobject
) {
67 struct k2n_table_node
*node
;
69 hv
= k2n_hash(kobject
);
71 node
= table
[hv
& K2N_TABLE_MASK
];
73 return k2n_table_lookup_next_internal(node
, kobject
);
76 static void k2n_table_enter(struct k2n_table_node
**table
, natural_t kobject
, ipc_info_name_t
*info_name
) {
78 struct k2n_table_node
*node
;
80 hv
= k2n_hash(kobject
);
82 node
= malloc(sizeof (struct k2n_table_node
));
85 node
->kobject
= kobject
;
86 node
->info_name
= info_name
;
87 assert(kobject
== info_name
->iin_object
);
89 node
->next
= table
[hv
& K2N_TABLE_MASK
];
90 table
[hv
& K2N_TABLE_MASK
] = node
;
95 static my_per_task_info_t NOT_FOUND_TASK_INFO
= {
99 .info
= {0,0,0,0,0,0},
106 .processName
= "Unknown",
107 .exceptionInfo
= {0},
110 .threadExceptionInfos
= NULL
113 char * get_task_name_by_pid(pid_t pid
);
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");
121 static my_per_task_info_t
*global_taskinfo
= NULL
;
122 static uint32_t global_taskcount
= 0;
124 my_per_task_info_t
* allocate_taskinfo_memory(uint32_t taskCount
)
126 my_per_task_info_t
* retval
= malloc(taskCount
* sizeof(my_per_task_info_t
));
128 bzero((void *)retval
, taskCount
* sizeof(my_per_task_info_t
));
130 global_taskcount
= taskCount
;
131 global_taskinfo
= retval
;
135 void deallocate_taskinfo_memory(my_per_task_info_t
*data
){
138 global_taskinfo
= NULL
;
139 global_taskcount
= 0;
143 kern_return_t
collect_per_task_info(my_per_task_info_t
*taskinfo
, task_t target_task
)
146 kern_return_t ret
= KERN_SUCCESS
;
147 unsigned int kotype
= 0;
148 vm_offset_t kobject
= (vm_offset_t
)0;
150 taskinfo
->task
= target_task
;
151 pid_for_task(target_task
, &taskinfo
->pid
);
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
);
155 if (ret
!= KERN_SUCCESS
) {
156 fprintf(stderr
, "task_get_exception_ports() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
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
);
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;
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
);
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
]);
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
));
189 if (excinfo
->count
!= 0) {
190 found_exception
= true;
193 taskinfo
->threadInfos
[i
].thread
= threadPorts
[i
];
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
;
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
);
207 mach_port_deallocate(mach_task_self(), th_voucher
);
210 mach_port_deallocate(mach_task_self(), threadPorts
[i
]);
211 threadPorts
[i
] = MACH_PORT_NULL
;
214 if (found_exception
== false) {
215 free(taskinfo
->threadExceptionInfos
);
216 taskinfo
->threadExceptionInfos
= NULL
;
221 vm_deallocate(mach_task_self(), threadPorts
, taskinfo
->threadCount
* sizeof(thread_act_t
));
224 ret
= mach_port_space_info(target_task
, &taskinfo
->info
, &taskinfo
->table
, &taskinfo
->tableCount
, &taskinfo
->tree
, &taskinfo
->treeCount
);
226 if (ret
!= KERN_SUCCESS
) {
227 fprintf(stderr
, "mach_port_space_info() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
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
]);
237 proc_pid_to_name(taskinfo
->pid
, taskinfo
->processName
);
239 ret
= mach_port_kernel_object(mach_task_self(), taskinfo
->task
, &kotype
, (unsigned *)&kobject
);
241 if (ret
== KERN_SUCCESS
&& kotype
== IKOT_TASK_CONTROL
) {
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
[i
] != MACH_PORT_NULL
) {
312 if (header_required
) {
314 printf(" exc_port flavor <behaviors> mask \n");
315 header_required
= FALSE
;
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
));
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
327 printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo
->exceptionInfo
.ports
[i
], taskinfo
->exceptionInfo
.flavors
[i
], behavior_string
, mask_string
);
332 JSON_ARRAY_END(json
); // exception ports
338 kern_return_t
print_task_threads_special_ports(my_per_task_info_t
*taskinfo
, JSON_t json
)
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
;
346 JSON_KEY(json
, threads
);
347 JSON_ARRAY_BEGIN(json
);
349 for (int i
= 0; i
< threadcount
; i
++) {
350 JSON_OBJECT_BEGIN(json
);
352 info
= &taskinfo
->threadInfos
[i
];
353 if (header_required
) {
354 printf("Thread_KObject Thread-ID Port Description.");
355 header_required
= FALSE
;
358 if (newline_required
) {
361 newline_required
= TRUE
;
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
);
368 JSON_OBJECT_SET(json
, kobject
, "0x%08x", info
->th_kobject
);
369 JSON_OBJECT_SET(json
, tid
, "0x%llx", info
->th_id
);
372 if (info
->voucher_detail
!= NULL
) {
373 /* TODO: include voucher detail in JSON */
374 printf("%s\n", info
->voucher_detail
);
377 JSON_KEY(json
, exception_ports
);
378 JSON_ARRAY_BEGIN(json
);
380 /* print the thread exception ports also */
381 if (taskinfo
->threadExceptionInfos
!= NULL
)
384 struct exc_port_info
*excinfo
= &taskinfo
->threadExceptionInfos
[i
];
385 char behavior_string
[30];
386 char mask_string
[200];
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
);
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
;
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
));
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
);
406 printf(" 0x%08x 0x%03x <%s> %s " , excinfo
->ports
[i
], excinfo
->flavors
[i
], behavior_string
, mask_string
);
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
)) {
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
);
419 printf(" -> 0x%08x 0x%08x (%d) %s\n",
421 actual_sendinfo
.iin_object
,
422 recv_holder_taskinfo
->pid
,
423 recv_holder_taskinfo
->processName
);
427 fprintf(stderr
, "failed to find");
433 JSON_OBJECT_END(json
); // exception port
437 JSON_ARRAY_END(json
); // exception ports
438 JSON_OBJECT_END(json
); // thread
441 JSON_ARRAY_END(json
); // threads
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
;
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
];
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
)
469 *out_taskinfo
= &NOT_FOUND_TASK_INFO
;
470 struct k2n_table_node
*k2nnode
;
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
);
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
;
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
];
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
;