blob: 7d9cd711fe72658458ecb35ce9f26c6d11e49ea5 [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 Cetinkaya6675be82018-10-30 12:13:27 +000014#include "Threading.h"
Sam McCall8dc9dbb2018-10-15 13:34:10 +000015#include "Trace.h"
16#include "index/IndexAction.h"
17#include "index/MemIndex.h"
18#include "index/Serialization.h"
19#include "llvm/Support/SHA1.h"
20#include <random>
21
22using namespace llvm;
23namespace clang {
24namespace clangd {
25
26BackgroundIndex::BackgroundIndex(Context BackgroundContext,
27 StringRef ResourceDir,
Eric Liu0b70a872018-10-22 15:37:58 +000028 const FileSystemProvider &FSProvider,
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000029 ArrayRef<std::string> URISchemes,
30 size_t ThreadPoolSize)
Sam McCallc008af62018-10-20 15:30:37 +000031 : SwapIndex(make_unique<MemIndex>()), ResourceDir(ResourceDir),
Sam McCall8dc9dbb2018-10-15 13:34:10 +000032 FSProvider(FSProvider), BackgroundContext(std::move(BackgroundContext)),
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000033 URISchemes(URISchemes) {
34 assert(ThreadPoolSize > 0 && "Thread pool size can't be zero.");
35 while (ThreadPoolSize--) {
36 ThreadPool.emplace_back([this] { run(); });
37 // Set priority to low, since background indexing is a long running task we
38 // do not want to eat up cpu when there are any other high priority threads.
39 // FIXME: In the future we might want a more general way of handling this to
40 // support a tasks with various priorities.
41 setThreadPriority(ThreadPool.back(), ThreadPriority::Low);
42 }
43}
Sam McCall8dc9dbb2018-10-15 13:34:10 +000044
45BackgroundIndex::~BackgroundIndex() {
46 stop();
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000047 for (auto &Thread : ThreadPool)
48 Thread.join();
Sam McCall8dc9dbb2018-10-15 13:34:10 +000049}
50
51void BackgroundIndex::stop() {
52 {
53 std::lock_guard<std::mutex> Lock(QueueMu);
54 ShouldStop = true;
55 }
56 QueueCV.notify_all();
57}
58
59void BackgroundIndex::run() {
Kadir Cetinkaya6675be82018-10-30 12:13:27 +000060 WithContext Background(BackgroundContext.clone());
Sam McCall8dc9dbb2018-10-15 13:34:10 +000061 while (true) {
Sam McCallc008af62018-10-20 15:30:37 +000062 Optional<Task> Task;
Sam McCall8dc9dbb2018-10-15 13:34:10 +000063 {
64 std::unique_lock<std::mutex> Lock(QueueMu);
65 QueueCV.wait(Lock, [&] { return ShouldStop || !Queue.empty(); });
66 if (ShouldStop) {
67 Queue.clear();
68 QueueCV.notify_all();
69 return;
70 }
71 ++NumActiveTasks;
72 Task = std::move(Queue.front());
73 Queue.pop_front();
74 }
75 (*Task)();
76 {
77 std::unique_lock<std::mutex> Lock(QueueMu);
78 assert(NumActiveTasks > 0 && "before decrementing");
79 --NumActiveTasks;
80 }
81 QueueCV.notify_all();
82 }
83}
84
85void BackgroundIndex::blockUntilIdleForTest() {
86 std::unique_lock<std::mutex> Lock(QueueMu);
87 QueueCV.wait(Lock, [&] { return Queue.empty() && NumActiveTasks == 0; });
88}
89
90void BackgroundIndex::enqueue(StringRef Directory,
91 tooling::CompileCommand Cmd) {
Sam McCallbca624a2018-10-16 09:05:13 +000092 {
93 std::lock_guard<std::mutex> Lock(QueueMu);
94 enqueueLocked(std::move(Cmd));
95 }
96 QueueCV.notify_all();
Sam McCall8dc9dbb2018-10-15 13:34:10 +000097}
98
99void BackgroundIndex::enqueueAll(StringRef Directory,
100 const tooling::CompilationDatabase &CDB) {
101 trace::Span Tracer("BackgroundIndexEnqueueCDB");
102 // FIXME: this function may be slow. Perhaps enqueue a task to re-read the CDB
103 // from disk and enqueue the commands asynchronously?
104 auto Cmds = CDB.getAllCompileCommands();
105 SPAN_ATTACH(Tracer, "commands", int64_t(Cmds.size()));
106 std::mt19937 Generator(std::random_device{}());
107 std::shuffle(Cmds.begin(), Cmds.end(), Generator);
108 log("Enqueueing {0} commands for indexing from {1}", Cmds.size(), Directory);
109 {
110 std::lock_guard<std::mutex> Lock(QueueMu);
111 for (auto &Cmd : Cmds)
112 enqueueLocked(std::move(Cmd));
113 }
114 QueueCV.notify_all();
115}
116
117void BackgroundIndex::enqueueLocked(tooling::CompileCommand Cmd) {
118 Queue.push_back(Bind(
119 [this](tooling::CompileCommand Cmd) {
120 std::string Filename = Cmd.Filename;
121 Cmd.CommandLine.push_back("-resource-dir=" + ResourceDir);
122 if (auto Error = index(std::move(Cmd)))
123 log("Indexing {0} failed: {1}", Filename, std::move(Error));
124 },
125 std::move(Cmd)));
126}
127
Sam McCallc008af62018-10-20 15:30:37 +0000128Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000129 trace::Span Tracer("BackgroundIndex");
130 SPAN_ATTACH(Tracer, "file", Cmd.Filename);
131 SmallString<128> AbsolutePath;
Sam McCallc008af62018-10-20 15:30:37 +0000132 if (sys::path::is_absolute(Cmd.Filename)) {
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000133 AbsolutePath = Cmd.Filename;
134 } else {
135 AbsolutePath = Cmd.Directory;
Sam McCallc008af62018-10-20 15:30:37 +0000136 sys::path::append(AbsolutePath, Cmd.Filename);
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000137 }
138
139 auto FS = FSProvider.getFileSystem();
140 auto Buf = FS->getBufferForFile(AbsolutePath);
141 if (!Buf)
142 return errorCodeToError(Buf.getError());
143 StringRef Contents = Buf->get()->getBuffer();
144 auto Hash = SHA1::hash({(const uint8_t *)Contents.data(), Contents.size()});
145
146 if (FileHash.lookup(AbsolutePath) == Hash) {
147 vlog("No need to index {0}, already up to date", AbsolutePath);
148 return Error::success();
149 }
150
151 log("Indexing {0}", Cmd.Filename, toHex(Hash));
152 ParseInputs Inputs;
153 Inputs.FS = std::move(FS);
154 Inputs.FS->setCurrentWorkingDirectory(Cmd.Directory);
155 Inputs.CompileCommand = std::move(Cmd);
156 auto CI = buildCompilerInvocation(Inputs);
157 if (!CI)
Sam McCallc008af62018-10-20 15:30:37 +0000158 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000159 "Couldn't build compiler invocation");
160 IgnoreDiagnostics IgnoreDiags;
161 auto Clang = prepareCompilerInstance(
162 std::move(CI), /*Preamble=*/nullptr, std::move(*Buf),
163 std::make_shared<PCHContainerOperations>(), Inputs.FS, IgnoreDiags);
164 if (!Clang)
Sam McCallc008af62018-10-20 15:30:37 +0000165 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000166 "Couldn't build compiler instance");
167
168 SymbolCollector::Options IndexOpts;
169 SymbolSlab Symbols;
170 RefSlab Refs;
171 IndexFileIn IndexData;
172 auto Action = createStaticIndexingAction(
173 IndexOpts, [&](SymbolSlab S) { Symbols = std::move(S); },
174 [&](RefSlab R) { Refs = std::move(R); });
175
176 // We're going to run clang here, and it could potentially crash.
177 // We could use CrashRecoveryContext to try to make indexing crashes nonfatal,
178 // but the leaky "recovery" is pretty scary too in a long-running process.
179 // If crashes are a real problem, maybe we should fork a child process.
180
181 const FrontendInputFile &Input = Clang->getFrontendOpts().Inputs.front();
182 if (!Action->BeginSourceFile(*Clang, Input))
Sam McCallc008af62018-10-20 15:30:37 +0000183 return createStringError(inconvertibleErrorCode(),
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000184 "BeginSourceFile() failed");
185 if (!Action->Execute())
Sam McCallc008af62018-10-20 15:30:37 +0000186 return createStringError(inconvertibleErrorCode(), "Execute() failed");
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000187 Action->EndSourceFile();
188
189 log("Indexed {0} ({1} symbols, {2} refs)", Inputs.CompileCommand.Filename,
Haojian Wu6ece6e72018-10-18 15:33:20 +0000190 Symbols.size(), Refs.numRefs());
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000191 SPAN_ATTACH(Tracer, "symbols", int(Symbols.size()));
Haojian Wu6ece6e72018-10-18 15:33:20 +0000192 SPAN_ATTACH(Tracer, "refs", int(Refs.numRefs()));
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000193 // FIXME: partition the symbols by file rather than TU, to avoid duplication.
194 IndexedSymbols.update(AbsolutePath,
195 llvm::make_unique<SymbolSlab>(std::move(Symbols)),
196 llvm::make_unique<RefSlab>(std::move(Refs)));
197 FileHash[AbsolutePath] = Hash;
198
199 // FIXME: this should rebuild once-in-a-while, not after every file.
200 // At that point we should use Dex, too.
201 vlog("Rebuilding automatic index");
Eric Liu0b70a872018-10-22 15:37:58 +0000202 reset(IndexedSymbols.buildIndex(IndexType::Light, URISchemes));
Sam McCall8dc9dbb2018-10-15 13:34:10 +0000203 return Error::success();
204}
205
206} // namespace clangd
207} // namespace clang