1 //===--- FileIndex.cpp - Indexes for files. ------------------------ C++-*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "FileIndex.h"
10 #include "CollectMacros.h"
11 #include "ParsedAST.h"
12 #include "SymbolCollector.h"
13 #include "index/CanonicalIncludes.h"
14 #include "index/Index.h"
15 #include "index/MemIndex.h"
16 #include "index/Merge.h"
17 #include "index/Ref.h"
18 #include "index/Relation.h"
19 #include "index/Serialization.h"
20 #include "index/Symbol.h"
21 #include "index/SymbolID.h"
22 #include "index/SymbolOrigin.h"
23 #include "index/dex/Dex.h"
24 #include "support/Logger.h"
25 #include "support/MemoryTree.h"
26 #include "support/Path.h"
27 #include "clang/AST/ASTContext.h"
28 #include "clang/Index/IndexingAction.h"
29 #include "clang/Index/IndexingOptions.h"
30 #include "clang/Lex/MacroInfo.h"
31 #include "clang/Lex/Preprocessor.h"
32 #include "llvm/ADT/DenseMap.h"
33 #include "llvm/ADT/Optional.h"
34 #include "llvm/ADT/STLExtras.h"
35 #include "llvm/ADT/StringMap.h"
36 #include "llvm/ADT/StringRef.h"
37 #include "llvm/Support/Error.h"
38 #include <algorithm>
39 #include <memory>
40 #include <tuple>
41 #include <utility>
42 #include <vector>
43 
44 namespace clang {
45 namespace clangd {
46 namespace {
47 
48 SlabTuple indexSymbols(ASTContext &AST, Preprocessor &PP,
49                        llvm::ArrayRef<Decl *> DeclsToIndex,
50                        const MainFileMacros *MacroRefsToIndex,
51                        const CanonicalIncludes &Includes, bool IsIndexMainAST,
52                        llvm::StringRef Version, bool CollectMainFileRefs) {
53   SymbolCollector::Options CollectorOpts;
54   CollectorOpts.CollectIncludePath = true;
55   CollectorOpts.Includes = &Includes;
56   CollectorOpts.CountReferences = false;
57   CollectorOpts.Origin = SymbolOrigin::Dynamic;
58   CollectorOpts.CollectMainFileRefs = CollectMainFileRefs;
59 
60   index::IndexingOptions IndexOpts;
61   // We only need declarations, because we don't count references.
62   IndexOpts.SystemSymbolFilter =
63       index::IndexingOptions::SystemSymbolFilterKind::DeclarationsOnly;
64   // We index function-local classes and its member functions only.
65   IndexOpts.IndexFunctionLocals = true;
66   if (IsIndexMainAST) {
67     // We only collect refs when indexing main AST.
68     CollectorOpts.RefFilter = RefKind::All;
69     // Comments for main file can always be obtained from sema, do not store
70     // them in the index.
71     CollectorOpts.StoreAllDocumentation = false;
72   } else {
73     IndexOpts.IndexMacrosInPreprocessor = true;
74     CollectorOpts.CollectMacro = true;
75     CollectorOpts.StoreAllDocumentation = true;
76   }
77 
78   SymbolCollector Collector(std::move(CollectorOpts));
79   Collector.setPreprocessor(PP);
80   index::indexTopLevelDecls(AST, PP, DeclsToIndex, Collector, IndexOpts);
81   if (MacroRefsToIndex)
82     Collector.handleMacros(*MacroRefsToIndex);
83 
84   const auto &SM = AST.getSourceManager();
85   const auto *MainFileEntry = SM.getFileEntryForID(SM.getMainFileID());
86   std::string FileName =
87       std::string(MainFileEntry ? MainFileEntry->getName() : "");
88 
89   auto Syms = Collector.takeSymbols();
90   auto Refs = Collector.takeRefs();
91   auto Relations = Collector.takeRelations();
92 
93   vlog("indexed {0} AST for {1} version {2}:\n"
94        "  symbol slab: {3} symbols, {4} bytes\n"
95        "  ref slab: {5} symbols, {6} refs, {7} bytes\n"
96        "  relations slab: {8} relations, {9} bytes",
97        IsIndexMainAST ? "file" : "preamble", FileName, Version, Syms.size(),
98        Syms.bytes(), Refs.size(), Refs.numRefs(), Refs.bytes(),
99        Relations.size(), Relations.bytes());
100   return std::make_tuple(std::move(Syms), std::move(Refs),
101                          std::move(Relations));
102 }
103 
104 // We keep only the node "U" and its edges. Any node other than "U" will be
105 // empty in the resultant graph.
106 IncludeGraph getSubGraph(llvm::StringRef URI, const IncludeGraph &FullGraph) {
107   IncludeGraph IG;
108 
109   auto Entry = IG.try_emplace(URI).first;
110   auto &Node = Entry->getValue();
111   Node = FullGraph.lookup(Entry->getKey());
112   Node.URI = Entry->getKey();
113 
114   // URIs inside nodes must point into the keys of the same IncludeGraph.
115   for (auto &Include : Node.DirectIncludes) {
116     auto I = IG.try_emplace(Include).first;
117     I->getValue().URI = I->getKey();
118     Include = I->getKey();
119   }
120   return IG;
121 }
122 } // namespace
123 
124 FileShardedIndex::FileShardedIndex(IndexFileIn Input)
125     : Index(std::move(Input)) {
126   // Used to build RelationSlabs.
127   llvm::DenseMap<SymbolID, FileShard *> SymbolIDToFile;
128 
129   // Attribute each Symbol to both their declaration and definition locations.
130   if (Index.Symbols) {
131     for (const auto &S : *Index.Symbols) {
132       auto It = Shards.try_emplace(S.CanonicalDeclaration.FileURI);
133       It.first->getValue().Symbols.insert(&S);
134       SymbolIDToFile[S.ID] = &It.first->getValue();
135       // Only bother if definition file is different than declaration file.
136       if (S.Definition &&
137           S.Definition.FileURI != S.CanonicalDeclaration.FileURI) {
138         auto It = Shards.try_emplace(S.Definition.FileURI);
139         It.first->getValue().Symbols.insert(&S);
140       }
141     }
142   }
143   // Attribute references into each file they occured in.
144   if (Index.Refs) {
145     for (const auto &SymRefs : *Index.Refs) {
146       for (const auto &R : SymRefs.second) {
147         const auto It = Shards.try_emplace(R.Location.FileURI);
148         It.first->getValue().Refs.insert(&R);
149         RefToSymID[&R] = SymRefs.first;
150       }
151     }
152   }
153   // The Subject and/or Object shards might be part of multiple TUs. In
154   // such cases there will be a race and the last TU to write the shard
155   // will win and all the other relations will be lost. To avoid this,
156   // we store relations in both shards. A race might still happen if the
157   // same translation unit produces different relations under different
158   // configurations, but that's something clangd doesn't handle in general.
159   if (Index.Relations) {
160     for (const auto &R : *Index.Relations) {
161       // FIXME: RelationSlab shouldn't contain dangling relations.
162       FileShard *SubjectFile = SymbolIDToFile.lookup(R.Subject);
163       FileShard *ObjectFile = SymbolIDToFile.lookup(R.Object);
164       if (SubjectFile)
165         SubjectFile->Relations.insert(&R);
166       if (ObjectFile && ObjectFile != SubjectFile)
167         ObjectFile->Relations.insert(&R);
168     }
169   }
170   // Store only the direct includes of a file in a shard.
171   if (Index.Sources) {
172     const auto &FullGraph = *Index.Sources;
173     for (const auto &It : FullGraph) {
174       auto ShardIt = Shards.try_emplace(It.first());
175       ShardIt.first->getValue().IG = getSubGraph(It.first(), FullGraph);
176     }
177   }
178 }
179 std::vector<llvm::StringRef> FileShardedIndex::getAllSources() const {
180   // It should be enough to construct a vector with {Shards.keys().begin(),
181   // Shards.keys().end()} but MSVC fails to compile that.
182   std::vector<PathRef> Result;
183   Result.reserve(Shards.size());
184   for (auto Key : Shards.keys())
185     Result.push_back(Key);
186   return Result;
187 }
188 
189 llvm::Optional<IndexFileIn>
190 FileShardedIndex::getShard(llvm::StringRef Uri) const {
191   auto It = Shards.find(Uri);
192   if (It == Shards.end())
193     return llvm::None;
194 
195   IndexFileIn IF;
196   IF.Sources = It->getValue().IG;
197   IF.Cmd = Index.Cmd;
198 
199   SymbolSlab::Builder SymB;
200   for (const auto *S : It->getValue().Symbols)
201     SymB.insert(*S);
202   IF.Symbols = std::move(SymB).build();
203 
204   RefSlab::Builder RefB;
205   for (const auto *Ref : It->getValue().Refs) {
206     auto SID = RefToSymID.lookup(Ref);
207     RefB.insert(SID, *Ref);
208   }
209   IF.Refs = std::move(RefB).build();
210 
211   RelationSlab::Builder RelB;
212   for (const auto *Rel : It->getValue().Relations) {
213     RelB.insert(*Rel);
214   }
215   IF.Relations = std::move(RelB).build();
216   // Explicit move here is needed by some compilers.
217   return std::move(IF);
218 }
219 
220 SlabTuple indexMainDecls(ParsedAST &AST) {
221   return indexSymbols(
222       AST.getASTContext(), AST.getPreprocessor(), AST.getLocalTopLevelDecls(),
223       &AST.getMacros(), AST.getCanonicalIncludes(),
224       /*IsIndexMainAST=*/true, AST.version(), /*CollectMainFileRefs=*/true);
225 }
226 
227 SlabTuple indexHeaderSymbols(llvm::StringRef Version, ASTContext &AST,
228                              Preprocessor &PP,
229                              const CanonicalIncludes &Includes) {
230   std::vector<Decl *> DeclsToIndex(
231       AST.getTranslationUnitDecl()->decls().begin(),
232       AST.getTranslationUnitDecl()->decls().end());
233   return indexSymbols(AST, PP, DeclsToIndex,
234                       /*MainFileMacros=*/nullptr, Includes,
235                       /*IsIndexMainAST=*/false, Version,
236                       /*CollectMainFileRefs=*/false);
237 }
238 
239 FileSymbols::FileSymbols(IndexContents IdxContents)
240     : IdxContents(IdxContents) {}
241 
242 void FileSymbols::update(llvm::StringRef Key,
243                          std::unique_ptr<SymbolSlab> Symbols,
244                          std::unique_ptr<RefSlab> Refs,
245                          std::unique_ptr<RelationSlab> Relations,
246                          bool CountReferences) {
247   std::lock_guard<std::mutex> Lock(Mutex);
248   ++Version;
249   if (!Symbols)
250     SymbolsSnapshot.erase(Key);
251   else
252     SymbolsSnapshot[Key] = std::move(Symbols);
253   if (!Refs) {
254     RefsSnapshot.erase(Key);
255   } else {
256     RefSlabAndCountReferences Item;
257     Item.CountReferences = CountReferences;
258     Item.Slab = std::move(Refs);
259     RefsSnapshot[Key] = std::move(Item);
260   }
261   if (!Relations)
262     RelationsSnapshot.erase(Key);
263   else
264     RelationsSnapshot[Key] = std::move(Relations);
265 }
266 
267 std::unique_ptr<SymbolIndex>
268 FileSymbols::buildIndex(IndexType Type, DuplicateHandling DuplicateHandle,
269                         size_t *Version) {
270   std::vector<std::shared_ptr<SymbolSlab>> SymbolSlabs;
271   std::vector<std::shared_ptr<RefSlab>> RefSlabs;
272   std::vector<std::shared_ptr<RelationSlab>> RelationSlabs;
273   llvm::StringSet<> Files;
274   std::vector<RefSlab *> MainFileRefs;
275   {
276     std::lock_guard<std::mutex> Lock(Mutex);
277     for (const auto &FileAndSymbols : SymbolsSnapshot) {
278       SymbolSlabs.push_back(FileAndSymbols.second);
279       Files.insert(FileAndSymbols.first());
280     }
281     for (const auto &FileAndRefs : RefsSnapshot) {
282       RefSlabs.push_back(FileAndRefs.second.Slab);
283       Files.insert(FileAndRefs.first());
284       if (FileAndRefs.second.CountReferences)
285         MainFileRefs.push_back(RefSlabs.back().get());
286     }
287     for (const auto &FileAndRelations : RelationsSnapshot) {
288       Files.insert(FileAndRelations.first());
289       RelationSlabs.push_back(FileAndRelations.second);
290     }
291 
292     if (Version)
293       *Version = this->Version;
294   }
295   std::vector<const Symbol *> AllSymbols;
296   std::vector<Symbol> SymsStorage;
297   switch (DuplicateHandle) {
298   case DuplicateHandling::Merge: {
299     llvm::DenseMap<SymbolID, Symbol> Merged;
300     for (const auto &Slab : SymbolSlabs) {
301       for (const auto &Sym : *Slab) {
302         assert(Sym.References == 0 &&
303                "Symbol with non-zero references sent to FileSymbols");
304         auto I = Merged.try_emplace(Sym.ID, Sym);
305         if (!I.second)
306           I.first->second = mergeSymbol(I.first->second, Sym);
307       }
308     }
309     for (const RefSlab *Refs : MainFileRefs)
310       for (const auto &Sym : *Refs) {
311         auto It = Merged.find(Sym.first);
312         // This might happen while background-index is still running.
313         if (It == Merged.end())
314           continue;
315         It->getSecond().References += Sym.second.size();
316       }
317     SymsStorage.reserve(Merged.size());
318     for (auto &Sym : Merged) {
319       SymsStorage.push_back(std::move(Sym.second));
320       AllSymbols.push_back(&SymsStorage.back());
321     }
322     break;
323   }
324   case DuplicateHandling::PickOne: {
325     llvm::DenseSet<SymbolID> AddedSymbols;
326     for (const auto &Slab : SymbolSlabs)
327       for (const auto &Sym : *Slab) {
328         assert(Sym.References == 0 &&
329                "Symbol with non-zero references sent to FileSymbols");
330         if (AddedSymbols.insert(Sym.ID).second)
331           AllSymbols.push_back(&Sym);
332       }
333     break;
334   }
335   }
336 
337   std::vector<Ref> RefsStorage; // Contiguous ranges for each SymbolID.
338   llvm::DenseMap<SymbolID, llvm::ArrayRef<Ref>> AllRefs;
339   {
340     llvm::DenseMap<SymbolID, llvm::SmallVector<Ref, 4>> MergedRefs;
341     size_t Count = 0;
342     for (const auto &RefSlab : RefSlabs)
343       for (const auto &Sym : *RefSlab) {
344         MergedRefs[Sym.first].append(Sym.second.begin(), Sym.second.end());
345         Count += Sym.second.size();
346       }
347     RefsStorage.reserve(Count);
348     AllRefs.reserve(MergedRefs.size());
349     for (auto &Sym : MergedRefs) {
350       auto &SymRefs = Sym.second;
351       // Sorting isn't required, but yields more stable results over rebuilds.
352       llvm::sort(SymRefs);
353       llvm::copy(SymRefs, back_inserter(RefsStorage));
354       AllRefs.try_emplace(
355           Sym.first,
356           llvm::ArrayRef<Ref>(&RefsStorage[RefsStorage.size() - SymRefs.size()],
357                               SymRefs.size()));
358     }
359   }
360 
361   std::vector<Relation> AllRelations;
362   for (const auto &RelationSlab : RelationSlabs) {
363     for (const auto &R : *RelationSlab)
364       AllRelations.push_back(R);
365   }
366   // Sort relations and remove duplicates that could arise due to
367   // relations being stored in both the shards containing their
368   // subject and object.
369   llvm::sort(AllRelations);
370   AllRelations.erase(std::unique(AllRelations.begin(), AllRelations.end()),
371                      AllRelations.end());
372 
373   size_t StorageSize =
374       RefsStorage.size() * sizeof(Ref) + SymsStorage.size() * sizeof(Symbol);
375   for (const auto &Slab : SymbolSlabs)
376     StorageSize += Slab->bytes();
377   for (const auto &RefSlab : RefSlabs)
378     StorageSize += RefSlab->bytes();
379 
380   // Index must keep the slabs and contiguous ranges alive.
381   switch (Type) {
382   case IndexType::Light:
383     return std::make_unique<MemIndex>(
384         llvm::make_pointee_range(AllSymbols), std::move(AllRefs),
385         std::move(AllRelations), std::move(Files), IdxContents,
386         std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs),
387                         std::move(RefsStorage), std::move(SymsStorage)),
388         StorageSize);
389   case IndexType::Heavy:
390     return std::make_unique<dex::Dex>(
391         llvm::make_pointee_range(AllSymbols), std::move(AllRefs),
392         std::move(AllRelations), std::move(Files), IdxContents,
393         std::make_tuple(std::move(SymbolSlabs), std::move(RefSlabs),
394                         std::move(RefsStorage), std::move(SymsStorage)),
395         StorageSize);
396   }
397   llvm_unreachable("Unknown clangd::IndexType");
398 }
399 
400 void FileSymbols::profile(MemoryTree &MT) const {
401   std::lock_guard<std::mutex> Lock(Mutex);
402   for (const auto &SymSlab : SymbolsSnapshot) {
403     MT.detail(SymSlab.first())
404         .child("symbols")
405         .addUsage(SymSlab.second->bytes());
406   }
407   for (const auto &RefSlab : RefsSnapshot) {
408     MT.detail(RefSlab.first())
409         .child("references")
410         .addUsage(RefSlab.second.Slab->bytes());
411   }
412   for (const auto &RelSlab : RelationsSnapshot) {
413     MT.detail(RelSlab.first())
414         .child("relations")
415         .addUsage(RelSlab.second->bytes());
416   }
417 }
418 
419 FileIndex::FileIndex()
420     : MergedIndex(&MainFileIndex, &PreambleIndex),
421       PreambleSymbols(IndexContents::Symbols | IndexContents::Relations),
422       PreambleIndex(std::make_unique<MemIndex>()),
423       MainFileSymbols(IndexContents::All),
424       MainFileIndex(std::make_unique<MemIndex>()) {}
425 
426 void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version,
427                                ASTContext &AST, Preprocessor &PP,
428                                const CanonicalIncludes &Includes) {
429   IndexFileIn IF;
430   std::tie(IF.Symbols, std::ignore, IF.Relations) =
431       indexHeaderSymbols(Version, AST, PP, Includes);
432   FileShardedIndex ShardedIndex(std::move(IF));
433   for (auto Uri : ShardedIndex.getAllSources()) {
434     auto IF = ShardedIndex.getShard(Uri);
435     // We are using the key received from ShardedIndex, so it should always
436     // exist.
437     assert(IF);
438     PreambleSymbols.update(
439         Uri, std::make_unique<SymbolSlab>(std::move(*IF->Symbols)),
440         std::make_unique<RefSlab>(),
441         std::make_unique<RelationSlab>(std::move(*IF->Relations)),
442         /*CountReferences=*/false);
443   }
444   size_t IndexVersion = 0;
445   auto NewIndex = PreambleSymbols.buildIndex(
446       IndexType::Heavy, DuplicateHandling::PickOne, &IndexVersion);
447   {
448     std::lock_guard<std::mutex> Lock(UpdateIndexMu);
449     if (IndexVersion <= PreambleIndexVersion) {
450       // We lost the race, some other thread built a later version.
451       return;
452     }
453     PreambleIndexVersion = IndexVersion;
454     PreambleIndex.reset(std::move(NewIndex));
455     vlog(
456         "Build dynamic index for header symbols with estimated memory usage of "
457         "{0} bytes",
458         PreambleIndex.estimateMemoryUsage());
459   }
460 }
461 
462 void FileIndex::updateMain(PathRef Path, ParsedAST &AST) {
463   auto Contents = indexMainDecls(AST);
464   MainFileSymbols.update(
465       URI::create(Path).toString(),
466       std::make_unique<SymbolSlab>(std::move(std::get<0>(Contents))),
467       std::make_unique<RefSlab>(std::move(std::get<1>(Contents))),
468       std::make_unique<RelationSlab>(std::move(std::get<2>(Contents))),
469       /*CountReferences=*/true);
470   size_t IndexVersion = 0;
471   auto NewIndex = MainFileSymbols.buildIndex(
472       IndexType::Light, DuplicateHandling::Merge, &IndexVersion);
473   {
474     std::lock_guard<std::mutex> Lock(UpdateIndexMu);
475     if (IndexVersion <= MainIndexVersion) {
476       // We lost the race, some other thread built a later version.
477       return;
478     }
479     MainIndexVersion = IndexVersion;
480     MainFileIndex.reset(std::move(NewIndex));
481     vlog(
482         "Build dynamic index for main-file symbols with estimated memory usage "
483         "of {0} bytes",
484         MainFileIndex.estimateMemoryUsage());
485   }
486 }
487 
488 void FileIndex::profile(MemoryTree &MT) const {
489   PreambleSymbols.profile(MT.child("preamble").child("slabs"));
490   MT.child("preamble")
491       .child("index")
492       .addUsage(PreambleIndex.estimateMemoryUsage());
493   MainFileSymbols.profile(MT.child("main_file").child("slabs"));
494   MT.child("main_file")
495       .child("index")
496       .addUsage(MainFileIndex.estimateMemoryUsage());
497 }
498 } // namespace clangd
499 } // namespace clang
500