1 //===--- GlobalCompilationDatabase.cpp ---------------------------*- 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 "GlobalCompilationDatabase.h" 10 #include "Config.h" 11 #include "FS.h" 12 #include "SourceCode.h" 13 #include "support/Logger.h" 14 #include "support/Path.h" 15 #include "support/Threading.h" 16 #include "support/ThreadsafeFS.h" 17 #include "clang/Frontend/CompilerInvocation.h" 18 #include "clang/Tooling/ArgumentsAdjusters.h" 19 #include "clang/Tooling/CompilationDatabase.h" 20 #include "clang/Tooling/CompilationDatabasePluginRegistry.h" 21 #include "clang/Tooling/JSONCompilationDatabase.h" 22 #include "llvm/ADT/None.h" 23 #include "llvm/ADT/Optional.h" 24 #include "llvm/ADT/PointerIntPair.h" 25 #include "llvm/ADT/STLExtras.h" 26 #include "llvm/ADT/ScopeExit.h" 27 #include "llvm/ADT/SmallString.h" 28 #include "llvm/ADT/StringMap.h" 29 #include "llvm/Support/FileSystem.h" 30 #include "llvm/Support/FileUtilities.h" 31 #include "llvm/Support/Path.h" 32 #include "llvm/Support/Program.h" 33 #include "llvm/Support/VirtualFileSystem.h" 34 #include <atomic> 35 #include <chrono> 36 #include <condition_variable> 37 #include <string> 38 #include <tuple> 39 #include <vector> 40 41 namespace clang { 42 namespace clangd { 43 namespace { 44 45 // Variant of parent_path that operates only on absolute paths. 46 PathRef absoluteParent(PathRef Path) { 47 assert(llvm::sys::path::is_absolute(Path)); 48 #if defined(_WIN32) 49 // llvm::sys says "C:\" is absolute, and its parent is "C:" which is relative. 50 // This unhelpful behavior seems to have been inherited from boost. 51 if (llvm::sys::path::relative_path(Path).empty()) { 52 return PathRef(); 53 } 54 #endif 55 PathRef Result = llvm::sys::path::parent_path(Path); 56 assert(Result.empty() || llvm::sys::path::is_absolute(Result)); 57 return Result; 58 } 59 60 // Runs the given action on all parent directories of filename, starting from 61 // deepest directory and going up to root. Stops whenever action succeeds. 62 void actOnAllParentDirectories(PathRef FileName, 63 llvm::function_ref<bool(PathRef)> Action) { 64 for (auto Path = absoluteParent(FileName); !Path.empty() && !Action(Path); 65 Path = absoluteParent(Path)) 66 ; 67 } 68 69 } // namespace 70 71 tooling::CompileCommand 72 GlobalCompilationDatabase::getFallbackCommand(PathRef File) const { 73 std::vector<std::string> Argv = {"clang"}; 74 // Clang treats .h files as C by default and files without extension as linker 75 // input, resulting in unhelpful diagnostics. 76 // Parsing as Objective C++ is friendly to more cases. 77 auto FileExtension = llvm::sys::path::extension(File); 78 if (FileExtension.empty() || FileExtension == ".h") 79 Argv.push_back("-xobjective-c++-header"); 80 Argv.push_back(std::string(File)); 81 tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File), 82 llvm::sys::path::filename(File), std::move(Argv), 83 /*Output=*/""); 84 Cmd.Heuristic = "clangd fallback"; 85 return Cmd; 86 } 87 88 // Loads and caches the CDB from a single directory. 89 // 90 // This class is threadsafe, which is to say we have independent locks for each 91 // directory we're searching for a CDB. 92 // Loading is deferred until first access. 93 // 94 // The DirectoryBasedCDB keeps a map from path => DirectoryCache. 95 // Typical usage is to: 96 // - 1) determine all the paths that might be searched 97 // - 2) acquire the map lock and get-or-create all the DirectoryCache entries 98 // - 3) release the map lock and query the caches as desired 99 class DirectoryBasedGlobalCompilationDatabase::DirectoryCache { 100 using stopwatch = std::chrono::steady_clock; 101 102 // CachedFile is used to read a CDB file on disk (e.g. compile_commands.json). 103 // It specializes in being able to quickly bail out if the file is unchanged, 104 // which is the common case. 105 // Internally, it stores file metadata so a stat() can verify it's unchanged. 106 // We don't actually cache the content as it's not needed - if the file is 107 // unchanged then the previous CDB is valid. 108 struct CachedFile { 109 CachedFile(llvm::StringRef Parent, llvm::StringRef Rel) { 110 llvm::SmallString<256> Path = Parent; 111 llvm::sys::path::append(Path, Rel); 112 this->Path = Path.str().str(); 113 } 114 std::string Path; 115 size_t Size = NoFileCached; 116 llvm::sys::TimePoint<> ModifiedTime; 117 FileDigest ContentHash; 118 119 static constexpr size_t NoFileCached = -1; 120 121 struct LoadResult { 122 enum { 123 FileNotFound, 124 TransientError, 125 FoundSameData, 126 FoundNewData, 127 } Result; 128 std::unique_ptr<llvm::MemoryBuffer> Buffer; // Set only if FoundNewData 129 }; 130 131 LoadResult load(llvm::vfs::FileSystem &FS, bool HasOldData); 132 }; 133 134 // If we've looked for a CDB here and found none, the time when that happened. 135 // (Atomics make it possible for get() to return without taking a lock) 136 std::atomic<stopwatch::rep> NoCDBAt = { 137 stopwatch::time_point::min().time_since_epoch().count()}; 138 139 // Guards the following cache state. 140 std::mutex Mu; 141 // When was the cache last known to be in sync with disk state? 142 stopwatch::time_point CachePopulatedAt = stopwatch::time_point::min(); 143 // Whether a new CDB has been loaded but not broadcast yet. 144 bool NeedsBroadcast = false; 145 // Last loaded CDB, meaningful if CachePopulatedAt was ever set. 146 // shared_ptr so we can overwrite this when callers are still using the CDB. 147 std::shared_ptr<tooling::CompilationDatabase> CDB; 148 // File metadata for the CDB files we support tracking directly. 149 CachedFile CompileCommandsJson; 150 CachedFile BuildCompileCommandsJson; 151 CachedFile CompileFlagsTxt; 152 // CachedFile member corresponding to CDB. 153 // CDB | ACF | Scenario 154 // null | null | no CDB found, or initial empty cache 155 // set | null | CDB was loaded via generic plugin interface 156 // null | set | found known CDB file, but parsing it failed 157 // set | set | CDB was parsed from a known file 158 CachedFile *ActiveCachedFile = nullptr; 159 160 public: 161 DirectoryCache(llvm::StringRef Path) 162 : CompileCommandsJson(Path, "compile_commands.json"), 163 BuildCompileCommandsJson(Path, "build/compile_commands.json"), 164 CompileFlagsTxt(Path, "compile_flags.txt"), Path(Path) { 165 assert(llvm::sys::path::is_absolute(Path)); 166 } 167 168 // Absolute canonical path that we're the cache for. (Not case-folded). 169 const std::string Path; 170 171 // Get the CDB associated with this directory. 172 // ShouldBroadcast: 173 // - as input, signals whether the caller is willing to broadcast a 174 // newly-discovered CDB. (e.g. to trigger background indexing) 175 // - as output, signals whether the caller should do so. 176 // (If a new CDB is discovered and ShouldBroadcast is false, we mark the 177 // CDB as needing broadcast, and broadcast it next time we can). 178 std::shared_ptr<const tooling::CompilationDatabase> 179 get(const ThreadsafeFS &TFS, bool &ShouldBroadcast, 180 stopwatch::time_point FreshTime, stopwatch::time_point FreshTimeMissing) { 181 // Fast path for common case without taking lock. 182 if (stopwatch::time_point(stopwatch::duration(NoCDBAt.load())) > 183 FreshTimeMissing) { 184 ShouldBroadcast = false; 185 return nullptr; 186 } 187 188 std::lock_guard<std::mutex> Lock(Mu); 189 auto RequestBroadcast = llvm::make_scope_exit([&, OldCDB(CDB.get())] { 190 // If we loaded a new CDB, it should be broadcast at some point. 191 if (CDB != nullptr && CDB.get() != OldCDB) 192 NeedsBroadcast = true; 193 else if (CDB == nullptr) // nothing to broadcast anymore! 194 NeedsBroadcast = false; 195 // If we have something to broadcast, then do so iff allowed. 196 if (!ShouldBroadcast) 197 return; 198 ShouldBroadcast = NeedsBroadcast; 199 NeedsBroadcast = false; 200 }); 201 202 // If our cache is valid, serve from it. 203 if (CachePopulatedAt > FreshTime) 204 return CDB; 205 206 if (/*MayCache=*/load(*TFS.view(/*CWD=*/llvm::None))) { 207 // Use new timestamp, as loading may be slow. 208 CachePopulatedAt = stopwatch::now(); 209 NoCDBAt.store((CDB ? stopwatch::time_point::min() : CachePopulatedAt) 210 .time_since_epoch() 211 .count()); 212 } 213 214 return CDB; 215 } 216 217 private: 218 // Updates `CDB` from disk state. Returns false on failure. 219 bool load(llvm::vfs::FileSystem &FS); 220 }; 221 222 DirectoryBasedGlobalCompilationDatabase::DirectoryCache::CachedFile::LoadResult 223 DirectoryBasedGlobalCompilationDatabase::DirectoryCache::CachedFile::load( 224 llvm::vfs::FileSystem &FS, bool HasOldData) { 225 auto Stat = FS.status(Path); 226 if (!Stat || !Stat->isRegularFile()) { 227 Size = NoFileCached; 228 ContentHash = {}; 229 return {LoadResult::FileNotFound, nullptr}; 230 } 231 // If both the size and mtime match, presume unchanged without reading. 232 if (HasOldData && Stat->getLastModificationTime() == ModifiedTime && 233 Stat->getSize() == Size) 234 return {LoadResult::FoundSameData, nullptr}; 235 auto Buf = FS.getBufferForFile(Path); 236 if (!Buf || (*Buf)->getBufferSize() != Stat->getSize()) { 237 // Don't clear the cache - possible we're seeing inconsistent size as the 238 // file is being recreated. If it ends up identical later, great! 239 // 240 // This isn't a complete solution: if we see a partial file but stat/read 241 // agree on its size, we're ultimately going to have spurious CDB reloads. 242 // May be worth fixing if generators don't write atomically (CMake does). 243 elog("Failed to read {0}: {1}", Path, 244 Buf ? "size changed" : Buf.getError().message()); 245 return {LoadResult::TransientError, nullptr}; 246 } 247 248 FileDigest NewContentHash = digest((*Buf)->getBuffer()); 249 if (HasOldData && NewContentHash == ContentHash) { 250 // mtime changed but data is the same: avoid rebuilding the CDB. 251 ModifiedTime = Stat->getLastModificationTime(); 252 return {LoadResult::FoundSameData, nullptr}; 253 } 254 255 Size = (*Buf)->getBufferSize(); 256 ModifiedTime = Stat->getLastModificationTime(); 257 ContentHash = NewContentHash; 258 return {LoadResult::FoundNewData, std::move(*Buf)}; 259 } 260 261 // Adapt CDB-loading functions to a common interface for DirectoryCache::load(). 262 static std::unique_ptr<tooling::CompilationDatabase> 263 parseJSON(PathRef Path, llvm::StringRef Data, std::string &Error) { 264 if (auto CDB = tooling::JSONCompilationDatabase::loadFromBuffer( 265 Data, Error, tooling::JSONCommandLineSyntax::AutoDetect)) { 266 // FS used for expanding response files. 267 // FIXME: ExpandResponseFilesDatabase appears not to provide the usual 268 // thread-safety guarantees, as the access to FS is not locked! 269 // For now, use the real FS, which is known to be threadsafe (if we don't 270 // use/change working directory, which ExpandResponseFilesDatabase doesn't). 271 auto FS = llvm::vfs::getRealFileSystem(); 272 return tooling::inferTargetAndDriverMode( 273 tooling::inferMissingCompileCommands( 274 expandResponseFiles(std::move(CDB), std::move(FS)))); 275 } 276 return nullptr; 277 } 278 static std::unique_ptr<tooling::CompilationDatabase> 279 parseFixed(PathRef Path, llvm::StringRef Data, std::string &Error) { 280 return tooling::FixedCompilationDatabase::loadFromBuffer( 281 llvm::sys::path::parent_path(Path), Data, Error); 282 } 283 284 bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load( 285 llvm::vfs::FileSystem &FS) { 286 dlog("Probing directory {0}", Path); 287 std::string Error; 288 289 // Load from the specially-supported compilation databases (JSON + Fixed). 290 // For these, we know the files they read and cache their metadata so we can 291 // cheaply validate whether they've changed, and hot-reload if they have. 292 // (As a bonus, these are also VFS-clean)! 293 struct CDBFile { 294 CachedFile *File; 295 // Wrapper for {Fixed,JSON}CompilationDatabase::loadFromBuffer. 296 llvm::function_ref<std::unique_ptr<tooling::CompilationDatabase>( 297 PathRef, 298 /*Data*/ llvm::StringRef, 299 /*ErrorMsg*/ std::string &)> 300 Parser; 301 }; 302 for (const auto &Entry : {CDBFile{&CompileCommandsJson, parseJSON}, 303 CDBFile{&BuildCompileCommandsJson, parseJSON}, 304 CDBFile{&CompileFlagsTxt, parseFixed}}) { 305 bool Active = ActiveCachedFile == Entry.File; 306 auto Loaded = Entry.File->load(FS, Active); 307 switch (Loaded.Result) { 308 case CachedFile::LoadResult::FileNotFound: 309 if (Active) { 310 log("Unloaded compilation database from {0}", Entry.File->Path); 311 ActiveCachedFile = nullptr; 312 CDB = nullptr; 313 } 314 // Continue looking at other candidates. 315 break; 316 case CachedFile::LoadResult::TransientError: 317 // File existed but we couldn't read it. Reuse the cache, retry later. 318 return false; // Load again next time. 319 case CachedFile::LoadResult::FoundSameData: 320 assert(Active && "CachedFile may not return 'same data' if !HasOldData"); 321 // This is the critical file, and it hasn't changed. 322 return true; 323 case CachedFile::LoadResult::FoundNewData: 324 // We have a new CDB! 325 CDB = Entry.Parser(Entry.File->Path, Loaded.Buffer->getBuffer(), Error); 326 if (CDB) 327 log("{0} compilation database from {1}", Active ? "Reloaded" : "Loaded", 328 Entry.File->Path); 329 else 330 elog("Failed to load compilation database from {0}: {1}", 331 Entry.File->Path, Error); 332 ActiveCachedFile = Entry.File; 333 return true; 334 } 335 } 336 337 // Fall back to generic handling of compilation databases. 338 // We don't know what files they read, so can't efficiently check whether 339 // they need to be reloaded. So we never do that. 340 // FIXME: the interface doesn't provide a way to virtualize FS access. 341 342 // Don't try these more than once. If we've scanned before, we're done. 343 if (CachePopulatedAt > stopwatch::time_point::min()) 344 return true; 345 for (const auto &Entry : 346 tooling::CompilationDatabasePluginRegistry::entries()) { 347 // Avoid duplicating the special cases handled above. 348 if (Entry.getName() == "fixed-compilation-database" || 349 Entry.getName() == "json-compilation-database") 350 continue; 351 auto Plugin = Entry.instantiate(); 352 if (auto CDB = Plugin->loadFromDirectory(Path, Error)) { 353 log("Loaded compilation database from {0} with plugin {1}", Path, 354 Entry.getName()); 355 this->CDB = std::move(CDB); 356 return true; 357 } 358 // Don't log Error here, it's usually just "couldn't find <file>". 359 } 360 dlog("No compilation database at {0}", Path); 361 return true; 362 } 363 364 DirectoryBasedGlobalCompilationDatabase:: 365 DirectoryBasedGlobalCompilationDatabase(const Options &Opts) 366 : Opts(Opts), Broadcaster(std::make_unique<BroadcastThread>(*this)) { 367 if (!this->Opts.ContextProvider) 368 this->Opts.ContextProvider = [](llvm::StringRef) { 369 return Context::current().clone(); 370 }; 371 } 372 373 DirectoryBasedGlobalCompilationDatabase:: 374 ~DirectoryBasedGlobalCompilationDatabase() = default; 375 376 llvm::Optional<tooling::CompileCommand> 377 DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const { 378 CDBLookupRequest Req; 379 Req.FileName = File; 380 Req.ShouldBroadcast = true; 381 auto Now = std::chrono::steady_clock::now(); 382 Req.FreshTime = Now - Opts.RevalidateAfter; 383 Req.FreshTimeMissing = Now - Opts.RevalidateMissingAfter; 384 385 auto Res = lookupCDB(Req); 386 if (!Res) { 387 log("Failed to find compilation database for {0}", File); 388 return llvm::None; 389 } 390 391 auto Candidates = Res->CDB->getCompileCommands(File); 392 if (!Candidates.empty()) 393 return std::move(Candidates.front()); 394 395 return None; 396 } 397 398 // For platforms where paths are case-insensitive (but case-preserving), 399 // we need to do case-insensitive comparisons and use lowercase keys. 400 // FIXME: Make Path a real class with desired semantics instead. 401 // This class is not the only place this problem exists. 402 // FIXME: Mac filesystems default to case-insensitive, but may be sensitive. 403 404 static std::string maybeCaseFoldPath(PathRef Path) { 405 #if defined(_WIN32) || defined(__APPLE__) 406 return Path.lower(); 407 #else 408 return std::string(Path); 409 #endif 410 } 411 412 std::vector<DirectoryBasedGlobalCompilationDatabase::DirectoryCache *> 413 DirectoryBasedGlobalCompilationDatabase::getDirectoryCaches( 414 llvm::ArrayRef<llvm::StringRef> Dirs) const { 415 std::vector<std::string> FoldedDirs; 416 FoldedDirs.reserve(Dirs.size()); 417 for (const auto &Dir : Dirs) { 418 #ifndef NDEBUG 419 if (!llvm::sys::path::is_absolute(Dir)) 420 elog("Trying to cache CDB for relative {0}"); 421 #endif 422 FoldedDirs.push_back(maybeCaseFoldPath(Dir)); 423 } 424 425 std::vector<DirectoryCache *> Ret; 426 Ret.reserve(Dirs.size()); 427 428 std::lock_guard<std::mutex> Lock(DirCachesMutex); 429 for (unsigned I = 0; I < Dirs.size(); ++I) 430 Ret.push_back(&DirCaches.try_emplace(FoldedDirs[I], Dirs[I]).first->second); 431 return Ret; 432 } 433 434 llvm::Optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult> 435 DirectoryBasedGlobalCompilationDatabase::lookupCDB( 436 CDBLookupRequest Request) const { 437 assert(llvm::sys::path::is_absolute(Request.FileName) && 438 "path must be absolute"); 439 440 std::string Storage; 441 std::vector<llvm::StringRef> SearchDirs; 442 if (Opts.CompileCommandsDir) // FIXME: unify this case with config. 443 SearchDirs = {Opts.CompileCommandsDir.getValue()}; 444 else { 445 WithContext WithProvidedContext(Opts.ContextProvider(Request.FileName)); 446 const auto &Spec = Config::current().CompileFlags.CDBSearch; 447 switch (Spec.Policy) { 448 case Config::CDBSearchSpec::NoCDBSearch: 449 return llvm::None; 450 case Config::CDBSearchSpec::FixedDir: 451 Storage = Spec.FixedCDBPath.getValue(); 452 SearchDirs = {Storage}; 453 break; 454 case Config::CDBSearchSpec::Ancestors: 455 // Traverse the canonical version to prevent false positives. i.e.: 456 // src/build/../a.cc can detect a CDB in /src/build if not 457 // canonicalized. 458 Storage = removeDots(Request.FileName); 459 actOnAllParentDirectories(Storage, [&](llvm::StringRef Dir) { 460 SearchDirs.push_back(Dir); 461 return false; 462 }); 463 } 464 } 465 466 std::shared_ptr<const tooling::CompilationDatabase> CDB = nullptr; 467 bool ShouldBroadcast = false; 468 DirectoryCache *DirCache = nullptr; 469 for (DirectoryCache *Candidate : getDirectoryCaches(SearchDirs)) { 470 bool CandidateShouldBroadcast = Request.ShouldBroadcast; 471 if ((CDB = Candidate->get(Opts.TFS, CandidateShouldBroadcast, 472 Request.FreshTime, Request.FreshTimeMissing))) { 473 DirCache = Candidate; 474 ShouldBroadcast = CandidateShouldBroadcast; 475 break; 476 } 477 } 478 479 if (!CDB) 480 return llvm::None; 481 482 CDBLookupResult Result; 483 Result.CDB = std::move(CDB); 484 Result.PI.SourceRoot = DirCache->Path; 485 486 if (ShouldBroadcast) 487 broadcastCDB(Result); 488 return Result; 489 } 490 491 // The broadcast thread announces files with new compile commands to the world. 492 // Primarily this is used to enqueue them for background indexing. 493 // 494 // It's on a separate thread because: 495 // - otherwise it would block the first parse of the initial file 496 // - we need to enumerate all files in the CDB, of which there are many 497 // - we (will) have to evaluate config for every file in the CDB, which is slow 498 class DirectoryBasedGlobalCompilationDatabase::BroadcastThread { 499 class Filter; 500 DirectoryBasedGlobalCompilationDatabase &Parent; 501 502 std::mutex Mu; 503 std::condition_variable CV; 504 // Shutdown flag (CV is notified after writing). 505 // This is atomic so that broadcasts can also observe it and abort early. 506 std::atomic<bool> ShouldStop = {false}; 507 struct Task { 508 CDBLookupResult Lookup; 509 Context Ctx; 510 }; 511 std::deque<Task> Queue; 512 llvm::Optional<Task> ActiveTask; 513 std::thread Thread; // Must be last member. 514 515 // Thread body: this is just the basic queue procesing boilerplate. 516 void run() { 517 std::unique_lock<std::mutex> Lock(Mu); 518 while (true) { 519 bool Stopping = false; 520 CV.wait(Lock, [&] { 521 return (Stopping = ShouldStop.load(std::memory_order_acquire)) || 522 !Queue.empty(); 523 }); 524 if (Stopping) { 525 Queue.clear(); 526 CV.notify_all(); 527 return; 528 } 529 ActiveTask = std::move(Queue.front()); 530 Queue.pop_front(); 531 532 Lock.unlock(); 533 { 534 WithContext WithCtx(std::move(ActiveTask->Ctx)); 535 process(ActiveTask->Lookup); 536 } 537 Lock.lock(); 538 ActiveTask.reset(); 539 CV.notify_all(); 540 } 541 } 542 543 // Inspects a new CDB and broadcasts the files it owns. 544 void process(const CDBLookupResult &T); 545 546 public: 547 BroadcastThread(DirectoryBasedGlobalCompilationDatabase &Parent) 548 : Parent(Parent), Thread([this] { run(); }) {} 549 550 void enqueue(CDBLookupResult Lookup) { 551 { 552 assert(!Lookup.PI.SourceRoot.empty()); 553 std::lock_guard<std::mutex> Lock(Mu); 554 // New CDB takes precedence over any queued one for the same directory. 555 llvm::erase_if(Queue, [&](const Task &T) { 556 return T.Lookup.PI.SourceRoot == Lookup.PI.SourceRoot; 557 }); 558 Queue.push_back({std::move(Lookup), Context::current().clone()}); 559 } 560 CV.notify_all(); 561 } 562 563 bool blockUntilIdle(Deadline Timeout) { 564 std::unique_lock<std::mutex> Lock(Mu); 565 return wait(Lock, CV, Timeout, 566 [&] { return Queue.empty() && !ActiveTask.hasValue(); }); 567 } 568 569 ~BroadcastThread() { 570 ShouldStop.store(true, std::memory_order_release); 571 CV.notify_all(); 572 Thread.join(); 573 } 574 }; 575 576 // The DirBasedCDB associates each file with a specific CDB. 577 // When a CDB is discovered, it may claim to describe files that we associate 578 // with a different CDB. We do not want to broadcast discovery of these, and 579 // trigger background indexing of them. 580 // 581 // We must filter the list, and check whether they are associated with this CDB. 582 // This class attempts to do so efficiently. 583 // 584 // Roughly, it: 585 // - loads the config for each file, and determines the relevant search path 586 // - gathers all directories that are part of any search path 587 // - (lazily) checks for a CDB in each such directory at most once 588 // - walks the search path for each file and determines whether to include it. 589 class DirectoryBasedGlobalCompilationDatabase::BroadcastThread::Filter { 590 llvm::StringRef ThisDir; 591 DirectoryBasedGlobalCompilationDatabase &Parent; 592 593 // Keep track of all directories we might check for CDBs. 594 struct DirInfo { 595 DirectoryCache *Cache = nullptr; 596 enum { Unknown, Missing, TargetCDB, OtherCDB } State = Unknown; 597 DirInfo *Parent = nullptr; 598 }; 599 llvm::StringMap<DirInfo> Dirs; 600 601 // A search path starts at a directory, and either includes ancestors or not. 602 using SearchPath = llvm::PointerIntPair<DirInfo *, 1>; 603 604 // Add all ancestor directories of FilePath to the tracked set. 605 // Returns the immediate parent of the file. 606 DirInfo *addParents(llvm::StringRef FilePath) { 607 DirInfo *Leaf = nullptr; 608 DirInfo *Child = nullptr; 609 actOnAllParentDirectories(FilePath, [&](llvm::StringRef Dir) { 610 auto &Info = Dirs[Dir]; 611 // If this is the first iteration, then this node is the overall result. 612 if (!Leaf) 613 Leaf = &Info; 614 // Fill in the parent link from the previous iteration to this parent. 615 if (Child) 616 Child->Parent = &Info; 617 // Keep walking, whether we inserted or not, if parent link is missing. 618 // (If it's present, parent links must be present up to the root, so stop) 619 Child = &Info; 620 return Info.Parent != nullptr; 621 }); 622 return Leaf; 623 } 624 625 // Populates DirInfo::Cache (and State, if it is TargetCDB). 626 void grabCaches() { 627 // Fast path out if there were no files, or CDB loading is off. 628 if (Dirs.empty()) 629 return; 630 631 std::vector<llvm::StringRef> DirKeys; 632 std::vector<DirInfo *> DirValues; 633 DirKeys.reserve(Dirs.size() + 1); 634 DirValues.reserve(Dirs.size()); 635 for (auto &E : Dirs) { 636 DirKeys.push_back(E.first()); 637 DirValues.push_back(&E.second); 638 } 639 640 // Also look up the cache entry for the CDB we're broadcasting. 641 // Comparing DirectoryCache pointers is more robust than checking string 642 // equality, e.g. reuses the case-sensitivity handling. 643 DirKeys.push_back(ThisDir); 644 auto DirCaches = Parent.getDirectoryCaches(DirKeys); 645 const DirectoryCache *ThisCache = DirCaches.back(); 646 DirCaches.pop_back(); 647 DirKeys.pop_back(); 648 649 for (unsigned I = 0; I < DirKeys.size(); ++I) { 650 DirValues[I]->Cache = DirCaches[I]; 651 if (DirCaches[I] == ThisCache) 652 DirValues[I]->State = DirInfo::TargetCDB; 653 } 654 } 655 656 // Should we include a file from this search path? 657 bool shouldInclude(SearchPath P) { 658 DirInfo *Info = P.getPointer(); 659 if (!Info) 660 return false; 661 if (Info->State == DirInfo::Unknown) { 662 assert(Info->Cache && "grabCaches() should have filled this"); 663 // Given that we know that CDBs have been moved/generated, don't trust 664 // caches. (This should be rare, so it's OK to add a little latency). 665 constexpr auto IgnoreCache = std::chrono::steady_clock::time_point::max(); 666 // Don't broadcast CDBs discovered while broadcasting! 667 bool ShouldBroadcast = false; 668 bool Exists = 669 nullptr != Info->Cache->get(Parent.Opts.TFS, ShouldBroadcast, 670 /*FreshTime=*/IgnoreCache, 671 /*FreshTimeMissing=*/IgnoreCache); 672 Info->State = Exists ? DirInfo::OtherCDB : DirInfo::Missing; 673 } 674 // If we have a CDB, include the file if it's the target CDB only. 675 if (Info->State != DirInfo::Missing) 676 return Info->State == DirInfo::TargetCDB; 677 // If we have no CDB and no relevant parent, don't include the file. 678 if (!P.getInt() || !Info->Parent) 679 return false; 680 // Walk up to the next parent. 681 return shouldInclude(SearchPath(Info->Parent, 1)); 682 } 683 684 public: 685 Filter(llvm::StringRef ThisDir, 686 DirectoryBasedGlobalCompilationDatabase &Parent) 687 : ThisDir(ThisDir), Parent(Parent) {} 688 689 std::vector<std::string> filter(std::vector<std::string> AllFiles, 690 std::atomic<bool> &ShouldStop) { 691 std::vector<std::string> Filtered; 692 // Allow for clean early-exit of the slow parts. 693 auto ExitEarly = [&] { 694 if (ShouldStop.load(std::memory_order_acquire)) { 695 log("Giving up on broadcasting CDB, as we're shutting down"); 696 Filtered.clear(); 697 return true; 698 } 699 return false; 700 }; 701 // Compute search path for each file. 702 std::vector<SearchPath> SearchPaths(AllFiles.size()); 703 for (unsigned I = 0; I < AllFiles.size(); ++I) { 704 if (Parent.Opts.CompileCommandsDir) { // FIXME: unify with config 705 SearchPaths[I].setPointer( 706 &Dirs[Parent.Opts.CompileCommandsDir.getValue()]); 707 continue; 708 } 709 if (ExitEarly()) // loading config may be slow 710 return Filtered; 711 WithContext WithProvidedContent(Parent.Opts.ContextProvider(AllFiles[I])); 712 const Config::CDBSearchSpec &Spec = 713 Config::current().CompileFlags.CDBSearch; 714 switch (Spec.Policy) { 715 case Config::CDBSearchSpec::NoCDBSearch: 716 break; 717 case Config::CDBSearchSpec::Ancestors: 718 SearchPaths[I].setInt(/*Recursive=*/1); 719 SearchPaths[I].setPointer(addParents(AllFiles[I])); 720 break; 721 case Config::CDBSearchSpec::FixedDir: 722 SearchPaths[I].setPointer(&Dirs[Spec.FixedCDBPath.getValue()]); 723 break; 724 } 725 } 726 // Get the CDB cache for each dir on the search path, but don't load yet. 727 grabCaches(); 728 // Now work out which files we want to keep, loading CDBs where needed. 729 for (unsigned I = 0; I < AllFiles.size(); ++I) { 730 if (ExitEarly()) // loading CDBs may be slow 731 return Filtered; 732 if (shouldInclude(SearchPaths[I])) 733 Filtered.push_back(std::move(AllFiles[I])); 734 } 735 return Filtered; 736 } 737 }; 738 739 void DirectoryBasedGlobalCompilationDatabase::BroadcastThread::process( 740 const CDBLookupResult &T) { 741 vlog("Broadcasting compilation database from {0}", T.PI.SourceRoot); 742 std::vector<std::string> GovernedFiles = 743 Filter(T.PI.SourceRoot, Parent).filter(T.CDB->getAllFiles(), ShouldStop); 744 if (!GovernedFiles.empty()) 745 Parent.OnCommandChanged.broadcast(std::move(GovernedFiles)); 746 } 747 748 void DirectoryBasedGlobalCompilationDatabase::broadcastCDB( 749 CDBLookupResult Result) const { 750 assert(Result.CDB && "Trying to broadcast an invalid CDB!"); 751 Broadcaster->enqueue(Result); 752 } 753 754 bool DirectoryBasedGlobalCompilationDatabase::blockUntilIdle( 755 Deadline Timeout) const { 756 return Broadcaster->blockUntilIdle(Timeout); 757 } 758 759 llvm::Optional<ProjectInfo> 760 DirectoryBasedGlobalCompilationDatabase::getProjectInfo(PathRef File) const { 761 CDBLookupRequest Req; 762 Req.FileName = File; 763 Req.ShouldBroadcast = false; 764 Req.FreshTime = Req.FreshTimeMissing = 765 std::chrono::steady_clock::time_point::min(); 766 auto Res = lookupCDB(Req); 767 if (!Res) 768 return llvm::None; 769 return Res->PI; 770 } 771 772 OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base, 773 std::vector<std::string> FallbackFlags, 774 tooling::ArgumentsAdjuster Adjuster) 775 : DelegatingCDB(Base), ArgsAdjuster(std::move(Adjuster)), 776 FallbackFlags(std::move(FallbackFlags)) {} 777 778 llvm::Optional<tooling::CompileCommand> 779 OverlayCDB::getCompileCommand(PathRef File) const { 780 llvm::Optional<tooling::CompileCommand> Cmd; 781 { 782 std::lock_guard<std::mutex> Lock(Mutex); 783 auto It = Commands.find(removeDots(File)); 784 if (It != Commands.end()) 785 Cmd = It->second; 786 } 787 if (!Cmd) 788 Cmd = DelegatingCDB::getCompileCommand(File); 789 if (!Cmd) 790 return llvm::None; 791 if (ArgsAdjuster) 792 Cmd->CommandLine = ArgsAdjuster(Cmd->CommandLine, Cmd->Filename); 793 return Cmd; 794 } 795 796 tooling::CompileCommand OverlayCDB::getFallbackCommand(PathRef File) const { 797 auto Cmd = DelegatingCDB::getFallbackCommand(File); 798 std::lock_guard<std::mutex> Lock(Mutex); 799 Cmd.CommandLine.insert(Cmd.CommandLine.end(), FallbackFlags.begin(), 800 FallbackFlags.end()); 801 if (ArgsAdjuster) 802 Cmd.CommandLine = ArgsAdjuster(Cmd.CommandLine, Cmd.Filename); 803 return Cmd; 804 } 805 806 void OverlayCDB::setCompileCommand( 807 PathRef File, llvm::Optional<tooling::CompileCommand> Cmd) { 808 // We store a canonical version internally to prevent mismatches between set 809 // and get compile commands. Also it assures clients listening to broadcasts 810 // doesn't receive different names for the same file. 811 std::string CanonPath = removeDots(File); 812 { 813 std::unique_lock<std::mutex> Lock(Mutex); 814 if (Cmd) 815 Commands[CanonPath] = std::move(*Cmd); 816 else 817 Commands.erase(CanonPath); 818 } 819 OnCommandChanged.broadcast({CanonPath}); 820 } 821 822 DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base) 823 : Base(Base) { 824 if (Base) 825 BaseChanged = Base->watch([this](const std::vector<std::string> Changes) { 826 OnCommandChanged.broadcast(Changes); 827 }); 828 } 829 830 DelegatingCDB::DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base) 831 : DelegatingCDB(Base.get()) { 832 BaseOwner = std::move(Base); 833 } 834 835 llvm::Optional<tooling::CompileCommand> 836 DelegatingCDB::getCompileCommand(PathRef File) const { 837 if (!Base) 838 return llvm::None; 839 return Base->getCompileCommand(File); 840 } 841 842 llvm::Optional<ProjectInfo> DelegatingCDB::getProjectInfo(PathRef File) const { 843 if (!Base) 844 return llvm::None; 845 return Base->getProjectInfo(File); 846 } 847 848 tooling::CompileCommand DelegatingCDB::getFallbackCommand(PathRef File) const { 849 if (!Base) 850 return GlobalCompilationDatabase::getFallbackCommand(File); 851 return Base->getFallbackCommand(File); 852 } 853 854 bool DelegatingCDB::blockUntilIdle(Deadline D) const { 855 if (!Base) 856 return true; 857 return Base->blockUntilIdle(D); 858 } 859 860 } // namespace clangd 861 } // namespace clang 862