Index: metrics_daemon.cc |
diff --git a/metrics_daemon.cc b/metrics_daemon.cc |
index 4820590d1b035af0355d1d19701309df8d35bfe4..a542a0167fef3b1855c5a479c51ad99745235002 100644 |
--- a/metrics_daemon.cc |
+++ b/metrics_daemon.cc |
@@ -102,6 +102,8 @@ const char MetricsDaemon::kMetricWriteSectorsShortName[] = |
const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds |
const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds |
+const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds |
+ |
// Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte |
// sectors. |
const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second |
@@ -248,6 +250,9 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, |
DiskStatsReporterInit(); |
} |
+ // Start collecting meminfo stats. |
+ ScheduleMeminfoCallback(kMetricMeminfoInterval); |
+ |
// Don't setup D-Bus and GLib in test mode. |
if (testing) |
return; |
@@ -617,6 +622,140 @@ void MetricsDaemon::DiskStatsCallback() { |
} |
} |
+void MetricsDaemon::ScheduleMeminfoCallback(int wait) { |
+ if (testing_) { |
+ return; |
+ } |
+ g_timeout_add_seconds(wait, MeminfoCallbackStatic, this); |
+} |
+ |
+// static |
+gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) { |
+ return (static_cast<MetricsDaemon*>(handle))->MeminfoCallback(); |
+} |
+ |
+gboolean MetricsDaemon::MeminfoCallback() { |
+ char meminfo[4096]; |
+ int nchars; |
+ const char* meminfo_path = "/proc/meminfo"; |
+ int file = HANDLE_EINTR(open(meminfo_path, O_RDONLY)); |
+ if (file < 0) { |
+ PLOG(WARNING) << "cannot open " << meminfo_path; |
+ return false; |
+ } |
+ nchars = HANDLE_EINTR(read(file, meminfo, sizeof(meminfo))); |
+ HANDLE_EINTR(close(file)); |
+ |
+ if (nchars < 0) { |
+ LOG(WARNING) << "cannot read from " << meminfo_path; |
+ return false; |
+ } |
+ if (nchars == sizeof(meminfo)) { |
+ LOG(WARNING) << meminfo_path << " was truncated"; |
+ nchars -= 1; |
+ } |
+ meminfo[nchars] = '\0'; |
+ return ProcessMeminfo(meminfo); |
+} |
+ |
+gboolean MetricsDaemon::ProcessMeminfo(const char* meminfo) { |
+ /* This array has one string for every item of /proc/meminfo that we want to |
+ * report to UMA. They must be listed in the same order in which |
+ * /proc/meminfo prints them. |
+ */ |
+ struct { |
+ const char* name; |
+ const char* match; |
+ } fields[] = { |
+ { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory |
+ { "MemFree", "MemFree" }, |
+ { "Buffers", "Buffers" }, |
+ { "Cached", "Cached" }, |
+ // { "SwapCached", "SwapCached" }, |
+ { "Active", "Active" }, |
+ { "Inactive", "Inactive" }, |
+ { "ActiveAnon", "Active(anon)" }, |
+ { "InactiveAnon", "Inactive(anon)" }, |
+ { "ActiveFile" , "Active(file)" }, |
+ { "InactiveFile", "Inactive(file)" }, |
+ // { "Unevictable", "Unevictable" }, |
Mandeep Singh Baines
2011/04/06 22:35:26
Want this. Not percentage.
|
+ // { "Mlocked", "Mlocked" }, |
+ // { "SwapTotal", "SwapTotal" }, |
+ // { "SwapFree", "SwapFree" }, |
+ // { "Dirty", "Dirty" }, |
+ // { "Writeback", "Writeback" }, |
+ { "AnonPages", "AnonPages" }, |
+ { "Mapped", "Mapped" }, |
+ // { "Shmem", "Shmem" }, |
Mandeep Singh Baines
2011/04/06 22:35:26
Want this. Not percentage (luigi's comment)
|
+ // { "Slab", "Slab" }, |
Mandeep Singh Baines
2011/04/06 22:35:26
Want this. Not percentage (luigi's comment)
|
+ // { "SReclaimable", "SReclaimable" }, |
+ // { "SUnreclaim", "SUnreclaim" }, |
+ }; |
+ const int nfields = sizeof(fields) / sizeof(fields[0]); |
+ int total_memory = 0; |
+ |
+ /* Scan meminfo output and collect field values. Each field name has to |
+ * match a meminfo entry (case insensitive) after removing non-alpha |
+ * characters from the entry. */ |
+ int i = 0; |
+ const char* p = meminfo; |
+ |
+ for (;;) { |
+ if (i == nfields) { |
+ /* all fields are matched */ |
+ return true; |
+ } |
+ if (*p == '\0') { |
+ /* end of input reached while scanning */ |
+ LOG(WARNING) << "cannot find field " << fields[i].match << |
+ " and following"; |
+ return false; |
+ } |
+ if (strncmp(fields[i].match, p, strlen(fields[i].match)) == 0) { |
+ /* name matches: parse value and report */ |
+ int meminfo_value; |
+ char metrics_name[128]; |
+ char* rest; |
+ p += strlen(fields[i].match); |
+ while (!isdigit(*p)) { |
+ if (*p == '\0') { |
+ LOG(WARNING) << "error parsing meminfo value"; |
+ return false; |
+ } |
+ p++; |
+ } |
+ meminfo_value = (int) strtol(p, &rest, 10); |
+ if (p < rest) { |
+ p = rest; |
+ } else { |
+ LOG(WARNING) << "missing meminfo value"; |
+ return false; |
+ } |
+ if (i == 0) { |
+ /* special case: total memory */ |
+ total_memory = meminfo_value; |
+ } else { |
+ /* report value as percent of total memory */ |
+ snprintf(metrics_name, sizeof(metrics_name), |
+ "Platform.Meminfo%s", fields[i].name); |
+ SendLinearMetric(metrics_name, meminfo_value * 100 / total_memory, |
+ 100, 101); |
+ } |
+ /* start looking for next field */ |
+ i++; |
+ } |
+ /* move to next input line */ |
+ while (*p != '\n') { |
+ if (*p == '\0') { |
+ LOG(WARNING) << "error parsing meminfo line"; |
+ return false; |
+ } |
+ p++; |
+ } |
+ p++; |
+ } |
+} |
+ |
// static |
void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { |
if (count <= 0) |
@@ -636,3 +775,13 @@ void MetricsDaemon::SendMetric(const string& name, int sample, |
<< min << " " << max << " " << nbuckets; |
metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); |
} |
+ |
+void MetricsDaemon::SendLinearMetric(const string& name, int sample, |
+ int max, int nbuckets) { |
+ DLOG(INFO) << "received linear metric: " << name << " " << sample << " " |
+ << max << " " << nbuckets; |
+ /* TODO(semenzato): add a proper linear histogram to the Chrome external |
+ * metrics API. */ |
+ LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale"; |
+ metrics_lib_->SendEnumToUMA(name, sample, max); |
+} |