]> git.saurik.com Git - apple/xnu.git/blob - tools/tests/darwintests/proc_info.c
xnu-4570.41.2.tar.gz
[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 uint64_t up_overflow[2] = {0};
150 uptrs_count = proc_list_uptrs(getpid(), up_overflow, sizeof(uint64_t)+1);
151 T_ASSERT_EQ(up_overflow[1], 0 , "overflow check");
152 }
153
154 #pragma mark dynamic kqueue info
155
156 #define EXPECTED_ID UINT64_C(0x1122334455667788)
157 #define EXPECTED_UDATA UINT64_C(0x99aabbccddeeff00)
158 #ifndef KQ_WORKLOOP
159 #define KQ_WORKLOOP 0x80
160 #endif
161
162 static void
163 setup_kevent_id(kqueue_id_t id)
164 {
165 struct kevent_qos_s events_id[] = {{
166 .filter = EVFILT_USER,
167 .ident = 1,
168 .flags = EV_ADD,
169 .udata = EXPECTED_UDATA
170 }};
171
172 int err = kevent_id(id, events_id, 1, NULL, 0, NULL, NULL,
173 KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_IMMEDIATE);
174 T_ASSERT_POSIX_SUCCESS(err, "register event with kevent_id");
175 }
176
177 static kqueue_id_t *
178 list_kqids(pid_t pid, int *nkqids_out)
179 {
180 int kqids_len = 256;
181 int nkqids;
182 kqueue_id_t *kqids = NULL;
183 uint32_t kqids_size;
184
185 retry:
186 if (os_mul_overflow(sizeof(kqueue_id_t), kqids_len, &kqids_size)) {
187 T_QUIET; T_ASSERT_GT(kqids_len, PROC_PIDDYNKQUEUES_MAX, NULL);
188 kqids_len = PROC_PIDDYNKQUEUES_MAX;
189 goto retry;
190 }
191 if (!kqids) {
192 kqids = malloc(kqids_size);
193 T_QUIET; T_ASSERT_NOTNULL(kqids, "malloc(%" PRIu32 ")", kqids_size);
194 }
195
196 nkqids = proc_list_dynkqueueids(pid, kqids, kqids_size);
197 if (nkqids > kqids_len && kqids_len < PROC_PIDDYNKQUEUES_MAX) {
198 kqids_len *= 2;
199 if (kqids_len > PROC_PIDDYNKQUEUES_MAX) {
200 kqids_len = PROC_PIDDYNKQUEUES_MAX;
201 }
202 free(kqids);
203 kqids = NULL;
204 goto retry;
205 }
206
207 *nkqids_out = nkqids;
208 return kqids;
209 }
210
211 T_DECL(list_dynamic_kqueues,
212 "the kernel should list IDs of dynamic kqueues",
213 T_META_ALL_VALID_ARCHS(true))
214 {
215 int nkqids;
216 bool found = false;
217
218 setup_kevent_id(EXPECTED_ID);
219 kqueue_id_t *kqids = list_kqids(getpid(), &nkqids);
220 T_ASSERT_GE(nkqids, 1, "at least one dynamic kqueue is listed");
221 for (int i = 0; i < nkqids; i++) {
222 if (kqids[i] == EXPECTED_ID) {
223 found = true;
224 T_PASS("found expected dynamic kqueue ID");
225 } else {
226 T_LOG("found another dynamic kqueue with ID %#" PRIx64, kqids[i]);
227 }
228 }
229
230 if (!found) {
231 T_FAIL("could not find dynamic ID of kqueue created");
232 }
233
234 free(kqids);
235 }
236
237 T_DECL(dynamic_kqueue_basic_info,
238 "the kernel should report valid basic dynamic kqueue info",
239 T_META_ALL_VALID_ARCHS(true))
240 {
241 struct kqueue_info kqinfo;
242 int ret;
243
244 setup_kevent_id(EXPECTED_ID);
245 ret = proc_piddynkqueueinfo(getpid(), PROC_PIDDYNKQUEUE_INFO, EXPECTED_ID,
246 &kqinfo, sizeof(kqinfo));
247 T_ASSERT_POSIX_SUCCESS(ret,
248 "proc_piddynkqueueinfo(... PROC_PIDDYNKQUEUE_INFO ...)");
249 T_QUIET; T_ASSERT_GE(ret, (int)sizeof(kqinfo),
250 "PROC_PIDDYNKQUEUE_INFO should return the right size");
251
252 T_EXPECT_NE(kqinfo.kq_state & KQ_WORKLOOP, 0U,
253 "kqueue info should be for a workloop kqueue");
254 T_EXPECT_EQ(kqinfo.kq_stat.vst_ino, EXPECTED_ID,
255 "inode field should be the kqueue's ID");
256 }
257
258 T_DECL(dynamic_kqueue_extended_info,
259 "the kernel should report valid extended dynamic kqueue info",
260 T_META_ALL_VALID_ARCHS(true))
261 {
262 struct kevent_extinfo kqextinfo[1];
263 int ret;
264
265 setup_kevent_id(EXPECTED_ID);
266 ret = proc_piddynkqueueinfo(getpid(), PROC_PIDDYNKQUEUE_EXTINFO,
267 EXPECTED_ID, kqextinfo, sizeof(kqextinfo));
268 T_ASSERT_POSIX_SUCCESS(ret,
269 "proc_piddynkqueueinfo(... PROC_PIDDYNKQUEUE_EXTINFO ...)");
270 T_QUIET; T_ASSERT_EQ(ret, 1,
271 "PROC_PIDDYNKQUEUE_EXTINFO should return a single knote");
272
273 T_EXPECT_EQ(kqextinfo[0].kqext_kev.ident, 1ULL,
274 "kevent identifier matches what was configured");
275 T_EXPECT_EQ(kqextinfo[0].kqext_kev.filter, (short)EVFILT_USER,
276 "kevent filter matches what was configured");
277 T_EXPECT_EQ(kqextinfo[0].kqext_kev.udata, EXPECTED_UDATA,
278 "kevent udata matches what was configured");
279 }
280
281 #pragma mark proc_listpids
282
283 T_DECL(list_kdebug_pids,
284 "the kernel should report processes that are filtered by kdebug",
285 T_META_ASROOT(YES))
286 {
287 int mib[4] = { CTL_KERN, KERN_KDEBUG };
288 int npids;
289 int pids[1];
290 int ret;
291 kd_regtype reg = {};
292 size_t regsize = sizeof(reg);
293
294 mib[2] = KERN_KDREMOVE;
295 ret = sysctl(mib, 3, NULL, NULL, NULL, 0);
296 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "KERN_KDREMOVE sysctl");
297
298 mib[2] = KERN_KDSETBUF; mib[3] = 100000;
299 ret = sysctl(mib, 4, NULL, NULL, NULL, 0);
300 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "KERN_KDSETBUF sysctl");
301
302 mib[2] = KERN_KDSETUP;
303 ret = sysctl(mib, 3, NULL, NULL, NULL, 0);
304 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "KERN_KDSETUP sysctl");
305
306 npids = proc_listpids(PROC_KDBG_ONLY, 0, pids, sizeof(pids));
307 T_EXPECT_EQ(npids, 0, "no processes should be filtered initially");
308
309 reg.type = KDBG_TYPENONE;
310 reg.value1 = getpid();
311 reg.value2 = 1; /* set the pid in the filter */
312 mib[2] = KERN_KDPIDTR;
313 ret = sysctl(mib, 3, &reg, &regsize, NULL, 0);
314 T_ASSERT_POSIX_SUCCESS(ret,
315 "KERN_KDPIDTR sysctl to set a pid in the filter");
316
317 npids = proc_listpids(PROC_KDBG_ONLY, 0, pids, sizeof(pids));
318 npids /= 4;
319 T_EXPECT_EQ(npids, 1, "a process should be filtered");
320 T_EXPECT_EQ(pids[0], getpid(),
321 "process filtered should be the one that was set");
322
323 mib[2] = KERN_KDREMOVE;
324 ret = sysctl(mib, 3, NULL, NULL, NULL, 0);
325 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "KERN_KDREMOVE sysctl");
326 }