blob: 1ae0f0328c809ab345a52990240123fafa50135e [file] [log] [blame]
// Copyright 2022 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 SRC_MEDIA_LIB_CODEC_IMPL_INCLUDE_LIB_MEDIA_CODEC_IMPL_CODEC_DIAGNOSTICS_H_
#define SRC_MEDIA_LIB_CODEC_IMPL_INCLUDE_LIB_MEDIA_CODEC_IMPL_CODEC_DIAGNOSTICS_H_
#include <lib/async/default.h>
#include <lib/inspect/component/cpp/component.h>
#include <lib/inspect/cpp/inspect.h>
#include <lib/inspect/cpp/inspector.h>
#include <lib/inspect/cpp/vmo/types.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/trace/event.h>
#include <lib/zx/clock.h>
#include <lib/zx/time.h>
#include <lib/zx/vmo.h>
#include <atomic>
#include <list>
#include <memory>
#include <string_view>
#include <fbl/macros.h>
#include <trace-vthread/event_vthread.h>
class CodecDiagnostics;
// Class is used to store codec inspect data for a codec where we do not have control of the
// underlying hardware or the codec is software based. Since we do not control the hardware, or
// there is no underlying hardware acceleration, we cannot report the utilization of the codec.
class ComponentCodecDiagnostics {
public:
explicit ComponentCodecDiagnostics(CodecDiagnostics& driver_diagnostics, inspect::Node root);
~ComponentCodecDiagnostics();
// Nodes and NumericProperty should be cleaned up when this class
// is destroyed, so ensure it is not copy to imply ownership so that
// we can use a RAII approach.
ComponentCodecDiagnostics(const ComponentCodecDiagnostics&) = delete;
ComponentCodecDiagnostics& operator=(const ComponentCodecDiagnostics&) = delete;
ComponentCodecDiagnostics(ComponentCodecDiagnostics&&) = default;
ComponentCodecDiagnostics& operator=(ComponentCodecDiagnostics&&) = default;
private:
static constexpr std::string_view kCreationTime = "creation_time";
std::reference_wrapper<CodecDiagnostics> driver_diagnostics_;
inspect::Node root_;
inspect::UintProperty creation_time_;
};
// Class is used to store codec inspect data for a codec where we control the underlying
// hardware (example: amlogic driver). Because we control the hardware we can calculate
// utilization of the underlying hardware and report it via the inspect data.
class DriverCodecDiagnostics {
public:
struct TimePeriod {
TimePeriod() = default;
TimePeriod(zx::time in_end_time, zx::duration in_total_time, zx::duration in_active_time)
: end_time(in_end_time), total_time(in_total_time), active_time(in_active_time) {}
~TimePeriod() = default;
zx::time end_time;
zx::duration total_time;
zx::duration active_time;
};
explicit DriverCodecDiagnostics(CodecDiagnostics& driver_diagnostics, inspect::Node root);
~DriverCodecDiagnostics();
// Nodes and NumericProperty should be cleaned up when this class
// is destroyed, so ensure it is not copy to imply ownership so that
// we can use a RAII approach.
DriverCodecDiagnostics(const DriverCodecDiagnostics&) = delete;
DriverCodecDiagnostics& operator=(const DriverCodecDiagnostics&) = delete;
DriverCodecDiagnostics(DriverCodecDiagnostics&&) = default;
DriverCodecDiagnostics& operator=(DriverCodecDiagnostics&&) = default;
void UpdateHardwareUtilizationStatus(zx::time now, bool is_utilizing_hardware);
private:
static constexpr std::string_view kCreationTime = "creation_time";
static constexpr std::string_view kCurrentlyUtilizingHardware = "utilizing_hardware";
static constexpr std::string_view kAllocation = "allocation_pct";
static constexpr std::string_view kTotalAllocatedTime = "total_allocated_time_ns";
static constexpr zx::duration kMemoryDuration = zx::msec(1000);
static constexpr zx::duration kBucketDuration = zx::msec(50);
static std::atomic<trace_counter_id_t> CurrentTraceCounter;
// Store a reference to the drive diagnostic that created us, however
// we can not outlive it.
// Note: This has to be in a reference wrapper inorder to support the move
// assignment operator.
std::reference_wrapper<CodecDiagnostics> driver_diagnostics_;
std::list<TimePeriod> time_periods_;
inspect::Node root_;
inspect::UintProperty creation_time_;
inspect::UintProperty utilizing_hardware_;
inspect::DoubleProperty allocation_;
inspect::UintProperty total_allocated_time_;
zx::time last_checked_time_ = zx::clock::get_monotonic();
bool currently_utilizing_hardware_ = false;
trace_counter_id_t trace_counter_id_ = CurrentTraceCounter++;
};
class CodecDiagnostics {
public:
CodecDiagnostics(std::string_view driver_name, async_dispatcher_t* dispatcher);
// Nodes and NumericProperty should be cleaned up when this class
// is destroyed, so ensure it is not copy to imply ownership so that
// we can use a RAII approach.
CodecDiagnostics(const CodecDiagnostics&) = delete;
CodecDiagnostics& operator=(const CodecDiagnostics&) = delete;
CodecDiagnostics(CodecDiagnostics&&) = delete;
CodecDiagnostics& operator=(CodecDiagnostics&&) = delete;
zx::vmo DuplicateVmo() const;
// Sets the the time of the driver binding to the device at the current value
// of the monotic clock.
void SetBindTime();
void IncrementCurrentlyDecoding();
void DecrementCurrentlyDecoding();
// Creates a driver codec diagnostic instance with the given name. The name is guaranteed to be
// unique.
DriverCodecDiagnostics CreateDriverCodec(std::string_view codec_name);
// Create a component codec diagnostic instance with the given name.
ComponentCodecDiagnostics CreateComponentCodec(std::string_view codec_name);
// Should be called when a codec is unloaded.
void RemoveCodec();
private:
static constexpr std::string_view kBindTime = "bind_time";
static constexpr std::string_view kNumOfActiveCodecs = "num_of_active_codecs";
static constexpr std::string_view kCurrentlyDecoding = "currently_decoding";
inspect::ComponentInspector inspector_;
inspect::Node root_;
inspect::UintProperty bind_time_;
inspect::UintProperty num_of_active_codecs_;
inspect::BoolProperty currently_decoding_;
int32_t num_of_currently_decoding_ = 0;
};
// Wrapper class that allows for the getting and setting of a decoder state. When setting
// the decoder state the class will update trace data to reflect the current decoder state and
// also calls the UpdateDiagnostics() on the VideoDecoder class to update the decoder's diagnostics.
template <typename StateType>
class DiagnosticStateWrapper {
public:
// State is an enum so get the underlying type for casting
using UnderlyingType = std::underlying_type_t<StateType>;
DiagnosticStateWrapper(fit::closure update_diagnostics_function, StateType state_value,
fit::function<const char*(StateType)> state_name_function)
: update_diagnostics_function_(std::move(update_diagnostics_function)),
state_value_(state_value),
state_name_function_(std::move(state_name_function)),
vthread_id_(GetNextVthreadID()) {
TRACE_VTHREAD_DURATION_BEGIN("media", state_name_function_(state_value_), "Decoder",
vthread_id_, zx_ticks_get());
}
~DiagnosticStateWrapper() {
TRACE_VTHREAD_DURATION_END("media", state_name_function_(state_value_), "Decoder", vthread_id_,
zx_ticks_get());
}
// Wrapper assignment operator. When a different state is assigned, end the current trace for
// this decoder and start a trace for the new state, update the underlying state and call
// UpdateDiagnostics() so the decoder's diagnostics are updated
DiagnosticStateWrapper& operator=(StateType new_statue) {
// Only process updates if the state has changed
if (state_value_ != new_statue) {
TRACE_VTHREAD_DURATION_END("media", state_name_function_(state_value_), "Decoder",
vthread_id_, zx_ticks_get());
state_value_ = new_statue;
TRACE_VTHREAD_DURATION_BEGIN("media", state_name_function_(state_value_), "Decoder",
vthread_id_, zx_ticks_get());
update_diagnostics_function_();
}
return *this;
}
// Comparison operators, just passthrough to the underlying state
bool operator==(StateType other_state) const noexcept { return (state_value_ == other_state); }
bool operator!=(StateType other_state) const noexcept { return (state_value_ != other_state); }
explicit operator StateType() const noexcept { return state_value_; }
explicit operator UnderlyingType() const noexcept {
return static_cast<UnderlyingType>(state_value_);
}
private:
static trace_vthread_id_t GetNextVthreadID() {
static std::atomic<uint64_t> id;
// Vthread IDs are rounded to the nearest 1000 due to the double->float conversion. See
// https://fxbug.dev/42097004/
constexpr uint32_t kVthreadIdDistance = 2000;
return id.fetch_add(kVthreadIdDistance);
}
fit::closure update_diagnostics_function_;
StateType state_value_;
fit::function<const char*(StateType)> state_name_function_;
const trace_vthread_id_t vthread_id_;
};
#endif // SRC_MEDIA_LIB_CODEC_IMPL_INCLUDE_LIB_MEDIA_CODEC_IMPL_CODEC_DIAGNOSTICS_H_