1010#include " ClangdUnit.h"
1111#include " Compiler.h"
1212#include " Context.h"
13- #include " FSProvider.h"
1413#include " Headers.h"
1514#include " Logger.h"
1615#include " Path.h"
1918#include " Threading.h"
2019#include " Trace.h"
2120#include " URI.h"
22- #include " index/BackgroundIndexLoader.h"
2321#include " index/FileIndex.h"
2422#include " index/IndexAction.h"
2523#include " index/MemIndex.h"
3028#include " clang/Basic/SourceLocation.h"
3129#include " clang/Basic/SourceManager.h"
3230#include " clang/Driver/Types.h"
33- #include " llvm/ADT/ArrayRef.h"
34- #include " llvm/ADT/DenseSet.h"
3531#include " llvm/ADT/Hashing.h"
3632#include " llvm/ADT/STLExtras.h"
3733#include " llvm/ADT/ScopeExit.h"
4642#include < atomic>
4743#include < chrono>
4844#include < condition_variable>
49- #include < cstddef>
5045#include < memory>
5146#include < mutex>
5247#include < numeric>
5348#include < queue>
5449#include < random>
5550#include < string>
5651#include < thread>
57- #include < utility>
58- #include < vector>
5952
6053namespace clang {
6154namespace clangd {
@@ -126,18 +119,6 @@ llvm::SmallString<128> getAbsolutePath(const tooling::CompileCommand &Cmd) {
126119 }
127120 return AbsolutePath;
128121}
129-
130- bool shardIsStale (const LoadedShard &LS, llvm::vfs::FileSystem *FS) {
131- auto Buf = FS->getBufferForFile (LS.AbsolutePath );
132- if (!Buf) {
133- elog (" Background-index: Couldn't read {0} to validate stored index: {1}" ,
134- LS.AbsolutePath , Buf.getError ().message ());
135- // There is no point in indexing an unreadable file.
136- return false ;
137- }
138- return digest (Buf->get ()->getBuffer ()) != LS.Digest ;
139- }
140-
141122} // namespace
142123
143124BackgroundIndex::BackgroundIndex (
@@ -175,14 +156,14 @@ BackgroundQueue::Task BackgroundIndex::changedFilesTask(
175156 log (" Enqueueing {0} commands for indexing" , ChangedFiles.size ());
176157 SPAN_ATTACH (Tracer, " files" , int64_t (ChangedFiles.size ()));
177158
178- auto NeedsReIndexing = loadProject (std::move (ChangedFiles));
159+ auto NeedsReIndexing = loadShards (std::move (ChangedFiles));
179160 // Run indexing for files that need to be updated.
180161 std::shuffle (NeedsReIndexing.begin (), NeedsReIndexing.end (),
181162 std::mt19937 (std::random_device{}()));
182163 std::vector<BackgroundQueue::Task> Tasks;
183164 Tasks.reserve (NeedsReIndexing.size ());
184- for (auto &Cmd : NeedsReIndexing)
185- Tasks.push_back (indexFileTask (std::move (Cmd) ));
165+ for (auto &Elem : NeedsReIndexing)
166+ Tasks.push_back (indexFileTask (std::move (Elem. first ), Elem. second ));
186167 Queue.append (std::move (Tasks));
187168 });
188169
@@ -197,12 +178,13 @@ static llvm::StringRef filenameWithoutExtension(llvm::StringRef Path) {
197178}
198179
199180BackgroundQueue::Task
200- BackgroundIndex::indexFileTask (tooling::CompileCommand Cmd) {
201- BackgroundQueue::Task T ([this , Cmd] {
181+ BackgroundIndex::indexFileTask (tooling::CompileCommand Cmd,
182+ BackgroundIndexStorage *Storage) {
183+ BackgroundQueue::Task T ([this , Storage, Cmd] {
202184 // We can't use llvm::StringRef here since we are going to
203185 // move from Cmd during the call below.
204186 const std::string FileName = Cmd.Filename ;
205- if (auto Error = index (std::move (Cmd)))
187+ if (auto Error = index (std::move (Cmd), Storage ))
206188 elog (" Indexing {0} failed: {1}" , FileName, std::move (Error));
207189 });
208190 T.QueuePri = IndexFile;
@@ -225,7 +207,7 @@ void BackgroundIndex::boostRelated(llvm::StringRef Path) {
225207void BackgroundIndex::update (
226208 llvm::StringRef MainFile, IndexFileIn Index,
227209 const llvm::StringMap<ShardVersion> &ShardVersionsSnapshot,
228- bool HadErrors) {
210+ BackgroundIndexStorage *IndexStorage, bool HadErrors) {
229211 // Partition symbols/references into files.
230212 struct File {
231213 llvm::DenseSet<const Symbol *> Symbols;
@@ -309,21 +291,22 @@ void BackgroundIndex::update(
309291 // We need to store shards before updating the index, since the latter
310292 // consumes slabs.
311293 // FIXME: Also skip serializing the shard if it is already up-to-date.
312- BackgroundIndexStorage *IndexStorage = IndexStorageFactory (Path);
313- IndexFileOut Shard;
314- Shard.Symbols = SS.get ();
315- Shard.Refs = RS.get ();
316- Shard.Relations = RelS.get ();
317- Shard.Sources = IG.get ();
318-
319- // Only store command line hash for main files of the TU, since our
320- // current model keeps only one version of a header file.
321- if (Path == MainFile)
322- Shard.Cmd = Index.Cmd .getPointer ();
323-
324- if (auto Error = IndexStorage->storeShard (Path, Shard))
325- elog (" Failed to write background-index shard for file {0}: {1}" , Path,
326- std::move (Error));
294+ if (IndexStorage) {
295+ IndexFileOut Shard;
296+ Shard.Symbols = SS.get ();
297+ Shard.Refs = RS.get ();
298+ Shard.Relations = RelS.get ();
299+ Shard.Sources = IG.get ();
300+
301+ // Only store command line hash for main files of the TU, since our
302+ // current model keeps only one version of a header file.
303+ if (Path == MainFile)
304+ Shard.Cmd = Index.Cmd .getPointer ();
305+
306+ if (auto Error = IndexStorage->storeShard (Path, Shard))
307+ elog (" Failed to write background-index shard for file {0}: {1}" , Path,
308+ std::move (Error));
309+ }
327310
328311 {
329312 std::lock_guard<std::mutex> Lock (ShardVersionsMu);
@@ -346,7 +329,8 @@ void BackgroundIndex::update(
346329 }
347330}
348331
349- llvm::Error BackgroundIndex::index (tooling::CompileCommand Cmd) {
332+ llvm::Error BackgroundIndex::index (tooling::CompileCommand Cmd,
333+ BackgroundIndexStorage *IndexStorage) {
350334 trace::Span Tracer (" BackgroundIndex" );
351335 SPAN_ATTACH (Tracer, " file" , Cmd.Filename );
352336 auto AbsolutePath = getAbsolutePath (Cmd);
@@ -440,78 +424,176 @@ llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
440424 for (auto &It : *Index.Sources )
441425 It.second .Flags |= IncludeGraphNode::SourceFlag::HadErrors;
442426 }
443- update (AbsolutePath, std::move (Index), ShardVersionsSnapshot, HadErrors);
427+ update (AbsolutePath, std::move (Index), ShardVersionsSnapshot, IndexStorage,
428+ HadErrors);
444429
445430 Rebuilder.indexedTU ();
446431 return llvm::Error::success ();
447432}
448433
449- // Restores shards for \p MainFiles from index storage. Then checks staleness of
450- // those shards and returns a list of TUs that needs to be indexed to update
451- // staleness.
452- std::vector<tooling::CompileCommand>
453- BackgroundIndex::loadProject (std::vector<std::string> MainFiles) {
454- std::vector<tooling::CompileCommand> NeedsReIndexing;
434+ std::vector<BackgroundIndex::Source>
435+ BackgroundIndex::loadShard (const tooling::CompileCommand &Cmd,
436+ BackgroundIndexStorage *IndexStorage,
437+ llvm::StringSet<> &LoadedShards) {
438+ struct ShardInfo {
439+ std::string AbsolutePath;
440+ std::unique_ptr<IndexFileIn> Shard;
441+ FileDigest Digest = {};
442+ bool CountReferences = false ;
443+ bool HadErrors = false ;
444+ };
445+ std::vector<ShardInfo> IntermediateSymbols;
446+ // Make sure we don't have duplicate elements in the queue. Keys are absolute
447+ // paths.
448+ llvm::StringSet<> InQueue;
449+ auto FS = FSProvider.getFileSystem ();
450+ // Dependencies of this TU, paired with the information about whether they
451+ // need to be re-indexed or not.
452+ std::vector<Source> Dependencies;
453+ std::queue<Source> ToVisit;
454+ std::string AbsolutePath = getAbsolutePath (Cmd).str ();
455+ // Up until we load the shard related to a dependency it needs to be
456+ // re-indexed.
457+ ToVisit.emplace (AbsolutePath, true );
458+ InQueue.insert (AbsolutePath);
459+ // Goes over each dependency.
460+ while (!ToVisit.empty ()) {
461+ Dependencies.push_back (std::move (ToVisit.front ()));
462+ // Dependencies is not modified during the rest of the loop, so it is safe
463+ // to keep the reference.
464+ auto &CurDependency = Dependencies.back ();
465+ ToVisit.pop ();
466+ // If we have already seen this shard before(either loaded or failed) don't
467+ // re-try again. Since the information in the shard won't change from one TU
468+ // to another.
469+ if (!LoadedShards.try_emplace (CurDependency.Path ).second ) {
470+ // If the dependency needs to be re-indexed, first occurence would already
471+ // have detected that, so we don't need to issue it again.
472+ CurDependency.NeedsReIndexing = false ;
473+ continue ;
474+ }
455475
456- Rebuilder.startLoading ();
457- // Load shards for all of the mainfiles.
458- const std::vector<LoadedShard> Result =
459- loadIndexShards (MainFiles, IndexStorageFactory, CDB);
460- size_t LoadedShards = 0 ;
476+ auto Shard = IndexStorage->loadShard (CurDependency.Path );
477+ if (!Shard || !Shard->Sources ) {
478+ // File will be returned as requiring re-indexing to caller.
479+ vlog (" Failed to load shard: {0}" , CurDependency.Path );
480+ continue ;
481+ }
482+ // These are the edges in the include graph for current dependency.
483+ for (const auto &I : *Shard->Sources ) {
484+ auto U = URI::parse (I.getKey ());
485+ if (!U)
486+ continue ;
487+ auto AbsolutePath = URI::resolve (*U, CurDependency.Path );
488+ if (!AbsolutePath)
489+ continue ;
490+ // Add file as dependency if haven't seen before.
491+ if (InQueue.try_emplace (*AbsolutePath).second )
492+ ToVisit.emplace (*AbsolutePath, true );
493+ // The node contains symbol information only for current file, the rest is
494+ // just edges.
495+ if (*AbsolutePath != CurDependency.Path )
496+ continue ;
497+
498+ // We found source file info for current dependency.
499+ assert (I.getValue ().Digest != FileDigest{{0 }} && " Digest is empty?" );
500+ ShardInfo SI;
501+ SI.AbsolutePath = CurDependency.Path ;
502+ SI.Shard = std::move (Shard);
503+ SI.Digest = I.getValue ().Digest ;
504+ SI.CountReferences =
505+ I.getValue ().Flags & IncludeGraphNode::SourceFlag::IsTU;
506+ SI.HadErrors =
507+ I.getValue ().Flags & IncludeGraphNode::SourceFlag::HadErrors;
508+ IntermediateSymbols.push_back (std::move (SI));
509+ // Check if the source needs re-indexing.
510+ // Get the digest, skip it if file doesn't exist.
511+ auto Buf = FS->getBufferForFile (CurDependency.Path );
512+ if (!Buf) {
513+ elog (" Couldn't get buffer for file: {0}: {1}" , CurDependency.Path ,
514+ Buf.getError ().message ());
515+ continue ;
516+ }
517+ // If digests match then dependency doesn't need re-indexing.
518+ // FIXME: Also check for dependencies(sources) of this shard and compile
519+ // commands for cache invalidation.
520+ CurDependency.NeedsReIndexing =
521+ digest (Buf->get ()->getBuffer ()) != I.getValue ().Digest ;
522+ }
523+ }
524+ // Load shard information into background-index.
461525 {
462- // Update in-memory state.
463526 std::lock_guard<std::mutex> Lock (ShardVersionsMu);
464- for (auto &LS : Result) {
465- if (!LS.Shard )
466- continue ;
527+ // This can override a newer version that is added in another thread,
528+ // if this thread sees the older version but finishes later. This
529+ // should be rare in practice.
530+ for (const ShardInfo &SI : IntermediateSymbols) {
467531 auto SS =
468- LS .Shard ->Symbols
469- ? llvm::make_unique<SymbolSlab>(std::move (*LS .Shard ->Symbols ))
532+ SI .Shard ->Symbols
533+ ? llvm::make_unique<SymbolSlab>(std::move (*SI .Shard ->Symbols ))
470534 : nullptr ;
471- auto RS = LS .Shard ->Refs
472- ? llvm::make_unique<RefSlab>(std::move (*LS .Shard ->Refs ))
535+ auto RS = SI .Shard ->Refs
536+ ? llvm::make_unique<RefSlab>(std::move (*SI .Shard ->Refs ))
473537 : nullptr ;
474538 auto RelS =
475- LS .Shard ->Relations
476- ? llvm::make_unique<RelationSlab>(std::move (*LS .Shard ->Relations ))
539+ SI .Shard ->Relations
540+ ? llvm::make_unique<RelationSlab>(std::move (*SI .Shard ->Relations ))
477541 : nullptr ;
478- ShardVersion &SV = ShardVersions[LS.AbsolutePath ];
479- SV.Digest = LS.Digest ;
480- SV.HadErrors = LS.HadErrors ;
481- ++LoadedShards;
542+ ShardVersion &SV = ShardVersions[SI.AbsolutePath ];
543+ SV.Digest = SI.Digest ;
544+ SV.HadErrors = SI.HadErrors ;
482545
483- IndexedSymbols.update (LS .AbsolutePath , std::move (SS), std::move (RS),
484- std::move (RelS), LS .CountReferences );
546+ IndexedSymbols.update (SI .AbsolutePath , std::move (SS), std::move (RS),
547+ std::move (RelS), SI .CountReferences );
485548 }
486549 }
487- Rebuilder. loadedShard (LoadedShards);
488- Rebuilder.doneLoading ();
550+ if (!IntermediateSymbols. empty ())
551+ Rebuilder.loadedTU ();
489552
490- auto FS = FSProvider.getFileSystem ();
491- llvm::DenseSet<PathRef> TUsToIndex;
492- // We'll accept data from stale shards, but ensure the files get reindexed
493- // soon.
494- for (auto &LS : Result) {
495- if (!shardIsStale (LS, FS.get ()))
496- continue ;
497- PathRef TUForFile = LS.DependentTU ;
498- assert (!TUForFile.empty () && " File without a TU!" );
499-
500- // FIXME: Currently, we simply schedule indexing on a TU whenever any of
501- // its dependencies needs re-indexing. We might do it smarter by figuring
502- // out a minimal set of TUs that will cover all the stale dependencies.
503- // FIXME: Try looking at other TUs if no compile commands are available
504- // for this TU, i.e TU was deleted after we performed indexing.
505- TUsToIndex.insert (TUForFile);
506- }
553+ return Dependencies;
554+ }
507555
508- for (PathRef TU : TUsToIndex) {
509- auto Cmd = CDB.getCompileCommand (TU);
556+ // Goes over each changed file and loads them from index. Returns the list of
557+ // TUs that had out-of-date/no shards.
558+ std::vector<std::pair<tooling::CompileCommand, BackgroundIndexStorage *>>
559+ BackgroundIndex::loadShards (std::vector<std::string> ChangedFiles) {
560+ std::vector<std::pair<tooling::CompileCommand, BackgroundIndexStorage *>>
561+ NeedsReIndexing;
562+ // Keeps track of the files that will be reindexed, to make sure we won't
563+ // re-index same dependencies more than once. Keys are AbsolutePaths.
564+ llvm::StringSet<> FilesToIndex;
565+ // Keeps track of the loaded shards to make sure we don't perform redundant
566+ // disk IO. Keys are absolute paths.
567+ llvm::StringSet<> LoadedShards;
568+ Rebuilder.startLoading ();
569+ for (const auto &File : ChangedFiles) {
570+ auto Cmd = CDB.getCompileCommand (File);
510571 if (!Cmd)
511572 continue ;
512- NeedsReIndexing.emplace_back (std::move (*Cmd));
513- }
514573
574+ std::string ProjectRoot;
575+ if (auto PI = CDB.getProjectInfo (File))
576+ ProjectRoot = std::move (PI->SourceRoot );
577+
578+ BackgroundIndexStorage *IndexStorage = IndexStorageFactory (ProjectRoot);
579+ auto Dependencies = loadShard (*Cmd, IndexStorage, LoadedShards);
580+ for (const auto &Dependency : Dependencies) {
581+ if (!Dependency.NeedsReIndexing || FilesToIndex.count (Dependency.Path ))
582+ continue ;
583+ // FIXME: Currently, we simply schedule indexing on a TU whenever any of
584+ // its dependencies needs re-indexing. We might do it smarter by figuring
585+ // out a minimal set of TUs that will cover all the stale dependencies.
586+ vlog (" Enqueueing TU {0} because its dependency {1} needs re-indexing." ,
587+ Cmd->Filename , Dependency.Path );
588+ NeedsReIndexing.push_back ({std::move (*Cmd), IndexStorage});
589+ // Mark all of this TU's dependencies as to-be-indexed so that we won't
590+ // try to re-index those.
591+ for (const auto &Dependency : Dependencies)
592+ FilesToIndex.insert (Dependency.Path );
593+ break ;
594+ }
595+ }
596+ Rebuilder.doneLoading ();
515597 return NeedsReIndexing;
516598}
517599
0 commit comments