The branch main has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=28f4cf9217c27edea42c5c024c9e71816890bb3c

commit 28f4cf9217c27edea42c5c024c9e71816890bb3c
Author:     Konstantin Belousov <k...@freebsd.org>
AuthorDate: 2025-02-26 00:29:49 +0000
Commit:     Konstantin Belousov <k...@freebsd.org>
CommitDate: 2025-03-13 16:09:35 +0000

    procstat(1): dump kqueues
    
    Example output from the 'procstat -a kqueues':
        PID       KQFD   FILTER      IDENT      FLAGS     FFLAGS       DATA     
 UDATA     STATUS
       2323         13     READ          5          -          -          0 
0x19c290616000          -
       2323         13     READ          8          -          -          0 
0x19c29064f070          -
       2323         13     READ         10          -          -          0 
0x19c29064f0e0          -
       2323         13   SIGNAL          1          C          -          0     
   0x0          -
       2323         13   SIGNAL          2          C          -          0     
   0x0          -
       2323         13   SIGNAL          3          C          -          0     
   0x0          -
       2323         13   SIGNAL         13          C          -          0     
   0x0          -
       2323         13   SIGNAL         14          C          -          0     
   0x0          -
       2323         13   SIGNAL         15          C          -          0     
   0x0          -
       2323         13   SIGNAL         20          C          -          0     
   0x0          -
    
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D49163
---
 usr.bin/procstat/Makefile          |   1 +
 usr.bin/procstat/procstat.c        |  20 +++
 usr.bin/procstat/procstat.h        |   1 +
 usr.bin/procstat/procstat_kqueue.c | 319 +++++++++++++++++++++++++++++++++++++
 4 files changed, 341 insertions(+)

diff --git a/usr.bin/procstat/Makefile b/usr.bin/procstat/Makefile
index d0e5d89478e7..c143e74d516c 100644
--- a/usr.bin/procstat/Makefile
+++ b/usr.bin/procstat/Makefile
@@ -11,6 +11,7 @@ SRCS= procstat.c              \
        procstat_cred.c         \
        procstat_cs.c           \
        procstat_files.c        \
+       procstat_kqueue.c       \
        procstat_kstack.c       \
        procstat_penv.c         \
        procstat_ptlwpinfo.c    \
diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c
index 49595537f273..62076094b073 100644
--- a/usr.bin/procstat/procstat.c
+++ b/usr.bin/procstat/procstat.c
@@ -65,6 +65,7 @@ static void cmdopt_signals(int argc, char * const argv[]);
 static void cmdopt_rusage(int argc, char * const argv[]);
 static void cmdopt_files(int argc, char * const argv[]);
 static void cmdopt_cpuset(int argc, char * const argv[]);
+static void cmdopt_kqueue(int argc, char * const argv[]);
 
 static const char *progname;
 
@@ -103,6 +104,8 @@ static const struct procstat_cmd cmd_table[] = {
            PS_CMP_PLURAL },
        { "file", "files", "[-C]", &procstat_files, &cmdopt_files,
            PS_CMP_PLURAL },
+       { "kqueue", "kqueues", NULL, &procstat_kqueues, &cmdopt_kqueue,
+           PS_CMP_PLURAL },
        { "kstack", "kstack", "[-v]", &procstat_kstack, &cmdopt_verbose,
            PS_CMP_NORMAL },
        { "pargs", "args", NULL, &procstat_pargs, &cmdopt_none,
@@ -631,3 +634,20 @@ cmdopt_cpuset(int argc, char * const argv[])
        procstat_opts |= PS_OPT_PERTHREAD;
        cmdopt_none(argc, argv);
 }
+
+void
+cmdopt_kqueue(int argc, char * const argv[])
+{
+       int ch;
+
+       while ((ch = getopt(argc, argv, "v")) != -1) {
+               switch (ch) {
+               case 'v':
+                       procstat_opts |= PS_OPT_VERBOSE;
+                       break;
+               case '?':
+               default:
+                       usage(NULL);
+               }
+       }
+}
diff --git a/usr.bin/procstat/procstat.h b/usr.bin/procstat/procstat.h
index f106fac79127..e075065315f8 100644
--- a/usr.bin/procstat/procstat.h
+++ b/usr.bin/procstat/procstat.h
@@ -64,6 +64,7 @@ void  procstat_cred(struct procstat *prstat, struct 
kinfo_proc *kipp);
 void   procstat_cs(struct procstat *prstat, struct kinfo_proc *kipp);
 void   procstat_env(struct procstat *prstat, struct kinfo_proc *kipp);
 void   procstat_files(struct procstat *prstat, struct kinfo_proc *kipp);
