]>
git.saurik.com Git - apple/system_cmds.git/blob - lskq.tproj/lskq.c
2 * Copyright (c) 2015 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
;
47 static char *self
= "lskq";
49 static inline const char *
50 filt_name(int16_t filt
)
53 if (idx
>= 0 && idx
< ARRAYLEN(filt_strs
)) {
54 return filt_strs
[idx
];
60 static inline const char *
61 fdtype_str(uint32_t type
)
63 if (type
< ARRAYLEN(fdtype_strs
)) {
64 return fdtype_strs
[type
];
71 fflags_build(struct kevent_extinfo
*info
, char *str
, int len
)
73 unsigned ff
= info
->kqext_sfflags
;
75 switch (info
->kqext_kev
.filter
) {
78 snprintf(str
, len
, "%c ",
79 (ff
& NOTE_LOWAT
) ? 'l' : '-'
84 case EVFILT_MACHPORT
: {
85 snprintf(str
, len
, "%c ",
86 (ff
& MACH_RCV_MSG
) ? 'r' : '-'
92 snprintf(str
, len
, "%c%c%c%c%c%c%c",
93 (ff
& NOTE_DELETE
) ? 'd' : '-',
94 (ff
& NOTE_WRITE
) ? 'w' : '-',
95 (ff
& NOTE_EXTEND
) ? 'e' : '-',
96 (ff
& NOTE_ATTRIB
) ? 'a' : '-',
97 (ff
& NOTE_LINK
) ? 'l' : '-',
98 (ff
& NOTE_RENAME
) ? 'r' : '-',
99 (ff
& NOTE_REVOKE
) ? 'v' : '-'
105 /* NOTE_REAP is deprecated, but we still want to show if it's used */
106 #pragma clang diagnostic push
107 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
108 snprintf(str
, len
, "%c%c%c%c%c%c ",
109 (ff
& NOTE_EXIT
) ? 'x' : '-',
110 (ff
& NOTE_EXITSTATUS
) ? 't' : '-',
111 (ff
& NOTE_FORK
) ? 'f' : '-',
112 (ff
& NOTE_EXEC
) ? 'e' : '-',
113 (ff
& NOTE_SIGNAL
) ? 's' : '-',
114 (ff
& NOTE_REAP
) ? 'r' : '-'
117 #pragma clang diagnostic pop
121 snprintf(str
, len
, "%c%c%c%c%c ",
122 (ff
& NOTE_SECONDS
) ? 's' :
123 (ff
& NOTE_USECONDS
) ? 'u' :
124 (ff
& NOTE_NSECONDS
) ? 'n' : '?',
125 (ff
& NOTE_ABSOLUTE
) ? 'a' : '-',
126 (ff
& NOTE_CRITICAL
) ? 'c' : '-',
127 (ff
& NOTE_BACKGROUND
) ? 'b' : '-',
128 (ff
& NOTE_LEEWAY
) ? 'l' : '-'
134 snprintf(str
, len
, "");
143 filter_is_fd_type(int filter
)
145 if (filter
<= EVFILT_READ
&& filter
>= EVFILT_VNODE
) {
153 * find index of fd in a list of fdinfo of length nfds
156 fd_list_getfd(struct proc_fdinfo
*fds
, int nfds
, int fd
)
159 for (i
= 0; i
< nfds
; i
++) {
160 if (fds
[i
].proc_fd
== fd
) {
169 * left truncate URL-form process names
172 shorten_procname(const char *proc
, int width
)
174 if (strcasestr(proc
, "com.") == proc
) {
175 long len
= strlen(proc
);
177 return &proc
[len
- width
];
187 * stringify knote ident where possible (signals, processes)
190 print_ident(uint64_t ident
, int16_t filter
, int width
)
199 int numlen
= sprintf(num
, "%llu", ident
);
200 int strwidth
= width
- numlen
- 3; // add room for brackets and space
202 if (filter
== EVFILT_SIGNAL
) {
203 if (ident
< ARRAYLEN(sig_strs
)) {
204 snprintf(str
, strwidth
+ 1, "%s", sig_strs
[ident
]);
207 /* FIXME: this should be cached */
208 struct proc_bsdinfo bsdinfo
;
209 int ret
= proc_pidinfo((int)ident
, PROC_PIDTBSDINFO
, 0, &bsdinfo
, sizeof(bsdinfo
));
210 if (ret
== sizeof(bsdinfo
)) {
211 char *procname
= bsdinfo
.pbi_name
;
212 if (strlen(procname
) == 0) {
213 procname
= bsdinfo
.pbi_comm
;
215 snprintf(str
, strwidth
+ 1, "%s",
216 shorten_procname(procname
, strwidth
));
220 if (str
[0] != '\0') {
221 snprintf(out
, width
+ 1, "(%s) %s", str
, num
);
223 snprintf(out
, width
+ 1, "%s", num
);
226 printf("%*s ", width
, out
);
231 printf("%*llu ", width
, ident
);
238 print_kqfd(int kqfd
, int width
)
241 printf("%*s ", width
, "wq");
243 printf("%*u ", width
, kqfd
);
248 print_kq_info(int pid
, const char *procname
, int kqfd
, int state
)
251 strlcpy(tmpstr
, shorten_procname(procname
, 10), 11);
252 printf("%-10s ", tmpstr
);
256 (state
& KQ_SLEEP
) ? 'k' : '-',
257 (state
& KQ_SEL
) ? 's' : '-',
258 (state
& KQ_KEV32
) ? '3' :
259 (state
& KQ_KEV64
) ? '6' :
260 (state
& KQ_KEV_QOS
) ? 'q' : '-'
264 #define MAXENTRIES 2048
267 process_kqueue_on_fd(int pid
, const char *procname
, int kqfd
, struct proc_fdinfo
*fdlist
, int nfds
)
273 * get the basic kqueue info
275 struct kqueue_fdinfo kqfdinfo
= {};
276 ret
= proc_pidfdinfo(pid
, kqfd
, PROC_PIDFDKQUEUEINFO
, &kqfdinfo
, sizeof(kqfdinfo
));
277 if (ret
!= sizeof(kqfdinfo
) && kqfd
!= -1) {
278 /* every proc has an implicit workq kqueue, dont warn if its unused */
279 fprintf(stderr
, "WARN: FD table changed (pid %i, kq %i)\n", pid
, kqfd
);
283 * get extended kqueue info
285 struct kevent_extinfo kqextinfo
[MAXENTRIES
];
288 nknotes
= proc_pidfdinfo(pid
, kqfd
, PROC_PIDFDKQUEUE_EXTINFO
, kqextinfo
, sizeof(kqextinfo
));
291 /* proc_*() can't distinguish between error and empty list */
292 } else if (errno
== EAGAIN
) {
294 } else if (errno
== EBADF
) {
295 fprintf(stderr
, "WARN: FD table changed (pid %i, kq %i)\n", pid
, kqfd
);
298 perror("failed to get extended kqueue info");
303 if (nknotes
> MAXENTRIES
) {
304 fprintf(stderr
, "WARN: truncated knote list (pid %i, kq %i)\n", pid
, kqfd
);
305 nknotes
= MAXENTRIES
;
310 /* for empty kqueues, print a single empty entry */
311 print_kq_info(pid
, procname
, kqfd
, kqfdinfo
.kqueueinfo
.kq_state
);
312 printf("%20s \n", "-");
317 for (i
= 0; i
< nknotes
; i
++) {
318 struct kevent_extinfo
*info
= &kqextinfo
[i
];
320 print_kq_info(pid
, procname
, kqfd
, kqfdinfo
.kqueueinfo
.kq_state
);
321 print_ident(info
->kqext_kev
.ident
, info
->kqext_kev
.filter
, 20);
322 printf("%-9s ", filt_name(info
->kqext_kev
.filter
));
324 /* for kevents attached to file descriptors, print the type of FD (file, socket, etc) */
325 const char *fdstr
= "";
326 if (filter_is_fd_type(info
->kqext_kev
.filter
)) {
328 int knfd
= (info
->kqext_kev
.ident
< nfds
)
329 ? fd_list_getfd(fdlist
, nfds
, (int)info
->kqext_kev
.ident
)
332 fdstr
= fdtype_str(fdlist
[knfd
].proc_fdtype
);
335 printf("%-8s ", fdstr
);
337 /* print filter flags */
338 printf("%7s ", fflags_build(info
, tmpstr
, sizeof(tmpstr
)));
340 /* print generic flags */
341 unsigned flg
= info
->kqext_kev
.flags
;
342 printf("%c%c%c%c%c%c%c%c%c ",
343 (flg
& EV_ADD
) ? 'a' : '-',
344 (flg
& EV_ENABLE
) ? 'n' : '-',
345 (flg
& EV_DISABLE
) ? 'd' : '-',
346 (flg
& EV_DELETE
) ? 'x' : '-',
347 (flg
& EV_RECEIPT
) ? 'r' : '-',
348 (flg
& EV_ONESHOT
) ? '1' : '-',
349 (flg
& EV_CLEAR
) ? 'c' : '-',
350 (flg
& EV_EOF
) ? 'o' : '-',
351 (flg
& EV_ERROR
) ? 'e' : '-'
354 unsigned st
= info
->kqext_status
;
356 (st
& KN_ACTIVE
) ? 'a' : '-',
357 (st
& KN_QUEUED
) ? 'q' : '-',
358 (st
& KN_DISABLED
) ? 'd' : '-',
359 (st
& KN_STAYQUEUED
) ? 's' : '-'
362 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.data
);
365 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.udata
);
366 if (kqfdinfo
.kqueueinfo
.kq_state
& (KQ_KEV64
|KQ_KEV_QOS
)) {
367 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.ext
[0]);
368 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.ext
[1]);
370 if (kqfdinfo
.kqueueinfo
.kq_state
& KQ_KEV_QOS
) {
371 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.ext
[2]);
372 printf("%#18llx ", (unsigned long long)info
->kqext_kev
.ext
[3]);
373 printf("%#10lx ", (unsigned long)info
->kqext_kev
.xflags
);
384 process_pid(pid_t pid
)
388 /* enumerate file descriptors */
389 struct proc_fdinfo fdlist
[MAXENTRIES
];
390 nfds
= proc_pidinfo(pid
, PROC_PIDLISTFDS
, 0, fdlist
, sizeof(fdlist
));
392 fprintf(stderr
, "%s: failed enumerating file descriptors of process %i: %s",
393 self
, pid
, strerror(errno
));
394 if (errno
== EPERM
&& geteuid() != 0) {
395 fprintf(stderr
, " (are you root?)");
397 fprintf(stderr
, "\n");
401 nfds
/= sizeof(struct proc_fdinfo
);
402 if (nfds
> MAXENTRIES
) {
403 fprintf(stderr
, "WARN: truncated FD list (proc %i)\n", pid
);
407 /* get bsdinfo for the process name */
408 struct proc_bsdinfo bsdinfo
;
409 ret
= proc_pidinfo(pid
, PROC_PIDTBSDINFO
, 0, &bsdinfo
, sizeof(bsdinfo
));
410 if (ret
!= sizeof(bsdinfo
)) {
411 perror("failed retrieving process info");
415 char *procname
= bsdinfo
.pbi_name
;
416 if (strlen(procname
) == 0) {
417 procname
= bsdinfo
.pbi_comm
;
420 /* handle the special workq kq */
421 ret
= process_kqueue_on_fd(pid
, procname
, -1, fdlist
, nfds
);
426 for (i
= 0; i
< nfds
; i
++) {
427 if (fdlist
[i
].proc_fdtype
== PROX_FDTYPE_KQUEUE
) {
428 ret
= process_kqueue_on_fd(pid
, procname
, fdlist
[i
].proc_fd
, fdlist
, nfds
);
441 process_all_pids(void)
446 npids
= proc_listpids(PROC_ALL_PIDS
, 0, pids
, sizeof(pids
));
448 perror("failed enumerating pids");
451 npids
/= sizeof(int);
453 for (i
= 0; i
< npids
; i
++) {
454 /* listpids gives us pid 0 for some reason */
456 ret
= process_pid(pids
[i
]);
469 fprintf(stderr
, "usage: %s [-vhe] [-a | -p <pid>]\n", self
);
472 int main(int argc
, char *argv
[])
481 while ((opt
= getopt(argc
, argv
, "eahvp:")) != -1) {
509 /* also allow lskq <pid> */
510 if (pid
|| all_pids
) {
516 } else if (argc
> 1) {
521 /* exactly one of -p or -a is required */
522 if (!pid
&& !all_pids
) {
525 } else if (pid
&& all_pids
) {
530 printf("command pid kq kqst ident filter fdtype fflags flags evst data");
532 printf(" udata ext0 ext1 ext2 ext3 xflags");
535 printf("---------- ----- ----- ---- -------------------- --------- -------- ------- --------- ---- ------------------");
537 printf(" ------------------ ------------------ ------------------ ------------------ ------------------ ----------");
542 return process_all_pids();
544 return process_pid(pid
);