1 #include "os/internal.h"
2 #include "platform/introspection_private.h"
3 #include "introspection_internal.h"
5 #include <mach/mach_error.h>
6 #include <mach/mach_init.h>
7 #include <mach/mach_port.h>
8 #include <mach/mach_vm.h>
10 #include <mach/thread_act.h>
12 #include <sys/sysctl.h>
14 #include "libkern/OSAtomic.h"
16 // Returns the number of thread entries that can be stored in a task page.
18 _platform_threads_per_page(void)
20 // Subtract out the header storage.
21 return (int)(vm_page_size
/ sizeof(struct platform_thread_s
)) - 1;
24 // Returns the page-aligned base address for a given pthread structure address.
26 _platform_pthread_addr(mach_vm_address_t addr
) {
27 return trunc_page(addr
); // XXX approximation
30 // Returns the page-aligned size for a given pthread structure address.
32 _platform_pthread_size(mach_vm_address_t addr
) {
33 return vm_page_size
; // XXX approximation
37 _platform_thread_deallocate(platform_thread_t thread
)
40 if (MACH_PORT_VALID(thread
->act
)) {
41 mach_port_deallocate(mach_task_self(), thread
->act
);
42 thread
->act
= MACH_PORT_NULL
;
45 if (thread
->pthread_addr
!= 0) {
46 ret
= mach_vm_deallocate(mach_task_self(),
47 _platform_pthread_addr(thread
->pthread_addr
),
48 _platform_pthread_size(thread
->pthread_addr
));
49 thread
->pthread_addr
= 0;
55 _platform_task_deallocate(platform_task_t task
)
60 return KERN_INVALID_TASK
;
63 task_t port
= task
->metadata
.port
;
64 if (port
!= MACH_PORT_NULL
) {
65 mach_port_deallocate(mach_task_self(), port
);
68 platform_task_t ptr
= task
;
70 mach_vm_address_t addr
= (mach_vm_address_t
)ptr
;
72 // Deallocate threads.
73 int i
, start
= (ptr
== task
) ? 1 : 0; // Skip over meta data.
74 for (i
= start
; i
< _platform_threads_per_page() - start
; ++i
) {
75 _platform_thread_deallocate(&ptr
->threads
[i
]);
78 ptr
= ptr
->header
.next
;
79 ret
= mach_vm_deallocate(mach_task_self(), addr
, vm_page_size
);
85 extern int __sysctl(int *, unsigned int, void *, size_t *, void *, size_t);
88 _platform_task_query_64_bit(platform_task_t task
)
90 task_flags_info_data_t task_flags_info
;
91 mach_msg_type_number_t count
= TASK_FLAGS_INFO_COUNT
;
93 kern_return_t ret
= task_info(task
->metadata
.port
, TASK_FLAGS_INFO
, (task_info_t
) &task_flags_info
, &count
);
94 if (ret
== KERN_SUCCESS
) {
95 task
->metadata
.is_64_bit
= (task_flags_info
.flags
& TF_LP64
) ? true : false;
96 } else if (ret
== KERN_INVALID_ARGUMENT
) {
98 kern_return_t ret
= pid_for_task(task
->metadata
.port
, &pid
);
99 if (ret
!= KERN_SUCCESS
) return ret
;
101 struct kinfo_proc info
;
102 size_t size
= sizeof(info
);
103 int mib
[] = { CTL_KERN
, KERN_PROC
, KERN_PROC_PID
, pid
};
104 unsigned int len
= sizeof(mib
) / sizeof(*mib
);
105 int res
= __sysctl(mib
, len
, &info
, &size
, NULL
, 0);
106 if (res
== 0 && size
>= sizeof(info
)) {
107 task
->metadata
.is_64_bit
= (info
.kp_proc
.p_flag
& P_LP64
) != 0;
115 platform_task_attach(platform_task_t
*out_task
, task_t port
)
119 // Test some invariants.
120 _Static_assert(sizeof(struct platform_task_header_s
) == 32, "");
121 _Static_assert(sizeof(struct platform_task_metadata_s
) == 32, "");
122 _Static_assert(sizeof(struct platform_thread_s
) == 32, "");
124 // Allocate storage for the returned task handle.
125 mach_vm_address_t addr
= 0;
126 mach_vm_size_t size
= vm_page_size
;
127 ret
= mach_vm_allocate(mach_task_self(), &addr
, size
, VM_FLAGS_ANYWHERE
);
128 if (ret
!= KERN_SUCCESS
) return ret
;
130 platform_task_t result
= (platform_task_t
)addr
;
132 // Keep a reference to the task port.
133 ret
= mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_SEND
, 1);
134 if (ret
!= KERN_SUCCESS
) {
135 _platform_task_deallocate(result
);
139 result
->header
.head
= result
;
140 result
->metadata
.port
= port
;
142 ret
= _platform_task_query_64_bit(result
);
143 if (ret
!= KERN_SUCCESS
) {
144 _platform_task_deallocate(result
);
154 platform_task_detach(platform_task_t task
)
157 ret
= _platform_task_deallocate(task
);
162 platform_task_is_64_bit(platform_task_t task
)
164 return task
->metadata
.is_64_bit
;
168 platform_task_suspend_threads(platform_task_t task
)
170 return KERN_NOT_SUPPORTED
;
174 platform_task_resume_threads(platform_task_t task
)
176 return KERN_NOT_SUPPORTED
;
180 platform_task_perform(platform_task_t task
,
181 mach_vm_address_t func_addr
,
182 mach_vm_address_t data_addr
)
184 return KERN_NOT_SUPPORTED
;
187 static platform_task_t
188 _platform_thread_get_task(platform_thread_t thread
)
190 platform_task_t task
= (platform_task_t
)trunc_page((uintptr_t)thread
);
191 platform_task_t head
= task
->header
.head
;
199 _platform_thread_map(platform_task_t task
,
200 platform_thread_t thread
,
201 mach_vm_address_t thread_handle
)
204 vm_prot_t cur_protection
, max_protection
;
206 vm_offset_t data
= 0;
207 mach_vm_size_t wordsize
= task
->metadata
.is_64_bit
? 8 : 4;
208 mach_msg_type_number_t size
;
209 ret
= mach_vm_read(_platform_thread_get_task(thread
)->metadata
.port
,
210 thread_handle
, // &TSD[0]
214 if (ret
!= KERN_SUCCESS
) return ret
;
216 mach_vm_address_t pthread_addr
= (uintptr_t)*(void **)data
; // deref TSD[0]
217 mach_vm_deallocate(mach_task_self(), data
, size
);
219 mach_vm_address_t src_addr
= _platform_pthread_addr(pthread_addr
);
220 mach_vm_offset_t offset
= pthread_addr
- src_addr
;
221 mach_vm_address_t dst_addr
= 0;
222 ret
= mach_vm_remap(mach_task_self(),
224 _platform_pthread_size(pthread_addr
),
227 _platform_thread_get_task(thread
)->metadata
.port
,
233 if (ret
== KERN_SUCCESS
) {
234 thread
->pthread_addr
= dst_addr
+ offset
;
240 // Add a mach thread to the task's thread array. Updates the existing entry
241 // with the same unique id if one exists, otherwise allocates a new entry.
242 // Consumes the reference to the thread act mach port.
244 _platform_task_add_mach_thread(platform_task_t task
, thread_act_t act
)
249 thread_identifier_info_data_t info
;
250 mach_msg_type_number_t info_count
= THREAD_IDENTIFIER_INFO_COUNT
;
251 ret
= thread_info(act
,
252 THREAD_IDENTIFIER_INFO
,
253 (thread_info_t
)&info
,
255 if (ret
!= KERN_SUCCESS
) return ret
;
257 // Anything older than the previous generation is a candidate for recycling.
258 uint32_t gen
= task
->metadata
.gen
- 1;
260 // Look for an existing slot with this unique ID or the first empty slot.
261 platform_thread_t empty
= NULL
;
262 platform_thread_t found
= NULL
;
263 platform_task_t last
, ptr
= task
;
265 int start
= (ptr
== task
) ? 1 : 0; // Skip over meta data.
266 for (i
= start
; i
< _platform_threads_per_page() - start
; ++i
) {
267 platform_thread_t thread
= &ptr
->threads
[i
];
269 thread
->refcnt
== 0 &&
270 (thread
->unique_id
== 0 || thread
->gen
< gen
)) {
271 empty
= &ptr
->threads
[i
];
272 } else if (task
->threads
[i
].unique_id
== info
.thread_id
) {
273 found
= &ptr
->threads
[i
];
278 } while (!found
&& (ptr
= ptr
->header
.next
));
281 mach_port_deallocate(mach_task_self(), found
->act
);
283 found
->gen
= task
->metadata
.gen
;
286 // Allocate new storage if necessary.
287 mach_vm_address_t addr
= 0;
288 mach_vm_size_t size
= vm_page_size
;
289 ret
= mach_vm_allocate(mach_task_self(),
293 if (ret
!= KERN_SUCCESS
) return ret
;
295 last
= last
->header
.next
= (platform_task_t
)addr
;
296 last
->header
.head
= task
;
298 empty
= &last
->threads
[0];
300 _platform_thread_deallocate(empty
);
303 empty
->act
= act
; // transfer ownership
304 empty
->gen
= task
->metadata
.gen
;
305 empty
->unique_id
= info
.thread_id
;
306 ret
= _platform_thread_map(task
, empty
, info
.thread_handle
);
313 platform_task_update_threads(platform_task_t task
)
316 thread_act_array_t array
;
317 mach_msg_type_number_t array_count
;
318 ret
= task_threads(task
->metadata
.port
, &array
, &array_count
);
319 if (ret
!= KERN_SUCCESS
) return ret
;
321 ++task
->metadata
.gen
;
322 task
->metadata
.cursor
= &task
->threads
[1]; // Reset iteration cursor.
325 for (i
= 0; i
< array_count
; ++i
) {
326 ret
= _platform_task_add_mach_thread(task
, array
[i
]);
329 mach_vm_size_t array_size
= array_count
* sizeof(*array
);
330 mach_vm_deallocate(mach_task_self(), (mach_vm_address_t
)array
, array_size
);
336 platform_task_copy_next_thread(platform_task_t task
)
339 platform_thread_t result
= NULL
;
340 platform_thread_t cursor
= task
->metadata
.cursor
;
342 if (cursor
== NULL
) {
347 uint32_t gen
= task
->metadata
.gen
;
348 platform_task_t ptr
= (platform_task_t
)trunc_page((uintptr_t)cursor
);
351 if (cursor
->gen
== gen
&& cursor
->unique_id
!= 0) {
357 if ((uintptr_t)cursor
>= ((uintptr_t)ptr
+ vm_page_size
)) {
358 ptr
= ptr
->header
.next
;
360 cursor
= &ptr
->threads
[0];
365 } while (!result
&& cursor
);
367 task
->metadata
.cursor
= cursor
;
370 OSAtomicIncrement32(&result
->refcnt
);
377 platform_thread_get_unique_id(platform_thread_t thread
)
379 return thread
->unique_id
;
383 platform_thread_release(platform_thread_t thread
)
385 int32_t refcnt
= OSAtomicDecrement32(&thread
->refcnt
);
387 __LIBPLATFORM_CLIENT_CRASH__(refcnt
, "Over-release of platform thread object");
392 platform_thread_abort_safely(platform_thread_t thread
)
395 ret
= thread_abort_safely(thread
->act
);
400 platform_thread_suspend(platform_thread_t thread
)
403 ret
= thread_suspend(thread
->act
);
408 platform_thread_resume(platform_thread_t thread
)
411 ret
= thread_resume(thread
->act
);
416 platform_thread_info(platform_thread_t thread
,
417 thread_flavor_t flavor
,
422 mach_msg_type_number_t count
= (int)*size
/ sizeof(natural_t
);
423 ret
= thread_info(thread
->act
, flavor
, info
, &count
);
424 *size
= count
* sizeof(natural_t
);
429 platform_thread_get_state(platform_thread_t thread
,
430 thread_state_flavor_t flavor
,
435 mach_msg_type_number_t count
= (int)*size
/ (int)sizeof(natural_t
);
436 ret
= thread_get_state(thread
->act
, flavor
, state
, &count
);
437 *size
= count
* sizeof(natural_t
);
442 platform_thread_set_state(platform_thread_t thread
,
443 thread_state_flavor_t flavor
,
448 mach_msg_type_number_t count
= (int)size
/ (int)sizeof(natural_t
);
449 ret
= thread_set_state(thread
->act
, flavor
, (thread_state_t
)state
, count
);
454 platform_thread_perform(platform_thread_t thread
,
455 mach_vm_address_t func_addr
,
456 mach_vm_address_t data_addr
)
458 return KERN_NOT_SUPPORTED
;
462 platform_thread_get_pthread(platform_thread_t thread
)
464 return (const void *) thread
->pthread_addr
;
469 // cc -DMAIN -I../../include/platform introspection.c
474 int main(int argc
, char *argv
[]) {
477 task_t port
= MACH_PORT_NULL
;
478 ret
= task_for_pid(mach_task_self(), getppid(), &port
);
479 if (ret
!= KERN_SUCCESS
) {
480 mach_error("task_for_pid", ret
);
484 platform_task_t task
= NULL
;
485 ret
= platform_task_attach(&task
, port
);
486 if (ret
!= KERN_SUCCESS
) {
487 mach_error("platform_task_attach", ret
);
491 printf("Task is %s.\n", platform_task_is_64_bit(task
) ? "64-bit" : "32-bit");
494 for (i
= 0; i
< 3; ++i
) {
495 ret
= platform_task_update_threads(task
);
496 if (ret
!= KERN_SUCCESS
) {
497 mach_error("platform_task_update_threads", ret
);
501 platform_thread_t thread
;
502 while ((thread
= platform_task_copy_next_thread(task
))) {
503 printf("thread = { .unique_id = 0x%llx, pthread_addr = 0x%llx }\n",
505 thread
->pthread_addr
);
506 printf("pthread = { .sig = %lx, .unique_id = 0x%llx }\n",
507 *(unsigned long *)thread
->pthread_addr
,
508 *(uint64_t *)((uintptr_t)thread
->pthread_addr
+ 32));
510 platform_thread_release(thread
);
516 ret
= platform_task_detach(task
);
517 if (ret
!= KERN_SUCCESS
) {
518 mach_error("platform_task_detach", ret
);