Merge "metricsd: Collect average cpu usage information." am: 900d9df1e5

am: 410d7cc881

* commit '410d7cc8810d9fc791960adfa8a2e22997ba3579':
  metricsd: Collect average cpu usage information.
This commit is contained in:
Bertrand Simonnet 2015-11-04 23:29:21 +00:00 committed by android-build-merger
commit 2caa1e920a
6 changed files with 250 additions and 65 deletions

View file

@ -27,6 +27,7 @@ metrics_client_sources := \
metrics_daemon_common := \
collectors/averaged_statistics_collector.cc \
collectors/cpu_usage_collector.cc \
collectors/disk_usage_collector.cc \
metrics_daemon.cc \
persistent_integer.cc \
@ -41,6 +42,7 @@ metrics_daemon_common := \
metrics_tests_sources := \
collectors/averaged_statistics_collector_test.cc \
collectors/cpu_usage_collector_test.cc \
metrics_daemon_test.cc \
metrics_library_test.cc \
persistent_integer_test.cc \

View file

@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "collectors/cpu_usage_collector.h"
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/message_loop/message_loop.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/sys_info.h>
#include "metrics/metrics_library.h"
namespace {
const char kCpuUsagePercent[] = "Platform.CpuUsage.Percent";
const char kMetricsProcStatFileName[] = "/proc/stat";
const int kMetricsProcStatFirstLineItemsCount = 11;
// Collect every minute.
const int kCollectionIntervalSecs = 60;
} // namespace
using base::TimeDelta;
CpuUsageCollector::CpuUsageCollector(MetricsLibraryInterface* metrics_library) {
CHECK(metrics_library);
metrics_lib_ = metrics_library;
collect_interval_ = TimeDelta::FromSeconds(kCollectionIntervalSecs);
}
void CpuUsageCollector::Init() {
num_cpu_ = base::SysInfo::NumberOfProcessors();
// Get ticks per second (HZ) on this system.
// Sysconf cannot fail, so no sanity checks are needed.
ticks_per_second_ = sysconf(_SC_CLK_TCK);
CHECK_GT(ticks_per_second_, uint64_t(0))
<< "Number of ticks per seconds should be positive.";
latest_cpu_use_ = GetCumulativeCpuUse();
}
void CpuUsageCollector::CollectCallback() {
Collect();
Schedule();
}
void CpuUsageCollector::Schedule() {
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&CpuUsageCollector::CollectCallback, base::Unretained(this)),
collect_interval_);
}
void CpuUsageCollector::Collect() {
TimeDelta cpu_use = GetCumulativeCpuUse();
TimeDelta diff_per_cpu = (cpu_use - latest_cpu_use_) / num_cpu_;
latest_cpu_use_ = cpu_use;
// Report the cpu usage as a percentage of the total cpu usage possible.
int percent_use = diff_per_cpu.InMilliseconds() * 100 /
(kCollectionIntervalSecs * 1000);
metrics_lib_->SendEnumToUMA(kCpuUsagePercent, percent_use, 101);
}
TimeDelta CpuUsageCollector::GetCumulativeCpuUse() {
base::FilePath proc_stat_path(kMetricsProcStatFileName);
std::string proc_stat_string;
if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
return TimeDelta();
}
uint64_t user_ticks, user_nice_ticks, system_ticks;
if (!ParseProcStat(proc_stat_string, &user_ticks, &user_nice_ticks,
&system_ticks)) {
return TimeDelta();
}
uint64_t total = user_ticks + user_nice_ticks + system_ticks;
return TimeDelta::FromMicroseconds(
total * 1000 * 1000 / ticks_per_second_);
}
bool CpuUsageCollector::ParseProcStat(const std::string& stat_content,
uint64_t *user_ticks,
uint64_t *user_nice_ticks,
uint64_t *system_ticks) {
std::vector<std::string> proc_stat_lines;
base::SplitString(stat_content, '\n', &proc_stat_lines);
if (proc_stat_lines.empty()) {
LOG(WARNING) << "No lines found in " << kMetricsProcStatFileName;
return false;
}
std::vector<std::string> proc_stat_totals;
base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
proc_stat_totals[0] != "cpu" ||
!base::StringToUint64(proc_stat_totals[1], user_ticks) ||
!base::StringToUint64(proc_stat_totals[2], user_nice_ticks) ||
!base::StringToUint64(proc_stat_totals[3], system_ticks)) {
LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
return false;
}
return true;
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
#define METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
#include <base/time/time.h>
#include "metrics/metrics_library.h"
class CpuUsageCollector {
public:
CpuUsageCollector(MetricsLibraryInterface* metrics_library);
// Initialize this collector's state.
void Init();
// Schedule a collection interval.
void Schedule();
// Callback called at the end of the collection interval.
void CollectCallback();
// Measure the cpu use and report it.
void Collect();
// Gets the current cumulated Cpu usage.
base::TimeDelta GetCumulativeCpuUse();
private:
FRIEND_TEST(CpuUsageTest, ParseProcStat);
bool ParseProcStat(const std::string& stat_content,
uint64_t *user_ticks,
uint64_t *user_nice_ticks,
uint64_t *system_ticks);
int num_cpu_;
uint32_t ticks_per_second_;
base::TimeDelta collect_interval_;
base::TimeDelta latest_cpu_use_;
MetricsLibraryInterface* metrics_lib_;
};
#endif // METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_

