blob: d4ad39580d1284a3678671c3a6ac86733543c340 [file] [log] [blame]
Sam McCall8dc9dbb2018-10-15 13:34:10 +00001//===-- Background.cpp - Build an index in a background thread ------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "index/Background.h"
11#include "ClangdUnit.h"
12#include "Compiler.h"
13#include "Logger.h"
Kadir Cetinkayad08eab42018-11-27 16:08:53 +000014#include "SourceCode.h"
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000015#include "Threading.h"
Sam McCall8dc9dbb2018-10-15 13:34:10 +000016#include "Trace.h"
Eric Liuad588af2018-11-06 10:55:21 +000017#include "URI.h"
Sam McCall8dc9dbb2018-10-15 13:34:10 +000018#include "index/IndexAction.h"
19#include "index/MemIndex.h"
20#include "index/Serialization.h"
Eric Liuad588af2018-11-06 10:55:21 +000021#include "index/SymbolCollector.h"
22#include "clang/Basic/SourceLocation.h"
23#include "clang/Basic/SourceManager.h"
24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/StringMap.h"
26#include "llvm/ADT/StringRef.h"
Sam McCall8dc9dbb2018-10-15 13:34:10 +000027#include "llvm/Support/SHA1.h"
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +000028
29#include <memory>
Sam McCall7d0e4842018-11-26 13:35:02 +000030#include <numeric>
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +000031#include <queue>
Sam McCall8dc9dbb2018-10-15 13:34:10 +000032#include <random>
Eric Liuad588af2018-11-06 10:55:21 +000033#include <string>
Sam McCall8dc9dbb2018-10-15 13:34:10 +000034
35using namespace llvm;
36namespace clang {
37namespace clangd {
38
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +000039BackgroundIndex::BackgroundIndex(
40 Context BackgroundContext, StringRef ResourceDir,
Sam McCall6e2d2a32018-11-26 09:51:50 +000041 const FileSystemProvider &FSProvider, const GlobalCompilationDatabase &CDB,
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +000042 BackgroundIndexStorage::Factory IndexStorageFactory, size_t ThreadPoolSize)
Sam McCallc008af62018-10-20 15:30:37 +000043 : SwapIndex(make_unique<MemIndex>()), ResourceDir(ResourceDir),
Sam McCall6e2d2a32018-11-26 09:51:50 +000044 FSProvider(FSProvider), CDB(CDB),
45 BackgroundContext(std::move(BackgroundContext)),
46 IndexStorageFactory(std::move(IndexStorageFactory)),
47 CommandsChanged(
48 CDB.watch([&](const std::vector<std::string> &ChangedFiles) {
49 enqueue(ChangedFiles);
50 })) {
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000051 assert(ThreadPoolSize > 0 && "Thread pool size can't be zero.");
Haojian Wu1bf52c52018-11-16 09:41:14 +000052 assert(this->IndexStorageFactory && "Storage factory can not be null!");
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000053 while (ThreadPoolSize--) {
54 ThreadPool.emplace_back([this] { run(); });
55 // Set priority to low, since background indexing is a long running task we
56 // do not want to eat up cpu when there are any other high priority threads.
57 // FIXME: In the future we might want a more general way of handling this to
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +000058 // support tasks with various priorities.
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000059 setThreadPriority(ThreadPool.back(), ThreadPriority::Low);
60 }
61}
Sam McCall8dc9dbb2018-10-15 13:34:10 +000062
63BackgroundIndex::~BackgroundIndex() {
64 stop();
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000065 for (auto &Thread : ThreadPool)
66 Thread.join();
Sam McCall8dc9dbb2018-10-15 13:34:10 +000067}
68
69void BackgroundIndex::stop() {
70 {
71 std::lock_guard<std::mutex> Lock(QueueMu);
72 ShouldStop = true;
73 }
74 QueueCV.notify_all();
75}
76
77void BackgroundIndex::run() {
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000078 WithContext Background(BackgroundContext.clone());
Sam McCall8dc9dbb2018-10-15 13:34:10 +000079 while (true) {
Sam McCallc008af62018-10-20 15:30:37 +000080 Optional<Task> Task;
Sam McCall8dc9dbb2018-10-15 13:34:10 +000081 {
82 std::unique_lock<std::mutex> Lock(QueueMu);
83 QueueCV.wait(Lock, [&] { return ShouldStop || !Queue.empty(); });
84 if (ShouldStop) {
85 Queue.clear();
86 QueueCV.notify_all();
87 return;
88 }
89 ++NumActiveTasks;
90 Task = std::move(Queue.front());
91 Queue.pop_front();
92 }
93 (*Task)();
94 {
95 std::unique_lock<std::mutex> Lock(QueueMu);
96 assert(NumActiveTasks > 0 && "before decrementing");
97 --NumActiveTasks;
98 }
99 QueueCV.notify_all();
100 }
101}
102
Sam McCall422c8282018-11-26 16:00:11 +0000103bool BackgroundIndex::blockUntilIdleForTest(
104 llvm::Optional<double> TimeoutSeconds) {
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000105 std::unique_lock<std::mutex> Lock(QueueMu);
Sam McCall422c8282018-11-26 16:00:11 +0000106 return wait(Lock, QueueCV, timeoutSeconds(TimeoutSeconds),
107 [&] { return Queue.empty() && NumActiveTasks == 0; });
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000108}
109
Sam McCall6e2d2a32018-11-26 09:51:50 +0000110void BackgroundIndex::enqueue(const std::vector<std::string> &ChangedFiles) {
111 enqueueTask([this, ChangedFiles] {
112 trace::Span Tracer("BackgroundIndexEnqueue");
113 // We're doing this asynchronously, because we'll read shards here too.
114 // FIXME: read shards here too.
115
116 log("Enqueueing {0} commands for indexing", ChangedFiles.size());
117 SPAN_ATTACH(Tracer, "files", int64_t(ChangedFiles.size()));
118
119 // We shuffle the files because processing them in a random order should
120 // quickly give us good coverage of headers in the project.
121 std::vector<unsigned> Permutation(ChangedFiles.size());
122 std::iota(Permutation.begin(), Permutation.end(), 0);
123 std::mt19937 Generator(std::random_device{}());
124 std::shuffle(Permutation.begin(), Permutation.end(), Generator);
125
126 for (const unsigned I : Permutation)
127 enqueue(ChangedFiles[I]);
128 });
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000129}
130
Sam McCall6e2d2a32018-11-26 09:51:50 +0000131void BackgroundIndex::enqueue(const std::string &File) {
132 ProjectInfo Project;
133 if (auto Cmd = CDB.getCompileCommand(File, &Project)) {
134 auto *Storage = IndexStorageFactory(Project.SourceRoot);
135 enqueueTask(Bind(
136 [this, File, Storage](tooling::CompileCommand Cmd) {
137 Cmd.CommandLine.push_back("-resource-dir=" + ResourceDir);
138 if (auto Error = index(std::move(Cmd), Storage))
139 log("Indexing {0} failed: {1}", File, std::move(Error));
140 },
141 std::move(*Cmd)));
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000142 }
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000143}
144
Sam McCall6e2d2a32018-11-26 09:51:50 +0000145void BackgroundIndex::enqueueTask(Task T) {
146 {
147 std::lock_guard<std::mutex> Lock(QueueMu);
148 Queue.push_back(std::move(T));
149 }
150 QueueCV.notify_all();
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000151}
152
Eric Liuad588af2018-11-06 10:55:21 +0000153// Resolves URI to file paths with cache.
154class URIToFileCache {
155public:
156 URIToFileCache(llvm::StringRef HintPath) : HintPath(HintPath) {}
157
158 llvm::StringRef resolve(llvm::StringRef FileURI) {
159 auto I = URIToPathCache.try_emplace(FileURI);
160 if (I.second) {
161 auto U = URI::parse(FileURI);
162 if (!U) {
163 elog("Failed to parse URI {0}: {1}", FileURI, U.takeError());
164 assert(false && "Failed to parse URI");
165 return "";
166 }
167 auto Path = URI::resolve(*U, HintPath);
168 if (!Path) {
169 elog("Failed to resolve URI {0}: {1}", FileURI, Path.takeError());
170 assert(false && "Failed to resolve URI");
171 return "";
172 }
173 I.first->second = *Path;
174 }
175 return I.first->second;
176 }
177
178private:
179 std::string HintPath;
180 llvm::StringMap<std::string> URIToPathCache;
181};
182
183/// Given index results from a TU, only update files in \p FilesToUpdate.
Kadir Cetinkayad08eab42018-11-27 16:08:53 +0000184void BackgroundIndex::update(StringRef MainFile, IndexFileIn Index,
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +0000185 const StringMap<FileDigest> &FilesToUpdate,
186 BackgroundIndexStorage *IndexStorage) {
Eric Liuad588af2018-11-06 10:55:21 +0000187 // Partition symbols/references into files.
188 struct File {
189 DenseSet<const Symbol *> Symbols;
190 DenseSet<const Ref *> Refs;
191 };
192 StringMap<File> Files;
193 URIToFileCache URICache(MainFile);
Kadir Cetinkayad08eab42018-11-27 16:08:53 +0000194 for (const auto &Sym : *Index.Symbols) {
Eric Liuad588af2018-11-06 10:55:21 +0000195 if (Sym.CanonicalDeclaration) {
196 auto DeclPath = URICache.resolve(Sym.CanonicalDeclaration.FileURI);
197 if (FilesToUpdate.count(DeclPath) != 0)
198 Files[DeclPath].Symbols.insert(&Sym);
199 }
200 // For symbols with different declaration and definition locations, we store
201 // the full symbol in both the header file and the implementation file, so
202 // that merging can tell the preferred symbols (from canonical headers) from
203 // other symbols (e.g. forward declarations).
204 if (Sym.Definition &&
205 Sym.Definition.FileURI != Sym.CanonicalDeclaration.FileURI) {
206 auto DefPath = URICache.resolve(Sym.Definition.FileURI);
207 if (FilesToUpdate.count(DefPath) != 0)
208 Files[DefPath].Symbols.insert(&Sym);
209 }
210 }
211 DenseMap<const Ref *, SymbolID> RefToIDs;
Kadir Cetinkayad08eab42018-11-27 16:08:53 +0000212 for (const auto &SymRefs : *Index.Refs) {
Eric Liuad588af2018-11-06 10:55:21 +0000213 for (const auto &R : SymRefs.second) {
214 auto Path = URICache.resolve(R.Location.FileURI);
215 if (FilesToUpdate.count(Path) != 0) {
216 auto &F = Files[Path];
217 RefToIDs[&R] = SymRefs.first;
218 F.Refs.insert(&R);
219 }
220 }
221 }
222
223 // Build and store new slabs for each updated file.
224 for (const auto &F : Files) {
225 StringRef Path = F.first();
226 vlog("Update symbols in {0}", Path);
227 SymbolSlab::Builder Syms;
228 RefSlab::Builder Refs;
229 for (const auto *S : F.second.Symbols)
230 Syms.insert(*S);
231 for (const auto *R : F.second.Refs)
232 Refs.insert(RefToIDs[R], *R);
233
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +0000234 auto SS = llvm::make_unique<SymbolSlab>(std::move(Syms).build());
235 auto RS = llvm::make_unique<RefSlab>(std::move(Refs).build());
236
237 auto Hash = FilesToUpdate.lookup(Path);
238 // We need to store shards before updating the index, since the latter
239 // consumes slabs.
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +0000240 if (IndexStorage) {
241 IndexFileOut Shard;
242 Shard.Symbols = SS.get();
243 Shard.Refs = RS.get();
Kadir Cetinkayad08eab42018-11-27 16:08:53 +0000244
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +0000245 if (auto Error = IndexStorage->storeShard(Path, Shard))
246 elog("Failed to write background-index shard for file {0}: {1}", Path,
247 std::move(Error));
248 }
249
Eric Liuad588af2018-11-06 10:55:21 +0000250 std::lock_guard<std::mutex> Lock(DigestsMu);
251 // This can override a newer version that is added in another thread,
252 // if this thread sees the older version but finishes later. This should be
253 // rare in practice.
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +0000254 IndexedFileDigests[Path] = Hash;
255 IndexedSymbols.update(Path, std::move(SS), std::move(RS));
Eric Liuad588af2018-11-06 10:55:21 +0000256 }
257}
258
259// Creates a filter to not collect index results from files with unchanged
260// digests.
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +0000261// \p FileDigests contains file digests for the current indexed files, and all
262// changed files will be added to \p FilesToUpdate.
Eric Liuad588af2018-11-06 10:55:21 +0000263decltype(SymbolCollector::Options::FileFilter) createFileFilter(
Kadir Cetinkayad08eab42018-11-27 16:08:53 +0000264 const llvm::StringMap<FileDigest> &FileDigests,
265 llvm::StringMap<FileDigest> &FilesToUpdate) {
Eric Liuad588af2018-11-06 10:55:21 +0000266 return [&FileDigests, &FilesToUpdate](const SourceManager &SM, FileID FID) {
267 StringRef Path;
268 if (const auto *F = SM.getFileEntryForID(FID))
269 Path = F->getName();
270 if (Path.empty())
271 return false; // Skip invalid files.
272 SmallString<128> AbsPath(Path);
273 if (std::error_code EC =
274 SM.getFileManager().getVirtualFileSystem()->makeAbsolute(AbsPath)) {
275 elog("Warning: could not make absolute file: {0}", EC.message());
276 return false; // Skip files without absolute path.
277 }
278 sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true);
279 auto Digest = digestFile(SM, FID);
280 if (!Digest)
281 return false;
282 auto D = FileDigests.find(AbsPath);
283 if (D != FileDigests.end() && D->second == Digest)
284 return false; // Skip files that haven't changed.
285
286 FilesToUpdate[AbsPath] = *Digest;
287 return true;
288 };
289}
290
Kadir Cetinkaya06553bf2018-11-16 09:03:56 +0000291Error BackgroundIndex::index(tooling::CompileCommand Cmd,
292 BackgroundIndexStorage *IndexStorage) {
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000293 trace::Span Tracer("BackgroundIndex");
294 SPAN_ATTACH(Tracer, "file", Cmd.Filename);
Kadir Cetinkayaed18e782018-11-15 10:34:39 +0000295 SmallString<128> AbsolutePath;
296 if (sys::path::is_absolute(Cmd.Filename)) {
297 AbsolutePath = Cmd.Filename;
298 } else {
299 AbsolutePath = Cmd.Directory;
300 sys::path::append(AbsolutePath, Cmd.Filename);
301 }
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000302
303 auto FS = FSProvider.getFileSystem();
304 auto Buf = FS->getBufferForFile(AbsolutePath);
305 if (!Buf)
306 return errorCodeToError(Buf.getError());
Eric Liuad588af2018-11-06 10:55:21 +0000307 auto Hash = digest(Buf->get()->getBuffer());
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000308
Eric Liuad588af2018-11-06 10:55:21 +0000309 // Take a snapshot of the digests to avoid locking for each file in the TU.
310 llvm::StringMap<FileDigest> DigestsSnapshot;
311 {
312 std::lock_guard<std::mutex> Lock(DigestsMu);
313 if (IndexedFileDigests.lookup(AbsolutePath) == Hash) {
314 vlog("No need to index {0}, already up to date", AbsolutePath);
315 return Error::success();
316 }
317
318 DigestsSnapshot = IndexedFileDigests;
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000319 }
320
321 log("Indexing {0}", Cmd.Filename, toHex(Hash));
322 ParseInputs Inputs;
323 Inputs.FS = std::move(FS);
324 Inputs.FS->setCurrentWorkingDirectory(Cmd.Directory);
325 Inputs.CompileCommand = std::move(Cmd);
326 auto CI = buildCompilerInvocation(Inputs);
327 if (!CI)
Sam McCallc008af62018-10-20 15:30:37 +0000328 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000329 "Couldn't build compiler invocation");
330 IgnoreDiagnostics IgnoreDiags;
331 auto Clang = prepareCompilerInstance(
332 std::move(CI), /*Preamble=*/nullptr, std::move(*Buf),
333 std::make_shared<PCHContainerOperations>(), Inputs.FS, IgnoreDiags);
334 if (!Clang)
Sam McCallc008af62018-10-20 15:30:37 +0000335 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000336 "Couldn't build compiler instance");
337
338 SymbolCollector::Options IndexOpts;
Eric Liuad588af2018-11-06 10:55:21 +0000339 StringMap<FileDigest> FilesToUpdate;
340 IndexOpts.FileFilter = createFileFilter(DigestsSnapshot, FilesToUpdate);
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000341 SymbolSlab Symbols;
342 RefSlab Refs;
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000343 auto Action = createStaticIndexingAction(
344 IndexOpts, [&](SymbolSlab S) { Symbols = std::move(S); },
345 [&](RefSlab R) { Refs = std::move(R); });
346
347 // We're going to run clang here, and it could potentially crash.
348 // We could use CrashRecoveryContext to try to make indexing crashes nonfatal,
349 // but the leaky "recovery" is pretty scary too in a long-running process.
350 // If crashes are a real problem, maybe we should fork a child process.
351
352 const FrontendInputFile &Input = Clang->getFrontendOpts().Inputs.front();
353 if (!Action->BeginSourceFile(*Clang, Input))
Sam McCallc008af62018-10-20 15:30:37 +0000354 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000355 "BeginSourceFile() failed");
356 if (!Action->Execute())
Sam McCallc008af62018-10-20 15:30:37 +0000357 return createStringError(inconvertibleErrorCode(), "Execute() failed");
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000358 Action->EndSourceFile();
359
360 log("Indexed {0} ({1} symbols, {2} refs)", Inputs.CompileCommand.Filename,
Haojian Wu6ece6e72018-10-18 15:33:20 +0000361 Symbols.size(), Refs.numRefs());
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000362 SPAN_ATTACH(Tracer, "symbols", int(Symbols.size()));
Haojian Wu6ece6e72018-10-18 15:33:20 +0000363 SPAN_ATTACH(Tracer, "refs", int(Refs.numRefs()));
Kadir Cetinkayad08eab42018-11-27 16:08:53 +0000364 IndexFileIn Index;
365 Index.Symbols = std::move(Symbols);
366 Index.Refs = std::move(Refs);
367
368 update(AbsolutePath, std::move(Index), FilesToUpdate, IndexStorage);
Eric Liuad588af2018-11-06 10:55:21 +0000369 {
370 // Make sure hash for the main file is always updated even if there is no
371 // index data in it.
372 std::lock_guard<std::mutex> Lock(DigestsMu);
373 IndexedFileDigests[AbsolutePath] = Hash;
374 }
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000375
376 // FIXME: this should rebuild once-in-a-while, not after every file.
377 // At that point we should use Dex, too.
378 vlog("Rebuilding automatic index");
Eric Liuc0ac4bb2018-11-22 15:02:05 +0000379 reset(IndexedSymbols.buildIndex(IndexType::Light, DuplicateHandling::Merge));
Eric Liuad588af2018-11-06 10:55:21 +0000380
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000381 return Error::success();
382}
383
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000384} // namespace clangd
385} // namespace clang