Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 29c7ef9e authored by Luigi Semenzato's avatar Luigi Semenzato
Browse files

Add meminfo UMA collection.

Change-Id: Ief779a5bdc68b8e5bf2f1ed979bf30b50aca8e0f

BUG=chromium-os:13747
TEST=verify that Platform.Meminfo* entries are in about:histograms.

Review URL: http://codereview.chromium.org/6804014
parent 0f132bba
Loading
Loading
Loading
Loading
+134 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@

#include <base/file_util.h>
#include <base/logging.h>
#include <base/string_util.h>
#include <dbus/dbus-glib-lowlevel.h>

#include "counter.h"
@@ -102,6 +103,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 +251,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 +623,124 @@ 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() {
  std::string meminfo;
  const FilePath meminfo_path("/proc/meminfo");
  if (!file_util::ReadFileToString(meminfo_path, &meminfo)) {
    LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
    return false;
  }
  return ProcessMeminfo(meminfo);
}

gboolean MetricsDaemon::ProcessMeminfo(std::string meminfo) {
  // This array has one element 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;   // print name
    const char* match;  // string to match in output of /proc/meminfo
    int log_scale;      // report with log scale instead of linear percent
  } 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", 1 },
    // { "Mlocked", "Mlocked" },
    // { "SwapTotal", "SwapTotal" },
    // { "SwapFree", "SwapFree" },
    // { "Dirty", "Dirty" },
    // { "Writeback", "Writeback" },
    { "AnonPages", "AnonPages" },
    { "Mapped", "Mapped" },
    { "Shmem", "Shmem", 1 },
    { "Slab", "Slab", 1 },
    // { "SReclaimable", "SReclaimable" },
    // { "SUnreclaim", "SUnreclaim" },
  };
  // arraysize doesn't work here, probably can't handle anonymous structs
  const int nfields = sizeof(fields) / sizeof(fields[0]);
  int total_memory = 0;
  std::vector<std::string> lines;
  int nlines = Tokenize(meminfo, "\n", &lines);

  // 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;
  int iline = 0;
  for (;;) {
    if (i == nfields) {
      // all fields are matched
      return true;
    }
    if (iline == nlines) {
      // end of input reached while scanning
      LOG(WARNING) << "cannot find field " << fields[i].match
                   << " and following";
      return false;
    }

    std::vector<std::string> tokens;
    Tokenize(lines[iline], ": ", &tokens);

    if (strcmp(fields[i].match, tokens[0].c_str()) == 0) {
      // name matches: parse value and report
      int meminfo_value;
      char metrics_name[128];
      char* rest;
      meminfo_value = static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
      if (*rest != '\0') {
        LOG(WARNING) << "missing meminfo value";
        return false;
      }
      if (i == 0) {
        // special case: total memory
        total_memory = meminfo_value;
      } else {
        snprintf(metrics_name, sizeof(metrics_name),
                 "Platform.Meminfo%s", fields[i].name);
        if (fields[i].log_scale) {
          // report value in kbytes, log scale, 4Gb max
          SendMetric(metrics_name, meminfo_value, 1, 4 * 1000 * 1000, 100);
        } else {
          // report value as percent of total memory
          if (total_memory == 0) {
            // this "cannot happen"
            LOG(WARNING) << "borked meminfo parser";
            return false;
          }
          int percent = meminfo_value * 100 / total_memory;
          SendLinearMetric(metrics_name, percent, 100, 101);
        }
      }
      // start looking for next field
      i++;
    }
    iline++;
  }
}

// static
void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) {
  if (count <= 0)
@@ -636,3 +760,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);
}
+23 −0
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ class MetricsDaemon {
  FRIEND_TEST(MetricsDaemonTest, MessageFilter);
  FRIEND_TEST(MetricsDaemonTest, PowerStateChanged);
  FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash);
  FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo);
  FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo2);
  FRIEND_TEST(MetricsDaemonTest, ProcessUncleanShutdown);
  FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash);
  FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency);
