Add page fault statistics.
BUG=chromium-os:20624
TEST=observe that Platform.PageFaultXXX appear in about:histograms.
Change-Id: Ifc281d31e05102dc4133d133f732b737e19891f1
Reviewed-on: http://gerrit.chromium.org/gerrit/10143
Commit-Ready: Luigi Semenzato <semenzato@chromium.org>
Tested-by: Luigi Semenzato <semenzato@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc
index 7ce2858..1684f81 100644
--- a/metrics/metrics_daemon.cc
+++ b/metrics/metrics_daemon.cc
@@ -105,8 +105,8 @@
const char MetricsDaemon::kMetricWriteSectorsShortName[] =
"Platform.WriteSectorsShort";
-const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds
-const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds
+const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds
+const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds
const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds
@@ -114,6 +114,17 @@
// sectors.
const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second
const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets
+// Page size is 4k, sector size is 0.5k. We're not interested in page fault
+// rates that the disk cannot sustain.
+const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
+const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
+
+// Major page faults, i.e. the ones that require data to be read from disk.
+
+const char MetricsDaemon::kMetricPageFaultsLongName[] =
+ "Platform.PageFaultsLong";
+const char MetricsDaemon::kMetricPageFaultsShortName[] =
+ "Platform.PageFaultsShort";
// persistent metrics path
const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics";
@@ -171,8 +182,9 @@
memuse_interval_index_(0),
read_sectors_(0),
write_sectors_(0),
- diskstats_state_(kDiskStatsShort),
- diskstats_initial_time_(0) {}
+ page_faults_(0),
+ stats_state_(kStatsShort),
+ stats_initial_time_(0) {}
MetricsDaemon::~MetricsDaemon() {
DeleteFrequencyCounters();
@@ -251,7 +263,8 @@
}
void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib,
- const string& diskstats_path) {
+ const string& diskstats_path,
+ const string& vmstats_path) {
testing_ = testing;
DCHECK(metrics_lib != NULL);
metrics_lib_ = metrics_lib;
@@ -279,11 +292,9 @@
ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName);
ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName);
- // Don't attempt to collect disk stats if there is no disk stats file.
- if (!diskstats_path.empty()) {
- diskstats_path_ = diskstats_path;
- DiskStatsReporterInit();
- }
+ diskstats_path_ = diskstats_path;
+ vmstats_path_ = vmstats_path;
+ StatsReporterInit();
// Start collecting meminfo stats.
ScheduleMeminfoCallback(kMetricMeminfoInterval);
@@ -564,112 +575,198 @@
usemon_interval_ = 0;
}
-void MetricsDaemon::DiskStatsReporterInit() {
+void MetricsDaemon::StatsReporterInit() {
DiskStatsReadStats(&read_sectors_, &write_sectors_);
+ VmStatsReadStats(&page_faults_);
// The first time around just run the long stat, so we don't delay boot.
- diskstats_state_ = kDiskStatsLong;
- diskstats_initial_time_ = GetActiveTime();
- if (diskstats_initial_time_ < 0) {
+ stats_state_ = kStatsLong;
+ stats_initial_time_ = GetActiveTime();
+ if (stats_initial_time_ < 0) {
LOG(WARNING) << "not collecting disk stats";
} else {
- ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval);
+ ScheduleStatsCallback(kMetricStatsLongInterval);
}
}
-void MetricsDaemon::ScheduleDiskStatsCallback(int wait) {
+void MetricsDaemon::ScheduleStatsCallback(int wait) {
if (testing_) {
return;
}
- g_timeout_add_seconds(wait, DiskStatsCallbackStatic, this);
+ g_timeout_add_seconds(wait, StatsCallbackStatic, this);
}
-void MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
+bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors,
long int* write_sectors) {
int nchars;
int nitems;
+ bool success = false;
char line[200];
+ if (diskstats_path_.empty()) {
+ return false;
+ }
int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY));
if (file < 0) {
PLOG(WARNING) << "cannot open " << diskstats_path_;
- return;
+ return false;
}
nchars = HANDLE_EINTR(read(file, line, sizeof(line)));
if (nchars < 0) {
PLOG(WARNING) << "cannot read from " << diskstats_path_;
+ return false;
} else {
- LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in "
- << diskstats_path_;
+ LOG_IF(WARNING, nchars == sizeof(line))
+ << "line too long in " << diskstats_path_;
line[nchars] = '\0';
nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld",
read_sectors, write_sectors);
- LOG_IF(WARNING, nitems != 2) << "found " << nitems << " items in "
- << diskstats_path_ << ", expected 2";
+ if (nitems == 2) {
+ success = true;
+ } else {
+ LOG(WARNING) << "found " << nitems << " items in "
+ << diskstats_path_ << ", expected 2";
+ }
}
HANDLE_EINTR(close(file));
+ return success;
+}
+
+bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) {
+ static const char kPageFaultSearchString[] = "\npgmajfault ";
+ bool success = false;
+ /* Each line in the file has the form
+ * <ID> <VALUE>
+ * for instance:
+ * nr_free_pages 213427
+ */
+ char* s = strstr(stats, kPageFaultSearchString);
+ if (s == NULL) {
+ LOG(WARNING) << "cannot find page fault entry in vmstats";
+ } else {
+ char* endp;
+ /* Skip <ID> and space. Don't count the terminating null. */
+ s += sizeof(kPageFaultSearchString) - 1;
+ *page_faults = strtol(s, &endp, 10);
+ if (*endp == '\n') {
+ success = true;
+ } else {
+ LOG(WARNING) << "error parsing vmstats";
+ }
+ }
+ return success;
+}
+
+bool MetricsDaemon::VmStatsReadStats(long int* page_faults) {
+ char buffer[4000];
+ int nchars;
+ int success = false;
+ if (testing_) {
+ return false;
+ }
+ int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY));
+ if (file < 0) {
+ PLOG(WARNING) << "cannot open " << vmstats_path_;
+ return false;
+ }
+ nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1));
+ LOG_IF(WARNING, nchars == sizeof(buffer) - 1)
+ << "file too large in " << vmstats_path_;
+ if (nchars < 0) {
+ PLOG(WARNING) << "cannot read from " << vmstats_path_;
+ } else if (nchars == 0) {
+ LOG(WARNING) << vmstats_path_ << " is empty";
+ } else {
+ buffer[nchars] = '\0';
+ success = VmStatsParseStats(buffer, page_faults);
+ }
+ HANDLE_EINTR(close(file));
+ return success;
}
// static
-gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) {
- (static_cast<MetricsDaemon*>(handle))->DiskStatsCallback();
+gboolean MetricsDaemon::StatsCallbackStatic(void* handle) {
+ (static_cast<MetricsDaemon*>(handle))->StatsCallback();
return false; // one-time callback
}
-// Collects disk stats alternating over a short and a long interval.
+// Collects disk and vm stats alternating over a short and a long interval.
-void MetricsDaemon::DiskStatsCallback() {
- long int read_sectors_now, write_sectors_now;
+void MetricsDaemon::StatsCallback() {
+ long int read_sectors_now, write_sectors_now, page_faults_now;
double time_now = GetActiveTime();
- double delta_time = time_now - diskstats_initial_time_;
+ double delta_time = time_now - stats_initial_time_;
if (testing_) {
// Fake the time when testing.
- delta_time = diskstats_state_ == kDiskStatsShort ?
- kMetricDiskStatsShortInterval : kMetricDiskStatsLongInterval;
+ delta_time = stats_state_ == kStatsShort ?
+ kMetricStatsShortInterval : kMetricStatsLongInterval;
}
- DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
+ bool diskstats_success = DiskStatsReadStats(&read_sectors_now,
+ &write_sectors_now);
int delta_read = read_sectors_now - read_sectors_;
int delta_write = write_sectors_now - write_sectors_;
int read_sectors_per_second = delta_read / delta_time;
int write_sectors_per_second = delta_write / delta_time;
+ bool vmstats_success = VmStatsReadStats(&page_faults_now);
+ int delta_faults = page_faults_now - page_faults_;
+ int page_faults_per_second = delta_faults / delta_time;
- switch (diskstats_state_) {
- case kDiskStatsShort:
- SendMetric(kMetricReadSectorsShortName,
- read_sectors_per_second,
- 1,
- kMetricSectorsIOMax,
- kMetricSectorsBuckets);
- SendMetric(kMetricWriteSectorsShortName,
- write_sectors_per_second,
- 1,
- kMetricSectorsIOMax,
- kMetricSectorsBuckets);
+ switch (stats_state_) {
+ case kStatsShort:
+ if (diskstats_success) {
+ SendMetric(kMetricReadSectorsShortName,
+ read_sectors_per_second,
+ 1,
+ kMetricSectorsIOMax,
+ kMetricSectorsBuckets);
+ SendMetric(kMetricWriteSectorsShortName,
+ write_sectors_per_second,
+ 1,
+ kMetricSectorsIOMax,
+ kMetricSectorsBuckets);
+ }
+ if (vmstats_success) {
+ SendMetric(kMetricPageFaultsShortName,
+ page_faults_per_second,
+ 1,
+ kMetricPageFaultsMax,
+ kMetricPageFaultsBuckets);
+ }
// Schedule long callback.
- diskstats_state_ = kDiskStatsLong;
- ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval -
- kMetricDiskStatsShortInterval);
+ stats_state_ = kStatsLong;
+ ScheduleStatsCallback(kMetricStatsLongInterval -
+ kMetricStatsShortInterval);
break;
- case kDiskStatsLong:
- SendMetric(kMetricReadSectorsLongName,
- read_sectors_per_second,
- 1,
- kMetricSectorsIOMax,
- kMetricSectorsBuckets);
- SendMetric(kMetricWriteSectorsLongName,
- write_sectors_per_second,
- 1,
- kMetricSectorsIOMax,
- kMetricSectorsBuckets);
- // Reset sector counters.
- read_sectors_ = read_sectors_now;
- write_sectors_ = write_sectors_now;
+ case kStatsLong:
+ if (diskstats_success) {
+ SendMetric(kMetricReadSectorsLongName,
+ read_sectors_per_second,
+ 1,
+ kMetricSectorsIOMax,
+ kMetricSectorsBuckets);
+ SendMetric(kMetricWriteSectorsLongName,
+ write_sectors_per_second,
+ 1,
+ kMetricSectorsIOMax,
+ kMetricSectorsBuckets);
+ // Reset sector counters.
+ read_sectors_ = read_sectors_now;
+ write_sectors_ = write_sectors_now;
+ }
+ if (vmstats_success) {
+ SendMetric(kMetricPageFaultsLongName,
+ page_faults_per_second,
+ 1,
+ kMetricPageFaultsMax,
+ kMetricPageFaultsBuckets);
+ page_faults_ = page_faults_now;
+ }
// Set start time for new cycle.
- diskstats_initial_time_ = time_now;
+ stats_initial_time_ = time_now;
// Schedule short callback.
- diskstats_state_ = kDiskStatsShort;
- ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval);
+ stats_state_ = kStatsShort;
+ ScheduleStatsCallback(kMetricStatsShortInterval);
break;
default:
- LOG(FATAL) << "Invalid disk stats state";
+ LOG(FATAL) << "Invalid stats state";
}
}
@@ -685,7 +782,7 @@
return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback();
}
-gboolean MetricsDaemon::MeminfoCallback() {
+bool MetricsDaemon::MeminfoCallback() {
string meminfo_raw;
const FilePath meminfo_path("/proc/meminfo");
if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
@@ -695,7 +792,7 @@
return ProcessMeminfo(meminfo_raw);
}
-gboolean MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
+bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) {
static const MeminfoRecord fields_array[] = {
{ "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
{ "MemFree", "MemFree" },
@@ -747,8 +844,8 @@
return true;
}
-gboolean MetricsDaemon::FillMeminfo(const string& meminfo_raw,
- vector<MeminfoRecord>* fields) {
+bool MetricsDaemon::FillMeminfo(const string& meminfo_raw,
+ vector<MeminfoRecord>* fields) {
vector<string> lines;
unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
@@ -782,7 +879,7 @@
return true;
}
-void MetricsDaemon::ScheduleMemuseCallback(gboolean new_callback,
+void MetricsDaemon::ScheduleMemuseCallback(bool new_callback,
double time_elapsed) {
if (testing_) {
return;
@@ -825,7 +922,7 @@
}
}
-gboolean MetricsDaemon::MemuseCallbackWork() {
+bool MetricsDaemon::MemuseCallbackWork() {
string meminfo_raw;
const FilePath meminfo_path("/proc/meminfo");
if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) {
@@ -835,7 +932,7 @@
return ProcessMemuse(meminfo_raw);
}
-gboolean MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
+bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) {
static const MeminfoRecord fields_array[] = {
{ "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory
{ "ActiveAnon", "Active(anon)" },
diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h
index 68646bc..0197bd1 100644
--- a/metrics/metrics_daemon.h
+++ b/metrics/metrics_daemon.h
@@ -30,7 +30,8 @@
// Initializes.
void Init(bool testing, MetricsLibraryInterface* metrics_lib,
- const std::string& diskstats_path);
+ const std::string& diskstats_path,
+ const std::string& vmstats_path);
// Does all the work. If |run_as_daemon| is true, daemonizes by
// forking.
@@ -47,6 +48,7 @@
FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState);
FRIEND_TEST(MetricsDaemonTest, LookupSessionState);
FRIEND_TEST(MetricsDaemonTest, MessageFilter);
+ FRIEND_TEST(MetricsDaemonTest, ParseVmStats);
FRIEND_TEST(MetricsDaemonTest, PowerStateChanged);
FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash);
FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo);
@@ -82,9 +84,9 @@
};
// State for disk stats collector callback.
- enum DiskStatsState {
- kDiskStatsShort, // short wait before short interval collection
- kDiskStatsLong, // final wait before new collection
+ enum StatsState {
+ kStatsShort, // short wait before short interval collection
+ kStatsLong, // final wait before new collection
};
// Data record for aggregating daily usage.
@@ -133,12 +135,17 @@
static const char kMetricReadSectorsShortName[];
static const char kMetricWriteSectorsLongName[];
static const char kMetricWriteSectorsShortName[];
- static const int kMetricDiskStatsShortInterval;
- static const int kMetricDiskStatsLongInterval;
+ static const char kMetricPageFaultsShortName[];
+ static const char kMetricPageFaultsLongName[];
+ static const int kMetricStatsShortInterval;
+ static const int kMetricStatsLongInterval;
static const int kMetricMeminfoInterval;
static const int kMetricSectorsIOMax;
static const int kMetricSectorsBuckets;
+ static const int kMetricPageFaultsMax;
+ static const int kMetricPageFaultsBuckets;
static const char kMetricsDiskStatsPath[];
+ static const char kMetricsVmStatsPath[];
// D-Bus message match strings.
static const char* kDBusMatches_[];
@@ -254,21 +261,27 @@
void SendLinearMetric(const std::string& name, int sample,
int max, int nbuckets);
- // Initializes disk stats reporting.
- void DiskStatsReporterInit();
+ // Initializes vm and disk stats reporting.
+ void StatsReporterInit();
- // Schedules a callback for the next disk stats collection.
- void ScheduleDiskStatsCallback(int wait);
+ // Schedules a callback for the next vm and disk stats collection.
+ void ScheduleStatsCallback(int wait);
- // Reads cumulative disk statistics from sysfs.
- void DiskStatsReadStats(long int* read_sectors, long int* write_sectors);
+ // Reads cumulative disk statistics from sysfs. Returns true for success.
+ bool DiskStatsReadStats(long int* read_sectors, long int* write_sectors);
- // Reports disk statistics (static version for glib). Arguments are a glib
- // artifact.
- static gboolean DiskStatsCallbackStatic(void* handle);
+ // Reads cumulative vm statistics from procfs. Returns true for success.
+ bool VmStatsReadStats(long int* page_faults);
- // Reports disk statistics.
- void DiskStatsCallback();
+ // Parse cumulative vm statistics from a C string. Returns true for success.
+ bool VmStatsParseStats(char* stats, long int* page_faults);
+
+ // Reports disk and vm statistics (static version for glib). Arguments are a
+ // glib artifact.
+ static gboolean StatsCallbackStatic(void* handle);
+
+ // Reports disk and vm statistics.
+ void StatsCallback();
// Schedules meminfo collection callback.
void ScheduleMeminfoCallback(int wait);
@@ -278,26 +291,26 @@
static gboolean MeminfoCallbackStatic(void* handle);
// Reports memory statistics. Returns false on failure.
- gboolean MeminfoCallback();
+ bool MeminfoCallback();
// Parses content of /proc/meminfo and sends fields of interest to UMA.
// Returns false on errors. |meminfo_raw| contains the content of
// /proc/meminfo.
- gboolean ProcessMeminfo(const std::string& meminfo_raw);
+ bool ProcessMeminfo(const std::string& meminfo_raw);
// Parses meminfo data from |meminfo_raw|. |fields| is a vector containing
// the fields of interest. The order of the fields must be the same in which
// /proc/meminfo prints them. The result of parsing fields[i] is placed in
// fields[i].value.
- gboolean FillMeminfo(const std::string& meminfo_raw,
- std::vector<MeminfoRecord>* fields);
+ bool FillMeminfo(const std::string& meminfo_raw,
+ std::vector<MeminfoRecord>* fields);
// Schedule a memory use callback. |new_callback| is true when this callback
// is scheduled for the first time. When |new_callback| is false,
// |time_elapsed| is the active (non-sleep) time that has passed between now
// and the original callback scheduling time. We use it to reschedule a
// callback that fired too early because we slept.
- void ScheduleMemuseCallback(gboolean new_callback, double time_elapsed);
+ void ScheduleMemuseCallback(bool new_callback, double time_elapsed);
// Static wrapper for MemuseCallback. Always returns false.
static gboolean MemuseCallbackStatic(void* handle);
@@ -308,10 +321,10 @@
void MemuseCallback();
// Reads /proc/meminfo and sends total anonymous memory usage to UMA.
- gboolean MemuseCallbackWork();
+ bool MemuseCallbackWork();
// Parse meminfo data and send to UMA.
- gboolean ProcessMemuse(const std::string& meminfo_raw);
+ bool ProcessMemuse(const std::string& meminfo_raw);
// Test mode.
bool testing_;
@@ -368,13 +381,16 @@
// Selects the wait time for the next memory use callback.
unsigned int memuse_interval_index_;
- // Contains the most recent disk stats.
+ // Contain the most recent disk and vm cumulative stats.
long int read_sectors_;
long int write_sectors_;
+ long int page_faults_;
- DiskStatsState diskstats_state_;
+ StatsState stats_state_;
+ double stats_initial_time_;
+
std::string diskstats_path_;
- double diskstats_initial_time_;
+ std::string vmstats_path_;
};
#endif // METRICS_DAEMON_H_
diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc
index 1ee0611..d194e54 100644
--- a/metrics/metrics_daemon_main.cc
+++ b/metrics/metrics_daemon_main.cc
@@ -12,7 +12,8 @@
DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)");
-// Return the path to the disk stats in the sysfs.
+// 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];
@@ -41,6 +42,6 @@
MetricsLibrary metrics_lib;
metrics_lib.Init();
MetricsDaemon daemon;
- daemon.Init(false, &metrics_lib, MetricsMainDiskStatsPath());
+ daemon.Init(false, &metrics_lib, MetricsMainDiskStatsPath(), "/proc/vmstat");
daemon.Run(FLAGS_daemon);
}
diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc
index df95a5d..d7ce735 100644
--- a/metrics/metrics_daemon_test.cc
+++ b/metrics/metrics_daemon_test.cc
@@ -43,6 +43,8 @@
static const int kFakeReadSectors[] = {80000, 100000};
static const int kFakeWriteSectors[] = {3000, 4000};
+static const char kFakeVmStatsPath[] = "fake-vm-stats";
+
// This class allows a TimeTicks object to be initialized with seconds
// (rather than microseconds) through the protected TimeTicks(int64)
// constructor.
@@ -69,7 +71,7 @@
kFakeDiskStats[1] = StringPrintf(kFakeDiskStatsFormat,
kFakeReadSectors[1], kFakeWriteSectors[1]);
CreateFakeDiskStatsFile(kFakeDiskStats[0].c_str());
- daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath);
+ daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath, kFakeVmStatsPath);
// Check configuration of a few histograms.
FrequencyCounter* frequency_counter =
@@ -568,15 +570,15 @@
EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
- MetricsDaemon::DiskStatsState ds_state = daemon_.diskstats_state_;
+ MetricsDaemon::StatsState s_state = daemon_.stats_state_;
EXPECT_CALL(metrics_lib_,
SendToUMA(_, (kFakeReadSectors[1] - kFakeReadSectors[0]) / 30,
_, _, _));
EXPECT_CALL(metrics_lib_,
SendToUMA(_, (kFakeWriteSectors[1] - kFakeWriteSectors[0]) / 30,
_, _, _));
- daemon_.DiskStatsCallback();
- EXPECT_TRUE(ds_state != daemon_.diskstats_state_);
+ daemon_.StatsCallback();
+ EXPECT_TRUE(s_state != daemon_.stats_state_);
}
TEST_F(MetricsDaemonTest, ProcessMeminfo) {
@@ -643,6 +645,13 @@
EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo));
}
+TEST_F(MetricsDaemonTest, ParseVmStats) {
+ static char kVmStats[] = "foo 100\nbar 200\npgmajfault 42\netcetc 300\n";
+ long int page_faults = 0;
+ EXPECT_TRUE(daemon_.VmStatsParseStats(kVmStats, &page_faults));
+ EXPECT_EQ(page_faults, 42);
+}
+
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();