This is an archive of the discontinued LLVM Phabricator instance.

[llvm-cov] Prevent llvm-cov from hanging when a symblink doesn't exist.
ClosedPublic

Authored by liaoyuke on Mar 27 2018, 3:25 PM.

Details

Summary

Previous code hangs indefinitely when trying to iterate through a
symbol link file that points to an non-exist directory. This change
fixes the bug to make the addCollectedPath function exit ealier and
print out correct warning messages.

Patch by Yuke Liao (@liaoyuke).

Diff Detail

Repository
rL LLVM

Event Timeline

liaoyuke created this revision.Mar 27 2018, 3:25 PM
vsk added a comment.Mar 27 2018, 3:31 PM

Thanks for the patch. Some inline comments --

include/llvm/Support/FileSystem.h
1036 ↗(On Diff #140011)

Could you add a test for this in unittests/Support/Path.cpp?

tools/llvm-cov/CodeCoverage.cpp
202 ↗(On Diff #140011)

Nitpicks: could you capitalize 'source', and change 'exit' -> 'exist'?

222 ↗(On Diff #140011)

Nitpick, please capitalize 'code'.

Dor1s added inline comments.Mar 27 2018, 4:22 PM
tools/llvm-cov/CodeCoverage.cpp
213 ↗(On Diff #140011)

int: I think PascalCase is preferred, so this needs to be PathBeforeErrorOccurs

liaoyuke updated this revision to Diff 140028.Mar 27 2018, 5:46 PM
liaoyuke marked 4 inline comments as done.

Addressed comments and added unit test.

Thanks for the comments, please take another look. Thank you very much!

include/llvm/Support/FileSystem.h
1036 ↗(On Diff #140011)

Just found out there are already a test covering broken symlinks, but unfortunately, that test was artificially manipulated to pass, so it didn't catch the hang, I've modified the test.

vsk added inline comments.Mar 27 2018, 5:55 PM
unittests/Support/Path.cpp
911 ↗(On Diff #140028)

Could you keep the checks for what the visited set should be? I think we'd get the behavior we're interested in by changing the 'break' here to a 'continue'.

liaoyuke added inline comments.Mar 27 2018, 6:09 PM
unittests/Support/Path.cpp
911 ↗(On Diff #140028)

This is the part that gets tricky, we can't call i.increment() again because it will hang. We can do what the original test did to help this test pass, however, this won't help us catch real bugs.

Another option is that we can make make the recursive_directory_iterator more robust that it is capable of skipping broken symlink files by itself.

WDYT?

liaoyuke added inline comments.Mar 27 2018, 6:11 PM
unittests/Support/Path.cpp
911 ↗(On Diff #140028)

With the new behavior, a broken symlink won't prevent other paths from being collected, which actually makes more sense to me.

vsk added a comment.Mar 27 2018, 6:18 PM

Ideally, if the directory iterator finds a broken symlink, it would be able to report the error and continue its walk. I haven't looked at the code closely enough to say definitively, but I think this should be possible without changing the API.

Dor1s added inline comments.Mar 28 2018, 8:12 AM
unittests/Support/Path.cpp
911 ↗(On Diff #140028)

That indeed sounds better. Have you already looked into that?

liaoyuke added inline comments.Mar 28 2018, 8:42 AM
unittests/Support/Path.cpp
911 ↗(On Diff #140028)

Yes, will upload a new patch later.

liaoyuke updated this revision to Diff 140100.Mar 28 2018, 10:28 AM

When recursive directory iterator encounters an error, it either hangs
or exits immediately in previous implementations.

This change modifies the behavior to report the error and continue
its walk, and added corresponding unit tests to verify it.

vsk added a comment.Mar 28 2018, 1:58 PM

This is looking really good, thanks for digging into it. Without this patch, clients of the directory iterator need to check for broken symlinks explicitly and issue a call to "i.no_push()". That clearly isn't happening everywhere it should. This patch really improves the API.

Note that after this lands, we'll need to fix up clients of this API in llvm-profdata.cpp and in lldb (inside of FileSpec::EnumerateDirectory).

I have a few more suggestions inline --

include/llvm/Support/FileSystem.h
959 ↗(On Diff #140100)

Why not sink the "!ec" test into check_current_entry?

1006 ↗(On Diff #140100)

Could you rename this to something more explicit, such as update_error_code_for_current_entry?

unittests/Support/Path.cpp
914 ↗(On Diff #140100)

You can just write: "find(NamesOfBrokenSymlinks, CurrentFileName)". LLVM defines range-based algorithms in llvm/ADT/STLExtras.h.

918 ↗(On Diff #140100)

Should this be something like, ASSERT_EQUAL(ec.category(), std::errc::no_such_file_or_directory)?

925 ↗(On Diff #140100)

Could you retain the behavior of the original test and continue when a broken symlink is found?

As you did above, you can check that the set of broken symlink names is what you expect.

liaoyuke updated this revision to Diff 140285.Mar 29 2018, 10:35 AM
liaoyuke marked 5 inline comments as done.

Address comments.

vsk added a subscriber: bruno.Mar 29 2018, 1:32 PM

@liaoyuke I tested this out and found that it breaks the BrokenSymlinkRealFSRecursiveIteration unit test in clang, among some other modules tests. I couldn't find an issue with the test. Mind taking a look?

Of course! I'll look at it later, sorry about the inconvenience.

include/llvm/Support/FileSystem.h
959 ↗(On Diff #140100)

Good idea!

unittests/Support/Path.cpp
914 ↗(On Diff #140100)

This is definitely more concise, thanks for letting me know!

liaoyuke updated this revision to Diff 140649.Apr 2 2018, 11:11 AM

Fix unit test errors.

Please hold on reviewing, I've some problem uploading the changes under clang/

vsk accepted this revision.Apr 4 2018, 12:06 PM

Looks great!

I tested this (and D45178) out with 'check-{llvm,clang,lldb-lit,lldb-unit}'. While applying the patch, I noticed some tab/space inconsistencies which Phab isn't rendering, but that should be easy to fix before committing.

This revision is now accepted and ready to land.Apr 4 2018, 12:06 PM
liaoyuke updated this revision to Diff 141073.Apr 4 2018, 3:22 PM

Convert tabs to spaces and fix format.

I've converted all the tabs to spaces, sorry about the trouble. Please take another look. Thank you very much!

vsk added a comment.Apr 4 2018, 3:25 PM

No worries, feel free to commit whitespace/formatting fixes without review.

Dor1s edited the summary of this revision. (Show Details)Apr 5 2018, 12:41 PM
Dor1s retitled this revision from Prevent llvm-cov from hanging when a symblink doesn't exist. to [llvm-cov] Prevent llvm-cov from hanging when a symblink doesn't exist..
This revision was automatically updated to reflect the committed changes.

Sorry for the late followup here, I just found this patch while investigating some misbehavior.

It appears the update_error_code_for_current_entry prevents iterating over a directory without stat()ing every file in it.
This is bad for performance in the common case where only the name/type is required.
(There were other things in fs/vfs causing every file to be stat()ed, they are now fixed).

Moreover, the idea that a dangling symlink is an error for a *directory listing* operation is surprising, and seems like solving problems at the wrong layer.
(e.g. recursive_directory_iterator needs to know if an entry is a dangling symlink, but it should find out using opendir() rather than with stat())

If there are no objections, I'd like to remove this behavior of directory_iterator so we can have fast directory iteration, and find a different fix for recursive_directory_iterator.