comparison clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @ 221:79ff65ed7e25

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