Skip to content

Commit

Permalink
Added CPU temp information reporting.
Browse files Browse the repository at this point in the history
Device status collector collects also CPU temp information
to upload it to server.
Added a repeated field to DeviceStatusReportRequest, that contains cpu temp information: temperature and label.

BUG=499906

Review URL: https://codereview.chromium.org/1244383002

Cr-Commit-Position: refs/heads/master@{#341301}
  • Loading branch information
pbond authored and Commit bot committed Jul 31, 2015
1 parent 0ac4c73 commit 0d55b1a
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ void DeviceCloudPolicyManagerChromeOS::CreateStatusUploader() {
local_state_, chromeos::system::StatisticsProvider::GetInstance(),
DeviceStatusCollector::LocationUpdateRequester(),
DeviceStatusCollector::VolumeInfoFetcher(),
DeviceStatusCollector::CPUStatisticsFetcher())),
DeviceStatusCollector::CPUStatisticsFetcher(),
DeviceStatusCollector::CPUTempFetcher())),
task_runner_));
}

Expand Down
96 changes: 94 additions & 2 deletions chrome/browser/chromeos/policy/device_status_collector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/location.h"
Expand All @@ -22,6 +23,7 @@
#include "base/prefs/pref_service.h"
#include "base/prefs/scoped_user_pref_update.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h"
#include "base/values.h"
Expand Down Expand Up @@ -85,6 +87,12 @@ const char kTimestamp[] = "timestamp";
// The location we read our CPU statistics from.
const char kProcStat[] = "/proc/stat";

// The location we read our CPU temperature and channel label from.
const char kHwmonDir[] = "/sys/class/hwmon/";
const char kDeviceDir[] = "device";
const char kHwmonDirectoryPattern[] = "hwmon*";
const char kCPUTempFilePattern[] = "temp*_input";

