diff --git a/clang/test/CAS/daemon-cas-recovery.c b/clang/test/CAS/daemon-cas-recovery.c index 37468e621176b..317d7155a6a08 100644 --- a/clang/test/CAS/daemon-cas-recovery.c +++ b/clang/test/CAS/daemon-cas-recovery.c @@ -4,7 +4,7 @@ /// Construct a malformed CAS to recovery from. // RUN: echo "abc" | llvm-cas --cas %t/cas --make-blob --data - -// RUN: rm %t/cas/v1.1/v11.data +// RUN: rm %t/cas/v1.1/data.v1 // RUN: not llvm-cas --cas %t/cas --validate --check-hash // RUN: env LLVM_CACHE_CAS_PATH=%t/cas LLVM_CAS_FORCE_VALIDATION=1 %clang-cache \ diff --git a/clang/test/CAS/depscan-cas-log.c b/clang/test/CAS/depscan-cas-log.c index cd134b777f6e3..e3833553593a1 100644 --- a/clang/test/CAS/depscan-cas-log.c +++ b/clang/test/CAS/depscan-cas-log.c @@ -10,11 +10,11 @@ // RUN: -cc1-args -cc1 -triple x86_64-apple-macosx11.0.0 -emit-obj %s -o %t/t.o -fcas-path %t/cas // RUN: FileCheck %s --input-file %t/cas/v1.log -// CHECK: [[PID1:[0-9]*]] {{[0-9]*}}: mmap '{{.*}}v{{[0-9]+}}.index' +// CHECK: [[PID1:[0-9]*]] {{[0-9]*}}: mmap '{{.*}}index.v{{[0-9]+}}' // CHECK: [[PID1]] {{[0-9]*}}: create subtrie // Even a minimal compilation involves at least 9 records for the cache key. // CHECK-COUNT-9: [[PID1]] {{[0-9]*}}: create record -// CHECK: [[PID2:[0-9]*]] {{[0-9]*}}: mmap '{{.*}}v{{[0-9]+}}.index' -// CHECK: [[PID2]] {{[0-9]*}}: close mmap '{{.*}}v{{[0-9]+}}.index' +// CHECK: [[PID2:[0-9]*]] {{[0-9]*}}: mmap '{{.*}}index.v{{[0-9]+}}' +// CHECK: [[PID2]] {{[0-9]*}}: close mmap '{{.*}}index.v{{[0-9]+}}' diff --git a/clang/test/CAS/validate-once.c b/clang/test/CAS/validate-once.c index e8bd76528af40..d08f2b46b832c 100644 --- a/clang/test/CAS/validate-once.c +++ b/clang/test/CAS/validate-once.c @@ -1,7 +1,7 @@ // RUN: rm -rf %t // RUN: llvm-cas --cas %t/cas --ingest %s -// RUN: mv %t/cas/v1.1/v11.data %t/cas/v1.1/v11.data.bak +// RUN: mv %t/cas/v1.1/data.v1 %t/cas/v1.1/data.v1.bak // RUN: %clang -cc1depscand -execute %{clang-daemon-dir}/%basename_t -cas-args -fcas-path %t/cas -- \ // RUN: %clang -target x86_64-apple-macos11 -I %S/Inputs \ diff --git a/llvm/include/llvm/CAS/OnDiskDataAllocator.h b/llvm/include/llvm/CAS/OnDiskDataAllocator.h index c65cfebe67862..afe6c4a58e554 100644 --- a/llvm/include/llvm/CAS/OnDiskDataAllocator.h +++ b/llvm/include/llvm/CAS/OnDiskDataAllocator.h @@ -64,7 +64,7 @@ class OnDiskDataAllocator { /// \returns the buffer that was allocated at \p create time, with size /// \p UserHeaderSize. - MutableArrayRef getUserHeader(); + MutableArrayRef getUserHeader() const; size_t size() const; size_t capacity() const; diff --git a/llvm/include/llvm/CAS/OnDiskGraphDB.h b/llvm/include/llvm/CAS/OnDiskGraphDB.h index 08e28d17668cf..dd5ce1702d40f 100644 --- a/llvm/include/llvm/CAS/OnDiskGraphDB.h +++ b/llvm/include/llvm/CAS/OnDiskGraphDB.h @@ -1,10 +1,17 @@ -//===- OnDiskGraphDB.h ------------------------------------------*- C++ -*-===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +/// \file +/// This declares OnDiskGraphDB, an ondisk CAS database with a fixed length +/// hash. This is the class that implements the database storage scheme without +/// exposing the hashing algorithm. +// +//===----------------------------------------------------------------------===// #ifndef LLVM_CAS_ONDISKGRAPHDB_H #define LLVM_CAS_ONDISKGRAPHDB_H @@ -16,16 +23,13 @@ namespace llvm::cas::ondisk { -/// 8B reference. +/// standard 8B reference inside OnDiskGraphDB. class InternalRef { public: - FileOffset getFileOffset() const { return FileOffset(getRawOffset()); } - + FileOffset getFileOffset() const { return FileOffset(Data); } uint64_t getRawData() const { return Data; } - uint64_t getRawOffset() const { return Data; } static InternalRef getFromRawData(uint64_t Data) { return InternalRef(Data); } - static InternalRef getFromOffset(FileOffset Offset) { return InternalRef(Offset.get()); } @@ -40,19 +44,17 @@ class InternalRef { uint64_t Data; }; -/// 4B reference. +/// compact 4B reference inside OnDiskGraphDB for smaller references. class InternalRef4B { public: FileOffset getFileOffset() const { return FileOffset(Data); } - uint32_t getRawData() const { return Data; } /// Shrink to 4B reference. static std::optional tryToShrink(InternalRef Ref) { - uint64_t Offset = Ref.getRawOffset(); + uint64_t Offset = Ref.getRawData(); if (Offset > UINT32_MAX) return std::nullopt; - return InternalRef4B(Offset); } @@ -148,10 +150,9 @@ class InternalRefArrayRef { if (is4B()) { auto *B = cast(Begin); return ArrayRef((const uint8_t *)B, sizeof(InternalRef4B) * Size); - } else { - auto *B = cast(Begin); - return ArrayRef((const uint8_t *)B, sizeof(InternalRef) * Size); } + auto *B = cast(Begin); + return ArrayRef((const uint8_t *)B, sizeof(InternalRef) * Size); } InternalRefArrayRef(std::nullopt_t = std::nullopt) { @@ -172,8 +173,6 @@ class InternalRefArrayRef { size_t Size = 0; }; -struct OnDiskContent; - /// Reference to a node. The node's data may not be stored in the database. /// An \p ObjectID instance can only be used with the \p OnDiskGraphDB instance /// it came from. \p ObjectIDs from different \p OnDiskGraphDB instances are not @@ -217,6 +216,7 @@ class ObjectHandle { uint64_t Opaque; }; +/// Iterator for ObjectID. class object_refs_iterator : public iterator_facade_base { @@ -294,6 +294,7 @@ class OnDiskGraphDB { /// \returns the data part of the provided object handle. ArrayRef getObjectData(ObjectHandle Node) const; + /// \returns the object referenced by the provided object handle. object_refs_range getObjectRefs(ObjectHandle Node) const { InternalRefArrayRef Refs = getInternalRefs(Node); return make_range(Refs.begin(), Refs.end()); @@ -315,6 +316,13 @@ class OnDiskGraphDB { /// Hashing function type for validation. using HashingFuncT = function_ref>, ArrayRef, SmallVectorImpl &)>; + + /// Validate the OnDiskGraphDB. + /// + /// \param Deep if true, rehash all the objects to make sure no data + /// corruption in stored object, otherwise just validate the structure of + /// CAS database. + /// \param Hasher is the hashing function used for objects inside CAS. Error validate(bool Deep, HashingFuncT Hasher) const; /// How to fault-in nodes if an upstream database is used. @@ -347,9 +355,8 @@ class OnDiskGraphDB { ~OnDiskGraphDB(); private: + /// Forward declaration for a proxy for an ondisk index record. struct IndexProxy; - class TempFile; - class MappedTempFile; enum class ObjectPresence { Missing, @@ -357,9 +364,11 @@ class OnDiskGraphDB { OnlyInUpstreamDB, }; + // Check if object exists and if it is on upstream only. Expected getObjectPresence(ObjectID Ref, bool CheckUpstream) const; + // \returns true if object can be found in database. bool containsObject(ObjectID Ref, bool CheckUpstream) const { auto Presence = getObjectPresence(Ref, CheckUpstream); if (!Presence) { @@ -379,46 +388,56 @@ class OnDiskGraphDB { /// When \p load is called for a node that doesn't exist, this function tries /// to load it from the upstream store and copy it to the primary one. Expected> faultInFromUpstream(ObjectID PrimaryID); + + /// Import the entire tree from upstream with \param UpstreamNode as root. Error importFullTree(ObjectID PrimaryID, ObjectHandle UpstreamNode); + /// Import only the \param UpstreamNode. Error importSingleNode(ObjectID PrimaryID, ObjectHandle UpstreamNode); + /// Found the IndexProxy for the hash. Expected indexHash(ArrayRef Hash); + /// Get path for creating standalone data file. + void getStandalonePath(StringRef FileSuffix, const IndexProxy &I, + SmallVectorImpl &Path) const; + /// Create a standalone leaf file. Error createStandaloneLeaf(IndexProxy &I, ArrayRef Data); - Expected createTempFile(StringRef FinalPath, uint64_t Size); - - OnDiskContent getContentFromHandle(ObjectHandle H) const; - + /// @name Helper functions for internal data structures. + /// @{ static InternalRef getInternalRef(ObjectID Ref) { return InternalRef::getFromRawData(Ref.getOpaqueData()); } + static ObjectID getExternalReference(InternalRef Ref) { return ObjectID::fromOpaqueData(Ref.getRawData()); } static ObjectID getExternalReference(const IndexProxy &I); - void getStandalonePath(StringRef FileSuffix, const IndexProxy &I, - SmallVectorImpl &Path) const; + static InternalRef makeInternalRef(FileOffset IndexOffset); Expected> getDigest(InternalRef Ref) const; + ArrayRef getDigest(const IndexProxy &I) const; Expected getIndexProxyFromRef(InternalRef Ref) const; - static InternalRef makeInternalRef(FileOffset IndexOffset); - IndexProxy getIndexProxyFromPointer(OnDiskTrieRawHashMap::ConstOnDiskPtr P) const; InternalRefArrayRef getInternalRefs(ObjectHandle Node) const; + /// @} - void recordStandaloneSizeIncrease(size_t SizeIncrease); + /// Get the atomic variable that keeps track of the standalone data storage size. + std::atomic &standaloneStorageSize() const; - std::atomic &getStandaloneStorageSize(); + /// Increase the standalone data size. + void recordStandaloneSizeIncrease(size_t SizeIncrease); + /// Get the standalone data size. uint64_t getStandaloneStorageSize() const; + // Private constructor. OnDiskGraphDB(StringRef RootPath, OnDiskTrieRawHashMap Index, OnDiskDataAllocator DataPool, std::unique_ptr UpstreamDB, FaultInPolicy Policy, @@ -434,14 +453,19 @@ class OnDiskGraphDB { /// Data type is DataRecordHandle. OnDiskDataAllocator DataPool; - void *StandaloneData; // a StandaloneDataMap. + // a StandaloneDataMap. + void *StandaloneData; + // Path to the root directory. std::string RootPath; - /// Optional on-disk store to be used for faulting-in nodes. + // Optional on-disk store to be used for faulting-in nodes. std::unique_ptr UpstreamDB; + + // The policy used to fault in data from upstream. FaultInPolicy FIPolicy; + // Debug Logger. std::shared_ptr Logger; }; diff --git a/llvm/include/llvm/CAS/OnDiskKeyValueDB.h b/llvm/include/llvm/CAS/OnDiskKeyValueDB.h index ad02e99d8fae0..706369348f834 100644 --- a/llvm/include/llvm/CAS/OnDiskKeyValueDB.h +++ b/llvm/include/llvm/CAS/OnDiskKeyValueDB.h @@ -1,10 +1,16 @@ -//===- OnDiskKeyValueDB.h ---------------------------------------*- C++ -*-===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +/// \file +/// This declares OnDiskKeyValueDB, a key value storage database of fixed size +/// key and value. +// +//===----------------------------------------------------------------------===// #ifndef LLVM_CAS_ONDISKKEYVALUEDB_H #define LLVM_CAS_ONDISKKEYVALUEDB_H @@ -35,9 +41,7 @@ class OnDiskKeyValueDB { Expected>> get(ArrayRef Key); /// \returns Total size of stored data. - size_t getStorageSize() const { - return Cache.size(); - } + size_t getStorageSize() const { return Cache.size(); } /// \returns The precentage of space utilization of hard space limits. /// @@ -60,7 +64,10 @@ class OnDiskKeyValueDB { StringRef ValueName, size_t ValueSize, std::shared_ptr Logger = nullptr); - using CheckValueT = function_ref)>; + /// Validate the storage with a callback \p CheckValue to check the stored + /// value. + using CheckValueT = + function_ref Data)>; Error validate(CheckValueT CheckValue) const; private: diff --git a/llvm/include/llvm/Config/llvm-config.h.cmake b/llvm/include/llvm/Config/llvm-config.h.cmake index 39136bc45c292..6488d6c01b5c6 100644 --- a/llvm/include/llvm/Config/llvm-config.h.cmake +++ b/llvm/include/llvm/Config/llvm-config.h.cmake @@ -146,4 +146,7 @@ coverage bugs, and to 0 otherwise. */ #cmakedefine01 LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN +/* Define to 1 to enable LLVM OnDisk Content Addressable Storage */ +#cmakedefine01 LLVM_ENABLE_ONDISK_CAS + #endif diff --git a/llvm/lib/CAS/CMakeLists.txt b/llvm/lib/CAS/CMakeLists.txt index 5f60702529ac9..e43ccfe5e14cf 100644 --- a/llvm/lib/CAS/CMakeLists.txt +++ b/llvm/lib/CAS/CMakeLists.txt @@ -1,7 +1,3 @@ -if (LLVM_ENABLE_ONDISK_CAS) - add_definitions(-DLLVM_ENABLE_ONDISK_CAS=1) -endif() - add_llvm_component_library(LLVMCAS ActionCache.cpp ActionCaches.cpp diff --git a/llvm/lib/CAS/OnDiskCommon.cpp b/llvm/lib/CAS/OnDiskCommon.cpp index a54e1e68a85dd..9b716345fa2fa 100644 --- a/llvm/lib/CAS/OnDiskCommon.cpp +++ b/llvm/lib/CAS/OnDiskCommon.cpp @@ -7,13 +7,10 @@ //===----------------------------------------------------------------------===// #include "OnDiskCommon.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Config/config.h" #include "llvm/Support/Error.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Process.h" -#include #include -#include #include #if __has_include() @@ -29,6 +26,10 @@ #include #endif +#if __has_include() +#include // statfs +#endif + using namespace llvm; static uint64_t OnDiskCASMaxMappingSize = 0; @@ -117,21 +118,22 @@ cas::ondisk::tryLockFileThreadSafe(int FD, std::chrono::milliseconds Timeout, #endif } -Expected cas::ondisk::preallocateFileTail(int FD, size_t CurrentSize, size_t NewSize) { +Expected cas::ondisk::preallocateFileTail(int FD, size_t CurrentSize, + size_t NewSize) { auto CreateError = [&](std::error_code EC) -> Expected { if (EC == std::errc::not_supported) // Ignore ENOTSUP in case the filesystem cannot preallocate. return NewSize; #if defined(HAVE_POSIX_FALLOCATE) - if (EC == std::errc::invalid_argument && - CurrentSize < NewSize && // len > 0 + if (EC == std::errc::invalid_argument && CurrentSize < NewSize && // len > 0 NewSize < std::numeric_limits::max()) // 0 <= offset, len < max // Prior to 2024, POSIX required EINVAL for cases that should be ENOTSUP, // so handle it the same as above if it is not one of the other ways to // get EINVAL. return NewSize; #endif - return createStringError(EC, "failed to allocate to CAS file: " + EC.message()); + return createStringError(EC, + "failed to allocate to CAS file: " + EC.message()); }; #if defined(HAVE_POSIX_FALLOCATE) // Note: posix_fallocate returns its error directly, not via errno. @@ -156,6 +158,24 @@ Expected cas::ondisk::preallocateFileTail(int FD, size_t CurrentSize, si assert(CurrentSize + FAlloc.fst_bytesalloc >= NewSize); return CurrentSize + FAlloc.fst_bytesalloc; #else - return NewSize; // Pretend it worked. + (void)CreateError; // Silence unused variable. + return NewSize; // Pretend it worked. +#endif +} + +bool cas::ondisk::useSmallMappingSize(const Twine &P) { + // Add exceptions to use small database file here. +#if defined(__APPLE__) && __has_include() + // macOS tmpfs does not support sparse tails. + SmallString<128> PathStorage; + StringRef Path = P.toNullTerminatedStringRef(PathStorage); + struct statfs StatFS; + if (statfs(Path.data(), &StatFS) != 0) + return false; + + if (strcmp(StatFS.f_fstypename, "tmpfs") == 0) + return true; #endif + // Default to use regular datbase file. + return false; } diff --git a/llvm/lib/CAS/OnDiskCommon.h b/llvm/lib/CAS/OnDiskCommon.h index c2754349a9ef7..090ea28de9706 100644 --- a/llvm/lib/CAS/OnDiskCommon.h +++ b/llvm/lib/CAS/OnDiskCommon.h @@ -16,6 +16,10 @@ namespace llvm::cas::ondisk { +/// The version for all the ondisk database files. It needs to be bumped when +/// compatibility breaking changes are introduced. +constexpr StringLiteral CASVersion = "v1"; + /// Retrieves an overridden maximum mapping size for CAS files, if any, /// speicified by LLVM_CAS_MAX_MAPPING_SIZE in the environment or set by /// `setMaxMappingSize()`. If the value from environment is unreadable, returns @@ -27,11 +31,16 @@ Expected> getOverriddenMaxMappingSize(); /// created. Set value 0 to use default size. void setMaxMappingSize(uint64_t Size); +/// Use small file mapping for ondisk databases created in \p Path. +/// +/// For some file system that doesn't support sparse file, use a smaller file +/// mapping to avoid consuming too much disk space on creation. +bool useSmallMappingSize(const Twine &Path); + /// Thread-safe alternative to \c sys::fs::lockFile. This does not support all /// the platforms that \c sys::fs::lockFile does, so keep it in the CAS library /// for now. -std::error_code lockFileThreadSafe( - int FD, llvm::sys::fs::LockKind Kind = llvm::sys::fs::LockKind::Exclusive); +std::error_code lockFileThreadSafe(int FD, llvm::sys::fs::LockKind Kind); /// Thread-safe alternative to \c sys::fs::unlockFile. This does not support all /// the platforms that \c sys::fs::lockFile does, so keep it in the CAS library @@ -51,7 +60,8 @@ std::error_code tryLockFileThreadSafe( /// \c std::errc::no_space_on_device are detected before we write data. /// /// \returns the new size of the file, or an \c Error. -Expected preallocateFileTail(int FD, size_t CurrentSize, size_t NewSize); +Expected preallocateFileTail(int FD, size_t CurrentSize, + size_t NewSize); } // namespace llvm::cas::ondisk diff --git a/llvm/lib/CAS/OnDiskDataAllocator.cpp b/llvm/lib/CAS/OnDiskDataAllocator.cpp index e0615376a7c1e..6b7c70c8403f1 100644 --- a/llvm/lib/CAS/OnDiskDataAllocator.cpp +++ b/llvm/lib/CAS/OnDiskDataAllocator.cpp @@ -187,7 +187,7 @@ Expected> OnDiskDataAllocator::get(FileOffset Offset, return ArrayRef{Impl->File.getRegion().data() + Offset.get(), Size}; } -MutableArrayRef OnDiskDataAllocator::getUserHeader() { +MutableArrayRef OnDiskDataAllocator::getUserHeader() const { return Impl->Store.getUserHeader(); } @@ -224,7 +224,9 @@ Expected> OnDiskDataAllocator::get(FileOffset Offset, "OnDiskDataAllocator is not supported"); } -MutableArrayRef OnDiskDataAllocator::getUserHeader() { return {}; } +MutableArrayRef OnDiskDataAllocator::getUserHeader() const { + return {}; +} size_t OnDiskDataAllocator::size() const { return 0; } size_t OnDiskDataAllocator::capacity() const { return 0; } diff --git a/llvm/lib/CAS/OnDiskGraphDB.cpp b/llvm/lib/CAS/OnDiskGraphDB.cpp index 2592cbfc65641..997fc225129af 100644 --- a/llvm/lib/CAS/OnDiskGraphDB.cpp +++ b/llvm/lib/CAS/OnDiskGraphDB.cpp @@ -1,4 +1,4 @@ -//===- OnDiskGraphDB.cpp ----------------------------------------*- C++ -*-===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -6,50 +6,48 @@ // //===----------------------------------------------------------------------===// // -// On-disk CAS nodes database, independent of a particular hashing algorithm. -// -// Here's a top-level description of the current layout (could expose or make -// this configurable in the future). -// -// Files, each with a prefix set by \a FilePrefix: -// -// - db/.index: a file for the "index" table, named by \a -// IndexTableName and managed by \a HashMappedTrie. The contents are 8B -// that are accessed atomically, describing the object kind and where/how -// it's stored (including an optional file offset). See \a TrieRecord for -// more details. -// - db/.data: a file for the "data" table, named by \a -// DataPoolTableName and managed by \a DataStore. New objects within -// TrieRecord::MaxEmbeddedSize are inserted here as \a -// TrieRecord::StorageKind::DataPool. -// - db/..data: a file storing an object outside the main -// "data" table, named by its offset into the "index" table, with the -// format of \a TrieRecord::StorageKind::Standalone. -// - db/..leaf: a file storing a leaf node outside the -// main "data" table, named by its offset into the "index" table, with -// the format of \a TrieRecord::StorageKind::StandaloneLeaf. -// - db/..leaf+0: a file storing a leaf object outside the -// main "data" table, named by its offset into the "index" table, with -// the format of \a TrieRecord::StorageKind::StandaloneLeaf0. -// -// The "index", and "data" tables could be stored in a single file, -// (using a root record that points at the two types of stores), but splitting -// the files seems more convenient for now. -// -// ObjectID: this is a pointer to Trie record -// -// ObjectHandle: this is a pointer to Data record -// -// Eventually: consider creating a StringPool for strings instead of using -// RecordDataStore table. -// - Lookup by prefix tree -// - Store by suffix tree +/// \file +/// This file implements OnDiskGraphDB, an on-disk CAS nodes database, +/// independent of a particular hashing algorithm. It only needs to be +/// configured for the hash size and controls the schema of the storage. +/// +/// OnDiskGraphDB defines: +/// +/// - How the data are stored inside database, either as a standalone file, or +/// allocated inside a datapool. +/// - How references to other objects inside the same database is stored. They +/// are stored as internal references, instead of full hash value to save +/// space. +/// - How to chain databases together and import objects from upstream +/// databases. +/// +/// Here's a top-level description of the current layout: +/// +/// - db/index.: a file for the "index" table, named by \a +/// IndexTableName and managed by \a TrieRawHashMap. The contents are 8B +/// that are accessed atomically, describing the object kind and where/how +/// it's stored (including an optional file offset). See \a TrieRecord for +/// more details. +/// - db/data.: a file for the "data" table, named by \a +/// DataPoolTableName and managed by \a DataStore. New objects within +/// TrieRecord::MaxEmbeddedSize are inserted here as \a +/// TrieRecord::StorageKind::DataPool. +/// - db/obj..: a file storing an object outside the main +/// "data" table, named by its offset into the "index" table, with the +/// format of \a TrieRecord::StorageKind::Standalone. +/// - db/leaf..: a file storing a leaf node outside the +/// main "data" table, named by its offset into the "index" table, with +/// the format of \a TrieRecord::StorageKind::StandaloneLeaf. +/// - db/leaf+0..: a file storing a null-terminated leaf object +/// outside the main "data" table, named by its offset into the "index" table, +/// with the format of \a TrieRecord::StorageKind::StandaloneLeaf0. // //===----------------------------------------------------------------------===// #include "llvm/CAS/OnDiskGraphDB.h" #include "OnDiskCommon.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" #include "llvm/CAS/OnDiskCASLogger.h" #include "llvm/CAS/OnDiskDataAllocator.h" @@ -60,7 +58,6 @@ #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" @@ -68,10 +65,6 @@ #include #include -#if __has_include() -#include // statfs -#endif - #define DEBUG_TYPE "on-disk-cas" using namespace llvm; @@ -81,13 +74,12 @@ using namespace llvm::cas::ondisk; static constexpr StringLiteral IndexTableName = "llvm.cas.index"; static constexpr StringLiteral DataPoolTableName = "llvm.cas.data"; -static constexpr StringLiteral IndexFile = "index"; -static constexpr StringLiteral DataPoolFile = "data"; +static constexpr StringLiteral IndexFilePrefix = "index."; +static constexpr StringLiteral DataPoolFilePrefix = "data."; -static constexpr StringLiteral FilePrefix = "v11."; -static constexpr StringLiteral FileSuffixData = ".data"; -static constexpr StringLiteral FileSuffixLeaf = ".leaf"; -static constexpr StringLiteral FileSuffixLeaf0 = ".leaf+0"; +static constexpr StringLiteral FilePrefixObject = "obj."; +static constexpr StringLiteral FilePrefixLeaf = "leaf."; +static constexpr StringLiteral FilePrefixLeaf0 = "leaf+0."; static Error createCorruptObjectError(Expected> ID) { if (!ID) @@ -108,32 +100,32 @@ class TrieRecord { /// Unknown object. Unknown = 0, - /// vX.data: main pool, full DataStore record. + /// data.vX: main pool, full DataStore record. DataPool = 1, - /// vX..data: standalone, with a full DataStore record. + /// obj..vX: standalone, with a full DataStore record. Standalone = 10, - /// vX..leaf: standalone, just the data. File contents + /// leaf..vX: standalone, just the data. File contents /// exactly the data content and file size matches the data size. No refs. StandaloneLeaf = 11, - /// vX..leaf+0: standalone, just the data plus an + /// leaf+0..vX: standalone, just the data plus an /// extra null character ('\0'). File size is 1 bigger than the data size. /// No refs. StandaloneLeaf0 = 12, }; - static StringRef getStandaloneFileSuffix(StorageKind SK) { + static StringRef getStandaloneFilePrefix(StorageKind SK) { switch (SK) { default: llvm_unreachable("Expected standalone storage kind"); case TrieRecord::StorageKind::Standalone: - return FileSuffixData; - case TrieRecord::StorageKind::StandaloneLeaf0: - return FileSuffixLeaf0; + return FilePrefixObject; case TrieRecord::StorageKind::StandaloneLeaf: - return FileSuffixLeaf; + return FilePrefixLeaf; + case TrieRecord::StorageKind::StandaloneLeaf0: + return FilePrefixLeaf0; } } @@ -230,6 +222,7 @@ struct DataRecordHandle { 0, "Not enough bits"); + // layout of the DataRecordHandle and how to decode it. struct LayoutFlags { NumRefsFlags NumRefs; DataSizeFlags DataSize; @@ -351,15 +344,18 @@ struct DataRecordHandle { const Header *H = nullptr; }; +/// Proxy for any on-disk object or raw data. +struct OnDiskContent { + std::optional Record; + std::optional> Bytes; +}; + +// Data loaded inside the memory from standalone file. class StandaloneDataInMemory { public: OnDiskContent getContent() const; - /// FIXME: Should be mapped_file_region instead of MemoryBuffer to drop a - /// layer of indirection. - std::unique_ptr Region; - TrieRecord::StorageKind SK; - StandaloneDataInMemory(std::unique_ptr Region, + StandaloneDataInMemory(std::unique_ptr Region, TrieRecord::StorageKind SK) : Region(std::move(Region)), SK(SK) { #ifndef NDEBUG @@ -376,16 +372,20 @@ class StandaloneDataInMemory { assert(IsStandalone); #endif } + +private: + std::unique_ptr Region; + TrieRecord::StorageKind SK; }; -/// Container for "big" objects mapped in separately. +/// Container to lookup loaded standalone objects. template class StandaloneDataMap { static_assert(isPowerOf2_64(NumShards), "Expected power of 2"); public: - const StandaloneDataInMemory &insert(ArrayRef Hash, - TrieRecord::StorageKind SK, - std::unique_ptr Buffer); + const StandaloneDataInMemory & + insert(ArrayRef Hash, TrieRecord::StorageKind SK, + std::unique_ptr Region); const StandaloneDataInMemory *lookup(ArrayRef Hash) const; bool count(ArrayRef Hash) const { return bool(lookup(Hash)); } @@ -461,12 +461,6 @@ class InternalRefVector { } // namespace -/// Proxy for any on-disk object or raw data. -struct ondisk::OnDiskContent { - std::optional Record; - std::optional> Bytes; -}; - Expected DataRecordHandle::createWithError( function_ref(size_t Size)> Alloc, const Input &I) { Layout L(I); @@ -491,14 +485,14 @@ struct OnDiskGraphDB::IndexProxy { }; template -const StandaloneDataInMemory & -StandaloneDataMap::insert(ArrayRef Hash, TrieRecord::StorageKind SK, - std::unique_ptr Buffer) { +const StandaloneDataInMemory &StandaloneDataMap::insert( + ArrayRef Hash, TrieRecord::StorageKind SK, + std::unique_ptr Region) { auto &S = getShard(Hash); std::lock_guard Lock(S.Mutex); auto &V = S.Map[Hash.data()]; if (!V) - V = std::make_unique(std::move(Buffer), SK); + V = std::make_unique(std::move(Region), SK); return *V; } @@ -513,12 +507,14 @@ StandaloneDataMap::lookup(ArrayRef Hash) const { return &*I->second; } +namespace { + /// Copy of \a sys::fs::TempFile that skips RemoveOnSignal, which is too /// expensive to register/unregister at this rate. /// /// FIXME: Add a TempFileManager that maintains a thread-safe list of open temp /// files and has a signal handler registerd that removes them all. -class OnDiskGraphDB::TempFile { +class TempFile { bool Done = false; TempFile(StringRef Name, int FD, OnDiskCASLogger *Logger) : TmpName(std::string(Name)), FD(FD), Logger(Logger) {} @@ -552,7 +548,7 @@ class OnDiskGraphDB::TempFile { ~TempFile() { consumeError(discard()); } }; -class OnDiskGraphDB::MappedTempFile { +class MappedTempFile { public: char *data() const { return Map.data(); } size_t size() const { return Map.size(); } @@ -576,8 +572,9 @@ class OnDiskGraphDB::MappedTempFile { TempFile Temp; sys::fs::mapped_file_region Map; }; +} // namespace -Error OnDiskGraphDB::TempFile::discard() { +Error TempFile::discard() { Done = true; if (FD != -1) { sys::fs::file_t File = sys::fs::convertFDToNativeFile(FD); @@ -600,7 +597,7 @@ Error OnDiskGraphDB::TempFile::discard() { return Error::success(); } -Error OnDiskGraphDB::TempFile::keep(const Twine &Name) { +Error TempFile::keep(const Twine &Name) { assert(!Done); Done = true; // Always try to close and rename. @@ -620,8 +617,8 @@ Error OnDiskGraphDB::TempFile::keep(const Twine &Name) { return errorCodeToError(RenameEC); } -Expected -OnDiskGraphDB::TempFile::create(const Twine &Model, OnDiskCASLogger *Logger) { +Expected TempFile::create(const Twine &Model, + OnDiskCASLogger *Logger) { int FD; SmallString<128> ResultPath; if (std::error_code EC = sys::fs::createUniqueFile(Model, FD, ResultPath)) @@ -962,7 +959,7 @@ Error OnDiskGraphDB::validate(bool Deep, HashingFuncT Hasher) const { case TrieRecord::StorageKind::StandaloneLeaf: case TrieRecord::StorageKind::StandaloneLeaf0: SmallString<256> Path; - getStandalonePath(TrieRecord::getStandaloneFileSuffix(D.SK), *I, Path); + getStandalonePath(TrieRecord::getStandaloneFilePrefix(D.SK), *I, Path); // If need to validate the content of the file later, just load the // buffer here. Otherwise, just check the existance of the file. if (Deep) { @@ -1183,8 +1180,28 @@ ArrayRef OnDiskGraphDB::getDigest(const IndexProxy &I) const { return I.Hash; } +static OnDiskContent getContentFromHandle(const OnDiskDataAllocator &DataPool, + ObjectHandle OH) { + auto getInternalHandle = [](ObjectHandle Handle) -> InternalHandle { + uint64_t Data = Handle.getOpaqueData(); + if (Data & 1) + return InternalHandle(*reinterpret_cast( + Data & (-1ULL << 1))); + return InternalHandle(Data); + }; + + InternalHandle Handle = getInternalHandle(OH); + if (Handle.SDIM) + return Handle.SDIM->getContent(); + + auto DataHandle = cantFail( + DataRecordHandle::getFromDataPool(DataPool, Handle.getAsFileOffset())); + assert(DataHandle.getData().end()[0] == 0 && "Null termination"); + return OnDiskContent{DataHandle, std::nullopt}; +} + ArrayRef OnDiskGraphDB::getObjectData(ObjectHandle Node) const { - OnDiskContent Content = getContentFromHandle(Node); + OnDiskContent Content = getContentFromHandle(DataPool, Node); if (Content.Bytes) return *Content.Bytes; assert(Content.Record && "Expected record or bytes"); @@ -1193,7 +1210,7 @@ ArrayRef OnDiskGraphDB::getObjectData(ObjectHandle Node) const { InternalRefArrayRef OnDiskGraphDB::getInternalRefs(ObjectHandle Node) const { if (std::optional Record = - getContentFromHandle(Node).Record) + getContentFromHandle(DataPool, Node).Record) return Record->getRefs(); return std::nullopt; } @@ -1242,15 +1259,27 @@ OnDiskGraphDB::load(ObjectID ExternalRef) { // suitably 0-padded. Requiring null-termination here would be too expensive // for extremely large objects that happen to be page-aligned. SmallString<256> Path; - getStandalonePath(TrieRecord::getStandaloneFileSuffix(Object.SK), *I, Path); - ErrorOr> OwnedBuffer = MemoryBuffer::getFile( - Path, /*IsText=*/false, /*RequiresNullTerminator=*/false); - if (!OwnedBuffer) + getStandalonePath(TrieRecord::getStandaloneFilePrefix(Object.SK), *I, Path); + + auto File = sys::fs::openNativeFileForRead(Path); + if (!File) + return createFileError(Path, File.takeError()); + + auto CloseFile = make_scope_exit([&]() { sys::fs::closeFile(*File); }); + + sys::fs::file_status Status; + if (std::error_code EC = sys::fs::status(*File, Status)) return createCorruptObjectError(getDigest(*I)); - return toObjectHandle(InternalHandle( - static_cast(StandaloneData) - ->insert(I->Hash, Object.SK, std::move(*OwnedBuffer)))); + std::error_code EC; + auto Region = std::make_unique( + *File, sys::fs::mapped_file_region::readonly, Status.getSize(), 0, EC); + if (EC) + return createCorruptObjectError(getDigest(*I)); + + return toObjectHandle( + InternalHandle(static_cast(StandaloneData) + ->insert(I->Hash, Object.SK, std::move(Region)))); } Expected OnDiskGraphDB::isMaterialized(ObjectID Ref) { @@ -1293,29 +1322,10 @@ InternalRef OnDiskGraphDB::makeInternalRef(FileOffset IndexOffset) { return InternalRef::getFromOffset(IndexOffset); } -void OnDiskGraphDB::getStandalonePath(StringRef Suffix, const IndexProxy &I, +void OnDiskGraphDB::getStandalonePath(StringRef Prefix, const IndexProxy &I, SmallVectorImpl &Path) const { Path.assign(RootPath.begin(), RootPath.end()); - sys::path::append(Path, FilePrefix + Twine(I.Offset.get()) + Suffix); -} - -OnDiskContent OnDiskGraphDB::getContentFromHandle(ObjectHandle OH) const { - auto getInternalHandle = [](ObjectHandle Handle) -> InternalHandle { - uint64_t Data = Handle.getOpaqueData(); - if (Data & 1) - return InternalHandle(*reinterpret_cast( - Data & (-1ULL << 1))); - return InternalHandle(Data); - }; - - InternalHandle Handle = getInternalHandle(OH); - if (Handle.SDIM) - return Handle.SDIM->getContent(); - - auto DataHandle = cantFail( - DataRecordHandle::getFromDataPool(DataPool, Handle.getAsFileOffset())); - assert(DataHandle.getData().end()[0] == 0 && "Null termination"); - return OnDiskContent{DataHandle, std::nullopt}; + sys::path::append(Path, Prefix + Twine(I.Offset.get()) + "." + CASVersion); } OnDiskContent StandaloneDataInMemory::getContent() const { @@ -1335,24 +1345,23 @@ OnDiskContent StandaloneDataInMemory::getContent() const { } if (Leaf) { - assert(Region->getBuffer().drop_back(Leaf0).end()[0] == 0 && + StringRef Data(Region->data(), Region->size()); + assert(Data.drop_back(Leaf0).end()[0] == 0 && "Standalone node data missing null termination"); - return OnDiskContent{ - std::nullopt, - arrayRefFromStringRef(Region->getBuffer().drop_back(Leaf0))}; + return OnDiskContent{std::nullopt, + arrayRefFromStringRef(Data.drop_back(Leaf0))}; } - DataRecordHandle Record = DataRecordHandle::get(Region->getBuffer().data()); + DataRecordHandle Record = DataRecordHandle::get(Region->data()); assert(Record.getData().end()[0] == 0 && "Standalone object record missing null termination for data"); return OnDiskContent{Record, std::nullopt}; } -Expected -OnDiskGraphDB::createTempFile(StringRef FinalPath, uint64_t Size) { +static Expected +createTempFile(StringRef FinalPath, uint64_t Size, OnDiskCASLogger *Logger) { assert(Size && "Unexpected request for an empty temp file"); - Expected File = - TempFile::create(FinalPath + ".%%%%%%", Logger.get()); + Expected File = TempFile::create(FinalPath + ".%%%%%%", Logger); if (!File) return File.takeError(); @@ -1386,11 +1395,11 @@ Error OnDiskGraphDB::createStandaloneLeaf(IndexProxy &I, ArrayRef Data) { SmallString<256> Path; int64_t FileSize = Data.size() + Leaf0; - getStandalonePath(TrieRecord::getStandaloneFileSuffix(SK), I, Path); + getStandalonePath(TrieRecord::getStandaloneFilePrefix(SK), I, Path); // Write the file. Don't reuse this mapped_file_region, which is read/write. // Let load() pull up one that's read-only. - Expected File = createTempFile(Path, FileSize); + Expected File = createTempFile(Path, FileSize, Logger.get()); if (!File) return File.takeError(); assert(File->size() == (uint64_t)FileSize); @@ -1453,10 +1462,10 @@ Error OnDiskGraphDB::store(ObjectID ID, ArrayRef Refs, std::optional File; std::optional FileSize; auto AllocStandaloneFile = [&](size_t Size) -> Expected { - getStandalonePath(TrieRecord::getStandaloneFileSuffix( + getStandalonePath(TrieRecord::getStandaloneFilePrefix( TrieRecord::StorageKind::Standalone), *I, Path); - if (Error E = createTempFile(Path, Size).moveInto(File)) + if (Error E = createTempFile(Path, Size, Logger.get()).moveInto(File)) return std::move(E); assert(File->size() == Size); FileSize = Size; @@ -1539,10 +1548,10 @@ Error OnDiskGraphDB::store(ObjectID ID, ArrayRef Refs, } void OnDiskGraphDB::recordStandaloneSizeIncrease(size_t SizeIncrease) { - getStandaloneStorageSize().fetch_add(SizeIncrease, std::memory_order_relaxed); + standaloneStorageSize().fetch_add(SizeIncrease, std::memory_order_relaxed); } -std::atomic &OnDiskGraphDB::getStandaloneStorageSize() { +std::atomic &OnDiskGraphDB::standaloneStorageSize() const { MutableArrayRef UserHeader = DataPool.getUserHeader(); assert(UserHeader.size() == sizeof(std::atomic)); assert(isAddrAligned(Align(8), UserHeader.data())); @@ -1550,8 +1559,7 @@ std::atomic &OnDiskGraphDB::getStandaloneStorageSize() { } uint64_t OnDiskGraphDB::getStandaloneStorageSize() const { - return const_cast(this)->getStandaloneStorageSize().load( - std::memory_order_relaxed); + return standaloneStorageSize().load(std::memory_order_relaxed); } size_t OnDiskGraphDB::getStorageSize() const { @@ -1564,22 +1572,6 @@ unsigned OnDiskGraphDB::getHardStorageLimitUtilization() const { return std::max(IndexPercent, DataPercent); } -static bool useSmallMappedFiles(const Twine &P) { - // macOS tmpfs does not support sparse tails. -#if defined(__APPLE__) && __has_include() - SmallString<128> PathStorage; - StringRef Path = P.toNullTerminatedStringRef(PathStorage); - struct statfs StatFS; - if (statfs(Path.data(), &StatFS) != 0) - return false; - - if (strcmp(StatFS.f_fstypename, "tmpfs") == 0) - return true; -#endif - - return false; -} - Expected> OnDiskGraphDB::open( StringRef AbsPath, StringRef HashName, unsigned HashByteSize, std::unique_ptr UpstreamDB, @@ -1587,14 +1579,13 @@ Expected> OnDiskGraphDB::open( if (std::error_code EC = sys::fs::create_directories(AbsPath)) return createFileError(AbsPath, EC); - const StringRef Slash = sys::path::get_separator(); constexpr uint64_t MB = 1024ull * 1024ull; constexpr uint64_t GB = 1024ull * 1024ull * 1024ull; uint64_t MaxIndexSize = 12 * GB; uint64_t MaxDataPoolSize = 24 * GB; - if (useSmallMappedFiles(AbsPath)) { + if (useSmallMappingSize(AbsPath)) { MaxIndexSize = 1 * GB; MaxDataPoolSize = 2 * GB; } @@ -1605,22 +1596,26 @@ Expected> OnDiskGraphDB::open( if (*CustomSize) MaxIndexSize = MaxDataPoolSize = **CustomSize; + SmallString<256> IndexPath(AbsPath); + sys::path::append(IndexPath, IndexFilePrefix + CASVersion); std::optional Index; - if (Error E = - OnDiskTrieRawHashMap::create(AbsPath + Slash + FilePrefix + IndexFile, - IndexTableName + "[" + HashName + "]", - HashByteSize * CHAR_BIT, - /*DataSize=*/sizeof(TrieRecord), - MaxIndexSize, /*MinFileSize=*/MB, Logger) - .moveInto(Index)) + if (Error E = OnDiskTrieRawHashMap::create( + IndexPath, IndexTableName + "[" + HashName + "]", + HashByteSize * CHAR_BIT, + /*DataSize=*/sizeof(TrieRecord), MaxIndexSize, + /*MinFileSize=*/MB, Logger) + .moveInto(Index)) return std::move(E); uint32_t UserHeaderSize = sizeof(std::atomic); + + SmallString<256> DataPoolPath(AbsPath); + sys::path::append(DataPoolPath, DataPoolFilePrefix + CASVersion); std::optional DataPool; StringRef PolicyName = Policy == FaultInPolicy::SingleNode ? "single" : "full"; if (Error E = OnDiskDataAllocator::create( - AbsPath + Slash + FilePrefix + DataPoolFile, + DataPoolPath, DataPoolTableName + "[" + HashName + "]" + PolicyName, MaxDataPoolSize, /*MinFileSize=*/MB, UserHeaderSize, Logger, [](void *UserHeaderPtr) { @@ -1630,8 +1625,8 @@ Expected> OnDiskGraphDB::open( return std::move(E); if (DataPool->getUserHeader().size() != UserHeaderSize) return createStringError(llvm::errc::argument_out_of_domain, - "unexpected user header in '" + AbsPath + Slash + - FilePrefix + DataPoolFile + "'"); + "unexpected user header in '" + DataPoolPath + + "'"); return std::unique_ptr( new OnDiskGraphDB(AbsPath, std::move(*Index), std::move(*DataPool), @@ -1648,7 +1643,7 @@ OnDiskGraphDB::OnDiskGraphDB(StringRef RootPath, OnDiskTrieRawHashMap Index, FIPolicy(Policy), Logger(std::move(Logger)) { /// Lifetime for "big" objects not in DataPool. /// - /// NOTE: Could use ThreadSafeHashMappedTrie here. For now, doing something + /// NOTE: Could use ThreadSafeTrieRawHashMap here. For now, doing something /// simpler on the assumption there won't be much contention since most data /// is not big. If there is contention, and we've already fixed ObjectProxy /// object handles to be cheap enough to use consistently, the fix might be diff --git a/llvm/lib/CAS/OnDiskKeyValueDB.cpp b/llvm/lib/CAS/OnDiskKeyValueDB.cpp index 16dda5a5bd9fb..11d0680d3e23b 100644 --- a/llvm/lib/CAS/OnDiskKeyValueDB.cpp +++ b/llvm/lib/CAS/OnDiskKeyValueDB.cpp @@ -5,6 +5,17 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +/// \file +/// This file implements OnDiskKeyValueDB, an ondisk key value database. +/// +/// The KeyValue database file is named `actions.` inside the CAS +/// directory. The database stores a mapping between a fixed-sized key and a +/// fixed-sized value, where the size of key and value can be configured when +/// opening the database. +/// +// +//===----------------------------------------------------------------------===// #include "llvm/CAS/OnDiskKeyValueDB.h" #include "OnDiskCommon.h" @@ -18,8 +29,7 @@ using namespace llvm; using namespace llvm::cas; using namespace llvm::cas::ondisk; -static constexpr StringLiteral ActionCacheFile = "actions"; -static constexpr StringLiteral FilePrefix = "v6."; +static constexpr StringLiteral ActionCacheFile = "actions."; Expected> OnDiskKeyValueDB::put(ArrayRef Key, ArrayRef Value) { @@ -57,7 +67,7 @@ OnDiskKeyValueDB::open(StringRef Path, StringRef HashName, unsigned KeySize, return createFileError(Path, EC); SmallString<256> CachePath(Path); - sys::path::append(CachePath, FilePrefix + ActionCacheFile); + sys::path::append(CachePath, ActionCacheFile + CASVersion); constexpr uint64_t MB = 1024ull * 1024ull; constexpr uint64_t GB = 1024ull * 1024ull * 1024ull; @@ -96,7 +106,7 @@ Error OnDiskKeyValueDB::validate(CheckValueT CheckValue) const { if (Record.Data.size() != ValueSize) return formatError("wrong cache value size"); - if (!isAligned(Align(8), Record.Data.size())) + if (!isAddrAligned(Align(8), Record.Data.data())) return formatError("wrong cache value alignment"); if (CheckValue) return CheckValue(Offset, Record.Data); diff --git a/llvm/test/CAS/logging.test b/llvm/test/CAS/logging.test index 47de3c7d0fcd1..bad9919cc46d4 100644 --- a/llvm/test/CAS/logging.test +++ b/llvm/test/CAS/logging.test @@ -8,29 +8,29 @@ RUN: FileCheck %s --input-file %t/cas/v1.log RUN: FileCheck %s --input-file %t/cas/v1.log --check-prefix=STANDALONE -// CHECK: resize mapped file '{{.*}}v{{[0-9]+}}.index' -// CHECK: mmap '{{.*}}v{{[0-9]+}}.index' [[INDEX:0x[0-9a-f]+]] -// CHECK: resize mapped file '{{.*}}v{{[0-9]+}}.data' -// CHECK: mmap '{{.*}}v{{[0-9]+}}.data' [[DATA:0x[0-9a-f]+]] -// CHECK: resize mapped file '{{.*}}v{{[0-9]+}}.actions' -// CHECK: mmap '{{.*}}v{{[0-9]+}}.actions' [[ACTIONS:0x[0-9a-f]+]] +// CHECK: resize mapped file '{{.*}}index.v{{[0-9]+}}' +// CHECK: mmap '{{.*}}index.v{{[0-9]+}}' [[INDEX:0x[0-9a-f]+]] +// CHECK: resize mapped file '{{.*}}data.v{{[0-9]+}}' +// CHECK: mmap '{{.*}}data.v{{[0-9]+}}' [[DATA:0x[0-9a-f]+]] +// CHECK: resize mapped file '{{.*}}actions.v{{[0-9]+}}' +// CHECK: mmap '{{.*}}actions.v{{[0-9]+}}' [[ACTIONS:0x[0-9a-f]+]] // store input/a contents into the datapool // CHECK: create record region=[[INDEX]] offset=[[INPUT_A_OFF:0x[0-9a-f]+]] hash=9b096cd140f119 // CHECK: cmpxcgh subtrie region=[[INDEX]] offset={{.*}} slot={{.*}} expected=0x0 new=[[INPUT_A_OFF]] prev=0x0 // CHECK: alloc [[DATA]] -// CHECK: resize mapped file '{{.*}}v{{[0-9]+}}.actions' -// CHECK: close mmap '{{.*}}v{{[0-9]+}}.actions' -// CHECK: resize mapped file '{{.*}}v{{[0-9]+}}.data' -// CHECK: close mmap '{{.*}}v{{[0-9]+}}.data' -// CHECK: resize mapped file '{{.*}}v{{[0-9]+}}.index' -// CHECK: close mmap '{{.*}}v{{[0-9]+}}.index' +// CHECK: resize mapped file '{{.*}}actions.v{{[0-9]+}}' +// CHECK: close mmap '{{.*}}actions.v{{[0-9]+}}' +// CHECK: resize mapped file '{{.*}}data.v{{[0-9]+}}' +// CHECK: close mmap '{{.*}}data.v{{[0-9]+}}' +// CHECK: resize mapped file '{{.*}}index.v{{[0-9]+}}' +// CHECK: close mmap '{{.*}}index.v{{[0-9]+}}' // CHECK: validate-if-needed '{{.*}}cas' boot=[[BOOT:[0-9]+]] last-valid=0 check-hash=1 allow-recovery=0 force=0 llvm-cas={{.*}}llvm-cas // CHECK: validate-if-needed '{{.*}}cas' boot=[[BOOT]] last-valid=[[BOOT]] check-hash=0 allow-recovery=1 force=1 llvm-cas={{.*}}llvm-cas -// STANDALONE: standalone file create '[[PATH:.*v[0-9]+.[0-9a-f]*.leaf]].[[SUFFIX:[0-9a-f]*]]' +// STANDALONE: standalone file create '[[PATH:.*leaf.[0-9a-f]*.v[0-9]+]].[[SUFFIX:[0-9a-f]*]]' // STANDALONE: standalone file rename '[[PATH]].[[SUFFIX]]' to '[[PATH]]' //--- input/a diff --git a/llvm/test/CAS/validate-if-needed.test b/llvm/test/CAS/validate-if-needed.test index b3960b40186a9..a5b6163357191 100644 --- a/llvm/test/CAS/validate-if-needed.test +++ b/llvm/test/CAS/validate-if-needed.test @@ -1,6 +1,6 @@ RUN: rm -rf %t && mkdir %t RUN: llvm-cas --cas %t/cas --ingest %S/Inputs > %t/cas.id -RUN: mv %t/cas/v1.1/v11.data %t/cas/v1.1/v11.data.bak +RUN: mv %t/cas/v1.1/data.v1 %t/cas/v1.1/data.v1.bak # INVALID: bad record # VALID: validated successfully @@ -12,7 +12,7 @@ RUN: not llvm-cas --cas %t/cas --validate-if-needed 2>&1 | FileCheck %s -check-p RUN: not llvm-cas --cas %t/cas --validate-if-needed 2>&1 | FileCheck %s -check-prefix=INVALID # Validation happens once per boot. -RUN: mv %t/cas/v1.1/v11.data.bak %t/cas/v1.1/v11.data +RUN: mv %t/cas/v1.1/data.v1.bak %t/cas/v1.1/data.v1 RUN: llvm-cas --cas %t/cas --validate-if-needed | FileCheck %s -check-prefix=VALID RUN: llvm-cas --cas %t/cas --validate-if-needed | FileCheck %s -check-prefix=SKIPPED # Wrong timestamp triggers re-validation. @@ -20,7 +20,7 @@ RUN: echo '123' > %t/cas/v1.validation RUN: llvm-cas --cas %t/cas --validate-if-needed | FileCheck %s -check-prefix=VALID RUN: llvm-cas --cas %t/cas --validate-if-needed | FileCheck %s -check-prefix=SKIPPED # Skipped validation does not catch errors. -RUN: mv %t/cas/v1.1/v11.data %t/cas/v1.1/v11.data.bak +RUN: mv %t/cas/v1.1/data.v1 %t/cas/v1.1/data.v1.bak RUN: llvm-cas --cas %t/cas --validate-if-needed | FileCheck %s -check-prefix=SKIPPED # Unless forced. @@ -33,7 +33,7 @@ RUN: llvm-cas --cas %t/cas --validate-if-needed --allow-recovery | FileCheck %s RUN: llvm-cas --cas %t/cas --validate-if-needed --force | FileCheck %s -check-prefix=VALID RUN: rm -rf %t/cas/v1.1 RUN: cp -r %t/cas/corrupt.0.v1.1 %t/cas/v1.1 -RUN: mv %t/cas/v1.1/v11.data %t/cas/v1.1/v11.data.bak +RUN: mv %t/cas/v1.1/data.v1 %t/cas/v1.1/data.v1.bak RUN: llvm-cas --cas %t/cas --validate-if-needed --allow-recovery --force | FileCheck %s -check-prefix=RECOVERED RUN: ls %t/cas/corrupt.1.v1.1 diff --git a/llvm/test/tools/llvm-cas/validation.test b/llvm/test/tools/llvm-cas/validation.test index c9cd8189a6e93..9f6ea37c602f0 100644 --- a/llvm/test/tools/llvm-cas/validation.test +++ b/llvm/test/tools/llvm-cas/validation.test @@ -12,7 +12,7 @@ RUN: llvm-cas --cas %t/cas --ingest %S/Inputs > %t/cas.id RUN: llvm-cas --cas %t/cas --validate RUN: llvm-cas --cas %t/cas --validate --check-hash -RUN: rm %t/cas/v1.1/v11.data +RUN: rm %t/cas/v1.1/data.v1 RUN: not llvm-cas --cas %t/cas --validate RUN: not llvm-cas --cas %t/cas --validate --check-hash @@ -28,5 +28,5 @@ RUN: llvm-cas --cas %t/ac --put-cache-key @%t/abc.casid @%t/empty.casid RUN: llvm-cas --cas %t/ac --validate # Note: records are 40 bytes (32 hash bytes + 8 byte value), so trim the last # allocated record, leaving it invalid. -RUN: truncate -s -40 %t/ac/v1.1/v6.actions +RUN: truncate -s -40 %t/ac/v1.1/actions.v1 RUN: not llvm-cas --cas %t/ac --validate diff --git a/llvm/tools/llvm-cas-test/llvm-cas-test.cpp b/llvm/tools/llvm-cas-test/llvm-cas-test.cpp index 842ba2fb86580..45364b838e6bc 100644 --- a/llvm/tools/llvm-cas-test/llvm-cas-test.cpp +++ b/llvm/tools/llvm-cas-test/llvm-cas-test.cpp @@ -285,7 +285,7 @@ static int checkLockFiles() { ExitOnError ExitOnErr("llvm-cas-test: check-lock-files: "); SmallString<128> DataPoolPath(CASPath); - sys::path::append(DataPoolPath, "v1.1/v11.data"); + sys::path::append(DataPoolPath, "v1.1/data.v1"); auto OpenCASAndGetDataPoolSize = [&]() -> Expected { auto Result = createOnDiskUnifiedCASDatabases(CASPath); diff --git a/llvm/unittests/CAS/ActionCacheTest.cpp b/llvm/unittests/CAS/ActionCacheTest.cpp index 4b1a72b10aa56..0981f9bd88493 100644 --- a/llvm/unittests/CAS/ActionCacheTest.cpp +++ b/llvm/unittests/CAS/ActionCacheTest.cpp @@ -14,7 +14,7 @@ #include "llvm/CAS/ActionCache.h" #include "CASTestConfig.h" #include "llvm/CAS/ObjectStore.h" -#include "llvm/Support/FileSystem.h" +#include "llvm/Config/llvm-config.h" #include "llvm/Testing/Support/Error.h" #include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" @@ -74,8 +74,7 @@ TEST_P(CASTest, ActionCacheRewrite) { ASSERT_THAT_ERROR(Cache->put(*ID1, *ID1), Succeeded()); } -#if LLVM_ENABLE_ONDISK_CAS -TEST(OnDiskActionCache, ActionCacheResultInvalid) { +TEST_F(OnDiskCASTest, ActionCacheResultInvalid) { unittest::TempDir Temp("on-disk-cache", /*Unique=*/true); std::unique_ptr CAS1 = createInMemoryCAS(); std::unique_ptr CAS2 = createInMemoryCAS(); @@ -85,12 +84,7 @@ TEST(OnDiskActionCache, ActionCacheResultInvalid) { ASSERT_THAT_ERROR(CAS1->createProxy({}, "2").moveInto(ID2), Succeeded()); ASSERT_THAT_ERROR(CAS2->createProxy({}, "1").moveInto(ID3), Succeeded()); -#if !defined(LLVM_ENABLE_ONDISK_CAS) - // The following won't work without LLVM_ENABLE_ONDISK_CAS enabled. - // TODO: enable LLVM_ENABLE_ONDISK_CAS on windows - return; -#endif - +#if LLVM_ENABLE_ONDISK_CAS std::unique_ptr Cache1 = cantFail(createOnDiskActionCache(Temp.path())); // Test put and get. @@ -109,8 +103,8 @@ TEST(OnDiskActionCache, ActionCacheResultInvalid) { ASSERT_FALSE(CAS2->getReference(*Result2)); // Write a different value will cause error. ASSERT_THAT_ERROR(Cache2->put(*ID3, *ID3), Failed()); -} #endif +} TEST_P(CASTest, ActionCacheAsync) { std::shared_ptr CAS = createObjectStore(); diff --git a/llvm/unittests/CAS/BuiltinUnifiedCASDatabasesTest.cpp b/llvm/unittests/CAS/BuiltinUnifiedCASDatabasesTest.cpp index 165ae6cf1e04a..19522e9372d85 100644 --- a/llvm/unittests/CAS/BuiltinUnifiedCASDatabasesTest.cpp +++ b/llvm/unittests/CAS/BuiltinUnifiedCASDatabasesTest.cpp @@ -1,4 +1,13 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + #include "llvm/CAS/BuiltinUnifiedCASDatabases.h" +#include "CASTestConfig.h" #include "llvm/CAS/ActionCache.h" #include "llvm/CAS/ObjectStore.h" #include "llvm/Testing/Support/Error.h" @@ -8,9 +17,7 @@ using namespace llvm; using namespace llvm::cas; -#if LLVM_ENABLE_ONDISK_CAS -TEST(BuiltinUnifiedCASDatabases, - MaterializationCheckPreventsGarbageCollection) { +TEST_F(OnDiskCASTest, UnifiedCASMaterializationCheckPreventsGarbageCollection) { unittest::TempDir Temp("on-disk-unified-cas", /*Unique=*/true); auto WithCAS = [&](llvm::function_ref Action) { @@ -58,4 +65,3 @@ TEST(BuiltinUnifiedCASDatabases, ASSERT_TRUE(IsMaterialized); }); } -#endif diff --git a/llvm/unittests/CAS/CASConfigurationTest.cpp b/llvm/unittests/CAS/CASConfigurationTest.cpp index d6e584b1a2761..93cdd574721b7 100644 --- a/llvm/unittests/CAS/CASConfigurationTest.cpp +++ b/llvm/unittests/CAS/CASConfigurationTest.cpp @@ -1,4 +1,4 @@ -//===- CASConfiguration.cpp -----------------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/unittests/CAS/CASFileSystemTest.cpp b/llvm/unittests/CAS/CASFileSystemTest.cpp index 2c7b5f7534d8e..ad6342baa5499 100644 --- a/llvm/unittests/CAS/CASFileSystemTest.cpp +++ b/llvm/unittests/CAS/CASFileSystemTest.cpp @@ -1,4 +1,4 @@ -//===- CASFileSystemTest.cpp ----------------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/unittests/CAS/CASOutputBackendTest.cpp b/llvm/unittests/CAS/CASOutputBackendTest.cpp index bcc90381c35ae..3b4cac7b4ad0e 100644 --- a/llvm/unittests/CAS/CASOutputBackendTest.cpp +++ b/llvm/unittests/CAS/CASOutputBackendTest.cpp @@ -1,4 +1,4 @@ -//===- CASOutputBackendTest.cpp -------------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/unittests/CAS/CASProvidingFileSystemTest.cpp b/llvm/unittests/CAS/CASProvidingFileSystemTest.cpp index a104af9401d8e..a111549a23fa8 100644 --- a/llvm/unittests/CAS/CASProvidingFileSystemTest.cpp +++ b/llvm/unittests/CAS/CASProvidingFileSystemTest.cpp @@ -1,4 +1,4 @@ -//===- CASProvidingFileSystemTest.cpp -------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/unittests/CAS/CASTestConfig.cpp b/llvm/unittests/CAS/CASTestConfig.cpp index cab359430c0de..61b56b32a5a85 100644 --- a/llvm/unittests/CAS/CASTestConfig.cpp +++ b/llvm/unittests/CAS/CASTestConfig.cpp @@ -1,4 +1,4 @@ -//===- CASTestConfig.cpp --------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,6 +11,8 @@ #include "llvm/CAS/ObjectStore.h" #include "llvm/RemoteCachingService/RemoteCachingService.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Testing/Support/Error.h" +#include "llvm/Config/llvm-config.h" #include "gtest/gtest.h" #include diff --git a/llvm/unittests/CAS/CASTestConfig.h b/llvm/unittests/CAS/CASTestConfig.h index 22c2945fb578a..fab420dce8cb1 100644 --- a/llvm/unittests/CAS/CASTestConfig.h +++ b/llvm/unittests/CAS/CASTestConfig.h @@ -11,7 +11,6 @@ #include "llvm/CAS/ActionCache.h" #include "llvm/CAS/ObjectStore.h" -#include "llvm/Testing/Support/Error.h" #include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" #include @@ -34,6 +33,16 @@ struct CASTestingEnv { void setMaxOnDiskCASMappingSize(); +// Test fixture for on-disk data base tests. +class OnDiskCASTest : public ::testing::Test { +protected: + void SetUp() override { + // Use a smaller database size for testing to conserve disk space. + setMaxOnDiskCASMappingSize(); + } +}; + +// Parametered test fixture for ObjectStore and ActionCache tests. class CASTest : public testing::TestWithParam> { protected: diff --git a/llvm/unittests/CAS/CMakeLists.txt b/llvm/unittests/CAS/CMakeLists.txt index 97117f08fdbd8..8fe6840f9fea7 100644 --- a/llvm/unittests/CAS/CMakeLists.txt +++ b/llvm/unittests/CAS/CMakeLists.txt @@ -1,12 +1,38 @@ -if (LLVM_ENABLE_ONDISK_CAS) - add_definitions(-DLLVM_ENABLE_ONDISK_CAS=1) -endif() - if (LLVM_CAS_ENABLE_REMOTE_CACHE) add_definitions(-DLLVM_CAS_ENABLE_REMOTE_CACHE=1) set(ADDITIONAL_CAS_TEST_DEPS "RemoteCacheServer") endif() +set(ONDISK_CAS_TEST_SOURCES + BuiltinUnifiedCASDatabasesTest.cpp + CASConfigurationTest.cpp + OnDiskCASLoggerTest.cpp + OnDiskGraphDBTest.cpp + OnDiskDataAllocatorTest.cpp + OnDiskKeyValueDBTest.cpp + OnDiskTrieRawHashMapTest.cpp + PluginCASTest.cpp + ProgramTest.cpp + UnifiedOnDiskCacheTest.cpp + ) + +set(REMOTE_CACHE_TEST_SOURCES + MockGRPCServer.cpp + ) + +set(LLVM_OPTIONAL_SOURCES + ${ONDISK_CAS_TEST_SOURCES} + ${REMOTE_CACHE_TEST_SOURCES} + ) + +if (NOT LLVM_ENABLE_ONDISK_CAS) + unset(ONDISK_CAS_TEST_SOURCES) +endif() + +if (NOT LLVM_CAS_ENABLE_REMOTE_CACHE) + unset(REMOTE_CACHE_TEST_SOURCES) +endif() + set(LLVM_LINK_COMPONENTS Support CAS @@ -17,26 +43,18 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest(CASTests ActionCacheTest.cpp - BuiltinUnifiedCASDatabasesTest.cpp - CASConfigurationTest.cpp CASFileSystemTest.cpp CASTestConfig.cpp CASOutputBackendTest.cpp CASProvidingFileSystemTest.cpp CachingOnDiskFileSystemTest.cpp HierarchicalTreeBuilderTest.cpp - MockGRPCServer.cpp ObjectStoreTest.cpp - OnDiskCASLoggerTest.cpp - OnDiskGraphDBTest.cpp - OnDiskDataAllocatorTest.cpp - OnDiskKeyValueDBTest.cpp - OnDiskTrieRawHashMapTest.cpp - PluginCASTest.cpp - ProgramTest.cpp ThreadSafeAllocatorTest.cpp TreeSchemaTest.cpp - UnifiedOnDiskCacheTest.cpp + + ${ONDISK_CAS_TEST_SOURCES} + ${REMOTE_CACHE_TEST_SOURCES} ) target_link_libraries(CASTests PRIVATE LLVMTestingSupport) diff --git a/llvm/unittests/CAS/CachingOnDiskFileSystemTest.cpp b/llvm/unittests/CAS/CachingOnDiskFileSystemTest.cpp index bb10ad25e3742..1c94b03d65be0 100644 --- a/llvm/unittests/CAS/CachingOnDiskFileSystemTest.cpp +++ b/llvm/unittests/CAS/CachingOnDiskFileSystemTest.cpp @@ -1,4 +1,4 @@ -//===- CachingOnDiskFileSystemTest.cpp ------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,7 +14,6 @@ #include "llvm/Support/Path.h" #include "llvm/Support/PrefixMapper.h" #include "llvm/Support/SourceMgr.h" -#include "llvm/Support/StringSaver.h" #include "llvm/Testing/Support/Error.h" #include "llvm/Testing/Support/SupportHelpers.h" #include "gmock/gmock.h" diff --git a/llvm/unittests/CAS/HierarchicalTreeBuilderTest.cpp b/llvm/unittests/CAS/HierarchicalTreeBuilderTest.cpp index 3372d42b2cbc2..e751d46f825c6 100644 --- a/llvm/unittests/CAS/HierarchicalTreeBuilderTest.cpp +++ b/llvm/unittests/CAS/HierarchicalTreeBuilderTest.cpp @@ -1,4 +1,4 @@ -//===- HierarchicalTreeBuilderTest.cpp ------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/unittests/CAS/MockGRPCServer.cpp b/llvm/unittests/CAS/MockGRPCServer.cpp index 0ea4560c71dce..e46b0435bf7e1 100644 --- a/llvm/unittests/CAS/MockGRPCServer.cpp +++ b/llvm/unittests/CAS/MockGRPCServer.cpp @@ -1,4 +1,4 @@ -//===- MockGRPCServer.cpp -------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/unittests/CAS/ObjectStoreTest.cpp b/llvm/unittests/CAS/ObjectStoreTest.cpp index 5fd301200c5be..b74a011e3532c 100644 --- a/llvm/unittests/CAS/ObjectStoreTest.cpp +++ b/llvm/unittests/CAS/ObjectStoreTest.cpp @@ -1,4 +1,4 @@ -//===- ObjectStoreTest.cpp ------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -348,7 +348,7 @@ TEST_P(CASTest, BlobsBigParallel) { #if LLVM_ENABLE_ONDISK_CAS #ifndef _WIN32 // create_link won't work for directories on Windows -TEST(OnDiskCASTest, BlobsParallelMultiCAS) { +TEST_F(OnDiskCASTest, OnDiskCASBlobsParallelMultiCAS) { // This test intentionally uses symlinked paths to the same CAS to subvert the // shared memory mappings that would normally be created within a single // process. This breaks the lock file guarantees, so we must be careful not @@ -377,7 +377,8 @@ TEST(OnDiskCASTest, BlobsParallelMultiCAS) { uint64_t Size = 1ULL * 1024; ASSERT_NO_FATAL_FAILURE(testBlobsParallel(*CAS1, *CAS2, *CAS3, *CAS4, Size)); } -TEST(OnDiskCASTest, BlobsBigParallelMultiCAS) { + +TEST_F(OnDiskCASTest, OnDiskCASBlobsBigParallelMultiCAS) { // See comment in BlobsParallelMultiCAS. unittest::TempDir Temp("on-disk-cas", /*Unique=*/true); ASSERT_EQ(sys::fs::create_directory(Temp.path("real_cas")), @@ -405,8 +406,7 @@ TEST(OnDiskCASTest, BlobsBigParallelMultiCAS) { } #endif // _WIN32 -TEST(OnDiskCASTest, DiskSize) { - setMaxOnDiskCASMappingSize(); +TEST_F(OnDiskCASTest, OnDiskCASDiskSize) { unittest::TempDir Temp("on-disk-cas", /*Unique=*/true); std::unique_ptr CAS; ASSERT_THAT_ERROR(createOnDiskCAS(Temp.path()).moveInto(CAS), Succeeded()); @@ -419,7 +419,8 @@ TEST(OnDiskCASTest, DiskSize) { std::error_code EC; for (sys::fs::directory_iterator I(Temp.path(), EC), E; I != E && !EC; I.increment(EC)) { - if (StringRef(I->path()).ends_with(".index")) { + StringRef Filename = sys::path::filename(I->path()); + if (Filename.starts_with("index.") && !Filename.ends_with(".shared")) { FoundIndex = true; ASSERT_TRUE(I->status()); if (Mapped) @@ -427,7 +428,7 @@ TEST(OnDiskCASTest, DiskSize) { else EXPECT_LT(I->status()->getSize(), MaxSize); } - if (StringRef(I->path()).ends_with(".data")) { + if (Filename.starts_with("data.") && !Filename.ends_with(".shared")) { FoundData = true; ASSERT_TRUE(I->status()); if (Mapped) diff --git a/llvm/unittests/CAS/OnDiskCASLoggerTest.cpp b/llvm/unittests/CAS/OnDiskCASLoggerTest.cpp index 84993c8467da1..46f41676d6a6c 100644 --- a/llvm/unittests/CAS/OnDiskCASLoggerTest.cpp +++ b/llvm/unittests/CAS/OnDiskCASLoggerTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/CAS/OnDiskCASLoggerTest.cpp --------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/unittests/CAS/OnDiskCommonUtils.h b/llvm/unittests/CAS/OnDiskCommonUtils.h index d97f766d35d55..5f7e20390719c 100644 --- a/llvm/unittests/CAS/OnDiskCommonUtils.h +++ b/llvm/unittests/CAS/OnDiskCommonUtils.h @@ -1,10 +1,14 @@ -//===- llvm/unittest/CAS/OnDiskCommonUtils.h --------------------*- C++ -*-===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +/// \file Helper function to test OnDiskCASDatabases. +// +//===----------------------------------------------------------------------===// #include "llvm/CAS/BuiltinObjectHasher.h" #include "llvm/CAS/OnDiskGraphDB.h" diff --git a/llvm/unittests/CAS/OnDiskDataAllocatorTest.cpp b/llvm/unittests/CAS/OnDiskDataAllocatorTest.cpp index 299a608e842b2..67fdf70626da1 100644 --- a/llvm/unittests/CAS/OnDiskDataAllocatorTest.cpp +++ b/llvm/unittests/CAS/OnDiskDataAllocatorTest.cpp @@ -8,13 +8,10 @@ #include "llvm/CAS/OnDiskDataAllocator.h" #include "llvm/CAS/MappedFileRegionArena.h" -#include "llvm/Config/llvm-config.h" #include "llvm/Support/Alignment.h" #include "llvm/Testing/Support/Error.h" #include "llvm/Testing/Support/SupportHelpers.h" -#if LLVM_ENABLE_ONDISK_CAS - using namespace llvm; using namespace llvm::cas; @@ -51,5 +48,3 @@ TEST(OnDiskDataAllocatorTest, Allocate) { ASSERT_LE(Allocator->size(), MB); } } - -#endif // LLVM_ENABLE_ONDISK_CAS diff --git a/llvm/unittests/CAS/OnDiskGraphDBTest.cpp b/llvm/unittests/CAS/OnDiskGraphDBTest.cpp index 77e8bd9e89ec2..3da2027ee41f2 100644 --- a/llvm/unittests/CAS/OnDiskGraphDBTest.cpp +++ b/llvm/unittests/CAS/OnDiskGraphDBTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/CAS/OnDiskGraphDBTest.cpp ----------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -12,14 +12,12 @@ #include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" -#if LLVM_ENABLE_ONDISK_CAS - using namespace llvm; using namespace llvm::cas; using namespace llvm::cas::ondisk; using namespace llvm::unittest::cas; -TEST(OnDiskGraphDBTest, Basic) { +TEST_F(OnDiskCASTest, OnDiskGraphDBTest) { unittest::TempDir Temp("ondiskcas", /*Unique=*/true); std::unique_ptr DB; ASSERT_THAT_ERROR( @@ -81,7 +79,7 @@ TEST(OnDiskGraphDBTest, Basic) { EXPECT_EQ(DB->getStorageSize(), StorageSize); } -TEST(OnDiskGraphDBTest, FaultInSingleNode) { +TEST_F(OnDiskCASTest, OnDiskGraphDBFaultInSingleNode) { unittest::TempDir TempUpstream("ondiskcas-upstream", /*Unique=*/true); std::unique_ptr UpstreamDB; ASSERT_THAT_ERROR( @@ -171,7 +169,7 @@ TEST(OnDiskGraphDBTest, FaultInSingleNode) { } } -TEST(OnDiskGraphDBTest, FaultInFullTree) { +TEST_F(OnDiskCASTest, OnDiskGraphDBFaultInFullTree) { unittest::TempDir TempUpstream("ondiskcas-upstream", /*Unique=*/true); std::unique_ptr UpstreamDB; ASSERT_THAT_ERROR( @@ -255,7 +253,7 @@ TEST(OnDiskGraphDBTest, FaultInFullTree) { EXPECT_EQ(PrintedTree, Expected); } -TEST(OnDiskGraphDBTest, FaultInPolicyConflict) { +TEST_F(OnDiskCASTest, OnDiskGraphDBFaultInPolicyConflict) { auto tryFaultInPolicyConflict = [](OnDiskGraphDB::FaultInPolicy Policy1, OnDiskGraphDB::FaultInPolicy Policy2) { unittest::TempDir TempUpstream("ondiskcas-upstream", /*Unique=*/true); @@ -288,7 +286,7 @@ TEST(OnDiskGraphDBTest, FaultInPolicyConflict) { } #if defined(EXPENSIVE_CHECKS) -TEST(OnDiskGraphDBTest, SpaceLimit) { +TEST_F(OnDiskCASTest, OnDiskGraphDBSpaceLimit) { setMaxOnDiskCASMappingSize(); unittest::TempDir Temp("ondiskcas", /*Unique=*/true); std::unique_ptr DB; @@ -312,4 +310,3 @@ TEST(OnDiskGraphDBTest, SpaceLimit) { EXPECT_GE(DB->getHardStorageLimitUtilization(), 99U); } #endif -#endif // LLVM_ENABLE_ONDISK_CAS diff --git a/llvm/unittests/CAS/OnDiskKeyValueDBTest.cpp b/llvm/unittests/CAS/OnDiskKeyValueDBTest.cpp index 3edc5e77f64fb..89c03b890a488 100644 --- a/llvm/unittests/CAS/OnDiskKeyValueDBTest.cpp +++ b/llvm/unittests/CAS/OnDiskKeyValueDBTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/CAS/OnDiskKeyValueDBTest.cpp -------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,19 +7,18 @@ //===----------------------------------------------------------------------===// #include "llvm/CAS/OnDiskKeyValueDB.h" +#include "CASTestConfig.h" #include "OnDiskCommonUtils.h" #include "llvm/Testing/Support/Error.h" #include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" -#if LLVM_ENABLE_ONDISK_CAS - using namespace llvm; using namespace llvm::cas; using namespace llvm::cas::ondisk; using namespace llvm::unittest::cas; -TEST(OnDiskKeyValueDBTest, Basic) { +TEST_F(OnDiskCASTest, OnDiskKeyValueDBTest) { unittest::TempDir Temp("ondiskkv", /*Unique=*/true); std::unique_ptr DB; ASSERT_THAT_ERROR(OnDiskKeyValueDB::open(Temp.path(), "blake3", @@ -27,7 +26,6 @@ TEST(OnDiskKeyValueDBTest, Basic) { sizeof(ValueType)) .moveInto(DB), Succeeded()); - { std::optional> Val; ASSERT_THAT_ERROR(DB->get(digest("hello")).moveInto(Val), Succeeded()); @@ -49,6 +47,31 @@ TEST(OnDiskKeyValueDBTest, Basic) { EXPECT_TRUE(Val.has_value()); EXPECT_EQ(*Val, ArrayRef(ValW)); } -} -#endif // LLVM_ENABLE_ONDISK_CAS + // Validate + { + auto ValidateFunc = [](FileOffset Offset, ArrayRef Data) -> Error { + EXPECT_EQ(Data.size(), sizeof(ValueType)); + return Error::success(); + }; + ASSERT_THAT_ERROR(DB->validate(ValidateFunc), Succeeded()); + } + + // Size + { + size_t InitSize = DB->getStorageSize(); + unsigned InitPrecent = DB->getHardStorageLimitUtilization(); + + // Insert a lot of entries. + for (unsigned I = 0; I < 1024 * 100; ++I) { + std::string Index = Twine(I).str(); + ArrayRef Val; + ASSERT_THAT_ERROR( + DB->put(digest(Index), valueFromString(Index)).moveInto(Val), + Succeeded()); + } + + EXPECT_GT(DB->getStorageSize(), InitSize); + EXPECT_GT(DB->getHardStorageLimitUtilization(), InitPrecent); + } +} diff --git a/llvm/unittests/CAS/OnDiskTrieRawHashMapTest.cpp b/llvm/unittests/CAS/OnDiskTrieRawHashMapTest.cpp index 8465a71019f90..8f91c2bb7764e 100644 --- a/llvm/unittests/CAS/OnDiskTrieRawHashMapTest.cpp +++ b/llvm/unittests/CAS/OnDiskTrieRawHashMapTest.cpp @@ -8,18 +8,14 @@ #include "llvm/CAS/OnDiskTrieRawHashMap.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/Config/llvm-config.h" #include "llvm/Support/Alignment.h" #include "llvm/Testing/Support/Error.h" #include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" -#if LLVM_ENABLE_ONDISK_CAS using namespace llvm; using namespace llvm::cas; -namespace { - struct OnDiskTrieRawHashMapTestFixture : public ::testing::TestWithParam { static constexpr size_t MB = 1024u * 1024u; @@ -214,7 +210,3 @@ TEST(OnDiskTrieRawHashMapTest, OutOfSpace) { ASSERT_THAT_ERROR(Trie->insert({Hash0, Data0v1}).moveInto(Insertion), Failed()); } - -} // namespace - -#endif // LLVM_ENABLE_ONDISK_CAS diff --git a/llvm/unittests/CAS/PluginCASTest.cpp b/llvm/unittests/CAS/PluginCASTest.cpp index 6584e6694e60a..99a3fd293c7bd 100644 --- a/llvm/unittests/CAS/PluginCASTest.cpp +++ b/llvm/unittests/CAS/PluginCASTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/CAS/PluginCASTest.cpp --------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -14,8 +14,6 @@ #include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" -#if LLVM_ENABLE_ONDISK_CAS - using namespace llvm; using namespace llvm::cas; @@ -104,5 +102,3 @@ TEST(PluginCASTest, isMaterialized) { EXPECT_TRUE(IsMaterialized); } } - -#endif // LLVM_ENABLE_ONDISK_CAS diff --git a/llvm/unittests/CAS/ProgramTest.cpp b/llvm/unittests/CAS/ProgramTest.cpp index e6fc03988e8de..078cf54bf569d 100644 --- a/llvm/unittests/CAS/ProgramTest.cpp +++ b/llvm/unittests/CAS/ProgramTest.cpp @@ -8,7 +8,6 @@ #include "llvm/Support/Program.h" #include "llvm/CAS/MappedFileRegionArena.h" -#include "llvm/Config/llvm-config.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/ExponentialBackoff.h" #include "llvm/Support/FileSystem.h" @@ -26,8 +25,6 @@ extern char **environ; using namespace llvm; using namespace llvm::cas; -#if LLVM_ENABLE_ONDISK_CAS - extern const char *TestMainArgv0; static char ProgramID = 0; @@ -241,5 +238,3 @@ TEST_F(CASProgramTest, MappedFileRegionArenaSizeTest) { sys::fs::remove(FilePath); sys::fs::remove_directories(sys::path::parent_path(FilePath)); } - -#endif // LLVM_ENABLE_ONDISK_CAS diff --git a/llvm/unittests/CAS/ThreadSafeAllocatorTest.cpp b/llvm/unittests/CAS/ThreadSafeAllocatorTest.cpp index c0952b5ce25df..a35c4943f06f8 100644 --- a/llvm/unittests/CAS/ThreadSafeAllocatorTest.cpp +++ b/llvm/unittests/CAS/ThreadSafeAllocatorTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/CAS/ThreadSafeAllocatorTest.cpp ----------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. diff --git a/llvm/unittests/CAS/TreeSchemaTest.cpp b/llvm/unittests/CAS/TreeSchemaTest.cpp index 7916867ef87fd..a136b23167d7d 100644 --- a/llvm/unittests/CAS/TreeSchemaTest.cpp +++ b/llvm/unittests/CAS/TreeSchemaTest.cpp @@ -1,4 +1,4 @@ -//===- TreeSchemaTest.cpp -------------------------------------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,10 +9,7 @@ #include "llvm/CAS/TreeSchema.h" #include "llvm/CAS/HierarchicalTreeBuilder.h" #include "llvm/CAS/ObjectStore.h" -#include "llvm/Config/llvm-config.h" -#include "llvm/Support/FileSystem.h" #include "llvm/Testing/Support/Error.h" -#include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" using namespace llvm; diff --git a/llvm/unittests/CAS/UnifiedOnDiskCacheTest.cpp b/llvm/unittests/CAS/UnifiedOnDiskCacheTest.cpp index a5af073f5d812..e25288a26eb92 100644 --- a/llvm/unittests/CAS/UnifiedOnDiskCacheTest.cpp +++ b/llvm/unittests/CAS/UnifiedOnDiskCacheTest.cpp @@ -1,4 +1,4 @@ -//===- llvm/unittest/CAS/UnifiedOnDiskCacheTest.cpp -----------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -7,13 +7,12 @@ //===----------------------------------------------------------------------===// #include "llvm/CAS/UnifiedOnDiskCache.h" +#include "CASTestConfig.h" #include "OnDiskCommonUtils.h" #include "llvm/Testing/Support/Error.h" #include "llvm/Testing/Support/SupportHelpers.h" #include "gtest/gtest.h" -#if LLVM_ENABLE_ONDISK_CAS - using namespace llvm; using namespace llvm::cas; using namespace llvm::cas::ondisk; @@ -43,7 +42,7 @@ static Expected countFileSizes(StringRef Path) { return TotalSize; } -TEST(UnifiedOnDiskCacheTest, Basic) { +TEST_F(OnDiskCASTest, UnifiedOnDiskCacheTest) { unittest::TempDir Temp("ondisk-unified", /*Unique=*/true); std::unique_ptr UniDB; @@ -190,5 +189,3 @@ TEST(UnifiedOnDiskCacheTest, Basic) { EXPECT_FALSE(Val.has_value()); } } - -#endif // LLVM_ENABLE_ONDISK_CAS