fsmonitor: skip sanity check if the index is split
cover fsmonitor: skip sanity check if the index is split
The checks added in 3444ec2 to ensure that the fsmonitor_dirty bitmap does not have more bits than the index do not play well with the split index.
git update-index --fsmonitor --split-index calls write_locked_index which calls write_shared_index as well as write_split_index. The first call fills up the fsmonitor_dirty bitmap, and the second modifies the index such that istate->cache_nr is zero and this assert is hit.
The test written does reproduce the error, but only flakily. There is limited difference with GIT_TEST_FSMONITOR=fsmonitor-all or GIT_TEST_FSMONITOR=fsmonitor-watchman, so the flakiness might come from somewhere else, which I haven't tracked down.
The test also requires checkout of a new branch, and checking out back to master. It's clear that the index gets into some poor state through these operations, and there is a deeper bug somewhere.
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard case. Also, I haven't been able to reproduce this with "standard" user commands, like status/checkout/stash, so the blast radius seems limited.
Utsav Shah (1):
fsmonitor: skip sanity check if the index is split
fsmonitor.c | 8 ++++---- t/t7519-status-fsmonitor.sh | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-)
base-commit: 566a1439f6f56c2171b8853ddbca0ad3f5098770
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-458%2FUtsav2%2Fsplit-fix-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-458/Utsav2/split-fix-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/458
Signature · 1 line
gitgitgadget
PATCH 1/1 fsmonitor: skip sanity check if the index is split
From: Utsav Shah <[email protected]>
The checks added in 3444ec2eb2 ("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-11), to ensure that the fsmonitor_dirty bitmap does not have more bits than the index do not play well with the split index.
git update-index --fsmonitor --split-index calls write_locked_index which calls write_shared_index as well as write_split_index. The first call fills up the fsmonitor_dirty bitmap,
and the second modifies the index such that istate->cache_nr is zero and this assert is hit.
The test written does reproduce the error, but only flakily. There is limited difference with GIT_TEST_FSMONITOR=fsmonitor-all or GIT_TEST_FSMONITOR=fsmonitor-watchman, so the flakiness might come from somewhere else, which I haven't tracked down.
The test also requires checkout of a new branch, and checking out back to master. It's clear that the index gets into some poor state through these operations, and there is a deeper bug somewhere.
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard case. Also, I haven't been able to reproduce this with "standard" user commands, like status/checkout/stash, so the blast radius seems limited.
--- fsmonitor.c | 8 ++++---- t/t7519-status-fsmonitor.sh | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-)
fsmonitor.c +4 -4
@@ -16,7 +16,7 @@ static void fsmonitor_ewah_callback(size_t pos, void *is)struct index_state *istate = (struct index_state *)is;struct cache_entry *ce; -if (pos >= istate->cache_nr) +if (!istate->split_index && pos >= istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" >= %u)" , (uintmax_t )pos, istate->cache_nr);
@@ -55,7 +55,7 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data, } istate->fsmonitor_dirty = fsmonitor_dirty; -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr);
@@ -83,7 +83,7 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)uint32_t ewah_size =0 ;int fixup =0 ; -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr);
@@ -252,7 +252,7 @@ void tweak_fsmonitor(struct index_state *istate) }/* Mark all previously saved entries as dirty */ -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr);ewah_each_bit (istate->fsmonitor_dirty, fsmonitor_ewah_callback, istate);
t/t7519-status-fsmonitor.sh +23 -0
@@ -371,4 +371,27 @@ test_expect_success 'status succeeds after staging/unstaging ' ' ) ' +# Git will only split indices if we have a bunch of files created, +# so that prep work of creating a few hundred files is required. +# Note that this test doesn't fail determinstically without +# its corresponding bugfix. +test_expect_success 'update-index succeeds after staging with split index' ' + test_create_repo fsmonitor-stage-split && + ( + cd fsmonitor-stage-split && + test_commit initial && + files=$(test_seq 1 100) && + echo "hello world" > file && + touch $files && + git add -A && + git commit -m "next" && + git config core.fsmonitor "$TEST_DIRECTORY/t7519/fsmonitor-watchman" && + echo "hello world" > file && + git checkout -b new-branch && + git checkout master && + echo hello >> file && + git update-index --split-index --untracked-cache --fsmonitor + ) +' + test_done
Signature · 1 line
gitgitgadget
Quoted 2 lines · Utsav Shah via GitGitGadget
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard case.
OK, it sounds more like this "it does not make any sense to compare the position in the fsmonitor bitmap (which covers the entire thing) with the position in just a split part of the index (which covers only the delta over the base index)"? If that is the case, it means that the "check" is even worse than being "over-eager"---it simply is not correct.
Thanks, will queue.
Quoted 11 lines · Junio C Hamano
Quoted 2 lines · Utsav Shah via GitGitGadget
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard case.
OK, it sounds more like this "it does not make any sense to compare the position in the fsmonitor bitmap (which covers the entire thing) with the position in just a split part of the index (which covers only the delta over the base index)"? If that is the case, it means that the "check" is even worse than being "over-eager"---it simply is not correct.
Having said all that, I wonder if we are doing the right thing with or without 3444ec2e ("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-11) in the split-index mode in the first place.
The fact that your "loosen the check and allow 'pos' that identifies a tracked path used by the fsmonitor bitmap to be larger than the size of the istate->cache[]" patch under discussion is needed is that 'pos' may sometimes be larger than isate->cache[] no? Then what happens in this hunk, for example?
fsmonitor.c +6 -1
@@ -14,8 +14,13 @@ struct trace_key trace_fsmonitor = TRACE_KEY_INIT(FSMONITOR);static void fsmonitor_ewah_callback (size_t pos,void *is) {struct index_state *istate = (struct index_state *)is; -struct cache_entry *ce = istate->cache[pos]; +struct cache_entry *ce; +if (pos >= istate->cache_nr) +BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" >= %u)" , + (uintmax_t )pos, istate->cache_nr); + + ce = istate->cache[pos]; ce->ce_flags &= ~CE_FSMONITOR_VALID ;
The istate->cache[] is a dynamic array whose size is managed via the usual ALLOC_GROW() using istate->cache_nr and istate->cache_alloc, whether the split-index feature is in use. When your patch makes a difference, then, doesn't the access to istate->cache[] pick up a random garbage and then flip the bit?
Puzzled... In any case, "check is worse than over-eager, it simply is wrong" I wrote in the message I am responding to is totally incorrect, it seems. It smells like lifting the check would just hide the underlying problem under the rug?
Quoted 52 lines
From: [email protected] <[email protected]> On Behalf Of Junio C Hamano
Sent: Sunday, November 10, 2019 7:01 PMQuoted 11 lines · Junio C Hamano
Quoted 2 lines · Utsav Shah via GitGitGadget
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard case.
OK, it sounds more like this "it does not make any sense to compare the position in the fsmonitor bitmap (which covers the entire thing) with the position in just a split part of the index (which covers only the delta over the base index)"? If that is the case, it means that the "check" is even worse than being "over-eager"---it simply is not correct.
Having said all that, I wonder if we are doing the right thing with or without
3444ec2e("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-
11) in the split-index mode in the first place.The fact that your "loosen the check and allow 'pos' that identifies a tracked path used by the fsmonitor bitmap to be larger than the size of the istate-
Quoted 1 line
cache[]" patch under discussion is needed is that 'pos' may sometimes be
larger than isate->cache[] no? Then what happens in this hunk, for example?
fsmonitor.c +0
-0@@ -14,8 +14,13 @@ struct trace_key trace_fsmonitor =
TRACE_KEY_INIT(FSMONITOR); static voidfsmonitor_ewah_callback(size_t pos, void *is){fsmonitor.c +3
-1struct index_state *istate = (struct index_state *)is; -struct cache_entry *ce = istate->cache[pos]; +struct cache_entry *ce; +if (pos >= istate->cache_nr) + BUG("fsmonitor_dirty has more entries than the index(%"PRIuMAX" >= %u)",
fsmonitor.c +3
-0+ (uintmax_t)pos, istate->cache_nr); + + ce = istate->cache[pos]; ce->ce_flags &= ~CE_FSMONITOR_VALID ;The istate->cache[] is a dynamic array whose size is managed via the usual
ALLOC_GROW()usingistate->cache_nrandistate->cache_alloc, whether the split-index feature is in use. When your patch makes a difference, then, doesn't the access to istate->cache[] pick up a random garbage and then flip the bit?
Quoted 4 lines
Puzzled... In any case, "check is worse than over-eager, it simply is wrong" I wrote in the message I am responding to is totally incorrect, it seems. It smells like lifting the check would just hide the underlying problem under the rug?
I agree. The only 2 places that excluding the split-index make sense are in read_fsmonitor_extension and write_fsmonitor_extension because the index_state that is being passing into those methods could be the delta index in which case the number of entries for the fsmonitor bitmap would almost always be more and cause the BUG to be hit which it should not be.
The reason it is not needed and should not be in the other 2 places is they are ran from tweak_fsmonitor which is ran at post_read_index_from which is after the base and delta indexes have been loaded into the indes_state and the index_state will have all the entries and if the fsmonitor bitmap is bigger than the number of entries then the BUG should be hit.
Kevin
Quoted 64 lines · Kevin Willford
Quoted 53 lines
From: [email protected] <[email protected]> On Behalf Of Junio C Hamano
Sent: Sunday, November 10, 2019 7:01 PMQuoted 11 lines · Junio C Hamano
Quoted 2 lines · Utsav Shah via GitGitGadget
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard case.
OK, it sounds more like this "it does not make any sense to compare the position in the fsmonitor bitmap (which covers the entire thing) with the position in just a split part of the index (which covers only the delta over the base index)"? If that is the case, it means that the "check" is even worse than being "over-eager"---it simply is not correct.
Having said all that, I wonder if we are doing the right thing with or without
3444ec2e("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-
11) in the split-index mode in the first place.The fact that your "loosen the check and allow 'pos' that identifies a tracked path used by the fsmonitor bitmap to be larger than the size of the istate-
Quoted 1 line
cache[]" patch under discussion is needed is that 'pos' may sometimes be
larger than isate->cache[] no? Then what happens in this hunk, for example?
fsmonitor.c +0
-0@@ -14,8 +14,13 @@ struct trace_key trace_fsmonitor =
TRACE_KEY_INIT(FSMONITOR); static void fsmonitor_ewah_callback(size_tpos, void *is) { struct index_state *istate = (struct index_state *)is;fsmonitor.c +3
-1-struct cache_entry *ce = istate->cache[pos]; +struct cache_entry *ce; +if (pos >= istate->cache_nr) + BUG("fsmonitor_dirty has more entries than the index(%"PRIuMAX" >= %u)",
fsmonitor.c +3
-0+ (uintmax_t)pos, istate->cache_nr); + + ce = istate->cache[pos]; ce->ce_flags &= ~CE_FSMONITOR_VALID ;The istate->cache[] is a dynamic array whose size is managed via the usual
ALLOC_GROW()usingistate->cache_nrandistate->cache_alloc, whether the split-index feature is in use. When your patch makes a difference, then, doesn't the access to istate->cache[] pick up a random garbage and then flip the bit?Quoted 4 lines
Puzzled... In any case, "check is worse than over-eager, it simply is wrong" I wrote in the message I am responding to is totally incorrect, it seems. It smells like lifting the check would just hide the underlying problem under the rug?
I agree. The only 2 places that excluding the split-index make sense are in
read_fsmonitor_extensionandwrite_fsmonitor_extensionbecause theindex_statethat is being passing into those methods could be the delta index in which case the number of entries for the fsmonitor bitmap would almost always be more and cause the BUG to be hit which it should not be.
Quoted 5 lines
The reason it is not needed and should not be in the other 2 places is they are ran from
tweak_fsmonitorwhich is ran atpost_read_index_fromwhich is after the base and delta indexes have been loaded into theindes_stateand theindex_statewill have all the entries and if the fsmonitor bitmap is bigger than the number of entries then the BUG should be hit.
Thanks. What exactly is the delta index? Is it the "split" index, vs the shared indices? I was surprised to see cache_nr being zero. My understanding was that cache and cache_nr would always be the materialized version of the entire index, which is clearly incorrect.
Quoted 2 lines
Kevin
Quoted 18 lines · Kevin Willford
Quoted 11 lines
The istate->cache[] is a dynamic array whose size is managed via the usual
ALLOC_GROW()usingistate->cache_nrandistate->cache_alloc, whether the split-index feature is in use. When your patch makes a difference, then, doesn't the access to istate->cache[] pick up a random garbage and then flip the bit?Puzzled... In any case, "check is worse than over-eager, it simply is wrong" I wrote in the message I am responding to is totally incorrect, it seems. It smells like lifting the check would just hide the underlying problem under the rug?
I agree. The only 2 places that excluding the split-index make sense are in
read_fsmonitor_extensionandwrite_fsmonitor_extensionbecause theindex_statethat is being passing into those methods could be the delta index in which case the number of entries for the fsmonitor bitmap would almost always be more and cause the BUG to be hit which it should not be.
Quoted 6 lines
The reason it is not needed and should not be in the other 2 places is they are ran from
tweak_fsmonitorwhich is ran atpost_read_index_fromwhich is after the base and delta indexes have been loaded into theindes_stateand theindex_statewill have all the entries and if the fsmonitor bitmap is bigger than the number of entries then the BUG should be hit.
I agree. While working on the 3444ec2e patch I missed that read_fsmonitor_extension and write_fsmonitor_extension could be called with the delta index rather than the full index.
I think it makes sense to leave the check in the other two places.
Thanks,
William
Quoted 81 lines
From: Utsav Shah <[email protected]>
Sent: Monday, November 11, 2019 10:26 AMQuoted 11 lines · Kevin Willford
Quoted 11 lines
From: [email protected] <[email protected]> On Behalf Of Junio C Hamano
Sent: Sunday, November 10, 2019 7:01 PMQuoted 4 lines · Junio C Hamano
Quoted 2 lines · Utsav Shah via GitGitGadget
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard
case.
Quoted 13 lines
Quoted 13 lines
Quoted 7 lines
OK, it sounds more like this "it does not make any sense to compare the position in the fsmonitor bitmap (which covers the entire thing) with the position in just a split part of the index (which covers only the delta over the base index)"? If that is the case, it means that the "check" is even worse than being "over-eager"---it simply is not correct.
Having said all that, I wonder if we are doing the right thing with or without
3444ec2e("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-
11) in the split-index mode in the first place.Quoted 6 lines
Quoted 6 lines
The fact that your "loosen the check and allow 'pos' that identifies a tracked path used by the fsmonitor bitmap to be larger than the size of the istate-
Quoted 2 lines
cache[]" patch under discussion is needed is that 'pos' may sometimes be
larger than isate->cache[] no? Then what happens in this hunk, for
example?
Quoted 27 lines
Quoted 27 lines
fsmonitor.c index 231e83a94d..1f4aa1b150 renamed from fsmonitor.c +0
-0100644
fsmonitor.c +0
-0@@ -14,8 +14,13 @@ struct trace_key trace_fsmonitor =
TRACE_KEY_INIT(FSMONITOR); static voidfsmonitor_ewah_callback(size_t pos, void *is) { struct index_state *istate = (struct index_state *)is;fsmonitor.c +3
-1-struct cache_entry *ce = istate->cache[pos]; +struct cache_entry *ce; +if (pos >= istate->cache_nr) + BUG("fsmonitor_dirty has more entries than the index(%"PRIuMAX" >= %u)",
fsmonitor.c +3
-0+ (uintmax_t)pos, istate->cache_nr); + + ce = istate->cache[pos]; ce->ce_flags &= ~CE_FSMONITOR_VALID ;The istate->cache[] is a dynamic array whose size is managed via the usual
ALLOC_GROW()usingistate->cache_nrandistate->cache_alloc, whether the split-index feature is in use. When your patch makes a difference, then, doesn't the access to istate->cache[] pick up a random garbage and then flip the bit?Quoted 10 lines
Quoted 4 lines
Puzzled... In any case, "check is worse than over-eager, it simply is wrong" I wrote in the message I am responding to is totally incorrect, it seems. It smells like lifting the check would just hide the underlying problem under the rug?
I agree. The only 2 places that excluding the split-index make sense are in
read_fsmonitor_extensionandwrite_fsmonitor_extensionbecause theindex_statethat is being passing into those methods could be the delta index in which case the number of entries for the fsmonitor bitmap would almost always be more and cause the BUG to be hit which itshould not be.
Quoted 6 lines
The reason it is not needed and should not be in the other 2 places is they are ran from
tweak_fsmonitorwhich is ran atpost_read_index_fromwhich is after the base and delta indexes have been loaded into theindes_stateand theindex_statewill have all the entries and if the fsmonitor bitmap is bigger than the number of entries then the BUG should
Quoted 4 lines
be hit.
Thanks. What exactly is the delta index? Is it the "split" index, vs the shared indices?
Yes the delta is the same as the split index mentioned here
https://git-scm.com/docs/git-update-index#_split_index.
Quoted 3 lines
I was surprised to see
cache_nrbeing zero. My understanding was that cache andcache_nrwould always be the materialized version of the entire index, which is clearly incorrect.
Most of the time that is correct but if you look in read_index_from, the index is loaded with the call to
ret = do_read_index(istate, path, 0);
This will read the index extensions so read_fsmonitor_extension will be called and the cache will only have the entries from the split/delta index.
The base/shared index isn't loaded and in the cache until later when merge_base_index(istate); is called which is right before the call to post_read_index_from where tweak_fsmonitor will get called from.
Quoted 10 lines · Utsav Shah via GitGitGadget
The checks added in
3444ec2eb2("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-11), to ensure that thefsmonitor_dirtybitmap does not have more bits than the index do not play well with the split index.git update-index --fsmonitor --split-index calls
write_locked_indexwhich callswrite_shared_indexas well aswrite_split_index. The first call fills up thefsmonitor_dirtybitmap,
and the second modifies the index such thatistate->cache_nris zero and this assert is hit.
Just to make sure that we are on the same page, is this the one?
BUG: fsmonitor.c:88: fsmonitor_dirty has more entries than the index (102 > 1)
Quoted 7 lines
The test written does reproduce the error, but only flakily. There is limited difference with GIT_TEST_FSMONITOR=fsmonitor-all or GIT_TEST_FSMONITOR=fsmonitor-watchman, so the flakiness might come from somewhere else, which I haven't tracked down.
The test also requires checkout of a new branch, and checking out back to master.
It doesn't; see below.
Quoted 18 lines
It's clear that the index gets into some poor state through these operations, and there is a deeper bug somewhere.
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard case. Also, I haven't been able to reproduce this with "standard" user commands, like status/checkout/stash, so the blast radius seems limited.
Helped-by Kevin Willford <[email protected]>Helped-by Junio C Hamano <[email protected]>Signed-off-by Utsav Shah <[email protected]>---
t/t7519-status-fsmonitor.sh +0
-0
t/t7519-status-fsmonitor.sh test_expect_success 'status succeeds after staging/unstaging ' ' · line 375
Quoted 5 lines
t/t7519-status-fsmonitor.sh +2
-0@@ -371,4 +371,27 @@ test_expect_success 'status succeeds after staging/unstaging ' ' ) ' +# Git will only split indices if we have a bunch of files created, +# so that prep work of creating a few hundred files is required.
'git update-index --split-index' splits the index no matter what; it even splits an empty index.
Quoted 10 lines
t/t7519-status-fsmonitor.sh +10
-0+# Note that this test doesn't fail determinstically without +# its corresponding bugfix. +test_expect_success 'update-index succeeds after staging with split index' ' + test_create_repo fsmonitor-stage-split && + ( + cd fsmonitor-stage-split && + test_commit initial && + files=$(test_seq 1 100) && + echo "hello world" > file && + touch $files &&
t/t7519-status-fsmonitor.sh test_expect_success 'status succeeds after staging/unstaging ' ' · line 395
Quoted 10 lines
t/t7519-status-fsmonitor.sh +10
-0+ git add -A && + git commit -m "next" && + git config core.fsmonitor "$TEST_DIRECTORY/t7519/fsmonitor-watchman" && + echo "hello world" > file && + git checkout -b new-branch && + git checkout master && + echo hello >> file && + git update-index --split-index --untracked-cache --fsmonitor + ) +'
I could reproduce the failure with '-r 30 --stress' relatively easily [1], but with those options I could shave off a lot from this test:
test_create_repo fsmonitor-stage-split &&
(
cd fsmonitor-stage-split &&
>tracked &&
git add tracked &&
git config core.fsmonitor
"$TEST_DIRECTORY/t7519/fsmonitor-watchman" &&
>untracked &&
git update-index --split-index --untracked-cache --fsmonitor
)and could still trigger the failure:
+ git update-index --split-index --untracked-cache --fsmonitor
open2: exec of watchman -j failed at /home/szeder/src/git/t/t7519/fsmonitor-watchman line 47.
BUG: fsmonitor.c:88: fsmonitor_dirty has more entries than the index (1 > 0)
[1] There is a quite expensive lazy prereq evaluation outside of 'test_expect_*' that I disabled it with the following for speedier stress testing:
t/t7519-status-fsmonitor.sh +1 -2
@@ -50,8 +50,7 @@ write_integration_script () { } test_lazy_prereq UNTRACKED_CACHE ' - { git update-index --test-untracked-cache; ret=$?; } && - test $ret -ne 1 + true ' test_expect_success 'setup' '
Quoted 13 lines · SZEDER Gábor
Quoted 10 lines · Utsav Shah via GitGitGadget
The checks added in
3444ec2eb2("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-11), to ensure that thefsmonitor_dirtybitmap does not have more bits than the index do not play well with the split index.git update-index --fsmonitor --split-index calls
write_locked_indexwhich callswrite_shared_indexas well aswrite_split_index. The first call fills up thefsmonitor_dirtybitmap,
and the second modifies the index such thatistate->cache_nris zero and this assert is hit.
Quoted 4 lines
Just to make sure that we are on the same page, is this the one?
BUG: fsmonitor.c:88: fsmonitor_dirty has more entries than the index (102 > 1)
Yes, that's the one.
Quoted 97 lines
Quoted 7 lines
The test written does reproduce the error, but only flakily. There is limited difference with GIT_TEST_FSMONITOR=fsmonitor-all or GIT_TEST_FSMONITOR=fsmonitor-watchman, so the flakiness might come from somewhere else, which I haven't tracked down.
The test also requires checkout of a new branch, and checking out back to master.
It doesn't; see below.
Quoted 18 lines
It's clear that the index gets into some poor state through these operations, and there is a deeper bug somewhere.
At the very least, this patch mitigates an over-eager check for split index users while maintaining good invariants for the standard case. Also, I haven't been able to reproduce this with "standard" user commands, like status/checkout/stash, so the blast radius seems limited.
Helped-by Kevin Willford <[email protected]>Helped-by Junio C Hamano <[email protected]>Signed-off-by Utsav Shah <[email protected]>---
t/t7519-status-fsmonitor.sh +0
-0t/t7519-status-fsmonitor.sh test_expect_success 'status succeeds after staging/unstaging ' ' · line 375
Quoted 5 lines
t/t7519-status-fsmonitor.sh +2
-0@@ -371,4 +371,27 @@ test_expect_success 'status succeeds after staging/unstaging ' ' ) ' +# Git will only split indices if we have a bunch of files created, +# so that prep work of creating a few hundred files is required.'git update-index --split-index' splits the index no matter what; it even splits an empty index.
Quoted 10 lines
t/t7519-status-fsmonitor.sh +10
-0+# Note that this test doesn't fail determinstically without +# its corresponding bugfix. +test_expect_success 'update-index succeeds after staging with split index' ' + test_create_repo fsmonitor-stage-split && + ( + cd fsmonitor-stage-split && + test_commit initial && + files=$(test_seq 1 100) && + echo "hello world" > file && + touch $files &&t/t7519-status-fsmonitor.sh test_expect_success 'status succeeds after staging/unstaging ' ' · line 395
Quoted 10 lines
t/t7519-status-fsmonitor.sh +10
-0+ git add -A && + git commit -m "next" && + git config core.fsmonitor "$TEST_DIRECTORY/t7519/fsmonitor-watchman" && + echo "hello world" > file && + git checkout -b new-branch && + git checkout master && + echo hello >> file && + git update-index --split-index --untracked-cache --fsmonitor + ) +'I could reproduce the failure with '-r 30 --stress' relatively easily [1], but with those options I could shave off a lot from this test:
test_create_repo fsmonitor-stage-split && ( cd fsmonitor-stage-split && >tracked && git add tracked && git config core.fsmonitor "$TEST_DIRECTORY/t7519/fsmonitor-watchman" && >untracked && git update-index --split-index --untracked-cache --fsmonitor )and could still trigger the failure:
+ git update-index --split-index --untracked-cache --fsmonitor
open2: exec of watchman -j failed at/home/szeder/src/git/t/t7519/fsmonitor-watchmanline 47.BUG: fsmonitor.c:88: fsmonitor_dirty has more entries than the index (1 > 0)[1] There is a quite expensive lazy prereq evaluation outside of 'test_expect_*' that I disabled it with the following for speedier stress testing:
t/t7519-status-fsmonitor.sh +1
-2@@ -50,8 +50,7 @@ write_integration_script () { } test_lazy_prereq UNTRACKED_CACHE ' - { git update-index --test-untracked-cache; ret=$?; } && - test $ret -ne 1 + true ' test_expect_success 'setup' '
Quoted 5 lines · Kevin Willford
I agree. The only 2 places that excluding the split-index make sense are in
read_fsmonitor_extensionandwrite_fsmonitor_extensionbecause theindex_statethat is being passing into those methods could be the delta index in which case the number of entries for the fsmonitor bitmap would almost always be more and cause the BUG to be hit which it should not be.
Thanks. Here is what I came up with to tie the loose ends of this thread.
-- >8 --
From: Junio C Hamano <[email protected]>
Subject: [PATCH] fsmonitor: do not compare bitmap size with size of split index
3444ec2e ("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-11) added a handful of sanity checks that make sure that a bit position in fsmonitor bitmap does not go beyond the end of the index. As each bit in the bitmap corresponds to a path in the index, this is the right check most of the time.
Except for the case when we are in the split-index mode and looking at a delta index that is to be overlayed on the base index but before the base index has actually been merged in, namely in read_ and write_fsmonitor_extension(). In these codepaths, the entries in the split/delta index is typically a small subset of the entire set of paths (otherwise why would we be using split-index?), so the bitmap used by the fsmonitor is almost always larger than the number of entries in the partial index, and the incorrect comparison would trigger the BUG().
--- fsmonitor.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
fsmonitor.c +4 -2
@@ -55,7 +55,8 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data, } istate->fsmonitor_dirty = fsmonitor_dirty; -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && + istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr);
@@ -83,7 +84,8 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)uint32_t ewah_size =0 ;int fixup =0 ; -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && + istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr);
Signature · 1 line
2.24.0-346-gee0de6d492
This looks good to me.
Quoted 67 lines · Junio C Hamano
Quoted 5 lines · Kevin Willford
I agree. The only 2 places that excluding the split-index make sense are in
read_fsmonitor_extensionandwrite_fsmonitor_extensionbecause theindex_statethat is being passing into those methods could be the delta index in which case the number of entries for the fsmonitor bitmap would almost always be more and cause the BUG to be hit which it should not be.Thanks. Here is what I came up with to tie the loose ends of this thread.
-- >8 --
From: Junio C Hamano <[email protected]>
Subject: [PATCH] fsmonitor: do not compare bitmap size with size of split index
3444ec2e("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-11) added a handful of sanity checks that make sure that a bit position in fsmonitor bitmap does not go beyond the end of the index. As each bit in the bitmap corresponds to a path in the index, this is the right check most of the time.Except for the case when we are in the split-index mode and looking at a delta index that is to be overlayed on the base index but before the base index has actually been merged in, namely in
read_andwrite_fsmonitor_extension(). In these codepaths, the entries in the split/delta index is typically a small subset of the entire set of paths (otherwise why would we be using split-index?), so the bitmap used by the fsmonitor is almost always larger than the number of entries in the partial index, and the incorrect comparison would trigger theBUG().Reported-by Utsav Shah <[email protected]>Helped-by Kevin Willford <[email protected]>Helped-by William Baker <[email protected]>Signed-off-by Junio C Hamano <[email protected]>--- fsmonitor.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)fsmonitor.c +4
-3@@ -55,7 +55,8 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data, } istate->fsmonitor_dirty = fsmonitor_dirty; -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && + istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr);@@ -83,7 +84,8 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)uint32_t ewah_size =0 ;int fixup =0 ; -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && + istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr); --2.24.0-346-gee0de6d492
Quoted 47 lines · Junio C Hamano
Thanks. Here is what I came up with to tie the loose ends of this thread.
-- >8 --
From: Junio C Hamano <[email protected]>
Subject: [PATCH] fsmonitor: do not compare bitmap size with size of split index
3444ec2e("fsmonitor: don't fill bitmap with entries to be removed", 2019-10-11) added a handful of sanity checks that make sure that a bit position in fsmonitor bitmap does not go beyond the end of the index. As each bit in the bitmap corresponds to a path in the index, this is the right check most of the time.Except for the case when we are in the split-index mode and looking at a delta index that is to be overlayed on the base index but before the base index has actually been merged in, namely in
read_andwrite_fsmonitor_extension(). In these codepaths, the entries in the split/delta index is typically a small subset of the entire set of paths (otherwise why would we be using split-index?), so the bitmap used by the fsmonitor is almost always larger than the number of entries in the partial index, and the incorrect comparison would trigger theBUG().Reported-by Utsav Shah <[email protected]>Helped-by Kevin Willford <[email protected]>Helped-by William Baker <[email protected]>Signed-off-by Junio C Hamano <[email protected]>--- fsmonitor.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)fsmonitor.c +2
-1@@ -55,7 +55,8 @@ int read_fsmonitor_extension(struct index_state *istate, const void *data, } istate->fsmonitor_dirty = fsmonitor_dirty; -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && + istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr);
fsmonitor.c void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate) · line 91
Quoted 9 lines
fsmonitor.c +2
-1@@ -83,7 +84,8 @@ void write_fsmonitor_extension(struct strbuf *sb, struct index_state *istate)uint32_t ewah_size =0 ;int fixup =0 ; -if (istate->fsmonitor_dirty->bit_size > istate->cache_nr) +if (!istate->split_index && + istate->fsmonitor_dirty->bit_size > istate->cache_nr)BUG ("fsmonitor_dirty has more entries than the index (%" PRIuMAX" > %u)" , (uintmax_t )istate->fsmonitor_dirty->bit_size, istate->cache_nr);
This looks good to me.
Thanks,
William
Quoted 6 lines · William Baker
Quoted 3 lines · Junio C Hamano
Thanks. Here is what I came up with to tie the loose ends of this thread.
...This looks good to me.
Thanks.