Index: clang-tools-extra/trunk/clangd/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clangd/CMakeLists.txt +++ clang-tools-extra/trunk/clangd/CMakeLists.txt @@ -73,6 +73,7 @@ XRefs.cpp index/Background.cpp + index/BackgroundIndexLoader.cpp index/BackgroundIndexStorage.cpp index/BackgroundQueue.cpp index/BackgroundRebuild.cpp Index: clang-tools-extra/trunk/clangd/ClangdServer.cpp =================================================================== --- clang-tools-extra/trunk/clangd/ClangdServer.cpp +++ clang-tools-extra/trunk/clangd/ClangdServer.cpp @@ -127,7 +127,8 @@ if (Opts.BackgroundIndex) { BackgroundIdx = llvm::make_unique( Context::current().clone(), FSProvider, CDB, - BackgroundIndexStorage::createDiskBackedStorageFactory()); + BackgroundIndexStorage::createDiskBackedStorageFactory( + [&CDB](llvm::StringRef File) { return CDB.getProjectInfo(File); })); AddIndex(BackgroundIdx.get()); } if (DynamicIdx) Index: clang-tools-extra/trunk/clangd/index/Background.h =================================================================== --- clang-tools-extra/trunk/clangd/index/Background.h +++ clang-tools-extra/trunk/clangd/index/Background.h @@ -12,6 +12,7 @@ #include "Context.h" #include "FSProvider.h" #include "GlobalCompilationDatabase.h" +#include "Path.h" #include "SourceCode.h" #include "Threading.h" #include "index/BackgroundRebuild.h" @@ -49,15 +50,17 @@ virtual std::unique_ptr loadShard(llvm::StringRef ShardIdentifier) const = 0; - // The factory provides storage for each CDB. + // The factory provides storage for each File. // It keeps ownership of the storage instances, and should manage caching // itself. Factory must be threadsafe and never returns nullptr. - using Factory = - llvm::unique_function; + using Factory = llvm::unique_function; // Creates an Index Storage that saves shards into disk. Index storage uses - // CDBDirectory + ".clangd/index/" as the folder to save shards. - static Factory createDiskBackedStorageFactory(); + // CDBDirectory + ".clangd/index/" as the folder to save shards. CDBDirectory + // is the first directory containing a CDB in parent directories of a file, or + // user's home directory if none was found, e.g. standard library headers. + static Factory createDiskBackedStorageFactory( + std::function(PathRef)> GetProjectInfo); }; // A priority queue of tasks which can be run on (external) worker threads. @@ -157,15 +160,14 @@ /// information on IndexStorage. void update(llvm::StringRef MainFile, IndexFileIn Index, const llvm::StringMap &ShardVersionsSnapshot, - BackgroundIndexStorage *IndexStorage, bool HadErrors); + bool HadErrors); // configuration const FileSystemProvider &FSProvider; const GlobalCompilationDatabase &CDB; Context BackgroundContext; - llvm::Error index(tooling::CompileCommand, - BackgroundIndexStorage *IndexStorage); + llvm::Error index(tooling::CompileCommand); FileSymbols IndexedSymbols; BackgroundIndexRebuilder Rebuilder; @@ -173,25 +175,13 @@ std::mutex ShardVersionsMu; BackgroundIndexStorage::Factory IndexStorageFactory; - struct Source { - std::string Path; - bool NeedsReIndexing; - Source(llvm::StringRef Path, bool NeedsReIndexing) - : Path(Path), NeedsReIndexing(NeedsReIndexing) {} - }; - // Loads the shards for a single TU and all of its dependencies. Returns the - // list of sources and whether they need to be re-indexed. - std::vector loadShard(const tooling::CompileCommand &Cmd, - BackgroundIndexStorage *IndexStorage, - llvm::StringSet<> &LoadedShards); - // Tries to load shards for the ChangedFiles. - std::vector> - loadShards(std::vector ChangedFiles); + // Tries to load shards for the MainFiles and their dependencies. + std::vector + loadProject(std::vector MainFiles); BackgroundQueue::Task changedFilesTask(const std::vector &ChangedFiles); - BackgroundQueue::Task indexFileTask(tooling::CompileCommand Cmd, - BackgroundIndexStorage *Storage); + BackgroundQueue::Task indexFileTask(tooling::CompileCommand Cmd); // from lowest to highest priority enum QueuePriority { Index: clang-tools-extra/trunk/clangd/index/Background.cpp =================================================================== --- clang-tools-extra/trunk/clangd/index/Background.cpp +++ clang-tools-extra/trunk/clangd/index/Background.cpp @@ -10,6 +10,7 @@ #include "ClangdUnit.h" #include "Compiler.h" #include "Context.h" +#include "FSProvider.h" #include "Headers.h" #include "Logger.h" #include "Path.h" @@ -18,6 +19,7 @@ #include "Threading.h" #include "Trace.h" #include "URI.h" +#include "index/BackgroundIndexLoader.h" #include "index/FileIndex.h" #include "index/IndexAction.h" #include "index/MemIndex.h" @@ -28,6 +30,8 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Driver/Types.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" @@ -42,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +54,8 @@ #include #include #include +#include +#include namespace clang { namespace clangd { @@ -119,6 +126,18 @@ } return AbsolutePath; } + +bool shardIsStale(const LoadedShard &LS, llvm::vfs::FileSystem *FS) { + auto Buf = FS->getBufferForFile(LS.AbsolutePath); + if (!Buf) { + elog("Background-index: Couldn't read {0} to validate stored index: {1}", + LS.AbsolutePath, Buf.getError().message()); + // There is no point in indexing an unreadable file. + return false; + } + return digest(Buf->get()->getBuffer()) != LS.Digest; +} + } // namespace BackgroundIndex::BackgroundIndex( @@ -156,14 +175,14 @@ log("Enqueueing {0} commands for indexing", ChangedFiles.size()); SPAN_ATTACH(Tracer, "files", int64_t(ChangedFiles.size())); - auto NeedsReIndexing = loadShards(std::move(ChangedFiles)); + auto NeedsReIndexing = loadProject(std::move(ChangedFiles)); // Run indexing for files that need to be updated. std::shuffle(NeedsReIndexing.begin(), NeedsReIndexing.end(), std::mt19937(std::random_device{}())); std::vector Tasks; Tasks.reserve(NeedsReIndexing.size()); - for (auto &Elem : NeedsReIndexing) - Tasks.push_back(indexFileTask(std::move(Elem.first), Elem.second)); + for (auto &Cmd : NeedsReIndexing) + Tasks.push_back(indexFileTask(std::move(Cmd))); Queue.append(std::move(Tasks)); }); @@ -178,13 +197,12 @@ } BackgroundQueue::Task -BackgroundIndex::indexFileTask(tooling::CompileCommand Cmd, - BackgroundIndexStorage *Storage) { - BackgroundQueue::Task T([this, Storage, Cmd] { +BackgroundIndex::indexFileTask(tooling::CompileCommand Cmd) { + BackgroundQueue::Task T([this, Cmd] { // We can't use llvm::StringRef here since we are going to // move from Cmd during the call below. const std::string FileName = Cmd.Filename; - if (auto Error = index(std::move(Cmd), Storage)) + if (auto Error = index(std::move(Cmd))) elog("Indexing {0} failed: {1}", FileName, std::move(Error)); }); T.QueuePri = IndexFile; @@ -207,7 +225,7 @@ void BackgroundIndex::update( llvm::StringRef MainFile, IndexFileIn Index, const llvm::StringMap &ShardVersionsSnapshot, - BackgroundIndexStorage *IndexStorage, bool HadErrors) { + bool HadErrors) { // Partition symbols/references into files. struct File { llvm::DenseSet Symbols; @@ -291,22 +309,21 @@ // We need to store shards before updating the index, since the latter // consumes slabs. // FIXME: Also skip serializing the shard if it is already up-to-date. - if (IndexStorage) { - IndexFileOut Shard; - Shard.Symbols = SS.get(); - Shard.Refs = RS.get(); - Shard.Relations = RelS.get(); - Shard.Sources = IG.get(); - - // Only store command line hash for main files of the TU, since our - // current model keeps only one version of a header file. - if (Path == MainFile) - Shard.Cmd = Index.Cmd.getPointer(); - - if (auto Error = IndexStorage->storeShard(Path, Shard)) - elog("Failed to write background-index shard for file {0}: {1}", Path, - std::move(Error)); - } + BackgroundIndexStorage *IndexStorage = IndexStorageFactory(Path); + IndexFileOut Shard; + Shard.Symbols = SS.get(); + Shard.Refs = RS.get(); + Shard.Relations = RelS.get(); + Shard.Sources = IG.get(); + + // Only store command line hash for main files of the TU, since our + // current model keeps only one version of a header file. + if (Path == MainFile) + Shard.Cmd = Index.Cmd.getPointer(); + + if (auto Error = IndexStorage->storeShard(Path, Shard)) + elog("Failed to write background-index shard for file {0}: {1}", Path, + std::move(Error)); { std::lock_guard Lock(ShardVersionsMu); @@ -329,8 +346,7 @@ } } -llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd, - BackgroundIndexStorage *IndexStorage) { +llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) { trace::Span Tracer("BackgroundIndex"); SPAN_ATTACH(Tracer, "file", Cmd.Filename); auto AbsolutePath = getAbsolutePath(Cmd); @@ -424,176 +440,78 @@ for (auto &It : *Index.Sources) It.second.Flags |= IncludeGraphNode::SourceFlag::HadErrors; } - update(AbsolutePath, std::move(Index), ShardVersionsSnapshot, IndexStorage, - HadErrors); + update(AbsolutePath, std::move(Index), ShardVersionsSnapshot, HadErrors); Rebuilder.indexedTU(); return llvm::Error::success(); } -std::vector -BackgroundIndex::loadShard(const tooling::CompileCommand &Cmd, - BackgroundIndexStorage *IndexStorage, - llvm::StringSet<> &LoadedShards) { - struct ShardInfo { - std::string AbsolutePath; - std::unique_ptr Shard; - FileDigest Digest = {}; - bool CountReferences = false; - bool HadErrors = false; - }; - std::vector IntermediateSymbols; - // Make sure we don't have duplicate elements in the queue. Keys are absolute - // paths. - llvm::StringSet<> InQueue; - auto FS = FSProvider.getFileSystem(); - // Dependencies of this TU, paired with the information about whether they - // need to be re-indexed or not. - std::vector Dependencies; - std::queue ToVisit; - std::string AbsolutePath = getAbsolutePath(Cmd).str(); - // Up until we load the shard related to a dependency it needs to be - // re-indexed. - ToVisit.emplace(AbsolutePath, true); - InQueue.insert(AbsolutePath); - // Goes over each dependency. - while (!ToVisit.empty()) { - Dependencies.push_back(std::move(ToVisit.front())); - // Dependencies is not modified during the rest of the loop, so it is safe - // to keep the reference. - auto &CurDependency = Dependencies.back(); - ToVisit.pop(); - // If we have already seen this shard before(either loaded or failed) don't - // re-try again. Since the information in the shard won't change from one TU - // to another. - if (!LoadedShards.try_emplace(CurDependency.Path).second) { - // If the dependency needs to be re-indexed, first occurence would already - // have detected that, so we don't need to issue it again. - CurDependency.NeedsReIndexing = false; - continue; - } +// Restores shards for \p MainFiles from index storage. Then checks staleness of +// those shards and returns a list of TUs that needs to be indexed to update +// staleness. +std::vector +BackgroundIndex::loadProject(std::vector MainFiles) { + std::vector NeedsReIndexing; - auto Shard = IndexStorage->loadShard(CurDependency.Path); - if (!Shard || !Shard->Sources) { - // File will be returned as requiring re-indexing to caller. - vlog("Failed to load shard: {0}", CurDependency.Path); - continue; - } - // These are the edges in the include graph for current dependency. - for (const auto &I : *Shard->Sources) { - auto U = URI::parse(I.getKey()); - if (!U) - continue; - auto AbsolutePath = URI::resolve(*U, CurDependency.Path); - if (!AbsolutePath) - continue; - // Add file as dependency if haven't seen before. - if (InQueue.try_emplace(*AbsolutePath).second) - ToVisit.emplace(*AbsolutePath, true); - // The node contains symbol information only for current file, the rest is - // just edges. - if (*AbsolutePath != CurDependency.Path) - continue; - - // We found source file info for current dependency. - assert(I.getValue().Digest != FileDigest{{0}} && "Digest is empty?"); - ShardInfo SI; - SI.AbsolutePath = CurDependency.Path; - SI.Shard = std::move(Shard); - SI.Digest = I.getValue().Digest; - SI.CountReferences = - I.getValue().Flags & IncludeGraphNode::SourceFlag::IsTU; - SI.HadErrors = - I.getValue().Flags & IncludeGraphNode::SourceFlag::HadErrors; - IntermediateSymbols.push_back(std::move(SI)); - // Check if the source needs re-indexing. - // Get the digest, skip it if file doesn't exist. - auto Buf = FS->getBufferForFile(CurDependency.Path); - if (!Buf) { - elog("Couldn't get buffer for file: {0}: {1}", CurDependency.Path, - Buf.getError().message()); - continue; - } - // If digests match then dependency doesn't need re-indexing. - // FIXME: Also check for dependencies(sources) of this shard and compile - // commands for cache invalidation. - CurDependency.NeedsReIndexing = - digest(Buf->get()->getBuffer()) != I.getValue().Digest; - } - } - // Load shard information into background-index. + Rebuilder.startLoading(); + // Load shards for all of the mainfiles. + const std::vector Result = + loadIndexShards(MainFiles, IndexStorageFactory, CDB); + size_t LoadedShards = 0; { + // Update in-memory state. std::lock_guard Lock(ShardVersionsMu); - // This can override a newer version that is added in another thread, - // if this thread sees the older version but finishes later. This - // should be rare in practice. - for (const ShardInfo &SI : IntermediateSymbols) { + for (auto &LS : Result) { + if (!LS.Shard) + continue; auto SS = - SI.Shard->Symbols - ? llvm::make_unique(std::move(*SI.Shard->Symbols)) + LS.Shard->Symbols + ? llvm::make_unique(std::move(*LS.Shard->Symbols)) : nullptr; - auto RS = SI.Shard->Refs - ? llvm::make_unique(std::move(*SI.Shard->Refs)) + auto RS = LS.Shard->Refs + ? llvm::make_unique(std::move(*LS.Shard->Refs)) : nullptr; auto RelS = - SI.Shard->Relations - ? llvm::make_unique(std::move(*SI.Shard->Relations)) + LS.Shard->Relations + ? llvm::make_unique(std::move(*LS.Shard->Relations)) : nullptr; - ShardVersion &SV = ShardVersions[SI.AbsolutePath]; - SV.Digest = SI.Digest; - SV.HadErrors = SI.HadErrors; + ShardVersion &SV = ShardVersions[LS.AbsolutePath]; + SV.Digest = LS.Digest; + SV.HadErrors = LS.HadErrors; + ++LoadedShards; - IndexedSymbols.update(SI.AbsolutePath, std::move(SS), std::move(RS), - std::move(RelS), SI.CountReferences); + IndexedSymbols.update(LS.AbsolutePath, std::move(SS), std::move(RS), + std::move(RelS), LS.CountReferences); } } - if (!IntermediateSymbols.empty()) - Rebuilder.loadedTU(); + Rebuilder.loadedShard(LoadedShards); + Rebuilder.doneLoading(); - return Dependencies; -} + auto FS = FSProvider.getFileSystem(); + llvm::DenseSet TUsToIndex; + // We'll accept data from stale shards, but ensure the files get reindexed + // soon. + for (auto &LS : Result) { + if (!shardIsStale(LS, FS.get())) + continue; + PathRef TUForFile = LS.DependentTU; + assert(!TUForFile.empty() && "File without a TU!"); -// Goes over each changed file and loads them from index. Returns the list of -// TUs that had out-of-date/no shards. -std::vector> -BackgroundIndex::loadShards(std::vector ChangedFiles) { - std::vector> - NeedsReIndexing; - // Keeps track of the files that will be reindexed, to make sure we won't - // re-index same dependencies more than once. Keys are AbsolutePaths. - llvm::StringSet<> FilesToIndex; - // Keeps track of the loaded shards to make sure we don't perform redundant - // disk IO. Keys are absolute paths. - llvm::StringSet<> LoadedShards; - Rebuilder.startLoading(); - for (const auto &File : ChangedFiles) { - auto Cmd = CDB.getCompileCommand(File); + // FIXME: Currently, we simply schedule indexing on a TU whenever any of + // its dependencies needs re-indexing. We might do it smarter by figuring + // out a minimal set of TUs that will cover all the stale dependencies. + // FIXME: Try looking at other TUs if no compile commands are available + // for this TU, i.e TU was deleted after we performed indexing. + TUsToIndex.insert(TUForFile); + } + + for (PathRef TU : TUsToIndex) { + auto Cmd = CDB.getCompileCommand(TU); if (!Cmd) continue; - - std::string ProjectRoot; - if (auto PI = CDB.getProjectInfo(File)) - ProjectRoot = std::move(PI->SourceRoot); - - BackgroundIndexStorage *IndexStorage = IndexStorageFactory(ProjectRoot); - auto Dependencies = loadShard(*Cmd, IndexStorage, LoadedShards); - for (const auto &Dependency : Dependencies) { - if (!Dependency.NeedsReIndexing || FilesToIndex.count(Dependency.Path)) - continue; - // FIXME: Currently, we simply schedule indexing on a TU whenever any of - // its dependencies needs re-indexing. We might do it smarter by figuring - // out a minimal set of TUs that will cover all the stale dependencies. - vlog("Enqueueing TU {0} because its dependency {1} needs re-indexing.", - Cmd->Filename, Dependency.Path); - NeedsReIndexing.push_back({std::move(*Cmd), IndexStorage}); - // Mark all of this TU's dependencies as to-be-indexed so that we won't - // try to re-index those. - for (const auto &Dependency : Dependencies) - FilesToIndex.insert(Dependency.Path); - break; - } + NeedsReIndexing.emplace_back(std::move(*Cmd)); } - Rebuilder.doneLoading(); + return NeedsReIndexing; } Index: clang-tools-extra/trunk/clangd/index/BackgroundIndexLoader.h =================================================================== --- clang-tools-extra/trunk/clangd/index/BackgroundIndexLoader.h +++ clang-tools-extra/trunk/clangd/index/BackgroundIndexLoader.h @@ -0,0 +1,54 @@ +//===--- BackgroundIndexLoader.h - Load shards from index storage-*- C++-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_BACKGROUND_INDEX_LOADER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_BACKGROUND_INDEX_LOADER_H + +#include "Path.h" +#include "index/Background.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/VirtualFileSystem.h" +#include +#include + +namespace clang { +namespace clangd { + +/// Represents a shard loaded from storage, stores contents in \p Shard and +/// metadata about the source file that generated this shard. +struct LoadedShard { + /// Path of the source file that produced this shard. + Path AbsolutePath; + /// Digest of the source file contents that produced this shard. + FileDigest Digest = {}; + /// Whether the RefSlab in Shard should be used for updating symbol reference + /// counts when building an index. + bool CountReferences = false; + /// Whether the indexing action producing that shard had errors. + bool HadErrors = false; + /// Path to a TU that is depending on this shard. + Path DependentTU; + /// Will be nullptr when index storage couldn't provide a valid shard for + /// AbsolutePath. + std::unique_ptr Shard; +}; + +/// Loads all shards for the TU \p MainFile from \p Storage. +std::vector +loadIndexShards(llvm::ArrayRef MainFiles, + BackgroundIndexStorage::Factory &IndexStorageFactory, + const GlobalCompilationDatabase &CDB); + +} // namespace clangd +} // namespace clang + +#endif Index: clang-tools-extra/trunk/clangd/index/BackgroundIndexLoader.cpp =================================================================== --- clang-tools-extra/trunk/clangd/index/BackgroundIndexLoader.cpp +++ clang-tools-extra/trunk/clangd/index/BackgroundIndexLoader.cpp @@ -0,0 +1,143 @@ +//===-- BackgroundIndexLoader.cpp - ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "index/BackgroundIndexLoader.h" +#include "GlobalCompilationDatabase.h" +#include "Logger.h" +#include "Path.h" +#include "index/Background.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/Path.h" +#include +#include +#include + +namespace clang { +namespace clangd { +namespace { + +llvm::Optional uriToAbsolutePath(llvm::StringRef URI, PathRef HintPath) { + auto U = URI::parse(URI); + if (!U) + return llvm::None; + auto AbsolutePath = URI::resolve(*U, HintPath); + if (!AbsolutePath) + return llvm::None; + return *AbsolutePath; +} + +/// A helper class to cache BackgroundIndexStorage operations and keep the +/// inverse dependency mapping. +class BackgroundIndexLoader { +public: + BackgroundIndexLoader(BackgroundIndexStorage::Factory &IndexStorageFactory) + : IndexStorageFactory(IndexStorageFactory) {} + /// Load the shards for \p MainFile and all of its dependencies. + void load(PathRef MainFile); + + /// Consumes the loader and returns all shards. + std::vector takeResult() &&; + +private: + /// Returns the Shard for \p StartSourceFile from cache or loads it from \p + /// Storage. Also returns paths for dependencies of \p StartSourceFile if it + /// wasn't cached yet. + std::pair> + loadShard(PathRef StartSourceFile, PathRef DependentTU); + + /// Cache for Storage lookups. + llvm::StringMap LoadedShards; + + BackgroundIndexStorage::Factory &IndexStorageFactory; +}; + +std::pair> +BackgroundIndexLoader::loadShard(PathRef StartSourceFile, PathRef DependentTU) { + auto It = LoadedShards.try_emplace(StartSourceFile); + LoadedShard &LS = It.first->getValue(); + std::vector Edges = {}; + // Return the cached shard. + if (!It.second) + return {LS, Edges}; + + LS.AbsolutePath = StartSourceFile.str(); + LS.DependentTU = DependentTU; + BackgroundIndexStorage *Storage = IndexStorageFactory(LS.AbsolutePath); + auto Shard = Storage->loadShard(StartSourceFile); + if (!Shard || !Shard->Sources) { + vlog("Failed to load shard: {0}", StartSourceFile); + return {LS, Edges}; + } + + LS.Shard = std::move(Shard); + for (const auto &It : *LS.Shard->Sources) { + auto AbsPath = uriToAbsolutePath(It.getKey(), StartSourceFile); + if (!AbsPath) + continue; + // A shard contains only edges for non main-file sources. + if (*AbsPath != StartSourceFile) { + Edges.push_back(*AbsPath); + continue; + } + + // Fill in shard metadata. + const IncludeGraphNode &IGN = It.getValue(); + LS.Digest = IGN.Digest; + LS.CountReferences = IGN.Flags & IncludeGraphNode::SourceFlag::IsTU; + LS.HadErrors = IGN.Flags & IncludeGraphNode::SourceFlag::HadErrors; + } + assert(LS.Digest != FileDigest{{0}} && "Digest is empty?"); + return {LS, Edges}; +} + +void BackgroundIndexLoader::load(PathRef MainFile) { + llvm::StringSet<> InQueue; + // Following containers points to strings inside InQueue. + std::queue ToVisit; + InQueue.insert(MainFile); + ToVisit.push(MainFile); + + while (!ToVisit.empty()) { + PathRef SourceFile = ToVisit.front(); + ToVisit.pop(); + + auto ShardAndEdges = loadShard(SourceFile, MainFile); + for (PathRef Edge : ShardAndEdges.second) { + auto It = InQueue.insert(Edge); + if (It.second) + ToVisit.push(It.first->getKey()); + } + } +} + +std::vector BackgroundIndexLoader::takeResult() && { + std::vector Result; + Result.reserve(LoadedShards.size()); + for (auto &It : LoadedShards) + Result.push_back(std::move(It.getValue())); + return Result; +} +} // namespace + +std::vector +loadIndexShards(llvm::ArrayRef MainFiles, + BackgroundIndexStorage::Factory &IndexStorageFactory, + const GlobalCompilationDatabase &CDB) { + BackgroundIndexLoader Loader(IndexStorageFactory); + for (llvm::StringRef MainFile : MainFiles) { + assert(llvm::sys::path::is_absolute(MainFile)); + Loader.load(MainFile); + } + return std::move(Loader).takeResult(); +} + +} // namespace clangd +} // namespace clang Index: clang-tools-extra/trunk/clangd/index/BackgroundIndexStorage.cpp =================================================================== --- clang-tools-extra/trunk/clangd/index/BackgroundIndexStorage.cpp +++ clang-tools-extra/trunk/clangd/index/BackgroundIndexStorage.cpp @@ -6,13 +6,21 @@ // //===----------------------------------------------------------------------===// +#include "GlobalCompilationDatabase.h" #include "Logger.h" +#include "Path.h" #include "index/Background.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include namespace clang { namespace clangd { @@ -118,12 +126,21 @@ // Creates and owns IndexStorages for multiple CDBs. class DiskBackedIndexStorageManager { public: - DiskBackedIndexStorageManager() - : IndexStorageMapMu(llvm::make_unique()) {} + DiskBackedIndexStorageManager( + std::function(PathRef)> GetProjectInfo) + : IndexStorageMapMu(llvm::make_unique()), + GetProjectInfo(std::move(GetProjectInfo)) { + llvm::SmallString<128> HomeDir; + llvm::sys::path::home_directory(HomeDir); + this->HomeDir = HomeDir.str().str(); + } - // Creates or fetches to storage from cache for the specified CDB. - BackgroundIndexStorage *operator()(llvm::StringRef CDBDirectory) { + // Creates or fetches to storage from cache for the specified project. + BackgroundIndexStorage *operator()(PathRef File) { std::lock_guard Lock(*IndexStorageMapMu); + Path CDBDirectory = HomeDir; + if (auto PI = GetProjectInfo(File)) + CDBDirectory = PI->SourceRoot; auto &IndexStorage = IndexStorageMap[CDBDirectory]; if (!IndexStorage) IndexStorage = create(CDBDirectory); @@ -131,21 +148,28 @@ } private: - std::unique_ptr create(llvm::StringRef CDBDirectory) { - if (CDBDirectory.empty()) + std::unique_ptr create(PathRef CDBDirectory) { + if (CDBDirectory.empty()) { + elog("Tried to create storage for empty directory!"); return llvm::make_unique(); + } return llvm::make_unique(CDBDirectory); } + Path HomeDir; + llvm::StringMap> IndexStorageMap; std::unique_ptr IndexStorageMapMu; + + std::function(PathRef)> GetProjectInfo; }; } // namespace BackgroundIndexStorage::Factory -BackgroundIndexStorage::createDiskBackedStorageFactory() { - return DiskBackedIndexStorageManager(); +BackgroundIndexStorage::createDiskBackedStorageFactory( + std::function(PathRef)> GetProjectInfo) { + return DiskBackedIndexStorageManager(std::move(GetProjectInfo)); } } // namespace clangd Index: clang-tools-extra/trunk/clangd/index/BackgroundRebuild.h =================================================================== --- clang-tools-extra/trunk/clangd/index/BackgroundRebuild.h +++ clang-tools-extra/trunk/clangd/index/BackgroundRebuild.h @@ -17,6 +17,7 @@ #include "index/FileIndex.h" #include "index/Index.h" #include "llvm/Support/Threading.h" +#include namespace clang { namespace clangd { @@ -61,7 +62,7 @@ // sessions may happen concurrently. void startLoading(); // Called to indicate some shards were actually loaded from disk. - void loadedTU(); + void loadedShard(size_t ShardCount); // Called to indicate we're finished loading shards from disk. // May rebuild (if any were loaded). void doneLoading(); @@ -89,7 +90,7 @@ unsigned IndexedTUsAtLastRebuild = 0; // Are we loading shards? May be multiple concurrent sessions. unsigned Loading = 0; - unsigned LoadedTUs; // In the current loading session. + unsigned LoadedShards; // In the current loading session. SwapIndex *Target; FileSymbols *Source; Index: clang-tools-extra/trunk/clangd/index/BackgroundRebuild.cpp =================================================================== --- clang-tools-extra/trunk/clangd/index/BackgroundRebuild.cpp +++ clang-tools-extra/trunk/clangd/index/BackgroundRebuild.cpp @@ -78,13 +78,13 @@ void BackgroundIndexRebuilder::startLoading() { std::lock_guard Lock(Mu); if (!Loading) - LoadedTUs = 0; + LoadedShards = 0; ++Loading; } -void BackgroundIndexRebuilder::loadedTU() { +void BackgroundIndexRebuilder::loadedShard(size_t ShardCount) { std::lock_guard Lock(Mu); assert(Loading); - ++LoadedTUs; + LoadedShards += ShardCount; } void BackgroundIndexRebuilder::doneLoading() { maybeRebuild("after loading index from disk", [this] { @@ -93,7 +93,7 @@ if (Loading) // was loading multiple batches concurrently return false; // rebuild once the last batch is done. // Rebuild if we loaded any shards, or if we stopped an indexedTU rebuild. - return LoadedTUs > 0 || enoughTUsToRebuild(); + return LoadedShards > 0 || enoughTUsToRebuild(); }); } Index: clang-tools-extra/trunk/clangd/test/Inputs/background-index/definition.jsonrpc =================================================================== --- clang-tools-extra/trunk/clangd/test/Inputs/background-index/definition.jsonrpc +++ clang-tools-extra/trunk/clangd/test/Inputs/background-index/definition.jsonrpc @@ -18,7 +18,7 @@ "uri": "file://DIRECTORY/bar.cpp", "languageId": "cpp", "version": 1, - "text": "#include \"foo.h\"\nint main(){\nreturn foo();\n}" + "text": "#include \"sub_dir/foo.h\"\nint main(){\nreturn foo();\n}" } } } Index: clang-tools-extra/trunk/clangd/test/Inputs/background-index/foo.h =================================================================== --- clang-tools-extra/trunk/clangd/test/Inputs/background-index/foo.h +++ clang-tools-extra/trunk/clangd/test/Inputs/background-index/foo.h @@ -1,4 +0,0 @@ -#ifndef FOO_H -#define FOO_H -int foo(); -#endif Index: clang-tools-extra/trunk/clangd/test/Inputs/background-index/foo.cpp =================================================================== --- clang-tools-extra/trunk/clangd/test/Inputs/background-index/foo.cpp +++ clang-tools-extra/trunk/clangd/test/Inputs/background-index/foo.cpp @@ -1,2 +1,2 @@ -#include "foo.h" +#include "sub_dir/foo.h" int foo() { return 42; } Index: clang-tools-extra/trunk/clangd/test/Inputs/background-index/sub_dir/foo.h =================================================================== --- clang-tools-extra/trunk/clangd/test/Inputs/background-index/sub_dir/foo.h +++ clang-tools-extra/trunk/clangd/test/Inputs/background-index/sub_dir/foo.h @@ -0,0 +1,4 @@ +#ifndef FOO_H +#define FOO_H +int foo(); +#endif Index: clang-tools-extra/trunk/clangd/test/background-index.test =================================================================== --- clang-tools-extra/trunk/clangd/test/background-index.test +++ clang-tools-extra/trunk/clangd/test/background-index.test @@ -5,7 +5,8 @@ # RUN: rm -rf %t # RUN: cp -r %S/Inputs/background-index %t # Need to embed the correct temp path in the actual JSON-RPC requests. -# RUN: sed -i -e "s|DIRECTORY|%t|" %t/* +# RUN: sed -i -e "s|DIRECTORY|%t|" %t/definition.jsonrpc +# RUN: sed -i -e "s|DIRECTORY|%t|" %t/compile_commands.json # We're editing bar.cpp, which includes foo.h. # foo() is declared in foo.h and defined in foo.cpp. @@ -14,6 +15,7 @@ # Test that the index is writing files in the expected location. # RUN: ls %t/.clangd/index/foo.cpp.*.idx +# RUN: ls %t/sub_dir/.clangd/index/foo.h.*.idx # Test the index is read from disk: delete code and restart clangd. # RUN: rm %t/foo.cpp Index: clang-tools-extra/trunk/clangd/unittests/BackgroundIndexTests.cpp =================================================================== --- clang-tools-extra/trunk/clangd/unittests/BackgroundIndexTests.cpp +++ clang-tools-extra/trunk/clangd/unittests/BackgroundIndexTests.cpp @@ -211,7 +211,7 @@ OverlayCDB CDB(/*Base=*/nullptr); BackgroundIndex Idx(Context::empty(), FS, CDB, [&](llvm::StringRef) { return &MSS; }); - CDB.setCompileCommand(testPath("root"), Cmd); + CDB.setCompileCommand(testPath("root/A.cc"), Cmd); ASSERT_TRUE(Idx.blockUntilIdleForTest()); } EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache. @@ -335,7 +335,7 @@ OverlayCDB CDB(/*Base=*/nullptr); BackgroundIndex Idx(Context::empty(), FS, CDB, [&](llvm::StringRef) { return &MSS; }); - CDB.setCompileCommand(testPath("root"), Cmd); + CDB.setCompileCommand(testPath("root/A.cc"), Cmd); ASSERT_TRUE(Idx.blockUntilIdleForTest()); } EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache. @@ -353,7 +353,7 @@ OverlayCDB CDB(/*Base=*/nullptr); BackgroundIndex Idx(Context::empty(), FS, CDB, [&](llvm::StringRef) { return &MSS; }); - CDB.setCompileCommand(testPath("root"), Cmd); + CDB.setCompileCommand(testPath("root/A.cc"), Cmd); ASSERT_TRUE(Idx.blockUntilIdleForTest()); } EXPECT_EQ(CacheHits, 2U); // Check both A.cc and A.h loaded from cache. @@ -621,8 +621,8 @@ TEST_F(BackgroundIndexRebuilderTest, LoadingShards) { Rebuilder.startLoading(); - Rebuilder.loadedTU(); - Rebuilder.loadedTU(); + Rebuilder.loadedShard(10); + Rebuilder.loadedShard(20); EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); })); // No rebuild for no shards. @@ -631,11 +631,11 @@ // Loads can overlap. Rebuilder.startLoading(); - Rebuilder.loadedTU(); + Rebuilder.loadedShard(1); Rebuilder.startLoading(); - Rebuilder.loadedTU(); + Rebuilder.loadedShard(1); EXPECT_FALSE(checkRebuild([&] { Rebuilder.doneLoading(); })); - Rebuilder.loadedTU(); + Rebuilder.loadedShard(1); EXPECT_TRUE(checkRebuild([&] { Rebuilder.doneLoading(); })); // No rebuilding for indexed files while loading.