Date: Tue, 28 Jul 2026 23:35:09 +0000 From: Devin Teske <dteske@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 434283fda99e - main - dwatch: add nine diagnostic modules; grow errno, io, proc, sched Message-ID: <6a693cad.3708f.6ece9ba5@gitrepo.freebsd.org>
index | next in thread | raw e-mail
The branch main has been updated by dteske: URL: https://cgit.FreeBSD.org/src/commit/?id=434283fda99e89af20c3fe95fde427624ae96d04 commit 434283fda99e89af20c3fe95fde427624ae96d04 Author: Devin Teske <dteske@FreeBSD.org> AuthorDate: 2026-07-28 23:32:57 +0000 Commit: Devin Teske <dteske@FreeBSD.org> CommitDate: 2026-07-28 23:32:57 +0000 dwatch: add nine diagnostic modules; grow errno, io, proc, sched Grow the module collection to answer, each with a single command, the first questions asked when diagnosing a sick system: why is my application stalling, where is the kernel fighting over locks, what file could it not find, why is this process getting EPERM, what killed my process, will that fatal signal actually leave a core behind, what was my process stuck on, where is my kernel memory going, who is creating or entering jails, is the network slow because TCP is resending, how long did my thread wait to run, and is the disk itself slow. Every module keeps to the house style: invocation-name overloading through hard links, predicate-only D with inline lookup tables (no if-statements), and stable providers only (syscall, proc, sched, io, dtmalloc, and the lockstat, vfs, priv, and mib SDT providers), so the modules remain drop-in compatible with older releases (the one documented exception is noted below). No kernel changes: new and extended profiles under cddl/usr.sbin/dwatch/libexec plus one libdtrace inline table (priv.d). slow (slow-fsync, slow-open, slow-read, slow-syscall, slow-write, or any slow-NAME by new link) records syscall entry timestamps in thread-local storage and prints, at return, any call whose latency meets a threshold (DWATCH_SLOW_MS, default 100), naming the syscall, the elapsed time to the microsecond, and any errno returned. The bare profile watches a curated set of filesystem-related calls expected to be fast; slow-syscall watches everything; unrecognized invocation names fall through to syscall::NAME:return with the matching entry probe derived mechanically from the return probe list. lock (lock-adaptive, lock-block, lock-lockmgr, lock-rw, lock-spin, lock-sx, lock-thread) rides the dtrace_lockstat(4) block and spin probes, printing the held-off thread (free from the standard event tag), the holdoff duration, the lock class, the lo_name of the lock through a single cast of arg0 to struct lock_object (the first member of every kernel lock), and reader/writer intent on the probes that report it. Holdoffs shorter than DWATCH_LOCK_MS (default 1; 0 shows everything) are suppressed. namei (namei-enoent, namei-entry, namei-failure) records the pathname at vfs:namei:lookup:entry and reports it with the result at return. Unlike the vop_lookup profile, which reconstructs paths from the name cache one component at a time, this sees the whole path exactly as the process requested it. namei-enoent hunts file-not-found storms -- the single most common use of truss(1) -- without stopping the victim. priv (priv-err, priv-ok) watches priv_check(9) verdicts, naming the exact privilege denied -- something no syscall tracer can see, because by the time EPERM surfaces the priv(9) value is gone. The number is decoded by priv_string[], a new libdtrace inline table in the errno.d and signal.d tradition, mechanically generated from sys/priv.h (247 entries) and installed to /usr/lib/dtrace where dtrace(1) auto-loads it; on older releases it is a drop-in file like the module itself. coredump (coredump-top) watches for delivery of signals whose default action produces a core, per the SIGPROP_CORE entries of the sigproptbl in kern_sig.c, and renders a verdict the same way and in the same order the kernel will decide it: ignored or caught per the target's struct sigacts, then the coredump() gauntlet of kern.coredump, kern.sugid_coredump vs P_SUGID, procctl(2) PROC_TRACE_CTL, and RLIMIT_CORE -- the sysctl knobs read live through kernel globals. Where a coredump-worthy signal will produce no core, the verdict says precisely which policy ate it. coredump-top maintains a cumulative catalog of coredump-worthy signals by process and signal, refreshed every 3 seconds in the style of systop; combine the event profile with `-O cmd' to capture state as each event occurs. hang (hang-top) pairs sched:::sleep with sched:::wakeup through a tid-keyed timestamp array and prints, as each thread wakes, any sleep that meets a threshold (DWATCH_HANG_MS, default 1000), naming the sleeper in the details and the waker in the standard event tag. This is the blocking the slow module structurally cannot see: a syscall that never returns never reports its latency, while hang reports the moment the wait ends, with the full duration. hang-top maintains a cumulative catalog of long sleeps by process (count and maximum) in the style of coredump-top. jail (jail-attach, jail-get, jail-remove, jail-set) watches the jail management plane -- jail(2), jail_set(2), jail_get(2), jail_attach(2), and jail_remove(2) -- naming the operation, the jail id (taken from the entry argument for attach/remove, from the return value for the others), and any errno. Complements the dwatch `-j jail' filter, which scopes any profile to processes inside one jail; this watches who manipulates jails, from any jail or none. dtmalloc (dtmalloc-top, or any dtmalloc-NAME by new link) rides the dtmalloc provider (one malloc and one free probe per malloc(9) type). The event profile prints allocations and frees meeting a size threshold (DWATCH_MALLOC_MIN, default 65536) -- who is allocating huge kernel buffers. dtmalloc-top maintains a running catalog of net bytes and outstanding allocation balance by type, sorted by net bytes so leak suspects rise: a type that climbs without bound while the system is in steady state is the suspect. The catalog reflects activity since the watch began, and is honest about caches holding what they allocate. mib (tcp-retransmit, or any mib-NAME by new link) rides the per-counter mib SDT probes of the network stack. The tcp-retransmit profile curates the counters that signal send-path congestion or loss -- data packet retransmissions, unnecessary retransmissions, retransmit timer expirations, and connections dropped by retransmit exhaustion -- decoded through an inline description table, answering "is this network slow because TCP is resending?" as events with process context rather than netstat(1) deltas. NB: the mib probes exist only in kernels built with options KDTRACE_MIB_SDT (default in -CURRENT via std.debug); the module documents this and dtrace(1) refuses the script elsewhere, making the dependency self-announcing. Four existing modules gain personalities. proc grows proc-signal-fatal, filtering signal-send to signals whose default disposition terminates the receiver, most-notably including kernel-generated SIGSEGV/SIGBUS/SIGILL/SIGFPE that no kill(2) watcher will ever see. errno now reads its invocation name: errno-NAME shows only syscalls returning that errno, where NAME is a symbolic name from errno.d or a number; links are installed for errno-EACCES, errno-ECAPMODE, errno-ENOENT, errno-ENOTCAPABLE, and errno-EPERM (the latter pairs covering capsicum(4) capability-mode violations), and any other errno needs only a new link. sched grows sched-latency, recording a timestamp at sched:::enqueue keyed by tid and printing at sched:::on-cpu any run-queue wait meeting a threshold (DWATCH_SCHED_MS, default 10) -- the literal measurement of scheduler delay on a system with idle CPU that still feels sluggish. io grows io-slow, pairing io:::start with io:::done through a bio-keyed timestamp array and printing any request that meets a threshold (DWATCH_IO_MS, default 100), naming the device, command, size, and elapsed time; watched against zvols and a pool's leaf vdevs this brackets where in a ZFS stack the time is going, without touching unstable providers. Document all of the above plus the DWATCH_HANG_MS, DWATCH_IO_MS, DWATCH_LOCK_MS, DWATCH_MALLOC_MIN, DWATCH_SCHED_MS, and DWATCH_SLOW_MS knobs in dwatch(1). All 46 new invocation names were exercised through `dwatch -d' with a profile-path sandbox emulating the installed hard links: every one sources cleanly and emits the intended D -- probe selection per alias, entry/return and sleep/wakeup pairing through thread-local and global associative arrays, threshold and mask predicates picking up their knobs, aggregation clauses and printa column layout in the -top profiles, multi-line predicate rendering, and `-t' correctly displacing each module's default test were verified by inspection of the generated scripts. Invocations untouched by this pass generate D identical to their previous output. Modules pass sh -n, fit 80 columns, and dwatch.1 passes mandoc -Tlint with no new warnings. A validation harness performing a `dwatch -e' compile per profile against the live kernel globs every staged profile for runs wherever the dtrace device is present. Reviewed by: markj Differential Revision: https://reviews.freebsd.org/D58093 --- cddl/lib/libdtrace/Makefile | 6 +- cddl/lib/libdtrace/mkpriv | 119 +++++++++++++++++++ cddl/usr.sbin/dwatch/dwatch.1 | 105 ++++++++++++++++- cddl/usr.sbin/dwatch/libexec/Makefile | 42 +++++++ cddl/usr.sbin/dwatch/libexec/coredump | 210 ++++++++++++++++++++++++++++++++++ cddl/usr.sbin/dwatch/libexec/dtmalloc | 146 +++++++++++++++++++++++ cddl/usr.sbin/dwatch/libexec/errno | 15 ++- cddl/usr.sbin/dwatch/libexec/hang | 174 ++++++++++++++++++++++++++++ cddl/usr.sbin/dwatch/libexec/io | 72 +++++++++++- cddl/usr.sbin/dwatch/libexec/jail | 92 +++++++++++++++ cddl/usr.sbin/dwatch/libexec/lock | 136 ++++++++++++++++++++++ cddl/usr.sbin/dwatch/libexec/mib | 78 +++++++++++++ cddl/usr.sbin/dwatch/libexec/namei | 84 ++++++++++++++ cddl/usr.sbin/dwatch/libexec/priv | 59 ++++++++++ cddl/usr.sbin/dwatch/libexec/proc | 23 +++- cddl/usr.sbin/dwatch/libexec/sched | 65 ++++++++++- cddl/usr.sbin/dwatch/libexec/slow | 131 +++++++++++++++++++++ 17 files changed, 1544 insertions(+), 13 deletions(-) diff --git a/cddl/lib/libdtrace/Makefile b/cddl/lib/libdtrace/Makefile index fbc6d5fbbec2..08836a91b164 100644 --- a/cddl/lib/libdtrace/Makefile +++ b/cddl/lib/libdtrace/Makefile @@ -55,6 +55,7 @@ DSRCS= cam.d \ errno.d \ io.d \ ip.d \ + priv.d \ psinfo.d \ sctp.d \ siftr.d \ @@ -150,7 +151,7 @@ LDFLAGS+= -fsanitize=address -fsanitize=undefined LIBADD= ctf elf proc pthread rtld_db xo -CLEANFILES= dt_errtags.c dt_names.c +CLEANFILES= dt_errtags.c dt_names.c priv.d .include <bsd.lib.mk> @@ -159,3 +160,6 @@ dt_errtags.c: ${OPENSOLARIS_USR_DISTDIR}/lib/libdtrace/common/dt_errtags.h dt_names.c: ${OPENSOLARIS_SYS_DISTDIR}/uts/common/sys/dtrace.h sh ${OPENSOLARIS_USR_DISTDIR}/lib/libdtrace/common/mknames.sh < ${.ALLSRC} > ${.TARGET} + +priv.d: mkpriv ${SRCTOP}/sys/sys/priv.h + sh ${.CURDIR}/mkpriv -o ${.TARGET} ${SRCTOP}/sys/sys/priv.h diff --git a/cddl/lib/libdtrace/mkpriv b/cddl/lib/libdtrace/mkpriv new file mode 100755 index 000000000000..d213161f8356 --- /dev/null +++ b/cddl/lib/libdtrace/mkpriv @@ -0,0 +1,119 @@ +#!/bin/sh +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ LICENSE +# +# Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org> +# +# SPDX-License-Identifier: BSD-2-Clause +# +############################################################ IDENT(1) +# +# $Title: Generate priv.d from <sys/priv.h> for libdtrace(3) $ +# $Copyright: 2026 Devin Teske. All rights reserved. $ +# +############################################################ GLOBALS + +VERSION='$Version: 1.0 $' + +pgm="${0##*/}" # Program basename + +# +# Global exit status +# +SUCCESS=0 +FAILURE=1 + +# +# Command-line options +# +OUTPUT_FILE= # -o file + +# +# Miscellaneous +# +PRIV_H= + +############################################################ FUNCTIONS + +usage() +{ + local fmt="$1" + local optfmt="\t%-5s %s\n" + + exec >&2 + if [ "$fmt" ]; then + shift 1 # fmt + printf "%s: $fmt\n" "$pgm" "$@" + fi + printf "Usage: %s [-hv] [-o file] priv.h\n" "$pgm" + printf "Options:\n" + printf "$optfmt" "-h" "Print this usage statment and exit." + printf "$optfmt" "-o file" "Write to output file instead of stdout." + printf "$optfmt" "-v" "Print version information and exit." + exit $FAILURE +} + +############################################################ MAIN + +# +# Process command-line options +# +while getopts ho:v flag; do + case "$flag" in + o) OUTPUT_FILE="$OPTARG" ;; + v) VERSION="${VERSION#*: }" + echo "${VERSION% $}" + exit $SUCCESS ;; + *) usage # NOTREACHED + esac +done +shift $(( $OPTIND - 1 )) + +# +# Check command-line arguments +# +[ $# -gt 0 ] || usage "Too few arguments" # NOTREACHED +[ $# -eq 1 ] || usage "Too many arguments" # NOTREACHED +[ "$1" ] || usage "Missing priv.h argument" # NOTREACHED +[ -e "$1" ] || usage "%s: No such file or directory" "$1" # NOTREACHED + +# +# Generate priv.d contents from priv.h input +# +PRIV_H="$1" +exec 9<<PRIV_D_EOF +/*- + * Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* + * Map priv(9) privilege values to their symbolic names. + * NB: Mechanically generated from <sys/priv.h> -- keep in-sync. + * NB: Generated by /usr/src/cddl/lib/libdtrace/$pgm + */ + +#pragma D binding "1.13" priv_string +inline string priv_string[int priv] = +$( awk ' + /^#define[[:space:]]+PRIV_[A-Z0-9_]+[[:space:]]+[0-9]+/ { + for (i = 0; ++i <= NF; ) if ($i ~ /define/) break + name = $++i + value = $++i + printf "\tpriv == %s ?\t\t\"%s\" :\n", value, name + }' "$PRIV_H" +) + strjoin("PRIV_UNKNOWN#", lltostr(priv)); +PRIV_D_EOF + +# +# Output contents +# +[ ! "$OUTPUT_FILE" ] || exec > "$OUTPUT_FILE" +cat <&9 + +################################################################################ +# END +################################################################################ diff --git a/cddl/usr.sbin/dwatch/dwatch.1 b/cddl/usr.sbin/dwatch/dwatch.1 index 81efb2c2c437..8c201d970620 100644 --- a/cddl/usr.sbin/dwatch/dwatch.1 +++ b/cddl/usr.sbin/dwatch/dwatch.1 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2014-2018 Devin Teske +.\" Copyright (c) 2014-2026 Devin Teske .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without @@ -22,7 +22,7 @@ .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd April 18, 2025 +.Dd July 4, 2026 .Dt DWATCH 1 .Os .Sh NAME @@ -382,17 +382,69 @@ Print mode and path from .Xr chmod 2 , .Xr lchmod 2 , .Xr fchmodat 2 +.It coredump +Print coredump-worthy signal deliveries with a verdict of whether a +core will actually be written +.It coredump-top +Maintain a running catalog of coredump-worthy signals by process and +signal +.It dtmalloc +Print kernel +.Xr malloc 9 +and +.Xr free 9 +activity by malloc type, +meeting a size threshold +.It dtmalloc-top +Maintain a running catalog of net bytes and allocation balance by +malloc type +.Pq leak suspects rise .It errno Print non-zero errno results from system calls +.It errno- Ns Ar NAME +Print system calls returning the given errno, +by symbolic name or number +.Pq e.g., errno-ENOENT, errno-ENOTCAPABLE, errno-13 +.It hang +Print threads that were asleep longer than a threshold, +as they wake, +naming the sleeper and the waker +.It hang-top +Maintain a running catalog of long sleeps by process .It io Print disk I/O details provided by .Xr dtrace_io 4 +.It io-slow +Print disk I/O requests exceeding a latency threshold .It ip Print IPv4 and IPv6 details provided by .Xr dtrace_ip 4 +.It jail +Print jail management activity from +.Xr jail 2 , +.Xr jail_set 2 , +.Xr jail_get 2 , +.Xr jail_attach 2 , +.Xr jail_remove 2 .It kill Print signal and pid from .Xr kill 2 +.It lock +Print kernel lock contention details provided by +.Xr dtrace_lockstat 4 +.It lock-spin +Print kernel lock spinning details provided by +.Xr dtrace_lockstat 4 +.It mib +Print network stack statistics counters as they increment +.Pq requires a kernel built with options KDTRACE_MIB_SDT +.It namei +Print pathname resolution details provided by the +.Fn vfs:namei:lookup +probes +.It namei-failure +Print failed pathname resolutions +.Pq also namei-enoent for ENOENT only .It nanosleep Print requested time from .Xr nanosleep 2 @@ -400,12 +452,19 @@ Print requested time from Print path from .Xr open 2 , .Xr openat 2 +.It priv +Print privilege requests denied by +.Xr priv_check 9 +.Pq also priv-ok for those granted .It proc Print process execution details provided by .Xr dtrace_proc 4 .It proc-signal Print process signal details provided by .Xr dtrace_proc 4 +.It proc-signal-fatal +Print only signals whose default disposition is to terminate the +receiving process .It rw Print buffer contents from .Xr read 2 , @@ -413,12 +472,24 @@ Print buffer contents from .It sched Print CPU scheduling details provided by .Xr dtrace_sched 4 +.It sched-latency +Print threads that waited longer than a threshold between becoming +runnable and running +.It slow +Print filesystem-related syscalls exceeding a latency threshold +.It slow-syscall +Print any syscall exceeding a latency threshold .It tcp Print TCP address/port details provided by .Xr dtrace_tcp 4 .It tcp-io Print TCP I/O details provided by .Xr dtrace_tcp 4 +.It tcp-retransmit +Print TCP retransmissions, +retransmit timeouts, +and connections dropped by retransmit exhaustion +.Pq requires a kernel built with options KDTRACE_MIB_SDT .It udp Print UDP I/O details provided by .Xr dtrace_udp 4 @@ -454,6 +525,26 @@ Print symlink paths being created by These environment variables affect the execution of .Nm : .Bl -tag -width "DWATCH_PROFILES_PATH" +.It Ev DWATCH_HANG_MS +Minimum sleep duration, +in milliseconds, +displayed by the hang profiles +.Pq default 1000; 0 to show everything . +.It Ev DWATCH_IO_MS +Minimum disk I/O request latency, +in milliseconds, +displayed by the io-slow profile +.Pq default 100; 0 to show everything . +.It Ev DWATCH_LOCK_MS +Minimum lock holdoff duration, +in milliseconds, +displayed by the lock profiles +.Pq default 1; 0 to show everything . +.It Ev DWATCH_MALLOC_MIN +Minimum allocation size, +in bytes, +displayed by the dtmalloc profiles +.Pq default 65536; 0 to show everything . .It Ev DWATCH_PROFILES_PATH If .Ev DWATCH_PROFILES_PATH @@ -464,6 +555,16 @@ variable instead of the default .Ql Li /usr/libexec/dwatch:/usr/local/libexec/dwatch . If set to NULL, profiles are not loaded. +.It Ev DWATCH_SCHED_MS +Minimum run-queue wait, +in milliseconds, +displayed by the sched-latency profile +.Pq default 10; 0 to show everything . +.It Ev DWATCH_SLOW_MS +Minimum syscall latency, +in milliseconds, +displayed by the slow profiles +.Pq default 100 . .El .Sh EXIT STATUS .Ex -std diff --git a/cddl/usr.sbin/dwatch/libexec/Makefile b/cddl/usr.sbin/dwatch/libexec/Makefile index 72ea5be686fb..d32c54c34c83 100644 --- a/cddl/usr.sbin/dwatch/libexec/Makefile +++ b/cddl/usr.sbin/dwatch/libexec/Makefile @@ -1,16 +1,25 @@ PACKAGE= dwatch FILESDIR= ${LIBEXECDIR}/dwatch FILES= chmod \ + coredump \ + dtmalloc \ errno \ + hang \ io \ ip \ + jail \ kill \ + lock \ + mib \ + namei \ nanosleep \ open \ + priv \ proc \ rw \ sched \ sendrecv \ + slow \ systop \ tcp \ udp \ @@ -23,11 +32,37 @@ FILES= chmod \ LINKMODE= ${SHAREMODE} LINKS= ${LIBEXECDIR}/dwatch/chmod ${LIBEXECDIR}/dwatch/fchmodat LINKS+= ${LIBEXECDIR}/dwatch/chmod ${LIBEXECDIR}/dwatch/lchmod +LINKS+= ${LIBEXECDIR}/dwatch/coredump ${LIBEXECDIR}/dwatch/coredump-top +LINKS+= ${LIBEXECDIR}/dwatch/dtmalloc ${LIBEXECDIR}/dwatch/dtmalloc-top +LINKS+= ${LIBEXECDIR}/dwatch/errno ${LIBEXECDIR}/dwatch/errno-EACCES +LINKS+= ${LIBEXECDIR}/dwatch/errno ${LIBEXECDIR}/dwatch/errno-ECAPMODE +LINKS+= ${LIBEXECDIR}/dwatch/errno ${LIBEXECDIR}/dwatch/errno-ENOENT +LINKS+= ${LIBEXECDIR}/dwatch/errno ${LIBEXECDIR}/dwatch/errno-ENOTCAPABLE +LINKS+= ${LIBEXECDIR}/dwatch/errno ${LIBEXECDIR}/dwatch/errno-EPERM +LINKS+= ${LIBEXECDIR}/dwatch/hang ${LIBEXECDIR}/dwatch/hang-top LINKS+= ${LIBEXECDIR}/dwatch/io ${LIBEXECDIR}/dwatch/io-done +LINKS+= ${LIBEXECDIR}/dwatch/io ${LIBEXECDIR}/dwatch/io-slow LINKS+= ${LIBEXECDIR}/dwatch/io ${LIBEXECDIR}/dwatch/io-start LINKS+= ${LIBEXECDIR}/dwatch/ip ${LIBEXECDIR}/dwatch/ip-receive LINKS+= ${LIBEXECDIR}/dwatch/ip ${LIBEXECDIR}/dwatch/ip-send +LINKS+= ${LIBEXECDIR}/dwatch/jail ${LIBEXECDIR}/dwatch/jail-attach +LINKS+= ${LIBEXECDIR}/dwatch/jail ${LIBEXECDIR}/dwatch/jail-get +LINKS+= ${LIBEXECDIR}/dwatch/jail ${LIBEXECDIR}/dwatch/jail-remove +LINKS+= ${LIBEXECDIR}/dwatch/jail ${LIBEXECDIR}/dwatch/jail-set +LINKS+= ${LIBEXECDIR}/dwatch/lock ${LIBEXECDIR}/dwatch/lock-adaptive +LINKS+= ${LIBEXECDIR}/dwatch/lock ${LIBEXECDIR}/dwatch/lock-block +LINKS+= ${LIBEXECDIR}/dwatch/lock ${LIBEXECDIR}/dwatch/lock-lockmgr +LINKS+= ${LIBEXECDIR}/dwatch/lock ${LIBEXECDIR}/dwatch/lock-rw +LINKS+= ${LIBEXECDIR}/dwatch/lock ${LIBEXECDIR}/dwatch/lock-spin +LINKS+= ${LIBEXECDIR}/dwatch/lock ${LIBEXECDIR}/dwatch/lock-sx +LINKS+= ${LIBEXECDIR}/dwatch/lock ${LIBEXECDIR}/dwatch/lock-thread +LINKS+= ${LIBEXECDIR}/dwatch/mib ${LIBEXECDIR}/dwatch/tcp-retransmit +LINKS+= ${LIBEXECDIR}/dwatch/namei ${LIBEXECDIR}/dwatch/namei-enoent +LINKS+= ${LIBEXECDIR}/dwatch/namei ${LIBEXECDIR}/dwatch/namei-entry +LINKS+= ${LIBEXECDIR}/dwatch/namei ${LIBEXECDIR}/dwatch/namei-failure LINKS+= ${LIBEXECDIR}/dwatch/open ${LIBEXECDIR}/dwatch/openat +LINKS+= ${LIBEXECDIR}/dwatch/priv ${LIBEXECDIR}/dwatch/priv-err +LINKS+= ${LIBEXECDIR}/dwatch/priv ${LIBEXECDIR}/dwatch/priv-ok LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-create LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-exec LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-exec-failure @@ -36,6 +71,7 @@ LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-exit LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-signal LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-signal-clear LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-signal-discard +LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-signal-fatal LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-signal-send LINKS+= ${LIBEXECDIR}/dwatch/proc ${LIBEXECDIR}/dwatch/proc-status LINKS+= ${LIBEXECDIR}/dwatch/rw ${LIBEXECDIR}/dwatch/read @@ -45,6 +81,7 @@ LINKS+= ${LIBEXECDIR}/dwatch/sched ${LIBEXECDIR}/dwatch/sched-cpu LINKS+= ${LIBEXECDIR}/dwatch/sched ${LIBEXECDIR}/dwatch/sched-dequeue LINKS+= ${LIBEXECDIR}/dwatch/sched ${LIBEXECDIR}/dwatch/sched-enqueue LINKS+= ${LIBEXECDIR}/dwatch/sched ${LIBEXECDIR}/dwatch/sched-exec +LINKS+= ${LIBEXECDIR}/dwatch/sched ${LIBEXECDIR}/dwatch/sched-latency LINKS+= ${LIBEXECDIR}/dwatch/sched ${LIBEXECDIR}/dwatch/sched-lend-pri LINKS+= ${LIBEXECDIR}/dwatch/sched ${LIBEXECDIR}/dwatch/sched-load-change LINKS+= ${LIBEXECDIR}/dwatch/sched ${LIBEXECDIR}/dwatch/sched-off-cpu @@ -63,6 +100,11 @@ LINKS+= ${LIBEXECDIR}/dwatch/sendrecv ${LIBEXECDIR}/dwatch/recvmsg LINKS+= ${LIBEXECDIR}/dwatch/sendrecv ${LIBEXECDIR}/dwatch/send LINKS+= ${LIBEXECDIR}/dwatch/sendrecv ${LIBEXECDIR}/dwatch/sendmsg LINKS+= ${LIBEXECDIR}/dwatch/sendrecv ${LIBEXECDIR}/dwatch/sendto +LINKS+= ${LIBEXECDIR}/dwatch/slow ${LIBEXECDIR}/dwatch/slow-fsync +LINKS+= ${LIBEXECDIR}/dwatch/slow ${LIBEXECDIR}/dwatch/slow-open +LINKS+= ${LIBEXECDIR}/dwatch/slow ${LIBEXECDIR}/dwatch/slow-read +LINKS+= ${LIBEXECDIR}/dwatch/slow ${LIBEXECDIR}/dwatch/slow-syscall +LINKS+= ${LIBEXECDIR}/dwatch/slow ${LIBEXECDIR}/dwatch/slow-write LINKS+= ${LIBEXECDIR}/dwatch/tcp ${LIBEXECDIR}/dwatch/tcp-accept LINKS+= ${LIBEXECDIR}/dwatch/tcp ${LIBEXECDIR}/dwatch/tcp-accept-established LINKS+= ${LIBEXECDIR}/dwatch/tcp ${LIBEXECDIR}/dwatch/tcp-accept-refused diff --git a/cddl/usr.sbin/dwatch/libexec/coredump b/cddl/usr.sbin/dwatch/libexec/coredump new file mode 100644 index 000000000000..0b68564691de --- /dev/null +++ b/cddl/usr.sbin/dwatch/libexec/coredump @@ -0,0 +1,210 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for coredump-worthy signal delivery $ +# $Copyright: 2026 Devin Teske. All rights reserved. $ +# +############################################################ DESCRIPTION +# +# Print signals whose default action produces a coredump, as they are +# sent, together with a verdict of whether a core will actually be +# written, rendered the same way and in the same order the kernel will +# decide it: the target may be ignoring or catching the signal, dumps +# may be disabled (kern.coredump), the target may be flagged sugid +# (kern.sugid_coredump) or may have disabled tracing via procctl(2) +# PROC_TRACE_CTL, or RLIMIT_CORE may be 0. +# Coredump-worthy is defined by the SIGPROP_CORE entries of the +# sigproptbl in kern_sig.c: SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGEMT, +# SIGFPE, SIGBUS, SIGSEGV, and SIGSYS. +# The coredump-top profile maintains a running catalog, updated +# every 3 seconds, of coredump-worthy signals by process and signal. +# Combine with `-O cmd' to capture state as each event occurs. +# +############################################################ PRAGMAS + +case "$PROFILE" in +coredump-top) + DTRACE_PRAGMA=" + option quiet + option aggsortrev + " # END-QUOTE + ;; +esac + +############################################################ PROBE + +case "$PROFILE" in +coredump-top) + : ${PROBE:=profile:::tick-3s} ;; +*) + : ${PROBE:=proc:::signal-send} +esac + +############################################################ GLOBALS + +exec 9<<EOF +/* + * Signals whose default action includes a coredump, per the + * SIGPROP_CORE entries of the sigproptbl in kern_sig.c of FreeBSD + */ +inline int coredump_sig[int sig] = + sig == SIGQUIT ? 1 : + sig == SIGILL ? 1 : + sig == SIGTRAP ? 1 : + sig == SIGABRT ? 1 : /* SIGIOT */ + sig == SIGEMT ? 1 : + sig == SIGFPE ? 1 : + sig == SIGBUS ? 1 : + sig == SIGSEGV ? 1 : + sig == SIGSYS ? 1 : + 0; +EOF +GLOBALS=$( cat <&9 ) + +############################################################ EVENT ACTION + +[ "$CUSTOM_TEST" ] || case "$PROFILE" in +coredump-top) ;; +*) EVENT_TEST="coredump_sig[this->sig]" +esac + +############################################################ ACTIONS + +if [ "$PROFILE" = "coredump-top" ]; then +exec 9<<EOF +$GLOBALS + +BEGIN { printf("Cataloging coredump-worthy signals ...") } /* probe ID $ID */ + +proc:::signal-send /coredump_sig[(int)arg2]/ /* probe ID $(( $ID + 1 )) */ +{ + @cores[stringof(((struct proc *)args[1])->p_comm), + signal_string[(int)arg2]] = count(); +} +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $ID + 2 )) +else +exec 9<<EOF +$GLOBALS + +this int sig; +this pid_t pid; +this string verdict; +this struct proc * target; +this u_int core_catch; +this u_int core_ign; +this u_int core_notrace; +this u_int core_sugid; +this uint64_t core_limit; + +$PROBE /* probe ID $ID */ +{${TRACE:+ + printf("<$ID>"); +} + this->target = (struct proc *)args[1]; + this->pid = (pid_t)this->target->p_pid; + this->sig = (int)arg2; +} + +$PROBE /* probe ID $(( $ID + 1 )) */ +{${TRACE:+ + printf("<$(( $ID + 1 ))>"); +} + /* + * Render a verdict the way the kernel will: disposition first + * (struct sigacts), then the coredump() gauntlet in its order: + * kern.coredump, kern.sugid_coredump vs P_SUGID, procctl(2) + * PROC_TRACE_CTL (P2_NOTRACE), and finally RLIMIT_CORE + */ + this->core_ign = + this->target->p_sigacts->ps_sigignore.__bits[ + (this->sig - 1) >> 5] & + (1 << ((this->sig - 1) & 31)); + this->core_catch = + this->target->p_sigacts->ps_sigcatch.__bits[ + (this->sig - 1) >> 5] & + (1 << ((this->sig - 1) & 31)); + this->core_sugid = + this->target->p_flag & 0x00000100; /* P_SUGID */ + this->core_notrace = + this->target->p_flag2 & 0x00000002; /* P2_NOTRACE */ + this->core_limit = this->target->p_limit-> + pl_rlimit[4].rlim_cur; /* RLIMIT_CORE */ + + this->verdict = + this->core_ign ? "ignored" : + this->core_catch ? "caught" : + \`do_coredump == 0 ? "denied by kern.coredump" : + this->core_sugid && \`sugid_coredump == 0 ? + "denied by kern.sugid_coredump" : + this->core_notrace ? "denied by procctl trace ctl" : + this->core_limit == 0 ? "denied by RLIMIT_CORE" : + "will dump core"; + + $( pproc -P _core "(struct proc *)args[1]" ) +} +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $ID + 2 )) +fi + +############################################################ EVENT TAG + +# For the running catalog, override the default `UID.GID CMD[PID]: ' tag +# with ANSI cursor-homing and screen-clearing codes plus column headers. + +if [ "$PROFILE" = "coredump-top" ]; then +size=$( stty size 2> /dev/null ) +rows="${size%% *}" +cols="${size#* }" + +exec 9<<EOF + printf("\033[H"); /* Position the cursor at top-left */ + printf("\033[J"); /* Clear display from cursor to end */ + + /* Header line containing probe (left) and date (right) */ + printf("%-*s%s%Y%s\n", + $(( ${cols:-80} - 20 )), "$PROBE", + console ? "\033[32m" : "", + walltimestamp, + console ? "\033[39m" : ""); + + /* Column headers */ + printf("%s%8s %-20s %s%s\n", + console ? "\033[1m" : "", + "COUNT", + "EXECNAME", + "SIGNAL", + console ? "\033[22m" : ""); +EOF +EVENT_TAG=$( cat <&9 ) +fi + +############################################################ EVENT DETAILS + +if [ "$PROFILE" = "coredump-top" ]; then +exec 9<<EOF + /* NB: Cumulative; not truncated between updates */ + printa("%@8u %-20s %s\n", @cores); +EOF +EVENT_DETAILS=$( cat <&9 ) +elif [ ! "$CUSTOM_DETAILS" ]; then +exec 9<<EOF + /* + * Print coredump-worthy signal details + */ + printf("%s[%d] pid %d (%s) -- %s", + signal_string[this->sig], + this->sig, + this->pid, + this->verdict, + this->args_core); +EOF +EVENT_DETAILS=$( cat <&9 ) +fi + +################################################################################ +# END +################################################################################ diff --git a/cddl/usr.sbin/dwatch/libexec/dtmalloc b/cddl/usr.sbin/dwatch/libexec/dtmalloc new file mode 100644 index 000000000000..ad1a044020fa --- /dev/null +++ b/cddl/usr.sbin/dwatch/libexec/dtmalloc @@ -0,0 +1,146 @@ +# -*- tab-width: 4 -*- ;; Emacs +# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM +############################################################ IDENT(1) +# +# $Title: dwatch(8) module for malloc(9) type activity $ +# $Copyright: 2026 Devin Teske. All rights reserved. $ +# +############################################################ DESCRIPTION +# +# Print kernel malloc(9) and free(9) activity by malloc type, via the +# dtmalloc provider (one malloc and one free probe per type; see +# vmstat -m for the types). The default profile prints allocations and +# frees meeting a size threshold (default 65536 bytes; tunable via +# DWATCH_MALLOC_MIN in the environment, 0 to show everything), +# answering "who is allocating huge kernel buffers?" Use +# dtmalloc-NAME to watch a single type (e.g., dtmalloc-devbuf). +# The dtmalloc-top profile maintains a running catalog, updated every +# 3 seconds, of net bytes and outstanding allocation balance by type: +# a type whose net bytes climb without bound while the system is in +# steady state is a leak suspect. NB: the catalog reflects activity +# since the watch began, not preexisting allocations, and caches +# legitimately hold what they allocate. +# +############################################################ PRAGMAS + +case "$PROFILE" in +dtmalloc-top) + DTRACE_PRAGMA=" + option quiet + option aggsortrev + " # END-QUOTE + ;; +esac + +############################################################ PROBE + +case "$PROFILE" in +dtmalloc) + : ${PROBE:=dtmalloc:::malloc, dtmalloc:::free} ;; +dtmalloc-top) + : ${PROBE:=profile:::tick-3s} ;; +*) + : ${PROBE:=$( echo \ + dtmalloc::${PROFILE#dtmalloc-}:malloc, \ + dtmalloc::${PROFILE#dtmalloc-}:free )} ;; +esac + +############################################################ EVENT ACTION + +: ${DWATCH_MALLOC_MIN:=65536} + +case "$DWATCH_MALLOC_MIN" in +""|*[!0-9]*) die "DWATCH_MALLOC_MIN must be a number" ;; # NOTREACHED +esac + +[ "$CUSTOM_TEST" ] || case "$PROFILE" in +dtmalloc-top) ;; +*) EVENT_TEST="(uint64_t)arg3 >= $DWATCH_MALLOC_MIN" +esac + +############################################################ ACTIONS + +if [ "$PROFILE" = "dtmalloc-top" ]; then +exec 9<<EOF +this int64_t dtmalloc_delta; + +BEGIN { printf("Cataloging malloc(9) activity ...") } /* probe ID $ID */ + +dtmalloc:::malloc /* probe ID $(( $ID + 1 )) */ +{ + this->dtmalloc_delta = (int64_t)arg3; +} + +dtmalloc:::free /* probe ID $(( $ID + 2 )) */ +{ + this->dtmalloc_delta = -(int64_t)arg3; +} + +dtmalloc:::malloc, dtmalloc:::free /* probe ID $(( $ID + 3 )) */ +{ + @dtmalloc_bytes[probefunc] = sum(this->dtmalloc_delta); + @dtmalloc_allocs[probefunc] = + sum(this->dtmalloc_delta > 0 ? 1 : -1); +} +EOF +ACTIONS=$( cat <&9 ) +ID=$(( $ID + 4 )) +fi + +############################################################ EVENT TAG + +# For the running catalog, override the default `UID.GID CMD[PID]: ' tag +# with ANSI cursor-homing and screen-clearing codes plus column headers. + +if [ "$PROFILE" = "dtmalloc-top" ]; then +size=$( stty size 2> /dev/null ) +rows="${size%% *}" +cols="${size#* }" + +exec 9<<EOF + printf("\033[H"); /* Position the cursor at top-left */ + printf("\033[J"); /* Clear display from cursor to end */ + + /* Header line containing probe (left) and date (right) */ + printf("%-*s%s%Y%s\n", + $(( ${cols:-80} - 20 )), "$PROBE", + console ? "\033[32m" : "", + walltimestamp, + console ? "\033[39m" : ""); + + /* Column headers */ + printf("%s%14s %10s %s%s\n", + console ? "\033[1m" : "", + "NET(B)", + "BALANCE", + "TYPE", + console ? "\033[22m" : ""); +EOF +EVENT_TAG=$( cat <&9 ) +fi + +############################################################ EVENT DETAILS + +if [ "$PROFILE" = "dtmalloc-top" ]; then +exec 9<<EOF + /* NB: Cumulative; not truncated between updates */ + printa("%@14d %@10d %s\n", @dtmalloc_bytes, @dtmalloc_allocs); +EOF +EVENT_DETAILS=$( cat <&9 ) +elif [ ! "$CUSTOM_DETAILS" ]; then +exec 9<<EOF + /* + * Print malloc(9) activity details + */ + printf("%s(9) type %s %d byte%s", + probename, + probefunc, + (long)arg3, + (long)arg3 == 1 ? "" : "s"); +EOF +EVENT_DETAILS=$( cat <&9 ) +fi + +################################################################################ +# END +################################################################################ diff --git a/cddl/usr.sbin/dwatch/libexec/errno b/cddl/usr.sbin/dwatch/libexec/errno index d899906a1313..e87cbde7ce50 100644 --- a/cddl/usr.sbin/dwatch/libexec/errno +++ b/cddl/usr.sbin/dwatch/libexec/errno @@ -3,7 +3,7 @@ ############################################################ IDENT(1) # # $Title: dwatch(8) module for syscall errno logging $ -# $Copyright: 2014-2018 Devin Teske. All rights reserved. $ +# $Copyright: 2014-2026 Devin Teske. All rights reserved. $ # ############################################################ DESCRIPTION # @@ -11,13 +11,24 @@ *** 1079 LINES SKIPPED ***home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?6a693cad.3708f.6ece9ba5>
