2 * Copyright (c) 2002-20014 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>
33 static my_per_task_info_t NOT_FOUND_TASK_INFO
= {
37 .info
= {0,0,0,0,0,0},
43 .processName
= "Unknown",
47 .threadExceptionInfos
= NULL
50 char * get_task_name_by_pid(pid_t pid
);
52 static void proc_pid_to_name(int pid
, char *pname
){
53 if (0 == proc_name(pid
, pname
, PROC_NAME_LEN
)) {
54 strcpy(pname
, "Unknown Process");
58 static my_per_task_info_t
*global_taskinfo
= NULL
;
59 static uint32_t global_taskcount
= 0;
61 my_per_task_info_t
* allocate_taskinfo_memory(uint32_t taskCount
)
63 my_per_task_info_t
* retval
= malloc(taskCount
* sizeof(my_per_task_info_t
));
65 bzero((void *)retval
, taskCount
* sizeof(my_per_task_info_t
));
67 global_taskcount
= taskCount
;
68 global_taskinfo
= retval
;
72 void deallocate_taskinfo_memory(my_per_task_info_t
*data
){
75 global_taskinfo
= NULL
;
80 kern_return_t
collect_per_task_info(my_per_task_info_t
*taskinfo
, task_t target_task
)
82 kern_return_t ret
= KERN_SUCCESS
;
83 unsigned int kotype
= 0;
84 vm_offset_t kobject
= (vm_offset_t
)0;
86 taskinfo
->task
= target_task
;
87 pid_for_task(target_task
, &taskinfo
->pid
);
89 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
);
91 if (ret
!= KERN_SUCCESS
) {
92 fprintf(stderr
, "task_get_exception_ports() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
96 /* collect threads port as well */
97 taskinfo
->threadCount
= 0;
98 thread_act_array_t threadPorts
;
99 ret
= task_threads(taskinfo
->task
, &threadPorts
, &taskinfo
->threadCount
);
101 if (ret
!= KERN_SUCCESS
) {
102 fprintf(stderr
, "task_threads() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
103 taskinfo
->threadCount
= 0;
105 /* collect the thread information */
106 taskinfo
->threadInfos
= (struct my_per_thread_info
*)malloc(sizeof(struct my_per_thread_info
) * taskinfo
->threadCount
);
107 bzero(taskinfo
->threadInfos
, sizeof(struct my_per_thread_info
) * taskinfo
->threadCount
);
109 /* now collect exception ports for each of those threads as well */
110 taskinfo
->threadExceptionInfos
= (struct exc_port_info
*) malloc(sizeof(struct exc_port_info
) * taskinfo
->threadCount
);
111 boolean_t found_exception
= false;
112 for (int i
= 0; i
< taskinfo
->threadCount
; i
++) {
113 unsigned th_kobject
= 0;
114 unsigned th_kotype
= 0;
115 ipc_voucher_t th_voucher
= IPC_VOUCHER_NULL
;
116 thread_identifier_info_data_t th_info
;
117 mach_msg_type_number_t th_info_count
= THREAD_IDENTIFIER_INFO_COUNT
;
118 struct exc_port_info
*excinfo
= &(taskinfo
->threadExceptionInfos
[i
]);
120 ret
= thread_get_exception_ports(threadPorts
[i
], EXC_MASK_ALL
, excinfo
->masks
, &excinfo
->count
, excinfo
->ports
, excinfo
->behaviors
, excinfo
->flavors
);
121 if (ret
!= KERN_SUCCESS
){
122 fprintf(stderr
, "thread_get_exception_ports() failed: pid: %d thread: %d error %s\n", taskinfo
->pid
, threadPorts
[i
], mach_error_string(ret
));
125 if (excinfo
->count
!= 0) {
126 found_exception
= true;
129 taskinfo
->threadInfos
[i
].thread
= threadPorts
[i
];
131 if (KERN_SUCCESS
== mach_port_kernel_object(mach_task_self(), threadPorts
[i
], &th_kotype
, &th_kobject
)) {
132 taskinfo
->threadInfos
[i
].th_kobject
= th_kobject
;
133 if (KERN_SUCCESS
== thread_info(threadPorts
[i
], THREAD_IDENTIFIER_INFO
, (thread_info_t
)&th_info
, &th_info_count
)) {
134 taskinfo
->threadInfos
[i
].th_id
= th_info
.thread_id
;
138 if (KERN_SUCCESS
== thread_get_mach_voucher(threadPorts
[i
], 0, &th_voucher
) && th_voucher
!= IPC_VOUCHER_NULL
) {
139 char *detail
= copy_voucher_detail(mach_task_self(), th_voucher
);
140 taskinfo
->threadInfos
[i
].voucher_detail
= strndup(detail
, VOUCHER_DETAIL_MAXLEN
);
143 mach_port_deallocate(mach_task_self(), th_voucher
);
146 mach_port_deallocate(mach_task_self(), threadPorts
[i
]);
147 threadPorts
[i
] = MACH_PORT_NULL
;
150 if (found_exception
== false) {
151 free(taskinfo
->threadExceptionInfos
);
152 taskinfo
->threadExceptionInfos
= NULL
;
157 vm_deallocate(mach_task_self(), threadPorts
, taskinfo
->threadCount
* sizeof(thread_act_t
));
160 ret
= mach_port_space_info(target_task
, &taskinfo
->info
, &taskinfo
->table
, &taskinfo
->tableCount
, &taskinfo
->tree
, &taskinfo
->treeCount
);
162 if (ret
!= KERN_SUCCESS
) {
163 fprintf(stderr
, "mach_port_space_info() failed: pid:%d error: %s\n",taskinfo
->pid
, mach_error_string(ret
));
168 proc_pid_to_name(taskinfo
->pid
, taskinfo
->processName
);
170 ret
= mach_port_kernel_object(mach_task_self(), taskinfo
->task
, &kotype
, (unsigned *)&kobject
);
172 if (ret
== KERN_SUCCESS
&& kotype
== IKOT_TASK
) {
173 taskinfo
->task_kobject
= kobject
;
174 taskinfo
->valid
= TRUE
;
182 void get_exc_behavior_string(exception_behavior_t b
, char *out_string
, size_t len
)
186 if (b
& MACH_EXCEPTION_CODES
)
187 strncat(out_string
, "MACH +", len
);
188 switch (b
& ~MACH_EXCEPTION_CODES
) {
189 case EXCEPTION_DEFAULT
:
190 strncat(out_string
, " DEFAULT", len
);
192 case EXCEPTION_STATE
:
193 strncat(out_string
, " STATE", len
);
195 case EXCEPTION_STATE_IDENTITY
:
196 strncat(out_string
, " IDENTITY", len
);
199 strncat(out_string
, " UNKNOWN", len
);
203 void get_exc_mask_string(exception_mask_t m
, char *out_string
, size_t len
)
207 if (m
& (1<<EXC_BAD_ACCESS
))
208 strncat(out_string
, " BAD_ACCESS", len
);
209 if (m
& (1<<EXC_BAD_INSTRUCTION
))
210 strncat(out_string
," BAD_INSTRUCTION", len
);
211 if (m
& (1<<EXC_ARITHMETIC
))
212 strncat(out_string
," ARITHMETIC", len
);
213 if (m
& (1<<EXC_EMULATION
))
214 strncat(out_string
," EMULATION", len
);
215 if (m
& (1<<EXC_SOFTWARE
))
216 strncat(out_string
," SOFTWARE", len
);
217 if (m
& (1<<EXC_BREAKPOINT
))
218 strncat(out_string
," BREAKPOINT", len
);
219 if (m
& (1<<EXC_SYSCALL
))
220 strncat(out_string
," SYSCALL", len
);
221 if (m
& (1<<EXC_MACH_SYSCALL
))
222 strncat(out_string
," MACH_SYSCALL", len
);
223 if (m
& (1<<EXC_RPC_ALERT
))
224 strncat(out_string
," RPC_ALERT", len
);
225 if (m
& (1<<EXC_CRASH
))
226 strncat(out_string
," CRASH", len
);
227 if (m
& (1<<EXC_RESOURCE
))
228 strncat(out_string
," RESOURCE", len
);
229 if (m
& (1<<EXC_GUARD
))
230 strncat(out_string
," GUARD", len
);
234 kern_return_t
print_task_exception_info(my_per_task_info_t
*taskinfo
)
237 char behavior_string
[30];
238 char mask_string
[200];
240 boolean_t header_required
= TRUE
;
241 for (int i
= 0; i
< taskinfo
->exceptionInfo
.count
; i
++) {
242 if (taskinfo
->exceptionInfo
.ports
[i
] != MACH_PORT_NULL
) {
243 if (header_required
) {
245 printf(" exc_port flavor <behaviors> mask \n");
246 header_required
= FALSE
;
248 get_exc_behavior_string(taskinfo
->exceptionInfo
.behaviors
[i
], behavior_string
, sizeof(behavior_string
));
249 get_exc_mask_string(taskinfo
->exceptionInfo
.masks
[i
], mask_string
, 200);
250 printf(" 0x%08x 0x%03x <%s> %s \n" , taskinfo
->exceptionInfo
.ports
[i
], taskinfo
->exceptionInfo
.flavors
[i
], behavior_string
, mask_string
);
259 kern_return_t
print_task_threads_special_ports(my_per_task_info_t
*taskinfo
)
261 kern_return_t kret
= KERN_SUCCESS
;
262 mach_msg_type_number_t threadcount
= taskinfo
->threadCount
;
263 boolean_t header_required
= TRUE
;
264 boolean_t newline_required
= TRUE
;
265 struct my_per_thread_info
* info
= NULL
;
267 for (int i
= 0; i
< threadcount
; i
++) {
268 info
= &taskinfo
->threadInfos
[i
];
269 if (header_required
) {
270 printf("Thread_KObject Thread-ID Port Description.");
271 header_required
= FALSE
;
274 if (newline_required
) {
277 newline_required
= TRUE
;
279 if (info
->th_kobject
!= 0) {
280 /* TODO: Should print tid and stuff */
281 printf("0x%08x ", info
->th_kobject
);
282 printf("0x%llx ", info
->th_id
);
285 if (info
->voucher_detail
!= NULL
) {
286 printf("%s\n", info
->voucher_detail
);
289 /* print the thread exception ports also */
290 if (taskinfo
->threadExceptionInfos
!= NULL
)
293 struct exc_port_info
*excinfo
= &taskinfo
->threadExceptionInfos
[i
];
294 char behavior_string
[30];
295 char mask_string
[200];
297 if (excinfo
->count
> 0) {
298 boolean_t header_required
= TRUE
;
299 for (int i
= 0; i
< excinfo
->count
; i
++) {
300 if (excinfo
->ports
[i
] != MACH_PORT_NULL
) {
301 if (header_required
) {
302 printf("\n exc_port flavor <behaviors> mask -> name owner\n");
303 header_required
= FALSE
;
305 get_exc_behavior_string(excinfo
->behaviors
[i
], behavior_string
, sizeof(behavior_string
));
306 get_exc_mask_string(excinfo
->masks
[i
], mask_string
, sizeof(mask_string
));
307 printf(" 0x%08x 0x%03x <%s> %s " , excinfo
->ports
[i
], excinfo
->flavors
[i
], behavior_string
, mask_string
);
309 ipc_info_name_t actual_sendinfo
;
310 if (KERN_SUCCESS
== get_ipc_info_from_lsmp_spaceinfo(excinfo
->ports
[i
], &actual_sendinfo
)) {
311 my_per_task_info_t
*recv_holder_taskinfo
;
312 mach_port_name_t recv_name
= MACH_PORT_NULL
;
313 if (KERN_SUCCESS
== get_taskinfo_of_receiver_by_send_right(&actual_sendinfo
, &recv_holder_taskinfo
, &recv_name
)) {
314 printf(" -> 0x%08x 0x%08x (%d) %s\n",
316 actual_sendinfo
.iin_object
,
317 recv_holder_taskinfo
->pid
,
318 recv_holder_taskinfo
->processName
);
322 fprintf(stderr
, "failed to find");
339 char * get_task_name_by_pid(pid_t pid
) {
340 char * retval
= "Unknown";
341 for (int i
= 0; i
< global_taskcount
; i
++) {
342 if (pid
== global_taskinfo
[i
].pid
) {
343 return global_taskinfo
[i
].processName
;
349 my_per_task_info_t
* get_taskinfo_by_kobject(natural_t kobj
) {
350 my_per_task_info_t
*retval
= &NOT_FOUND_TASK_INFO
;
351 for (int j
= 0; j
< global_taskcount
; j
++) {
352 if (global_taskinfo
[j
].task_kobject
== kobj
) {
353 retval
= &global_taskinfo
[j
];
360 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
)
362 kern_return_t retval
= KERN_FAILURE
;
363 boolean_t found
= FALSE
;
364 *out_taskinfo
= &NOT_FOUND_TASK_INFO
;
366 for (int j
= 0; j
< global_taskcount
&& !found
; j
++) {
367 for (int k
= 0; k
< global_taskinfo
[j
].tableCount
&& !found
; k
++) {
368 if ((global_taskinfo
[j
].table
[k
].iin_type
& MACH_PORT_TYPE_RECEIVE
) &&
369 global_taskinfo
[j
].table
[k
].iin_object
== sendright
->iin_object
) {
370 *out_taskinfo
= &global_taskinfo
[j
];
371 *out_recv_info
= global_taskinfo
[j
].table
[k
].iin_name
;
373 retval
= KERN_SUCCESS
;
380 kern_return_t
get_ipc_info_from_lsmp_spaceinfo(mach_port_t port_name
, ipc_info_name_t
*out_sendright
){
381 kern_return_t retval
= KERN_FAILURE
;
382 bzero(out_sendright
, sizeof(ipc_info_name_t
));
383 my_per_task_info_t
*mytaskinfo
= NULL
;
384 for (int i
= global_taskcount
- 1; i
>= 0; i
--){
385 if (global_taskinfo
[i
].task
== mach_task_self()){
386 mytaskinfo
= &global_taskinfo
[i
];
391 for (int k
= 0; k
< mytaskinfo
->tableCount
; k
++) {
392 if (port_name
== mytaskinfo
->table
[k
].iin_name
){
393 bcopy(&mytaskinfo
->table
[k
], out_sendright
, sizeof(ipc_info_name_t
));
394 retval
= KERN_SUCCESS
;