| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/power/suspend_perf_reporter.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/functional/bind.h" |
| #include "base/logging.h" |
| #include "base/metrics/dummy_histogram.h" |
| #include "base/metrics/histogram.h" |
| #include "base/metrics/histogram_base.h" |
| #include "base/metrics/statistics_recorder.h" |
| #include "base/strings/strcat.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr base::TimeDelta kDelayBetweenSnapshot = base::Minutes(1); |
| |
| constexpr char kAfterResumeMetricSuffix[] = ".1MinAfterResume"; |
| |
| // UMA metrics to collect for 1 minutes after resuming the device. |
| // |
| // The metrics to take snapshots should satisfy these conditions. |
| // |
| // * The metrics should be submitted frequently enough in the 1 minute window. |
| // * The metrics should be submitted on the browser process mainly. |
| // `HistogramBase::SnapshotSamples()` may not contains samples of |
| // sub-processes depending on the timing. |
| static const constexpr std::string_view kMetricNames[] = { |
| // Browser.MainThreadsCongestion is submitted every 30 seconds on the |
| // browser process. At least 1 data point is collected in 1 minute window. |
| "Browser.MainThreadsCongestion", |
| // Ash.EventLatency.MousePressed.TotalLatency is submitted on the main |
| // thread of the browser process every frame drop which is frequent enough. |
| "Ash.EventLatency.MousePressed.TotalLatency", |
| // The characteristcs of Ash.EventLatency.KeyPressed.TotalLatency is the |
| // same as Ash.EventLatency.MousePressed.TotalLatency. |
| "Ash.EventLatency.KeyPressed.TotalLatency", |
| // Graphics.Smoothness.PercentDroppedFrames3.AllAnimations is submitted |
| // every 100 frames which is less than 2 seconds if it is 60fps. |
| // This is mainly submitted on the main thread of the browser process, but |
| // renderer process can submit. |
| "Graphics.Smoothness.PercentDroppedFrames3.AllAnimations", |
| // The characteristcs of |
| // Graphics.Smoothness.PercentDroppedFrames3.AllInteractions is the same as |
| // Graphics.Smoothness.PercentDroppedFrames3.AllAnimations. |
| "Graphics.Smoothness.PercentDroppedFrames3.AllInteractions"}; |
| |
| struct MetricsSnapshot { |
| std::string_view histogram_name; |
| std::unique_ptr<base::HistogramSamples> samples; |
| }; |
| |
| void OneMinuteAfterResume(std::vector<MetricsSnapshot> snapshots) { |
| DCHECK(snapshots.size() == std::size(kMetricNames)); |
| |
| for (auto& snapshot : snapshots) { |
| base::HistogramBase* histogram_base = |
| base::StatisticsRecorder::FindHistogram(snapshot.histogram_name); |
| if (!histogram_base) { |
| continue; |
| } |
| |
| switch (histogram_base->GetHistogramType()) { |
| case base::HistogramType::HISTOGRAM: |
| case base::HistogramType::LINEAR_HISTOGRAM: |
| case base::HistogramType::BOOLEAN_HISTOGRAM: |
| case base::HistogramType::CUSTOM_HISTOGRAM: |
| // These 4 histogram types inherit base::Histogram. |
| break; |
| default: |
| DLOG(FATAL) << "SuspendPerfReporter does not support histogram type of " |
| << snapshot.histogram_name; |
| continue; |
| } |
| base::Histogram* histogram = static_cast<base::Histogram*>(histogram_base); |
| |
| std::unique_ptr<base::HistogramSamples> samples = |
| histogram->SnapshotSamples(); |
| samples->Subtract(*snapshot.samples.get()); |
| |
| base::Histogram::FactoryGet( |
| base::StrCat({snapshot.histogram_name, kAfterResumeMetricSuffix}), |
| histogram->declared_min(), histogram->declared_max(), |
| histogram->bucket_count(), |
| base::HistogramBase::Flags::kUmaTargetedHistogramFlag) |
| ->AddSamples(*samples.get()); |
| } |
| } |
| |
| } // namespace |
| |
| SuspendPerfReporter::SuspendPerfReporter( |
| chromeos::PowerManagerClient* power_manager_client) { |
| observation_.Observe(power_manager_client); |
| } |
| |
| SuspendPerfReporter::~SuspendPerfReporter() = default; |
| |
| void SuspendPerfReporter::SuspendDone(base::TimeDelta duration) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| std::vector<MetricsSnapshot> snapshot_samples; |
| for (auto& histogram_name : kMetricNames) { |
| base::HistogramBase* histogram = |
| base::StatisticsRecorder::FindHistogram(histogram_name); |
| if (!histogram) { |
| histogram = base::DummyHistogram::GetInstance(); |
| } |
| snapshot_samples.push_back({histogram_name, histogram->SnapshotSamples()}); |
| } |
| |
| // Note: If user suspends and resumes the device again in a minute, the |
| // previous metrics is replaced with the new data. |
| timer_.Start( |
| FROM_HERE, kDelayBetweenSnapshot, |
| base::BindOnce(&OneMinuteAfterResume, std::move(snapshot_samples))); |
| } |
| |
| } // namespace ash |