summaryrefslogtreecommitdiffstats
path: root/t/perf/p7519-fsmonitor.sh
blob: b1cb23880fb2bf44faad8745d58f392ceff17a94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#!/bin/sh

test_description="Test core.fsmonitor"

. ./perf-lib.sh

#
# Performance test for the fsmonitor feature which enables git to talk to a
# file system change monitor and avoid having to scan the working directory
# for new or modified files.
#
# By default, the performance test will utilize the Watchman file system
# monitor if it is installed.  If Watchman is not installed, it will use a
# dummy integration script that does not report any new or modified files.
# The dummy script has very little overhead which provides optimistic results.
#
# The performance test will also use the untracked cache feature if it is
# available as fsmonitor uses it to speed up scanning for untracked files.
#
# There are 3 environment variables that can be used to alter the default
# behavior of the performance test:
#
# GIT_PERF_7519_UNTRACKED_CACHE: used to configure core.untrackedCache
# GIT_PERF_7519_SPLIT_INDEX: used to configure core.splitIndex
# GIT_PERF_7519_FSMONITOR: used to configure core.fsMonitor. May be an
#   absolute path to an integration. May be a space delimited list of
#   absolute paths to integrations.
#
# The big win for using fsmonitor is the elimination of the need to scan the
# working directory looking for changed and untracked files. If the file
# information is all cached in RAM, the benefits are reduced.
#
# GIT_PERF_7519_DROP_CACHE: if set, the OS caches are dropped between tests
#
# GIT_PERF_7519_TRACE: if set, enable trace logging during the test.
#   Trace logs will be grouped by fsmonitor provider.

test_perf_large_repo
test_checkout_worktree

test_lazy_prereq UNTRACKED_CACHE '
	{ git update-index --test-untracked-cache; ret=$?; } &&
	test $ret -ne 1
'

test_lazy_prereq WATCHMAN '
	command -v watchman
'

if test_have_prereq WATCHMAN
then
	# Convert unix style paths to escaped Windows style paths for Watchman
	case "$(uname -s)" in
	MSYS_NT*)
	  GIT_WORK_TREE="$(cygpath -aw "$PWD" | sed 's,\\,/,g')"
	  ;;
	*)
	  GIT_WORK_TREE="$PWD"
	  ;;
	esac
fi

trace_start () {
	if test -n "$GIT_PERF_7519_TRACE"
	then
		name="$1"
		TEST_TRACE_DIR="$TEST_OUTPUT_DIRECTORY/test-trace/p7519/"
		echo "Writing trace logging to $TEST_TRACE_DIR"

		mkdir -p "$TEST_TRACE_DIR"

		# Start Trace2 logging and any other GIT_TRACE_* logs that you
		# want for this named test case.

		GIT_TRACE2_PERF="$TEST_TRACE_DIR/$name.trace2perf"
		export GIT_TRACE2_PERF

		>"$GIT_TRACE2_PERF"
	fi
}

trace_stop () {
	if test -n "$GIT_PERF_7519_TRACE"
	then
		unset GIT_TRACE2_PERF
	fi
}

touch_files () {
	n=$1 &&
	d="$n"_files &&

	(cd $d && test_seq 1 $n | xargs touch )
}

test_expect_success "one time repo setup" '
	# set untrackedCache depending on the environment
	if test -n "$GIT_PERF_7519_UNTRACKED_CACHE"
	then
		git config core.untrackedCache "$GIT_PERF_7519_UNTRACKED_CACHE"
	else
		if test_have_prereq UNTRACKED_CACHE
		then
			git config core.untrackedCache true
		else
			git config core.untrackedCache false
		fi
	fi &&

	# set core.splitindex depending on the environment
	if test -n "$GIT_PERF_7519_SPLIT_INDEX"
	then
		git config core.splitIndex "$GIT_PERF_7519_SPLIT_INDEX"
	fi &&

	mkdir 1_file 10_files 100_files 1000_files 10000_files &&
	: 1_file directory should be left empty &&
	touch_files 10 &&
	touch_files 100 &&
	touch_files 1000 &&
	touch_files 10000 &&
	git add 1_file 10_files 100_files 1000_files 10000_files &&
	git commit -qm "Add files" &&

	# If Watchman exists, watch the work tree and attempt a query.
	if test_have_prereq WATCHMAN; then
		watchman watch "$GIT_WORK_TREE" &&
		watchman watch-list | grep -q -F "p7519-fsmonitor"
	fi
'

