blob: 25546dbb182d06a9922ed302c7eafdb6e100d1f5 [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.
#include <lib/fit/bridge.h>
#include <lib/fit/sequencer.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/cpp/vmo/block.h>
#include <lib/inspect/cpp/vmo/state.h>
#include <functional>
#include <sstream>
namespace inspect {
namespace internal {
namespace {
// Helper class to support RAII locking of the generation count.
class AutoGenerationIncrement final {
public:
AutoGenerationIncrement(BlockIndex target, Heap* heap);
~AutoGenerationIncrement();
// Disallow copy assign and move.
AutoGenerationIncrement(AutoGenerationIncrement&&) = delete;
AutoGenerationIncrement(const AutoGenerationIncrement&) = delete;
AutoGenerationIncrement& operator=(AutoGenerationIncrement&&) = delete;
AutoGenerationIncrement& operator=(const AutoGenerationIncrement&) = delete;
private:
// Acquire the generation count lock.
// This consists of atomically incrementing the count using
// acquire-release ordering, ensuring readers see this increment before
// any changes to the buffer.
void Acquire(Block* block);
// Release the generation count lock.
// This consists of atomically incrementing the count using release
// ordering, ensuring readers see this increment after all changes to
// the buffer are committed.
void Release(Block* block);
BlockIndex target_;
Heap* heap_;
};
AutoGenerationIncrement ::AutoGenerationIncrement(BlockIndex target, Heap* heap)
: target_(target), heap_(heap) {
Acquire(heap_->GetBlock(target_));
}
AutoGenerationIncrement::~AutoGenerationIncrement() { Release(heap_->GetBlock(target_)); }
void AutoGenerationIncrement ::Acquire(Block* block) {
uint64_t* ptr = &block->payload.u64;
__atomic_fetch_add(ptr, 1, std::memory_order_acq_rel);
}
void AutoGenerationIncrement::Release(Block* block) {
uint64_t* ptr = &block->payload.u64;
__atomic_fetch_add(ptr, 1, std::memory_order_release);
}
} // namespace
template <typename NumericType, typename WrapperType, BlockType BlockTypeValue>
WrapperType State::InnerCreateArray(const std::string& name, BlockIndex parent, size_t slots,
ArrayBlockFormat format) {
size_t block_size_needed = slots * sizeof(NumericType) + kMinOrderSize;
ZX_DEBUG_ASSERT_MSG(block_size_needed <= kMaxOrderSize,
"The requested array size cannot fit in a block");
if (block_size_needed > kMaxOrderSize) {
return WrapperType();
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
BlockIndex name_index, value_index;
zx_status_t status;
status = InnerCreateValue(name, BlockType::kArrayValue, parent, &name_index, &value_index,
block_size_needed);
if (status != ZX_OK) {
return WrapperType();
}
auto* block = heap_->GetBlock(value_index);
block->payload.u64 = ArrayBlockPayload::EntryType::Make(BlockTypeValue) |
ArrayBlockPayload::Flags::Make(format) |
ArrayBlockPayload::Count::Make(slots);
return WrapperType(weak_self_ptr_.lock(), name_index, value_index);
}
template <typename NumericType, typename WrapperType, BlockType BlockTypeValue>
void State::InnerSetArray(WrapperType* metric, size_t index, NumericType value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_ASSERT(GetType(block) == BlockType::kArrayValue);
auto entry_type = ArrayBlockPayload::EntryType::Get<BlockType>(block->payload.u64);
ZX_ASSERT(entry_type == BlockTypeValue);
auto* slot = GetArraySlot<NumericType>(block, index);
if (slot != nullptr) {
*slot = value;
}
}
template <typename NumericType, typename WrapperType, BlockType BlockTypeValue, typename Operation>
void State::InnerOperationArray(WrapperType* metric, size_t index, NumericType value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_ASSERT(GetType(block) == BlockType::kArrayValue);
auto entry_type = ArrayBlockPayload::EntryType::Get<BlockType>(block->payload.u64);
ZX_ASSERT(entry_type == BlockTypeValue);
auto* slot = GetArraySlot<NumericType>(block, index);
if (slot != nullptr) {
*slot = Operation()(*slot, value);
}
}
template <typename WrapperType>
void State::InnerFreeArray(WrapperType* value) {
ZX_DEBUG_ASSERT_MSG(value->state_.get() == this, "Array being freed from the wrong state");
if (value->state_.get() != this) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
DecrementParentRefcount(value->value_index_);
heap_->Free(value->name_index_);
heap_->Free(value->value_index_);
value->state_ = nullptr;
}
std::shared_ptr<State> State::Create(std::unique_ptr<Heap> heap) {
BlockIndex header;
if (heap->Allocate(sizeof(Block), &header) != ZX_OK) {
return nullptr;
}
ZX_DEBUG_ASSERT_MSG(header == 0, "Header must be at index 0");
if (header != 0) {
return nullptr;
}
auto* block = heap->GetBlock(header);
block->header = HeaderBlockFields::Order::Make(GetOrder(block)) |
HeaderBlockFields::Type::Make(BlockType::kHeader) |
HeaderBlockFields::Version::Make(kVersion);
memcpy(&block->header_data[4], kMagicNumber, 4);
block->payload.u64 = 0;
std::shared_ptr<State> ret(new State(std::move(heap), header));
ret->weak_self_ptr_ = ret;
return ret;
}
std::shared_ptr<State> State::CreateWithSize(size_t size) {
zx::vmo vmo;
if (size == 0 || ZX_OK != zx::vmo::create(size, 0, &vmo)) {
return nullptr;
}
return State::Create(std::make_unique<Heap>(std::move(vmo)));
}
State::~State() { heap_->Free(header_); }
const zx::vmo& State::GetVmo() const {
std::lock_guard<std::mutex> lock(mutex_);
return heap_->GetVmo();
}
bool State::DuplicateVmo(zx::vmo* vmo) const {
std::lock_guard<std::mutex> lock(mutex_);
return ZX_OK == heap_->GetVmo().duplicate(ZX_RIGHTS_BASIC | ZX_RIGHT_READ | ZX_RIGHT_MAP, vmo);
}
bool State::Copy(zx::vmo* vmo) const {
std::lock_guard<std::mutex> lock(mutex_);
size_t size = heap_->size();
if (zx::vmo::create(size, 0, vmo) != ZX_OK) {
return false;
}
if (vmo->write(heap_->data(), 0, size) != ZX_OK) {
return false;
}
return true;
}
bool State::CopyBytes(std::vector<uint8_t>* out) const {
std::lock_guard<std::mutex> lock(mutex_);
size_t size = heap_->size();
if (size == 0) {
return false;
}
out->resize(size);
memcpy(out->data(), heap_->data(), size);
return true;
}
IntProperty State::CreateIntProperty(const std::string& name, BlockIndex parent, int64_t value) {
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
BlockIndex name_index, value_index;
zx_status_t status;
status = InnerCreateValue(name, BlockType::kIntValue, parent, &name_index, &value_index);
if (status != ZX_OK) {
return IntProperty();
}
auto* block = heap_->GetBlock(value_index);
block->payload.i64 = value;
return IntProperty(weak_self_ptr_.lock(), name_index, value_index);
}
UintProperty State::CreateUintProperty(const std::string& name, BlockIndex parent, uint64_t value) {
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
BlockIndex name_index, value_index;
zx_status_t status;
status = InnerCreateValue(name, BlockType::kUintValue, parent, &name_index, &value_index);
if (status != ZX_OK) {
return UintProperty();
}
auto* block = heap_->GetBlock(value_index);
block->payload.u64 = value;
return UintProperty(weak_self_ptr_.lock(), name_index, value_index);
}
DoubleProperty State::CreateDoubleProperty(const std::string& name, BlockIndex parent,
double value) {
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
BlockIndex name_index, value_index;
zx_status_t status;
status = InnerCreateValue(name, BlockType::kDoubleValue, parent, &name_index, &value_index);
if (status != ZX_OK) {
return DoubleProperty();
}
auto* block = heap_->GetBlock(value_index);
block->payload.f64 = value;
return DoubleProperty(weak_self_ptr_.lock(), name_index, value_index);
}
BoolProperty State::CreateBoolProperty(const std::string& name, BlockIndex parent, bool value) {
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
BlockIndex name_index, value_index;
zx_status_t status;
status = InnerCreateValue(name, BlockType::kBoolValue, parent, &name_index, &value_index);
if (status != ZX_OK) {
return BoolProperty();
}
auto* block = heap_->GetBlock(value_index);
block->payload.u64 = value;
return BoolProperty(weak_self_ptr_.lock(), name_index, value_index);
}
IntArray State::CreateIntArray(const std::string& name, BlockIndex parent, size_t slots,
ArrayBlockFormat format) {
return InnerCreateArray<int64_t, IntArray, BlockType::kIntValue>(name, parent, slots, format);
}
UintArray State::CreateUintArray(const std::string& name, BlockIndex parent, size_t slots,
ArrayBlockFormat format) {
return InnerCreateArray<uint64_t, UintArray, BlockType::kUintValue>(name, parent, slots, format);
}
DoubleArray State::CreateDoubleArray(const std::string& name, BlockIndex parent, size_t slots,
ArrayBlockFormat format) {
return InnerCreateArray<double, DoubleArray, BlockType::kDoubleValue>(name, parent, slots,
format);
}
template <typename WrapperType, typename ValueType>
WrapperType State::InnerCreateProperty(const std::string& name, BlockIndex parent,
const char* value, size_t length,
PropertyBlockFormat format) {
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
BlockIndex name_index, value_index;
zx_status_t status;
status = InnerCreateValue(name, BlockType::kBufferValue, parent, &name_index, &value_index);
if (status != ZX_OK) {
return WrapperType();
}
auto* block = heap_->GetBlock(value_index);
block->payload.u64 = PropertyBlockPayload::Flags::Make(format);
status = InnerSetStringExtents(value_index, value, length);
if (status != ZX_OK) {
heap_->Free(name_index);
heap_->Free(value_index);
return WrapperType();
}
return WrapperType(weak_self_ptr_.lock(), name_index, value_index);
}
StringProperty State::CreateStringProperty(const std::string& name, BlockIndex parent,
const std::string& value) {
return InnerCreateProperty<StringProperty, std::string>(
name, parent, value.data(), value.length(), PropertyBlockFormat::kUtf8);
}
ByteVectorProperty State::CreateByteVectorProperty(const std::string& name, BlockIndex parent,
const std::vector<uint8_t>& value) {
return InnerCreateProperty<ByteVectorProperty, std::vector<uint8_t>>(
name, parent, reinterpret_cast<const char*>(value.data()), value.size(),
PropertyBlockFormat::kBinary);
}
Link State::CreateLink(const std::string& name, BlockIndex parent, const std::string& content,
LinkBlockDisposition disposition) {
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
BlockIndex name_index, value_index, content_index;
zx_status_t status;
status = InnerCreateValue(name, BlockType::kLinkValue, parent, &name_index, &value_index);
if (status != ZX_OK) {
return Link();
}
status = CreateName(content, &content_index);
if (status != ZX_OK) {
DecrementParentRefcount(value_index);
heap_->Free(name_index);
heap_->Free(value_index);
return Link();
}
auto* block = heap_->GetBlock(value_index);
block->payload.u64 = LinkBlockPayload::ContentIndex::Make(content_index) |
LinkBlockPayload::Flags::Make(disposition);
return Link(weak_self_ptr_.lock(), name_index, value_index, content_index);
}
Node State::CreateRootNode() {
std::lock_guard<std::mutex> lock(mutex_);
return Node(weak_self_ptr_.lock(), 0, 0);
}
LazyNode State::InnerCreateLazyLink(const std::string& name, BlockIndex parent,
LazyNodeCallbackFn callback, LinkBlockDisposition disposition) {
auto content = UniqueLinkName(name);
auto link = CreateLink(name, parent, content, disposition);
{
std::lock_guard<std::mutex> lock(mutex_);
link_callbacks_.emplace(content, LazyNodeCallbackHolder(std::move(callback)));
return LazyNode(weak_self_ptr_.lock(), std::move(content), std::move(link));
}
}
LazyNode State::CreateLazyNode(const std::string& name, BlockIndex parent,
LazyNodeCallbackFn callback) {
return InnerCreateLazyLink(name, parent, std::move(callback), LinkBlockDisposition::kChild);
}
LazyNode State::CreateLazyValues(const std::string& name, BlockIndex parent,
LazyNodeCallbackFn callback) {
return InnerCreateLazyLink(name, parent, std::move(callback), LinkBlockDisposition::kInline);
}
Node State::CreateNode(const std::string& name, BlockIndex parent) {
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
BlockIndex name_index, value_index;
zx_status_t status;
status = InnerCreateValue(name, BlockType::kNodeValue, parent, &name_index, &value_index);
if (status != ZX_OK) {
return Node();
}
return Node(weak_self_ptr_.lock(), name_index, value_index);
}
void State::SetIntProperty(IntProperty* metric, int64_t value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kIntValue, "Expected int metric, got %d",
static_cast<int>(GetType(block)));
block->payload.i64 = value;
}
void State::SetUintProperty(UintProperty* metric, uint64_t value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kUintValue, "Expected uint metric, got %d",
static_cast<int>(GetType(block)));
block->payload.u64 = value;
}
void State::SetDoubleProperty(DoubleProperty* metric, double value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kDoubleValue, "Expected double metric, got %d",
static_cast<int>(GetType(block)));
block->payload.f64 = value;
}
void State::SetBoolProperty(BoolProperty* metric, bool value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kBoolValue, "Expected bool metric, got %d",
static_cast<int>(GetType(block)));
block->payload.u64 = value;
}
void State::SetIntArray(IntArray* array, size_t index, int64_t value) {
InnerSetArray<int64_t, IntArray, BlockType::kIntValue>(array, index, value);
}
void State::SetUintArray(UintArray* array, size_t index, uint64_t value) {
InnerSetArray<uint64_t, UintArray, BlockType::kUintValue>(array, index, value);
}
void State::SetDoubleArray(DoubleArray* array, size_t index, double value) {
InnerSetArray<double, DoubleArray, BlockType::kDoubleValue>(array, index, value);
}
void State::AddIntProperty(IntProperty* metric, int64_t value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kIntValue, "Expected int metric, got %d",
static_cast<int>(GetType(block)));
block->payload.i64 += value;
}
void State::AddUintProperty(UintProperty* metric, uint64_t value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kUintValue, "Expected uint metric, got %d",
static_cast<int>(GetType(block)));
block->payload.u64 += value;
}
void State::AddDoubleProperty(DoubleProperty* metric, double value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kDoubleValue, "Expected double metric, got %d",
static_cast<int>(GetType(block)));
block->payload.f64 += value;
}
void State::SubtractIntProperty(IntProperty* metric, int64_t value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kIntValue, "Expected int metric, got %d",
static_cast<int>(GetType(block)));
block->payload.i64 -= value;
}
void State::SubtractUintProperty(UintProperty* metric, uint64_t value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kUintValue, "Expected uint metric, got %d",
static_cast<int>(GetType(block)));
block->payload.u64 -= value;
}
void State::SubtractDoubleProperty(DoubleProperty* metric, double value) {
ZX_ASSERT(metric->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(metric->value_index_);
ZX_DEBUG_ASSERT_MSG(GetType(block) == BlockType::kDoubleValue, "Expected double metric, got %d",
static_cast<int>(GetType(block)));
block->payload.f64 -= value;
}
void State::AddIntArray(IntArray* array, size_t index, int64_t value) {
InnerOperationArray<int64_t, IntArray, BlockType::kIntValue, std::plus<int64_t>>(array, index,
value);
}
void State::SubtractIntArray(IntArray* array, size_t index, int64_t value) {
InnerOperationArray<int64_t, IntArray, BlockType::kIntValue, std::minus<int64_t>>(array, index,
value);
}
void State::AddUintArray(UintArray* array, size_t index, uint64_t value) {
InnerOperationArray<uint64_t, UintArray, BlockType::kUintValue, std::plus<uint64_t>>(array, index,
value);
}
void State::SubtractUintArray(UintArray* array, size_t index, uint64_t value) {
InnerOperationArray<uint64_t, UintArray, BlockType::kUintValue, std::minus<uint64_t>>(
array, index, value);
}
void State::AddDoubleArray(DoubleArray* array, size_t index, double value) {
InnerOperationArray<double, DoubleArray, BlockType::kDoubleValue, std::plus<double>>(array, index,
value);
}
void State::SubtractDoubleArray(DoubleArray* array, size_t index, double value) {
InnerOperationArray<double, DoubleArray, BlockType::kDoubleValue, std::minus<double>>(
array, index, value);
}
template <typename WrapperType>
void State::InnerSetProperty(WrapperType* property, const char* value, size_t length) {
ZX_ASSERT(property->state_.get() == this);
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
InnerSetStringExtents(property->value_index_, value, length);
}
void State::SetStringProperty(StringProperty* property, const std::string& value) {
InnerSetProperty(property, value.data(), value.size());
}
void State::SetByteVectorProperty(ByteVectorProperty* property, const std::vector<uint8_t>& value) {
InnerSetProperty(property, reinterpret_cast<const char*>(value.data()), value.size());
}
void State::DecrementParentRefcount(BlockIndex value_index) {
Block* value = heap_->GetBlock(value_index);
ZX_ASSERT(value);
BlockIndex parent_index = ValueBlockFields::ParentIndex::Get<BlockIndex>(value->header);
Block* parent;
while ((parent = heap_->GetBlock(parent_index)) != nullptr) {
ZX_ASSERT(parent);
switch (GetType(parent)) {
case BlockType::kHeader:
return;
case BlockType::kNodeValue:
// Stop decrementing parent refcounts when we observe a live object.
ZX_ASSERT(parent->payload.u64 != 0);
--parent->payload.u64;
return;
case BlockType::kTombstone:
ZX_ASSERT(parent->payload.u64 != 0);
if (--parent->payload.u64 == 0) {
// The tombstone parent is no longer referenced and can be deleted.
// Continue decrementing refcounts.
BlockIndex next_parent_index =
ValueBlockFields::ParentIndex::Get<BlockIndex>(parent->header);
heap_->Free(ValueBlockFields::NameIndex::Get<BlockIndex>(parent->header));
heap_->Free(parent_index);
parent_index = next_parent_index;
break;
}
// The tombstone parent is still referenced. Done decrementing refcounts.
return;
default:
ZX_DEBUG_ASSERT_MSG(false, "Invalid parent type %u",
static_cast<uint32_t>(GetType(parent)));
return;
}
}
}
void State::FreeIntProperty(IntProperty* metric) {
ZX_DEBUG_ASSERT_MSG(metric->state_.get() == this, "Property being freed from the wrong state");
if (metric->state_.get() != this) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
DecrementParentRefcount(metric->value_index_);
heap_->Free(metric->name_index_);
heap_->Free(metric->value_index_);
metric->state_ = nullptr;
}
void State::FreeUintProperty(UintProperty* metric) {
ZX_DEBUG_ASSERT_MSG(metric->state_.get() == this, "Property being freed from the wrong state");
if (metric->state_.get() != this) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
DecrementParentRefcount(metric->value_index_);
heap_->Free(metric->name_index_);
heap_->Free(metric->value_index_);
metric->state_ = nullptr;
}
void State::FreeDoubleProperty(DoubleProperty* metric) {
ZX_DEBUG_ASSERT_MSG(metric->state_.get() == this, "Property being freed from the wrong state");
if (metric->state_.get() != this) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
DecrementParentRefcount(metric->value_index_);
heap_->Free(metric->name_index_);
heap_->Free(metric->value_index_);
metric->state_ = nullptr;
}
void State::FreeBoolProperty(BoolProperty* metric) {
ZX_DEBUG_ASSERT_MSG(metric->state_.get() == this, "Property being freed from wrong state");
if (metric->state_.get() != this) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
DecrementParentRefcount(metric->value_index_);
heap_->Free(metric->name_index_);
heap_->Free(metric->value_index_);
metric->state_ = nullptr;
}
void State::FreeIntArray(IntArray* array) { InnerFreeArray<IntArray>(array); }
void State::FreeUintArray(UintArray* array) { InnerFreeArray<UintArray>(array); }
void State::FreeDoubleArray(DoubleArray* array) { InnerFreeArray<DoubleArray>(array); }
template <typename WrapperType>
void State::InnerFreePropertyWithExtents(WrapperType* property) {
ZX_DEBUG_ASSERT_MSG(property->state_.get() == this, "Property being freed from the wrong state");
if (property->state_.get() != this) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
DecrementParentRefcount(property->value_index_);
InnerFreeStringExtents(property->value_index_);
heap_->Free(property->name_index_);
heap_->Free(property->value_index_);
property->state_ = nullptr;
}
void State::FreeStringProperty(StringProperty* property) { InnerFreePropertyWithExtents(property); }
void State::FreeByteVectorProperty(ByteVectorProperty* property) {
InnerFreePropertyWithExtents(property);
}
void State::FreeLink(Link* link) {
ZX_DEBUG_ASSERT_MSG(link->state_.get() == this, "Link being freed from the wrong state");
if (link->state_.get() != this) {
return;
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
DecrementParentRefcount(link->value_index_);
heap_->Free(link->name_index_);
heap_->Free(link->value_index_);
heap_->Free(link->content_index_);
link->state_ = nullptr;
}
void State::FreeNode(Node* object) {
ZX_DEBUG_ASSERT_MSG(object->state_.get() == this, "Node being freed from the wrong state");
if (object->state_.get() != this) {
return;
}
if (object->value_index_ == 0) {
// This is a special "root" node, it cannot be deleted.
return;
}
std::lock_guard<std::mutex> lock(mutex_);
AutoGenerationIncrement gen(header_, heap_.get());
auto* block = heap_->GetBlock(object->value_index_);
if (block) {
if (block->payload.u64 == 0) {
// Actually free the block, decrementing parent refcounts.
DecrementParentRefcount(object->value_index_);
// Node has no refs, free it.
heap_->Free(object->name_index_);
heap_->Free(object->value_index_);
} else {
// Node has refs, change type to tombstone so it can be removed
// when the last ref is gone.
ValueBlockFields::Type::Set(&block->header, static_cast<uint64_t>(BlockType::kTombstone));
}
}
}
void State::FreeLazyNode(LazyNode* object) {
ZX_DEBUG_ASSERT_MSG(object->state_.get() == this, "Node being freed from the wrong state");
if (object->state_.get() != this) {
return;
}
// Free the contained link, which removes the reference to the value in the map.
FreeLink(&object->link_);
LazyNodeCallbackHolder holder;
{
// Separately lock the current state, and remove the callback for this lazy node.
std::lock_guard<std::mutex> lock(mutex_);
auto it = link_callbacks_.find(object->content_value_);
if (it != link_callbacks_.end()) {
holder = it->second;
link_callbacks_.erase(it);
}
object->state_ = nullptr;
}
// Cancel the Holder without State locked. This avoids a deadlock in which we could be locking the
// holder with the state lock held, meanwhile the callback itself is modifying state (with holder
// locked).
//
// At this point in time, the LazyNode is still *live* and the callback may be getting executed.
// Following this cancel call, the LazyNode is no longer live and the callback will never be
// called again.
holder.cancel();
}
std::vector<std::string> State::GetLinkNames() const {
std::lock_guard<std::mutex> lock(mutex_);
std::vector<std::string> ret;
for (const auto& entry : link_callbacks_) {
ret.push_back(entry.first);
}
return ret;
}
fit::promise<Inspector> State::CallLinkCallback(const std::string& name) {
LazyNodeCallbackHolder holder;
{
std::lock_guard<std::mutex> lock(mutex_);
auto it = link_callbacks_.find(name);
if (it == link_callbacks_.end()) {
return fit::make_result_promise<Inspector>(fit::error());
}
// Copy out the holder.
holder = it->second;
}
// Call the callback.
// This occurs without state locked, but deletion of the LazyNode synchronizes on the internal
// mutex in the Holder. If the LazyNode is deleted before this call, the callback will not be
// executed. If the LazyNode is being deleted concurrent with this call, it will be delayed until
// after the callback returns.
return holder.call();
}
zx_status_t State::InnerCreateValue(const std::string& name, BlockType type,
BlockIndex parent_index, BlockIndex* out_name,
BlockIndex* out_value, size_t min_size_required) {
BlockIndex value_index, name_index;
zx_status_t status;
status = heap_->Allocate(min_size_required, &value_index);
if (status != ZX_OK) {
return status;
}
status = CreateName(name, &name_index);
if (status != ZX_OK) {
heap_->Free(value_index);
return status;
}
auto* block = heap_->GetBlock(value_index);
block->header = ValueBlockFields::Order::Make(GetOrder(block)) |
ValueBlockFields::Type::Make(type) |
ValueBlockFields::ParentIndex::Make(parent_index) |
ValueBlockFields::NameIndex::Make(name_index);
memset(&block->payload, 0, min_size_required - sizeof(block->header));
// Increment the parent refcount.
Block* parent = heap_->GetBlock(parent_index);
ZX_DEBUG_ASSERT_MSG(parent, "Index %lu is invalid", parent_index);
// In release mode, do cleanup if parent is invalid.
BlockType parent_type = (parent) ? GetType(parent) : BlockType::kFree;
switch (parent_type) {
case BlockType::kHeader:
break;
case BlockType::kNodeValue:
case BlockType::kTombstone:
// Increment refcount.
parent->payload.u64++;
break;
default:
ZX_DEBUG_ASSERT_MSG(false, "Invalid parent block type %u for 0x%lx",
static_cast<uint32_t>(GetType(parent)), parent_index);
heap_->Free(name_index);
heap_->Free(value_index);
return ZX_ERR_INVALID_ARGS;
}
*out_name = name_index;
*out_value = value_index;
return ZX_OK;
}
void State::InnerFreeStringExtents(BlockIndex string_index) {
auto* str = heap_->GetBlock(string_index);
if (!str || GetType(str) != BlockType::kBufferValue) {
return;
}
auto extent_index = PropertyBlockPayload::ExtentIndex::Get<BlockIndex>(str->payload.u64);
auto* extent = heap_->GetBlock(extent_index);
while (IsExtent(extent)) {
auto next_extent = ExtentBlockFields::NextExtentIndex::Get<BlockIndex>(extent->header);
heap_->Free(extent_index);
extent_index = next_extent;
extent = heap_->GetBlock(extent_index);
}
// Leave the string value allocated (and empty).
str->payload.u64 = PropertyBlockPayload::TotalLength::Make(0) |
PropertyBlockPayload::ExtentIndex::Make(0) |
PropertyBlockPayload::Flags::Make(
PropertyBlockPayload::Flags::Get<uint8_t>(str->payload.u64));
}
zx_status_t State::InnerSetStringExtents(BlockIndex string_index, const char* value,
size_t length) {
InnerFreeStringExtents(string_index);
auto* block = heap_->GetBlock(string_index);
if (length == 0) {
// The extent index is 0 if no extents were needed (the value is empty).
block->payload.u64 = PropertyBlockPayload::TotalLength::Make(length) |
PropertyBlockPayload::ExtentIndex::Make(0) |
PropertyBlockPayload::Flags::Make(
PropertyBlockPayload::Flags::Get<uint8_t>(block->payload.u64));
return ZX_OK;
}
BlockIndex extent_index;
zx_status_t status;
status = heap_->Allocate(std::min(kMaxOrderSize, BlockSizeForPayload(length)), &extent_index);
if (status != ZX_OK) {
return status;
}
block->payload.u64 = PropertyBlockPayload::TotalLength::Make(length) |
PropertyBlockPayload::ExtentIndex::Make(extent_index) |
PropertyBlockPayload::Flags::Make(
PropertyBlockPayload::Flags::Get<uint8_t>(block->payload.u64));
// Thread the value through extents, creating new extents as needed.
size_t offset = 0;
while (offset < length) {
auto* extent = heap_->GetBlock(extent_index);
extent->header = ExtentBlockFields::Order::Make(GetOrder(extent)) |
ExtentBlockFields::Type::Make(BlockType::kExtent) |
ExtentBlockFields::NextExtentIndex::Make(0);
size_t len = std::min(PayloadCapacity(GetOrder(extent)), length - offset);
memcpy(extent->payload.data, value + offset, len);
offset += len;
if (offset < length) {
status = heap_->Allocate(std::min(kMaxOrderSize, BlockSizeForPayload(length - offset)),
&extent_index);
if (status != ZX_OK) {
InnerFreeStringExtents(string_index);
return status;
}
ExtentBlockFields::NextExtentIndex::Set(&extent->header, extent_index);
}
}
return ZX_OK;
}
std::string State::UniqueLinkName(const std::string& prefix) {
return prefix + "-" +
std::to_string(next_unique_link_number_.fetch_add(1, std::memory_order_relaxed));
}
zx_status_t State::CreateName(const std::string& name, BlockIndex* out) {
ZX_DEBUG_ASSERT_MSG(name.size() <= kMaxPayloadSize, "Name too long (length is %lu)", name.size());
if (name.size() > kMaxPayloadSize) {
return ZX_ERR_INVALID_ARGS;
}
zx_status_t status;
status = heap_->Allocate(BlockSizeForPayload(name.size()), out);
if (status != ZX_OK) {
return status;
}
auto* block = heap_->GetBlock(*out);
block->header = NameBlockFields::Order::Make(GetOrder(block)) |
NameBlockFields::Type::Make(BlockType::kName) |
NameBlockFields::Length::Make(name.size());
memset(block->payload.data, 0, PayloadCapacity(GetOrder(block)));
memcpy(block->payload.data, name.data(), name.size());
return ZX_OK;
}
std::string State::UniqueName(const std::string& prefix) {
std::ostringstream out;
uint64_t value = next_unique_id_.fetch_add(1, std::memory_order_relaxed);
out << prefix << "0x" << std::hex << value;
return out.str();
}
} // namespace internal
} // namespace inspect