blob: 2523a78b2e1f6c6c4b5d99d5378ccd55d4edaa44 [file] [log] [blame]
// 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_