blob: dca80591ea9f0f8d3ce2ad15713d5205db29f8e1 [file] [log] [blame]
// Copyright 2019 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 <fuchsia/inspect/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/cpp/binding_set.h>
#include <lib/fpromise/single_threaded_executor.h>
#include <lib/inspect/component/cpp/component.h>
#include <lib/inspect/component/cpp/service.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/stdcompat/variant.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/vfs/cpp/pseudo_dir.h>
#include <lib/vfs/cpp/service.h>
#include <string>
#include <vector>
#include <diagnostics/validate/cpp/fidl.h>
namespace dv = diagnostics::validate;
using cpp17::get;
using cpp17::holds_alternative;
using dv::Action;
using dv::InitializationParams;
using dv::LazyAction;
using dv::LinkDisposition;
using dv::ROOT_ID;
using dv::TestResult;
using dv::ValueType;
using inspect::LazyNode;
using Value =
cpp17::variant<cpp17::monostate, inspect::Node, inspect::IntProperty, inspect::UintProperty,
inspect::DoubleProperty, inspect::StringProperty, inspect::ByteVectorProperty,
inspect::BoolProperty, inspect::IntArray, inspect::UintArray,
inspect::DoubleArray, inspect::LinearIntHistogram, inspect::LinearUintHistogram,
inspect::LinearDoubleHistogram, inspect::ExponentialIntHistogram,
inspect::ExponentialUintHistogram, inspect::ExponentialDoubleHistogram,
inspect::StringArray>;
class Actor {
public:
Actor() : inspector_() {}
Actor(inspect::InspectSettings settings) : inspector_(settings) {}
inspect::Inspector& inspector() { return inspector_; }
TestResult Act(const Action& action) {
switch (action.Which()) {
case Action::Tag::kCreateNode:
return HandleCreateNode(action);
case Action::Tag::kDeleteNode:
return HandleDeleteNode(action);
case Action::Tag::kDeleteProperty:
return HandleDeleteProperty(action);
case Action::Tag::kCreateNumericProperty:
return HandleCreateNumeric(action);
case Action::Tag::kCreateBytesProperty:
return HandleCreateBytes(action);
case Action::Tag::kCreateStringProperty:
return HandleCreateString(action);
case Action::Tag::kCreateBoolProperty:
return HandleCreateBool(action);
case Action::Tag::kSetNumber:
return HandleSetNumber(action);
case Action::Tag::kAddNumber:
return HandleAddNumber(action);
case Action::Tag::kSubtractNumber:
return HandleSubtractNumber(action);
case Action::Tag::kSetString:
return HandleSetString(action);
case Action::Tag::kSetBytes:
return HandleSetBytes(action);
case Action::Tag::kSetBool:
return HandleSetBool(action);
case Action::Tag::kCreateArrayProperty:
return HandleCreateArray(action);
case Action::Tag::kArraySet:
return HandleArraySet(action);
case Action::Tag::kArrayAdd:
return HandleArrayAdd(action);
case Action::Tag::kArraySubtract:
return HandleArraySubtract(action);
case Action::Tag::kCreateLinearHistogram:
return HandleCreateLinearHistogram(action);
case Action::Tag::kCreateExponentialHistogram:
return HandleCreateExponentialHistogram(action);
case Action::Tag::kInsert:
return HandleInsert(action);
case Action::Tag::kInsertMultiple:
return HandleInsertMultiple(action);
default:
return TestResult::UNIMPLEMENTED;
}
}
TestResult ActLazy(const LazyAction& lazy_action) {
switch (lazy_action.Which()) {
case LazyAction::Tag::kCreateLazyNode:
return HandleCreateLazyNode(lazy_action);
case LazyAction::Tag::kDeleteLazyNode:
return HandleDeleteLazyNode(lazy_action);
default:
return TestResult::UNIMPLEMENTED;
}
}
private:
template <typename T>
T* GetFromValueMap(uint64_t id) {
auto it = value_map_.find(id);
if (it == value_map_.end()) {
return nullptr;
}
if (!holds_alternative<T>(it->second)) {
return nullptr;
}
return &get<T>(it->second);
}
inspect::Node* GetNode(uint64_t id) {
if (id == ROOT_ID) {
return &inspector_.GetRoot();
} else {
return GetFromValueMap<inspect::Node>(id);
}
}
bool ValueMapContains(uint64_t id) { return value_map_.find(id) != value_map_.end(); }
bool LazyChildrenMapContains(uint64_t id) {
return lazy_children_map_.find(id) != lazy_children_map_.end();
}
// Emplace all currently held Values into the underlying Inspector object.
// We do this so that when this instance goes of scope, the Value objects don't call their
// destructors.
void Freeze() {
for (auto& [k, v] : value_map_) {
inspector_.emplace(std::move(v));
}
value_map_.clear();
}
TestResult HandleCreateNode(const Action& raw_action) {
auto& action = raw_action.create_node();
if (ValueMapContains(action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(action.parent);
if (!parent) {
return TestResult::FAILED;
}
value_map_.emplace(action.id, parent->CreateChild(action.name));
return TestResult::OK;
}
TestResult HandleDeleteNode(const Action& raw_action) {
auto& action = raw_action.delete_node();
if (!ValueMapContains(action.id)) {
return TestResult::FAILED;
}
value_map_.erase(action.id);
return TestResult::OK;
}
TestResult HandleCreateNumeric(const Action& raw_action) {
auto& action = raw_action.create_numeric_property();
if (ValueMapContains(action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(action.parent);
if (!parent) {
return TestResult::FAILED;
}
if (action.value.is_int_t()) {
value_map_.emplace(action.id, parent->CreateInt(action.name, action.value.int_t()));
} else if (action.value.is_uint_t()) {
value_map_.emplace(action.id, parent->CreateUint(action.name, action.value.uint_t()));
} else if (action.value.is_double_t()) {
value_map_.emplace(action.id, parent->CreateDouble(action.name, action.value.double_t()));
} else {
return TestResult::UNIMPLEMENTED;
}
return TestResult::OK;
}
TestResult HandleCreateBytes(const Action& raw_action) {
auto& action = raw_action.create_bytes_property();
if (ValueMapContains(action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(action.parent);
if (!parent) {
return TestResult::FAILED;
}
value_map_.emplace(action.id, parent->CreateByteVector(action.name, action.value));
return TestResult::OK;
}
TestResult HandleCreateString(const Action& raw_action) {
auto& action = raw_action.create_string_property();
if (ValueMapContains(action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(action.parent);
if (!parent) {
return TestResult::FAILED;
}
value_map_.emplace(action.id, parent->CreateString(action.name, action.value));
return TestResult::OK;
}
TestResult HandleCreateBool(const Action& raw_action) {
auto& action = raw_action.create_bool_property();
if (ValueMapContains(action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(action.parent);
if (!parent) {
return TestResult::FAILED;
}
value_map_.emplace(action.id, parent->CreateBool(action.name, action.value));
return TestResult::OK;
}
TestResult HandleDeleteProperty(const Action& raw_action) {
auto& action = raw_action.delete_property();
if (!ValueMapContains(action.id)) {
return TestResult::FAILED;
}
value_map_.erase(action.id);
return TestResult::OK;
}
// Helpful macro for deduplicating the 3 operations for numbers (Set, Add, and Subtract).
#define CREATE_NUMERIC_HANDLER(OP_NAME, TYPE_FUNC) \
TestResult Handle##OP_NAME##Number(const Action& raw_action) { \
auto& action = raw_action.TYPE_FUNC(); \
\
if (action.value.is_int_t()) { \
if (auto* val = GetFromValueMap<inspect::IntProperty>(action.id)) { \
val->OP_NAME(action.value.int_t()); \
return TestResult::OK; \
} \
} else if (action.value.is_uint_t()) { \
if (auto* val = GetFromValueMap<inspect::UintProperty>(action.id)) { \
val->OP_NAME(action.value.uint_t()); \
return TestResult::OK; \
} \
} else if (action.value.is_double_t()) { \
if (auto* val = GetFromValueMap<inspect::DoubleProperty>(action.id)) { \
val->OP_NAME(action.value.double_t()); \
return TestResult::OK; \
} \
} else { \
return TestResult::UNIMPLEMENTED; \
} \
\
return TestResult::FAILED; \
}
CREATE_NUMERIC_HANDLER(Add, add_number)
CREATE_NUMERIC_HANDLER(Subtract, subtract_number)
CREATE_NUMERIC_HANDLER(Set, set_number)
TestResult HandleSetString(const Action& raw_action) {
auto& action = raw_action.set_string();
if (auto* val = GetFromValueMap<inspect::StringProperty>(action.id)) {
val->Set(action.value);
return TestResult::OK;
} else {
return TestResult::FAILED;
}
}
TestResult HandleSetBytes(const Action& raw_action) {
auto& action = raw_action.set_bytes();
if (auto* val = GetFromValueMap<inspect::ByteVectorProperty>(action.id)) {
val->Set(action.value);
return TestResult::OK;
} else {
return TestResult::FAILED;
}
}
TestResult HandleSetBool(const Action& raw_action) {
auto& action = raw_action.set_bool();
if (auto* val = GetFromValueMap<inspect::BoolProperty>(action.id)) {
val->Set(action.value);
return TestResult::OK;
} else {
return TestResult::FAILED;
}
}
TestResult HandleCreateArray(const Action& raw_action) {
auto& action = raw_action.create_array_property();
if (ValueMapContains(action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(action.parent);
if (!parent) {
return TestResult::FAILED;
}
switch (action.value_type) {
case ValueType::INT:
value_map_.emplace(action.id, parent->CreateIntArray(action.name, action.slots));
break;
case ValueType::UINT:
value_map_.emplace(action.id, parent->CreateUintArray(action.name, action.slots));
break;
case ValueType::DOUBLE:
value_map_.emplace(action.id, parent->CreateDoubleArray(action.name, action.slots));
break;
case ValueType::STRING:
value_map_.emplace(action.id, parent->CreateStringArray(action.name, action.slots));
break;
default:
return TestResult::UNIMPLEMENTED;
};
return TestResult::OK;
}
TestResult HandleArraySet(const Action& raw_action) {
auto& action = raw_action.array_set();
if (action.value.is_int_t()) {
if (auto* val = GetFromValueMap<inspect::IntArray>(action.id)) {
val->Set(action.index, action.value.int_t());
return TestResult::OK;
}
} else if (action.value.is_uint_t()) {
if (auto* val = GetFromValueMap<inspect::UintArray>(action.id)) {
val->Set(action.index, action.value.uint_t());
return TestResult::OK;
}
} else if (action.value.is_double_t()) {
if (auto* val = GetFromValueMap<inspect::DoubleArray>(action.id)) {
val->Set(action.index, action.value.double_t());
return TestResult::OK;
}
} else if (action.value.is_string_t()) {
if (auto* val = GetFromValueMap<inspect::StringArray>(action.id)) {
val->Set(action.index, action.value.string_t());
return TestResult::OK;
}
} else {
return TestResult::UNIMPLEMENTED;
}
return TestResult::FAILED;
}
// Helpful macro for deduplicating the operations for numeric arrays (Add and Subtract).
#define CREATE_NUMERIC_ARRAY_HANDLER(OP_NAME, TYPE_FUNC) \
TestResult HandleArray##OP_NAME(const Action& raw_action) { \
auto& action = raw_action.TYPE_FUNC(); \
\
if (action.value.is_int_t()) { \
if (auto* val = GetFromValueMap<inspect::IntArray>(action.id)) { \
val->OP_NAME(action.index, action.value.int_t()); \
return TestResult::OK; \
} \
} else if (action.value.is_uint_t()) { \
if (auto* val = GetFromValueMap<inspect::UintArray>(action.id)) { \
val->OP_NAME(action.index, action.value.uint_t()); \
return TestResult::OK; \
} \
} else if (action.value.is_double_t()) { \
if (auto* val = GetFromValueMap<inspect::DoubleArray>(action.id)) { \
val->OP_NAME(action.index, action.value.double_t()); \
return TestResult::OK; \
} \
} else { \
return TestResult::UNIMPLEMENTED; \
} \
\
return TestResult::FAILED; \
}
CREATE_NUMERIC_ARRAY_HANDLER(Add, array_add)
CREATE_NUMERIC_ARRAY_HANDLER(Subtract, array_subtract)
TestResult HandleCreateLinearHistogram(const Action& raw_action) {
auto& action = raw_action.create_linear_histogram();
if (ValueMapContains(action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(action.parent);
if (!parent) {
return TestResult::FAILED;
}
if (action.floor.is_int_t()) {
value_map_.emplace(
action.id, parent->CreateLinearIntHistogram(action.name, action.floor.int_t(),
action.step_size.int_t(), action.buckets));
} else if (action.floor.is_uint_t()) {
value_map_.emplace(
action.id, parent->CreateLinearUintHistogram(action.name, action.floor.uint_t(),
action.step_size.uint_t(), action.buckets));
} else if (action.floor.is_double_t()) {
value_map_.emplace(action.id, parent->CreateLinearDoubleHistogram(
action.name, action.floor.double_t(),
action.step_size.double_t(), action.buckets));
} else {
return TestResult::UNIMPLEMENTED;
}
return TestResult::OK;
}
TestResult HandleCreateExponentialHistogram(const Action& raw_action) {
auto& action = raw_action.create_exponential_histogram();
if (ValueMapContains(action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(action.parent);
if (!parent) {
return TestResult::FAILED;
}
if (action.floor.is_int_t()) {
value_map_.emplace(action.id,
parent->CreateExponentialIntHistogram(
action.name, action.floor.int_t(), action.initial_step.int_t(),
action.step_multiplier.int_t(), action.buckets));
} else if (action.floor.is_uint_t()) {
value_map_.emplace(action.id,
parent->CreateExponentialUintHistogram(
action.name, action.floor.uint_t(), action.initial_step.uint_t(),
action.step_multiplier.uint_t(), action.buckets));
} else if (action.floor.is_double_t()) {
value_map_.emplace(action.id,
parent->CreateExponentialDoubleHistogram(
action.name, action.floor.double_t(), action.initial_step.double_t(),
action.step_multiplier.double_t(), action.buckets));
} else {
return TestResult::UNIMPLEMENTED;
}
return TestResult::OK;
}
TestResult HandleInsert(const Action& raw_action) {
auto& action = raw_action.insert();
if (action.value.is_int_t()) {
if (auto* val = GetFromValueMap<inspect::LinearIntHistogram>(action.id)) {
val->Insert(action.value.int_t());
} else if (auto* val = GetFromValueMap<inspect::ExponentialIntHistogram>(action.id)) {
val->Insert(action.value.int_t());
} else {
return TestResult::FAILED;
}
} else if (action.value.is_uint_t()) {
if (auto* val = GetFromValueMap<inspect::LinearUintHistogram>(action.id)) {
val->Insert(action.value.uint_t());
} else if (auto* val = GetFromValueMap<inspect::ExponentialUintHistogram>(action.id)) {
val->Insert(action.value.uint_t());
} else {
return TestResult::FAILED;
}
} else if (action.value.is_double_t()) {
if (auto* val = GetFromValueMap<inspect::LinearDoubleHistogram>(action.id)) {
val->Insert(action.value.double_t());
} else if (auto* val = GetFromValueMap<inspect::ExponentialDoubleHistogram>(action.id)) {
val->Insert(action.value.double_t());
} else {
return TestResult::FAILED;
}
} else {
return TestResult::UNIMPLEMENTED;
}
return TestResult::OK;
}
TestResult HandleInsertMultiple(const Action& raw_action) {
auto& action = raw_action.insert_multiple();
if (action.value.is_int_t()) {
if (auto* val = GetFromValueMap<inspect::LinearIntHistogram>(action.id)) {
val->Insert(action.value.int_t(), action.count);
} else if (auto* val = GetFromValueMap<inspect::ExponentialIntHistogram>(action.id)) {
val->Insert(action.value.int_t(), action.count);
} else {
return TestResult::FAILED;
}
} else if (action.value.is_uint_t()) {
if (auto* val = GetFromValueMap<inspect::LinearUintHistogram>(action.id)) {
val->Insert(action.value.uint_t(), action.count);
} else if (auto* val = GetFromValueMap<inspect::ExponentialUintHistogram>(action.id)) {
val->Insert(action.value.uint_t(), action.count);
} else {
return TestResult::FAILED;
}
} else if (action.value.is_double_t()) {
if (auto* val = GetFromValueMap<inspect::LinearDoubleHistogram>(action.id)) {
val->Insert(action.value.double_t(), action.count);
} else if (auto* val = GetFromValueMap<inspect::ExponentialDoubleHistogram>(action.id)) {
val->Insert(action.value.double_t(), action.count);
} else {
return TestResult::FAILED;
}
} else {
return TestResult::UNIMPLEMENTED;
}
return TestResult::OK;
}
TestResult HandleCreateLazyNode(const LazyAction& raw_lazy_action) {
auto& lazy_action = raw_lazy_action.create_lazy_node();
if (LazyChildrenMapContains(lazy_action.id)) {
return TestResult::FAILED;
}
auto* parent = GetNode(lazy_action.parent);
if (!parent) {
return TestResult::FAILED;
}
Actor actor;
for (const auto& action : lazy_action.actions) {
actor.Act(action);
}
actor.Freeze();
auto cb = [clone = std::move(actor.inspector())]() { return fpromise::make_ok_promise(clone); };
if (lazy_action.disposition == LinkDisposition::CHILD) {
lazy_children_map_.emplace(lazy_action.id, parent->CreateLazyNode(lazy_action.name, cb));
} else {
lazy_children_map_.emplace(lazy_action.id, parent->CreateLazyValues(lazy_action.name, cb));
}
return TestResult::OK;
}
TestResult HandleDeleteLazyNode(const LazyAction& raw_lazy_action) {
auto& lazy_action = raw_lazy_action.delete_lazy_node();
if (!LazyChildrenMapContains(lazy_action.id)) {
return TestResult::FAILED;
}
lazy_children_map_.erase(lazy_action.id);
return TestResult::OK;
}
inspect::Inspector inspector_;
std::map<uint64_t, Value> value_map_;
std::map<uint64_t, LazyNode> lazy_children_map_;
};
class Puppet : public dv::InspectPuppet {
public:
explicit Puppet(async_dispatcher_t* dispatcher, std::unique_ptr<sys::ComponentContext> context)
: dispatcher_(dispatcher), context_(std::move(context)) {
context_->outgoing()->AddPublicService(bindings_.GetHandler(this));
}
void Initialize(InitializationParams params, InitializeCallback callback) override {
if (actor_ != nullptr) {
callback(
dv::InspectPuppet_Initialize_Result::WithResponse(dv::InspectPuppet_Initialize_Response(
std::make_tuple(zx::vmo(ZX_HANDLE_INVALID), TestResult::ILLEGAL))));
return;
}
actor_ = std::make_unique<Actor>(inspect::InspectSettings{.maximum_size = params.vmoSize()});
if (!bool(actor_->inspector())) {
callback(
dv::InspectPuppet_Initialize_Result::WithResponse(dv::InspectPuppet_Initialize_Response(
std::make_tuple(zx::vmo(ZX_HANDLE_INVALID), TestResult::FAILED))));
} else {
callback(
dv::InspectPuppet_Initialize_Result::WithResponse(dv::InspectPuppet_Initialize_Response(
std::make_tuple(actor_->inspector().DuplicateVmo(), TestResult::OK))));
}
}
void InitializeTree(InitializationParams params, InitializeTreeCallback callback) override {
if (actor_ != nullptr) {
callback(dv::InspectPuppet_InitializeTree_Result::WithResponse(
dv::InspectPuppet_InitializeTree_Response(
std::make_tuple(nullptr, TestResult::ILLEGAL))));
return;
}
actor_ = std::make_unique<Actor>(inspect::InspectSettings{.maximum_size = params.vmoSize()});
auto endpoints = fidl::CreateEndpoints<fuchsia_inspect::Tree>();
inspect::TreeServer::StartSelfManagedServer(actor_->inspector(), {}, dispatcher_,
std::move(endpoints->server));
fuchsia::inspect::TreePtr tree_ptr;
tree_ptr.Bind(endpoints->client.TakeChannel(), dispatcher_);
callback(dv::InspectPuppet_InitializeTree_Result::WithResponse(
dv::InspectPuppet_InitializeTree_Response(
std::make_tuple(std::move(tree_ptr), TestResult::OK))));
}
void GetConfig(GetConfigCallback callback) override {
callback(dv::InspectPuppet_GetConfig_Result::WithResponse(
dv::InspectPuppet_GetConfig_Response(std::make_tuple("cpp-puppet", dv::Options{}))));
}
void Publish(PublishCallback callback) override {
if (actor_ == nullptr) {
callback(dv::InspectPuppet_Publish_Result::WithResponse(
dv::InspectPuppet_Publish_Response(TestResult::ILLEGAL)));
return;
}
component_inspector_ =
inspect::ComponentInspector(dispatcher_, {.inspector = actor_->inspector()});
callback(dv::InspectPuppet_Publish_Result::WithResponse(
dv::InspectPuppet_Publish_Response(TestResult::OK)));
}
void Act(Action action, ActCallback callback) override {
if (actor_ == nullptr) {
callback(dv::InspectPuppet_Act_Result::WithResponse(
dv::InspectPuppet_Act_Response(TestResult::ILLEGAL)));
} else {
callback(dv::InspectPuppet_Act_Result::WithResponse(
dv::InspectPuppet_Act_Response(actor_->Act(action))));
}
}
void ActLazy(LazyAction lazy_action, ActLazyCallback callback) override {
if (actor_ == nullptr) {
callback(dv::InspectPuppet_ActLazy_Result::WithResponse(
dv::InspectPuppet_ActLazy_Response(TestResult::ILLEGAL)));
} else {
callback(dv::InspectPuppet_ActLazy_Result::WithResponse(
dv::InspectPuppet_ActLazy_Response(actor_->ActLazy(lazy_action))));
}
}
void handle_unknown_method(uint64_t ordinal, bool method_has_response) override {}
private:
async_dispatcher_t* dispatcher_;
std::unique_ptr<sys::ComponentContext> context_;
std::optional<inspect::ComponentInspector> component_inspector_ = std::nullopt;
fidl::BindingSet<dv::InspectPuppet> bindings_;
std::unique_ptr<Actor> actor_;
};
int main(int argc, const char** argv) {
// must be first thing
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
Puppet puppet(loop.dispatcher(), sys::ComponentContext::CreateAndServeOutgoingDirectory());
loop.Run();
return 0;
}