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 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. 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 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 } 181 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> 192 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 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 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 241 FileSymbols::FileSymbols(IndexContents IdxContents) 242 : IdxContents(IdxContents) {} 243 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> 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 402 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 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 428 void FileIndex::updatePreamble(PathRef Path, llvm::StringRef Version, 429 ASTContext &AST, Preprocessor &PP, 430 const CanonicalIncludes &Includes) { 431 IndexFileIn IF; 432 std::tie(IF.Symbols, std::ignore, IF.Relations) = 433 indexHeaderSymbols(Version, AST, PP, Includes); 434 FileShardedIndex ShardedIndex(std::move(IF)); 435 for (auto Uri : ShardedIndex.getAllSources()) { 436 auto IF = ShardedIndex.getShard(Uri); 437 // We are using the key received from ShardedIndex, so it should always 438 // exist. 439 assert(IF); 440 PreambleSymbols.update( 441 Uri, std::make_unique<SymbolSlab>(std::move(*IF->Symbols)), 442 std::make_unique<RefSlab>(), 443 std::make_unique<RelationSlab>(std::move(*IF->Relations)), 444 /*CountReferences=*/false); 445 } 446 size_t IndexVersion = 0; 447 auto NewIndex = PreambleSymbols.buildIndex( 448 IndexType::Heavy, DuplicateHandling::PickOne, &IndexVersion); 449 { 450 std::lock_guard<std::mutex> Lock(UpdateIndexMu); 451 if (IndexVersion <= PreambleIndexVersion) { 452 // We lost the race, some other thread built a later version. 453 return; 454 } 455 PreambleIndexVersion = IndexVersion; 456 PreambleIndex.reset(std::move(NewIndex)); 457 vlog( 458 "Build dynamic index for header symbols with estimated memory usage of " 459 "{0} bytes", 460 PreambleIndex.estimateMemoryUsage()); 461 } 462 } 463 464 void FileIndex::updateMain(PathRef Path, ParsedAST &AST) { 465 auto Contents = indexMainDecls(AST); 466 MainFileSymbols.update( 467 URI::create(Path).toString(), 468 std::make_unique<SymbolSlab>(std::move(std::get<0>(Contents))), 469 std::make_unique<RefSlab>(std::move(std::get<1>(Contents))), 470 std::make_unique<RelationSlab>(std::move(std::get<2>(Contents))), 471 /*CountReferences=*/true); 472 size_t IndexVersion = 0; 473 auto NewIndex = MainFileSymbols.buildIndex( 474 IndexType::Light, DuplicateHandling::Merge, &IndexVersion); 475 { 476 std::lock_guard<std::mutex> Lock(UpdateIndexMu); 477 if (IndexVersion <= MainIndexVersion) { 478 // We lost the race, some other thread built a later version. 479 return; 480 } 481 MainIndexVersion = IndexVersion; 482 MainFileIndex.reset(std::move(NewIndex)); 483 vlog( 484 "Build dynamic index for main-file symbols with estimated memory usage " 485 "of {0} bytes", 486 MainFileIndex.estimateMemoryUsage()); 487 } 488 } 489 490 void FileIndex::profile(MemoryTree &MT) const { 491 PreambleSymbols.profile(MT.child("preamble").child("slabs")); 492 MT.child("preamble") 493 .child("index") 494 .addUsage(PreambleIndex.estimateMemoryUsage()); 495 MainFileSymbols.profile(MT.child("main_file").child("slabs")); 496 MT.child("main_file") 497 .child("index") 498 .addUsage(MainFileIndex.estimateMemoryUsage()); 499 } 500 } // namespace clangd 501 } // namespace clang 502