diff options
Diffstat (limited to 'metricsd')
-rw-r--r-- | metricsd/Android.mk | 2 | ||||
-rw-r--r-- | metricsd/collectors/cpu_usage_collector.cc | 125 | ||||
-rw-r--r-- | metricsd/collectors/cpu_usage_collector.h | 59 | ||||
-rw-r--r-- | metricsd/collectors/cpu_usage_collector_test.cc | 50 | ||||
-rw-r--r-- | metricsd/metrics_daemon.cc | 67 | ||||
-rw-r--r-- | metricsd/metrics_daemon.h | 12 |
6 files changed, 250 insertions, 65 deletions
diff --git a/metricsd/Android.mk b/metricsd/Android.mk index b08c153e4..291c98341 100644 --- a/metricsd/Android.mk +++ b/metricsd/Android.mk @@ -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 \ diff --git a/metricsd/collectors/cpu_usage_collector.cc b/metricsd/collectors/cpu_usage_collector.cc new file mode 100644 index 000000000..05934b47c --- /dev/null +++ b/metricsd/collectors/cpu_usage_collector.cc @@ -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; +} diff --git a/metricsd/collectors/cpu_usage_collector.h b/metricsd/collectors/cpu_usage_collector.h new file mode 100644 index 000000000..f81dfcb95 --- /dev/null +++ b/metricsd/collectors/cpu_usage_collector.h @@ -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_ diff --git a/metricsd/collectors/cpu_usage_collector_test.cc b/metricsd/collectors/cpu_usage_collector_test.cc new file mode 100644 index 000000000..ee5c92b29 --- /dev/null +++ b/metricsd/collectors/cpu_usage_collector_test.cc @@ -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); +} diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc index ed786e1fb..b606fd0ca 100644 --- a/metricsd/metrics_daemon.cc +++ b/metricsd/metrics_daemon.cc @@ -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(); diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h index 3d691c5ee..f12b02eee 100644 --- a/metricsd/metrics_daemon.h +++ b/metricsd/metrics_daemon.h @@ -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_; |