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
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 12dfa18..9620e16 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -84,6 +84,7 @@
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 @@
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 @@
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 2838119..e35bc28 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -84,6 +84,8 @@
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 kVmStatFileName[] = "/proc/vmstat";
const char kMeminfoFileName[] = "/proc/meminfo";
const int kMetricsProcStatFirstLineItemsCount = 11;
+const int kDiskMetricsStatItemCount = 11;
// Thermal CPU throttling.
@@ -215,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,
@@ -273,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() {
@@ -283,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;
@@ -313,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_));
@@ -494,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);
@@ -712,11 +767,7 @@
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 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"