2018-11-16 17:03:56 +08:00
|
|
|
//== BackgroundIndexStorage.cpp - Provide caching support to BackgroundIndex ==/
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2018-11-16 17:03:56 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "Logger.h"
|
|
|
|
#include "index/Background.h"
|
2018-12-17 20:38:22 +08:00
|
|
|
#include "llvm/ADT/ScopeExit.h"
|
|
|
|
#include "llvm/Support/Error.h"
|
2018-11-16 17:03:56 +08:00
|
|
|
#include "llvm/Support/FileSystem.h"
|
|
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
|
|
#include "llvm/Support/Path.h"
|
|
|
|
#include "llvm/Support/SHA1.h"
|
|
|
|
|
|
|
|
namespace clang {
|
|
|
|
namespace clangd {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
using FileDigest = decltype(llvm::SHA1::hash({}));
|
|
|
|
|
|
|
|
static FileDigest digest(StringRef Content) {
|
|
|
|
return llvm::SHA1::hash({(const uint8_t *)Content.data(), Content.size()});
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getShardPathFromFilePath(llvm::StringRef ShardRoot,
|
|
|
|
llvm::StringRef FilePath) {
|
|
|
|
llvm::SmallString<128> ShardRootSS(ShardRoot);
|
|
|
|
llvm::sys::path::append(ShardRootSS, llvm::sys::path::filename(FilePath) +
|
|
|
|
"." + llvm::toHex(digest(FilePath)) +
|
|
|
|
".idx");
|
[clangd] Enable auto-index behind a flag.
Summary:
Ownership and configuration:
The auto-index (background index) is maintained by ClangdServer, like Dynamic.
(This means ClangdServer will be able to enqueue preamble indexing in future).
For now it's enabled by a simple boolean flag in ClangdServer::Options, but
we probably want to eventually allow injecting the storage strategy.
New 'sync' command:
In order to meaningfully test the integration (not just unit-test components)
we need a way for tests to ensure the asynchronous index reads/writes occur
before a certain point.
Because these tests and assertions are few, I think exposing an explicit "sync"
command for use in tests is simpler than allowing threading to be completely
disabled in the background index (as we do for TUScheduler).
Bugs:
I fixed a couple of trivial bugs I found while testing, but there's one I can't.
JSONCompilationDatabase::getAllFiles() may return relative paths, and currently
we trigger an assertion that assumes they are absolute.
There's no efficient way to resolve them (you have to retrieve the corresponding
command and then resolve against its directory property). In general I think
this behavior is broken and we should fix it in JSONCompilationDatabase and
require CompilationDatabase::getAllFiles() to be absolute.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits
Differential Revision: https://reviews.llvm.org/D54894
llvm-svn: 347567
2018-11-27 00:00:11 +08:00
|
|
|
return ShardRootSS.str();
|
2018-11-16 17:03:56 +08:00
|
|
|
}
|
|
|
|
|
2018-12-17 20:38:22 +08:00
|
|
|
llvm::Error
|
|
|
|
writeAtomically(llvm::StringRef OutPath,
|
|
|
|
llvm::function_ref<void(llvm::raw_ostream &)> Writer) {
|
|
|
|
// Write to a temporary file first.
|
|
|
|
llvm::SmallString<128> TempPath;
|
|
|
|
int FD;
|
|
|
|
auto EC =
|
|
|
|
llvm::sys::fs::createUniqueFile(OutPath + ".tmp.%%%%%%%%", FD, TempPath);
|
|
|
|
if (EC)
|
|
|
|
return llvm::errorCodeToError(EC);
|
|
|
|
// Make sure temp file is destroyed on failure.
|
|
|
|
auto RemoveOnFail =
|
|
|
|
llvm::make_scope_exit([TempPath] { llvm::sys::fs::remove(TempPath); });
|
|
|
|
llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
|
|
|
|
Writer(OS);
|
|
|
|
OS.close();
|
|
|
|
if (OS.has_error())
|
|
|
|
return llvm::errorCodeToError(OS.error());
|
|
|
|
// Then move to real location.
|
|
|
|
EC = llvm::sys::fs::rename(TempPath, OutPath);
|
|
|
|
if (EC)
|
|
|
|
return llvm::errorCodeToError(EC);
|
|
|
|
// If everything went well, we already moved the file to another name. So
|
|
|
|
// don't delete the file, as the name might be taken by another file.
|
|
|
|
RemoveOnFail.release();
|
|
|
|
return llvm::ErrorSuccess();
|
|
|
|
}
|
|
|
|
|
2018-11-16 17:03:56 +08:00
|
|
|
// Uses disk as a storage for index shards. Creates a directory called
|
|
|
|
// ".clangd-index/" under the path provided during construction.
|
|
|
|
class DiskBackedIndexStorage : public BackgroundIndexStorage {
|
|
|
|
std::string DiskShardRoot;
|
|
|
|
|
|
|
|
public:
|
|
|
|
// Sets DiskShardRoot to (Directory + ".clangd-index/") which is the base
|
|
|
|
// directory for all shard files.
|
|
|
|
DiskBackedIndexStorage(llvm::StringRef Directory) {
|
|
|
|
llvm::SmallString<128> CDBDirectory(Directory);
|
|
|
|
llvm::sys::path::append(CDBDirectory, ".clangd-index/");
|
|
|
|
DiskShardRoot = CDBDirectory.str();
|
|
|
|
std::error_code OK;
|
|
|
|
std::error_code EC = llvm::sys::fs::create_directory(DiskShardRoot);
|
|
|
|
if (EC != OK) {
|
|
|
|
elog("Failed to create directory {0} for index storage: {1}",
|
|
|
|
DiskShardRoot, EC.message());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<IndexFileIn>
|
|
|
|
loadShard(llvm::StringRef ShardIdentifier) const override {
|
|
|
|
const std::string ShardPath =
|
|
|
|
getShardPathFromFilePath(DiskShardRoot, ShardIdentifier);
|
|
|
|
auto Buffer = llvm::MemoryBuffer::getFile(ShardPath);
|
|
|
|
if (!Buffer)
|
|
|
|
return nullptr;
|
|
|
|
if (auto I = readIndexFile(Buffer->get()->getBuffer()))
|
|
|
|
return llvm::make_unique<IndexFileIn>(std::move(*I));
|
|
|
|
else
|
|
|
|
elog("Error while reading shard {0}: {1}", ShardIdentifier,
|
|
|
|
I.takeError());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Error storeShard(llvm::StringRef ShardIdentifier,
|
|
|
|
IndexFileOut Shard) const override {
|
2018-12-17 20:38:22 +08:00
|
|
|
return writeAtomically(
|
|
|
|
getShardPathFromFilePath(DiskShardRoot, ShardIdentifier),
|
|
|
|
[&Shard](llvm::raw_ostream &OS) { OS << Shard; });
|
2018-11-16 17:03:56 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
[clangd] Auto-index watches global CDB for changes.
Summary:
Instead of receiving compilation commands, auto-index is triggered by just
filenames to reindex, and gets commands from the global comp DB internally.
This has advantages:
- more of the work can be done asynchronously (fetching compilation commands
upfront can be slow for large CDBs)
- we get access to the CDB which can be used to retrieve interpolated commands
for headers (useful in some cases where the original TU goes away)
- fits nicely with the filename-only change observation from r347297
The interface to GlobalCompilationDatabase gets extended: when retrieving a
compile command, the GCDB can optionally report the project the file belongs to.
This naturally fits together with getCompileCommand: it's hard to implement one
without the other. But because most callers don't care, I've ended up with an
awkward optional-out-param-in-virtual method pattern - maybe there's a better
one.
This is the main missing integration point between ClangdServer and
BackgroundIndex, after this we should be able to add an auto-index flag.
Reviewers: ioeric, kadircet
Subscribers: MaskRay, jkorous, arphaman, cfe-commits, ilya-biryukov
Differential Revision: https://reviews.llvm.org/D54865
llvm-svn: 347538
2018-11-26 17:51:50 +08:00
|
|
|
// Doesn't persist index shards anywhere (used when the CDB dir is unknown).
|
|
|
|
// We could consider indexing into ~/.clangd/ or so instead.
|
|
|
|
class NullStorage : public BackgroundIndexStorage {
|
|
|
|
public:
|
|
|
|
std::unique_ptr<IndexFileIn>
|
|
|
|
loadShard(llvm::StringRef ShardIdentifier) const override {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
llvm::Error storeShard(llvm::StringRef ShardIdentifier,
|
|
|
|
IndexFileOut Shard) const override {
|
|
|
|
vlog("Couldn't find project for {0}, indexing in-memory only",
|
|
|
|
ShardIdentifier);
|
|
|
|
return llvm::Error::success();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-11-16 17:03:56 +08:00
|
|
|
// Creates and owns IndexStorages for multiple CDBs.
|
|
|
|
class DiskBackedIndexStorageManager {
|
|
|
|
public:
|
[clangd] Enable auto-index behind a flag.
Summary:
Ownership and configuration:
The auto-index (background index) is maintained by ClangdServer, like Dynamic.
(This means ClangdServer will be able to enqueue preamble indexing in future).
For now it's enabled by a simple boolean flag in ClangdServer::Options, but
we probably want to eventually allow injecting the storage strategy.
New 'sync' command:
In order to meaningfully test the integration (not just unit-test components)
we need a way for tests to ensure the asynchronous index reads/writes occur
before a certain point.
Because these tests and assertions are few, I think exposing an explicit "sync"
command for use in tests is simpler than allowing threading to be completely
disabled in the background index (as we do for TUScheduler).
Bugs:
I fixed a couple of trivial bugs I found while testing, but there's one I can't.
JSONCompilationDatabase::getAllFiles() may return relative paths, and currently
we trigger an assertion that assumes they are absolute.
There's no efficient way to resolve them (you have to retrieve the corresponding
command and then resolve against its directory property). In general I think
this behavior is broken and we should fix it in JSONCompilationDatabase and
require CompilationDatabase::getAllFiles() to be absolute.
Reviewers: kadircet
Subscribers: ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, cfe-commits
Differential Revision: https://reviews.llvm.org/D54894
llvm-svn: 347567
2018-11-27 00:00:11 +08:00
|
|
|
DiskBackedIndexStorageManager()
|
|
|
|
: IndexStorageMapMu(llvm::make_unique<std::mutex>()) {}
|
|
|
|
|
2018-11-16 17:03:56 +08:00
|
|
|
// Creates or fetches to storage from cache for the specified CDB.
|
|
|
|
BackgroundIndexStorage *operator()(llvm::StringRef CDBDirectory) {
|
|
|
|
std::lock_guard<std::mutex> Lock(*IndexStorageMapMu);
|
|
|
|
auto &IndexStorage = IndexStorageMap[CDBDirectory];
|
|
|
|
if (!IndexStorage)
|
[clangd] Auto-index watches global CDB for changes.
Summary:
Instead of receiving compilation commands, auto-index is triggered by just
filenames to reindex, and gets commands from the global comp DB internally.
This has advantages:
- more of the work can be done asynchronously (fetching compilation commands
upfront can be slow for large CDBs)
- we get access to the CDB which can be used to retrieve interpolated commands
for headers (useful in some cases where the original TU goes away)
- fits nicely with the filename-only change observation from r347297
The interface to GlobalCompilationDatabase gets extended: when retrieving a
compile command, the GCDB can optionally report the project the file belongs to.
This naturally fits together with getCompileCommand: it's hard to implement one
without the other. But because most callers don't care, I've ended up with an
awkward optional-out-param-in-virtual method pattern - maybe there's a better
one.
This is the main missing integration point between ClangdServer and
BackgroundIndex, after this we should be able to add an auto-index flag.
Reviewers: ioeric, kadircet
Subscribers: MaskRay, jkorous, arphaman, cfe-commits, ilya-biryukov
Differential Revision: https://reviews.llvm.org/D54865
llvm-svn: 347538
2018-11-26 17:51:50 +08:00
|
|
|
IndexStorage = create(CDBDirectory);
|
2018-11-16 17:03:56 +08:00
|
|
|
return IndexStorage.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creates or fetches to storage from cache for the specified CDB.
|
|
|
|
BackgroundIndexStorage *createStorage(llvm::StringRef CDBDirectory);
|
|
|
|
|
|
|
|
private:
|
[clangd] Auto-index watches global CDB for changes.
Summary:
Instead of receiving compilation commands, auto-index is triggered by just
filenames to reindex, and gets commands from the global comp DB internally.
This has advantages:
- more of the work can be done asynchronously (fetching compilation commands
upfront can be slow for large CDBs)
- we get access to the CDB which can be used to retrieve interpolated commands
for headers (useful in some cases where the original TU goes away)
- fits nicely with the filename-only change observation from r347297
The interface to GlobalCompilationDatabase gets extended: when retrieving a
compile command, the GCDB can optionally report the project the file belongs to.
This naturally fits together with getCompileCommand: it's hard to implement one
without the other. But because most callers don't care, I've ended up with an
awkward optional-out-param-in-virtual method pattern - maybe there's a better
one.
This is the main missing integration point between ClangdServer and
BackgroundIndex, after this we should be able to add an auto-index flag.
Reviewers: ioeric, kadircet
Subscribers: MaskRay, jkorous, arphaman, cfe-commits, ilya-biryukov
Differential Revision: https://reviews.llvm.org/D54865
llvm-svn: 347538
2018-11-26 17:51:50 +08:00
|
|
|
std::unique_ptr<BackgroundIndexStorage> create(llvm::StringRef CDBDirectory) {
|
|
|
|
if (CDBDirectory.empty())
|
|
|
|
return llvm::make_unique<NullStorage>();
|
|
|
|
return llvm::make_unique<DiskBackedIndexStorage>(CDBDirectory);
|
|
|
|
}
|
|
|
|
|
2018-11-16 17:03:56 +08:00
|
|
|
llvm::StringMap<std::unique_ptr<BackgroundIndexStorage>> IndexStorageMap;
|
|
|
|
std::unique_ptr<std::mutex> IndexStorageMapMu;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
BackgroundIndexStorage::Factory
|
|
|
|
BackgroundIndexStorage::createDiskBackedStorageFactory() {
|
|
|
|
return DiskBackedIndexStorageManager();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace clangd
|
|
|
|
} // namespace clang
|