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 struct k2n_table_node
*k2n_table_lookup_next(struct k2n_table_node
*node
, natural_t kobject
) {
48 if (kobject
== node
->kobject
)
57 struct k2n_table_node
*k2n_table_lookup(struct k2n_table_node
**table
, natural_t kobject
) {
59 struct k2n_table_node
*node
;
61 hv
= k2n_hash(kobject
);
63 node
= table
[hv
& K2N_TABLE_MASK
];
65 return k2n_table_lookup_next(node
, kobject
);
68 static void k2n_table_enter(struct k2n_table_node
**table
, natural_t kobject
, ipc_info_name_t
*info_name
) {
70 struct k2n_table_node
*node
;
72 hv
= k2n_hash(kobject
);
74 node
= malloc(sizeof (struct k2n_table_node
));
77 node
->kobject
= kobject
;
78 node
->info_name
= info_name
;
80 node
->next
= table
[hv
& K2N_TABLE_MASK
];
81 table
[hv
& K2N_TABLE_MASK
] = node
;
86 static my_per_task_info_t NOT_FOUND_TASK_INFO
= {
90 .info
= {0,0,0,0,0,0},
97 .processName
= "Unknown",
101 .threadExceptionInfos
= NULL
104 char * get_task_name_by_pid(pid_t pid
);
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");
112 static my_per_task_info_t
*global_taskinfo
= NULL
;
113 static uint32_t global_taskcount
= 0;
115 my_per_task_info_t
* allocate_taskinfo_memory(uint32_t taskCount
)
117 my_per_task_info_t
* retval
= malloc(taskCount
* sizeof(my_per_task_info_t
));
119 bzero((void *)retval
, taskCount
* sizeof(my_per_task_info_t
));
121 global_taskcount
= taskCount
;
122 global_taskinfo
= retval
;
126 void deallocate_taskinfo_memory(my_per_task_info_t
*data
){
129 global_taskinfo
= NULL
;
130 global_taskcount
= 0;
134 kern_return_t
collect_per_task_info(my_per_task_info_t
*taskinfo
, task_t target_task
)
137 kern_return_t ret
= KERN_SUCCESS
;
138 unsigned int kotype
= 0;
139 vm_offset_t kobject
= (vm_offset_t
)0;
141 taskinfo
->task
= target_task
;
142 pid_for_task(target_task
, &taskinfo
->pid
);
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
);
146 if (ret
!= KERN_SUCCESS
) {
147 fprintf(stderr
, "task_get_exception_ports() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
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
);
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;
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
);
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
]);
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
));
180 if (excinfo
->count
!= 0) {
181 found_exception
= true;
184 taskinfo
->threadInfos
[i
].thread
= threadPorts
[i
];
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
;
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
, NULL
);
195 taskinfo
->threadInfos
[i
].voucher_detail
= strndup(detail
, VOUCHER_DETAIL_MAXLEN
);
198 mach_port_deallocate(mach_task_self(), th_voucher
);
201 mach_port_deallocate(mach_task_self(), threadPorts
[i
]);
202 threadPorts
[i
] = MACH_PORT_NULL
;
205 if (found_exception
== false) {
206 free(taskinfo
->threadExceptionInfos
);
207 taskinfo
->threadExceptionInfos
= NULL
;
212 vm_deallocate(mach_task_self(), threadPorts
, taskinfo
->threadCount
* sizeof(thread_act_t
));
215 ret
= mach_port_space_info(target_task
, &taskinfo
->info
, &taskinfo
->table
, &taskinfo
->tableCount
, &taskinfo
->tree
, &taskinfo
->treeCount
);
217 if (ret
!= KERN_SUCCESS
) {
218 fprintf(stderr
, "mach_port_space_info() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
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
]);
228 proc_pid_to_name(taskinfo
->pid
, taskinfo
->processName
);
230 ret
= mach_port_kernel_object(mach_task_self(), taskinfo
->task
, &kotype
, (unsigned *)&kobject
);
232 if (ret
== KERN_SUCCESS
&& kotype
== IKOT_TASK
) {
233 taskinfo
->task_kobject
= kobject
;
234 taskinfo
->valid
= TRUE
;
240 void get_exc_behavior_string(exception_behavior_t b
, char *out_string
, size_t len
)
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
);
250 case EXCEPTION_STATE
:
251 strncat(out_string
, " STATE", len
);
253 case EXCEPTION_STATE_IDENTITY
:
254 strncat(out_string
, " IDENTITY", len
);
257 strncat(out_string
, " UNKNOWN", len
);
261 void get_exc_mask_string(exception_mask_t m
, char *out_string
, size_t len
)
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
);
291 kern_return_t
print_task_exception_info(my_per_task_info_t
*taskinfo
, JSON_t json
)
294 char behavior_string
[30];
295 char mask_string
[200];
297 JSON_KEY(json
, exception_ports
);
298 JSON_ARRAY_BEGIN(json
);
300 boolean_t header_required
= TRUE
;
301 for (int i
= 0; i
< taskinfo
->exceptionInfo
.count
; i
++) {
302 if (taskinfo
->exceptionInfo
.ports
[i
] != MACH_PORT_NULL
) {
303 if (header_required
) {
305 printf(" exc_port flavor <behaviors> mask \n");
306 header_required
= FALSE
;
308 get_exc_behavior_string(taskinfo
->exceptionInfo
.behaviors
[i
], behavior_string
, sizeof(behavior_string
));
309 get_exc_mask_string(taskinfo
->exceptionInfo
.masks
[i
], mask_string
, 200);
311 JSON_OBJECT_BEGIN(json
);
312 JSON_OBJECT_SET(json
, port
, "0x%08x", taskinfo
->exceptionInfo
.ports
[i
]);
313 JSON_OBJECT_SET(json
, flavor
, "0x%03x", taskinfo
->exceptionInfo
.flavors
[i
]);
314 JSON_OBJECT_SET(json
, behavior
, "%s", behavior_string
);
315 JSON_OBJECT_SET(json
, mask
, "%s", mask_string
);
316 JSON_OBJECT_END(json
); // exception port
318 printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo
->exceptionInfo
.ports
[i
], taskinfo
->exceptionInfo
.flavors
[i
], behavior_string
, mask_string
);
323 JSON_ARRAY_END(json
); // exception ports
329 kern_return_t
print_task_threads_special_ports(my_per_task_info_t
*taskinfo
, JSON_t json
)
331 kern_return_t kret
= KERN_SUCCESS
;
332 mach_msg_type_number_t threadcount
= taskinfo
->threadCount
;
333 boolean_t header_required
= TRUE
;
334 boolean_t newline_required
= TRUE
;
335 struct my_per_thread_info
* info
= NULL
;
337 JSON_KEY(json
, threads
);
338 JSON_ARRAY_BEGIN(json
);
340 for (int i
= 0; i
< threadcount
; i
++) {
341 JSON_OBJECT_BEGIN(json
);
343 info
= &taskinfo
->threadInfos
[i
];
344 if (header_required
) {
345 printf("Thread_KObject Thread-ID Port Description.");
346 header_required
= FALSE
;
349 if (newline_required
) {
352 newline_required
= TRUE
;
354 if (info
->th_kobject
!= 0) {
355 /* TODO: Should print tid and stuff */
356 printf("0x%08x ", info
->th_kobject
);
357 printf("0x%llx ", info
->th_id
);
359 JSON_OBJECT_SET(json
, kobject
, "0x%08x", info
->th_kobject
);
360 JSON_OBJECT_SET(json
, tid
, "0x%llx", info
->th_id
);
363 if (info
->voucher_detail
!= NULL
) {
364 /* TODO: include voucher detail in JSON */
365 printf("%s\n", info
->voucher_detail
);
368 JSON_KEY(json
, exception_ports
);
369 JSON_ARRAY_BEGIN(json
);
371 /* print the thread exception ports also */
372 if (taskinfo
->threadExceptionInfos
!= NULL
)
375 struct exc_port_info
*excinfo
= &taskinfo
->threadExceptionInfos
[i
];
376 char behavior_string
[30];
377 char mask_string
[200];
379 if (excinfo
->count
> 0) {
380 boolean_t header_required
= TRUE
;
381 for (int i
= 0; i
< excinfo
->count
; i
++) {
382 JSON_OBJECT_BEGIN(json
);
384 if (excinfo
->ports
[i
] != MACH_PORT_NULL
) {
385 if (header_required
) {
386 printf("\n exc_port flavor <behaviors> mask -> name owner\n");
387 header_required
= FALSE
;
389 get_exc_behavior_string(excinfo
->behaviors
[i
], behavior_string
, sizeof(behavior_string
));
390 get_exc_mask_string(excinfo
->masks
[i
], mask_string
, sizeof(mask_string
));
392 JSON_OBJECT_SET(json
, port
, "0x%08x", excinfo
->ports
[i
]);
393 JSON_OBJECT_SET(json
, flavor
, "0x%03x", excinfo
->flavors
[i
]);
394 JSON_OBJECT_SET(json
, behavior
, "%s", behavior_string
);
395 JSON_OBJECT_SET(json
, mask
, "%s", mask_string
);
397 printf(" 0x%08x 0x%03x <%s> %s " , excinfo
->ports
[i
], excinfo
->flavors
[i
], behavior_string
, mask_string
);
399 ipc_info_name_t actual_sendinfo
;
400 if (KERN_SUCCESS
== get_ipc_info_from_lsmp_spaceinfo(excinfo
->ports
[i
], &actual_sendinfo
)) {
401 my_per_task_info_t
*recv_holder_taskinfo
;
402 mach_port_name_t recv_name
= MACH_PORT_NULL
;
403 if (KERN_SUCCESS
== get_taskinfo_of_receiver_by_send_right(&actual_sendinfo
, &recv_holder_taskinfo
, &recv_name
)) {
405 JSON_OBJECT_SET(json
, name
, "0x%08x", recv_name
);
406 JSON_OBJECT_SET(json
, ipc
-object
, "0x%08x", actual_sendinfo
.iin_object
);
407 JSON_OBJECT_SET(json
, pid
, %d
, recv_holder_taskinfo
->pid
);
408 JSON_OBJECT_SET(json
, process
, "%s", recv_holder_taskinfo
->processName
);
410 printf(" -> 0x%08x 0x%08x (%d) %s\n",
412 actual_sendinfo
.iin_object
,
413 recv_holder_taskinfo
->pid
,
414 recv_holder_taskinfo
->processName
);
418 fprintf(stderr
, "failed to find");
424 JSON_OBJECT_END(json
); // exception port
428 JSON_ARRAY_END(json
); // exception ports
429 JSON_OBJECT_END(json
); // thread
432 JSON_ARRAY_END(json
); // threads
437 char * get_task_name_by_pid(pid_t pid
) {
438 char * retval
= "Unknown";
439 for (int i
= 0; i
< global_taskcount
; i
++) {
440 if (pid
== global_taskinfo
[i
].pid
) {
441 return global_taskinfo
[i
].processName
;
447 my_per_task_info_t
* get_taskinfo_by_kobject(natural_t kobj
) {
448 my_per_task_info_t
*retval
= &NOT_FOUND_TASK_INFO
;
449 for (int j
= 0; j
< global_taskcount
; j
++) {
450 if (global_taskinfo
[j
].task_kobject
== kobj
) {
451 retval
= &global_taskinfo
[j
];
458 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
)
460 *out_taskinfo
= &NOT_FOUND_TASK_INFO
;
461 struct k2n_table_node
*k2nnode
;
463 for (int j
= 0; j
< global_taskcount
; j
++) {
464 if ((k2nnode
= k2n_table_lookup(global_taskinfo
[j
].k2ntable
, sendright
->iin_object
))) {
465 assert(k2nnode
->info_name
->iin_object
== sendright
->iin_object
);
467 if (k2nnode
->info_name
->iin_type
& MACH_PORT_TYPE_RECEIVE
) {
468 *out_taskinfo
= &global_taskinfo
[j
];
469 *out_recv_info
= k2nnode
->info_name
->iin_name
;
478 kern_return_t
get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name
, ipc_info_name_t
*out_sendright
){
479 kern_return_t retval
= KERN_FAILURE
;
480 bzero(out_sendright
, sizeof(ipc_info_name_t
));
481 my_per_task_info_t
*mytaskinfo
= NULL
;
482 for (int i
= global_taskcount
- 1; i
>= 0; i
--){
483 if (global_taskinfo
[i
].task
== mach_task_self()){
484 mytaskinfo
= &global_taskinfo
[i
];
489 for (int k
= 0; k
< mytaskinfo
->tableCount
; k
++) {
490 if (port_name
== mytaskinfo
->table
[k
].iin_name
){
491 bcopy(&mytaskinfo
->table
[k
], out_sendright
, sizeof(ipc_info_name_t
));
492 retval
= KERN_SUCCESS
;