Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit 329e886

Browse files
committed
[VFS] Reconstruct the VFS overlay tree for more accurate lookup
The way we currently build the internal VFS overlay representation leads to inefficient path search and might yield wrong answers when asked for recursive or regular directory iteration. Currently, when reading an YAML file, each YAML root entry is placed inside a new root in the filesystem overlay. In the crash reproducer, a simple "@import Foundation" currently maps to 43 roots, and when looking up paths, we traverse a directory tree for each of these different roots, until we find a match (or don't). This has two consequences: - It's slow. - Directory iteration gives incomplete results since it only return results within one root - since contents of the same directory can be declared inside different roots, the result isn't accurate. This is in part fault of the way we currently write out the YAML file when emitting the crash reproducer - we could generate only one root and that would make it fast and correct again. However, we should not rely on how the client writes the YAML, but provide a good internal representation regardless. This patch builds a proper virtual directory tree out of the YAML representation, allowing faster search and proper iteration. Besides the crash reproducer, this potentially benefits other VFS clients. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@269100 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 06fd863 commit 329e886

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

Diff for: lib/Basic/VirtualFileSystem.cpp

+80-1
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,13 @@ class RedirectingDirectoryEntry : public Entry {
719719
Status S)
720720
: Entry(EK_Directory, Name), Contents(std::move(Contents)),
721721
S(std::move(S)) {}
722+
RedirectingDirectoryEntry(StringRef Name, Status S)
723+
: Entry(EK_Directory, Name), S(std::move(S)) {}
722724
Status getStatus() { return S; }
725+
void addContent(std::unique_ptr<Entry> Content) {
726+
Contents.push_back(std::move(Content));
727+
}
728+
Entry *getLastContent() const { return Contents.back().get(); }
723729
typedef decltype(Contents)::iterator iterator;
724730
iterator contents_begin() { return Contents.begin(); }
725731
iterator contents_end() { return Contents.end(); }
@@ -747,6 +753,7 @@ class RedirectingFileEntry : public Entry {
747753
return UseName == NK_NotSet ? GlobalUseExternalName
748754
: (UseName == NK_External);
749755
}
756+
NameKind getUseName() const { return UseName; }
750757
static bool classof(const Entry *E) { return E->getKind() == EK_File; }
751758
};
752759

@@ -1023,6 +1030,70 @@ class RedirectingFileSystemParser {
10231030
return true;
10241031
}
10251032

1033+
Entry *lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name,
1034+
Entry *ParentEntry = nullptr) {
1035+
if (!ParentEntry) { // Look for a existent root
1036+
for (const std::unique_ptr<Entry> &Root : FS->Roots) {
1037+
if (Name.equals(Root->getName())) {
1038+
ParentEntry = Root.get();
1039+
return ParentEntry;
1040+
}
1041+
}
1042+
} else { // Advance to the next component
1043+
auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry);
1044+
for (std::unique_ptr<Entry> &Content :
1045+
llvm::make_range(DE->contents_begin(), DE->contents_end())) {
1046+
auto *DirContent = dyn_cast<RedirectingDirectoryEntry>(Content.get());
1047+
if (DirContent && Name.equals(Content->getName()))
1048+
return DirContent;
1049+
}
1050+
}
1051+
1052+
// ... or create a new one
1053+
std::unique_ptr<Entry> E = llvm::make_unique<RedirectingDirectoryEntry>(
1054+
Name, Status("", getNextVirtualUniqueID(), sys::TimeValue::now(), 0, 0,
1055+
0, file_type::directory_file, sys::fs::all_all));
1056+
1057+
if (!ParentEntry) { // Add a new root to the overlay
1058+
FS->Roots.push_back(std::move(E));
1059+
ParentEntry = FS->Roots.back().get();
1060+
return ParentEntry;
1061+
}
1062+
1063+
auto *DE = dyn_cast<RedirectingDirectoryEntry>(ParentEntry);
1064+
DE->addContent(std::move(E));
1065+
return DE->getLastContent();
1066+
}
1067+
1068+
void uniqueOverlayTree(RedirectingFileSystem *FS, Entry *SrcE,
1069+
Entry *NewParentE = nullptr) {
1070+
StringRef Name = SrcE->getName();
1071+
switch (SrcE->getKind()) {
1072+
case EK_Directory: {
1073+
auto *DE = dyn_cast<RedirectingDirectoryEntry>(SrcE);
1074+
assert(DE && "Must be a directory");
1075+
// Empty directories could be present in the YAML as a way to
1076+
// describe a file for a current directory after some of its subdir
1077+
// is parsed. This only leads to redundant walks, ignore it.
1078+
if (!Name.empty())
1079+
NewParentE = lookupOrCreateEntry(FS, Name, NewParentE);
1080+
for (std::unique_ptr<Entry> &SubEntry :
1081+
llvm::make_range(DE->contents_begin(), DE->contents_end()))
1082+
uniqueOverlayTree(FS, SubEntry.get(), NewParentE);
1083+
break;
1084+
}
1085+
case EK_File: {
1086+
auto *FE = dyn_cast<RedirectingFileEntry>(SrcE);
1087+
assert(FE && "Must be a file");
1088+
assert(NewParentE && "Parent entry must exist");
1089+
auto *DE = dyn_cast<RedirectingDirectoryEntry>(NewParentE);
1090+
DE->addContent(llvm::make_unique<RedirectingFileEntry>(
1091+
Name, FE->getExternalContentsPath(), FE->getUseName()));
1092+
break;
1093+
}
1094+
}
1095+
}
1096+
10261097
std::unique_ptr<Entry> parseEntry(yaml::Node *N, RedirectingFileSystem *FS) {
10271098
yaml::MappingNode *M = dyn_cast<yaml::MappingNode>(N);
10281099
if (!M) {
@@ -1225,6 +1296,7 @@ class RedirectingFileSystemParser {
12251296
};
12261297

12271298
DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields));
1299+
std::vector<std::unique_ptr<Entry>> RootEntries;
12281300

