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