xref: /freebsd/cddl/usr.sbin/dwatch/libexec/lock (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 dtrace_lockstat(4) contention $
6# $Copyright: 2026 Devin Teske. All rights reserved. $
7#
8############################################################ DESCRIPTION
9#
10# Print kernel lock contention events reported by dtrace_lockstat(4),
11# naming the lock, the thread held-off by it, and for how long. Answers
12# "the system is slow -- where is it fighting over locks?" The default
13# profile watches block events (thread went off-CPU waiting); lock-spin
14# watches spin events instead. Contention shorter than a threshold is
15# suppressed (default 1 ms; tunable via DWATCH_LOCK_MS in the environment,
16# 0 to show everything).
17#
18############################################################ PROBE
19
20case "$PROFILE" in
21lock|lock-block)
22	: ${PROBE:=$( echo \
23		lockstat:::adaptive-block, \
24		lockstat:::lockmgr-block, \
25		lockstat:::rw-block, \
26		lockstat:::sx-block )} ;;
27lock-spin)
28	: ${PROBE:=$( echo \
29		lockstat:::adaptive-spin, \
30		lockstat:::rw-spin, \
31		lockstat:::spin-spin, \
32		lockstat:::sx-spin, \
33		lockstat:::thread-spin )} ;;
34lock-adaptive)
35	: ${PROBE:=lockstat:::adaptive-block, lockstat:::adaptive-spin} ;;
36lock-lockmgr)
37	: ${PROBE:=lockstat:::lockmgr-block} ;;
38lock-rw)
39	: ${PROBE:=lockstat:::rw-block, lockstat:::rw-spin} ;;
40lock-sx)
41	: ${PROBE:=lockstat:::sx-block, lockstat:::sx-spin} ;;
42lock-thread)
43	: ${PROBE:=lockstat:::thread-spin} ;;
44*)
45	: ${PROBE:=lockstat:::${PROFILE#lock-}}
46esac
47
48############################################################ EVENT ACTION
49
50: ${DWATCH_LOCK_MS:=1}
51
52case "$DWATCH_LOCK_MS" in
53""|*[!0-9]*) die "DWATCH_LOCK_MS must be a number" ;; # NOTREACHED
54esac
55
56[ "$CUSTOM_TEST" ] ||
57	EVENT_TEST="(int64_t)arg1 >= (int64_t)$DWATCH_LOCK_MS * 1000000"
58
59############################################################ ACTIONS
60
61exec 9<<EOF
62this string	lock_class;
63this string	lock_how;
64this string	lock_name;
65this string	lock_verb;
66
67/*
68 * Lock classes from sys/lock.h as-witnessed by dtrace_lockstat(4)
69 */
70inline string lockstat_class[string name] =
71	name == "adaptive-block" ?	"mtx" :
72	name == "adaptive-spin" ?	"mtx" :
73	name == "lockmgr-block" ?	"lockmgr" :
74	name == "rw-block" ?		"rw" :
75	name == "rw-spin" ?		"rw" :
76	name == "spin-spin" ?		"spin mtx" :
77	name == "sx-block" ?		"sx" :
78	name == "sx-spin" ?		"sx" :
79	name == "thread-spin" ?		"thread" :
80	name;
81
82inline string lockstat_verb[string name] =
83	name == "adaptive-block" ?	"blocked" :
84	name == "lockmgr-block" ?	"blocked" :
85	name == "rw-block" ?		"blocked" :
86	name == "sx-block" ?		"blocked" :
87	"spun";
88
89$PROBE /* probe ID $ID */
90{${TRACE:+
91	printf("<$ID>");
92}
93	/*
94	 * struct lock_object * (first member of every kernel lock)
95	 */
96	this->lock_name =
97		stringof(((struct lock_object *)arg0)->lo_name);
98	this->lock_class = lockstat_class[probename];
99	this->lock_verb = lockstat_verb[probename];
100	this->lock_how = "";
101}
102
103lockstat:::lockmgr-block,
104lockstat:::rw-block,
105lockstat:::sx-block /* probe ID $(( $ID + 1 )) */
106{${TRACE:+
107	printf("<$(( $ID + 1 ))>");
108}
109	/* arg2 is 0 when acquiring as writer, 1 as reader */
110	this->lock_how = arg2 == 0 ? " as writer" : " as reader";
111}
112EOF
113ACTIONS=$( cat <&9 )
114ID=$(( $ID + 2 ))
115
116############################################################ EVENT DETAILS
117
118if [ ! "$CUSTOM_DETAILS" ]; then
119exec 9<<EOF
120	/*
121	 * Print lock contention details
122	 */
123	printf("%s %d.%03d ms on %s \"%s\"%s",
124		this->lock_verb,
125		(int64_t)arg1 / 1000000,
126		((int64_t)arg1 % 1000000) / 1000,
127		this->lock_class,
128		this->lock_name,
129		this->lock_how);
130EOF
131EVENT_DETAILS=$( cat <&9 )
132fi
133
134################################################################################
135# END
136################################################################################
137