12291301
// Parse configuration and 'roots'
12301302
for (yaml::MappingNode::iterator I = Top->begin(), E = Top->end(); I != E;
@@ -1247,7 +1319,7 @@ class RedirectingFileSystemParser {
12471319
for (yaml::SequenceNode::iterator I = Roots->begin(), E = Roots->end();
12481320
I != E; ++I) {
12491321
if (std::unique_ptr<Entry> E = parseEntry(&*I, FS))
1250-
FS->Roots.push_back(std::move(E));
1322+
RootEntries.push_back(std::move(E));
12511323
else
12521324
return false;
12531325
}
@@ -1288,6 +1360,13 @@ class RedirectingFileSystemParser {
12881360

12891361
if (!checkMissingKeys(Top, Keys))
12901362
return false;
1363+
1364+
// Now that we sucessefully parsed the YAML file, canonicalize the internal
1365+
// representation to a proper directory tree so that we can search faster
1366+
// inside the VFS.
1367+
for (std::unique_ptr<Entry> &E : RootEntries)
1368+
uniqueOverlayTree(FS, E.get());
1369+
12911370
return true;
12921371
}
12931372
};

Diff for: unittests/Basic/VirtualFileSystemTest.cpp

+30-1
Original file line numberDiff line numberDiff line change
@@ -1022,9 +1022,14 @@ TEST_F(VFSFromYAMLTest, DirectoryIteration) {
10221022
Lower->addDirectory("//root/");
10231023
Lower->addDirectory("//root/foo");
10241024
Lower->addDirectory("//root/foo/bar");
1025+
Lower->addDirectory("//root/zab");
1026+
Lower->addDirectory("//root/baz");
10251027
Lower->addRegularFile("//root/foo/bar/a");
10261028
Lower->addRegularFile("//root/foo/bar/b");
10271029
Lower->addRegularFile("//root/file3");
1030+
Lower->addRegularFile("//root/zab/a");
1031+
Lower->addRegularFile("//root/zab/b");
1032+
Lower->addRegularFile("//root/baz/c");
10281033
IntrusiveRefCntPtr<vfs::FileSystem> FS =
10291034
getFromYAMLString("{ 'use-external-names': false,\n"
10301035
" 'roots': [\n"
@@ -1042,6 +1047,26 @@ TEST_F(VFSFromYAMLTest, DirectoryIteration) {
10421047
" 'external-contents': '//root/foo/bar/b'\n"
10431048
" }\n"
10441049
" ]\n"
1050+
"},\n"
1051+
"{\n"
1052+
" 'type': 'directory',\n"
1053+
" 'name': '//root/baz',\n"
1054+
" 'contents': [ {\n"
1055+
" 'type': 'file',\n"
1056+
" 'name': 'x',\n"
1057+
" 'external-contents': '//root/zab/a'\n"
1058+
" }\n"
1059+
" ]\n"
1060+
"},\n"
1061+
"{\n"
1062+
" 'type': 'directory',\n"
1063+
" 'name': '//root/baz',\n"
1064+
" 'contents': [ {\n"
1065+
" 'type': 'file',\n"
1066+
" 'name': 'y',\n"
1067+
" 'external-contents': '//root/zab/b'\n"
1068+
" }\n"
1069+
" ]\n"
10451070
"}\n"
10461071
"]\n"
10471072
"}",
@@ -1054,8 +1079,12 @@ TEST_F(VFSFromYAMLTest, DirectoryIteration) {
10541079

10551080
std::error_code EC;
10561081
checkContents(O->dir_begin("//root/", EC),
1057-
{"//root/file1", "//root/file2", "//root/file3", "//root/foo"});
1082+
{"//root/file1", "//root/file2", "//root/baz", "//root/file3",
1083+
"//root/foo", "//root/zab"});
10581084

10591085
checkContents(O->dir_begin("//root/foo/bar", EC),
10601086
{"//root/foo/bar/a", "//root/foo/bar/b"});
1087+
1088+
checkContents(O->dir_begin("//root/baz", EC),
1089+
{"//root/baz/x", "//root/baz/y", "//root/baz/c"});
10611090
}

0 commit comments

Comments
 (0)