Commit b13c87d5 authored by Bailey Berro's avatar Bailey Berro Committed by Commit Bot

Add CpuUsageDataObserver to SystemDataProvider

This change adds the CpuUsageDataObvserver to SystemDataProvider.
A subsequent change will add throttling data to the CpuUsageData object

JS-side implementation of observer: https://source.chromium.org/chromium/chromium/src/+/master:chromeos/components/diagnostics_ui/resources/cpu_card.js;l=58-69

Bug: 1128204
Change-Id: I7ef493691dea63460753c56f066f6744591dbea4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2518265Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarZentaro Kavanagh <zentaro@chromium.org>
Commit-Queue: Bailey Berro <baileyberro@chromium.org>
Cr-Commit-Position: refs/heads/master@{#826471}
parent 29e3879d
......@@ -31,6 +31,7 @@ using ProbeCategories = healthd::ProbeCategoryEnum;
constexpr int kBatteryHealthRefreshIntervalInSeconds = 60;
constexpr int kChargeStatusRefreshIntervalInSeconds = 15;
constexpr int kCpuUsageRefreshIntervalInSeconds = 1;
constexpr int kMemoryUsageRefreshIntervalInSeconds = 10;
constexpr int kMilliampsInAnAmp = 1000;
......@@ -140,11 +141,44 @@ void PopulateMemoryUsage(const healthd::MemoryInfo& memory_info,
out_memory_usage.free_memory_kib = memory_info.free_memory_kib;
out_memory_usage.available_memory_kib = memory_info.available_memory_kib;
}
CpuUsageData CalculateCpuUsage(
const std::vector<healthd::LogicalCpuInfoPtr>& logical_cpu_infos) {
CpuUsageData new_usage_data;
DCHECK_GE(logical_cpu_infos.size(), 1u);
for (const auto& logical_cpu_ptr : logical_cpu_infos) {
new_usage_data += CpuUsageData(logical_cpu_ptr->user_time_user_hz,
logical_cpu_ptr->system_time_user_hz,
logical_cpu_ptr->idle_time_user_hz);
}
return new_usage_data;
}
void PopulateCpuUsagePercentages(const CpuUsageData& new_usage,
const CpuUsageData& old_usage,
mojom::CpuUsage& out_cpu_usage) {
CpuUsageData delta = new_usage - old_usage;
const uint64_t total_delta = delta.GetTotalTime();
if (total_delta == 0) {
return;
}
// Mulitply by 100 to convert to percentages.
out_cpu_usage.percent_usage_user = 100 * delta.GetUserTime() / total_delta;
out_cpu_usage.percent_usage_system =
100 * delta.GetSystemTime() / total_delta;
out_cpu_usage.percent_usage_free = 100 * delta.GetIdleTime() / total_delta;
}
} // namespace
SystemDataProvider::SystemDataProvider() {
battery_charge_status_timer_ = std::make_unique<base::RepeatingTimer>();
battery_health_timer_ = std::make_unique<base::RepeatingTimer>();
cpu_usage_timer_ = std::make_unique<base::RepeatingTimer>();
memory_usage_timer_ = std::make_unique<base::RepeatingTimer>();
PowerManagerClient::Get()->AddObserver(this);
}
......@@ -214,6 +248,21 @@ void SystemDataProvider::ObserveMemoryUsage(
UpdateMemoryUsage();
}
void SystemDataProvider::ObserveCpuUsage(
mojo::PendingRemote<mojom::CpuUsageObserver> observer) {
cpu_usage_observers_.Add(std::move(observer));
if (!cpu_usage_timer_->IsRunning()) {
previous_cpu_usage_data_ = CpuUsageData();
cpu_usage_timer_->Start(
FROM_HERE,
base::TimeDelta::FromSeconds(kCpuUsageRefreshIntervalInSeconds),
base::BindRepeating(&SystemDataProvider::UpdateCpuUsage,
base::Unretained(this)));
}
UpdateCpuUsage();
}
void SystemDataProvider::PowerChanged(
const power_manager::PowerSupplyProperties& proto) {
if (battery_charge_status_observers_.empty()) {
......@@ -248,6 +297,11 @@ void SystemDataProvider::SetMemoryUsageTimerForTesting(
memory_usage_timer_ = std::move(timer);
}
void SystemDataProvider::SetCpuUsageTimerForTesting(
std::unique_ptr<base::RepeatingTimer> timer) {
cpu_usage_timer_ = std::move(timer);
}
void SystemDataProvider::OnSystemInfoProbeResponse(
GetSystemInfoCallback callback,
healthd::TelemetryInfoPtr info_ptr) {
......@@ -348,6 +402,15 @@ void SystemDataProvider::UpdateMemoryUsage() {
base::Unretained(this)));
}
void SystemDataProvider::UpdateCpuUsage() {
BindCrosHealthdProbeServiceIfNeccessary();
probe_service_->ProbeTelemetryInfo(
{ProbeCategories::kCpu},
base::BindOnce(&SystemDataProvider::OnCpuUsageUpdated,
base::Unretained(this)));
}
void SystemDataProvider::OnBatteryChargeStatusUpdated(
const base::Optional<PowerSupplyProperties>& power_supply_properties,
healthd::TelemetryInfoPtr info_ptr) {
......@@ -429,6 +492,55 @@ void SystemDataProvider::OnMemoryUsageUpdated(
NotifyMemoryUsageObservers(memory_usage);
}
void SystemDataProvider::OnCpuUsageUpdated(healthd::TelemetryInfoPtr info_ptr) {
mojom::CpuUsagePtr cpu_usage = mojom::CpuUsage::New();
if (info_ptr.is_null()) {
LOG(ERROR) << "Null response from croshealthd::ProbeTelemetryInfo.";
NotifyCpuUsageObservers(cpu_usage);
cpu_usage_timer_.reset();
return;
}
const healthd::CpuInfo* cpu_info = GetCpuInfo(*info_ptr);
if (cpu_info == nullptr) {
LOG(ERROR) << "No CpuInfo in response from cros_healthd.";
NotifyCpuUsageObservers(cpu_usage);
cpu_usage_timer_.reset();
return;
}
ComputeAndPopulateCpuUsage(*cpu_info, *cpu_usage.get());
NotifyCpuUsageObservers(cpu_usage);
}
void SystemDataProvider::ComputeAndPopulateCpuUsage(
const healthd::CpuInfo& cpu_info,
mojom::CpuUsage& out_cpu_usage) {
// For simplicity, assume that all devices have just one physical CPU, made
// up of one or more virtual CPUs.
// TODO(baileyberro): Handle devices with multiple physical CPUs.
if (cpu_info.physical_cpus.size() > 1) {
LOG(ERROR) << "Device has more than one physical CPU";
}
const healthd::PhysicalCpuInfoPtr& physical_cpu_ptr =
cpu_info.physical_cpus[0];
CpuUsageData new_usage_data =
CalculateCpuUsage(physical_cpu_ptr->logical_cpus);
// We use the first usage data we get back as a baseline. On subsequent
// fetches, we use the previous cumulative data to calculate a delta.
if (previous_cpu_usage_data_.IsInitialized()) {
PopulateCpuUsagePercentages(new_usage_data, previous_cpu_usage_data_,
out_cpu_usage);
}
previous_cpu_usage_data_ = new_usage_data;
}
void SystemDataProvider::NotifyBatteryChargeStatusObservers(
const mojom::BatteryChargeStatusPtr& battery_charge_status) {
for (auto& observer : battery_charge_status_observers_) {
......@@ -450,6 +562,13 @@ void SystemDataProvider::NotifyMemoryUsageObservers(
}
}
void SystemDataProvider::NotifyCpuUsageObservers(
const mojom::CpuUsagePtr& cpu_usage) {
for (auto& observer : cpu_usage_observers_) {
observer->OnCpuUsageUpdated(cpu_usage.Clone());
}
}
void SystemDataProvider::BindCrosHealthdProbeServiceIfNeccessary() {
if (!probe_service_ || !probe_service_.is_connected()) {
cros_healthd::ServiceConnection::GetInstance()->GetProbeService(
......
......@@ -8,6 +8,7 @@
#include <memory>
#include "base/optional.h"
#include "chromeos/components/diagnostics_ui/backend/cpu_usage_data.h"
#include "chromeos/components/diagnostics_ui/mojom/system_data_provider.mojom.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom.h"
......@@ -43,6 +44,8 @@ class SystemDataProvider : public mojom::SystemDataProvider,
mojo::PendingRemote<mojom::BatteryHealthObserver> observer) override;
void ObserveMemoryUsage(
mojo::PendingRemote<mojom::MemoryUsageObserver> observer) override;
void ObserveCpuUsage(
mojo::PendingRemote<mojom::CpuUsageObserver> observer) override;
// PowerManagerClient::Observer:
void PowerChanged(const power_manager::PowerSupplyProperties& proto) override;
......@@ -59,6 +62,8 @@ class SystemDataProvider : public mojom::SystemDataProvider,
void SetMemoryUsageTimerForTesting(
std::unique_ptr<base::RepeatingTimer> timer);
void SetCpuUsageTimerForTesting(std::unique_ptr<base::RepeatingTimer> timer);
private:
void BindCrosHealthdProbeServiceIfNeccessary();
......@@ -78,6 +83,8 @@ class SystemDataProvider : public mojom::SystemDataProvider,
void UpdateMemoryUsage();
void UpdateCpuUsage();
void NotifyBatteryChargeStatusObservers(
const mojom::BatteryChargeStatusPtr& battery_charge_status);
......@@ -86,6 +93,8 @@ class SystemDataProvider : public mojom::SystemDataProvider,
void NotifyMemoryUsageObservers(const mojom::MemoryUsagePtr& memory_usage);
void NotifyCpuUsageObservers(const mojom::CpuUsagePtr& cpu_usage);
void OnBatteryChargeStatusUpdated(
const base::Optional<power_manager::PowerSupplyProperties>&
power_supply_properties,
......@@ -95,17 +104,26 @@ class SystemDataProvider : public mojom::SystemDataProvider,
void OnMemoryUsageUpdated(cros_healthd::mojom::TelemetryInfoPtr info_ptr);
void OnCpuUsageUpdated(cros_healthd::mojom::TelemetryInfoPtr info_ptr);
void ComputeAndPopulateCpuUsage(const cros_healthd::mojom::CpuInfo& cpu_info,
mojom::CpuUsage& out_cpu_usage);
CpuUsageData previous_cpu_usage_data_;
mojo::Remote<cros_healthd::mojom::CrosHealthdProbeService> probe_service_;
mojo::RemoteSet<mojom::BatteryChargeStatusObserver>
battery_charge_status_observers_;
mojo::RemoteSet<mojom::BatteryHealthObserver> battery_health_observers_;
mojo::RemoteSet<mojom::MemoryUsageObserver> memory_usage_observers_;
mojo::RemoteSet<mojom::CpuUsageObserver> cpu_usage_observers_;
mojo::Receiver<mojom::SystemDataProvider> receiver_{this};
std::unique_ptr<base::RepeatingTimer> battery_charge_status_timer_;
std::unique_ptr<base::RepeatingTimer> battery_health_timer_;
std::unique_ptr<base::RepeatingTimer> memory_usage_timer_;
std::unique_ptr<base::RepeatingTimer> cpu_usage_timer_;
};
} // namespace diagnostics
......
......@@ -15,6 +15,7 @@
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/timer/mock_timer.h"
#include "chromeos/components/diagnostics_ui/backend/cpu_usage_data.h"
#include "chromeos/components/diagnostics_ui/backend/power_manager_client_conversions.h"
#include "chromeos/dbus/cros_healthd/cros_healthd_client.h"
#include "chromeos/dbus/cros_healthd/fake_cros_healthd_client.h"
......@@ -220,6 +221,32 @@ void SetCrosHealthdMemoryUsageResponse(uint32_t total_memory_kib,
/*system_info=*/nullptr);
}
// Sets the CpuUsage response on cros_healthd. |usage_data| should contain one
// entry for each logical cpu.
void SetCrosHealthdCpuUsageResponse(
const std::vector<CpuUsageData>& usage_data) {
auto cpu_info_ptr = cros_healthd::mojom::CpuInfo::New();
auto physical_cpu_info_ptr = cros_healthd::mojom::PhysicalCpuInfo::New();
for (const auto& data : usage_data) {
auto logical_cpu_info_ptr = cros_healthd::mojom::LogicalCpuInfo::New();
logical_cpu_info_ptr->user_time_user_hz = data.GetUserTime();
logical_cpu_info_ptr->system_time_user_hz = data.GetSystemTime();
logical_cpu_info_ptr->idle_time_user_hz = data.GetIdleTime();
physical_cpu_info_ptr->logical_cpus.emplace_back(
std::move(logical_cpu_info_ptr));
}
cpu_info_ptr->physical_cpus.emplace_back(std::move(physical_cpu_info_ptr));
SetProbeTelemetryInfoResponse(/*battery_info=*/nullptr,
std::move(cpu_info_ptr),
/*memory_info=*/nullptr,
/*system_info=*/nullptr);
}
bool AreValidPowerTimes(int64_t time_to_full, int64_t time_to_empty) {
// Exactly one of |time_to_full| or |time_to_empty| must be zero. The other
// can be a positive integer to represent the time to charge/discharge or -1
......@@ -338,6 +365,15 @@ void VerifyMemoryUsageResult(const mojom::MemoryUsagePtr& update,
EXPECT_EQ(expected_available_memory_kib, update->available_memory_kib);
}
void VerifyCpuUsageResult(const mojom::CpuUsagePtr& update,
uint8_t expected_percent_user,
uint8_t expected_percent_system,
uint8_t expected_percent_free) {
EXPECT_EQ(expected_percent_user, update->percent_usage_user);
EXPECT_EQ(expected_percent_system, update->percent_usage_system);
EXPECT_EQ(expected_percent_free, update->percent_usage_free);
}
} // namespace
struct FakeBatteryChargeStatusObserver
......@@ -345,7 +381,7 @@ struct FakeBatteryChargeStatusObserver
// mojom::BatteryChargeStatusObserver
void OnBatteryChargeStatusUpdated(
mojom::BatteryChargeStatusPtr status_ptr) override {
updates.emplace_back(std::move(status_ptr));
updates.push_back(std::move(status_ptr));
}
// Tracks calls to OnBatteryChargeStatusUpdated. Each call adds an element to
......@@ -358,7 +394,7 @@ struct FakeBatteryChargeStatusObserver
struct FakeBatteryHealthObserver : public mojom::BatteryHealthObserver {
// mojom::BatteryHealthObserver
void OnBatteryHealthUpdated(mojom::BatteryHealthPtr status_ptr) override {
updates.emplace_back(std::move(status_ptr));
updates.push_back(std::move(status_ptr));
}
// Tracks calls to OnBatteryHealthUpdated. Each call adds an element to
......@@ -371,7 +407,7 @@ struct FakeBatteryHealthObserver : public mojom::BatteryHealthObserver {
struct FakeMemoryUsageObserver : public mojom::MemoryUsageObserver {
// mojom::MemoryUsageObserver
void OnMemoryUsageUpdated(mojom::MemoryUsagePtr status_ptr) override {
updates.emplace_back(std::move(status_ptr));
updates.push_back(std::move(status_ptr));
}
// Tracks calls to OnMemoryUsageUpdated. Each call adds an element to
......@@ -381,6 +417,19 @@ struct FakeMemoryUsageObserver : public mojom::MemoryUsageObserver {
mojo::Receiver<mojom::MemoryUsageObserver> receiver{this};
};
struct FakeCpuUsageObserver : public mojom::CpuUsageObserver {
// mojom::CpuUsageObserver
void OnCpuUsageUpdated(mojom::CpuUsagePtr status_ptr) override {
updates.push_back(std::move(status_ptr));
}
// Tracks calls to OnCpuUsageUpdated. Each call adds an element to
// the vector.
std::vector<mojom::CpuUsagePtr> updates;
mojo::Receiver<mojom::CpuUsageObserver> receiver{this};
};
class SystemDataProviderTest : public testing::Test {
public:
SystemDataProviderTest() {
......@@ -647,5 +696,91 @@ TEST_F(SystemDataProviderTest, MemoryUsageObserver) {
free_memory_kib, new_available_memory_kib);
}
TEST_F(SystemDataProviderTest, CpuUsageObserverOneProcessor) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
// Setup initial data
CpuUsageData core_1(1000, 1000, 1000);
SetCrosHealthdCpuUsageResponse({core_1});
// Registering as an observer should trigger one update.
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
// There should be one update with no percentages since we could not calculate
// a delta yet.
EXPECT_EQ(1u, cpu_usage_observer.updates.size());
VerifyCpuUsageResult(cpu_usage_observer.updates[0],
/*expected_percent_user=*/0,
/*expected_percent_system=*/0,
/*expected_percent_free=*/0);
CpuUsageData delta(3000, 2500, 4500);
SetCrosHealthdCpuUsageResponse({core_1 + delta});
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(2u, cpu_usage_observer.updates.size());
VerifyCpuUsageResult(cpu_usage_observer.updates[1],
delta.GetUserTime() * 100 / delta.GetTotalTime(),
delta.GetSystemTime() * 100 / delta.GetTotalTime(),
delta.GetIdleTime() * 100 / delta.GetTotalTime());
}
TEST_F(SystemDataProviderTest, CpuUsageObserverTwoProcessor) {
// Setup Timer
auto timer = std::make_unique<base::MockRepeatingTimer>();
auto* timer_ptr = timer.get();
system_data_provider_->SetCpuUsageTimerForTesting(std::move(timer));
// Setup initial data
CpuUsageData core_1(1000, 1000, 1000);
CpuUsageData core_2(2000, 2000, 2000);
SetCrosHealthdCpuUsageResponse({core_1, core_2});
// Registering as an observer should trigger one update.
FakeCpuUsageObserver cpu_usage_observer;
system_data_provider_->ObserveCpuUsage(
cpu_usage_observer.receiver.BindNewPipeAndPassRemote());
base::RunLoop().RunUntilIdle();
// There should be one update with no percentages since we could not calculate
// a delta yet.
EXPECT_EQ(1u, cpu_usage_observer.updates.size());
VerifyCpuUsageResult(cpu_usage_observer.updates[0],
/*expected_percent_user=*/0,
/*expected_percent_system=*/0,
/*expected_percent_free=*/0);
CpuUsageData core_1_delta(3000, 2500, 4500);
CpuUsageData core_2_delta(1000, 5500, 3500);
SetCrosHealthdCpuUsageResponse(
{core_1 + core_1_delta, core_2 + core_2_delta});
timer_ptr->Fire();
base::RunLoop().RunUntilIdle();
// The result should be the averages of the times from the two cores.
const int64_t expected_percent_user = 20;
const int64_t expected_percent_system = 40;
const int64_t expected_percent_free = 40;
EXPECT_EQ(2u, cpu_usage_observer.updates.size());
VerifyCpuUsageResult(cpu_usage_observer.updates[1], expected_percent_user,
expected_percent_system, expected_percent_free);
}
} // namespace diagnostics
} // namespace chromeos
......@@ -71,6 +71,12 @@ struct MemoryUsage {
uint32 free_memory_kib;
};
struct CpuUsage {
uint8 percent_usage_user;
uint8 percent_usage_system;
uint8 percent_usage_free;
};
// Implemented by clients that wish to be updated periodically about changes to
// the battery charge status.
interface BatteryChargeStatusObserver {
......@@ -99,6 +105,15 @@ interface MemoryUsageObserver {
OnMemoryUsageUpdated(MemoryUsage memory_usage);
};
// Implemented by clients that wish to be updated periodically about the
// cpu usage of the device.
interface CpuUsageObserver {
// OnCpuUsageUpdated calls can be triggered by either of 2 conditions:
// 1) A CpuUsageObserver is registered with SystemDataProvider
// 2) A periodic update is sent by SystemDataProvider
OnCpuUsageUpdated(CpuUsage cpu_usage);
};
// Provides telemetric information about the system. This API is exposed to the
// Diagnostics SWA.
interface SystemDataProvider {
......@@ -120,4 +135,7 @@ interface SystemDataProvider {
// Registers an observer of MemoryUsage information.
ObserveMemoryUsage(pending_remote<MemoryUsageObserver> observer);
// Registers an observer of CpuUsage information.
ObserveCpuUsage(pending_remote<CpuUsageObserver> observer);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment