2 * Copyright (c) 2015-2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
31 #include <sys/types.h>
32 #include <sys/event.h>
34 #include <sys/proc_info.h>
35 #include <mach/message.h>
40 #define ARRAYLEN(x) (sizeof((x))/sizeof((x[0])))
42 /* command line options */
45 static int ignore_empty
;
48 static char *self
= "lskq";
50 static inline const char *
51 filt_name(int16_t filt
)
53 static char unkn_filt
[32];
55 if (idx
>= 0 && idx
< ARRAYLEN(filt_strs
)) {
56 return filt_strs
[idx
];
58 snprintf(unkn_filt
, sizeof(unkn_filt
), "%i (?)", idx
);
63 static inline const char *
64 fdtype_str(uint32_t type
)
66 static char unkn_fdtype
[32];
67 if (type
< ARRAYLEN(fdtype_strs
)) {
68 return fdtype_strs
[type
];
70 snprintf(unkn_fdtype
, sizeof(unkn_fdtype
), "%i (?)", type
);
76 fflags_build(struct kevent_extinfo
*info
, char *str
, int len
)
78 unsigned ff
= info
->kqext_sfflags
;
80 switch (info
->kqext_kev
.filter
) {
83 snprintf(str
, len
, "%c ",
84 (ff
& NOTE_LOWAT
) ? 'l' : '-'
89 case EVFILT_MACHPORT
: {
90 snprintf(str
, len
, "%c ",
91 (ff
& MACH_RCV_MSG
) ? 'r' : '-'
97 snprintf(str
, len
, "%c%c%c%c%c%c%c",
98 (ff
& NOTE_DELETE
) ? 'd' : '-',
99 (ff
& NOTE_WRITE
) ? 'w' : '-',
100 (ff
& NOTE_EXTEND
) ? 'e' : '-',
101 (ff
& NOTE_ATTRIB
) ? 'a' : '-',
102 (ff
& NOTE_LINK
) ? 'l' : '-',
103 (ff
& NOTE_RENAME
) ? 'r' : '-',
104 (ff
& NOTE_REVOKE
) ? 'v' : '-'
110 /* NOTE_REAP is deprecated, but we still want to show if it's used */
111 #pragma clang diagnostic push
112 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
113 snprintf(str
, len
, "%c%c%c%c%c%c%c",
114 (ff
& NOTE_EXIT
) ? 'x' : '-',
115 (ff
& NOTE_EXITSTATUS
) ? 't' : '-',
116 (ff
& NOTE_EXIT_DETAIL
)? 'd' : '-',
117 (ff
& NOTE_FORK
) ? 'f' : '-',
118 (ff
& NOTE_EXEC
) ? 'e' : '-',
119 (ff
& NOTE_SIGNAL
) ? 's' : '-',
120 (ff
& NOTE_REAP
) ? 'r' : '-'
123 #pragma clang diagnostic pop
127 snprintf(str
, len
, "%c%c%c%c%c ",
128 (ff
& NOTE_SECONDS
) ? 's' :
129 (ff
& NOTE_USECONDS
) ? 'u' :
130 (ff
& NOTE_NSECONDS
) ? 'n' : '?',
131 (ff
& NOTE_ABSOLUTE
) ? 'a' : '-',
132 (ff
& NOTE_CRITICAL
) ? 'c' : '-',
133 (ff
& NOTE_BACKGROUND
) ? 'b' : '-',
134 (ff
& NOTE_LEEWAY
) ? 'l' : '-'
140 snprintf(str
, len
, "%c%c%c ",
141 (ff
& NOTE_TRIGGER
) ? 't' : '-',
142 (ff
& NOTE_FFAND
) ? 'a' : '-',
143 (ff
& NOTE_FFOR
) ? 'o' : '-'
149 snprintf(str
, len
, "");
158 filter_is_fd_type(int filter
)
160 if (filter
<= EVFILT_READ
&& filter
>= EVFILT_VNODE
) {
168 * find index of fd in a list of fdinfo of length nfds
171 fd_list_getfd(struct proc_fdinfo
*fds
, int nfds
, int fd
)
174 for (i
= 0; i
< nfds
; i
++) {
175 if (fds
[i
].proc_fd
== fd
) {
184 * left truncate URL-form process names
187 shorten_procname(const char *proc
, int width
)
189 if (strcasestr(proc
, "com.") == proc
) {
190 long len
= strlen(proc
);
192 return &proc
[len
- width
];
202 * stringify knote ident where possible (signals, processes)
205 print_ident(uint64_t ident
, int16_t filter
, int width
)
208 printf("%#*llx ", width
, ident
);
219 int numlen
= sprintf(num
, "%llu", ident
);
220 int strwidth
= width
- numlen
- 1; // add room for a space
222 if (filter
== EVFILT_SIGNAL
) {
223 if (ident
< ARRAYLEN(sig_strs
)) {
224 snprintf(str
, strwidth
+ 1, "%s", sig_strs
[ident
]);
227 /* FIXME: this should be cached */
228 struct proc_bsdinfo bsdinfo
;
229 int ret
= proc_pidinfo((int)ident
, PROC_PIDTBSDINFO
, 0, &bsdinfo
, sizeof(bsdinfo
));
230 if (ret
== sizeof(bsdinfo
)) {
231 char *procname
= bsdinfo
.pbi_name
;
232 if (strlen(procname
) == 0) {
233 procname
= bsdinfo
.pbi_comm
;
235 snprintf(str
, strwidth
+ 1, "%s", shorten_procname(procname
, strwidth
));
239 if (str
[0] != '\0') {
240 snprintf(out
, width
+ 1, "%-*s %s", strwidth
, str
, num
);
242 snprintf(out
, width
+ 1, "%s", num
);
245 printf("%*s ", width
, out
);
249 case EVFILT_MACHPORT
:
251 /* hex, to match lsmp */
252 printf("%#*llx ", width
, ident
);
256 printf("%*llu ", width
, ident
);
263 print_kqfd(int kqfd
, int width
)
266 printf("%*s ", width
, "wq");
268 printf("%*u ", width
, kqfd
);
272 #define PROCNAME_WIDTH 20
275 print_kq_info(int pid
, const char *procname
, int kqfd
, int state
)
280 printf("%#10x ", state
);
282 char tmpstr
[PROCNAME_WIDTH
+1];
283 strlcpy(tmpstr
, shorten_procname(procname
, PROCNAME_WIDTH
), PROCNAME_WIDTH
+1);
284 printf("%-*s ", PROCNAME_WIDTH
, tmpstr
);
288 (state
& KQ_SLEEP
) ? 'k' : '-',
289 (state
& KQ_SEL
) ? 's' : '-',
290 (state
& KQ_KEV32
) ? '3' :
291 (state
& KQ_KEV64
) ? '6' :
292 (state
& KQ_KEV_QOS
) ? 'q' : '-'
298 process_kqueue_on_fd(int pid
, const char *procname
, int kqfd
, struct proc_fdinfo
*fdlist
, int nfds
)
302 int maxknotes
= 256; /* arbitrary starting point */
304 bool overflow
= false;
307 * get the basic kqueue info
309 struct kqueue_fdinfo kqfdinfo
= {};
310 ret
= proc_pidfdinfo(pid
, kqfd
, PROC_PIDFDKQUEUEINFO
, &kqfdinfo
, sizeof(kqfdinfo
));
311 if (ret
!= sizeof(kqfdinfo
) && kqfd
!= -1) {
312 /* every proc has an implicit workq kqueue, dont warn if its unused */
313 fprintf(stderr
, "WARN: FD table changed (pid %i, kq %i)\n", pid
, kqfd
);
317 * get extended kqueue info
319 struct kevent_extinfo
*kqextinfo
= NULL
;
322 kqextinfo
= malloc(sizeof(struct kevent_extinfo
) * maxknotes
);
325 perror("failed allocating memory");
331 nknotes
= proc_pidfdinfo(pid
, kqfd
, PROC_PIDFDKQUEUE_EXTINFO
, kqextinfo
,
332 sizeof(struct kevent_extinfo
) * maxknotes
);
335 /* proc_*() can't distinguish between error and empty list */
336 } else if (errno
== EAGAIN
) {
338 } else if (errno
== EBADF
) {
339 fprintf(stderr
, "WARN: FD table changed (pid %i, kq %i)\n", pid
, kqfd
);
342 perror("failed to get extended kqueue info");
348 if (nknotes
> maxknotes
) {
349 maxknotes
= nknotes
+ 16; /* arbitrary safety margin */
355 if (nknotes
>= PROC_PIDFDKQUEUE_KNOTES_MAX
) {
361 /* for empty kqueues, print a single empty entry */
362 print_kq_info(pid
, procname
, kqfd
, kqfdinfo
.kqueueinfo
.kq_state
);
363 printf("%18s \n", "-");
368 for (i
= 0; i
< nknotes
; i
++) {
369 struct kevent_extinfo
*info
= &kqextinfo
[i
];
371 print_kq_info(pid
, procname
, kqfd
, kqfdinfo
.kqueueinfo
.kq_state
);
372 print_ident(info
->kqext_kev
.ident
, info
->kqext_kev
.filter
, 18);
373 printf("%-9s ", filt_name(info
->kqext_kev
.filter
));
376 printf("%#10x ", info
->kqext_sfflags
);
377 printf("%#10x ", info
->kqext_kev
.flags
);
378 printf("%#10x ", info
->kqext_status
);
381 /* for kevents attached to file descriptors, print the type of FD (file, socket, etc) */
382 const char *fdstr
= "";
383 if (filter_is_fd_type(info
->kqext_kev
.filter
)) {
385 int knfd
= fd_list_getfd(fdlist
, nfds
, (int)info
->kqext_kev
.ident
);
387 fdstr
= fdtype_str(fdlist
[knfd
].proc_fdtype
);
390 printf("%-8s ", fdstr
);
392 /* print filter flags */
393 printf("%7s ", fflags_build(info
, tmpstr
, sizeof(tmpstr
)));
395 /* print generic flags */
396 unsigned flg
= info
->kqext_kev
.flags
;
397 printf("%c%c%c%c %c%c%c%c %c%c%c%c%c ",
398 (flg
& EV_ADD
) ? 'a' : '-',
399 (flg
& EV_ENABLE
) ? 'n' : '-',
400 (flg
& EV_DISABLE
) ? 'd' : '-',
401 (flg
& EV_DELETE
) ? 'x' : '-',
403 (flg
& EV_RECEIPT
) ? 'r' : '-',
404 (flg
& EV_ONESHOT
) ? '1' : '-',
405 (flg
& EV_CLEAR
) ? 'c' : '-',
406 (flg
& EV_DISPATCH
) ? 's' : '-',
408 (flg
& EV_UDATA_SPECIFIC
) ? 'u' : '-',
409 (flg
& EV_FLAG0
) ? 'p' : '-',
410 (flg
& EV_FLAG1
) ? 'b' : '-',
411 (flg
& EV_EOF
) ? 'o' : '-',
412 (flg
& EV_ERROR
) ? 'e' : '-'
415 unsigned st
= info
->kqext_status
;
416 printf("%c%c%c%c %c%c%c%c%c",
417 (st
& KN_ACTIVE
) ? 'a' : '-',
418 (st
& KN_QUEUED
) ? 'q' : '-',
419 (st
& KN_DISABLED
) ? 'd' : '-',
420 (st
& KN_STAYQUEUED
) ? 's' : '-',
422 (st
& KN_DROPPING
) ? 'o' : '-',
423 (st
& KN_USEWAIT
) ? 'u' : '-',
424 (st
& KN_ATTACHING
) ? 'c' : '-',
425 (st
& KN_DEFERDROP
) ? 'f' : '-',
426 (st
& KN_TOUCH
) ? 't' : '-'
430 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.data
);
433 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.udata
);
434 if (kqfdinfo
.kqueueinfo
.kq_state
& (KQ_KEV64
|KQ_KEV_QOS
)) {
435 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.ext
[0]);
436 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.ext
[1]);
438 if (kqfdinfo
.kqueueinfo
.kq_state
& KQ_KEV_QOS
) {
439 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.ext
[2]);
440 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.ext
[3]);
441 printf("%#10lx ", (unsigned long)info
->kqext_kev
.xflags
);
449 printf(" ***** output truncated (>=%i knotes on kq %i, proc %i) *****\n",
463 process_pid(pid_t pid
)
467 int maxfds
= 256; /* arbitrary starting point */
468 struct proc_fdinfo
*fdlist
= NULL
;
470 /* enumerate file descriptors */
473 fdlist
= malloc(sizeof(struct proc_fdinfo
) * maxfds
);
476 perror("failed to allocate");
481 nfds
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, fdlist
,
482 sizeof(struct proc_fdinfo
) * maxfds
);
484 fprintf(stderr
, "%s: failed enumerating file descriptors of process %i: %s",
485 self
, pid
, strerror(errno
));
486 if (errno
== EPERM
&& geteuid() != 0) {
487 fprintf(stderr
, " (are you root?)");
489 fprintf(stderr
, "\n");
494 nfds
/= sizeof(struct proc_fdinfo
);
495 if (nfds
>= maxfds
) {
502 /* get bsdinfo for the process name */
503 struct proc_bsdinfo bsdinfo
;
504 ret
= proc_pidinfo(pid
, PROC_PIDTBSDINFO
, 0, &bsdinfo
, sizeof(bsdinfo
));
505 if (ret
!= sizeof(bsdinfo
)) {
506 perror("failed retrieving process info");
511 char *procname
= bsdinfo
.pbi_name
;
512 if (strlen(procname
) == 0) {
513 procname
= bsdinfo
.pbi_comm
;
516 /* handle the special workq kq */
517 ret
= process_kqueue_on_fd(pid
, procname
, -1, fdlist
, nfds
);
522 for (i
= 0; i
< nfds
; i
++) {
523 if (fdlist
[i
].proc_fdtype
== PROX_FDTYPE_KQUEUE
) {
524 ret
= process_kqueue_on_fd(pid
, procname
, fdlist
[i
].proc_fd
, fdlist
, nfds
);
541 process_all_pids(void)
550 pids
= malloc(sizeof(int) * maxpids
);
553 perror("failed allocating pids[]");
558 npids
= proc_listpids(PROC_ALL_PIDS
, 0, pids
, sizeof(int) * maxpids
);
562 } else if (errno
== EAGAIN
) {
565 perror("failed enumerating pids");
571 npids
/= sizeof(int);
572 if (npids
>= maxpids
) {
573 maxpids
= npids
+ 16;
579 for (i
= 0; i
< npids
; i
++) {
580 /* listpids gives us pid 0 for some reason */
582 ret
= process_pid(pids
[i
]);
601 fprintf(stderr
, "\nFilter-independent flags:\n\n\
602 \033[1mcommand pid kq kqst ident filter fdtype fflags flags evst\033[0m\n\
603 \033[1m-------------------- ----- ----- ---- ------------------ --------- -------- ------- --------------- ----------\033[0m\n\
604 ┌ EV_UDATA_SPECIFIC\n\
605 EV_DISPATCH ┐ │┌ EV_FLAG0 (EV_POLL)\n\
606 EV_CLEAR ┐│ ││┌ EV_FLAG1 (EV_OOBAND)\n\
607 EV_ONESHOT ┐││ │││┌ EV_EOF\n\
608 EV_RECEIPT ┐│││ ││││┌ EV_ERROR\n\
610 \033[1mlaunchd 1 4 ks- netbiosd 250 PROC ------- andx r1cs upboe aqds oucft\033[0m \n\
611 │ │││ ││││ ││││ │││││\n\
612 kqueue file descriptor ┘ │││ EV_ADD ┘│││ KN_ACTIVE ┘│││ ││││└ KN_TOUCH\n\
613 KQ_SLEEP ┘││ EV_ENABLE ┘││ KN_QUEUED ┘││ │││└ KN_DEFERDROP\n\
614 KQ_SEL ┘│ EV_DISABLE ┘│ KN_DISABLED ┘│ ││└ KN_ATTACHING\n\
615 KEV32 (3) ┤ EV_DELETE ┘ KN_STAYQUEUED ┘ │└ KN_USEWAIT\n\
616 KEV64 (6) ┤ └ KN_DROPPING\n\
624 fprintf(stderr
, "usage: %s [-vher] [-a | -p <pid>]\n", self
);
631 printf(" pid kq kqst ident filter fflags flags evst data");
633 printf(" udata ext0 ext1 ext2 ext3 xflags");
636 printf("----- ----- ---------- ------------------ --------- ---------- ---------- ---------- ------------------");
639 printf("command pid kq kqst ident filter fdtype fflags flags evst data");
641 printf(" udata ext0 ext1 ext2 ext3 xflags");
644 printf("-------------------- ----- ----- ---- ------------------ --------- -------- ------- --------------- ---------- -----------------");
648 printf(" ------------------ ------------------ ------------------ ------------------ ------------------ ----------");
654 main(int argc
, char *argv
[])
665 while ((opt
= getopt(argc
, argv
, "eahvrp:")) != -1) {
697 /* also allow lskq <pid> */
698 if (pid
|| all_pids
) {
704 } else if (argc
> 1) {
709 /* exactly one of -p or -a is required */
710 if (!pid
&& !all_pids
) {
713 } else if (pid
&& all_pids
) {
721 return process_all_pids();
723 return process_pid(pid
);