1# -*- tab-width: 4 -*- ;; Emacs 2# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM 3############################################################ IDENT(1) 4# 5# $Title: dwatch(8) module for coredump-worthy signal delivery $ 6# $Copyright: 2026 Devin Teske. All rights reserved. $ 7# 8############################################################ DESCRIPTION 9# 10# Print signals whose default action produces a coredump, as they are 11# sent, together with a verdict of whether a core will actually be 12# written, rendered the same way and in the same order the kernel will 13# decide it: the target may be ignoring or catching the signal, dumps 14# may be disabled (kern.coredump), the target may be flagged sugid 15# (kern.sugid_coredump) or may have disabled tracing via procctl(2) 16# PROC_TRACE_CTL, or RLIMIT_CORE may be 0. 17# Coredump-worthy is defined by the SIGPROP_CORE entries of the 18# sigproptbl in kern_sig.c: SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGEMT, 19# SIGFPE, SIGBUS, SIGSEGV, and SIGSYS. 20# The coredump-top profile maintains a running catalog, updated 21# every 3 seconds, of coredump-worthy signals by process and signal. 22# Combine with `-O cmd' to capture state as each event occurs. 23# 24############################################################ PRAGMAS 25 26case "$PROFILE" in 27coredump-top) 28 DTRACE_PRAGMA=" 29 option quiet 30 option aggsortrev 31 " # END-QUOTE 32 ;; 33esac 34 35############################################################ PROBE 36 37case "$PROFILE" in 38coredump-top) 39 : ${PROBE:=profile:::tick-3s} ;; 40*) 41 : ${PROBE:=proc:::signal-send} 42esac 43 44############################################################ GLOBALS 45 46exec 9<<EOF 47/* 48 * Signals whose default action includes a coredump, per the 49 * SIGPROP_CORE entries of the sigproptbl in kern_sig.c of FreeBSD 50 */ 51inline int coredump_sig[int sig] = 52 sig == SIGQUIT ? 1 : 53 sig == SIGILL ? 1 : 54 sig == SIGTRAP ? 1 : 55 sig == SIGABRT ? 1 : /* SIGIOT */ 56 sig == SIGEMT ? 1 : 57 sig == SIGFPE ? 1 : 58 sig == SIGBUS ? 1 : 59 sig == SIGSEGV ? 1 : 60 sig == SIGSYS ? 1 : 61 0; 62EOF 63GLOBALS=$( cat <&9 ) 64 65############################################################ EVENT ACTION 66 67[ "$CUSTOM_TEST" ] || case "$PROFILE" in 68coredump-top) ;; 69*) EVENT_TEST="coredump_sig[this->sig]" 70esac 71 72############################################################ ACTIONS 73 74if [ "$PROFILE" = "coredump-top" ]; then 75exec 9<<EOF 76$GLOBALS 77 78BEGIN { printf("Cataloging coredump-worthy signals ...") } /* probe ID $ID */ 79 80proc:::signal-send /coredump_sig[(int)arg2]/ /* probe ID $(( $ID + 1 )) */ 81{ 82 @cores[stringof(((struct proc *)args[1])->p_comm), 83 signal_string[(int)arg2]] = count(); 84} 85EOF 86ACTIONS=$( cat <&9 ) 87ID=$(( $ID + 2 )) 88else 89exec 9<<EOF 90$GLOBALS 91 92this int sig; 93this pid_t pid; 94this string verdict; 95this struct proc * target; 96this u_int core_catch; 97this u_int core_ign; 98this u_int core_notrace; 99this u_int core_sugid; 100this uint64_t core_limit; 101 102$PROBE /* probe ID $ID */ 103{${TRACE:+ 104 printf("<$ID>"); 105} 106 this->target = (struct proc *)args[1]; 107 this->pid = (pid_t)this->target->p_pid; 108 this->sig = (int)arg2; 109} 110 111$PROBE /* probe ID $(( $ID + 1 )) */ 112{${TRACE:+ 113 printf("<$(( $ID + 1 ))>"); 114} 115 /* 116 * Render a verdict the way the kernel will: disposition first 117 * (struct sigacts), then the coredump() gauntlet in its order: 118 * kern.coredump, kern.sugid_coredump vs P_SUGID, procctl(2) 119 * PROC_TRACE_CTL (P2_NOTRACE), and finally RLIMIT_CORE 120 */ 121 this->core_ign = 122 this->target->p_sigacts->ps_sigignore.__bits[ 123 (this->sig - 1) >> 5] & 124 (1 << ((this->sig - 1) & 31)); 125 this->core_catch = 126 this->target->p_sigacts->ps_sigcatch.__bits[ 127 (this->sig - 1) >> 5] & 128 (1 << ((this->sig - 1) & 31)); 129 this->core_sugid = 130 this->target->p_flag & 0x00000100; /* P_SUGID */ 131 this->core_notrace = 132 this->target->p_flag2 & 0x00000002; /* P2_NOTRACE */ 133 this->core_limit = this->target->p_limit-> 134 pl_rlimit[4].rlim_cur; /* RLIMIT_CORE */ 135 136 this->verdict = 137 this->core_ign ? "ignored" : 138 this->core_catch ? "caught" : 139 \`do_coredump == 0 ? "denied by kern.coredump" : 140 this->core_sugid && \`sugid_coredump == 0 ? 141 "denied by kern.sugid_coredump" : 142 this->core_notrace ? "denied by procctl trace ctl" : 143 this->core_limit == 0 ? "denied by RLIMIT_CORE" : 144 "will dump core"; 145 146 $( pproc -P _core "(struct proc *)args[1]" ) 147} 148EOF 149ACTIONS=$( cat <&9 ) 150ID=$(( $ID + 2 )) 151fi 152 153############################################################ EVENT TAG 154 155# For the running catalog, override the default `UID.GID CMD[PID]: ' tag 156# with ANSI cursor-homing and screen-clearing codes plus column headers. 157 158if [ "$PROFILE" = "coredump-top" ]; then 159size=$( stty size 2> /dev/null ) 160rows="${size%% *}" 161cols="${size#* }" 162 163exec 9<<EOF 164 printf("\033[H"); /* Position the cursor at top-left */ 165 printf("\033[J"); /* Clear display from cursor to end */ 166 167 /* Header line containing probe (left) and date (right) */ 168 printf("%-*s%s%Y%s\n", 169 $(( ${cols:-80} - 20 )), "$PROBE", 170 console ? "\033[32m" : "", 171 walltimestamp, 172 console ? "\033[39m" : ""); 173 174 /* Column headers */ 175 printf("%s%8s %-20s %s%s\n", 176 console ? "\033[1m" : "", 177 "COUNT", 178 "EXECNAME", 179 "SIGNAL", 180 console ? "\033[22m" : ""); 181EOF 182EVENT_TAG=$( cat <&9 ) 183fi 184 185############################################################ EVENT DETAILS 186 187if [ "$PROFILE" = "coredump-top" ]; then 188exec 9<<EOF 189 /* NB: Cumulative; not truncated between updates */ 190 printa("%@8u %-20s %s\n", @cores); 191EOF 192EVENT_DETAILS=$( cat <&9 ) 193elif [ ! "$CUSTOM_DETAILS" ]; then 194exec 9<<EOF 195 /* 196 * Print coredump-worthy signal details 197 */ 198 printf("%s[%d] pid %d (%s) -- %s", 199 signal_string[this->sig], 200 this->sig, 201 this->pid, 202 this->verdict, 203 this->args_core); 204EOF 205EVENT_DETAILS=$( cat <&9 ) 206fi 207 208################################################################################ 209# END 210################################################################################ 211