150 lines · cpp
1//== BackgroundIndexStorage.cpp - Provide caching support to BackgroundIndex ==/2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "GlobalCompilationDatabase.h"10#include "index/Background.h"11#include "support/Logger.h"12#include "support/Path.h"13#include "llvm/ADT/SmallString.h"14#include "llvm/ADT/StringRef.h"15#include "llvm/Support/Error.h"16#include "llvm/Support/FileSystem.h"17#include "llvm/Support/MemoryBuffer.h"18#include "llvm/Support/Path.h"19#include "llvm/Support/raw_ostream.h"20#include <functional>21#include <optional>22 23namespace clang {24namespace clangd {25namespace {26 27std::string getShardPathFromFilePath(llvm::StringRef ShardRoot,28 llvm::StringRef FilePath) {29 llvm::SmallString<128> ShardRootSS(ShardRoot);30 llvm::sys::path::append(ShardRootSS, llvm::sys::path::filename(FilePath) +31 "." + llvm::toHex(digest(FilePath)) +32 ".idx");33 return std::string(ShardRootSS);34}35 36// Uses disk as a storage for index shards.37class DiskBackedIndexStorage : public BackgroundIndexStorage {38 std::string DiskShardRoot;39 40public:41 // Creates `DiskShardRoot` and any parents during construction.42 DiskBackedIndexStorage(llvm::StringRef Directory) : DiskShardRoot(Directory) {43 std::error_code OK;44 std::error_code EC = llvm::sys::fs::create_directories(DiskShardRoot);45 if (EC != OK) {46 elog("Failed to create directory {0} for index storage: {1}",47 DiskShardRoot, EC.message());48 }49 }50 51 std::unique_ptr<IndexFileIn>52 loadShard(llvm::StringRef ShardIdentifier) const override {53 const std::string ShardPath =54 getShardPathFromFilePath(DiskShardRoot, ShardIdentifier);55 auto Buffer = llvm::MemoryBuffer::getFile(ShardPath);56 if (!Buffer)57 return nullptr;58 if (auto I =59 readIndexFile(Buffer->get()->getBuffer(), SymbolOrigin::Background))60 return std::make_unique<IndexFileIn>(std::move(*I));61 else62 elog("Error while reading shard {0}: {1}", ShardIdentifier,63 I.takeError());64 return nullptr;65 }66 67 llvm::Error storeShard(llvm::StringRef ShardIdentifier,68 IndexFileOut Shard) const override {69 auto ShardPath = getShardPathFromFilePath(DiskShardRoot, ShardIdentifier);70 return llvm::writeToOutput(ShardPath, [&Shard](llvm::raw_ostream &OS) {71 OS << Shard;72 return llvm::Error::success();73 });74 }75};76 77// Doesn't persist index shards anywhere (used when the CDB dir is unknown).78// We could consider indexing into ~/.clangd/ or so instead.79class NullStorage : public BackgroundIndexStorage {80public:81 std::unique_ptr<IndexFileIn>82 loadShard(llvm::StringRef ShardIdentifier) const override {83 return nullptr;84 }85 86 llvm::Error storeShard(llvm::StringRef ShardIdentifier,87 IndexFileOut Shard) const override {88 vlog("Couldn't find project for {0}, indexing in-memory only",89 ShardIdentifier);90 return llvm::Error::success();91 }92};93 94// Creates and owns IndexStorages for multiple CDBs.95// When a CDB root is found, shards are stored in $ROOT/.cache/clangd/index/.96// When no root is found, the fallback path is ~/.cache/clangd/index/.97class DiskBackedIndexStorageManager {98public:99 DiskBackedIndexStorageManager(100 std::function<std::optional<ProjectInfo>(PathRef)> GetProjectInfo)101 : IndexStorageMapMu(std::make_unique<std::mutex>()),102 GetProjectInfo(std::move(GetProjectInfo)) {103 llvm::SmallString<128> FallbackDir;104 if (llvm::sys::path::cache_directory(FallbackDir))105 llvm::sys::path::append(FallbackDir, "clangd", "index");106 this->FallbackDir = FallbackDir.str().str();107 }108 109 // Creates or fetches to storage from cache for the specified project.110 BackgroundIndexStorage *operator()(PathRef File) {111 std::lock_guard<std::mutex> Lock(*IndexStorageMapMu);112 llvm::SmallString<128> StorageDir(FallbackDir);113 if (auto PI = GetProjectInfo(File)) {114 StorageDir = PI->SourceRoot;115 llvm::sys::path::append(StorageDir, ".cache", "clangd", "index");116 }117 auto &IndexStorage = IndexStorageMap[StorageDir];118 if (!IndexStorage)119 IndexStorage = create(StorageDir);120 return IndexStorage.get();121 }122 123private:124 std::unique_ptr<BackgroundIndexStorage> create(PathRef CDBDirectory) {125 if (CDBDirectory.empty()) {126 elog("Tried to create storage for empty directory!");127 return std::make_unique<NullStorage>();128 }129 return std::make_unique<DiskBackedIndexStorage>(CDBDirectory);130 }131 132 Path FallbackDir;133 134 llvm::StringMap<std::unique_ptr<BackgroundIndexStorage>> IndexStorageMap;135 std::unique_ptr<std::mutex> IndexStorageMapMu;136 137 std::function<std::optional<ProjectInfo>(PathRef)> GetProjectInfo;138};139 140} // namespace141 142BackgroundIndexStorage::Factory143BackgroundIndexStorage::createDiskBackedStorageFactory(144 std::function<std::optional<ProjectInfo>(PathRef)> GetProjectInfo) {145 return DiskBackedIndexStorageManager(std::move(GetProjectInfo));146}147 148} // namespace clangd149} // namespace clang150