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