10
10
#include " ClangdUnit.h"
11
11
#include " Compiler.h"
12
12
#include " Context.h"
13
- #include " FSProvider.h"
14
13
#include " Headers.h"
15
14
#include " Logger.h"
16
15
#include " Path.h"
19
18
#include " Threading.h"
20
19
#include " Trace.h"
21
20
#include " URI.h"
22
- #include " index/BackgroundIndexLoader.h"
23
21
#include " index/FileIndex.h"
24
22
#include " index/IndexAction.h"
25
23
#include " index/MemIndex.h"
30
28
#include " clang/Basic/SourceLocation.h"
31
29
#include " clang/Basic/SourceManager.h"
32
30
#include " clang/Driver/Types.h"
33
- #include " llvm/ADT/ArrayRef.h"
34
- #include " llvm/ADT/DenseSet.h"
35
31
#include " llvm/ADT/Hashing.h"
36
32
#include " llvm/ADT/STLExtras.h"
37
33
#include " llvm/ADT/ScopeExit.h"
46
42
#include < atomic>
47
43
#include < chrono>
48
44
#include < condition_variable>
49
- #include < cstddef>
50
45
#include < memory>
51
46
#include < mutex>
52
47
#include < numeric>
53
48
#include < queue>
54
49
#include < random>
55
50
#include < string>
56
51
#include < thread>
57
- #include < utility>
58
- #include < vector>
59
52
60
53
namespace clang {
61
54
namespace clangd {
@@ -126,18 +119,6 @@ llvm::SmallString<128> getAbsolutePath(const tooling::CompileCommand &Cmd) {
126
119
}
127
120
return AbsolutePath;
128
121
}
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
-
141
122
} // namespace
142
123
143
124
BackgroundIndex::BackgroundIndex (
@@ -175,14 +156,14 @@ BackgroundQueue::Task BackgroundIndex::changedFilesTask(
175
156
log (" Enqueueing {0} commands for indexing" , ChangedFiles.size ());
176
157
SPAN_ATTACH (Tracer, " files" , int64_t (ChangedFiles.size ()));
177
158
178
- auto NeedsReIndexing = loadProject (std::move (ChangedFiles));
159
+ auto NeedsReIndexing = loadShards (std::move (ChangedFiles));
179
160
// Run indexing for files that need to be updated.
180
161
std::shuffle (NeedsReIndexing.begin (), NeedsReIndexing.end (),
181
162
std::mt19937 (std::random_device{}()));
182
163
std::vector<BackgroundQueue::Task> Tasks;
183
164
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 ));
186
167
Queue.append (std::move (Tasks));
187
168
});
188
169
@@ -197,12 +178,13 @@ static llvm::StringRef filenameWithoutExtension(llvm::StringRef Path) {
197
178
}
198
179
199
180
BackgroundQueue::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] {
202
184
// We can't use llvm::StringRef here since we are going to
203
185
// move from Cmd during the call below.
204
186
const std::string FileName = Cmd.Filename ;
205
- if (auto Error = index (std::move (Cmd)))
187
+ if (auto Error = index (std::move (Cmd), Storage ))
206
188
elog (" Indexing {0} failed: {1}" , FileName, std::move (Error));
207
189
});
208
190
T.QueuePri = IndexFile;
@@ -225,7 +207,7 @@ void BackgroundIndex::boostRelated(llvm::StringRef Path) {
225
207
void BackgroundIndex::update (
226
208
llvm::StringRef MainFile, IndexFileIn Index,
227
209
const llvm::StringMap<ShardVersion> &ShardVersionsSnapshot,
228
- bool HadErrors) {
210
+ BackgroundIndexStorage *IndexStorage, bool HadErrors) {
229
211
// Partition symbols/references into files.
230
212
struct File {
231
213
llvm::DenseSet<const Symbol *> Symbols;
@@ -309,21 +291,22 @@ void BackgroundIndex::update(
309
291
// We need to store shards before updating the index, since the latter
310
292
// consumes slabs.
311
293
// 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
+ }
327
310
328
311
{
329
312
std::lock_guard<std::mutex> Lock (ShardVersionsMu);
@@ -346,7 +329,8 @@ void BackgroundIndex::update(
346
329
}
347
330
}
348
331
349
- llvm::Error BackgroundIndex::index (tooling::CompileCommand Cmd) {
332
+ llvm::Error BackgroundIndex::index (tooling::CompileCommand Cmd,
333
+ BackgroundIndexStorage *IndexStorage) {
350
334
trace::Span Tracer (" BackgroundIndex" );
351
335
SPAN_ATTACH (Tracer, " file" , Cmd.Filename );
352
336
auto AbsolutePath = getAbsolutePath (Cmd);
@@ -440,78 +424,176 @@ llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
440
424
for (auto &It : *Index.Sources )
441
425
It.second .Flags |= IncludeGraphNode::SourceFlag::HadErrors;
442
426
}
443
- update (AbsolutePath, std::move (Index), ShardVersionsSnapshot, HadErrors);
427
+ update (AbsolutePath, std::move (Index), ShardVersionsSnapshot, IndexStorage,
428
+ HadErrors);
444
429
445
430
Rebuilder.indexedTU ();
446
431
return llvm::Error::success ();
447
432
}
448
433
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
+ }
455
475
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.
461
525
{
462
- // Update in-memory state.
463
526
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) {
467
531
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 ))
470
534
: 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 ))
473
537
: nullptr ;
474
538
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 ))
477
541
: 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 ;
482
545
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 );
485
548
}
486
549
}
487
- Rebuilder. loadedShard (LoadedShards);
488
- Rebuilder.doneLoading ();
550
+ if (!IntermediateSymbols. empty ())
551
+ Rebuilder.loadedTU ();
489
552
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
+ }
507
555
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);
510
571
if (!Cmd)
511
572
continue ;
512
- NeedsReIndexing.emplace_back (std::move (*Cmd));
513
- }
514
573
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 ();
515
597
return NeedsReIndexing;
516
598
}
517
599
0 commit comments