View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include "collectors/cpu_usage_collector.h"
#include "metrics/metrics_library_mock.h"
TEST(CpuUsageTest, ParseProcStat) {
MetricsLibraryMock metrics_lib_mock;
CpuUsageCollector collector(&metrics_lib_mock);
std::vector<std::string> invalid_contents = {
"",
// First line does not start with cpu.
"spu 17191 11 36579 151118 289 0 2 0 0 0\n"
"cpu0 1564 2 866 48650 68 0 2 0 0 0\n"
"cpu1 14299 0 35116 1844 81 0 0 0 0 0\n",
// One of the field is not a number.
"cpu a17191 11 36579 151118 289 0 2 0 0 0",
// To many numbers in the first line.
"cpu 17191 11 36579 151118 289 0 2 0 0 0 102"
};
uint64_t user, nice, system;
for (int i = 0; i < invalid_contents.size(); i++) {
ASSERT_FALSE(collector.ParseProcStat(invalid_contents[i], &user, &nice,
&system));
}
ASSERT_TRUE(collector.ParseProcStat(
std::string("cpu 17191 11 36579 151118 289 0 2 0 0 0"),
&user, &nice, &system));
ASSERT_EQ(17191, user);
ASSERT_EQ(11, nice);
ASSERT_EQ(36579, system);
}

View file

