| // 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_COMPONENT_CPP_EXPOSE_H_ |
| #define LIB_COMPONENT_CPP_EXPOSE_H_ |
| |
| #include <fbl/auto_lock.h> |
| #include <fbl/mutex.h> |
| #include <fbl/string.h> |
| #include <fs/lazy-dir.h> |
| #include <fs/pseudo-file.h> |
| #include <fuchsia/inspect/cpp/fidl.h> |
| #include <lib/fidl/cpp/binding_set.h> |
| #include <lib/fit/function.h> |
| |
| #include <set> |
| #include <unordered_map> |
| #include <variant> |
| |
| namespace component { |
| |
| // Property is a string value associated with an |Object| belonging to a |
| // |Component|. The string value may be updated lazily at read time through the |
| // use of a callback. |
| // |
| // This class is not thread safe; concurrent accesses require external |
| // coordination. |
| class Property { |
| public: |
| using ByteVector = std::vector<uint8_t>; |
| using StringValueCallback = fit::function<std::string()>; |
| using VectorValueCallback = fit::function<ByteVector()>; |
| |
| // Constructs an empty property with string value "". |
| Property() { Set(""); } |
| |
| // Constructs a property from a string. |
| explicit Property(std::string value) { Set(std::move(value)); } |
| explicit Property(ByteVector value) { Set(std::move(value)); } |
| |
| // Constructs a property with value set on each read by the given callback. |
| explicit Property(StringValueCallback callback) { Set(std::move(callback)); } |
| explicit Property(VectorValueCallback callback) { Set(std::move(callback)); } |
| |
| // Sets the property from a string. |
| void Set(std::string value); |
| void Set(ByteVector value); |
| |
| // Sets the property with value set on each read by the given callback. |
| void Set(StringValueCallback callback); |
| void Set(VectorValueCallback callback); |
| |
| fuchsia::inspect::Property ToFidl(const std::string& name) const; |
| |
| private: |
| // Variants of possible values for this property. |
| std::variant<std::string, ByteVector, StringValueCallback, |
| VectorValueCallback> |
| value_; |
| }; |
| |
| // Metric is a numeric value associated with an |Object| belonging to |
| // a |Component|. |
| // A Metric has a type, which is one of: |
| // INT: int64_t |
| // UINT: uint64_t |
| // DOUBLE: double |
| // CALLBACK: Set by a callback function. |
| // |
| // Calling Set*() on a metric changes its type, but Add and Sub |
| // simply perform += or -= respectively, not changing the type of the |
| // Metric. This means the result of an operation will be cast back to the |
| // original type. |
| // |
| // This class is not thread safe; concurrent accesses require external |
| // coordination. |
| class Metric { |
| public: |
| using ValueCallback = fit::function<void(Metric*)>; |
| enum Type { INT, UINT, DOUBLE, CALLBACK }; |
| |
| // Constructs an INT metric with value 0. |
| Metric() { SetInt(0); } |
| |
| // Constructs a metric set on read by the given callback. |
| explicit Metric(ValueCallback callback) { SetCallback(std::move(callback)); } |
| |
| // Sets the type of this metric to INT with the given value. |
| void SetInt(int64_t value); |
| |
| // Sets the type of this metric to UINT with the given value. |
| void SetUInt(uint64_t value); |
| |
| // Sets the type of this metric to DOUBLE with the given value. |
| void SetDouble(double value); |
| |
| // Sets the type of this metric to CALLBACK, where the given callback |
| // is responsible for the value of this metric. |
| void SetCallback(ValueCallback callback); |
| |
| // Gets the value of this metric as a string. |
| std::string ToString() const; |
| |
| // Converts the value of this metric into its FIDL representation, |
| // using the given name for the |name| field. |
| fuchsia::inspect::Metric ToFidl(const std::string& name) const; |
| |
| // Adds a numeric type to the value of this metric. The type of |
| // the metric will not be affected by this operation regardless of the |
| // type passed in. Adding to a CALLBACK metric does nothing. |
| template <typename T> |
| void Add(T amount) { |
| switch (type_) { |
| case INT: |
| int_value_ += static_cast<int64_t>(amount); |
| break; |
| case UINT: |
| uint_value_ += static_cast<uint64_t>(amount); |
| break; |
| case DOUBLE: |
| double_value_ += static_cast<double>(amount); |
| break; |
| case CALLBACK: |
| break; |
| } |
| } |
| |
| // Subtracts a numeric type to the value of this metric. The type of |
| // the metric will not be affected by this operation regardless of the |
| // type passed in. Subtracting from a CALLBACK metric does nothing. |
| template <typename T> |
| void Sub(T amount) { |
| switch (type_) { |
| case INT: |
| int_value_ -= static_cast<int64_t>(amount); |
| break; |
| case UINT: |
| uint_value_ -= static_cast<uint64_t>(amount); |
| break; |
| case DOUBLE: |
| double_value_ -= static_cast<double>(amount); |
| break; |
| case CALLBACK: |
| break; |
| } |
| } |
| |
| private: |
| // The current type of this metric. |
| Type type_; |
| // Union of 64-bit value types for the value of this metric. |
| union { |
| int64_t int_value_; |
| uint64_t uint_value_; |
| double double_value_; |
| }; |
| // Callback to be used if type_ == CALLBACK. |
| ValueCallback callback_; |
| }; |
| |
| Metric IntMetric(int64_t value); |
| Metric UIntMetric(uint64_t value); |
| Metric DoubleMetric(double value); |
| Metric CallbackMetric(Metric::ValueCallback callback); |
| |
| // A component |Object| is any named entity that a component wishes to expose |
| // for inspection. An |Object| consists of any number of string |Property| and |
| // numeric |Metric| values. They may also have any number of uniquely named |
| // children. The set of children may be set dynamically at read time. |
| // |
| // |Object| implements the |Inspect| interface to expose its values and |
| // children over FIDL, and it implements |LazyDir| to expose the same over a |
| // file system. |
| // |
| // In the directory implementation, the special file `.channel` exposes a |
| // |Service| file to bind to the FIDL interface. The special file `.data` |
| // exposes the current values of the |Object| in a TAB-separated format for |
| // debugging. `.data` should be used strictly for debugging, and all user-facing |
| // utilities must communicate over the FIDL interface. |
| // |
| // This class is thread safe. |
| class Object : public fuchsia::inspect::Inspect, public fs::LazyDir { |
| public: |
| using ObjectVector = std::vector<fbl::RefPtr<Object>>; |
| using ChildrenCallback = fit::function<void(ObjectVector*)>; |
| using StringOutputVector = fidl::VectorPtr<std::string>; |
| |
| // Constructs a new |Object| with the given name. |
| // Every object requires a name, and names for children must be unique. |
| explicit Object(fbl::String name); |
| |
| // Gets the name of this |Object|. |
| fbl::String name() { return name_; } |
| |
| // Gets a new reference to a child by name. The return value may be empty if |
| // the child does not exist. |
| fbl::RefPtr<Object> GetChild(fbl::String name); |
| |
| // Sets a child to a new reference. If the child already exists, the contained |
| // reference will be dropped and replaced with the given one. |
| void SetChild(fbl::RefPtr<Object> child); |
| |
| // Takes a child from this |Object|. This |Object| will no longer contain a |
| // reference to the returned child. The return value may be empty if the child |
| // does not exist. |
| fbl::RefPtr<Object> TakeChild(fbl::String name); |
| |
| // Sets a callback to dynamically populate children. The children returned by |
| // this callback are in addition to the children already contained by this |
| // |Object|. |
| void SetChildrenCallback(ChildrenCallback callback); |
| |
| // Clears the callback for dynamic children. After calling this function, the |
| // returned children will consist only of children contained by this object. |
| void ClearChildrenCallback(); |
| |
| // Remove a property from the object, returning true if it was found and |
| // removed. |
| bool RemoveProperty(const std::string& name); |
| |
| // Remove a metric from the object, returning true if it was found and |
| // removed. |
| bool RemoveMetric(const std::string& name); |
| |
| // Sets a |Property| on this |Object| to the given value. |
| // The name of the property cannot include null bytes. |
| bool SetProperty(const std::string& name, Property value); |
| |
| // Sets a |Metric| on this |Object| to the given value. |
| // The name of the metric cannot include null bytes. |
| bool SetMetric(const std::string& name, Metric metric); |
| |
| // Adds to a numeric |Metric| on this |Object|. |
| template <typename T> |
| bool AddMetric(const std::string& name, T amount) { |
| fbl::AutoLock lock(&mutex_); |
| auto it = metrics_.find(name); |
| if (it == metrics_.end()) { |
| return false; |
| } |
| it->second.Add(amount); |
| return true; |
| } |
| |
| // Subtracts from a numeric |Metric| on this |Object|. |
| template <typename T> |
| bool SubMetric(const std::string& name, T amount) { |
| fbl::AutoLock lock(&mutex_); |
| auto it = metrics_.find(name); |
| if (it == metrics_.end()) { |
| return false; |
| } |
| it->second.Sub(amount); |
| return true; |
| } |
| |
| // |Inspect| implementation |
| |
| // Reads local properties and metrics. |
| void ReadData(ReadDataCallback callback) override; |
| |
| // Lists the children of this Object, including dynamic ones if they exist. |
| void ListChildren(ListChildrenCallback callback) override; |
| |
| // Opens a channel with the requested child |
| void OpenChild(std::string name, |
| ::fidl::InterfaceRequest<Inspect> child_channel, |
| OpenChildCallback callback) override; |
| |
| // |LazyDir| implementation |
| |
| // Gets contents for directory listing. |
| // WARNING: Changes to the set of children, including dynamic children, is |
| // logically a removal of all directory contents followed by a repopulation of |
| // the contents. This means that readdir(3) operations may give inconsistent |
| // results in the face of rapid content changes. Use |Inspect| |
| // implementation to avoid this. |
| void GetContents(LazyEntryVector* out_vector) override; |
| |
| // Gets a reference to a child object or special file as a Vnode. |
| // IMPLEMENTATION NOTE: This is safe to use even if contents change rapidly, |
| // so long as the requested |name| is present at the time it is requested. |
| zx_status_t GetFile(fbl::RefPtr<Vnode>* out_vnode, uint64_t id, |
| fbl::String name) override; |
| |
| // Turn this |Object| into its FIDL representation. |
| fuchsia::inspect::Object ToFidl(); |
| |
| // Returns the names of this Object's children in a vector. |
| StringOutputVector GetChildren(); |
| |
| private: |
| enum { kChanId = 1, kSpecialIdMax }; |
| |
| // Helper function to populate an output vector of children objects. |
| void PopulateChildVector(StringOutputVector* out_vector) |
| __TA_EXCLUDES(mutex_); |
| |
| // Mutex protecting fields below. |
| mutable fbl::Mutex mutex_; |
| // The name of this object. |
| fbl::String name_; |
| // The bindings for channels connected to this |Inspect|. |
| fidl::BindingSet<fuchsia::inspect::Inspect, fbl::RefPtr<Object>> bindings_ |
| __TA_GUARDED(mutex_); |
| // |Property| for this object, keyed by name. |
| std::unordered_map<std::string, Property> properties_ __TA_GUARDED(mutex_); |
| // |Property| for this object, keyed by name. |
| std::unordered_map<std::string, Metric> metrics_ __TA_GUARDED(mutex_); |
| // |Children| for this object, keyed by name. Ordered structure for consistent |
| // iteration. |
| std::map<std::string, fbl::RefPtr<Object>> children_ __TA_GUARDED(mutex_); |
| // Callback for retrieving lazily generated children. May be empty. |
| ChildrenCallback lazy_object_callback_ __TA_GUARDED(mutex_); |
| }; |
| |
| } // namespace component |
| |
| #endif // LIB_COMPONENT_CPP_EXPOSE_H_ |