setup_for_fsmonitor_hook () {
	# set INTEGRATION_SCRIPT depending on the environment
	if test -n "$INTEGRATION_PATH"
	then
		INTEGRATION_SCRIPT="$INTEGRATION_PATH"
	else
		#
		# Choose integration script based on existence of Watchman.
		# Fall back to an empty integration script.
		#
		mkdir .git/hooks &&
		if test_have_prereq WATCHMAN
		then
			INTEGRATION_SCRIPT=".git/hooks/fsmonitor-watchman" &&
			cp "$TEST_DIRECTORY/../templates/hooks--fsmonitor-watchman.sample" "$INTEGRATION_SCRIPT"
		else
			INTEGRATION_SCRIPT=".git/hooks/fsmonitor-empty" &&
			write_script "$INTEGRATION_SCRIPT"<<-\EOF
			EOF
		fi
	fi &&

	git config core.fsmonitor "$INTEGRATION_SCRIPT" &&
	git update-index --fsmonitor 2>error &&
	if test_have_prereq WATCHMAN
	then
		test_must_be_empty error  # ensure no silent error
	else
		grep "Empty last update token" error
	fi
}

test_perf_w_drop_caches () {
	if test -n "$GIT_PERF_7519_DROP_CACHE"; then
		test_perf "$1" --setup "test-tool drop-caches" "$2"
	else
		test_perf "$@"
	fi
}

test_fsmonitor_suite () {
	if test -n "$USE_FSMONITOR_DAEMON"
	then
		DESC="builtin fsmonitor--daemon"
	elif test -n "$INTEGRATION_SCRIPT"
	then
		DESC="fsmonitor=$(basename $INTEGRATION_SCRIPT)"
	else
		DESC="fsmonitor=disabled"
	fi

	test_expect_success "test_initialization" '
		git reset --hard &&
		git status  # Warm caches
	'

	test_perf_w_drop_caches "status ($DESC)" '
		git status
	'

	test_perf_w_drop_caches "status -uno ($DESC)" '
		git status -uno
	'

	test_perf_w_drop_caches "status -uall ($DESC)" '
		git status -uall
	'

	# Update the mtimes on upto 100k files to make status think
	# that they are dirty.  For simplicity, omit any files with
	# LFs (i.e. anything that ls-files thinks it needs to dquote)
	# and any files with whitespace so that they pass thru xargs
	# properly.
	#
	test_perf_w_drop_caches "status (dirty) ($DESC)" '
		git ls-files | \
			head -100000 | \
			grep -v \" | \
			grep -v " ." | \
			xargs test-tool chmtime -300 &&
		git status
	'

	test_perf_w_drop_caches "diff ($DESC)" '
		git diff
	'

	test_perf_w_drop_caches "diff HEAD ($DESC)" '
		git diff HEAD
	'

	test_perf_w_drop_caches "diff -- 0_files ($DESC)" '
		git diff -- 1_file
	'

	test_perf_w_drop_caches "diff -- 10_files ($DESC)" '
		git diff -- 10_files
	'

	test_perf_w_drop_caches "diff -- 100_files ($DESC)" '
		git diff -- 100_files
	'

	test_perf_w_drop_caches "diff -- 1000_files ($DESC)" '
		git diff -- 1000_files
	'

	test_perf_w_drop_caches "diff -- 10000_files ($DESC)" '
		git diff -- 10000_files
	'

	test_perf_w_drop_caches "add ($DESC)" '
		git add  --all
	'
}

#
# Run a full set of perf tests using each Hook-based fsmonitor provider,
# such as Watchman.
#

trace_start fsmonitor-watchman
if test -n "$GIT_PERF_7519_FSMONITOR"; then
	for INTEGRATION_PATH in $GIT_PERF_7519_FSMONITOR; do
		test_expect_success "setup for fsmonitor $INTEGRATION_PATH" 'setup_for_fsmonitor_hook'
		test_fsmonitor_suite
	done
else
	test_expect_success "setup for fsmonitor hook" 'setup_for_fsmonitor_hook'
	test_fsmonitor_suite
fi

if test_have_prereq WATCHMAN
then
	watchman watch-del "$GIT_WORK_TREE" >/dev/null 2>&1 &&

	# Work around Watchman bug on Windows where it holds on to handles
	# preventing the removal of the trash directory
	watchman shutdown-server >/dev/null 2>&1
fi
trace_stop

#
# Run a full set of perf tests with the fsmonitor feature disabled.
#

trace_start fsmonitor-disabled
test_expect_success "setup without fsmonitor" '
	unset INTEGRATION_SCRIPT &&
	git config --unset core.fsmonitor &&
	git update-index --no-fsmonitor
'

test_fsmonitor_suite
trace_stop

#
# Run a full set of perf tests using the built-in fsmonitor--daemon.
# It does not use the Hook API, so it has a different setup.
# Explicitly start the daemon here and before we start client commands
# so that we can later add custom tracing.
#
if test_have_prereq FSMONITOR_DAEMON
then
	USE_FSMONITOR_DAEMON=t

	test_expect_success "setup for builtin fsmonitor" '
		trace_start fsmonitor--daemon--server &&
		git fsmonitor--daemon start &&

		trace_start fsmonitor--daemon--client &&

		git config core.fsmonitor true &&
		git update-index --fsmonitor
	'

	test_fsmonitor_suite

	git fsmonitor--daemon stop
	trace_stop
fi

test_done