blob: 6d52b77e1e9ed082c674737917b25928c63cf196 [file] [log] [blame]
// Copyright 2013 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef POWER_MANAGER_POWERD_METRICS_COLLECTOR_H_
#define POWER_MANAGER_POWERD_METRICS_COLLECTOR_H_
#include <stdint.h>
#include <deque>
#include <memory>
#include <string>
#include <vector>
#include <base/compiler_specific.h>
#include <base/time/time.h>
#include <base/timer/timer.h>
#include <gtest/gtest_prod.h>
#include "power_manager/common/clock.h"
#include "power_manager/common/metrics_constants.h"
#include "power_manager/common/power_constants.h"
#include "power_manager/powerd/policy/suspender.h"
#include "power_manager/powerd/system/power_supply.h"
#include "privacy_screen/proto_bindings/privacy_screen.pb.h"
namespace power_manager {
class PrefsInterface;
namespace policy {
class BacklightController;
}
namespace metrics {
// Interface class for reading a residency counter.
class ResidencyReader {
public:
// Read the current residency counter value from the file and return it in
// microseconds. Use |InvalidValue| to indicate a failed read.
virtual base::TimeDelta ReadResidency() = 0;
// Default virtual destructor for polymorphic destruction of children.
virtual ~ResidencyReader() = default;
// A helper invalid residency time definition.
constexpr static base::TimeDelta InvalidValue = base::Microseconds(-1);
};
// ResidencyReader for single-value files, e.g. Intel Big/Small core.
class SingleValueResidencyReader : public ResidencyReader {
public:
explicit SingleValueResidencyReader(const base::FilePath& path);
SingleValueResidencyReader() = delete;
// Read the current residency counter value from the file and return it in
// microseconds. Use a negative value as an invalid value.
base::TimeDelta ReadResidency() override;
private:
// Path to the residency counter file (e.g. debugfs).
base::FilePath path_;
};
// Idle state residency
//
// This class keeps track of idle state residency counters at two points in
// time: |PreSuspend()| and |PostResume()|. Counter value is read using an
// object implementing the |ResidencyReader| interface.
class IdleResidencyTracker {
public:
// Initialize the residency tracker with all invalid values.
IdleResidencyTracker() = default;
~IdleResidencyTracker() = default;
// Initialize the residency tracker for a given path and set invalid
// residency values.
explicit IdleResidencyTracker(std::shared_ptr<ResidencyReader> reader);
// Validate that current residency measurements are valid.
//
// The |pre_suspend_| or |post_resume_| can be invalid (negative) when there
// was an error reading them.
bool IsValid();
// Return the current pre-suspend measurement.
base::TimeDelta PreSuspend() const;
// Return the current post-resume measurement.
base::TimeDelta PostResume() const;
// Read the residency counter and updates the pre-suspend measurement.
void UpdatePreSuspend();
// Read the residency counter and updates the post-resume measurement.
void UpdatePostResume();
private:
// Test harness
friend class IdleResidencyTrackerTest;
// Counter-specific residency reader.
std::shared_ptr<ResidencyReader> reader_ = nullptr;
// The latest residency time read by |UpdatePreSuspend|.
base::TimeDelta pre_suspend_;
// The latest residency time read by |UpdatePostResume|.
base::TimeDelta post_resume_;
};
// Convenience struct holding enums for enumerating supported idle states.
//
// The struct provides namespace isolation similar to "enum class" but with
// implicit conversion to int without the need for static_cast<>.
struct IdleState {
enum { S0ix = 0, PC10, COUNT };
};
// Used by Daemon to report metrics by way of Chrome.
//
// This class handles the reporting of complex metrics (e.g. tracking the
// session start time and reporting related metrics after the session stops).
//
// Classes that just need to report simple metrics in response to an event
// should use the convenience functions declared in common/metrics_sender.h to
// send metrics directly.
class MetricsCollector {
public:
// Path to the CPU Package C10 residency counter based on the ACPI LPIT table.
// This counter indicates the time spent by the CPU in PC10 (in microseconds).
static constexpr char kAcpiPC10ResidencyPath[] =
"/sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us";
// Path to S0ix residency counter for big-core CPU. This counter indicates the
// time spent by the cpus in S0ix (in microseconds).
static constexpr char kBigCoreS0ixResidencyPath[] =
"/sys/kernel/debug/pmc_core/slp_s0_residency_usec";
// Path to S0ix residency counter for small-core CPU. This counter indicates
// the time spent by the cpus in S0ix (in microseconds).
static constexpr char kSmallCoreS0ixResidencyPath[] =
"/sys/kernel/debug/telemetry/s0ix_residency_usec";
// Expected overhead time to enter/exit S0ix after suspending. This is just an
// approximation to prevent aggressive warnings.
static constexpr base::TimeDelta KS0ixOverheadTime = base::Seconds(15);
// Expected overhead time for the runtime S0ix measurement which prevents
// overestimation of the residency due to reading the |time_before_suspend_|
// before |residency_trackers_| are updated. If more counters are added, this
// should be adjusted. Note that this value is just an empirical
// approximation.
static constexpr base::TimeDelta kRuntimeS0ixOverheadTime =
base::Microseconds(100);
// Path to the last-succeeded resume timestamp file, which records when the
// kernel finished to resume in MONOTONIC clock.
static constexpr char kLastSuccessResumeTimePath[] =
"/sys/power/suspend_stats/last_success_resume_time";
// Returns a copy of |enum_name| with a suffix describing |power_source|
// appended to it. Public so it can be called by tests.
static std::string AppendPowerSourceToEnumName(const std::string& enum_name,
PowerSource power_source);
// Returns a copy of |enum_name| with a suffix describing privacy screen state
// |state| appended to it. Public so it can be called by tests.
static std::string AppendPrivacyScreenStateToEnumName(
const std::string& enum_name,
const privacy_screen::PrivacyScreenSetting_PrivacyScreenState& state);
// Calculates an idle state residency percentage that should be reported
// as part of UMA metrics by MetricsCollector.
static int GetExpectedResidencyPercent(
const base::TimeDelta& reference_time,
const base::TimeDelta& actual_residency,
const base::TimeDelta& overhead = MetricsCollector::KS0ixOverheadTime);
MetricsCollector();
MetricsCollector(const MetricsCollector&) = delete;
MetricsCollector& operator=(const MetricsCollector&) = delete;
~MetricsCollector();
// Initializes the object and starts |generate_backlight_metrics_timer_|.
// Ownership of pointers remains with the caller.
void Init(PrefsInterface* prefs,
policy::BacklightController* display_backlight_controller,
policy::BacklightController* keyboard_backlight_controller,
const system::PowerStatus& power_status,
bool first_run_after_boot);
// Records changes to system state.
void HandleScreenDimmedChange(bool dimmed,
base::TimeTicks last_user_activity_time);
void HandleScreenOffChange(bool off, base::TimeTicks last_user_activity_time);
void HandleSessionStateChange(SessionState state);
void HandlePowerStatusUpdate(const system::PowerStatus& status);
void HandleShutdown(ShutdownReason reason, bool in_dark_resume);
void HandlePrivacyScreenStateChange(
const privacy_screen::PrivacyScreenSetting_PrivacyScreenState& state);
// Called at the beginning of a suspend request (which may consist of multiple
// suspend attempts).
void PrepareForSuspend();
// Called at the end of a successful suspend request. |num_suspend_attempts|
// contains the number of attempts up to and including the one in which the
// system successfully suspended..
void HandleResume(int num_suspend_attempts);
// Called after a suspend request (that is, a series of one or more suspend
// attempts performed in response to e.g. the lid being closed) is canceled.
void HandleCanceledSuspendRequest(int num_suspend_attempts);
// Called after a suspend request has completed (successfully or not).
// Generates UMA metrics for dark resume. The size of |wake_durations| is the
// number of times the system woke up in dark resume during the suspend
// request and the value of each element is the time spent in dark resume for
// the corresponding wake. |suspend_duration| is the total time the system
// spent in user-visible suspend (including the time spent in dark resume).
void GenerateDarkResumeMetrics(
const std::vector<policy::Suspender::DarkResumeInfo>& wake_durations,
base::TimeDelta suspend_duration);
// Generates UMA metrics on when leaving the idle state.
void GenerateUserActivityMetrics();
// Generates UMA metrics about the current backlight level.
void GenerateBacklightLevelMetrics();
// Generates UMA metrics about dimming events.
void GenerateDimEventMetrics(DimEvent sample);
// Generates UMA metrics about locking events.
void GenerateLockEventMetrics(LockEvent sample);
// Generates UMA metrics about Hps events (dimming, locking, deferring by Hps)
// durations.
void GenerateHpsEventDurationMetrics(const std::string& event_name,
base::TimeDelta duration);
// Generates UMA metric on number of Adaptive Charging Actives.
void GenerateAdaptiveChargingActiveMetrics(bool enabled);
// Generates UMA metrics about Adaptive Charging accuracy on AC unplug.
void GenerateAdaptiveChargingUnplugMetrics(
const AdaptiveChargingState state,
const base::TimeTicks& target_time,
const base::TimeTicks& hold_start_time,
const base::TimeTicks& hold_end_time,
const base::TimeTicks& charge_finished_time,
const base::TimeDelta& time_spent_slow_charging,
double display_battery_percent);
// Handles the power button being pressed or released.
void HandlePowerButtonEvent(ButtonState state);
// Sends a metric reporting the amount of time that Chrome took to acknowledge
// a power button event.
void SendPowerButtonAcknowledgmentDelayMetric(base::TimeDelta delay);
// Sets a prefix path which is used as file system root when testing.
// Setting to an empty path removes the prefix.
void set_prefix_path_for_testing(const base::FilePath& file) {
prefix_path_for_testing_ = file;
}
// Generates UMA metrics about Ambient Light level on Resume.
void GenerateAmbientLightResumeMetrics(int lux);
// Send the metric of the time duration for the first display after kernel
// resume.
void GenerateDisplayAfterResumeDurationMsMetric();
void SendSuspendJourneyResult(SuspendJourneyResult result);
private:
friend class MetricsCollectorTest;
friend class AdaptiveChargingMetricsTest;
FRIEND_TEST(MetricsCollectorTest, BacklightLevel);
FRIEND_TEST(MetricsCollectorTest, SendMetricWithPowerSource);
FRIEND_TEST(MetricsCollectorTest, WakeReasonToHistogramName);
FRIEND_TEST(MetricsCollectorTest, GatherDarkResumeMetrics);
// These methods append the current power source to |name|.
bool SendMetricWithPowerSource(
const std::string& name, int sample, int min, int max, int num_buckets);
bool SendEnumMetricWithPowerSource(const std::string& name,
int sample,
int max);
// This method appends the current privacy screen state and the current power
// source to |name|. Metrics are only sent if privacy screen is supported. If
// privacy screen is not supported, this method returns true but does not send
// metrics.
bool SendEnumMetricWithPrivacyScreenStatePowerSource(const std::string& name,
int sample,
int max);
// Generates a battery discharge rate UMA metric sample. Returns
// true if a sample was sent to UMA, false otherwise.
void GenerateBatteryDischargeRateMetric();
// Sends a histogram sample containing the rate at which the battery
// discharged while the system was suspended if the system was on battery
// power both before suspending and after resuming. Called by
// GenerateMetricsOnPowerEvent(). Returns true if the sample was sent.
void GenerateBatteryDischargeRateWhileSuspendedMetric();
// Increments the number of user sessions that have been active on the
// current battery charge.
void IncrementNumOfSessionsPerChargeMetric();
// Generates number of sessions per charge UMA metric sample if the current
// stored value is greater then 0.
void GenerateNumOfSessionsPerChargeMetric();
// On devices that suspend to idle (S0ix), the power rail that supplies power
// to the CPU is left on. Ideally CPUs enter the lowest power state (S0ix)
// during suspend. But a malfunctioning driver/peripheral can keep the CPUs
// busy, draining the battery.
// This function processes residency values recorded by |residency_trackers_|
// and generates an UMA metric for S0ix residency rate (%) in comparison to
// suspend time. Called only post-resume from sleep when |suspend_to_idle_|
// is enabled.
void GenerateS2IdleS0ixMetrics();
// Devices capable of low-power idle (S0ix) can utilize it in runtime. If the
// package enters PC10 state, connected devices enter their appropriate
// low-power states and there are no updates on the screen so that Panel Self
// Refresh (PSR) can be activated, system can enter one of the S0ix low-power
// states. However a malfunctioning driver/peripheral can keep the system
// busy, preventing entering S0ix.
//
// This function processes residency values recorded by |residency_trackers_|
// and generates two UMA metrics:
// 1. Power.PC10RuntimeResidencyRate - tracks how much of time is spent in
// PC10 state in relation to the current runtime session length (in %).
// 2. Power.PC10inS0ixRuntimeResidencyRate - tracks how much time is spent
// in S0ix in relation to the time spent in PC10 (in %) assuming that
// PC10 residency was non-0.
// Called only pre-suspend.
void GenerateRuntimeS0ixMetrics();
// Calculates the average of a given list of battery life data points.
double CalculateRollingAverage(const std::deque<double>& battery_lives);
// Returns new FilePath after prepending |prefix_path_for_testing_| to
// given file path.
base::FilePath GetPrefixedFilePath(const base::FilePath& file_path) const;
// Parse sysfs to retrieve the timestamp of the latest kernel resume.
base::TimeTicks GetLastKernelResumedTime();
PrefsInterface* prefs_ = nullptr;
policy::BacklightController* display_backlight_controller_ = nullptr;
policy::BacklightController* keyboard_backlight_controller_ = nullptr;
Clock clock_;
// Last power status passed to HandlePowerStatusUpdate().
system::PowerStatus last_power_status_;
// Current session state.
SessionState session_state_ = SessionState::STOPPED;
// Time at which the current session (if any) started.
base::TimeTicks session_start_time_;
// Runs GenerateBacklightLevelMetric().
base::RepeatingTimer generate_backlight_metrics_timer_;
// Last privacy screen state that we have been informed of.
privacy_screen::PrivacyScreenSetting_PrivacyScreenState
privacy_screen_state_ =
privacy_screen::PrivacyScreenSetting_PrivacyScreenState_NOT_SUPPORTED;
// Timestamp of the last generated battery discharge rate metric.
base::TimeTicks last_battery_discharge_rate_metric_timestamp_;
// Timestamp of the last time the power button was down.
base::TimeTicks last_power_button_down_timestamp_;
// Timestamp of the last idle event (that is, either
// |screen_dim_timestamp_| or |screen_off_timestamp_|).
base::TimeTicks last_idle_event_timestamp_;
// Idle duration as of the last idle event.
base::TimeDelta last_idle_timedelta_;
// Timestamps of the last idle-triggered power state transitions.
base::TimeTicks screen_dim_timestamp_;
base::TimeTicks screen_off_timestamp_;
// Information recorded by PrepareForSuspend() just before the system
// suspends. |time_before_suspend_| is initialized using CLOCK_BOOTTIME,
// which is identical to CLOCK_MONOTONIC, but includes any time spent in
// suspend.
double battery_energy_before_suspend_ = 0.0;
int battery_percent_before_suspend_ = 0;
bool on_line_power_before_suspend_ = false;
base::TimeTicks time_before_suspend_;
// Time recorded from the CLOCK_BOOTTIME source just after system
// resumes which is then used in S0ix runtime reporting.
base::TimeTicks time_after_resume_;
// Set by HandleResume() to indicate that
// GenerateBatteryDischargeRateWhileSuspendedMetric() should send a
// sample when it is next called.
bool report_battery_discharge_rate_while_suspended_ = false;
// Max residency that |s0ix_residency_path_| can report. On big-core
// platforms the default value is set to 100*UINT32_MAX in the Init().
base::TimeDelta max_s0ix_residency_ = base::TimeDelta::Max();
// Lists all idle state residency trackers initialized to update on
// |PrepareForSuspend| and |HandleResume|.
IdleResidencyTracker residency_trackers_[IdleState::COUNT];
// True if suspend to idle (S0ix) is enabled.
bool suspend_to_idle_ = false;
// Timestamp of the start of the last suspend in CLOCK_MONOTONIC.
// This is used for checking last kernel resume timestamp is sane.
base::TimeTicks time_before_suspend_monotonic_;
// If non-empty, contains a temp dir that will be prepended to paths.
base::FilePath prefix_path_for_testing_;
// For calculating battery life rolling averages.
std::deque<double> rolling_average_actual_{};
std::deque<double> rolling_average_design_{};
};
} // namespace metrics
} // namespace power_manager
#endif // POWER_MANAGER_POWERD_METRICS_COLLECTOR_H_