+void   procstat_kqueues(struct procstat *prstat, struct kinfo_proc *kipp);
 void   procstat_kstack(struct procstat *prstat, struct kinfo_proc *kipp);
 void   procstat_rlimitusage(struct procstat *procstat,
            struct kinfo_proc *kipp);
diff --git a/usr.bin/procstat/procstat_kqueue.c 
b/usr.bin/procstat/procstat_kqueue.c
new file mode 100644
index 000000000000..ce9d2cb42fe2
--- /dev/null
+++ b/usr.bin/procstat/procstat_kqueue.c
@@ -0,0 +1,319 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <k...@freebsd.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/event.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <err.h>
+#include <errno.h>
+#include <libprocstat.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "procstat.h"
+
+static const char kqs[] = "kqueues";
+static const char kq[] = "kqueue";
+
+#define FILT_ELEM(name)        [-EVFILT_##name] = #name,
+static const char *const filter_names[] = {
+       [0] = "invalid",
+       FILT_ELEM(READ)
+       FILT_ELEM(WRITE)
+       FILT_ELEM(AIO)
+       FILT_ELEM(VNODE)
+       FILT_ELEM(PROC)
+       FILT_ELEM(SIGNAL)
+       FILT_ELEM(TIMER)
+       FILT_ELEM(PROCDESC)
+       FILT_ELEM(FS)
+       FILT_ELEM(LIO)
+       FILT_ELEM(USER)
+       FILT_ELEM(SENDFILE)
+       FILT_ELEM(EMPTY)
+};
+#undef FILT_ELEM
+
+#define        PK_FLAG_ELEM(fname, str) { .flag = fname, .dispstr = str }
+#define        PK_NAME_ELEM(prefix, fname) { .flag = prefix##fname, .dispstr = 
#fname }
+#define PK_FLAG_LAST_ELEM() { .flag = -1, .dispstr = NULL }
+struct pk_elem {
+       unsigned int flag;
+       const char *dispstr;
+};
+
+static const struct pk_elem kn_status_names[] = {
+       PK_FLAG_ELEM(KNOTE_STATUS_ACTIVE, "A"),
+       PK_FLAG_ELEM(KNOTE_STATUS_QUEUED, "Q"),
+       PK_FLAG_ELEM(KNOTE_STATUS_DISABLED, "D"),
+       PK_FLAG_ELEM(KNOTE_STATUS_DETACHED, "d"),
+       PK_FLAG_ELEM(KNOTE_STATUS_KQUEUE, "K"),
+       PK_FLAG_LAST_ELEM(),
+};
+
+static const struct pk_elem ev_flags_names[] = {
+       PK_FLAG_ELEM(EV_ONESHOT, "O"),
+       PK_FLAG_ELEM(EV_CLEAR, "C"),
+       PK_FLAG_ELEM(EV_RECEIPT, "R"),
+       PK_FLAG_ELEM(EV_DISPATCH, "D"),
+       PK_FLAG_ELEM(EV_DROP, "d"),
+       PK_FLAG_ELEM(EV_FLAG1, "1"),
+       PK_FLAG_ELEM(EV_FLAG2, "2"),
+       PK_FLAG_LAST_ELEM(),
+};
+
+static char *
+procstat_kqueue_flags(const struct pk_elem *names, unsigned flags, bool commas)
+{
+       char *res;
+       const struct pk_elem *pl;
+       size_t len;
+       int i;
+       bool first;
+
+       first = true;
+       len = 0;
+       for (i = 0;; i++) {
+               pl = &names[i];
+               if (pl->flag == (unsigned)-1)
+                       break;
+               if ((flags & pl->flag) != 0) {
+                       if (first)
+                               first = false;
+                       else if (commas)
+                               len += sizeof(",");
+                       len += strlen(pl->dispstr);
+               }
+       }
+       len++;
+
+       res = malloc(len);
+       first = true;
+       res[0] = '\0';
+       for (i = 0;; i++) {
+               pl = &names[i];
+               if (pl->flag == (unsigned)-1)
+                       break;
+               if ((flags & pl->flag) != 0) {
+                       if (first)
+                               first = false;
+                       else if (commas)
+                               strlcat(res, ",", len);
+                       strlcat(res, pl->dispstr, len);
+               }
+       }
+
+       if (strlen(res) == 0)
+               return (strdup("-"));
+       return (res);
+}
+
+static const struct pk_elem rw_filter_names[] = {
+       PK_NAME_ELEM(NOTE_, LOWAT),
+       PK_NAME_ELEM(NOTE_, FILE_POLL),
+       PK_FLAG_LAST_ELEM(),
+};
+
+static const struct pk_elem user_filter_names[] = {
+       PK_NAME_ELEM(NOTE_, FFAND),
+       PK_NAME_ELEM(NOTE_, FFOR),
+       PK_NAME_ELEM(NOTE_, TRIGGER),
+       PK_FLAG_LAST_ELEM(),
+};
+
+static const struct pk_elem vnode_filter_names[] = {
+       PK_NAME_ELEM(NOTE_, DELETE),
+       PK_NAME_ELEM(NOTE_, WRITE),
+       PK_NAME_ELEM(NOTE_, EXTEND),
+       PK_NAME_ELEM(NOTE_, ATTRIB),
+       PK_NAME_ELEM(NOTE_, LINK),
+       PK_NAME_ELEM(NOTE_, RENAME),
+       PK_NAME_ELEM(NOTE_, REVOKE),
+       PK_NAME_ELEM(NOTE_, OPEN),
+       PK_NAME_ELEM(NOTE_, CLOSE),
+       PK_NAME_ELEM(NOTE_, CLOSE_WRITE),
+       PK_NAME_ELEM(NOTE_, READ),
+       PK_FLAG_LAST_ELEM(),
+};
+
+static const struct pk_elem proc_filter_names[] = {
+       PK_NAME_ELEM(NOTE_, EXIT),
+       PK_NAME_ELEM(NOTE_, FORK),
+       PK_NAME_ELEM(NOTE_, EXEC),
+       PK_NAME_ELEM(NOTE_, TRACK),
+       PK_NAME_ELEM(NOTE_, TRACKERR),
+       PK_NAME_ELEM(NOTE_, CHILD),
+       PK_FLAG_LAST_ELEM(),
+};
+
+static const struct pk_elem timer_filter_names[] = {
+       PK_NAME_ELEM(NOTE_, SECONDS),
+       PK_NAME_ELEM(NOTE_, MSECONDS),
+       PK_NAME_ELEM(NOTE_, USECONDS),
+       PK_NAME_ELEM(NOTE_, NSECONDS),
+       PK_NAME_ELEM(NOTE_, ABSTIME),
+       PK_FLAG_LAST_ELEM(),
+};
+
+#define FILT_ELEM(name)        [-EVFILT_##name] = "EVFILT_"#name
+static const struct pk_elem *const filter_pk_names[] = {
+       [0] =                   NULL,
+       [-EVFILT_READ] =        rw_filter_names,
+       [-EVFILT_WRITE] =       rw_filter_names,
+       [-EVFILT_AIO] =         rw_filter_names,
+       [-EVFILT_VNODE] =       vnode_filter_names,
+       [-EVFILT_PROC] =        proc_filter_names,
+       [-EVFILT_SIGNAL] =      NULL,
+       [-EVFILT_TIMER] =       timer_filter_names,
+       [-EVFILT_PROCDESC] =    proc_filter_names,
+       [-EVFILT_FS] =          NULL,
+       [-EVFILT_LIO] =         rw_filter_names,
+       [-EVFILT_USER] =        user_filter_names,
+       [-EVFILT_SENDFILE] =    NULL,
+       [-EVFILT_EMPTY] =       NULL,
+};
+
+static char *
+procstat_kqueue_fflags(int filter, unsigned fflags)
+{
+       const struct pk_elem *names;
+
+       names = NULL;
+       if (filter < 0 && -filter < (int)nitems(filter_pk_names))
+               names = filter_pk_names[-filter];
+       if (names == NULL)
+               return (strdup("-"));
+       return (procstat_kqueue_flags(names, fflags, true));
+}
+
+static const char *
+procstat_kqueue_get_filter_name(int filter)
+{
+       filter = -filter;
+       if (filter < 0 || filter >= (int)nitems(filter_names))
+               filter = 0;
+       return (filter_names[filter]);
+}
+
+static void
+procstat_kqueue(struct procstat *procstat, struct kinfo_proc *kipp, int fd,
+    bool verbose)
+{
+       struct kinfo_knote *kni, *knis;
+       char *flags, *fflags, *status;
+       unsigned int count, i, j;
+       char errbuf[_POSIX2_LINE_MAX];
+
+       errbuf[0] = '\0';
+       knis = procstat_get_kqueue_info(procstat, kipp, fd, &count, errbuf);
+       if (knis == NULL) {
+               warnx("%s\n", errbuf);
+               return;
+       }
+
+       for (i = 0; i < count; i++) {
+               kni = &knis[i];
+               flags = procstat_kqueue_flags(ev_flags_names,
+                   kni->knt_event.flags, false);
+               fflags = procstat_kqueue_fflags(kni->knt_event.filter,
+                   kni->knt_event.fflags);
+               status = procstat_kqueue_flags(kn_status_names,
+                   kni->knt_status, false);
+               xo_open_instance(kq);
+               xo_emit("{dk:process_id/%7d} ", kipp->ki_pid);
+               xo_emit("{:kqueue_fd/%10d} ", fd);
+               xo_emit("{:filter/%8s} ", procstat_kqueue_get_filter_name(
+                   kni->knt_event.filter));
+               xo_emit("{:ident/%10d} ", kni->knt_event.ident);
+               xo_emit("{:flags/%10s} ", flags);
+               xo_emit("{:fflags/%10s} ", fflags);
+               xo_emit("{:data/%#10jx} ", (uintmax_t)kni->knt_event.data);
+               xo_emit("{:udata/%10p} ", (uintmax_t)kni->knt_event.udata);
+               if (verbose) {
+                       for (j = 0; j < nitems(kni->knt_event.ext); j++) {
+                               xo_emit("{:ext%u/%#10jx} ", j,
+                                   (uintmax_t)kni->knt_event.ext[j]);
+                       }
+               }
+               xo_emit("{:status/%10s}\n", status);
+               free(flags);
+               free(fflags);
+               free(status);
+               xo_close_instance(kq);
+       }
+
+       procstat_freekqinfo(procstat, knis);
+}
+
+void
+procstat_kqueues(struct procstat *procstat, struct kinfo_proc *kipp)
+{
+       struct filestat_list *fl;
+       struct filestat *f;
+       bool verbose;
+
+       verbose = (procstat_opts & PS_OPT_VERBOSE) != 0;
+       if ((procstat_opts & PS_OPT_NOHEADER) == 0) {
+               if (verbose) {
+                       xo_emit("{T:/%7s %10s %8s %10s %10s %10s %10s %10s "
+                           "%10s %10s %10s %10s %10s}\n",
+                           "PID", "KQFD", "FILTER", "IDENT", "FLAGS", "FFLAGS",
+                           "DATA", "UDATA", "EXT0", "EXT1","EXT2","EXT3",
+                           "STATUS");
+               } else {
+                       xo_emit("{T:/%7s %10s %8s %10s %10s %10s %10s %10s "
+                           "%10s}\n",
+                           "PID", "KQFD", "FILTER", "IDENT", "FLAGS", "FFLAGS",
+                           "DATA", "UDATA", "STATUS");
+               }
+       }
+
+       xo_emit("{ek:process_id/%d}", kipp->ki_pid);
+
+       fl = procstat_getfiles(procstat, kipp, 0);
+       if (fl == NULL)
+               return;
+       xo_open_list(kqs);
+       STAILQ_FOREACH(f, fl, next) {
+               if (f->fs_type != PS_FST_TYPE_KQUEUE)
+                       continue;
+               xo_emit("{ek:kqueue/%d}", f->fs_fd);
+               xo_open_list(kq);
+               procstat_kqueue(procstat, kipp, f->fs_fd, verbose);
+               xo_close_list(kq);
+       }
+       xo_close_list(kqs);
+       procstat_freefiles(procstat, fl);
+}

Reply via email to