metricsd: Collect generic stats about the system.

Collect memory usage and disk IO statistics periodically.

BUG: 22953719

Change-Id: I2e35d4800ddc684284969e6a58a6f50497086b69
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 12dfa18..d619b32 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -133,7 +133,9 @@
   libchromeos-http \
   libchromeos-dbus \
   libcutils \
-  libdbus
+  libdbus \
+  librootdev
+
 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 cf4d79d..148e1be 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -112,6 +112,7 @@
 const char kVmStatFileName[] = "/proc/vmstat";
 const char kMeminfoFileName[] = "/proc/meminfo";
 const int kMetricsProcStatFirstLineItemsCount = 11;
+const int kDiskMetricsStatItemCount = 11;
 
 // Thermal CPU throttling.
 
@@ -217,6 +218,7 @@
                          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,
@@ -275,8 +277,13 @@
   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() {
@@ -285,6 +292,13 @@
   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;
 
@@ -315,6 +329,11 @@
     }
   }
 
+  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_));
@@ -496,6 +515,40 @@
       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<std::string> 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);
@@ -715,10 +768,7 @@
   }
   // 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 9180e23..7f7ea63 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -45,6 +45,7 @@
             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 @@
   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 @@
   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 @@
   scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
   scoped_ptr<PersistentInteger> 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 7f9ec43..43046f8 100644
--- a/metricsd/metrics_daemon_main.cc
+++ b/metricsd/metrics_daemon_main.cc
@@ -20,6 +20,7 @@
 #include <base/strings/string_util.h>
 #include <chromeos/flag_helper.h>
 #include <chromeos/syslog_logging.h>
+#include <rootdev.h>
 
 #include "constants.h"
 #include "metrics_daemon.h"
@@ -29,6 +30,28 @@
 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 @@
               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 0d2229c..4ef097e 100644
--- a/metricsd/metrics_daemon_test.cc
+++ b/metricsd/metrics_daemon_test.cc
@@ -82,6 +82,7 @@
                  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 @@
                      /* 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"