// Determine the day key (milliseconds since epoch for corresponding day in UTC)
// for a given |timestamp|.
int64 TimestampToDayKey(Time timestamp) {
Expand Down Expand Up @@ -139,6 +147,64 @@ std::string ReadCPUStatistics() {
return std::string();
}

// Reads the CPU temperature info from
// /sys/class/hwmon/hwmon*/device/temp*_input and
// /sys/class/hwmon/hwmon*/device/temp*_label files.
//
// temp*_input contains CPU temperature in millidegree Celsius
// temp*_label contains appropriate temperature channel label.
std::vector<em::CPUTempInfo> ReadCPUTempInfo() {
std::vector<em::CPUTempInfo> contents;
// Get directories /sys/class/hwmon/hwmon*
base::FileEnumerator hwmon_enumerator(base::FilePath(kHwmonDir), false,
base::FileEnumerator::DIRECTORIES,
kHwmonDirectoryPattern);

for (base::FilePath hwmon_path = hwmon_enumerator.Next(); !hwmon_path.empty();
hwmon_path = hwmon_enumerator.Next()) {
// Get files /sys/class/hwmon/hwmon*/device/temp*_input
const base::FilePath hwmon_device_dir = hwmon_path.Append(kDeviceDir);
base::FileEnumerator enumerator(hwmon_device_dir, false,
base::FileEnumerator::FILES,
kCPUTempFilePattern);
for (base::FilePath temperature_path = enumerator.Next();
!temperature_path.empty(); temperature_path = enumerator.Next()) {
// Get appropriate temp*_label file.
std::string label_path = temperature_path.MaybeAsASCII();
if (label_path.empty()) {
LOG(WARNING) << "Unable to parse a path to temp*_input file as ASCII";
continue;
}
base::ReplaceSubstringsAfterOffset(&label_path, 0, "input", "label");

// Read label.
std::string label;
if (!base::PathExists(base::FilePath(label_path)) ||
!base::ReadFileToString(base::FilePath(label_path), &label)) {
label = std::string();
}

// Read temperature in millidegree Celsius.
std::string temperature_string;
int32 temperature = 0;
if (base::ReadFileToString(temperature_path, &temperature_string) &&
sscanf(temperature_string.c_str(), "%d", &temperature) == 1) {
// CPU temp in millidegree Celsius to Celsius
temperature /= 1000;

em::CPUTempInfo info;
info.set_cpu_label(label);
info.set_cpu_temp(temperature);
contents.push_back(info);
} else {
LOG(WARNING) << "Unable to read CPU temp from "
<< temperature_path.MaybeAsASCII();
}
}
}
return contents;
}

// Returns the DeviceLocalAccount associated with the current kiosk session.
// Returns null if there is no active kiosk session, or if that kiosk
// session has been removed from policy since the session started, in which
Expand Down Expand Up @@ -172,7 +238,8 @@ DeviceStatusCollector::DeviceStatusCollector(
chromeos::system::StatisticsProvider* provider,
const LocationUpdateRequester& location_update_requester,
const VolumeInfoFetcher& volume_info_fetcher,
const CPUStatisticsFetcher& cpu_statistics_fetcher)
const CPUStatisticsFetcher& cpu_statistics_fetcher,
const CPUTempFetcher& cpu_temp_fetcher)
: max_stored_past_activity_days_(kMaxStoredPastActivityDays),
max_stored_future_activity_days_(kMaxStoredFutureActivityDays),
local_state_(local_state),
Expand All @@ -182,6 +249,7 @@ DeviceStatusCollector::DeviceStatusCollector(
geolocation_update_in_progress_(false),
volume_info_fetcher_(volume_info_fetcher),
cpu_statistics_fetcher_(cpu_statistics_fetcher),
cpu_temp_fetcher_(cpu_temp_fetcher),
statistics_provider_(provider),
last_cpu_active_(0),
last_cpu_idle_(0),
Expand All @@ -201,6 +269,9 @@ DeviceStatusCollector::DeviceStatusCollector(
if (cpu_statistics_fetcher_.is_null())
cpu_statistics_fetcher_ = base::Bind(&ReadCPUStatistics);

if (cpu_temp_fetcher_.is_null())
cpu_temp_fetcher_ = base::Bind(&ReadCPUTempInfo);

idle_poll_timer_.Start(FROM_HERE,
TimeDelta::FromSeconds(kIdlePollIntervalSeconds),
this, &DeviceStatusCollector::CheckIdleState);
Expand Down Expand Up @@ -492,7 +563,7 @@ void DeviceStatusCollector::SampleHardwareStatus() {
mount_points.push_back(mount_info.first);
}

// Call out to the blocking pool to measure disk and CPU usage.
// Call out to the blocking pool to measure disk, CPU usage and CPU temp.
base::PostTaskAndReplyWithResult(
content::BrowserThread::GetBlockingPool(),
FROM_HERE,
Expand All @@ -505,6 +576,11 @@ void DeviceStatusCollector::SampleHardwareStatus() {
cpu_statistics_fetcher_,
base::Bind(&DeviceStatusCollector::ReceiveCPUStatistics,
weak_factory_.GetWeakPtr()));

base::PostTaskAndReplyWithResult(
content::BrowserThread::GetBlockingPool(), FROM_HERE, cpu_temp_fetcher_,
base::Bind(&DeviceStatusCollector::StoreCPUTempInfo,
weak_factory_.GetWeakPtr()));
}

void DeviceStatusCollector::ReceiveCPUStatistics(const std::string& stats) {
Expand Down Expand Up @@ -560,6 +636,16 @@ void DeviceStatusCollector::ReceiveCPUStatistics(const std::string& stats) {
resource_usage_.pop_front();
}

void DeviceStatusCollector::StoreCPUTempInfo(
const std::vector<em::CPUTempInfo>& info) {
if (info.empty()) {
DLOG(WARNING) << "Unable to read CPU temp information.";
}

if (report_hardware_status_)
cpu_temp_info_ = info;
}

void DeviceStatusCollector::GetActivityTimes(
em::DeviceStatusReportRequest* request) {
DictionaryPrefUpdate update(local_state_, prefs::kDeviceActivityTimes);
Expand Down Expand Up @@ -807,6 +893,12 @@ void DeviceStatusCollector::GetHardwareStatus(
status->add_cpu_utilization_pct(usage.cpu_usage_percent);
status->add_system_ram_free(usage.bytes_of_ram_free);
}

// Add CPU temp info.
status->clear_cpu_temp_info();
for (const em::CPUTempInfo& info : cpu_temp_info_) {
*status->add_cpu_temp_info() = info;
}
}

bool DeviceStatusCollector::GetDeviceStatus(
Expand Down
25 changes: 21 additions & 4 deletions chrome/browser/chromeos/policy/device_status_collector.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,22 @@ class DeviceStatusCollector {
// cpu user_time nice_time system_time idle_time
using CPUStatisticsFetcher = base::Callback<std::string(void)>;

// Constructor. Callers can inject their own VolumeInfoFetcher and
// CPUStatisticsFetcher. A null callback can be passed for either parameter,
// to use the default implementation.
// Reads CPU temperatures from /sys/class/hwmon/hwmon*/temp*_input and
// appropriate labels from /sys/class/hwmon/hwmon*/temp*_label.
using CPUTempFetcher =
base::Callback<std::vector<enterprise_management::CPUTempInfo>()>;

// Constructor. Callers can inject their own VolumeInfoFetcher,
// CPUStatisticsFetcher and CPUTempFetcher. These callbacks are executed on
// Blocking Pool. A null callback can be passed for either parameter, to use
// the default implementation.
DeviceStatusCollector(
PrefService* local_state,
chromeos::system::StatisticsProvider* provider,
const LocationUpdateRequester& location_update_requester,
const VolumeInfoFetcher& volume_info_fetcher,
const CPUStatisticsFetcher& cpu_statistics_fetcher);
const CPUStatisticsFetcher& cpu_statistics_fetcher,
const CPUTempFetcher& cpu_temp_fetcher);
virtual ~DeviceStatusCollector();

// Fills in the passed proto with device status information. Will return
Expand Down Expand Up @@ -185,6 +192,10 @@ class DeviceStatusCollector {
// Callback invoked to update our cpu usage information.
void ReceiveCPUStatistics(const std::string& statistics);

// Callback invoked to update our CPU temp information.
void StoreCPUTempInfo(
const std::vector<enterprise_management::CPUTempInfo>& info);

// Helper routine to convert from Shill-provided signal strength (percent)
// to dBm units expected by server.
int ConvertWifiSignalStrength(int signal_strength);
Expand Down Expand Up @@ -216,6 +227,9 @@ class DeviceStatusCollector {
// Cached disk volume information.
std::vector<enterprise_management::VolumeInfo> volume_info_;

// Cached CPU temp information.
std::vector<enterprise_management::CPUTempInfo> cpu_temp_info_;

struct ResourceUsage {
// Sample of percentage-of-CPU-used.
int cpu_usage_percent;
Expand All @@ -235,6 +249,9 @@ class DeviceStatusCollector {
// Callback invoked to fetch information about cpu usage.
CPUStatisticsFetcher cpu_statistics_fetcher_;

// Callback invoked to fetch information about cpu temperature.
CPUTempFetcher cpu_temp_fetcher_;

chromeos::system::StatisticsProvider* statistics_provider_;

chromeos::CrosSettings* cros_settings_;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "base/prefs/pref_service.h"
#include "base/prefs/testing_pref_service.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/threading/sequenced_worker_pool.h"
#include "chrome/browser/chromeos/login/users/mock_user_manager.h"
#include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
Expand Down Expand Up @@ -96,12 +97,14 @@ class TestingDeviceStatusCollector : public policy::DeviceStatusCollector {
location_update_requester,
const policy::DeviceStatusCollector::VolumeInfoFetcher&
volume_info_fetcher,
const policy::DeviceStatusCollector::CPUStatisticsFetcher& cpu_fetcher)
const policy::DeviceStatusCollector::CPUStatisticsFetcher& cpu_fetcher,
const policy::DeviceStatusCollector::CPUTempFetcher& cpu_temp_fetcher)
: policy::DeviceStatusCollector(local_state,
provider,
location_update_requester,
volume_info_fetcher,
cpu_fetcher) {
cpu_fetcher,
cpu_temp_fetcher) {
// Set the baseline time to a fixed value (1 AM) to prevent test flakiness
// due to a single activity period spanning two days.
SetBaselineTime(Time::Now().LocalMidnight() + TimeDelta::FromHours(1));
Expand Down Expand Up @@ -218,6 +221,15 @@ std::vector<em::VolumeInfo> GetFakeVolumeInfo(
return volume_info;
}

std::vector<em::CPUTempInfo> GetEmptyCPUTempInfo() {
return std::vector<em::CPUTempInfo>();
}

std::vector<em::CPUTempInfo> GetFakeCPUTempInfo(
const std::vector<em::CPUTempInfo>& cpu_temp_info) {
return cpu_temp_info;
}

} // namespace

namespace policy {
Expand Down Expand Up @@ -277,7 +289,8 @@ class DeviceStatusCollectorTest : public testing::Test {
settings_helper_.CreateOwnerSettingsService(nullptr);

RestartStatusCollector(base::Bind(&GetEmptyVolumeInfo),
base::Bind(&GetEmptyCPUStatistics));
base::Bind(&GetEmptyCPUStatistics),
base::Bind(&GetEmptyCPUTempInfo));
}

void AddMountPoint(const std::string& mount_point) {
Expand Down Expand Up @@ -306,12 +319,14 @@ class DeviceStatusCollectorTest : public testing::Test {

void RestartStatusCollector(
const policy::DeviceStatusCollector::VolumeInfoFetcher& volume_info,
const policy::DeviceStatusCollector::CPUStatisticsFetcher& cpu_stats) {
const policy::DeviceStatusCollector::CPUStatisticsFetcher& cpu_stats,
const policy::DeviceStatusCollector::CPUTempFetcher& cpu_temp_fetcher) {
policy::DeviceStatusCollector::LocationUpdateRequester callback =
base::Bind(&MockPositionUpdateRequester);
std::vector<em::VolumeInfo> expected_volume_info;
status_collector_.reset(new TestingDeviceStatusCollector(
&prefs_, &fake_statistics_provider_, callback, volume_info, cpu_stats));
&prefs_, &fake_statistics_provider_, callback, volume_info, cpu_stats,
cpu_temp_fetcher));
}

void GetStatus() {
Expand Down Expand Up @@ -480,7 +495,8 @@ TEST_F(DeviceStatusCollectorTest, StateKeptInPref) {
// able to count the active periods found by the original collector, because
// the results are stored in a pref.
RestartStatusCollector(base::Bind(&GetEmptyVolumeInfo),
base::Bind(&GetEmptyCPUStatistics));
base::Bind(&GetEmptyCPUStatistics),
base::Bind(&GetEmptyCPUTempInfo));
status_collector_->Simulate(test_states,
sizeof(test_states) / sizeof(ui::IdleState));

Expand Down Expand Up @@ -731,7 +747,8 @@ TEST_F(DeviceStatusCollectorTest, Location) {
// retrieved from local state without requesting a geolocation update.
SetMockPositionToReturnNext(valid_fix);
RestartStatusCollector(base::Bind(&GetEmptyVolumeInfo),
base::Bind(&GetEmptyCPUStatistics));
base::Bind(&GetEmptyCPUStatistics),
base::Bind(&GetEmptyCPUTempInfo));
CheckThatAValidLocationIsReported();
EXPECT_TRUE(mock_position_to_return_next.get());

Expand Down Expand Up @@ -810,7 +827,8 @@ TEST_F(DeviceStatusCollectorTest, TestVolumeInfo) {
EXPECT_FALSE(expected_volume_info.empty());

RestartStatusCollector(base::Bind(&GetFakeVolumeInfo, expected_volume_info),
base::Bind(&GetEmptyCPUStatistics));
base::Bind(&GetEmptyCPUStatistics),
base::Bind(&GetEmptyCPUTempInfo));
// Force finishing tasks posted by ctor of DeviceStatusCollector.
content::BrowserThread::GetBlockingPool()->FlushForTesting();
message_loop_.RunUntilIdle();
Expand Down Expand Up @@ -862,7 +880,8 @@ TEST_F(DeviceStatusCollectorTest, TestCPUSamples) {
// Mock 100% CPU usage.
std::string full_cpu_usage("cpu 500 0 500 0 0 0 0");
RestartStatusCollector(base::Bind(&GetEmptyVolumeInfo),
base::Bind(&GetFakeCPUStatistics, full_cpu_usage));
base::Bind(&GetFakeCPUStatistics, full_cpu_usage),
base::Bind(&GetEmptyCPUTempInfo));
// Force finishing tasks posted by ctor of DeviceStatusCollector.
content::BrowserThread::GetBlockingPool()->FlushForTesting();
message_loop_.RunUntilIdle();
Expand Down Expand Up @@ -901,6 +920,47 @@ TEST_F(DeviceStatusCollectorTest, TestCPUSamples) {
EXPECT_EQ(0, status_.cpu_utilization_pct().size());
}

TEST_F(DeviceStatusCollectorTest, TestCPUTemp) {
std::vector<em::CPUTempInfo> expected_temp_info;
int cpu_cnt = 12;
for (int i = 0; i < cpu_cnt; ++i) {
em::CPUTempInfo info;
info.set_cpu_temp(i * 10 + 100);
info.set_cpu_label(base::StringPrintf("Core %d", i));
expected_temp_info.push_back(info);
}

RestartStatusCollector(base::Bind(&GetEmptyVolumeInfo),
base::Bind(&GetEmptyCPUStatistics),
base::Bind(&GetFakeCPUTempInfo, expected_temp_info));
// Force finishing tasks posted by ctor of DeviceStatusCollector.
content::BrowserThread::GetBlockingPool()->FlushForTesting();
message_loop_.RunUntilIdle();

GetStatus();
EXPECT_EQ(expected_temp_info.size(),
static_cast<size_t>(status_.cpu_temp_info_size()));

// Walk the returned CPUTempInfo to make sure it matches.
for (const em::CPUTempInfo& expected_info : expected_temp_info) {
bool found = false;
for (const em::CPUTempInfo& info : status_.cpu_temp_info()) {
if (info.cpu_label() == expected_info.cpu_label()) {
EXPECT_EQ(expected_info.cpu_temp(), info.cpu_temp());
found = true;
break;
}
}
EXPECT_TRUE(found) << "No matching CPUTempInfo for "
<< expected_info.cpu_label();
}

// Now turn off hardware status reporting - should have no data.
settings_helper_.SetBoolean(chromeos::kReportDeviceHardwareStatus, false);
GetStatus();
EXPECT_EQ(0, status_.cpu_temp_info_size());
}

TEST_F(DeviceStatusCollectorTest, NoSessionStatusIfNotKioskMode) {
// Should not report session status if we don't have an active kiosk app.
settings_helper_.SetBoolean(chromeos::kReportDeviceSessionStatus, true);
Expand Down
Loading

0 comments on commit 0d55b1a

Please sign in to comment.