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