From ebbe35c2b589372c3938133dd04e93eba30a4922 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Tue, 8 Sep 2015 10:13:35 -0700 Subject: [PATCH] metricsd: Collect generic stats about the system. Collect memory usage and disk IO statistics periodically. Also update the Android.mk file to use clang by default. BUG: 22953719 TEST: builds on external and internal branches. Change-Id: I1ee3683d014586cf7f711d2e090a99429752063c --- metricsd/Android.mk | 7 +++- metricsd/metrics_daemon.cc | 61 ++++++++++++++++++++++++++++++--- metricsd/metrics_daemon.h | 4 ++- metricsd/metrics_daemon_main.cc | 24 +++++++++++++ metricsd/metrics_daemon_test.cc | 16 +++++++++ 5 files changed, 105 insertions(+), 7 deletions(-) diff --git a/metricsd/Android.mk b/metricsd/Android.mk index 12dfa1806..9620e16db 100644 --- a/metricsd/Android.mk +++ b/metricsd/Android.mk @@ -84,6 +84,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libmetrics LOCAL_C_INCLUDES := $(metrics_includes) LOCAL_CFLAGS := $(metrics_CFLAGS) +LOCAL_CLANG := true LOCAL_CPP_EXTENSION := $(metrics_cpp_extension) LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include @@ -97,6 +98,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := metrics_client LOCAL_C_INCLUDES := $(metrics_includes) LOCAL_CFLAGS := $(metrics_CFLAGS) +LOCAL_CLANG := true LOCAL_CPP_EXTENSION := $(metrics_cpp_extension) LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \ @@ -133,7 +135,10 @@ LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \ libchromeos-http \ libchromeos-dbus \ libcutils \ - libdbus + libdbus \ + librootdev + +LOCAL_CLANG := true LOCAL_SRC_FILES := $(metrics_daemon_sources) LOCAL_STATIC_LIBRARIES := metrics_daemon_protos include $(BUILD_EXECUTABLE) diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc index 28381199d..e35bc28f1 100644 --- a/metricsd/metrics_daemon.cc +++ b/metricsd/metrics_daemon.cc @@ -84,6 +84,8 @@ const char kMetricWriteSectorsShortName[] = "Platform.WriteSectorsShort"; const int kMetricStatsShortInterval = 1; // seconds const int kMetricStatsLongInterval = 30; // seconds +const int kMetricMeminfoInterval = 30; // seconds + // Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte // sectors. const int kMetricSectorsIOMax = 500000; // sectors/second @@ -110,6 +112,7 @@ const char kMetricsProcStatFileName[] = "/proc/stat"; const char kVmStatFileName[] = "/proc/vmstat"; const char kMeminfoFileName[] = "/proc/meminfo"; const int kMetricsProcStatFirstLineItemsCount = 11; +const int kDiskMetricsStatItemCount = 11; // Thermal CPU throttling. @@ -215,6 +218,7 @@ void MetricsDaemon::Init(bool testing, bool uploader_active, bool dbus_enabled, MetricsLibraryInterface* metrics_lib, + const string& diskstats_path, const string& scaling_max_freq_path, const string& cpuinfo_max_freq_path, const base::TimeDelta& upload_interval, @@ -273,8 +277,13 @@ void MetricsDaemon::Init(bool testing, weekly_cycle_.reset(new PersistentInteger("weekly.cycle")); version_cycle_.reset(new PersistentInteger("version.cycle")); + diskstats_path_ = diskstats_path; scaling_max_freq_path_ = scaling_max_freq_path; cpuinfo_max_freq_path_ = cpuinfo_max_freq_path; + + // If testing, initialize Stats Reporter without connecting DBus + if (testing_) + StatsReporterInit(); } int MetricsDaemon::OnInit() { @@ -283,6 +292,13 @@ int MetricsDaemon::OnInit() { if (return_code != EX_OK) return return_code; + StatsReporterInit(); + + // Start collecting meminfo stats. + ScheduleMeminfoCallback(kMetricMeminfoInterval); + memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0]; + ScheduleMemuseCallback(kMemuseIntervals[0]); + if (testing_) return EX_OK; @@ -313,6 +329,11 @@ int MetricsDaemon::OnInit() { } } + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs)); + if (uploader_active_) { upload_service_.reset( new UploadService(new SystemProfileCache(), metrics_lib_, server_)); @@ -494,6 +515,40 @@ void MetricsDaemon::ScheduleStatsCallback(int wait) { base::TimeDelta::FromSeconds(wait)); } +bool MetricsDaemon::DiskStatsReadStats(uint64_t* read_sectors, + uint64_t* write_sectors) { + CHECK(read_sectors); + CHECK(write_sectors); + std::string line; + if (diskstats_path_.empty()) { + return false; + } + + if (!base::ReadFileToString(base::FilePath(diskstats_path_), &line)) { + PLOG(WARNING) << "Could not read disk stats from " << diskstats_path_; + return false; + } + + std::vector parts = base::SplitString( + line, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + if (parts.size() != kDiskMetricsStatItemCount) { + LOG(ERROR) << "Could not parse disk stat correctly. Expected " + << kDiskMetricsStatItemCount << " elements but got " + << parts.size(); + return false; + } + if (!base::StringToUint64(parts[2], read_sectors)) { + LOG(ERROR) << "Couldn't convert read sectors " << parts[2] << " to uint64"; + return false; + } + if (!base::StringToUint64(parts[6], write_sectors)) { + LOG(ERROR) << "Couldn't convert write sectors " << parts[6] << " to uint64"; + return false; + } + + return true; +} + bool MetricsDaemon::VmStatsParseStats(const char* stats, struct VmstatRecord* record) { CHECK(stats); @@ -712,11 +767,7 @@ void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) { return; } // Make both calls even if the first one fails. - bool success = ProcessMeminfo(meminfo_raw); - bool reschedule = - ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) && - success; - if (reschedule) { + if (ProcessMeminfo(meminfo_raw)) { base::MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this), wait), diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h index 9180e2334..7f7ea63c8 100644 --- a/metricsd/metrics_daemon.h +++ b/metricsd/metrics_daemon.h @@ -45,6 +45,7 @@ class MetricsDaemon : public chromeos::DBusDaemon { bool uploader_active, bool dbus_enabled, MetricsLibraryInterface* metrics_lib, + const std::string& diskstats_path, const std::string& cpuinfo_max_freq_path, const std::string& scaling_max_freq_path, const base::TimeDelta& upload_interval, @@ -78,6 +79,7 @@ class MetricsDaemon : public chromeos::DBusDaemon { FRIEND_TEST(MetricsDaemonTest, GetHistogramPath); FRIEND_TEST(MetricsDaemonTest, IsNewEpoch); FRIEND_TEST(MetricsDaemonTest, MessageFilter); + FRIEND_TEST(MetricsDaemonTest, ParseDiskStats); FRIEND_TEST(MetricsDaemonTest, ParseVmStats); FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash); FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo); @@ -86,7 +88,6 @@ class MetricsDaemon : public chromeos::DBusDaemon { FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash); FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency); FRIEND_TEST(MetricsDaemonTest, ReadFreqToInt); - FRIEND_TEST(MetricsDaemonTest, ReportDiskStats); FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval); FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval); FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval); @@ -324,6 +325,7 @@ class MetricsDaemon : public chromeos::DBusDaemon { scoped_ptr unclean_shutdowns_daily_count_; scoped_ptr unclean_shutdowns_weekly_count_; + std::string diskstats_path_; std::string scaling_max_freq_path_; std::string cpuinfo_max_freq_path_; diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc index 7f9ec43b8..43046f850 100644 --- a/metricsd/metrics_daemon_main.cc +++ b/metricsd/metrics_daemon_main.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include "constants.h" #include "metrics_daemon.h" @@ -29,6 +30,28 @@ const char kScalingMaxFreqPath[] = const char kCpuinfoMaxFreqPath[] = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; +// Returns the path to the disk stats in the sysfs. Returns the null string if +// it cannot find the disk stats file. +static +const std::string MetricsMainDiskStatsPath() { + char dev_path_cstr[PATH_MAX]; + std::string dev_prefix = "/dev/block/"; + std::string dev_path; + + int ret = rootdev(dev_path_cstr, sizeof(dev_path_cstr), true, true); + if (ret != 0) { + LOG(WARNING) << "error " << ret << " determining root device"; + return ""; + } + dev_path = dev_path_cstr; + // Check that rootdev begins with "/dev/block/". + if (!base::StartsWithASCII(dev_path, dev_prefix, false)) { + LOG(WARNING) << "unexpected root device " << dev_path; + return ""; + } + return "/sys/class/block/" + dev_path.substr(dev_prefix.length()) + "/stat"; +} + int main(int argc, char** argv) { DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); @@ -75,6 +98,7 @@ int main(int argc, char** argv) { FLAGS_uploader | FLAGS_uploader_test, FLAGS_withdbus, &metrics_lib, + MetricsMainDiskStatsPath(), kScalingMaxFreqPath, kCpuinfoMaxFreqPath, base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs), diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc index 0d2229cf3..4ef097ece 100644 --- a/metricsd/metrics_daemon_test.cc +++ b/metricsd/metrics_daemon_test.cc @@ -82,6 +82,7 @@ class MetricsDaemonTest : public testing::Test { false, true, &metrics_lib_, + disk_stats_path_.value(), scaling_max_freq_path_.value(), cpu_max_freq_path_.value(), base::TimeDelta::FromMinutes(30), @@ -198,6 +199,21 @@ TEST_F(MetricsDaemonTest, SendSample) { /* min */ 1, /* max */ 100, /* buckets */ 50); } +TEST_F(MetricsDaemonTest, ParseDiskStats) { + uint64_t read_sectors_now, write_sectors_now; + CreateFakeDiskStatsFile(kFakeDiskStats0); + ASSERT_TRUE(daemon_.DiskStatsReadStats(&read_sectors_now, + &write_sectors_now)); + EXPECT_EQ(read_sectors_now, kFakeReadSectors[0]); + EXPECT_EQ(write_sectors_now, kFakeWriteSectors[0]); + + CreateFakeDiskStatsFile(kFakeDiskStats1); + ASSERT_TRUE(daemon_.DiskStatsReadStats(&read_sectors_now, + &write_sectors_now)); + EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]); + EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]); +} + TEST_F(MetricsDaemonTest, ProcessMeminfo) { string meminfo = "MemTotal: 2000000 kB\nMemFree: 500000 kB\n"