blob: 8f40075cacc5efeff1241d4354de9977eb3ec484 [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"
14#include "Trace.h"
15#include "index/IndexAction.h"
16#include "index/MemIndex.h"
17#include "index/Serialization.h"
18#include "llvm/Support/SHA1.h"
19#include <random>
20
21using namespace llvm;
22namespace clang {
23namespace clangd {
24
25BackgroundIndex::BackgroundIndex(Context BackgroundContext,
26 StringRef ResourceDir,
27 const FileSystemProvider &FSProvider)
Sam McCallc008af62018-10-20 15:30:37 +000028 : SwapIndex(make_unique<MemIndex>()), ResourceDir(ResourceDir),
Sam McCall8dc9dbb2018-10-15 13:34:10 +000029 FSProvider(FSProvider), BackgroundContext(std::move(BackgroundContext)),
30 Thread([this] { run(); }) {}
31
32BackgroundIndex::~BackgroundIndex() {
33 stop();
34 Thread.join();
35}
36
37void BackgroundIndex::stop() {
38 {
39 std::lock_guard<std::mutex> Lock(QueueMu);
40 ShouldStop = true;
41 }
42 QueueCV.notify_all();
43}
44
45void BackgroundIndex::run() {
46 WithContext Background(std::move(BackgroundContext));
47 while (true) {
Sam McCallc008af62018-10-20 15:30:37 +000048 Optional<Task> Task;
Sam McCall8dc9dbb2018-10-15 13:34:10 +000049 {
50 std::unique_lock<std::mutex> Lock(QueueMu);
51 QueueCV.wait(Lock, [&] { return ShouldStop || !Queue.empty(); });
52 if (ShouldStop) {
53 Queue.clear();
54 QueueCV.notify_all();
55 return;
56 }
57 ++NumActiveTasks;
58 Task = std::move(Queue.front());
59 Queue.pop_front();
60 }
61 (*Task)();
62 {
63 std::unique_lock<std::mutex> Lock(QueueMu);
64 assert(NumActiveTasks > 0 && "before decrementing");
65 --NumActiveTasks;
66 }
67 QueueCV.notify_all();
68 }
69}
70
71void BackgroundIndex::blockUntilIdleForTest() {
72 std::unique_lock<std::mutex> Lock(QueueMu);
73 QueueCV.wait(Lock, [&] { return Queue.empty() && NumActiveTasks == 0; });
74}
75
76void BackgroundIndex::enqueue(StringRef Directory,
77 tooling::CompileCommand Cmd) {
Sam McCallbca624a2018-10-16 09:05:13 +000078 {
79 std::lock_guard<std::mutex> Lock(QueueMu);
80 enqueueLocked(std::move(Cmd));
81 }
82 QueueCV.notify_all();
Sam McCall8dc9dbb2018-10-15 13:34:10 +000083}
84
85void BackgroundIndex::enqueueAll(StringRef Directory,
86 const tooling::CompilationDatabase &CDB) {
87 trace::Span Tracer("BackgroundIndexEnqueueCDB");
88 // FIXME: this function may be slow. Perhaps enqueue a task to re-read the CDB
89 // from disk and enqueue the commands asynchronously?
90 auto Cmds = CDB.getAllCompileCommands();
91 SPAN_ATTACH(Tracer, "commands", int64_t(Cmds.size()));
92 std::mt19937 Generator(std::random_device{}());
93 std::shuffle(Cmds.begin(), Cmds.end(), Generator);
94 log("Enqueueing {0} commands for indexing from {1}", Cmds.size(), Directory);
95 {
96 std::lock_guard<std::mutex> Lock(QueueMu);
97 for (auto &Cmd : Cmds)
98 enqueueLocked(std::move(Cmd));
99 }
100 QueueCV.notify_all();
101}
102
103void BackgroundIndex::enqueueLocked(tooling::CompileCommand Cmd) {
104 Queue.push_back(Bind(
105 [this](tooling::CompileCommand Cmd) {
106 std::string Filename = Cmd.Filename;
107 Cmd.CommandLine.push_back("-resource-dir=" + ResourceDir);
108 if (auto Error = index(std::move(Cmd)))
109 log("Indexing {0} failed: {1}", Filename, std::move(Error));
110 },
111 std::move(Cmd)));
112}
113
Sam McCallc008af62018-10-20 15:30:37 +0000114Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000115 trace::Span Tracer("BackgroundIndex");
116 SPAN_ATTACH(Tracer, "file", Cmd.Filename);
117 SmallString<128> AbsolutePath;
Sam McCallc008af62018-10-20 15:30:37 +0000118 if (sys::path::is_absolute(Cmd.Filename)) {
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000119 AbsolutePath = Cmd.Filename;
120 } else {
121 AbsolutePath = Cmd.Directory;
Sam McCallc008af62018-10-20 15:30:37 +0000122 sys::path::append(AbsolutePath, Cmd.Filename);
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000123 }
124
125 auto FS = FSProvider.getFileSystem();
126 auto Buf = FS->getBufferForFile(AbsolutePath);
127 if (!Buf)
128 return errorCodeToError(Buf.getError());
129 StringRef Contents = Buf->get()->getBuffer();
130 auto Hash = SHA1::hash({(const uint8_t *)Contents.data(), Contents.size()});
131
132 if (FileHash.lookup(AbsolutePath) == Hash) {
133 vlog("No need to index {0}, already up to date", AbsolutePath);
134 return Error::success();
135 }
136
137 log("Indexing {0}", Cmd.Filename, toHex(Hash));
138 ParseInputs Inputs;
139 Inputs.FS = std::move(FS);
140 Inputs.FS->setCurrentWorkingDirectory(Cmd.Directory);
141 Inputs.CompileCommand = std::move(Cmd);
142 auto CI = buildCompilerInvocation(Inputs);
143 if (!CI)
Sam McCallc008af62018-10-20 15:30:37 +0000144 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000145 "Couldn't build compiler invocation");
146 IgnoreDiagnostics IgnoreDiags;
147 auto Clang = prepareCompilerInstance(
148 std::move(CI), /*Preamble=*/nullptr, std::move(*Buf),
149 std::make_shared<PCHContainerOperations>(), Inputs.FS, IgnoreDiags);
150 if (!Clang)
Sam McCallc008af62018-10-20 15:30:37 +0000151 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000152 "Couldn't build compiler instance");
153
154 SymbolCollector::Options IndexOpts;
155 SymbolSlab Symbols;
156 RefSlab Refs;
157 IndexFileIn IndexData;
158 auto Action = createStaticIndexingAction(
159 IndexOpts, [&](SymbolSlab S) { Symbols = std::move(S); },
160 [&](RefSlab R) { Refs = std::move(R); });
161
162 // We're going to run clang here, and it could potentially crash.
163 // We could use CrashRecoveryContext to try to make indexing crashes nonfatal,
164 // but the leaky "recovery" is pretty scary too in a long-running process.
165 // If crashes are a real problem, maybe we should fork a child process.
166
167 const FrontendInputFile &Input = Clang->getFrontendOpts().Inputs.front();
168 if (!Action->BeginSourceFile(*Clang, Input))
Sam McCallc008af62018-10-20 15:30:37 +0000169 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000170 "BeginSourceFile() failed");
171 if (!Action->Execute())
Sam McCallc008af62018-10-20 15:30:37 +0000172 return createStringError(inconvertibleErrorCode(), "Execute() failed");
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000173 Action->EndSourceFile();
174
175 log("Indexed {0} ({1} symbols, {2} refs)", Inputs.CompileCommand.Filename,
Haojian Wu6ece6e72018-10-18 15:33:20 +0000176 Symbols.size(), Refs.numRefs());
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000177 SPAN_ATTACH(Tracer, "symbols", int(Symbols.size()));
Haojian Wu6ece6e72018-10-18 15:33:20 +0000178 SPAN_ATTACH(Tracer, "refs", int(Refs.numRefs()));
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000179 // FIXME: partition the symbols by file rather than TU, to avoid duplication.
180 IndexedSymbols.update(AbsolutePath,
181 llvm::make_unique<SymbolSlab>(std::move(Symbols)),
182 llvm::make_unique<RefSlab>(std::move(Refs)));
183 FileHash[AbsolutePath] = Hash;
184
185 // FIXME: this should rebuild once-in-a-while, not after every file.
186 // At that point we should use Dex, too.
187 vlog("Rebuilding automatic index");
Sam McCall96f24892018-10-16 08:53:52 +0000188 reset(IndexedSymbols.buildIndex(IndexType::Light));
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000189 return Error::success();
190}
191
192} // namespace clangd
193} // namespace clang