xref: /llvm-project/clang-tools-extra/clangd/index/BackgroundIndexStorage.cpp (revision 5b2772e1fad3fc8b44400b91de4d5bd0724bba75)
106553bfeSKadir Cetinkaya //== BackgroundIndexStorage.cpp - Provide caching support to BackgroundIndex ==/
206553bfeSKadir Cetinkaya //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606553bfeSKadir Cetinkaya //
706553bfeSKadir Cetinkaya //===----------------------------------------------------------------------===//
806553bfeSKadir Cetinkaya 
991e5f4b4SKadir Cetinkaya #include "GlobalCompilationDatabase.h"
1006553bfeSKadir Cetinkaya #include "index/Background.h"
11ad97ccf6SSam McCall #include "support/Logger.h"
12ad97ccf6SSam McCall #include "support/Path.h"
1391e5f4b4SKadir Cetinkaya #include "llvm/ADT/SmallString.h"
1491e5f4b4SKadir Cetinkaya #include "llvm/ADT/StringRef.h"
15e913b956SKadir Cetinkaya #include "llvm/Support/Error.h"
1606553bfeSKadir Cetinkaya #include "llvm/Support/FileSystem.h"
1706553bfeSKadir Cetinkaya #include "llvm/Support/MemoryBuffer.h"
1806553bfeSKadir Cetinkaya #include "llvm/Support/Path.h"
192feac34aSHaojian Wu #include "llvm/Support/raw_ostream.h"
2091e5f4b4SKadir Cetinkaya #include <functional>
2171f55735SKazu Hirata #include <optional>
2206553bfeSKadir Cetinkaya 
2306553bfeSKadir Cetinkaya namespace clang {
2406553bfeSKadir Cetinkaya namespace clangd {
2506553bfeSKadir Cetinkaya namespace {
2606553bfeSKadir Cetinkaya 
getShardPathFromFilePath(llvm::StringRef ShardRoot,llvm::StringRef FilePath)2706553bfeSKadir Cetinkaya std::string getShardPathFromFilePath(llvm::StringRef ShardRoot,
2806553bfeSKadir Cetinkaya                                      llvm::StringRef FilePath) {
2906553bfeSKadir Cetinkaya   llvm::SmallString<128> ShardRootSS(ShardRoot);
3006553bfeSKadir Cetinkaya   llvm::sys::path::append(ShardRootSS, llvm::sys::path::filename(FilePath) +
3106553bfeSKadir Cetinkaya                                            "." + llvm::toHex(digest(FilePath)) +
3206553bfeSKadir Cetinkaya                                            ".idx");
33*5b2772e1SKazu Hirata   return std::string(ShardRootSS);
3406553bfeSKadir Cetinkaya }
3506553bfeSKadir Cetinkaya 
364e769e93SSam McCall // Uses disk as a storage for index shards.
3706553bfeSKadir Cetinkaya class DiskBackedIndexStorage : public BackgroundIndexStorage {
3806553bfeSKadir Cetinkaya   std::string DiskShardRoot;
3906553bfeSKadir Cetinkaya 
4006553bfeSKadir Cetinkaya public:
414e769e93SSam McCall   // Creates `DiskShardRoot` and any parents during construction.
DiskBackedIndexStorage(llvm::StringRef Directory)424e769e93SSam McCall   DiskBackedIndexStorage(llvm::StringRef Directory) : DiskShardRoot(Directory) {
4306553bfeSKadir Cetinkaya     std::error_code OK;
442754942cSIlya Biryukov     std::error_code EC = llvm::sys::fs::create_directories(DiskShardRoot);
4506553bfeSKadir Cetinkaya     if (EC != OK) {
4606553bfeSKadir Cetinkaya       elog("Failed to create directory {0} for index storage: {1}",
4706553bfeSKadir Cetinkaya            DiskShardRoot, EC.message());
4806553bfeSKadir Cetinkaya     }
4906553bfeSKadir Cetinkaya   }
5006553bfeSKadir Cetinkaya 
5106553bfeSKadir Cetinkaya   std::unique_ptr<IndexFileIn>
loadShard(llvm::StringRef ShardIdentifier) const5206553bfeSKadir Cetinkaya   loadShard(llvm::StringRef ShardIdentifier) const override {
5306553bfeSKadir Cetinkaya     const std::string ShardPath =
5406553bfeSKadir Cetinkaya         getShardPathFromFilePath(DiskShardRoot, ShardIdentifier);
5506553bfeSKadir Cetinkaya     auto Buffer = llvm::MemoryBuffer::getFile(ShardPath);
5606553bfeSKadir Cetinkaya     if (!Buffer)
5706553bfeSKadir Cetinkaya       return nullptr;
589c9119abSSam McCall     if (auto I =
599c9119abSSam McCall             readIndexFile(Buffer->get()->getBuffer(), SymbolOrigin::Background))
601c705d9cSJonas Devlieghere       return std::make_unique<IndexFileIn>(std::move(*I));
6106553bfeSKadir Cetinkaya     else
6206553bfeSKadir Cetinkaya       elog("Error while reading shard {0}: {1}", ShardIdentifier,
6306553bfeSKadir Cetinkaya            I.takeError());
6406553bfeSKadir Cetinkaya     return nullptr;
6506553bfeSKadir Cetinkaya   }
6606553bfeSKadir Cetinkaya 
storeShard(llvm::StringRef ShardIdentifier,IndexFileOut Shard) const6706553bfeSKadir Cetinkaya   llvm::Error storeShard(llvm::StringRef ShardIdentifier,
6806553bfeSKadir Cetinkaya                          IndexFileOut Shard) const override {
69f69c9178SJan Korous     auto ShardPath = getShardPathFromFilePath(DiskShardRoot, ShardIdentifier);
702feac34aSHaojian Wu     return llvm::writeToOutput(ShardPath, [&Shard](llvm::raw_ostream &OS) {
71f69c9178SJan Korous       OS << Shard;
72f69c9178SJan Korous       return llvm::Error::success();
73f69c9178SJan Korous     });
7406553bfeSKadir Cetinkaya   }
7506553bfeSKadir Cetinkaya };
7606553bfeSKadir Cetinkaya 
776e2d2a33SSam McCall // Doesn't persist index shards anywhere (used when the CDB dir is unknown).
786e2d2a33SSam McCall // We could consider indexing into ~/.clangd/ or so instead.
796e2d2a33SSam McCall class NullStorage : public BackgroundIndexStorage {
806e2d2a33SSam McCall public:
816e2d2a33SSam McCall   std::unique_ptr<IndexFileIn>
loadShard(llvm::StringRef ShardIdentifier) const826e2d2a33SSam McCall   loadShard(llvm::StringRef ShardIdentifier) const override {
836e2d2a33SSam McCall     return nullptr;
846e2d2a33SSam McCall   }
856e2d2a33SSam McCall 
storeShard(llvm::StringRef ShardIdentifier,IndexFileOut Shard) const866e2d2a33SSam McCall   llvm::Error storeShard(llvm::StringRef ShardIdentifier,
876e2d2a33SSam McCall                          IndexFileOut Shard) const override {
886e2d2a33SSam McCall     vlog("Couldn't find project for {0}, indexing in-memory only",
896e2d2a33SSam McCall          ShardIdentifier);
906e2d2a33SSam McCall     return llvm::Error::success();
916e2d2a33SSam McCall   }
926e2d2a33SSam McCall };
936e2d2a33SSam McCall 
9406553bfeSKadir Cetinkaya // Creates and owns IndexStorages for multiple CDBs.
959b55bc4dSSam McCall // When a CDB root is found, shards are stored in $ROOT/.cache/clangd/index/.
969b55bc4dSSam McCall // When no root is found, the fallback path is ~/.cache/clangd/index/.
9706553bfeSKadir Cetinkaya class DiskBackedIndexStorageManager {
9806553bfeSKadir Cetinkaya public:
DiskBackedIndexStorageManager(std::function<std::optional<ProjectInfo> (PathRef)> GetProjectInfo)9991e5f4b4SKadir Cetinkaya   DiskBackedIndexStorageManager(
100f71ffd3bSKazu Hirata       std::function<std::optional<ProjectInfo>(PathRef)> GetProjectInfo)
1011c705d9cSJonas Devlieghere       : IndexStorageMapMu(std::make_unique<std::mutex>()),
10291e5f4b4SKadir Cetinkaya         GetProjectInfo(std::move(GetProjectInfo)) {
1034e769e93SSam McCall     llvm::SmallString<128> FallbackDir;
1044e769e93SSam McCall     if (llvm::sys::path::cache_directory(FallbackDir))
1054e769e93SSam McCall       llvm::sys::path::append(FallbackDir, "clangd", "index");
1064e769e93SSam McCall     this->FallbackDir = FallbackDir.str().str();
10791e5f4b4SKadir Cetinkaya   }
108422c828dSSam McCall 
10991e5f4b4SKadir Cetinkaya   // Creates or fetches to storage from cache for the specified project.
operator ()(PathRef File)11091e5f4b4SKadir Cetinkaya   BackgroundIndexStorage *operator()(PathRef File) {
11106553bfeSKadir Cetinkaya     std::lock_guard<std::mutex> Lock(*IndexStorageMapMu);
1124e769e93SSam McCall     llvm::SmallString<128> StorageDir(FallbackDir);
1134e769e93SSam McCall     if (auto PI = GetProjectInfo(File)) {
1144e769e93SSam McCall       StorageDir = PI->SourceRoot;
1159b55bc4dSSam McCall       llvm::sys::path::append(StorageDir, ".cache", "clangd", "index");
1164e769e93SSam McCall     }
1174e769e93SSam McCall     auto &IndexStorage = IndexStorageMap[StorageDir];
11806553bfeSKadir Cetinkaya     if (!IndexStorage)
1194e769e93SSam McCall       IndexStorage = create(StorageDir);
12006553bfeSKadir Cetinkaya     return IndexStorage.get();
12106553bfeSKadir Cetinkaya   }
12206553bfeSKadir Cetinkaya 
12306553bfeSKadir Cetinkaya private:
create(PathRef CDBDirectory)12491e5f4b4SKadir Cetinkaya   std::unique_ptr<BackgroundIndexStorage> create(PathRef CDBDirectory) {
12591e5f4b4SKadir Cetinkaya     if (CDBDirectory.empty()) {
12691e5f4b4SKadir Cetinkaya       elog("Tried to create storage for empty directory!");
1271c705d9cSJonas Devlieghere       return std::make_unique<NullStorage>();
12891e5f4b4SKadir Cetinkaya     }
1291c705d9cSJonas Devlieghere     return std::make_unique<DiskBackedIndexStorage>(CDBDirectory);
1306e2d2a33SSam McCall   }
1316e2d2a33SSam McCall 
1324e769e93SSam McCall   Path FallbackDir;
13391e5f4b4SKadir Cetinkaya 
13406553bfeSKadir Cetinkaya   llvm::StringMap<std::unique_ptr<BackgroundIndexStorage>> IndexStorageMap;
13506553bfeSKadir Cetinkaya   std::unique_ptr<std::mutex> IndexStorageMapMu;
13691e5f4b4SKadir Cetinkaya 
137f71ffd3bSKazu Hirata   std::function<std::optional<ProjectInfo>(PathRef)> GetProjectInfo;
13806553bfeSKadir Cetinkaya };
13906553bfeSKadir Cetinkaya 
14006553bfeSKadir Cetinkaya } // namespace
14106553bfeSKadir Cetinkaya 
14206553bfeSKadir Cetinkaya BackgroundIndexStorage::Factory
createDiskBackedStorageFactory(std::function<std::optional<ProjectInfo> (PathRef)> GetProjectInfo)14391e5f4b4SKadir Cetinkaya BackgroundIndexStorage::createDiskBackedStorageFactory(
144f71ffd3bSKazu Hirata     std::function<std::optional<ProjectInfo>(PathRef)> GetProjectInfo) {
14591e5f4b4SKadir Cetinkaya   return DiskBackedIndexStorageManager(std::move(GetProjectInfo));
14606553bfeSKadir Cetinkaya }
14706553bfeSKadir Cetinkaya 
14806553bfeSKadir Cetinkaya } // namespace clangd
14906553bfeSKadir Cetinkaya } // namespace clang
150