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