]> git.saurik.com Git - apple/system_cmds.git/blob - lskq.tproj/lskq.c
system_cmds-805.200.21.tar.gz
[apple/system_cmds.git] / lskq.tproj / lskq.c
1 /*
2 * Copyright (c) 2015-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <inttypes.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <strings.h>
29 #include <assert.h>
30 #include <errno.h>
31
32 #include <sys/types.h>
33 #include <sys/event.h>
34 #include <sys/time.h>
35 #include <sys/proc_info.h>
36 #include <sys/param.h>
37 #include <pthread/pthread.h>
38 #include <mach/message.h>
39 #define PRIVATE
40 #include <libproc.h>
41 #undef PRIVATE
42 #include <os/assumes.h>
43 #include <os/overflow.h>
44
45 #include "common.h"
46
47 #define ARRAYLEN(x) (sizeof((x))/sizeof((x[0])))
48
49 /* command line options */
50 static int verbose;
51 static int all_pids;
52 static int ignore_empty;
53 static int raw;
54
55 static char *self = "lskq";
56
57 static inline const char *
58 filt_name(int16_t filt)
59 {
60 static char unkn_filt[32];
61 int idx = -filt;
62 if (idx >= 0 && idx < ARRAYLEN(filt_strs)) {
63 return filt_strs[idx];
64 } else {
65 snprintf(unkn_filt, sizeof(unkn_filt), "%i (?)", idx);
66 return unkn_filt;
67 }
68 }
69
70 static inline const char *
71 fdtype_str(uint32_t type)
72 {
73 static char unkn_fdtype[32];
74 if (type < ARRAYLEN(fdtype_strs)) {
75 return fdtype_strs[type];
76 } else {
77 snprintf(unkn_fdtype, sizeof(unkn_fdtype), "%i (?)", type);
78 return unkn_fdtype;
79 }
80 }
81
82 static char *
83 fflags_build(struct kevent_extinfo *info, char *str, int len)
84 {
85 unsigned ff = info->kqext_sfflags;
86
87 switch (info->kqext_kev.filter) {
88
89 case EVFILT_READ: {
90 snprintf(str, len, "%c ",
91 (ff & NOTE_LOWAT) ? 'l' : '-'
92 );
93 break;
94 }
95
96 case EVFILT_MACHPORT: {
97 snprintf(str, len, "%c ",
98 (ff & MACH_RCV_MSG) ? 'r' : '-'
99 );
100 break;
101 }
102
103 case EVFILT_VNODE: {
104 snprintf(str, len, "%c%c%c%c%c%c%c",
105 (ff & NOTE_DELETE) ? 'd' : '-',
106 (ff & NOTE_WRITE) ? 'w' : '-',
107 (ff & NOTE_EXTEND) ? 'e' : '-',
108 (ff & NOTE_ATTRIB) ? 'a' : '-',
109 (ff & NOTE_LINK) ? 'l' : '-',
110 (ff & NOTE_RENAME) ? 'r' : '-',
111 (ff & NOTE_REVOKE) ? 'v' : '-'
112 );
113 break;
114 }
115
116 case EVFILT_PROC: {
117 /* NOTE_REAP is deprecated, but we still want to show if it's used */
118 #pragma clang diagnostic push
119 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
120 snprintf(str, len, "%c%c%c%c%c%c%c",
121 (ff & NOTE_EXIT) ? 'x' : '-',
122 (ff & NOTE_EXITSTATUS) ? 't' : '-',
123 (ff & NOTE_EXIT_DETAIL)? 'd' : '-',
124 (ff & NOTE_FORK) ? 'f' : '-',
125 (ff & NOTE_EXEC) ? 'e' : '-',
126 (ff & NOTE_SIGNAL) ? 's' : '-',
127 (ff & NOTE_REAP) ? 'r' : '-'
128 );
129 break;
130 #pragma clang diagnostic pop
131 }
132
133 case EVFILT_TIMER: {
134 snprintf(str, len, "%c%c%c%c%c ",
135 (ff & NOTE_SECONDS) ? 's' :
136 (ff & NOTE_USECONDS) ? 'u' :
137 (ff & NOTE_NSECONDS) ? 'n' : '?',
138 (ff & NOTE_ABSOLUTE) ? 'a' : '-',
139 (ff & NOTE_CRITICAL) ? 'c' : '-',
140 (ff & NOTE_BACKGROUND) ? 'b' : '-',
141 (ff & NOTE_LEEWAY) ? 'l' : '-'
142 );
143 break;
144 }
145
146 case EVFILT_USER:
147 snprintf(str, len, "%c%c%c ",
148 (ff & NOTE_TRIGGER) ? 't' : '-',
149 (ff & NOTE_FFAND) ? 'a' : '-',
150 (ff & NOTE_FFOR) ? 'o' : '-'
151 );
152 break;
153
154 case EVFILT_WORKLOOP:
155 snprintf(str, len, "%c%c%c%c ",
156 (ff & NOTE_WL_THREAD_REQUEST) ? 't' :
157 (ff & NOTE_WL_SYNC_WAIT) ? 'w' : '-',
158 (ff & NOTE_WL_SYNC_WAKE) ? '!' : '-',
159 (ff & NOTE_WL_UPDATE_QOS) ? 'q' : '-',
160 (ff & NOTE_WL_UPDATE_OWNER) ? 'O' :
161 (ff & NOTE_WL_DISCOVER_OWNER) ? 'o' : '-'
162 );
163 break;
164
165 default:
166 snprintf(str, len, "");
167 break;
168 };
169
170 return str;
171 }
172
173
174 static inline int
175 filter_is_fd_type(int filter)
176 {
177 switch (filter) {
178 case EVFILT_VNODE ... EVFILT_READ:
179 case EVFILT_SOCK:
180 case EVFILT_NW_CHANNEL:
181 return 1;
182 default:
183 return 0;
184 }
185 }
186
187 static const char *
188 thread_qos_name(uint8_t th_qos)
189 {
190 switch (th_qos) {
191 case 0: return "--";
192 case 1: return "MT";
193 case 2: return "BG";
194 case 3: return "UT";
195 case 4: return "DF";
196 case 5: return "IN";
197 case 6: return "UI";
198 case 7: return "MG";
199 default: return "??";
200 }
201 }
202
203 /*
204 * find index of fd in a list of fdinfo of length nfds
205 */
206 static inline int
207 fd_list_getfd(struct proc_fdinfo *fds, int nfds, int fd)
208 {
209 int i;
210 for (i = 0; i < nfds; i++) {
211 if (fds[i].proc_fd == fd) {
212 return i;
213 }
214 }
215
216 return -1;
217 }
218
219 /*
220 * left truncate URL-form process names
221 */
222 static const char *
223 shorten_procname(const char *proc, int width)
224 {
225 if (strcasestr(proc, "com.") == proc) {
226 long len = strlen(proc);
227 if (len > width) {
228 return &proc[len - width];
229 } else {
230 return proc;
231 }
232 } else {
233 return proc;
234 }
235 }
236
237 /*
238 * stringify knote ident where possible (signals, processes)
239 */
240 static void
241 print_ident(uint64_t ident, int16_t filter, int width)
242 {
243 if (raw) {
244 printf("%#*llx ", width, ident);
245 return;
246 }
247
248 switch (filter) {
249
250 case EVFILT_SIGNAL:
251 case EVFILT_PROC: {
252 char str[128] = "";
253 char num[128];
254 char out[128];
255 int numlen = sprintf(num, "%llu", ident);
256 int strwidth = width - numlen - 1; // add room for a space
257
258 if (filter == EVFILT_SIGNAL) {
259 if (ident < ARRAYLEN(sig_strs)) {
260 snprintf(str, strwidth + 1, "%s", sig_strs[ident]);
261 }
262 } else {
263 /* FIXME: this should be cached */
264 struct proc_bsdinfo bsdinfo;
265 int ret = proc_pidinfo((int)ident, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof(bsdinfo));
266 if (ret == sizeof(bsdinfo)) {
267 char *procname = bsdinfo.pbi_name;
268 if (strlen(procname) == 0) {
269 procname = bsdinfo.pbi_comm;
270 }
271 snprintf(str, strwidth + 1, "%s", shorten_procname(procname, strwidth));
272 }
273 }
274
275 if (str[0] != '\0') {
276 snprintf(out, width + 1, "%-*s %s", strwidth, str, num);
277 } else {
278 snprintf(out, width + 1, "%s", num);
279 }
280
281 printf("%*s ", width, out);
282 break;
283 }
284
285 case EVFILT_MACHPORT:
286 case EVFILT_TIMER:
287 /* hex, to match lsmp */
288 printf("%#*llx ", width, ident);
289 break;
290
291 case EVFILT_WORKLOOP:
292 printf("%#*llx ", width, ident);
293 break;
294
295 default:
296 printf("%*llu ", width, ident);
297 break;
298 }
299
300 }
301
302 static void
303 print_kqid(int state, uint64_t kqid)
304 {
305 if (state & KQ_WORKQ) {
306 printf("%18s ", "wq");
307 } else if (state & KQ_WORKLOOP) {
308 printf("%#18" PRIx64 " ", kqid);
309 } else {
310 printf("fd %15" PRIi64 " ", kqid);
311 }
312 }
313
314 #define PROCNAME_WIDTH 20
315
316 static void
317 print_kq_info(int pid, const char *procname, uint64_t kqid, int state)
318 {
319 if (raw) {
320 printf("%5u ", pid);
321 print_kqid(state, kqid);
322 printf("%#10x ", state);
323 } else {
324 char tmpstr[PROCNAME_WIDTH+1];
325 strlcpy(tmpstr, shorten_procname(procname, PROCNAME_WIDTH), PROCNAME_WIDTH+1);
326 printf("%-*s ", PROCNAME_WIDTH, tmpstr);
327 printf("%5u ", pid);
328 print_kqid(state, kqid);
329 printf(" %c%c%c ",
330 (state & KQ_SLEEP) ? 'k' : '-',
331 (state & KQ_SEL) ? 's' : '-',
332 (state & KQ_WORKQ) ? 'q' :
333 (state & KQ_WORKLOOP) ? 'l' : '-'
334 );
335 }
336 }
337
338 enum kqtype {
339 KQTYPE_FD,
340 KQTYPE_DYNAMIC
341 };
342
343 #define POLICY_TIMESHARE 1
344 #define POLICY_RR 2
345 #define POLICY_FIFO 4
346
347 static int
348 process_kqueue(int pid, const char *procname, enum kqtype type, uint64_t kqid,
349 struct proc_fdinfo *fdlist, int nfds)
350 {
351 int ret, i, nknotes;
352 char tmpstr[256];
353 int maxknotes = 256; /* arbitrary starting point */
354 int kq_state;
355 bool is_kev_64, is_kev_qos;
356 int err = 0;
357 bool overflow = false;
358 int fd;
359 bool dynkq_printed = false;
360
361 /*
362 * get the basic kqueue info
363 */
364 struct kqueue_fdinfo kqfdinfo = {};
365 struct kqueue_dyninfo kqinfo = {};
366 switch (type) {
367 case KQTYPE_FD:
368 ret = proc_pidfdinfo(pid, (int)kqid, PROC_PIDFDKQUEUEINFO, &kqfdinfo, sizeof(kqfdinfo));
369 fd = (int)kqid;
370 break;
371 case KQTYPE_DYNAMIC:
372 ret = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_INFO, kqid, &kqinfo, sizeof(kqinfo));
373 break;
374 default:
375 os_crash("invalid kqueue type");
376 }
377
378 if (type == KQTYPE_FD && (int)kqid != -1) {
379 if (ret != sizeof(kqfdinfo)) {
380 /* every proc has an implicit workq kqueue, dont warn if its unused */
381 fprintf(stderr, "WARN: FD table changed (pid %i, kq %i)\n", pid,
382 fd);
383 }
384 } else if (type == KQTYPE_DYNAMIC) {
385 if (ret < sizeof(struct kqueue_info)) {
386 fprintf(stderr, "WARN: kqueue missing (pid %i, kq %#" PRIx64 ")\n",
387 pid, kqid);
388 } else {
389 kqfdinfo.kqueueinfo = kqinfo.kqdi_info;
390 }
391 if (verbose && ret >= sizeof(struct kqueue_dyninfo)) {
392 print_kq_info(pid, procname, kqid, kqinfo.kqdi_info.kq_state);
393
394 printf("%18s ", " "); // ident
395 printf("%-9s ", " "); // filter
396 dynkq_printed = true;
397
398 if (raw) {
399 printf("%-10s ", " "); // fflags
400 printf("%-10s ", " "); // flags
401 printf("%-10s ", " "); // evst
402 } else {
403 printf("%-8s ", " "); // fdtype
404 char policy_type;
405 switch (kqinfo.kqdi_pol) {
406 case POLICY_RR:
407 policy_type = 'R';
408 break;
409 case POLICY_FIFO:
410 policy_type = 'F';
411 case POLICY_TIMESHARE:
412 case 0:
413 default:
414 policy_type = '-';
415 break;
416 }
417 snprintf(tmpstr, 4, "%c%c%c", (kqinfo.kqdi_pri == 0)?'-':'P', policy_type, (kqinfo.kqdi_cpupercent == 0)?'-':'%');
418 printf("%-7s ", tmpstr); // fflags
419 printf("%-15s ", " "); // flags
420 printf("%-17s ", " "); // evst
421 }
422
423 if (!raw && kqinfo.kqdi_pri != 0) {
424 printf("%3d ", kqinfo.kqdi_pri); //qos
425 } else {
426 int qos = MAX(MAX(kqinfo.kqdi_events_qos, kqinfo.kqdi_async_qos),
427 kqinfo.kqdi_sync_waiter_qos);
428 printf("%3s ", thread_qos_name(qos)); //qos
429 }
430 printf("%-18s ", " "); // data
431 printf("%-18s ", " "); // udata
432 printf("%#18llx ", kqinfo.kqdi_servicer); // ext0
433 printf("%#18llx ", kqinfo.kqdi_owner); // ext1
434 printf("\n");
435 }
436 }
437
438 /*
439 * get extended kqueue info
440 */
441 struct kevent_extinfo *kqextinfo = NULL;
442 again:
443 if (!kqextinfo) {
444 kqextinfo = malloc(sizeof(struct kevent_extinfo) * maxknotes);
445 }
446 if (!kqextinfo) {
447 err = errno;
448 perror("failed allocating memory");
449 goto out;
450 }
451
452 errno = 0;
453 switch (type) {
454 case KQTYPE_FD:
455 nknotes = proc_pidfdinfo(pid, fd, PROC_PIDFDKQUEUE_EXTINFO,
456 kqextinfo, sizeof(struct kevent_extinfo) * maxknotes);
457 break;
458 case KQTYPE_DYNAMIC:
459 nknotes = proc_piddynkqueueinfo(pid, PROC_PIDDYNKQUEUE_EXTINFO, kqid,
460 kqextinfo, sizeof(struct kevent_extinfo) * maxknotes);
461 break;
462 default:
463 os_crash("invalid kqueue type");
464 }
465
466 if (nknotes <= 0) {
467 if (errno == 0) {
468 /* proc_*() can't distinguish between error and empty list */
469 } else if (errno == EAGAIN) {
470 goto again;
471 } else if (errno == EBADF) {
472 fprintf(stderr, "WARN: FD table changed (pid %i, kq %#" PRIx64 ")\n", pid, kqid);
473 goto out;
474 } else {
475 err = errno;
476 perror("failed to get extended kqueue info");
477 goto out;
478 }
479 }
480
481 if (nknotes > maxknotes) {
482 maxknotes = nknotes + 16; /* arbitrary safety margin */
483 free(kqextinfo);
484 kqextinfo = NULL;
485 goto again;
486 }
487
488 if (nknotes >= PROC_PIDFDKQUEUE_KNOTES_MAX) {
489 overflow = true;
490 }
491
492 kq_state = kqfdinfo.kqueueinfo.kq_state;
493 is_kev_64 = (kq_state & PROC_KQUEUE_64);
494 is_kev_qos = (kq_state & PROC_KQUEUE_QOS);
495
496 if (nknotes == 0) {
497 if (!ignore_empty && !dynkq_printed) {
498 /* for empty kqueues, print a single empty entry */
499 print_kq_info(pid, procname, kqid, kq_state);
500 printf("%18s \n", "-");
501 }
502 goto out;
503 }
504
505 for (i = 0; i < nknotes; i++) {
506 struct kevent_extinfo *info = &kqextinfo[i];
507
508 print_kq_info(pid, procname, kqid, kqfdinfo.kqueueinfo.kq_state);
509 print_ident(info->kqext_kev.ident, info->kqext_kev.filter, 18);
510 printf("%-9s ", filt_name(info->kqext_kev.filter));
511
512 if (raw) {
513 printf("%#10x ", info->kqext_sfflags);
514 printf("%#10x ", info->kqext_kev.flags);
515 printf("%#10x ", info->kqext_status);
516 } else {
517 /* for kevents attached to file descriptors, print the type of FD (file, socket, etc) */
518 const char *fdstr = "";
519 if (filter_is_fd_type(info->kqext_kev.filter)) {
520 fdstr = "<unkn>";
521 int knfd = fd_list_getfd(fdlist, nfds, (int)info->kqext_kev.ident);
522 if (knfd >= 0) {
523 fdstr = fdtype_str(fdlist[knfd].proc_fdtype);
524 }
525 }
526 printf("%-8s ", fdstr);
527
528 /* print filter flags */
529 printf("%7s ", fflags_build(info, tmpstr, sizeof(tmpstr)));
530
531 /* print generic flags */
532 unsigned flg = info->kqext_kev.flags;
533 printf("%c%c%c%c %c%c%c%c %c%c%c%c%c ",
534 (flg & EV_ADD) ? 'a' : '-',
535 (flg & EV_ENABLE) ? 'n' : '-',
536 (flg & EV_DISABLE) ? 'd' : '-',
537 (flg & EV_DELETE) ? 'x' : '-',
538
539 (flg & EV_RECEIPT) ? 'r' : '-',
540 (flg & EV_ONESHOT) ? '1' : '-',
541 (flg & EV_CLEAR) ? 'c' : '-',
542 (flg & EV_DISPATCH) ? 's' : '-',
543
544 (flg & EV_UDATA_SPECIFIC) ? 'u' : '-',
545 (flg & EV_FLAG0) ? 'p' : '-',
546 (flg & EV_FLAG1) ? 'b' : '-',
547 (flg & EV_EOF) ? 'o' : '-',
548 (flg & EV_ERROR) ? 'e' : '-'
549 );
550
551 unsigned st = info->kqext_status;
552 printf("%c%c%c%c %c%c%c%c %c%c%c%c %c%c ",
553 (st & KN_ACTIVE) ? 'a' : '-',
554 (st & KN_QUEUED) ? 'q' : '-',
555 (st & KN_DISABLED) ? 'd' : '-',
556 (st & KN_STAYACTIVE) ? 's' : '-',
557
558 (st & KN_DROPPING) ? 'd' : '-',
559 (st & KN_USEWAIT) ? 'w' : '-',
560 (st & KN_ATTACHING) ? 'c' : '-',
561 (st & KN_ATTACHED) ? 'a' : '-',
562
563 (st & KN_DISPATCH) ? 's' : '-',
564 (st & KN_UDATA_SPECIFIC) ? 'u' : '-',
565 (st & KN_SUPPRESSED) ? 'p' : '-',
566 (st & KN_STOLENDROP) ? 't' : '-',
567
568 (st & KN_REQVANISH) ? 'v' : '-',
569 (st & KN_VANISHED) ? 'n' : '-'
570 );
571 }
572
573 printf("%3s ", thread_qos_name(info->kqext_kev.qos));
574
575 printf("%#18llx ", (unsigned long long)info->kqext_kev.data);
576
577 if (verbose) {
578 printf("%#18llx ", (unsigned long long)info->kqext_kev.udata);
579 if (is_kev_qos || is_kev_64) {
580 printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[0]);
581 printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[1]);
582
583 if (is_kev_qos) {
584 printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[2]);
585 printf("%#18llx ", (unsigned long long)info->kqext_kev.ext[3]);
586 printf("%#10lx ", (unsigned long)info->kqext_kev.xflags);
587 }
588 }
589 }
590
591 printf("\n");
592 }
593
594 if (overflow) {
595 printf(" ***** output truncated (>=%i knotes on kq %" PRIu64 ", proc %i) *****\n",
596 nknotes, kqid, pid);
597 }
598
599 out:
600 if (kqextinfo) {
601 free(kqextinfo);
602 kqextinfo = NULL;
603 }
604
605 return err;
606 }
607
608 static int
609 pid_kqids(pid_t pid, kqueue_id_t **kqids_out)
610 {
611 static int kqids_len = 256;
612 static kqueue_id_t *kqids = NULL;
613 static uint32_t kqids_size;
614
615 int nkqids;
616
617 retry:
618 if (os_mul_overflow(sizeof(kqueue_id_t), kqids_len, &kqids_size)) {
619 assert(kqids_len > PROC_PIDDYNKQUEUES_MAX);
620 kqids_len = PROC_PIDDYNKQUEUES_MAX;
621 goto retry;
622 }
623 if (!kqids) {
624 kqids = malloc(kqids_size);
625 os_assert(kqids != NULL);
626 }
627
628 nkqids = proc_list_dynkqueueids(pid, kqids, kqids_size);
629 if (nkqids > kqids_len && kqids_len < PROC_PIDDYNKQUEUES_MAX) {
630 kqids_len *= 2;
631 if (kqids_len > PROC_PIDDYNKQUEUES_MAX) {
632 kqids_len = PROC_PIDDYNKQUEUES_MAX;
633 }
634 free(kqids);
635 kqids = NULL;
636 goto retry;
637 }
638
639 *kqids_out = kqids;
640 return MIN(nkqids, kqids_len);
641 }
642
643 static int
644 process_pid(pid_t pid)
645 {
646 int i, nfds, nkqids;
647 kqueue_id_t *kqids;
648 int ret = 0;
649 int maxfds = 256; /* arbitrary starting point */
650 struct proc_fdinfo *fdlist = NULL;
651
652 /* enumerate file descriptors */
653 again:
654 if (!fdlist) {
655 fdlist = malloc(sizeof(struct proc_fdinfo) * maxfds);
656 }
657 if (!fdlist) {
658 ret = errno;
659 perror("failed to allocate");
660 goto out;
661 }
662
663 nfds = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fdlist,
664 sizeof(struct proc_fdinfo) * maxfds);
665 if (nfds <= 0) {
666 ret = errno;
667 fprintf(stderr, "%s: failed enumerating file descriptors of process %i: %s",
668 self, pid, strerror(ret));
669 if (ret == EPERM && geteuid() != 0) {
670 fprintf(stderr, " (are you root?)");
671 }
672 fprintf(stderr, "\n");
673 goto out;
674 }
675
676 nfds /= sizeof(struct proc_fdinfo);
677 if (nfds >= maxfds) {
678 maxfds = nfds + 16;
679 free(fdlist);
680 fdlist = NULL;
681 goto again;
682 }
683
684 /* get bsdinfo for the process name */
685 struct proc_bsdinfo bsdinfo;
686 ret = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &bsdinfo, sizeof(bsdinfo));
687 if (ret != sizeof(bsdinfo)) {
688 perror("failed retrieving process info");
689 ret = -1;
690 goto out;
691 }
692
693 char *procname = bsdinfo.pbi_name;
694 if (strlen(procname) == 0) {
695 procname = bsdinfo.pbi_comm;
696 }
697
698 /* handle the special workq kq */
699 ret = process_kqueue(pid, procname, KQTYPE_FD, -1, fdlist, nfds);
700 if (ret) {
701 goto out;
702 }
703
704 for (i = 0; i < nfds; i++) {
705 if (fdlist[i].proc_fdtype == PROX_FDTYPE_KQUEUE) {
706 ret = process_kqueue(pid, procname, KQTYPE_FD,
707 (uint64_t)fdlist[i].proc_fd, fdlist, nfds);
708 if (ret) {
709 goto out;
710 }
711 }
712 }
713
714 nkqids = pid_kqids(pid, &kqids);
715
716 for (i = 0; i < nkqids; i++) {
717 ret = process_kqueue(pid, procname, KQTYPE_DYNAMIC, kqids[i], fdlist, nfds);
718 if (ret) {
719 goto out;
720 }
721 }
722
723 if (nkqids >= PROC_PIDDYNKQUEUES_MAX) {
724 printf(" ***** output truncated (>=%i dynamic kqueues in proc %i) *****\n",
725 nkqids, pid);
726 }
727
728 out:
729 if (fdlist) {
730 free(fdlist);
731 fdlist = NULL;
732 }
733
734 return ret;
735 }
736
737 static int
738 process_all_pids(void)
739 {
740 int i, npids;
741 int ret = 0;
742 int maxpids = 2048;
743 int *pids = NULL;
744
745 again:
746 if (!pids) {
747 pids = malloc(sizeof(int) * maxpids);
748 }
749 if (!pids) {
750 perror("failed allocating pids[]");
751 goto out;
752 }
753
754 errno = 0;
755 npids = proc_listpids(PROC_ALL_PIDS, 0, pids, sizeof(int) * maxpids);
756 if (npids <= 0) {
757 if (errno == 0) {
758 /* empty pid list */
759 } else if (errno == EAGAIN) {
760 goto again;
761 } else {
762 ret = errno;
763 perror("failed enumerating pids");
764 goto out;
765 }
766 }
767
768 npids /= sizeof(int);
769 if (npids >= maxpids) {
770 maxpids = npids + 16;
771 free(pids);
772 pids = NULL;
773 goto again;
774 }
775
776 for (i = 0; i < npids; i++) {
777 /* listpids gives us pid 0 for some reason */
778 if (pids[i]) {
779 ret = process_pid(pids[i]);
780 /* ignore races with processes exiting */
781 if (ret && ret != ESRCH) {
782 goto out;
783 }
784 }
785 }
786
787 out:
788 if (pids) {
789 free(pids);
790 pids = NULL;
791 }
792
793 return ret;
794 }
795
796 static void
797 cheatsheet(void)
798 {
799 fprintf(stderr, "\nFilter-independent flags:\n\n\
800 \033[1m\
801 command pid kq kqst knid filter fdtype fflags flags evst qos\033[0m\n\033[1m\
802 -------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- ----------------- ---\033[0m\n\
803 ┌ EV_UDATA_SPECIFIC\n\
804 EV_DISPATCH ┐ │┌ EV_FLAG0 (EV_POLL)\n\
805 EV_CLEAR ┐│ ││┌ EV_FLAG1 (EV_OOBAND)\n\
806 EV_ONESHOT ┐││ │││┌ EV_EOF\n\
807 EV_RECEIPT ┐│││ ││││┌ EV_ERROR\n\
808 ││││ │││││\n\033[1m\
809 launchd 1 4 ks- netbiosd 250 PROC ------- andx r1cs upboe aqds dwca supt vn IN\033[0m \n\
810 │ │││ ││││ ││││ ││││ ││││ ││\n\
811 kqueue file descriptor/dynamic ID ┘ │││ EV_ADD ┘│││ KN_ACTIVE ┘│││ ││││ ││││ ││\n\
812 KQ_SLEEP ┘││ EV_ENABLE ┘││ KN_QUEUED ┘││ ││││ ││││ ││\n\
813 KQ_SEL ┘│ EV_DISABLE ┘│ KN_DISABLED ┘│ ││││ ││││ │└ KN_VANISHED\n\
814 KQ_WORKQ (q) ┤ EV_DELETE ┘ KN_STAYACTIVE ┘ ││││ ││││ └ KN_REQVANISH\n\
815 KQ_WORKLOOP (l) ┘ ││││ ││││\n\
816 KN_DROPPING ┘│││ │││└ KN_STOLENDROP\n\
817 KN_USEWAIT ┘││ ││└ KN_SUPPRESSED\n\
818 KN_ATTACHING ┘│ │└ KN_UDATA_SPECIFIC\n\
819 KN_ATTACHED ┘ └ KN_DISPATCH\n\
820 \n");
821 }
822
823 static void
824 usage(void)
825 {
826 fprintf(stderr, "usage: %s [-vher] [-a | -p <pid>]\n", self);
827 }
828
829 static void
830 print_header(void)
831 {
832 if (raw) {
833 printf(" pid kq kqst knid filter fflags flags evst qos data");
834 } else {
835 printf("command pid kq kqst knid filter fdtype fflags flags evst qos data");
836 }
837
838 if (verbose) {
839 printf(" udata servicer / ext0 owner / ext1 ext2 ext3 xflags");
840 }
841
842 printf("\n");
843
844 if (raw) {
845 printf("----- ------------------ ---------- ------------------ --------- ---------- ---------- ---------- --- ------------------");
846 } else {
847 printf("-------------------- ----- ------------------ ---- ------------------ --------- -------- ------- --------------- ----------------- --- ------------------");
848 }
849
850 if (verbose) {
851 printf(" ------------------ ------------------ ------------------ ------------------ ------------------ ----------");
852 }
853 printf("\n");
854 }
855
856 int
857 main(int argc, char *argv[])
858 {
859 pid_t pid = 0;
860 int opt;
861
862 setlinebuf(stdout);
863
864 if (argc > 0) {
865 self = argv[0];
866 }
867
868 while ((opt = getopt(argc, argv, "eahvrp:")) != -1) {
869 switch (opt) {
870 case 'a':
871 all_pids = 1;
872 break;
873 case 'v':
874 verbose++;
875 break;
876 case 'p':
877 pid = atoi(optarg);
878 break;
879 case 'e':
880 ignore_empty = 1;
881 break;
882 case 'h':
883 usage();
884 cheatsheet();
885 return 0;
886 case 'r':
887 raw = 1;
888 break;
889 case '?':
890 default:
891 usage();
892 return 1;
893 }
894 }
895
896 argc -= optind;
897 argv += optind;
898
899 if (argc == 1) {
900 /* also allow lskq <pid> */
901 if (pid || all_pids) {
902 usage();
903 return 1;
904 }
905
906 pid = atoi(argv[0]);
907 } else if (argc > 1) {
908 usage();
909 return 1;
910 }
911
912 /* exactly one of -p or -a is required */
913 if (!pid && !all_pids) {
914 usage();
915 return 1;
916 } else if (pid && all_pids) {
917 usage();
918 return 1;
919 }
920
921 print_header();
922
923 if (all_pids) {
924 return process_all_pids();
925 } else {
926 return process_pid(pid);
927 }
928
929 return 0;
930 }