Skip to content

Commit

Permalink
Add one-time CPU usage tracking. (youtube#3755)
Browse files Browse the repository at this point in the history
b/341774149
  • Loading branch information
aee-google committed Jul 9, 2024
1 parent 7e6a57e commit c9b5dc3
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 38 deletions.
39 changes: 34 additions & 5 deletions cobalt/black_box_tests/testdata/cpu_usage_tracker_test.html
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.'));
Expand Down Expand Up @@ -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);
</script>
</body>
</html>
119 changes: 91 additions & 28 deletions cobalt/browser/cpu_usage_tracker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -55,7 +56,7 @@ struct Config {
std::vector<Interval> intervals;
};

base::Value GetDefaultConfigValue() {
base::Value GetDefaultIntervalsValue() {
return base::Value(
base::Value::List()
.Append(
Expand All @@ -66,19 +67,20 @@ base::Value GetDefaultConfigValue() {
.Append(base::Value::Dict().Set("type", "total").Set("seconds", 30)));
}

Config GetDefaultConfig() {
return Config({
std::vector<Interval> GetDefaultIntervals() {
return std::vector<Interval>({
Interval(IntervalType::PER_THREAD, /*seconds=*/2),
Interval(IntervalType::PER_THREAD, /*seconds=*/30),
Interval(IntervalType::TOTAL, /*seconds=*/2),
Interval(IntervalType::TOTAL, /*seconds=*/30),
});
}

absl::optional<Config> ParseConfig(const base::Value& config_value) {
const base::Value::List* list = config_value.GetIfList();
absl::optional<std::vector<Interval>> ParseIntervals(
const base::Value& intervals_value) {
const base::Value::List* list = intervals_value.GetIfList();
if (!list) return absl::nullopt;
Config config;
std::vector<Interval> intervals;
for (auto& item_value : *list) {
const base::Value::Dict* item = item_value.GetIfDict();
if (!item) return absl::nullopt;
Expand All @@ -94,9 +96,9 @@ absl::optional<Config> 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
Expand All @@ -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<base::CVal<std::string, base::CValPublic>>(
"CPU.PerThread.Usage.OneTime",
/*initial_value=*/"", /*description=*/"")),
cval_one_time_tracking_total_(
std::make_unique<base::CVal<double, base::CValPublic>>(
"CPU.Total.Usage.OneTime",
/*initial_value=*/0.0, /*description=*/"")) {
base::CurrentThread::Get()->AddDestructionObserver(this);
}

Expand All @@ -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() {
Expand Down Expand Up @@ -162,7 +178,7 @@ void CpuUsageTracker::CreatePerThreadIntervalContext(int interval_seconds) {
/*initial_value=*/"", /*description=*/"");
context->timer = std::make_unique<base::RepeatingTimer>(
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());
Expand All @@ -182,34 +198,81 @@ void CpuUsageTracker::CreateTotalIntervalContext(int interval_seconds) {
/*initial_value=*/0.0, /*description=*/"");
context->timer = std::make_unique<base::RepeatingTimer>(
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<std::string> 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> config = ParseConfig(config_value);
if (!config.has_value()) {
base::Value default_config = GetDefaultConfigValue();
config = GetDefaultConfig();
storage_->Set(kSettingKey, std::move(default_config));
absl::optional<std::vector<Interval>> 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<int> total_intervals_processed;
std::set<int> 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 "
Expand All @@ -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();
Expand All @@ -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();
Expand Down
16 changes: 13 additions & 3 deletions cobalt/browser/cpu_usage_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,18 @@ 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<CpuUsageTracker>;
CpuUsageTracker();
~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);
Expand Down Expand Up @@ -85,6 +87,14 @@ class CpuUsageTracker : base::CurrentThread::DestructionObserver {

persistent_storage::PersistentSettings* storage_;
scoped_refptr<base::SequencedTaskRunner> task_runner_;

bool one_time_tracking_started_;
std::unique_ptr<base::CVal<double, base::CValPublic>>
cval_one_time_tracking_total_;
base::TimeDelta one_time_tracking_total_at_start_;
std::unique_ptr<base::CVal<std::string, base::CValPublic>>
cval_one_time_tracking_per_thread_;
std::unique_ptr<base::Value> one_time_tracking_per_thread_at_start_;
};

} // namespace browser
Expand Down
27 changes: 26 additions & 1 deletion cobalt/doc/cvals.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
12 changes: 11 additions & 1 deletion cobalt/h5vcc/h5vcc_settings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,20 @@ bool H5vccSettings::Set(const std::string& name, SetValueType value) const {
value.IsType<std::string>() && value.AsType<std::string>().size() < 512) {
absl::optional<base::Value> config =
base::JSONReader::Read(value.AsType<std::string>());
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<int32>()) {
bool started = value.AsType<int32>() != 0;
if (started) {
browser::CpuUsageTracker::GetInstance()->StartOneTimeTracking();
} else {
browser::CpuUsageTracker::GetInstance()->StopAndCaptureOneTimeTracking();
}
return true;
}

if (name.compare(kSkiaRasterizer) == 0 && value.IsType<int32>()) {
if (!persistent_settings_) {
Expand Down

0 comments on commit c9b5dc3

Please sign in to comment.