xref: /freebsd/cddl/usr.sbin/dwatch/libexec/coredump (revision 434283fda99e89af20c3fe95fde427624ae96d04)
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