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-exception
6//
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(path&: ShardRootSS, a: llvm::sys::path::filename(path: FilePath) +
31 "." + llvm::toHex(Input: digest(Content: 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(path: DiskShardRoot);
45 if (EC != OK) {
46 elog(Fmt: "Failed to create directory {0} for index storage: {1}",
47 Vals&: DiskShardRoot, Vals: EC.message());
48 }
49 }
50
51 std::unique_ptr<IndexFileIn>
52 loadShard(llvm::StringRef ShardIdentifier) const override {
53 const std::string ShardPath =
54 getShardPathFromFilePath(ShardRoot: DiskShardRoot, FilePath: ShardIdentifier);
55 auto Buffer = llvm::MemoryBuffer::getFile(Filename: ShardPath);
56 if (!Buffer)
57 return nullptr;
58 if (auto I =
59 readIndexFile(Buffer->get()->getBuffer(), SymbolOrigin::Background))
60 return std::make_unique<IndexFileIn>(args: std::move(*I));
61 else
62 elog(Fmt: "Error while reading shard {0}: {1}", Vals&: ShardIdentifier,
63 Vals: I.takeError());
64 return nullptr;
65 }
66
67 llvm::Error storeShard(llvm::StringRef ShardIdentifier,
68 IndexFileOut Shard) const override {
69 auto ShardPath = getShardPathFromFilePath(ShardRoot: DiskShardRoot, FilePath: ShardIdentifier);
70 return llvm::writeToOutput(OutputFileName: ShardPath, Write: [&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(Fmt: "Couldn't find project for {0}, indexing in-memory only",
89 Vals&: 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(result&: FallbackDir))
105 llvm::sys::path::append(path&: FallbackDir, a: "clangd", b: "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(path&: StorageDir, a: ".cache", b: "clangd", c: "index");
116 }
117 auto &IndexStorage = IndexStorageMap[StorageDir];
118 if (!IndexStorage)
119 IndexStorage = create(CDBDirectory: StorageDir);
120 return IndexStorage.get();
121 }
122
123private:
124 std::unique_ptr<BackgroundIndexStorage> create(PathRef CDBDirectory) {
125 if (CDBDirectory.empty()) {
126 elog(Fmt: "Tried to create storage for empty directory!");
127 return std::make_unique<NullStorage>();
128 }
129 return std::make_unique<DiskBackedIndexStorage>(args&: 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} // namespace
141
142BackgroundIndexStorage::Factory
143BackgroundIndexStorage::createDiskBackedStorageFactory(
144 std::function<std::optional<ProjectInfo>(PathRef)> GetProjectInfo) {
145 return DiskBackedIndexStorageManager(std::move(GetProjectInfo));
146}
147
148} // namespace clangd
149} // namespace clang
150

source code of clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp