brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.5 KiB · 003c850 Raw
268 lines · cpp
1//===----------------------------------------------------------------------===//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/// \file This file implements the underlying ActionCache implementations.10///11//===----------------------------------------------------------------------===//12 13#include "BuiltinCAS.h"14#include "llvm/ADT/TrieRawHashMap.h"15#include "llvm/CAS/ActionCache.h"16#include "llvm/CAS/OnDiskKeyValueDB.h"17#include "llvm/CAS/UnifiedOnDiskCache.h"18#include "llvm/Config/llvm-config.h"19#include "llvm/Support/BLAKE3.h"20#include "llvm/Support/Errc.h"21 22#define DEBUG_TYPE "cas-action-caches"23 24using namespace llvm;25using namespace llvm::cas;26 27namespace {28 29using HasherT = BLAKE3;30using HashType = decltype(HasherT::hash(std::declval<ArrayRef<uint8_t> &>()));31 32template <size_t Size> class CacheEntry {33public:34  CacheEntry() = default;35  CacheEntry(ArrayRef<uint8_t> Hash) { llvm::copy(Hash, Value.data()); }36  CacheEntry(const CacheEntry &Entry) { llvm::copy(Entry.Value, Value.data()); }37  ArrayRef<uint8_t> getValue() const { return Value; }38 39private:40  std::array<uint8_t, Size> Value;41};42 43/// Builtin InMemory ActionCache that stores the mapping in memory.44class InMemoryActionCache final : public ActionCache {45public:46  InMemoryActionCache()47      : ActionCache(builtin::BuiltinCASContext::getDefaultContext()) {}48 49  Error putImpl(ArrayRef<uint8_t> ActionKey, const CASID &Result,50                bool CanBeDistributed) final;51  Expected<std::optional<CASID>> getImpl(ArrayRef<uint8_t> ActionKey,52                                         bool CanBeDistributed) const final;53 54  Error validate() const final {55    return createStringError("InMemoryActionCache doesn't support validate()");56  }57 58private:59  using DataT = CacheEntry<sizeof(HashType)>;60  using InMemoryCacheT = ThreadSafeTrieRawHashMap<DataT, sizeof(HashType)>;61 62  InMemoryCacheT Cache;63};64 65/// Builtin basic OnDiskActionCache that uses one underlying OnDiskKeyValueDB.66class OnDiskActionCache final : public ActionCache {67public:68  Error putImpl(ArrayRef<uint8_t> ActionKey, const CASID &Result,69                bool CanBeDistributed) final;70  Expected<std::optional<CASID>> getImpl(ArrayRef<uint8_t> ActionKey,71                                         bool CanBeDistributed) const final;72 73  static Expected<std::unique_ptr<OnDiskActionCache>> create(StringRef Path);74 75  Error validate() const final;76 77private:78  static StringRef getHashName() { return "BLAKE3"; }79 80  OnDiskActionCache(std::unique_ptr<ondisk::OnDiskKeyValueDB> DB);81 82  std::unique_ptr<ondisk::OnDiskKeyValueDB> DB;83  using DataT = CacheEntry<sizeof(HashType)>;84};85 86/// Builtin unified ActionCache that wraps around UnifiedOnDiskCache to provide87/// access to its ActionCache.88class UnifiedOnDiskActionCache final : public ActionCache {89public:90  Error putImpl(ArrayRef<uint8_t> ActionKey, const CASID &Result,91                bool CanBeDistributed) final;92  Expected<std::optional<CASID>> getImpl(ArrayRef<uint8_t> ActionKey,93                                         bool CanBeDistributed) const final;94 95  UnifiedOnDiskActionCache(std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB);96 97  Error validate() const final;98 99private:100  std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB;101};102} // end namespace103 104static Error createResultCachePoisonedError(ArrayRef<uint8_t> KeyHash,105                                            const CASContext &Context,106                                            CASID Output,107                                            ArrayRef<uint8_t> ExistingOutput) {108  std::string Existing =109      CASID::create(&Context, toStringRef(ExistingOutput)).toString();110  SmallString<64> Key;111  toHex(KeyHash, /*LowerCase=*/true, Key);112  return createStringError(std::make_error_code(std::errc::invalid_argument),113                           "cache poisoned for '" + Key + "' (new='" +114                               Output.toString() + "' vs. existing '" +115                               Existing + "')");116}117 118Expected<std::optional<CASID>>119InMemoryActionCache::getImpl(ArrayRef<uint8_t> Key,120                             bool /*CanBeDistributed*/) const {121  auto Result = Cache.find(Key);122  if (!Result)123    return std::nullopt;124  return CASID::create(&getContext(), toStringRef(Result->Data.getValue()));125}126 127Error InMemoryActionCache::putImpl(ArrayRef<uint8_t> Key, const CASID &Result,128                                   bool /*CanBeDistributed*/) {129  DataT Expected(Result.getHash());130  const InMemoryCacheT::value_type &Cached = *Cache.insertLazy(131      Key, [&](auto ValueConstructor) { ValueConstructor.emplace(Expected); });132 133  const DataT &Observed = Cached.Data;134  if (Expected.getValue() == Observed.getValue())135    return Error::success();136 137  return createResultCachePoisonedError(Key, getContext(), Result,138                                        Observed.getValue());139}140 141namespace llvm::cas {142 143std::unique_ptr<ActionCache> createInMemoryActionCache() {144  return std::make_unique<InMemoryActionCache>();145}146 147} // namespace llvm::cas148 149OnDiskActionCache::OnDiskActionCache(150    std::unique_ptr<ondisk::OnDiskKeyValueDB> DB)151    : ActionCache(builtin::BuiltinCASContext::getDefaultContext()),152      DB(std::move(DB)) {}153 154Expected<std::unique_ptr<OnDiskActionCache>>155OnDiskActionCache::create(StringRef AbsPath) {156  std::unique_ptr<ondisk::OnDiskKeyValueDB> DB;157  if (Error E = ondisk::OnDiskKeyValueDB::open(AbsPath, getHashName(),158                                               sizeof(HashType), getHashName(),159                                               sizeof(DataT))160                    .moveInto(DB))161    return std::move(E);162  return std::unique_ptr<OnDiskActionCache>(163      new OnDiskActionCache(std::move(DB)));164}165 166Expected<std::optional<CASID>>167OnDiskActionCache::getImpl(ArrayRef<uint8_t> Key,168                           bool /*CanBeDistributed*/) const {169  std::optional<ArrayRef<char>> Val;170  if (Error E = DB->get(Key).moveInto(Val))171    return std::move(E);172  if (!Val)173    return std::nullopt;174  return CASID::create(&getContext(), toStringRef(*Val));175}176 177Error OnDiskActionCache::putImpl(ArrayRef<uint8_t> Key, const CASID &Result,178                                 bool /*CanBeDistributed*/) {179  auto ResultHash = Result.getHash();180  ArrayRef Expected((const char *)ResultHash.data(), ResultHash.size());181  ArrayRef<char> Observed;182  if (Error E = DB->put(Key, Expected).moveInto(Observed))183    return E;184 185  if (Expected == Observed)186    return Error::success();187 188  return createResultCachePoisonedError(189      Key, getContext(), Result,190      ArrayRef((const uint8_t *)Observed.data(), Observed.size()));191}192 193Error OnDiskActionCache::validate() const {194  // FIXME: without the matching CAS there is nothing we can check about the195  // cached values. The hash size is already validated by the DB validator.196  return DB->validate(nullptr);197}198 199UnifiedOnDiskActionCache::UnifiedOnDiskActionCache(200    std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB)201    : ActionCache(builtin::BuiltinCASContext::getDefaultContext()),202      UniDB(std::move(UniDB)) {}203 204Expected<std::optional<CASID>>205UnifiedOnDiskActionCache::getImpl(ArrayRef<uint8_t> Key,206                                  bool /*CanBeDistributed*/) const {207  std::optional<ArrayRef<char>> Val;208  if (Error E = UniDB->getKeyValueDB().get(Key).moveInto(Val))209    return std::move(E);210  if (!Val)211    return std::nullopt;212  auto ID = ondisk::UnifiedOnDiskCache::getObjectIDFromValue(*Val);213  return CASID::create(&getContext(),214                       toStringRef(UniDB->getGraphDB().getDigest(ID)));215}216 217Error UnifiedOnDiskActionCache::putImpl(ArrayRef<uint8_t> Key,218                                        const CASID &Result,219                                        bool /*CanBeDistributed*/) {220  auto Expected = UniDB->getGraphDB().getReference(Result.getHash());221  if (LLVM_UNLIKELY(!Expected))222    return Expected.takeError();223 224  auto Value = ondisk::UnifiedOnDiskCache::getValueFromObjectID(*Expected);225  std::optional<ArrayRef<char>> Observed;226  if (Error E = UniDB->getKeyValueDB().put(Key, Value).moveInto(Observed))227    return E;228 229  auto ObservedID = ondisk::UnifiedOnDiskCache::getObjectIDFromValue(*Observed);230  if (*Expected == ObservedID)231    return Error::success();232 233  return createResultCachePoisonedError(234      Key, getContext(), Result, UniDB->getGraphDB().getDigest(ObservedID));235}236 237Error UnifiedOnDiskActionCache::validate() const {238  auto ValidateRef = [](FileOffset Offset, ArrayRef<char> Value) -> Error {239    auto ID = ondisk::UnifiedOnDiskCache::getObjectIDFromValue(Value);240    auto formatError = [&](Twine Msg) {241      return createStringError(242          llvm::errc::illegal_byte_sequence,243          "bad record at 0x" +244              utohexstr((unsigned)Offset.get(), /*LowerCase=*/true) + ": " +245              Msg.str());246    };247    if (ID.getOpaqueData() == 0)248      return formatError("zero is not a valid ref");249    return Error::success();250  };251  return UniDB->getKeyValueDB().validate(ValidateRef);252}253 254Expected<std::unique_ptr<ActionCache>>255cas::createOnDiskActionCache(StringRef Path) {256#if LLVM_ENABLE_ONDISK_CAS257  return OnDiskActionCache::create(Path);258#else259  return createStringError(inconvertibleErrorCode(), "OnDiskCache is disabled");260#endif261}262 263std::unique_ptr<ActionCache>264cas::builtin::createActionCacheFromUnifiedOnDiskCache(265    std::shared_ptr<ondisk::UnifiedOnDiskCache> UniDB) {266  return std::make_unique<UnifiedOnDiskActionCache>(std::move(UniDB));267}268