@ -71,10 +71,8 @@ const char kUncleanShutdownDetectedFile[] =
const int kMetricMeminfoInterval = 30; // seconds
const char kMetricsProcStatFileName[] = "/proc/stat";
const char kMeminfoFileName[] = "/proc/meminfo";
const char kVmStatFileName[] = "/proc/vmstat";
const int kMetricsProcStatFirstLineItemsCount = 11;
// Thermal CPU throttling.
@ -103,9 +101,7 @@ static const int kMemuseIntervals[] = {
MetricsDaemon::MetricsDaemon()
: memuse_final_time_(0),
memuse_interval_index_(0),
ticks_per_second_(0),
latest_cpu_use_ticks_(0) {}
memuse_interval_index_(0) {}
MetricsDaemon::~MetricsDaemon() {
}
@ -188,10 +184,6 @@ void MetricsDaemon::Init(bool testing,
upload_interval_ = upload_interval;
server_ = server;
// Get ticks per second (HZ) on this system.
// Sysconf cannot fail, so no sanity checks are needed.
ticks_per_second_ = sysconf(_SC_CLK_TCK);
daily_active_use_.reset(
new PersistentInteger("Platform.UseTime.PerDay"));
version_cumulative_active_use_.reset(
@ -235,6 +227,7 @@ void MetricsDaemon::Init(bool testing,
averaged_stats_collector_.reset(
new AveragedStatisticsCollector(metrics_lib_, diskstats_path,
kVmStatFileName));
cpu_usage_collector_.reset(new CpuUsageCollector(metrics_lib_));
}
int MetricsDaemon::OnInit() {
@ -290,6 +283,7 @@ int MetricsDaemon::OnInit() {
base::Bind(&MetricsDaemon::OnDisableMetrics, base::Unretained(this)));
}
latest_cpu_use_microseconds_ = cpu_usage_collector_->GetCumulativeCpuUse();
base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
base::Unretained(this)),
@ -404,53 +398,6 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
return DBUS_HANDLER_RESULT_HANDLED;
}
// One might argue that parts of this should go into
// chromium/src/base/sys_info_chromeos.c instead, but put it here for now.
TimeDelta MetricsDaemon::GetIncrementalCpuUse() {
FilePath proc_stat_path = FilePath(kMetricsProcStatFileName);
std::string proc_stat_string;
if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
return TimeDelta();
}
std::vector<std::string> proc_stat_lines;
base::SplitString(proc_stat_string, '\n', &proc_stat_lines);
if (proc_stat_lines.empty()) {
LOG(WARNING) << "cannot parse " << kMetricsProcStatFileName
<< ": " << proc_stat_string;
return TimeDelta();
}
std::vector<std::string> proc_stat_totals;
base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
uint64_t user_ticks, user_nice_ticks, system_ticks;
if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
proc_stat_totals[0] != "cpu" ||
!base::StringToUint64(proc_stat_totals[1], &user_ticks) ||
!base::StringToUint64(proc_stat_totals[2], &user_nice_ticks) ||
!base::StringToUint64(proc_stat_totals[3], &system_ticks)) {
LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
return TimeDelta(base::TimeDelta::FromSeconds(0));
}
uint64_t total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks;
// Sanity check.
if (total_cpu_use_ticks < latest_cpu_use_ticks_) {
LOG(WARNING) << "CPU time decreasing from " << latest_cpu_use_ticks_
<< " to " << total_cpu_use_ticks;
return TimeDelta();
}
uint64_t diff = total_cpu_use_ticks - latest_cpu_use_ticks_;
latest_cpu_use_ticks_ = total_cpu_use_ticks;
// Use microseconds to avoid significant truncations.
return base::TimeDelta::FromMicroseconds(
diff * 1000 * 1000 / ticks_per_second_);
}
void MetricsDaemon::ProcessUserCrash() {
// Counts the active time up to now.
UpdateStats(TimeTicks::Now(), Time::Now());
@ -506,6 +453,9 @@ bool MetricsDaemon::CheckSystemCrash(const string& crash_file) {
void MetricsDaemon::StatsReporterInit() {
disk_usage_collector_->Schedule();
cpu_usage_collector_->Init();
cpu_usage_collector_->Schedule();
// Don't start a collection cycle during the first run to avoid delaying the
// boot.
averaged_stats_collector_->ScheduleWait();
@ -910,7 +860,10 @@ void MetricsDaemon::UpdateStats(TimeTicks now_ticks,
version_cumulative_active_use_->Add(elapsed_seconds);
user_crash_interval_->Add(elapsed_seconds);
kernel_crash_interval_->Add(elapsed_seconds);
version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds());
TimeDelta cpu_use = cpu_usage_collector_->GetCumulativeCpuUse();
version_cumulative_cpu_use_->Add(
(cpu_use - latest_cpu_use_microseconds_).InMilliseconds());
latest_cpu_use_microseconds_ = cpu_use;
last_update_stats_time_ = now_ticks;
const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch();

View file

@ -32,6 +32,7 @@
#include <gtest/gtest_prod.h> // for FRIEND_TEST
#include "collectors/averaged_statistics_collector.h"
#include "collectors/cpu_usage_collector.h"
#include "collectors/disk_usage_collector.h"
#include "metrics/metrics_library.h"
#include "persistent_integer.h"
@ -164,10 +165,6 @@ class MetricsDaemon : public brillo::DBusDaemon {
// total number of kernel crashes since the last version update.
void SendKernelCrashesCumulativeCountStats();
// Returns the total (system-wide) CPU usage between the time of the most
// recent call to this function and now.
base::TimeDelta GetIncrementalCpuUse();
// Sends a sample representing the number of seconds of active use
// for a 24-hour period and reset |use|.
void SendAndResetDailyUseSample(
@ -268,12 +265,9 @@ class MetricsDaemon : public brillo::DBusDaemon {
// Selects the wait time for the next memory use callback.
unsigned int memuse_interval_index_;
// The system "HZ", or frequency of ticks. Some system data uses ticks as a
// unit, and this is used to convert to standard time units.
uint32_t ticks_per_second_;
// Used internally by GetIncrementalCpuUse() to return the CPU utilization
// between calls.
uint64_t latest_cpu_use_ticks_;
base::TimeDelta latest_cpu_use_microseconds_;
// Persistent values and accumulators for crash statistics.
scoped_ptr<PersistentInteger> daily_cycle_;
@ -302,6 +296,8 @@ class MetricsDaemon : public brillo::DBusDaemon {
scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
scoped_ptr<CpuUsageCollector> cpu_usage_collector_;
scoped_ptr<DiskUsageCollector> disk_usage_collector_;
scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_;