Page MenuHomeFreeBSD

D49163.id152062.diff
No OneTemporary

D49163.id152062.diff

diff --git a/lib/libc/gen/sysctl.3 b/lib/libc/gen/sysctl.3
--- a/lib/libc/gen/sysctl.3
+++ b/lib/libc/gen/sysctl.3
@@ -511,6 +511,7 @@
.It Dv KERN_PROC_NFDS Ta "Integer"
.It Dv KERN_PROC_SIGFASTBLK Ta "Integer"
.It Dv KERN_PROC_VM_LAYOUT Ta "struct kinfo_vm_layout"
+.It Dv KERN_PROC_KQUEUE Ta "struct kinfo_knote []"
.El
.Pp
.Bl -tag -compact
@@ -584,6 +585,15 @@
location, if active.
.It Dv KERN_PROC_VM_LAYOUT
Fills a structure describing process virtual address space layout.
+.It Dv KERN_PROC_KQUEUE
+Fills an array of structures describing events registered with
+the specified kqueue.
+The next two node's values are the
+.Va pid
+and
+.Va kqfd ,
+the process ID of the process, and the file descriptor of the kqueue
+in that process, to query.
.El
.It Li KERN_PS_STRINGS
Reports the location of the process
diff --git a/lib/libprocstat/Symbol.map b/lib/libprocstat/Symbol.map
--- a/lib/libprocstat/Symbol.map
+++ b/lib/libprocstat/Symbol.map
@@ -50,6 +50,8 @@
};
FBSD_1.8 {
+ procstat_get_kqueue_info;
procstat_getrlimitusage;
+ procstat_freekqinfo;
procstat_freerlimitusage;
};
\ No newline at end of file
diff --git a/lib/libprocstat/libprocstat.h b/lib/libprocstat/libprocstat.h
--- a/lib/libprocstat/libprocstat.h
+++ b/lib/libprocstat/libprocstat.h
@@ -110,6 +110,7 @@
struct kinfo_kstack;
struct kinfo_proc;
struct kinfo_vmentry;
+struct kinfo_knote;
struct procstat;
struct ptrace_lwpinfo;
struct rlimit;
@@ -204,6 +205,7 @@
#endif
void procstat_freeenvv(struct procstat *procstat);
void procstat_freegroups(struct procstat *procstat, gid_t *groups);
+void procstat_freekqinfo(struct procstat *procstat, struct kinfo_knote *kni);
void procstat_freekstack(struct procstat *procstat,
struct kinfo_kstack *kkstp);
void procstat_freeprocs(struct procstat *procstat, struct kinfo_proc *p);
@@ -219,6 +221,8 @@
struct kinfo_proc *kp, int mmapped);
struct kinfo_proc *procstat_getprocs(struct procstat *procstat,
int what, int arg, unsigned int *count);
+struct kinfo_knote *procstat_get_kqueue_info(struct procstat *procstat,
+ struct kinfo_proc *kp, int kqfd, unsigned int *count, char *errbuf);
int procstat_get_pipe_info(struct procstat *procstat, struct filestat *fst,
struct pipestat *pipe, char *errbuf);
int procstat_get_pts_info(struct procstat *procstat, struct filestat *fst,
diff --git a/lib/libprocstat/libprocstat.c b/lib/libprocstat/libprocstat.c
--- a/lib/libprocstat/libprocstat.c
+++ b/lib/libprocstat/libprocstat.c
@@ -2817,3 +2817,69 @@
{
free(resusage);
}
+
+static struct kinfo_knote *
+procstat_get_kqueue_info_sysctl(pid_t pid, int kqfd, unsigned int *cntp,
+ char *errbuf)
+{
+ int error, name[5];
+ struct kinfo_knote *val;
+ size_t len;
+
+ name[0] = CTL_KERN;
+ name[1] = KERN_PROC;
+ name[2] = KERN_PROC_KQUEUE;
+ name[3] = pid;
+ name[4] = kqfd;
+
+ len = 0;
+ error = sysctl(name, nitems(name), NULL, &len, NULL, 0);
+ if (error == -1) {
+ snprintf(errbuf, _POSIX2_LINE_MAX,
+ "KERN_PROC_KQUEUE.pid<%d>.kq<%d> (size q) failed: %s",
+ pid, kqfd, strerror(errno));
+ return (NULL);
+ }
+ val = malloc(len);
+ if (val == NULL) {
+ snprintf(errbuf, _POSIX2_LINE_MAX, "no memory");
+ return (NULL);
+ }
+
+ error = sysctl(name, nitems(name), val, &len, NULL, 0);
+ if (error == -1) {
+ snprintf(errbuf, _POSIX2_LINE_MAX,
+ "KERN_PROC_KQUEUE.pid<%d>.kq<%d> failed: %s",
+ pid, kqfd, strerror(errno));
+ free(val);
+ return (NULL);
+ }
+ *cntp = len / sizeof(*val);
+ return (val);
+}
+
+struct kinfo_knote *
+procstat_get_kqueue_info(struct procstat *procstat,
+ struct kinfo_proc *kp, int kqfd, unsigned int *count, char *errbuf)
+{
+ switch (procstat->type) {
+ case PROCSTAT_KVM:
+ warnx("kvm method is not supported");
+ return (NULL);
+ case PROCSTAT_SYSCTL:
+ return (procstat_get_kqueue_info_sysctl(kp->ki_pid, kqfd,
+ count, errbuf));
+ case PROCSTAT_CORE:
+ warnx("core method is not supported");
+ return (NULL);
+ default:
+ warnx("unknown access method: %d", procstat->type);
+ return (NULL);
+ }
+}
+
+void
+procstat_freekqinfo(struct procstat *procstat __unused, struct kinfo_knote *v)
+{
+ free(v);
+}
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -64,6 +64,7 @@
#include <sys/socketvar.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
+#include <sys/sysent.h>
#include <sys/sysproto.h>
#include <sys/syscallsubr.h>
#include <sys/taskqueue.h>
@@ -2730,8 +2731,15 @@
KQ_NOTOWNED(kq);
KQ_LOCK(kq);
- KASSERT(kn->kn_influx == 1,
- ("knote_drop called on %p with influx %d", kn, kn->kn_influx));
+ for (;;) {
+ KASSERT(kn->kn_influx >= 1,
+ ("knote_drop called on %p with influx %d",
+ kn, kn->kn_influx));
+ if (kn->kn_influx == 1)
+ break;
+ kq->kq_state |= KQ_FLUXWAIT;
+ msleep(kq, &kq->kq_lock, PSOCK, "kqflxwt", 0);
+ }
if (kn->kn_fop->f_isfd)
list = &kq->kq_knlist[kn->kn_id];
@@ -2829,3 +2837,132 @@
fdrop(fp, td);
return (error);
}
+
+struct knote_status_export_bit {
+ int kn_status_bit;
+ int knt_status_bit;
+};
+
+#define ST(name) \
+ { .kn_status_bit = KN_##name, .knt_status_bit = KNOTE_STATUS_##name }
+static const struct knote_status_export_bit knote_status_export_bits[] = {
+ ST(ACTIVE),
+ ST(QUEUED),
+ ST(DISABLED),
+ ST(DETACHED),
+ ST(KQUEUE),
+};
+#undef ST
+
+static int
+knote_status_export(int kn_status)
+{
+ const struct knote_status_export_bit *b;
+ unsigned i;
+ int res;
+
+ res = 0;
+ for (i = 0; i < nitems(knote_status_export_bits); i++) {
+ b = &knote_status_export_bits[i];
+ if ((kn_status & b->kn_status_bit) != 0)
+ res |= b->knt_status_bit;
+ }
+ return (res);
+}
+
+static int
+sysctl_kern_proc_kqueue_report_one(struct proc *p, struct sysctl_req *req,
+ struct kqueue *kq, struct knote *kn)
+{
+ struct kinfo_knote kin;
+ int error;
+
+ if (kn->kn_status == KN_MARKER)
+ return (0);
+
+ memset(&kin, 0, sizeof(kin));
+ memcpy(&kin.knt_event, &kn->kn_kevent, sizeof(struct kevent));
+ kin.knt_status = knote_status_export(kn->kn_status);
+ kn_enter_flux(kn);
+ KQ_UNLOCK_FLUX(kq);
+ if (kn->kn_fop->f_userdump != NULL)
+ (void)kn->kn_fop->f_userdump(p, kn, &kin);
+ error = SYSCTL_OUT(req, &kin, sizeof(kin));
+ maybe_yield();
+ KQ_LOCK(kq);
+ kn_leave_flux(kn);
+ return (error);
+}
+
+static int
+sysctl_kern_proc_kqueue(SYSCTL_HANDLER_ARGS)
+{
+ struct thread *td;
+ struct proc *p;
+ struct file *fp;
+ struct kqueue *kq;
+ struct knote *kn;
+ int error, i, *name;
+
+ name = (int *)arg1;
+ if ((u_int)arg2 != 2)
+ return (EINVAL);
+
+ error = pget((pid_t)name[0], PGET_HOLD | PGET_CANDEBUG, &p);
+ if (error != 0)
+ return (error);
+#ifdef COMPAT_FREEBSD32
+ if (SV_CURPROC_FLAG(SV_ILP32)) {
+ /* XXXKIB */
+ error = EOPNOTSUPP;
+ goto out1;
+ }
+#endif
+
+ td = curthread;
+ error = fget_remote(td, p, name[1] /* kqfd */, &fp);
+ if (error != 0)
+ goto out1;
+ if (fp->f_type != DTYPE_KQUEUE) {
+ error = EINVAL;
+ goto out2;
+ }
+
+ kq = fp->f_data;
+ if (req->oldptr == NULL) {
+ error = SYSCTL_OUT(req, NULL, sizeof(struct kinfo_knote) *
+ kq->kq_knlistsize * 11 / 10);
+ goto out2;
+ }
+
+ KQ_LOCK(kq);
+ for (i = 0; i < kq->kq_knlistsize; i++) {
+ SLIST_FOREACH(kn, &kq->kq_knlist[i], kn_link) {
+ error = sysctl_kern_proc_kqueue_report_one(p, req,
+ kq, kn);
+ if (error != 0)
+ goto out3;
+ }
+ }
+ if (kq->kq_knhashmask == 0)
+ goto out3;
+ for (i = 0; i <= kq->kq_knhashmask; i++) {
+ SLIST_FOREACH(kn, &kq->kq_knhash[i], kn_link) {
+ error = sysctl_kern_proc_kqueue_report_one(p, req,
+ kq, kn);
+ if (error != 0)
+ goto out3;
+ }
+ }
+out3:
+ KQ_UNLOCK_FLUX(kq);
+out2:
+ fdrop(fp, td);
+out1:
+ PRELE(p);
+ return (error);
+}
+
+static SYSCTL_NODE(_kern_proc, KERN_PROC_KQUEUE, kq,
+ CTLFLAG_RD | CTLFLAG_MPSAFE,
+ sysctl_kern_proc_kqueue, "KQueue events");
diff --git a/sys/kern/sys_eventfd.c b/sys/kern/sys_eventfd.c
--- a/sys/kern/sys_eventfd.c
+++ b/sys/kern/sys_eventfd.c
@@ -25,27 +25,25 @@
* SUCH DAMAGE.
*/
-#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/kernel.h>
-#include <sys/malloc.h>
-#include <sys/limits.h>
-#include <sys/lock.h>
-#include <sys/mutex.h>
-#include <sys/types.h>
-#include <sys/user.h>
+#include <sys/event.h>
+#include <sys/eventfd.h>
+#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/filio.h>
-#include <sys/stat.h>
-#include <sys/errno.h>
-#include <sys/event.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
#include <sys/poll.h>
#include <sys/proc.h>
-#include <sys/uio.h>
#include <sys/selinfo.h>
-#include <sys/eventfd.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/user.h>
#include <security/audit/audit.h>
diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c
--- a/sys/kern/sys_pipe.c
+++ b/sys/kern/sys_pipe.c
@@ -175,21 +175,26 @@
static int filt_pipenotsup(struct knote *kn, long hint);
static int filt_piperead(struct knote *kn, long hint);
static int filt_pipewrite(struct knote *kn, long hint);
+static int filt_pipedump(struct proc *p, struct knote *kn,
+ struct kinfo_knote *kin);
static const struct filterops pipe_nfiltops = {
.f_isfd = 1,
.f_detach = filt_pipedetach_notsup,
.f_event = filt_pipenotsup
+ /* no userdump */
};
static const struct filterops pipe_rfiltops = {
.f_isfd = 1,
.f_detach = filt_pipedetach,
- .f_event = filt_piperead
+ .f_event = filt_piperead,
+ .f_userdump = filt_pipedump,
};
static const struct filterops pipe_wfiltops = {
.f_isfd = 1,
.f_detach = filt_pipedetach,
- .f_event = filt_pipewrite
+ .f_event = filt_pipewrite,
+ .f_userdump = filt_pipedump,
};
/*
@@ -1900,3 +1905,14 @@
return (0);
}
+
+static int
+filt_pipedump(struct proc *p, struct knote *kn,
+ struct kinfo_knote *kin)
+{
+ struct pipe *pipe = kn->kn_hook;
+
+ kin->knt_extdata = KNOTE_EXTDATA_PIPE;
+ kin->knt_pipe.knt_pipe_ino = pipe->pipe_ino;
+ return (0);
+}
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -79,6 +79,7 @@
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
+#include <sys/user.h>
#include <sys/vmmeter.h>
#include <sys/vnode.h>
#include <sys/watchdog.h>
@@ -6483,7 +6484,7 @@
.f_isfd = 0,
.f_attach = filt_fsattach,
.f_detach = filt_fsdetach,
- .f_event = filt_fsevent
+ .f_event = filt_fsevent,
};
static int
@@ -6559,20 +6560,26 @@
static int filt_vfswrite(struct knote *kn, long hint);
static int filt_vfsvnode(struct knote *kn, long hint);
static void filt_vfsdetach(struct knote *kn);
+static int filt_vfsdump(struct proc *p, struct knote *kn,
+ struct kinfo_knote *kin);
+
static const struct filterops vfsread_filtops = {
.f_isfd = 1,
.f_detach = filt_vfsdetach,
- .f_event = filt_vfsread
+ .f_event = filt_vfsread,
+ .f_userdump = filt_vfsdump,
};
static const struct filterops vfswrite_filtops = {
.f_isfd = 1,
.f_detach = filt_vfsdetach,
- .f_event = filt_vfswrite
+ .f_event = filt_vfswrite,
+ .f_userdump = filt_vfsdump,
};
static const struct filterops vfsvnode_filtops = {
.f_isfd = 1,
.f_detach = filt_vfsdetach,
- .f_event = filt_vfsvnode
+ .f_event = filt_vfsvnode,
+ .f_userdump = filt_vfsdump,
};
static void
@@ -6721,6 +6728,41 @@
return (res);
}
+static int
+filt_vfsdump(struct proc *p, struct knote *kn, struct kinfo_knote *kin)
+{
+ struct vattr va;
+ struct vnode *vp;
+ char *fullpath, *freepath;
+ int error;
+
+ kin->knt_extdata = KNOTE_EXTDATA_VNODE;
+
+ vp = kn->kn_fp->f_vnode;
+ kin->knt_vnode.knt_vnode_type = vntype_to_kinfo(vp->v_type);
+
+ va.va_fsid = VNOVAL;
+ vn_lock(vp, LK_SHARED | LK_RETRY);
+ error = VOP_GETATTR(vp, &va, curthread->td_ucred);
+ VOP_UNLOCK(vp);
+ if (error != 0)
+ return (error);
+ kin->knt_vnode.knt_vnode_fsid = va.va_fsid;
+ kin->knt_vnode.knt_vnode_fileid = va.va_fileid;
+
+ freepath = NULL;
+ fullpath = "-";
+ error = vn_fullpath(vp, &fullpath, &freepath);
+ if (error == 0) {
+ strlcpy(kin->knt_vnode.knt_vnode_fullpath, fullpath,
+ sizeof(kin->knt_vnode.knt_vnode_fullpath));
+ }
+ if (freepath != NULL)
+ free(freepath, M_TEMP);
+
+ return (0);
+}
+
int
vfs_read_dirent(struct vop_readdir_args *ap, struct dirent *dp, off_t off)
{
diff --git a/sys/sys/event.h b/sys/sys/event.h
--- a/sys/sys/event.h
+++ b/sys/sys/event.h
@@ -262,12 +262,17 @@
#define EVENT_REGISTER 1
#define EVENT_PROCESS 2
+struct kinfo_knote;
+struct proc;
+
struct filterops {
int f_isfd; /* true if ident == filedescriptor */
int (*f_attach)(struct knote *kn);
void (*f_detach)(struct knote *kn);
int (*f_event)(struct knote *kn, long hint);
void (*f_touch)(struct knote *kn, struct kevent *kev, u_long type);
+ int (*f_userdump)(struct proc *p, struct knote *kn,
+ struct kinfo_knote *kin);
};
/*
diff --git a/sys/sys/sysctl.h b/sys/sys/sysctl.h
--- a/sys/sys/sysctl.h
+++ b/sys/sys/sysctl.h
@@ -1041,6 +1041,7 @@
#define KERN_PROC_SIGFASTBLK 44 /* address of fastsigblk magic word */
#define KERN_PROC_VM_LAYOUT 45 /* virtual address space layout info */
#define KERN_PROC_RLIMIT_USAGE 46 /* array of rlim_t */
+#define KERN_PROC_KQUEUE 47 /* array of struct kinfo_knote */
/*
* KERN_IPC identifiers
diff --git a/sys/sys/user.h b/sys/sys/user.h
--- a/sys/sys/user.h
+++ b/sys/sys/user.h
@@ -38,6 +38,7 @@
#ifndef _KERNEL
/* stuff that *used* to be included by user.h, or is now needed */
#include <sys/errno.h>
+#include <sys/event.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/ucred.h>
@@ -665,6 +666,33 @@
uintptr_t kvm_spare[12];
};
+#define KNOTE_STATUS_ACTIVE 0x00000001
+#define KNOTE_STATUS_QUEUED 0x00000002
+#define KNOTE_STATUS_DISABLED 0x00000004
+#define KNOTE_STATUS_DETACHED 0x00000008
+#define KNOTE_STATUS_KQUEUE 0x00000010
+
+#define KNOTE_EXTDATA_NONE 0
+#define KNOTE_EXTDATA_VNODE 1
+#define KNOTE_EXTDATA_PIPE 2
+
+struct kinfo_knote {
+ struct kevent knt_event;
+ int knt_status;
+ int knt_extdata;
+ union {
+ struct {
+ int knt_vnode_type;
+ uint64_t knt_vnode_fsid;
+ uint64_t knt_vnode_fileid;
+ char knt_vnode_fullpath[PATH_MAX];
+ } knt_vnode;
+ struct {
+ ino_t knt_pipe_ino;
+ } knt_pipe;
+ };
+};
+
#ifdef _KERNEL
/* Flags for kern_proc_out function. */
#define KERN_PROC_NOTHREADS 0x1
diff --git a/sys/vm/sg_pager.c b/sys/vm/sg_pager.c
--- a/sys/vm/sg_pager.c
+++ b/sys/vm/sg_pager.c
@@ -34,6 +34,7 @@
*/
#include <sys/param.h>
+#include <sys/event.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/rwlock.h>
diff --git a/usr.bin/procstat/Makefile b/usr.bin/procstat/Makefile
--- a/usr.bin/procstat/Makefile
+++ b/usr.bin/procstat/Makefile
@@ -11,6 +11,7 @@
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.h b/usr.bin/procstat/procstat.h
--- a/usr.bin/procstat/procstat.h
+++ b/usr.bin/procstat/procstat.h
@@ -64,6 +64,7 @@
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.1 b/usr.bin/procstat/procstat.1
--- a/usr.bin/procstat/procstat.1
+++ b/usr.bin/procstat/procstat.1
@@ -159,16 +159,24 @@
.Va pid
list can be used to limit the display of the locks, mostly because
some types of locks do not have local (or any) owning processes.
+.It Ar argument(s) | Fl c
+Display command line arguments for the process.
+.Pp
+Substring commands are accepted.
+.It Ar auxv | Fl x
+Display ELF auxiliary vector for the process.
.It Ar basic
Print basic process statistics (this is the default).
.It Ar binary | Fl b
Display binary information for the process.
.Pp
Substring commands are accepted.
-.It Ar argument(s) | Fl c
-Display command line arguments for the process.
+.It Ar credential(s) | Fl s
+Display security credential information for the process.
.Pp
Substring commands are accepted.
+.It Ar cpuset | Ar cs | Fl S
+Display the cpuset information for the thread.
.It Ar environment | Fl e
Display environment variables for the process.
.Pp
@@ -179,36 +187,67 @@
If the
.Fl C
subcommand flag is used then additional capability information is printed.
-.It Ar signal(s) | Fl i
-Display signal pending and disposition information for the process.
-.Pp
+.It Ar kqueue(s) Op Fl v
+Display the events registered in the process kqueues.
+The filter name, filter-specific identifier, flags, filter-specific flags,
+system and user data, and event status are displayed.
If the
-.Fl n
-subcommand option is used, the signal numbers are shown instead of signal
-names.
+.Fl v
+verbose flag is provided to the subcommand, the values of the ext array
+are displayed as well.
.Pp
-Substring commands are accepted.
-.It Ar tsignal(s) | Fl j
-Display signal pending and blocked information for the process's threads.
+For flags, the string consisting of the following symbols corresponding
+to the set flags, is printed:
+.Bl -tag -width X -compact
+.It O
+.Va EV_ONESHOT
+.It C
+.Va EV_CLEAR
+.It R
+.Va EV_RECEIPT
+.It D
+.Va EV_DISPATCH
+.It d
+.Va EV_DROP
+.It 1
+.Va EV_FLAG1
+.It 2
+.Va EV_FLAG2
+.El
.Pp
-If the
-.Fl n
-subcommand option is used, the signal numbers are shown instead of signal
-names.
-.Pp
-Substring commands are accepted.
+For status:
+.Bl -tag -width X -compact
+.It A
+.Va KNOTE_STATUS_ACTIVE
+.It A
+.Va KNOTE_STATUS_QUEUED
+.It D
+.Va KNOTE_STATUS_DISABLED
+.It d
+.Va KNOTE_STATUS_DETACHED
+.It K
+.Va KNOTE_STATUS_KQUEUE
+.El
.It Ar kstack | Fl k
Display the stacks of kernel threads in the process, excluding stacks of
-threads currently running on a CPU and threads with stacks swapped to disk.
+threads currently running on a CPU in userspace.
.Pp
If the
.Fl v
subcommand option is used (or the command flag is repeated), function
offsets as well as function names are printed.
-.It Ar rlimit | Fl l
-Display resource limits for the process.
+.It Ar pargs
+Display arguments for the process.
+.It Ar penv
+Display environment variables for the process.
.It Ar ptlwpinfo | Fl L
Display LWP info for the process pertaining to its signal driven exit.
+.It Ar pwdx
+Display current working directory for the process.
+.It Ar rlimit | Fl l
+Display resource limits for the process.
+.It Ar rlimitusage
+Display the usage of the resource limits for the process.
.It Ar rusage | Fl r
Display resource usage information for the process.
.Pp
@@ -220,24 +259,28 @@
statistics.
The second field in the table will list the thread ID to which the row of
information corresponds.
-.It Ar credential(s) | Fl s
-Display security credential information for the process.
+.It Ar signal(s) | Fl i
+Display signal pending and disposition information for the process.
+.Pp
+If the
+.Fl n
+subcommand option is used, the signal numbers are shown instead of signal
+names.
.Pp
Substring commands are accepted.
-.It Ar cpuset | Ar cs | Fl S
-Display the cpuset information for the thread.
.It Ar thread(s) | Fl t
Display thread information for the process.
+.It Ar tsignal(s) | Fl j
+Display signal pending and blocked information for the process's threads.
+.Pp
+If the
+.Fl n
+subcommand option is used, the signal numbers are shown instead of signal
+names.
+.Pp
+Substring commands are accepted.
.It Ar vm | Fl v
Display virtual memory mappings for the process.
-.It Ar auxv | Fl x
-Display ELF auxiliary vector for the process.
-.It Ar pargs
-Display arguments for the process.
-.It Ar penv
-Display environment variables for the process.
-.It Ar pwdx
-Display current working directory for the process.
.El
.Pp
All options generate output in the format of a table, the first field of
diff --git a/usr.bin/procstat/procstat.c b/usr.bin/procstat/procstat.c
--- a/usr.bin/procstat/procstat.c
+++ b/usr.bin/procstat/procstat.c
@@ -65,6 +65,7 @@
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 @@
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 @@
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_kqueue.c b/usr.bin/procstat/procstat_kqueue.c
new file mode 100644
--- /dev/null
+++ b/usr.bin/procstat/procstat_kqueue.c
@@ -0,0 +1,322 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * This software was developed by Konstantin Belousov <kib@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 *bres, *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 (res);
+ asprintf(&bres, "(%s)", res);
+ free(res);
+ return (bres);
+}
+
+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/%#10x%s} ", kni->knt_event.flags, flags);
+ xo_emit("{:fflags/%#10x%s} ", kni->knt_event.fflags, 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/%#10x%s}\n", (uintmax_t)kni->knt_status,
+ 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);
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Feb 11, 12:57 PM (12 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28651664
Default Alt Text
D49163.id152062.diff (29 KB)

Event Timeline