@@ -125,6 +127,7 @@ class MetricsDaemon {
  static const char kMetricWriteSectorsShortName[];
  static const int kMetricDiskStatsShortInterval;
  static const int kMetricDiskStatsLongInterval;
  static const int kMetricMeminfoInterval;
  static const int kMetricSectorsIOMax;
  static const int kMetricSectorsBuckets;
  static const char kMetricsDiskStatsPath[];
@@ -234,6 +237,12 @@ class MetricsDaemon {
  void SendMetric(const std::string& name, int sample,
                  int min, int max, int nbuckets);

  // Sends a linear histogram sample to Chrome for transport to UMA. See
  // MetricsLibrary::SendToUMA in metrics_library.h for a description of the
  // arguments.
  void SendLinearMetric(const std::string& name, int sample,
                        int max, int nbuckets);

  // Initializes disk stats reporting.
  void DiskStatsReporterInit();

@@ -250,6 +259,20 @@ class MetricsDaemon {
  // Reports disk statistics.
  void DiskStatsCallback();

  // Schedules meminfo collection callback.
  void ScheduleMeminfoCallback(int wait);

  // Reports memory statistics (static version for glib).  Argument is a glib
  // artifact.
  static gboolean MeminfoCallbackStatic(void* handle);

  // Reports memory statistics.  Returns false on failure.
  gboolean MeminfoCallback();

  // Parses content of /proc/meminfo and sends fields of interest to UMA.
  // Returns false on errors.
  gboolean ProcessMeminfo(std::string meminfo);

  // Test mode.
  bool testing_;

+61 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ using std::vector;
using ::testing::_;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::AtLeast;

static const int kSecondsPerDay = 24 * 60 * 60;

@@ -578,6 +579,66 @@ TEST_F(MetricsDaemonTest, ReportDiskStats) {
  EXPECT_TRUE(ds_state != daemon_.diskstats_state_);
}

TEST_F(MetricsDaemonTest, ProcessMeminfo) {
  const char* meminfo = "\
MemTotal:        2000000 kB\n\
MemFree:         1000000 kB\n\
Buffers:           10492 kB\n\
Cached:           213652 kB\n\
SwapCached:            0 kB\n\
Active:           133400 kB\n\
Inactive:         183396 kB\n\
Active(anon):      92984 kB\n\
Inactive(anon):    58860 kB\n\
Active(file):      40416 kB\n\
Inactive(file):   124536 kB\n\
Unevictable:           0 kB\n\
Mlocked:               0 kB\n\
SwapTotal:             0 kB\n\
SwapFree:              0 kB\n\
Dirty:                40 kB\n\
Writeback:             0 kB\n\
AnonPages:         92652 kB\n\
Mapped:            59716 kB\n\
Shmem:             59196 kB\n\
Slab:              16656 kB\n\
SReclaimable:       6132 kB\n\
SUnreclaim:        10524 kB\n\
KernelStack:        1648 kB\n\
PageTables:         2780 kB\n\
NFS_Unstable:          0 kB\n\
Bounce:                0 kB\n\
WritebackTmp:          0 kB\n\
CommitLimit:      970656 kB\n\
Committed_AS:    1260528 kB\n\
VmallocTotal:     122880 kB\n\
VmallocUsed:       12144 kB\n\
VmallocChunk:     103824 kB\n\
DirectMap4k:        9636 kB\n\
DirectMap2M:     1955840 kB\n\
";
  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, 100))
      .Times(AtLeast(1));
  EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _))
      .Times(AtLeast(1));
  EXPECT_CALL(metrics_lib_, SendToUMA("NFS_Unstable", _, _, _, _))
      .Times(0);
  EXPECT_CALL(metrics_lib_, SendEnumToUMA("NFS_Unstable", _, _))
      .Times(0);
  EXPECT_TRUE(daemon_.ProcessMeminfo(meminfo));
}

TEST_F(MetricsDaemonTest, ProcessMeminfo2) {
  const char* meminfo = "\
MemTotal:        2000000 kB\n\
MemFree:         1000000 kB\n\
";
  /* Not enough fields */
  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 50, 100))
      .Times(1);
  EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo));
}

int main(int argc, char** argv) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();