| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIB_INSPECT_CPP_VMO_STATE_H_ |
| #define LIB_INSPECT_CPP_VMO_STATE_H_ |
| |
| #include <lib/fit/function.h> |
| #include <lib/fit/thread_safety.h> |
| #include <lib/fpromise/promise.h> |
| #include <lib/fpromise/sequencer.h> |
| #include <lib/inspect/cpp/inspector.h> |
| #include <lib/inspect/cpp/vmo/block.h> |
| #include <lib/inspect/cpp/vmo/heap.h> |
| #include <lib/inspect/cpp/vmo/types.h> |
| #include <lib/stdcompat/optional.h> |
| #include <lib/stdcompat/span.h> |
| #include <lib/stdcompat/string_view.h> |
| #include <zircon/types.h> |
| |
| #include <cstdint> |
| #include <iterator> |
| #include <map> |
| #include <mutex> |
| #include <string> |
| #include <unordered_map> |
| #include <utility> |
| |
| namespace inspect { |
| |
| namespace internal { |
| |
| class AutoGenerationIncrement; |
| |
| // |State| wraps a |Heap| and implements the Inspect VMO API on top of |
| // that heap. This class contains the low-level operations necessary to |
| // deal with the various Inspect types and wrappers to denote ownership of |
| // those values. |
| // |
| // This class should not be used directly, prefer to use |Inspector|. |
| // |State| writes version 2 of the Inspect Format. |
| class State final { |
| public: |
| // Create a new State wrapping the given Heap. |
| // On failure, returns nullptr. |
| static std::shared_ptr<State> Create(std::unique_ptr<Heap> heap); |
| |
| // Create a new State wrapping a new heap of the given size. |
| // On failure, returns an empty shared_ptr. |
| static std::shared_ptr<State> CreateWithSize(size_t size); |
| |
| // Destructor for State, which performs necessary cleanup. |
| ~State(); |
| |
| // Disallow copy and assign. |
| State(const State&) = delete; |
| State(State&&) = delete; |
| State& operator=(const State&) = delete; |
| State& operator=(State&&) = delete; |
| |
| // Obtain a reference to the wrapped VMO. |
| // This may be duplicated read-only to pass to a reader process. |
| const zx::vmo& GetVmo() const; |
| |
| // Obtain a read-only duplicate of the VMO backing this State. |
| bool DuplicateVmo(zx::vmo* vmo) const; |
| |
| // Obtain a copy-on-write copy of the backing VMO. Generation count |
| // will be `kVmoFrozen`. |
| cpp17::optional<zx::vmo> FrozenVmoCopy() const; |
| |
| // Obtain a copy of the VMO backing this state. |
| // |
| // Returns true on success, false otherwise. |
| bool Copy(zx::vmo* vmo) const; |
| |
| // Obtain a copy of the bytes in the VMO backing this state. |
| // |
| // Returns true on success, false otherwise. |
| bool CopyBytes(std::vector<uint8_t>* out) const; |
| |
| // Create a new |IntProperty| in the Inspect VMO. The returned value releases |
| // the property when destroyed. |
| IntProperty CreateIntProperty(BorrowedStringValue name, BlockIndex parent, int64_t value); |
| |
| // Create a new |UintProperty| in the Inspect VMO. The returned value releases |
| // the property when destroyed. |
| UintProperty CreateUintProperty(BorrowedStringValue name, BlockIndex parent, uint64_t value); |
| |
| // Create a new |DoubleProperty| in the Inspect VMO. The returned value releases |
| // the property when destroyed. |
| DoubleProperty CreateDoubleProperty(BorrowedStringValue name, BlockIndex parent, double value); |
| |
| // Create a new |BoolProperty| in the Inspect VMO. The returned value releases |
| // the property when destroyed. |
| BoolProperty CreateBoolProperty(BorrowedStringValue name, BlockIndex parent, bool value); |
| |
| // Create a new |IntArray| in the Inspect VMO. The returned value releases |
| // the array when destroyed. |
| IntArray CreateIntArray(BorrowedStringValue name, BlockIndex parent, size_t slots, |
| ArrayBlockFormat format); |
| |
| // Create a new |StringArray| in the Inspect VMO. The returned value releases |
| // the array when destroyed. |
| StringArray CreateStringArray(BorrowedStringValue name, BlockIndex parent, size_t slots, |
| ArrayBlockFormat format); |
| |
| // Create a new |UintArray| in the Inspect VMO. The returned value releases |
| // the array when destroyed. |
| UintArray CreateUintArray(BorrowedStringValue name, BlockIndex parent, size_t slots, |
| ArrayBlockFormat format); |
| |
| // Create a new |DoubleArray| in the Inspect VMO. The returned value releases |
| // the array when destroyed. |
| DoubleArray CreateDoubleArray(BorrowedStringValue name, BlockIndex parent, size_t slots, |
| ArrayBlockFormat format); |
| |
| // Create a new |StringProperty| in the Inspect VMO. The returned value releases |
| // the property when destroyed. |
| StringProperty CreateStringProperty(BorrowedStringValue name, BlockIndex parent, |
| const std::string& value); |
| |
| // Create a new |ByteVectorProperty| in the Inspect VMO. The returned value releases |
| // the property when destroyed. |
| ByteVectorProperty CreateByteVectorProperty(BorrowedStringValue name, BlockIndex parent, |
| cpp20::span<const uint8_t> value); |
| |
| // Create a new [Link] in the Inspect VMO. The returned node releases the link when destroyed. |
| // |
| // A Link is a low-level reference to a new Inspector linked off of the one managed by this |
| // state. A Link alone is not sufficient to populate the linked tree, see CreateLazyNode and |
| // CreateLazyValues. |
| Link CreateLink(BorrowedStringValue name, BlockIndex parent, BorrowedStringValue content, |
| LinkBlockDisposition disposition); |
| |
| // Create a new |Node| in the Inspect VMO. Nodes are refcounted such that values nested under the |
| // node remain valid until all such values values are destroyed. |
| Node CreateNode(BorrowedStringValue name, BlockIndex parent); |
| |
| // Create and allocate, if necessary, a StringReference block. The reference count will be 1 |
| // if the block is new, and incremented if it already existed. |
| zx_status_t CreateAndIncrementStringReference(BorrowedStringValue value, BlockIndex* out); |
| |
| // Create a special root |Node| in the Inspect VMO. This node is not backed by any storage, |
| // rather it allows clients to use the |Node| interface to add properties and children directly |
| // to the root of the VMO. |
| Node CreateRootNode(); |
| |
| // Create a new |LazyNode| with a new named |Link| that calls the given callback with child |
| // disposition. |
| LazyNode CreateLazyNode(BorrowedStringValue name, BlockIndex parent, LazyNodeCallbackFn callback); |
| |
| // Create a new |LazyNode| with a new named |Link| that calls the given callback with inline |
| // disposition. |
| LazyNode CreateLazyValues(BorrowedStringValue name, BlockIndex parent, |
| LazyNodeCallbackFn callback); |
| // Setters for various property types |
| void SetIntProperty(IntProperty* property, int64_t value); |
| void SetUintProperty(UintProperty* property, uint64_t value); |
| void SetDoubleProperty(DoubleProperty* property, double value); |
| void SetBoolProperty(BoolProperty* property, bool value); |
| void SetIntArray(IntArray* array, size_t index, int64_t value); |
| void SetUintArray(UintArray* array, size_t index, uint64_t value); |
| void SetDoubleArray(DoubleArray* array, size_t index, double value); |
| void SetStringArray(StringArray* array, size_t index, BorrowedStringValue value); |
| void SetStringProperty(StringProperty* property, const std::string& value); |
| void SetByteVectorProperty(ByteVectorProperty* property, cpp20::span<const uint8_t> value); |
| |
| // Adders for various property types |
| void AddIntProperty(IntProperty* property, int64_t value); |
| void AddUintProperty(UintProperty* property, uint64_t value); |
| void AddDoubleProperty(DoubleProperty* property, double value); |
| void AddIntArray(IntArray* array, size_t index, int64_t value); |
| void AddUintArray(UintArray* array, size_t index, uint64_t value); |
| void AddDoubleArray(DoubleArray* array, size_t index, double value); |
| |
| // Subtractors for various property types |
| void SubtractIntProperty(IntProperty* property, int64_t value); |
| void SubtractUintProperty(UintProperty* property, uint64_t value); |
| void SubtractDoubleProperty(DoubleProperty* property, double value); |
| void SubtractIntArray(IntArray* array, size_t index, int64_t value); |
| void SubtractUintArray(UintArray* array, size_t index, uint64_t value); |
| void SubtractDoubleArray(DoubleArray* array, size_t index, double value); |
| |
| // Free various entities |
| void FreeIntProperty(IntProperty* property); |
| void FreeUintProperty(UintProperty* property); |
| void FreeDoubleProperty(DoubleProperty* property); |
| void FreeBoolProperty(BoolProperty* property); |
| void FreeIntArray(IntArray* array); |
| void FreeUintArray(UintArray* array); |
| void FreeDoubleArray(DoubleArray* array); |
| void FreeStringArray(StringArray* array); |
| void FreeStringProperty(StringProperty* property); |
| void FreeByteVectorProperty(ByteVectorProperty* property); |
| void FreeLink(Link* link); |
| void FreeNode(Node* node); |
| void FreeLazyNode(LazyNode* lazy_node); |
| void ReleaseStringReference(BlockIndex index); |
| |
| // Use transactions to ensure that all operations appear in the same generation. |
| void BeginTransaction(); |
| void EndTransaction(); |
| |
| // Get the names of all links in this state. |
| std::vector<std::string> GetLinkNames() const; |
| |
| // Call a specific link by name, return a promise for the Inspector it produces. |
| fpromise::promise<Inspector> CallLinkCallback(const std::string& name); |
| |
| // Create a unique name for children in this State. |
| // |
| // Returned strings are guaranteed to be unique and will start with the given prefix. |
| std::string UniqueName(const std::string& prefix); |
| |
| // Return stats about this state. |
| InspectStats GetStats() const; |
| |
| private: |
| // Holder for a LazyNodeCallbackFn. |
| // |
| // This class ensures that the callback function is only called once at a time, and it allows |
| // future calls to the callback to be cancelled to prevent calling it when the corresponding |
| // LazyNode has been deleted. |
| // |
| // This class is copyable and thread-safe. Each copy refers to the same underlying callback, and |
| // cancelling one copy cancels all copies. |
| class LazyNodeCallbackHolder { |
| public: |
| LazyNodeCallbackHolder() = default; |
| explicit LazyNodeCallbackHolder(LazyNodeCallbackFn callback) |
| : inner_(new Inner(std::move(callback))) {} |
| |
| // This class is copyable but not movable. This ensures LazyNodeCallbackHolder objects are |
| // always in a valid state. |
| LazyNodeCallbackHolder(const LazyNodeCallbackHolder&) = default; |
| LazyNodeCallbackHolder(LazyNodeCallbackHolder&&) = delete; |
| LazyNodeCallbackHolder& operator=(const LazyNodeCallbackHolder&) = default; |
| LazyNodeCallbackHolder& operator=(LazyNodeCallbackHolder&&) = delete; |
| |
| // Cancel and release the callback. Future attempts to call the callback will do nothing. |
| void cancel() { |
| std::lock_guard<std::mutex> lock(inner_->mutex); |
| inner_->callback = {}; |
| } |
| |
| // Call the callback if it is not cancelled. |
| fpromise::promise<Inspector> call() { |
| std::lock_guard<std::mutex> lock(inner_->mutex); |
| if (inner_->callback) { |
| return inner_->callback(); |
| } else { |
| return fpromise::make_result_promise<Inspector>(fpromise::error()); |
| } |
| } |
| |
| private: |
| // Inner structure to share a mutex and a callback. |
| struct Inner { |
| explicit Inner(LazyNodeCallbackFn fn) : callback(std::move(fn)) {} |
| |
| std::mutex mutex; |
| LazyNodeCallbackFn callback FIT_GUARDED(mutex); |
| }; |
| std::shared_ptr<Inner> inner_; |
| }; |
| |
| State(std::unique_ptr<Heap> heap, BlockIndex header); |
| |
| void DecrementParentRefcount(BlockIndex value_index) __TA_REQUIRES(mutex_); |
| |
| // Transaction-conscious helper functions for using AutoGenerationIncrement. |
| // In the event that at |
| // least one transaction is open (indicated by |transaction_count_|), the caller should _not_ |
| // increment the generation counter. These return an empty pointer in that case. |
| std::unique_ptr<AutoGenerationIncrement> MaybeIncrementGeneration() __TA_REQUIRES(mutex_); |
| |
| std::unique_ptr<AutoGenerationIncrement> MaybeFreezeAndIncrementGeneration() const |
| __TA_REQUIRES(mutex_); |
| |
| // Helper method for creating a new VALUE block type. |
| zx_status_t InnerCreateValue(BorrowedStringValue name, BlockType type, BlockIndex parent_index, |
| BlockIndex* out_name, BlockIndex* out_value, |
| size_t min_size_required = kMinOrderSize) __TA_REQUIRES(mutex_); |
| |
| // Helper method to create a new LINK block that calls a callback when followed. |
| LazyNode InnerCreateLazyLink(BorrowedStringValue name, BlockIndex parent, |
| LazyNodeCallbackFn callback, LinkBlockDisposition disposition); |
| |
| // Returns true if the block is an extent, false otherwise. |
| constexpr bool IsExtent(const Block* block) { |
| return block && GetType(block) == BlockType::kExtent; |
| } |
| |
| // Helper to set the value of a string across its extents. |
| std::pair<BlockIndex, zx_status_t> InnerCreateExtentChain(const char* value, size_t length) |
| __TA_REQUIRES(mutex_); |
| |
| // Helper to free all extents for a given first index in the extent chain. |
| void InnerFreeExtentChain(BlockIndex extent_index) __TA_REQUIRES(mutex_); |
| |
| // Helper to create a new name block with the given name. |
| zx_status_t InnerCreateAndIncrementStringReference(BorrowedStringValue name, BlockIndex* out) |
| __TA_REQUIRES(mutex_); |
| |
| // Helper function to create an array with the given name, number of slots, and format. |
| template <typename WrapperType, BlockType BlockTypeValue> |
| WrapperType InnerCreateArray(BorrowedStringValue name, BlockIndex parent, size_t slots, |
| ArrayBlockFormat format); |
| |
| // Helper function to create a property with a byte format. |
| template <typename WrapperType, typename ValueType> |
| WrapperType InnerCreateProperty(BorrowedStringValue name, BlockIndex parent, const char* value, |
| size_t length, PropertyBlockFormat format); |
| |
| template <typename WrapperType> |
| void InnerSetProperty(WrapperType* property, const char* value, size_t length); |
| |
| // Helper function to delete String or ByteVector properties. |
| template <typename WrapperType> |
| void InnerFreePropertyWithExtents(WrapperType* property); |
| |
| // Helper function to set the value of a specific index in an array. |
| template <typename NumericType, typename WrapperType, BlockType BlockTypeValue> |
| void InnerSetArray(WrapperType* property, size_t index, NumericType value); |
| |
| // Helper function to perform an operation on a specific index in an array. |
| // Common operations are std::plus and std::minus. |
| template <typename NumericType, typename WrapperType, BlockType BlockTypeValue, |
| typename Operation> |
| void InnerOperationArray(WrapperType* property, size_t index, NumericType value); |
| |
| // Helper function to free an array type. |
| template <typename WrapperType> |
| void InnerFreeArray(WrapperType* value); |
| |
| // Helper function to generate a unique name for a link. |
| std::string UniqueLinkName(cpp17::string_view prefix); |
| |
| // Create a new |StringReference| in the Inspect VMO, or get the existing index. |
| // If the StringReference is new, it will have a reference count of 0. If it already |
| // existed, the reference count will not be incremented. |
| zx_status_t InnerCreateStringReference(BorrowedStringValue value, BlockIndex* out) |
| __TA_REQUIRES(mutex_); |
| |
| zx_status_t InnerDoStringReferenceAllocations(cpp17::string_view data, BlockIndex* out) |
| __TA_REQUIRES(mutex_); |
| |
| // Free the block pointed to by |index| if and only if it has a reference count of 0. |
| // Attempting to free a reference that has a non-zero reference count is not an error, |
| // and will simply do nothing. |
| void InnerMaybeFreeStringReference(BlockIndex index, Block* block) __TA_REQUIRES(mutex_); |
| |
| void InnerReleaseStringReference(BlockIndex index) __TA_REQUIRES(mutex_); |
| |
| void InnerReadExtents(BlockIndex head_extent, size_t remaining_length, |
| std::vector<uint8_t>* buf) const __TA_REQUIRES(mutex_); |
| |
| zx_status_t WriteStringReferencePayload(Block* block, cpp17::string_view data) |
| __TA_REQUIRES(mutex_); |
| |
| friend cpp17::optional<std::string> TesterLoadStringReference(const State& state, |
| BlockIndex index); |
| |
| // Mutex wrapping all fields in the state. |
| // The mutex is mutable to support locking when reading fields of a |
| // const reference to state. |
| mutable std::mutex mutex_; |
| |
| // Weak pointer reference to this object, used to pass shared pointers to children. |
| std::weak_ptr<State> weak_self_ptr_; |
| |
| // The wrapped |Heap|, protected by the mutex. |
| std::unique_ptr<Heap> heap_ FIT_GUARDED(mutex_); |
| |
| // Map from the key of a linked inspect tree to the callback that populates that tree. |
| // |
| // An ordered map is used to ensure consistent iteration ordering for clients reading this data. |
| std::map<std::string, LazyNodeCallbackHolder> link_callbacks_ FIT_GUARDED(mutex_); |
| |
| // The index for the header block containing the generation count |
| // to increment |
| BlockIndex header_ FIT_GUARDED(mutex_); |
| |
| // The next unique ID to give out from UniqueName. |
| // |
| // Uses the fastest available atomic uint64 type for fetch_and_add. |
| std::atomic_uint_fast64_t next_unique_id_; |
| |
| // Next value to be used as a suffix for links. |
| // |
| // Uses the fastest available atomic uint64 type for fetch_and_add. |
| std::atomic_uint_fast64_t next_unique_link_number_; |
| |
| // Track how many transactions are currently open on this object. When a transaction is open, |
| // all operations will appear as part of the same generation, rather than each operation moving |
| // the generation counter separately. |
| uint32_t transaction_count_ FIT_GUARDED(mutex_); |
| std::unique_ptr<AutoGenerationIncrement> transaction_gen_ FIT_GUARDED(mutex_); |
| |
| // Map StringReference.ID to an index in the VMO and vice-versa. |
| class { |
| public: |
| void Insert(BlockIndex index, uint64_t id) { |
| state_id_to_block_index_.insert({id, index}); |
| block_index_to_state_id_.insert({index, id}); |
| } |
| |
| cpp17::optional<BlockIndex> GetBlockIndex(uint64_t id) { |
| const auto index = state_id_to_block_index_.find(id); |
| if (index == std::cend(state_id_to_block_index_)) { |
| return {}; |
| } |
| return index->second; |
| } |
| |
| cpp17::optional<uint64_t> GetStateId(BlockIndex index) { |
| const auto state_id = block_index_to_state_id_.find(index); |
| if (state_id == std::cend(block_index_to_state_id_)) { |
| return {}; |
| } |
| |
| return state_id->second; |
| } |
| |
| void EraseByIndex(BlockIndex index) { |
| const auto id = GetStateId(index); |
| if (!id.has_value()) { |
| return; |
| } |
| |
| state_id_to_block_index_.erase(*id); |
| block_index_to_state_id_.erase(index); |
| } |
| |
| private: |
| std::unordered_map<uint64_t, BlockIndex> state_id_to_block_index_; |
| std::unordered_map<BlockIndex, uint64_t> block_index_to_state_id_; |
| |
| } string_reference_ids_; |
| }; |
| |
| } // namespace internal |
| } // namespace inspect |
| |
| #endif // LIB_INSPECT_CPP_VMO_STATE_H_ |