]>
Commit | Line | Data |
---|---|---|
d9a64523 A |
1 | /* |
2 | * proc_info_list_kthreads | |
3 | * | |
4 | * list 64 bit thread ids of kernel_task | |
5 | */ | |
6 | ||
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
9 | #include <assert.h> | |
10 | #include <err.h> | |
11 | ||
12 | #include <libproc.h> | |
13 | #include <strings.h> | |
14 | #include <darwintest.h> | |
15 | #include <TargetConditionals.h> | |
16 | ||
cb323159 A |
17 | T_GLOBAL_META(T_META_RUN_CONCURRENTLY(true)); |
18 | ||
d9a64523 A |
19 | #define MAX_TRIES 20 |
20 | #define EXTRA_THREADS 15 | |
21 | ||
22 | #if TARGET_OS_OSX | |
23 | T_DECL(proc_info_list_kthreads, | |
0a7de745 A |
24 | "Test to verify PROC_PIDLISTTHREADIDS returns kernel thread IDs for pid 0", |
25 | T_META_ASROOT(true), | |
26 | T_META_CHECK_LEAKS(false)) | |
d9a64523 A |
27 | #else |
28 | T_DECL(proc_info_list_kthreads, | |
0a7de745 A |
29 | "Test to verify PROC_PIDLISTTHREADIDS returns kernel thread IDs for pid 0", |
30 | T_META_ASROOT(false), | |
31 | T_META_CHECK_LEAKS(false)) | |
d9a64523 A |
32 | #endif /* TARGET_OS_OSX */ |
33 | { | |
34 | int buf_used = 0; | |
35 | ||
36 | int thread_count = 0; | |
37 | uint64_t *thread_list = NULL; | |
38 | ||
39 | /* | |
40 | * To use PROC_PIDLISTTHREADIDS, we must pass a buffer of uint64_t's for each thread ID. | |
41 | * However, there is a TOCTOU race between asking for the thread count | |
42 | * and asking for the array of identifiers. | |
43 | * | |
44 | * Because the process could have allocated more threads since last we asked | |
45 | * how many threads there are, we instead pass an extra slot in the array, | |
46 | * and try again if it used that slot. | |
47 | */ | |
48 | ||
49 | int attempt = 1; | |
50 | while (!thread_count && (attempt < MAX_TRIES)) { | |
51 | struct proc_taskinfo ti; | |
52 | ||
53 | buf_used = proc_pidinfo(0, PROC_PIDTASKINFO, 0, &ti, sizeof(ti)); | |
54 | ||
55 | T_QUIET; T_WITH_ERRNO; T_ASSERT_GT(buf_used, 0, "proc_pidinfo(PROC_PIDTASKINFO) returned a value > 0"); | |
56 | T_QUIET; T_ASSERT_EQ(buf_used, (int)sizeof(ti), "proc_pidinfo(PROC_PIDTASKINFO) returned size %d == %lu", buf_used, sizeof(ti)); | |
57 | ||
58 | T_LOG("The kernel says it has %d threads", ti.pti_threadnum); | |
59 | ||
60 | int expected_size = ti.pti_threadnum * (int)sizeof(uint64_t); | |
61 | /* tack on five extra to detect newly allocated threads */ | |
0a7de745 | 62 | int allocated_size = expected_size + EXTRA_THREADS * (int)sizeof(uint64_t); |
d9a64523 A |
63 | uint64_t *thread_list_tmp = malloc((size_t)allocated_size); |
64 | T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(thread_list_tmp, "malloc(size = %d) failed", allocated_size); | |
65 | ||
66 | buf_used = proc_pidinfo(0, PROC_PIDLISTTHREADIDS, 0, thread_list_tmp, (int)allocated_size); | |
67 | T_LOG("proc_pidinfo(PROC_PIDLISTTHREADIDS) buf_used = %d, expected_size = %d", buf_used, expected_size); | |
68 | ||
69 | if (buf_used == 0) { | |
70 | T_WITH_ERRNO; T_ASSERT_FAIL("proc_pidinfo(PROC_PIDLISTTHREADIDS) failed"); | |
71 | } | |
72 | if (buf_used == expected_size) { | |
73 | /* success, we found the expected number of threads */ | |
74 | thread_list = thread_list_tmp; | |
75 | thread_count = expected_size / (int)sizeof(uint64_t); | |
76 | } else if (buf_used < expected_size) { | |
77 | /* there were fewer threads than we expected, fix up the allocation */ | |
78 | thread_list = realloc(thread_list_tmp, (size_t)buf_used); | |
79 | thread_count = buf_used / (int)sizeof(uint64_t); | |
80 | T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(thread_list, "realloc(size = %d) failed", buf_used); | |
81 | } else if (buf_used > expected_size) { | |
82 | if (buf_used < allocated_size) { | |
83 | thread_list = realloc(thread_list_tmp, (size_t)buf_used); | |
84 | thread_count = buf_used / (int)sizeof(uint64_t); | |
85 | T_QUIET; T_WITH_ERRNO; T_ASSERT_NOTNULL(thread_list, "realloc(size = %d) failed", buf_used); | |
86 | } else { | |
87 | /* | |
88 | * it used all the extra slots, meaning there are more | |
89 | * threads than we thought, try again! | |
90 | */ | |
91 | T_LOG("expected %d threads, but saw an extra thread: %d", | |
0a7de745 | 92 | expected_size / (int)sizeof(uint64_t), buf_used / (int)sizeof(uint64_t)); |
d9a64523 A |
93 | free(thread_list_tmp); |
94 | } | |
95 | } | |
96 | attempt++; | |
97 | } | |
98 | T_QUIET; T_ASSERT_LE(attempt, MAX_TRIES, "attempt <= MAX_TRIES"); | |
99 | T_QUIET; T_ASSERT_NOTNULL(thread_list, "thread_list != NULL"); | |
100 | T_QUIET; T_ASSERT_GT(thread_count, 0, "thread_count > 0"); | |
101 | ||
102 | struct proc_threadinfo pthinfo_64; | |
0a7de745 | 103 | for (int i = 0; i < thread_count; i++) { |
d9a64523 A |
104 | bzero(&pthinfo_64, sizeof(struct proc_threadinfo)); |
105 | int retval = proc_pidinfo(0, PROC_PIDTHREADID64INFO, thread_list[i], | |
0a7de745 | 106 | (void *)&pthinfo_64, (uint32_t)sizeof(pthinfo_64)); |
d9a64523 A |
107 | T_QUIET; T_WITH_ERRNO; T_EXPECT_GT(retval, 0, "proc_pidinfo(PROC_PIDTASKINFO) returned %d", retval); |
108 | T_QUIET; T_EXPECT_EQ(retval, (int)sizeof(pthinfo_64), "proc_pidinfo(PROC_PIDTASKINFO) returned size %d == %lu", | |
0a7de745 | 109 | retval, sizeof(pthinfo_64)); |
d9a64523 A |
110 | } |
111 | } |