xref: /freebsd/cddl/usr.sbin/dwatch/libexec/slow (revision 434283fda99e89af20c3fe95fde427624ae96d04)
1*434283fdSDevin Teske# -*- tab-width: 4 -*- ;; Emacs
2*434283fdSDevin Teske# vi: set filetype=sh tabstop=8 shiftwidth=8 noexpandtab :: Vi/ViM
3*434283fdSDevin Teske############################################################ IDENT(1)
4*434283fdSDevin Teske#
5*434283fdSDevin Teske# $Title: dwatch(8) module for slow syscall detection $
6*434283fdSDevin Teske# $Copyright: 2026 Devin Teske. All rights reserved. $
7*434283fdSDevin Teske#
8*434283fdSDevin Teske############################################################ DESCRIPTION
9*434283fdSDevin Teske#
10*434283fdSDevin Teske# Print syscalls whose entry-to-return latency meets or exceeds a threshold
11*434283fdSDevin Teske# (default 100 ms; tunable via DWATCH_SLOW_MS in the environment). Answers
12*434283fdSDevin Teske# "why is my application stalling?" by naming the slow operation, the time
13*434283fdSDevin Teske# it took, and any errno it returned. The default profile watches a curated
14*434283fdSDevin Teske# set of filesystem-related syscalls that are expected to be fast. Use
15*434283fdSDevin Teske# slow-syscall to watch every syscall (NB: intentionally-blocking syscalls
16*434283fdSDevin Teske# such as select(2), poll(2), kevent(2), and wait4(2) will dominate), or
17*434283fdSDevin Teske# slow-NAME (e.g., slow-connect) to watch a single syscall by name.
18*434283fdSDevin Teske#
19*434283fdSDevin Teske############################################################ PROBE
20*434283fdSDevin Teske
21*434283fdSDevin Teskecase "$PROFILE" in
22*434283fdSDevin Teskeslow)
23*434283fdSDevin Teske	: ${PROBE:=$( echo \
24*434283fdSDevin Teske		syscall::open:return, \
25*434283fdSDevin Teske		syscall::openat:return, \
26*434283fdSDevin Teske		syscall::close:return, \
27*434283fdSDevin Teske		syscall::read:return, \
28*434283fdSDevin Teske		syscall::readv:return, \
29*434283fdSDevin Teske		syscall::pread:return, \
30*434283fdSDevin Teske		syscall::preadv:return, \
31*434283fdSDevin Teske		syscall::write:return, \
32*434283fdSDevin Teske		syscall::writev:return, \
33*434283fdSDevin Teske		syscall::pwrite:return, \
34*434283fdSDevin Teske		syscall::pwritev:return, \
35*434283fdSDevin Teske		syscall::copy_file_range:return, \
36*434283fdSDevin Teske		syscall::getdirentries:return, \
37*434283fdSDevin Teske		syscall::readlink:return, \
38*434283fdSDevin Teske		syscall::readlinkat:return, \
39*434283fdSDevin Teske		syscall::fsync:return, \
40*434283fdSDevin Teske		syscall::fdatasync:return, \
41*434283fdSDevin Teske		syscall::rename:return, \
42*434283fdSDevin Teske		syscall::renameat:return, \
43*434283fdSDevin Teske		syscall::renameat2:return, \
44*434283fdSDevin Teske		syscall::unlink:return, \
45*434283fdSDevin Teske		syscall::unlinkat:return )} ;;
46*434283fdSDevin Teskeslow-open)
47*434283fdSDevin Teske	: ${PROBE:=syscall::open:return, syscall::openat:return} ;;
48*434283fdSDevin Teskeslow-read)
49*434283fdSDevin Teske	: ${PROBE:=$( echo \
50*434283fdSDevin Teske		syscall::read:return, \
51*434283fdSDevin Teske		syscall::readv:return, \
52*434283fdSDevin Teske		syscall::pread:return, \
53*434283fdSDevin Teske		syscall::preadv:return, \
54*434283fdSDevin Teske		syscall::getdirentries:return, \
55*434283fdSDevin Teske		syscall::readlink:return, \
56*434283fdSDevin Teske		syscall::readlinkat:return )} ;;
57*434283fdSDevin Teskeslow-write)
58*434283fdSDevin Teske	: ${PROBE:=$( echo \
59*434283fdSDevin Teske		syscall::write:return, \
60*434283fdSDevin Teske		syscall::writev:return, \
61*434283fdSDevin Teske		syscall::pwrite:return, \
62*434283fdSDevin Teske		syscall::pwritev:return, \
63*434283fdSDevin Teske		syscall::copy_file_range:return )} ;;
64*434283fdSDevin Teskeslow-fsync)
65*434283fdSDevin Teske	: ${PROBE:=syscall::fsync:return, syscall::fdatasync:return} ;;
66*434283fdSDevin Teskeslow-syscall)
67*434283fdSDevin Teske	: ${PROBE:=syscall:::return} ;;
68*434283fdSDevin Teske*)
69*434283fdSDevin Teske	: ${PROBE:=syscall::${PROFILE#slow-}:return}
70*434283fdSDevin Teskeesac
71*434283fdSDevin Teske
72*434283fdSDevin Teske#
73*434283fdSDevin Teske# Derive the matching entry probes from the return probes being watched
74*434283fdSDevin Teske#
75*434283fdSDevin TeskeENTRY_PROBE=$( echo "$PROBE" | awk 'gsub(/:return/, ":entry") || 1' )
76*434283fdSDevin Teske
77*434283fdSDevin Teske############################################################ EVENT ACTION
78*434283fdSDevin Teske
79*434283fdSDevin Teske: ${DWATCH_SLOW_MS:=100}
80*434283fdSDevin Teske
81*434283fdSDevin Teskecase "$DWATCH_SLOW_MS" in
82*434283fdSDevin Teske""|*[!0-9]*) die "DWATCH_SLOW_MS must be a number" ;; # NOTREACHED
83*434283fdSDevin Teskeesac
84*434283fdSDevin Teske
85*434283fdSDevin Teske[ "$CUSTOM_TEST" ] ||
86*434283fdSDevin Teske	EVENT_TEST="this->slow_ns >= (int64_t)$DWATCH_SLOW_MS * 1000000"
87*434283fdSDevin Teske
88*434283fdSDevin Teske############################################################ ACTIONS
89*434283fdSDevin Teske
90*434283fdSDevin Teskeexec 9<<EOF
91*434283fdSDevin Teskeself int64_t	slow_ts;
92*434283fdSDevin Teskethis int64_t	slow_ns;
93*434283fdSDevin Teske
94*434283fdSDevin Teske$ENTRY_PROBE /* probe ID $ID */
95*434283fdSDevin Teske{${TRACE:+
96*434283fdSDevin Teske	printf("<$ID>");}
97*434283fdSDevin Teske	self->slow_ts = timestamp;
98*434283fdSDevin Teske}
99*434283fdSDevin Teske
100*434283fdSDevin Teske$PROBE /* probe ID $(( $ID + 1 )) */
101*434283fdSDevin Teske{${TRACE:+
102*434283fdSDevin Teske	printf("<$(( $ID + 1 ))>");
103*434283fdSDevin Teske}
104*434283fdSDevin Teske	/* NB: -1 if we did not see the entry (enabled mid-syscall) */
105*434283fdSDevin Teske	this->slow_ns = self->slow_ts ? timestamp - self->slow_ts : -1;
106*434283fdSDevin Teske	self->slow_ts = 0;
107*434283fdSDevin Teske}
108*434283fdSDevin TeskeEOF
109*434283fdSDevin TeskeACTIONS=$( cat <&9 )
110*434283fdSDevin TeskeID=$(( $ID + 2 ))
111*434283fdSDevin Teske
112*434283fdSDevin Teske############################################################ EVENT DETAILS
113*434283fdSDevin Teske
114*434283fdSDevin Teskeif [ ! "$CUSTOM_DETAILS" ]; then
115*434283fdSDevin Teskeexec 9<<EOF
116*434283fdSDevin Teske	/*
117*434283fdSDevin Teske	 * Print syscall latency details
118*434283fdSDevin Teske	 */
119*434283fdSDevin Teske	printf("%s(2) %d.%03d ms%s%s",
120*434283fdSDevin Teske		probefunc,
121*434283fdSDevin Teske		this->slow_ns / 1000000,
122*434283fdSDevin Teske		(this->slow_ns % 1000000) / 1000,
123*434283fdSDevin Teske		errno > 0 ? " -- " : "",
124*434283fdSDevin Teske		errno > 0 ? strerror[errno] : "");
125*434283fdSDevin TeskeEOF
126*434283fdSDevin TeskeEVENT_DETAILS=$( cat <&9 )
127*434283fdSDevin Teskefi
128*434283fdSDevin Teske
129*434283fdSDevin Teske################################################################################
130*434283fdSDevin Teske# END
131*434283fdSDevin Teske################################################################################
132