From c9b5dc340fd23d2040bca750a73735b1843cd85b Mon Sep 17 00:00:00 2001 From: aee Date: Tue, 9 Jul 2024 12:52:53 -0700 Subject: [PATCH] Add one-time CPU usage tracking. (#3755) b/341774149 --- .../testdata/cpu_usage_tracker_test.html | 39 +++++- cobalt/browser/cpu_usage_tracker.cc | 119 +++++++++++++----- cobalt/browser/cpu_usage_tracker.h | 16 ++- cobalt/doc/cvals.md | 27 +++- cobalt/h5vcc/h5vcc_settings.cc | 12 +- 5 files changed, 175 insertions(+), 38 deletions(-) diff --git a/cobalt/black_box_tests/testdata/cpu_usage_tracker_test.html b/cobalt/black_box_tests/testdata/cpu_usage_tracker_test.html index 0a2206619f9e..5e860e421fd8 100644 --- a/cobalt/black_box_tests/testdata/cpu_usage_tracker_test.html +++ b/cobalt/black_box_tests/testdata/cpu_usage_tracker_test.html @@ -25,9 +25,10 @@ setTimeout(resolve, durationMs); }); setupFinished(); -const settingsKey = 'cpu_usage_tracker_intervals'; +window.h5vcc.settings.set('cpu_usage_tracker_one_time_tracking', 0); + // Set invalid config. -window.h5vcc.settings.set(settingsKey, ''); +window.h5vcc.settings.set('cpu_usage_tracker_intervals', ''); delay(2500).then(() => { const cvalKeys = window.h5vcc.cVal.keys().filter(k => k.startsWith('CPU.')); // Check for default CVals. @@ -69,7 +70,7 @@ {type: 'per_thread', seconds: 5}, {type: 'per_thread', seconds: 60}, ]; - window.h5vcc.settings.set(settingsKey, JSON.stringify(newConfig)); + window.h5vcc.settings.set('cpu_usage_tracker_intervals', JSON.stringify(newConfig)); return delay(1500); }).then(() => { const cvalKeys = window.h5vcc.cVal.keys().filter(k => k.startsWith('CPU.')); @@ -109,8 +110,36 @@ assertIncludes('utime', previousFirstKeys); // Set invalid config to reset to default config. - window.h5vcc.settings.set(settingsKey, ''); -}).then(onEndTest); + window.h5vcc.settings.set('cpu_usage_tracker_intervals', ''); +}).then(() => { + const cvalKeys = window.h5vcc.cVal.keys().filter(k => k.startsWith('CPU.')); + assertIncludes('CPU.PerThread.Usage.OneTime', cvalKeys); + assertIncludes('CPU.Total.Usage.OneTime', cvalKeys); + window.h5vcc.settings.set('cpu_usage_tracker_one_time_tracking', 1); + return delay(1000); +}).then(() => { + window.h5vcc.settings.set('cpu_usage_tracker_one_time_tracking', 0); + return delay(100); +}).then(() => { + const per_thread_usage = JSON.parse(window.h5vcc.cVal.getValue('CPU.PerThread.Usage.OneTime')); + assertIncludes('previous', Object.keys(per_thread_usage)); + assertIncludes('current', Object.keys(per_thread_usage)); + assertTrue(per_thread_usage.previous.length > 0); + assertTrue(per_thread_usage.current.length > 0); + const currentFirstKeys = Object.keys(per_thread_usage.current[0]); + assertIncludes('id', currentFirstKeys); + assertIncludes('name', currentFirstKeys); + assertIncludes('stime', currentFirstKeys); + assertIncludes('usage_seconds', currentFirstKeys); + assertIncludes('utime', currentFirstKeys); + const previousFirstKeys = Object.keys(per_thread_usage.previous[0]); + assertIncludes('id', previousFirstKeys); + assertIncludes('name', previousFirstKeys); + assertIncludes('stime', previousFirstKeys); + assertIncludes('usage_seconds', previousFirstKeys); + assertIncludes('utime', previousFirstKeys); + assertTrue(window.h5vcc.cVal.getValue('CPU.Total.Usage.OneTime') > 0); +}).then(onEndTest).catch(notReached); diff --git a/cobalt/browser/cpu_usage_tracker.cc b/cobalt/browser/cpu_usage_tracker.cc index 0b8607c5f2bc..56c8ec815759 100644 --- a/cobalt/browser/cpu_usage_tracker.cc +++ b/cobalt/browser/cpu_usage_tracker.cc @@ -34,7 +34,8 @@ namespace browser { namespace { -const char kSettingKey[] = "cpu_usage_tracker_intervals"; +const char kIntervals[] = "cpu_usage_tracker_intervals"; +const char kOneTimeTracking[] = "cpu_usage_tracker_one_time_tracking"; enum IntervalType { PER_THREAD, @@ -55,7 +56,7 @@ struct Config { std::vector intervals; }; -base::Value GetDefaultConfigValue() { +base::Value GetDefaultIntervalsValue() { return base::Value( base::Value::List() .Append( @@ -66,8 +67,8 @@ base::Value GetDefaultConfigValue() { .Append(base::Value::Dict().Set("type", "total").Set("seconds", 30))); } -Config GetDefaultConfig() { - return Config({ +std::vector GetDefaultIntervals() { + return std::vector({ Interval(IntervalType::PER_THREAD, /*seconds=*/2), Interval(IntervalType::PER_THREAD, /*seconds=*/30), Interval(IntervalType::TOTAL, /*seconds=*/2), @@ -75,10 +76,11 @@ Config GetDefaultConfig() { }); } -absl::optional ParseConfig(const base::Value& config_value) { - const base::Value::List* list = config_value.GetIfList(); +absl::optional> ParseIntervals( + const base::Value& intervals_value) { + const base::Value::List* list = intervals_value.GetIfList(); if (!list) return absl::nullopt; - Config config; + std::vector intervals; for (auto& item_value : *list) { const base::Value::Dict* item = item_value.GetIfDict(); if (!item) return absl::nullopt; @@ -94,9 +96,9 @@ absl::optional ParseConfig(const base::Value& config_value) { } int seconds = item->FindInt("seconds").value_or(0); if (seconds == 0) return absl::nullopt; - config.intervals.emplace_back(type, seconds); + intervals.emplace_back(type, seconds); } - return std::move(config); + return std::move(intervals); } } // namespace @@ -109,7 +111,16 @@ CpuUsageTracker* CpuUsageTracker::GetInstance() { CpuUsageTracker::CpuUsageTracker() : storage_(nullptr), - task_runner_(base::SequencedTaskRunner::GetCurrentDefault()) { + task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), + one_time_tracking_started_(false), + cval_one_time_tracking_per_thread_( + std::make_unique>( + "CPU.PerThread.Usage.OneTime", + /*initial_value=*/"", /*description=*/"")), + cval_one_time_tracking_total_( + std::make_unique>( + "CPU.Total.Usage.OneTime", + /*initial_value=*/0.0, /*description=*/"")) { base::CurrentThread::Get()->AddDestructionObserver(this); } @@ -126,9 +137,14 @@ void CpuUsageTracker::Initialize( void CpuUsageTracker::InitializeAsync() { base::ProcessMetricsHelper::PopulateClockTicksPerS(); - base::Value config; - storage_->Get(kSettingKey, &config); - UpdateConfig(config); + base::Value intervals; + storage_->Get(kIntervals, &intervals); + UpdateIntervalsDefinition(intervals); + base::Value one_time_tracking; + storage_->Get(kOneTimeTracking, &one_time_tracking); + if (one_time_tracking.GetIfBool().value_or(false)) { + StartOneTimeTracking(); + } } void CpuUsageTracker::ClearIntervalContexts() { @@ -162,7 +178,7 @@ void CpuUsageTracker::CreatePerThreadIntervalContext(int interval_seconds) { /*initial_value=*/"", /*description=*/""); context->timer = std::make_unique( FROM_HERE, base::TimeDelta::FromSecondsD(interval_seconds), - base::BindRepeating(&CpuUsageTracker::UpdatePerThread, + base::BindRepeating(&CpuUsageTracker::PerThreadIntervalTask, base::Unretained(this), uuid)); context->previous = base::Value::ToUniquePtrValue( base::ProcessMetricsHelper::GetCumulativeCPUUsagePerThread()); @@ -182,34 +198,81 @@ void CpuUsageTracker::CreateTotalIntervalContext(int interval_seconds) { /*initial_value=*/0.0, /*description=*/""); context->timer = std::make_unique( FROM_HERE, base::TimeDelta::FromSecondsD(interval_seconds), - base::BindRepeating(&CpuUsageTracker::UpdateTotal, base::Unretained(this), - uuid)); + base::BindRepeating(&CpuUsageTracker::TotalIntervalTask, + base::Unretained(this), uuid)); context->previous = base::ProcessMetricsHelper::GetCumulativeCPUUsage(); interval_contexts_.emplace(uuid, std::move(context)); interval_contexts_[uuid]->timer->Reset(); } -void CpuUsageTracker::UpdateConfig(const base::Value& config_value) { +void CpuUsageTracker::StartOneTimeTracking() { + if (!task_runner_->RunsTasksInCurrentSequence()) { + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&CpuUsageTracker::StartOneTimeTracking, + base::Unretained(this))); + return; + } + base::AutoLock auto_lock(lock_); + one_time_tracking_started_ = true; + storage_->Set(kOneTimeTracking, base::Value(true)); + one_time_tracking_per_thread_at_start_ = base::Value::ToUniquePtrValue( + base::ProcessMetricsHelper::GetCumulativeCPUUsagePerThread()); + one_time_tracking_total_at_start_ = + base::ProcessMetricsHelper::GetCumulativeCPUUsage(); +} + +void CpuUsageTracker::StopAndCaptureOneTimeTracking() { + if (!one_time_tracking_started_) return; + if (!task_runner_->RunsTasksInCurrentSequence()) { + task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&CpuUsageTracker::StopAndCaptureOneTimeTracking, + base::Unretained(this))); + return; + } + base::AutoLock auto_lock(lock_); + one_time_tracking_started_ = false; + storage_->Set(kOneTimeTracking, base::Value(false)); + { + base::Value current = + std::move(base::ProcessMetricsHelper::GetCumulativeCPUUsagePerThread()); + absl::optional serialized = base::WriteJson(base::Value( + base::Value::Dict() + .Set("previous", one_time_tracking_per_thread_at_start_->Clone()) + .Set("current", current.Clone()))); + DCHECK(serialized.has_value()); + *cval_one_time_tracking_per_thread_ = *serialized; + } + { + base::TimeDelta current = + base::ProcessMetricsHelper::GetCumulativeCPUUsage(); + *cval_one_time_tracking_total_ = + (current - one_time_tracking_total_at_start_).InSecondsF(); + } +} + +void CpuUsageTracker::UpdateIntervalsDefinition( + const base::Value& intervals_value) { if (!task_runner_->RunsTasksInCurrentSequence()) { task_runner_->PostTask( FROM_HERE, - base::BindOnce(&CpuUsageTracker::UpdateConfig, base::Unretained(this), - config_value.Clone())); + base::BindOnce(&CpuUsageTracker::UpdateIntervalsDefinition, + base::Unretained(this), intervals_value.Clone())); return; } base::AutoLock auto_lock(lock_); - absl::optional config = ParseConfig(config_value); - if (!config.has_value()) { - base::Value default_config = GetDefaultConfigValue(); - config = GetDefaultConfig(); - storage_->Set(kSettingKey, std::move(default_config)); + absl::optional> intervals = + ParseIntervals(intervals_value); + if (!intervals.has_value()) { + storage_->Set(kIntervals, GetDefaultIntervalsValue()); + intervals = GetDefaultIntervals(); } else { - storage_->Set(kSettingKey, config_value.Clone()); + storage_->Set(kIntervals, intervals_value.Clone()); } ClearIntervalContexts(); std::set total_intervals_processed; std::set per_thread_intervals_processed; - for (const Interval& interval : config->intervals) { + for (const Interval& interval : *intervals) { if (interval.type == IntervalType::PER_THREAD) { if (per_thread_intervals_processed.count(interval.seconds) == 1) { DLOG(WARNING) << "Duplicate CPU per-thread usage interval " @@ -231,7 +294,7 @@ void CpuUsageTracker::UpdateConfig(const base::Value& config_value) { } } -void CpuUsageTracker::UpdatePerThread(const base::Uuid& uuid) { +void CpuUsageTracker::PerThreadIntervalTask(const base::Uuid& uuid) { base::AutoLock auto_lock(lock_); if (interval_contexts_.count(uuid) == 0) { NOTREACHED(); @@ -250,7 +313,7 @@ void CpuUsageTracker::UpdatePerThread(const base::Uuid& uuid) { context->previous = base::Value::ToUniquePtrValue(std::move(current)); } -void CpuUsageTracker::UpdateTotal(const base::Uuid& uuid) { +void CpuUsageTracker::TotalIntervalTask(const base::Uuid& uuid) { base::AutoLock auto_lock(lock_); if (interval_contexts_.count(uuid) == 0) { NOTREACHED(); diff --git a/cobalt/browser/cpu_usage_tracker.h b/cobalt/browser/cpu_usage_tracker.h index 38c5c2752ccc..f23e95fc1b83 100644 --- a/cobalt/browser/cpu_usage_tracker.h +++ b/cobalt/browser/cpu_usage_tracker.h @@ -39,7 +39,9 @@ class CpuUsageTracker : base::CurrentThread::DestructionObserver { static CpuUsageTracker* GetInstance(); void Initialize(persistent_storage::PersistentSettings*); - void UpdateConfig(const base::Value&); + void UpdateIntervalsDefinition(const base::Value&); + void StartOneTimeTracking(); + void StopAndCaptureOneTimeTracking(); private: friend struct base::DefaultSingletonTraits; @@ -47,8 +49,8 @@ class CpuUsageTracker : base::CurrentThread::DestructionObserver { ~CpuUsageTracker(); void InitializeAsync(); - void UpdateTotal(const base::Uuid&); - void UpdatePerThread(const base::Uuid&); + void TotalIntervalTask(const base::Uuid&); + void PerThreadIntervalTask(const base::Uuid&); void ClearIntervalContexts(); void CreateTotalIntervalContext(int); void CreatePerThreadIntervalContext(int); @@ -85,6 +87,14 @@ class CpuUsageTracker : base::CurrentThread::DestructionObserver { persistent_storage::PersistentSettings* storage_; scoped_refptr task_runner_; + + bool one_time_tracking_started_; + std::unique_ptr> + cval_one_time_tracking_total_; + base::TimeDelta one_time_tracking_total_at_start_; + std::unique_ptr> + cval_one_time_tracking_per_thread_; + std::unique_ptr one_time_tracking_per_thread_at_start_; }; } // namespace browser diff --git a/cobalt/doc/cvals.md b/cobalt/doc/cvals.md index 49a1135675e5..11d002c2e51b 100644 --- a/cobalt/doc/cvals.md +++ b/cobalt/doc/cvals.md @@ -463,7 +463,21 @@ query): #### PublicCVals * **CPU.Total.Usage.IntervalSeconds.[INTERVAL_SECONDS]** - CPU usage in - seconds during the last time interval of `INTERVAL_SECONDS`. + seconds during the last time interval of `INTERVAL_SECONDS`. The intervals + are defined and enabled using `h5vcc.settings`. +``` +windows.h5vcc.settings.set('cpu_usage_tracker_intervals_enabled', 1); +windows.h5vcc.settings.set('cpu_usage_tracker_intervals', [ + {type: 'total', seconds: 1}, + {type: 'total', seconds: 3}, + {type: 'total', seconds: 4}, + {type: 'total', seconds: 120}, + {type: 'per_thread', seconds: 1}, + {type: 'per_thread', seconds: 3}, + {type: 'per_thread', seconds: 5}, + {type: 'per_thread', seconds: 60} +]); +``` * **CPU.PerThread.Usage.IntervalSeconds.[INTERVAL_SECONDS]** - CPU usage of all running threads. This is expressed as JSON in the following form. `id` is the thread ID. `name` is the thread name (not unique). `stime` is the number of @@ -495,3 +509,14 @@ query): ] } ``` +* **CPU.Total.Usage.OneTime** - CPU usage in seconds between setting the + one-time tracking on and then off. +``` +window.h5vcc.settings.set('cpu_usage_tracker_one_time_tracking', 1); +... +window.h5vcc.settings.set('cpu_usage_tracker_one_time_tracking', 0); +``` +* **CPU.PerThread.Usage.OneTime** - CPU usage of all running threads. Same + format as interval-base per-thread CPU usage CVals. The `previous` is + captured when one-time tracking is enabled. The CVal is updated with + `previous` and `current` when one-time tracking is disabled. diff --git a/cobalt/h5vcc/h5vcc_settings.cc b/cobalt/h5vcc/h5vcc_settings.cc index a78de3eae0fa..0683fcad7d5c 100644 --- a/cobalt/h5vcc/h5vcc_settings.cc +++ b/cobalt/h5vcc/h5vcc_settings.cc @@ -115,10 +115,20 @@ bool H5vccSettings::Set(const std::string& name, SetValueType value) const { value.IsType() && value.AsType().size() < 512) { absl::optional config = base::JSONReader::Read(value.AsType()); - browser::CpuUsageTracker::GetInstance()->UpdateConfig( + browser::CpuUsageTracker::GetInstance()->UpdateIntervalsDefinition( config.has_value() ? std::move(*config) : base::Value()); return true; } + if (name.compare("cpu_usage_tracker_one_time_tracking") == 0 && + value.IsType()) { + bool started = value.AsType() != 0; + if (started) { + browser::CpuUsageTracker::GetInstance()->StartOneTimeTracking(); + } else { + browser::CpuUsageTracker::GetInstance()->StopAndCaptureOneTimeTracking(); + } + return true; + } if (name.compare(kSkiaRasterizer) == 0 && value.IsType()) { if (!persistent_settings_) {