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