]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/darwintests/proc_info.c
11b042d0dd35ea196876f11aee91afffe0930ffe
[apple/xnu.git] / tools / tests / darwintests / proc_info.c
1 #include <darwintest.h>
2 #include <inttypes.h>
3 #include <limits.h>
4 #include <os/assumes.h>
5 #include <os/overflow.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/sysctl.h>
10 #include <System/sys/kdebug.h>
11 #include <unistd.h>
12
13 #define PRIVATE
14 #include <sys/proc_info.h>
15 #include <sys/event.h>
16 #include <libproc.h>
17 #undef PRIVATE
18
19 T_GLOBAL_META(T_META_NAMESPACE("xnu.all"));
20
21 #pragma mark proc_list_uptrs
22
23 #define NUPTRS 4
24 static uint64_t uptrs[NUPTRS] = {
25 0x1122334455667788ULL,
26 0x99aabbccddeeff00ULL,
27 0xaabbaaddccaaffeeULL,
28 0xcc000011ccaa7755ULL
29 };
30
31 static const char *uptr_names[NUPTRS];
32
33 static void
34 print_uptrs(int argc, char * const *argv)
35 {
36 for (int i = 0; i < argc; i++) {
37 char *end;
38 unsigned long pid = strtoul(argv[i], &end, 0);
39 if (pid > INT_MAX) {
40 printf("error: pid '%lu' would overflow an integer\n", pid);
41 }
42 if (end == argv[i]) {
43 printf("error: could not parse '%s' as a pid\n", argv[i]);
44 continue;
45 }
46 int uptrs_count = proc_list_uptrs((int)pid, NULL, 0);
47 if (uptrs_count == 0) {
48 printf("no uptrs for process %d\n", (int)pid);
49 return;
50 }
51
52 /* extra space */
53 unsigned int uptrs_len = (unsigned int)uptrs_count + 32;
54
55 uint64_t *uptrs_alloc = malloc(sizeof(uint64_t) * uptrs_len);
56 os_assert(uptrs_alloc != NULL);
57
58 uptrs_count = proc_list_uptrs((int)pid, uptrs_alloc,
59 (uint32_t)(sizeof(uint64_t) * uptrs_len));
60 printf("process %d has %d uptrs:\n", (int)pid, uptrs_count);
61 if (uptrs_count > (int)uptrs_len) {
62 uptrs_count = (int)uptrs_len;
63 }
64 for (int j = 0; j < uptrs_count; j++) {
65 printf("%#17" PRIx64 "\n", uptrs_alloc[j]);
66 }
67 }
68 }
69
70 T_DECL(proc_list_uptrs,
71 "the kernel should return any up-pointers it knows about",
72 T_META_ALL_VALID_ARCHS(YES))
73 {
74 if (argc > 0) {
75 print_uptrs(argc, argv);
76 T_SKIP("command line invocation of tool, not test");
77 }
78
79 unsigned int cur_uptr = 0;
80
81 int kq = kqueue();
82 T_QUIET; T_ASSERT_POSIX_SUCCESS(kq, "kqueue");
83
84 /*
85 * Should find uptrs on file-type knotes and generic knotes (two
86 * different search locations, internally).
87 */
88 struct kevent64_s events[2];
89 memset(events, 0, sizeof(events));
90
91 uptr_names[cur_uptr] = "kqueue file-backed knote";
92 events[0].filter = EVFILT_WRITE;
93 events[0].ident = STDOUT_FILENO;
94 events[0].flags = EV_ADD;
95 events[0].udata = uptrs[cur_uptr++];
96
97 uptr_names[cur_uptr] = "kqueue non-file-backed knote";
98 events[1].filter = EVFILT_USER;
99 events[1].ident = 1;
100 events[1].flags = EV_ADD;
101 events[1].udata = uptrs[cur_uptr++];
102
103 int kev_err = kevent64(kq, events, sizeof(events) / sizeof(events[0]), NULL,
104 0, KEVENT_FLAG_IMMEDIATE, NULL);
105 T_ASSERT_POSIX_SUCCESS(kev_err, "register events with kevent64");
106
107 /*
108 * Should find uptrs both on a kevent_id kqueue and in a workloop
109 * kqueue's knote's udata field.
110 */
111 uptr_names[cur_uptr] = "dynamic kqueue non-file-backed knote";
112 struct kevent_qos_s events_id[] = {{
113 .filter = EVFILT_USER,
114 .ident = 1,
115 .flags = EV_ADD,
116 .udata = uptrs[cur_uptr++]
117 }};
118
119 uptr_names[cur_uptr] = "dynamic kqueue ID";
120 kev_err = kevent_id(uptrs[cur_uptr++], events_id, 1, NULL, 0, NULL, NULL,
121 KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_IMMEDIATE);
122 T_ASSERT_POSIX_SUCCESS(kev_err, "register event with kevent_id");
123
124 errno = 0;
125 int uptrs_count = proc_list_uptrs(getpid(), NULL, 0);
126 T_QUIET; T_ASSERT_POSIX_SUCCESS(uptrs_count, "proc_list_uptrs");
127 T_QUIET; T_EXPECT_EQ(uptrs_count, NUPTRS,
128 "should see correct number of up-pointers");
129
130 uint64_t uptrs_obs[NUPTRS] = { 0 };
131 uptrs_count = proc_list_uptrs(getpid(), uptrs_obs, sizeof(uptrs_obs));
132 T_QUIET; T_ASSERT_POSIX_SUCCESS(uptrs_count, "proc_list_uptrs");
133
134 for (int i = 0; i < uptrs_count; i++) {
135 int found = -1;
136 for (int j = 0; j < NUPTRS; j++) {
137 if (uptrs_obs[i] == uptrs[j]) {
138 found = j;
139 goto next;
140 }
141 }
142 T_FAIL("unexpected up-pointer found: %#" PRIx64, uptrs_obs[i]);
143 next:;
144 if (found != -1) {
145 T_PASS("found up-pointer for %s", uptr_names[found]);
146 }
147 }
148 }
149
150 #pragma mark dynamic kqueue info
151
152 #define EXPECTED_ID UINT64_C(0x1122334455667788)
153 #define EXPECTED_UDATA UINT64_C(0x99aabbccddeeff00)
154 #ifndef KQ_WORKLOOP
155 #define KQ_WORKLOOP 0x80
156 #endif
157
158 static void
159 setup_kevent_id(kqueue_id_t id)
160 {
161 struct kevent_qos_s events_id[] = {{
162 .filter = EVFILT_USER,
163 .ident = 1,
164 .flags = EV_ADD,
165 .udata = EXPECTED_UDATA
166 }};
167
168 int err = kevent_id(id, events_id, 1, NULL, 0, NULL, NULL,
169 KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_IMMEDIATE);
170 T_ASSERT_POSIX_SUCCESS(err, "register event with kevent_id");
171 }
172
173 static kqueue_id_t *
174 list_kqids(pid_t pid, int *nkqids_out)
175 {
176 int kqids_len = 256;
177 int nkqids;
178 kqueue_id_t *kqids = NULL;
179 uint32_t kqids_size;
180
181 retry:
182 if (os_mul_overflow(sizeof(kqueue_id_t), kqids_len, &kqids_size)) {
183 T_QUIET; T_ASSERT_GT(kqids_len, PROC_PIDDYNKQUEUES_MAX, NULL);
184 kqids_len = PROC_PIDDYNKQUEUES_MAX;
185 goto retry;
186 }
187 if (!kqids) {
188 kqids = malloc(kqids_size);
189 T_QUIET; T_ASSERT_NOTNULL(kqids, "malloc(%" PRIu32 ")", kqids_size);
190 }
191
192 nkqids = proc_list_dynkqueueids(pid, kqids, kqids_size);
193 if (nkqids > kqids_len && kqids_len < PROC_PIDDYNKQUEUES_MAX) {
194 kqids_len *= 2;
195 if (kqids_len > PROC_PIDDYNKQUEUES_MAX) {
196 kqids_len = PROC_PIDDYNKQUEUES_MAX;
197 }
198 free(kqids);
199 kqids = NULL;
200 goto retry;
201 }
202
203 *nkqids_out = nkqids;
204 return kqids;
205 }
206
207 T_DECL(list_dynamic_kqueues,
208 "the kernel should list IDs of dynamic kqueues",
209 T_META_ALL_VALID_ARCHS(true))
210 {
211 int nkqids;
212 bool found = false;
213
214 setup_kevent_id(EXPECTED_ID);
215 kqueue_id_t *kqids = list_kqids(getpid(), &nkqids);
216 T_ASSERT_GE(nkqids, 1, "at least one dynamic kqueue is listed");
217 for (int i = 0; i < nkqids; i++) {
218 if (kqids[i] == EXPECTED_ID) {
219 found = true;
220 T_PASS("found expected dynamic kqueue ID");
221 } else {
222 T_LOG("found another dynamic kqueue with ID %#" PRIx64, kqids[i]);
223 }
224 }
225
226 if (!found) {
227 T_FAIL("could not find dynamic ID of kqueue created");
228 }
229
230 free(kqids);
231 }
232
233 T_DECL(dynamic_kqueue_basic_info,
234 "the kernel should report valid basic dynamic kqueue info",
235 T_META_ALL_VALID_ARCHS(true))
236 {
237 struct kqueue_info kqinfo;
238 int ret;
239
240 setup_kevent_id(EXPECTED_ID);
241 ret = proc_piddynkqueueinfo(getpid(), PROC_PIDDYNKQUEUE_INFO, EXPECTED_ID,
242 &kqinfo, sizeof(kqinfo));
243 T_ASSERT_POSIX_SUCCESS(ret,
244 "proc_piddynkqueueinfo(... PROC_PIDDYNKQUEUE_INFO ...)");
245 T_QUIET; T_ASSERT_GE(ret, (int)sizeof(kqinfo),
246 "PROC_PIDDYNKQUEUE_INFO should return the right size");
247
248 T_EXPECT_NE(kqinfo.kq_state & KQ_WORKLOOP, 0U,
249 "kqueue info should be for a workloop kqueue");
250 T_EXPECT_EQ(kqinfo.kq_stat.vst_ino, EXPECTED_ID,
251 "inode field should be the kqueue's ID");
252 }
253
254 T_DECL(dynamic_kqueue_extended_info,
255 "the kernel should report valid extended dynamic kqueue info",
256 T_META_ALL_VALID_ARCHS(true))
257 {
258 struct kevent_extinfo kqextinfo[1];
259 int ret;
260
261 setup_kevent_id(EXPECTED_ID);
262 ret = proc_piddynkqueueinfo(getpid(), PROC_PIDDYNKQUEUE_EXTINFO,
263 EXPECTED_ID, kqextinfo, sizeof(kqextinfo));
264 T_ASSERT_POSIX_SUCCESS(ret,
265 "proc_piddynkqueueinfo(... PROC_PIDDYNKQUEUE_EXTINFO ...)");
266 T_QUIET; T_ASSERT_EQ(ret, 1,
267 "PROC_PIDDYNKQUEUE_EXTINFO should return a single knote");
268
269 T_EXPECT_EQ(kqextinfo[0].kqext_kev.ident, 1ULL,
270 "kevent identifier matches what was configured");
271 T_EXPECT_EQ(kqextinfo[0].kqext_kev.filter, (short)EVFILT_USER,
272 "kevent filter matches what was configured");
273 T_EXPECT_EQ(kqextinfo[0].kqext_kev.udata, EXPECTED_UDATA,
274 "kevent udata matches what was configured");
275 }
276
277 #pragma mark proc_listpids
278
279 T_DECL(list_kdebug_pids,
280 "the kernel should report processes that are filtered by kdebug",
281 T_META_ASROOT(YES))
282 {
283 int mib[4] = { CTL_KERN, KERN_KDEBUG };
284 int npids;
285 int pids[1];
286 int ret;
287 kd_regtype reg = {};
288 size_t regsize = sizeof(reg);
289
290 mib[2] = KERN_KDREMOVE;
291 ret = sysctl(mib, 3, NULL, NULL, NULL, 0);
292 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "KERN_KDREMOVE sysctl");
293
294 mib[2] = KERN_KDSETBUF; mib[3] = 100000;
295 ret = sysctl(mib, 4, NULL, NULL, NULL, 0);
296 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "KERN_KDSETBUF sysctl");
297
298 mib[2] = KERN_KDSETUP;
299 ret = sysctl(mib, 3, NULL, NULL, NULL, 0);
300 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "KERN_KDSETUP sysctl");
301
302 npids = proc_listpids(PROC_KDBG_ONLY, 0, pids, sizeof(pids));
303 T_EXPECT_EQ(npids, 0, "no processes should be filtered initially");
304
305 reg.type = KDBG_TYPENONE;
306 reg.value1 = getpid();
307 reg.value2 = 1; /* set the pid in the filter */
308 mib[2] = KERN_KDPIDTR;
309 ret = sysctl(mib, 3, &reg, &regsize, NULL, 0);
310 T_ASSERT_POSIX_SUCCESS(ret,
311 "KERN_KDPIDTR sysctl to set a pid in the filter");
312
313 npids = proc_listpids(PROC_KDBG_ONLY, 0, pids, sizeof(pids));
314 npids /= 4;
315 T_EXPECT_EQ(npids, 1, "a process should be filtered");
316 T_EXPECT_EQ(pids[0], getpid(),
317 "process filtered should be the one that was set");
318
319 mib[2] = KERN_KDREMOVE;
320 ret = sysctl(mib, 3, NULL, NULL, NULL, 0);
321 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "KERN_KDREMOVE sysctl");
322 }