31#include "clang/Basic/SourceLocation.h"
32#include "clang/Basic/SourceManager.h"
33#include "clang/Basic/Stack.h"
34#include "clang/Frontend/FrontendAction.h"
35#include "llvm/ADT/ArrayRef.h"
36#include "llvm/ADT/DenseSet.h"
37#include "llvm/ADT/STLExtras.h"
38#include "llvm/ADT/StringMap.h"
39#include "llvm/ADT/StringRef.h"
40#include "llvm/Support/Error.h"
41#include "llvm/Support/Path.h"
42#include "llvm/Support/Threading.h"
43#include "llvm/Support/xxhash.h"
48#include <condition_variable>
68llvm::SmallString<128> getAbsolutePath(
const tooling::CompileCommand &Cmd) {
69 llvm::SmallString<128> AbsolutePath;
70 if (llvm::sys::path::is_absolute(Cmd.Filename)) {
71 AbsolutePath = Cmd.Filename;
73 AbsolutePath = Cmd.Directory;
74 llvm::sys::path::append(AbsolutePath, Cmd.Filename);
75 llvm::sys::path::remove_dots(AbsolutePath,
true);
80bool shardIsStale(
const LoadedShard &LS, llvm::vfs::FileSystem *FS) {
81 auto Buf = FS->getBufferForFile(LS.AbsolutePath);
83 vlog(
"Background-index: Couldn't read {0} to validate stored index: {1}",
84 LS.AbsolutePath, Buf.getError().message());
88 return digest(Buf->get()->getBuffer()) != LS.Digest;
97 IndexingPriority(Opts.IndexingPriority),
98 ContextProvider(std::move(Opts.ContextProvider)),
100 Rebuilder(this, &IndexedSymbols, Opts.ThreadPoolSize),
101 IndexStorageFactory(std::move(IndexStorageFactory)),
102 Queue(std::move(Opts.OnProgress)),
104 CDB.watch([&](const std::vector<std::string> &ChangedFiles) {
107 assert(Opts.ThreadPoolSize > 0 &&
"Thread pool size can't be zero.");
108 assert(this->IndexStorageFactory &&
"Storage factory can not be null!");
109 for (
unsigned I = 0; I < Opts.ThreadPoolSize; ++I) {
110 ThreadPool.
runAsync(
"background-worker-" + llvm::Twine(I + 1),
112 clang::noteBottomOfStack();
114 Queue.
work([&] { Rebuilder.
idle(); });
125 const std::vector<std::string> &ChangedFiles) {
129 std::optional<WithContext> WithProvidedContext;
131 WithProvidedContext.emplace(ContextProvider(
""));
134 log(
"Enqueueing {0} commands for indexing", ChangedFiles.size());
135 SPAN_ATTACH(Tracer,
"files", int64_t(ChangedFiles.size()));
137 auto NeedsReIndexing = loadProject(std::move(ChangedFiles));
139 std::shuffle(NeedsReIndexing.begin(), NeedsReIndexing.end(),
140 std::mt19937(std::random_device{}()));
141 std::vector<BackgroundQueue::Task> Tasks;
142 Tasks.reserve(NeedsReIndexing.size());
143 for (
const auto &
File : NeedsReIndexing)
144 Tasks.push_back(indexFileTask(std::move(
File)));
145 Queue.
append(std::move(Tasks));
148 T.QueuePri = LoadShards;
149 T.ThreadPri = llvm::ThreadPriority::Default;
154 Path = llvm::sys::path::filename(
Path);
155 return Path.drop_back(llvm::sys::path::extension(
Path).size());
158BackgroundQueue::Task BackgroundIndex::indexFileTask(std::string
Path) {
160 uint64_t Key = llvm::xxh3_64bits(
Path);
161 BackgroundQueue::Task T([
this,
Path(std::move(
Path))] {
162 std::optional<WithContext> WithProvidedContext;
164 WithProvidedContext.emplace(ContextProvider(
Path));
168 if (
auto Error = index(std::move(*Cmd)))
169 elog(
"Indexing {0} failed: {1}",
Path, std::move(
Error));
171 T.QueuePri = IndexFile;
172 T.ThreadPri = IndexingPriority;
173 T.Tag = std::move(
Tag);
186void BackgroundIndex::update(
188 const llvm::StringMap<ShardVersion> &ShardVersionsSnapshot,
191 llvm::StringMap<std::pair<Path, FileDigest>> FilesToUpdate;
194 for (
const auto &IndexIt : *Index.Sources) {
195 const auto &IGN = IndexIt.getValue();
198 elog(
"Failed to resolve URI: {0}", AbsPath.takeError());
201 const auto DigestIt = ShardVersionsSnapshot.find(*AbsPath);
203 if (DigestIt == ShardVersionsSnapshot.end() ||
204 DigestIt->getValue().Digest != IGN.Digest ||
205 (DigestIt->getValue().HadErrors && !HadErrors))
206 FilesToUpdate[IGN.URI] = {std::move(*AbsPath), IGN.Digest};
210 FileShardedIndex ShardedIndex(std::move(Index));
213 for (
const auto &FileIt : FilesToUpdate) {
214 auto Uri = FileIt.first();
215 auto IF = ShardedIndex.getShard(Uri);
216 assert(IF &&
"no shard for file in Index.Sources?");
227 if (
auto Error = IndexStorageFactory(
Path)->storeShard(
Path, *IF))
228 elog(
"Failed to write background-index shard for file {0}: {1}",
Path,
232 std::lock_guard<std::mutex> Lock(ShardVersionsMu);
233 const auto &Hash = FileIt.getValue().second;
234 auto DigestIt = ShardVersions.try_emplace(
Path);
235 ShardVersion &SV = DigestIt.first->second;
238 if (!DigestIt.second && SV.Digest == Hash && SV.HadErrors && !HadErrors)
241 SV.HadErrors = HadErrors;
247 Uri, std::make_unique<SymbolSlab>(std::move(*IF->Symbols)),
248 std::make_unique<RefSlab>(std::move(*IF->Refs)),
249 std::make_unique<RelationSlab>(std::move(*IF->Relations)),
255llvm::Error BackgroundIndex::index(tooling::CompileCommand Cmd) {
256 trace::Span Tracer(
"BackgroundIndex");
258 auto AbsolutePath = getAbsolutePath(Cmd);
260 auto FS = TFS.
view(Cmd.Directory);
261 auto Buf = FS->getBufferForFile(AbsolutePath);
263 return llvm::errorCodeToError(Buf.getError());
264 auto Hash =
digest(Buf->get()->getBuffer());
267 llvm::StringMap<ShardVersion> ShardVersionsSnapshot;
269 std::lock_guard<std::mutex> Lock(ShardVersionsMu);
270 ShardVersionsSnapshot = ShardVersions;
273 vlog(
"Indexing {0} (digest:={1})", Cmd.Filename, llvm::toHex(Hash));
276 Inputs.CompileCommand = std::move(Cmd);
280 return error(
"Couldn't build compiler invocation");
286 return error(
"Couldn't build compiler instance");
288 SymbolCollector::Options IndexOpts;
291 IndexOpts.FileFilter = [&ShardVersionsSnapshot](
const SourceManager &SM,
293 const auto F = SM.getFileEntryRefForID(FID);
302 auto D = ShardVersionsSnapshot.find(*AbsPath);
303 if (D != ShardVersionsSnapshot.end() && D->second.Digest == Digest &&
304 !D->second.HadErrors)
308 IndexOpts.CollectMainFileRefs =
true;
312 IndexOpts, [&](SymbolSlab S) { Index.Symbols = std::move(S); },
313 [&](RefSlab R) { Index.Refs = std::move(R); },
314 [&](RelationSlab R) { Index.Relations = std::move(R); },
315 [&](
IncludeGraph IG) { Index.Sources = std::move(IG); });
322 const FrontendInputFile &Input =
Clang->getFrontendOpts().Inputs.front();
324 return error(
"BeginSourceFile() failed");
325 if (llvm::Error Err =
Action->Execute())
330 Index.Cmd = Inputs.CompileCommand;
331 assert(Index.Symbols && Index.Refs && Index.Sources &&
332 "Symbols, Refs and Sources must be set.");
334 log(
"Indexed {0} ({1} symbols, {2} refs, {3} files)",
335 Inputs.CompileCommand.Filename, Index.Symbols->size(),
336 Index.Refs->numRefs(), Index.Sources->size());
337 SPAN_ATTACH(Tracer,
"symbols",
int(Index.Symbols->size()));
338 SPAN_ATTACH(Tracer,
"refs",
int(Index.Refs->numRefs()));
339 SPAN_ATTACH(Tracer,
"sources",
int(Index.Sources->size()));
341 bool HadErrors =
Clang->hasDiagnostics() &&
342 Clang->getDiagnostics().hasUncompilableErrorOccurred();
344 log(
"Failed to compile {0}, index may be incomplete", AbsolutePath);
345 for (
auto &It : *Index.Sources)
348 update(AbsolutePath, std::move(Index), ShardVersionsSnapshot, HadErrors);
351 return llvm::Error::success();
357std::vector<std::string>
358BackgroundIndex::loadProject(std::vector<std::string> MainFiles) {
361 llvm::erase_if(MainFiles, [&](
const std::string &TU) {
363 WithContext WithProvidedContext(ContextProvider(TU));
369 const std::vector<LoadedShard> Result =
371 size_t LoadedShards = 0;
374 std::lock_guard<std::mutex> Lock(ShardVersionsMu);
375 for (
auto &LS : Result) {
380 ? std::make_unique<SymbolSlab>(std::move(*LS.Shard->Symbols))
382 auto RS = LS.Shard->Refs
383 ? std::make_unique<RefSlab>(std::move(*LS.Shard->Refs))
387 ? std::make_unique<RelationSlab>(std::move(*LS.Shard->Relations))
389 ShardVersion &SV = ShardVersions[LS.AbsolutePath];
390 SV.Digest = LS.Digest;
391 SV.HadErrors = LS.HadErrors;
395 std::move(SS), std::move(RS), std::move(RelS),
402 auto FS = TFS.
view(std::nullopt);
403 llvm::DenseSet<PathRef> TUsToIndex;
406 for (
auto &LS : Result) {
407 if (!shardIsStale(LS, FS.get()))
409 PathRef TUForFile = LS.DependentTU;
410 assert(!TUForFile.empty() &&
"File without a TU!");
417 TUsToIndex.insert(TUForFile);
420 return {TUsToIndex.begin(), TUsToIndex.end()};
std::unique_ptr< CompilerInvocation > CI
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
void runAsync(const llvm::Twine &Name, llvm::unique_function< void()> Action)
void loadedShard(size_t ShardCount)
llvm::unique_function< BackgroundIndexStorage *(PathRef)> Factory
void profile(MemoryTree &MT) const
BackgroundIndex(const ThreadsafeFS &, const GlobalCompilationDatabase &CDB, BackgroundIndexStorage::Factory IndexStorageFactory, Options Opts)
Creates a new background index and starts its threads.
void boostRelated(llvm::StringRef Path)
Boosts priority of indexing related to Path.
void enqueue(const std::vector< std::string > &ChangedFiles)
void append(std::vector< Task >)
void work(std::function< void()> OnIdle=nullptr)
void boost(llvm::StringRef Tag, unsigned NewPriority)
static const Context & current()
Returns the context for the current thread, creating it if needed.
void update(llvm::StringRef Key, std::unique_ptr< SymbolSlab > Symbols, std::unique_ptr< RefSlab > Refs, std::unique_ptr< RelationSlab > Relations, bool CountReferences)
Updates all slabs associated with the Key.
void profile(MemoryTree &MT) const
Provides compilation arguments used for parsing C and C++ files.
virtual std::optional< tooling::CompileCommand > getCompileCommand(PathRef File) const =0
If there are any known-good commands for building this file, returns one.
MemIndex is a naive in-memory index suitable for a small set of symbols.
size_t estimateMemoryUsage() const override
Returns estimated size of index (in bytes).
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > view(std::nullopt_t CWD) const
Obtain a vfs::FileSystem with an arbitrary initial working directory.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
static llvm::Expected< std::string > resolve(const URI &U, llvm::StringRef HintPath="")
Resolves the absolute path of U.
WithContext replaces Context::current() with a provided scope.
Records an event whose duration is the lifetime of the Span object.
std::vector< LoadedShard > loadIndexShards(llvm::ArrayRef< Path > MainFiles, BackgroundIndexStorage::Factory &IndexStorageFactory, const GlobalCompilationDatabase &CDB)
Loads all shards for the TU MainFile from Storage.
IndexContents
Describes what data is covered by an index.
std::string Path
A typedef to represent a file path.
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
static llvm::StringRef filenameWithoutExtension(llvm::StringRef Path)
FileDigest digest(llvm::StringRef Content)
void vlog(const char *Fmt, Ts &&... Vals)
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
std::optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
std::optional< std::string > getCanonicalPath(const FileEntryRef F, FileManager &FileMgr)
Get the canonical path of F.
std::unique_ptr< CompilerInstance > prepareCompilerInstance(std::unique_ptr< clang::CompilerInvocation > CI, const PrecompiledPreamble *Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, DiagnosticConsumer &DiagsClient)
llvm::StringMap< IncludeGraphNode > IncludeGraph
std::unique_ptr< FrontendAction > createStaticIndexingAction(SymbolCollector::Options Opts, std::function< void(SymbolSlab)> SymbolsCallback, std::function< void(RefSlab)> RefsCallback, std::function< void(RelationSlab)> RelationsCallback, std::function< void(IncludeGraph)> IncludeGraphCallback)
void log(const char *Fmt, Ts &&... Vals)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
void elog(const char *Fmt, Ts &&... Vals)
bool isHeaderFile(llvm::StringRef FileName, std::optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A work item on the thread pool's queue.
static const Config & current()
Returns the Config of the current Context, or an empty configuration.
BackgroundPolicy Background
Whether this TU should be background-indexed.
struct clang::clangd::Config::@3 Index
Controls index behavior.
A tree that can be used to represent memory usage of nested components while preserving the hierarchy...
void addUsage(size_t Increment)
Increases size of current node by Increment.
MemoryTree & child(llvm::StringLiteral Name)
No copy of the Name.