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

Commit 5f646008 authored by James Hawkins's avatar James Hawkins
Browse files

metrics: Add an option to metrics_client to dump the logs cache.

The format of the metrics dump is rudimentary just yet. Here is an example:

  Metrics from /data/misc/metrics/uma-events

  name: hello     type: USER_ACTION
  name: world     type: USER_ACTION

This required the following changes:
* Added -d option to metrics_client
* Refactored file handling in SerializationUtils
 - Factored out file opening and log parsing into helper methods
 - Added ReadMetricsFromFile which is read-only so does not truncate the file

Change-Id: I6032d74242c79c678ec42a14e78fccc54e7af455
parent aaf4fcf4
Loading
Loading
Loading
Loading
+51 −2
Original line number Diff line number Diff line
@@ -17,9 +17,15 @@
#include <cstdio>
#include <cstdlib>

#include <base/memory/scoped_vector.h>

#include "constants.h"
#include "metrics/metrics_library.h"
#include "serialization/metric_sample.h"
#include "serialization/serialization_utils.h"

enum Mode {
    kModeDumpLogs,
    kModeSendSample,
    kModeSendEnumSample,
    kModeSendSparseSample,
@@ -36,12 +42,13 @@ void ShowUsage() {
          "        metrics_client -s   name sample\n"
          "        metrics_client -v   event\n"
          "        metrics_client -u action\n"
          "        metrics_client [-cg]\n"
          "        metrics_client [-cdg]\n"
          "\n"
          "  default: send metric with integer values \n"
          "           |min| > 0, |min| <= sample < |max|\n"
          "  -c: return exit status 0 if user consents to stats, 1 otherwise,\n"
          "      in guest mode always return 1\n"
          "  -d: dump cached logs to the console\n"
          "  -e: send linear/enumeration histogram data\n"
          "  -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
          "  -s: send a sparse histogram sample\n"
@@ -132,17 +139,57 @@ static int IsGuestMode() {
  return metrics_lib.IsGuestMode() ? 0 : 1;
}

static int DumpLogs() {
  printf("Metrics from %s\n\n", metrics::kMetricsEventsFilePath);

  ScopedVector<metrics::MetricSample> metrics;
  metrics::SerializationUtils::ReadMetricsFromFile(
      metrics::kMetricsEventsFilePath, &metrics);

  for (ScopedVector<metrics::MetricSample>::const_iterator i = metrics.begin();
       i != metrics.end(); ++i) {
    const metrics::MetricSample* sample = *i;
    printf("name: %s\t", sample->name().c_str());
    printf("type: ");

    switch (sample->type()) {
      case metrics::MetricSample::CRASH:
        printf("CRASH");
        break;
      case metrics::MetricSample::HISTOGRAM:
        printf("HISTOGRAM");
        break;
      case metrics::MetricSample::LINEAR_HISTOGRAM:
        printf("LINEAR_HISTOGRAM");
        break;
      case metrics::MetricSample::SPARSE_HISTOGRAM:
        printf("SPARSE_HISTOGRAM");
        break;
      case metrics::MetricSample::USER_ACTION:
        printf("USER_ACTION");
        break;
    }

    printf("\n");
  }

  return 0;
}

int main(int argc, char** argv) {
  enum Mode mode = kModeSendSample;
  bool secs_to_msecs = false;

  // Parse arguments
  int flag;
  while ((flag = getopt(argc, argv, "abcegstuv")) != -1) {
  while ((flag = getopt(argc, argv, "abcdegstuv")) != -1) {
    switch (flag) {
      case 'c':
        mode = kModeHasConsent;
        break;
      case 'd':
        mode = kModeDumpLogs;
        break;
      case 'e':
        mode = kModeSendEnumSample;
        break;
@@ -203,6 +250,8 @@ int main(int argc, char** argv) {
      return HasConsent();
    case kModeIsGuestMode:
      return IsGuestMode();
    case kModeDumpLogs:
      return DumpLogs();
    default:
      ShowUsage();
      return 0;
+60 −28
Original line number Diff line number Diff line
@@ -96,6 +96,50 @@ bool ReadMessage(int fd, std::string* message) {
  return true;
}


// Opens the metrics log file at |filename| in the given |mode|.
//
// Returns the file descriptor wrapped in a valid ScopedFD on success.
base::ScopedFD OpenMetricsFile(const std::string& filename, mode_t mode) {
  struct stat stat_buf;
  int result;

  result = stat(filename.c_str(), &stat_buf);
  if (result < 0) {
    if (errno != ENOENT)
      DPLOG(ERROR) << filename << ": bad metrics file stat";

    // Nothing to collect---try later.
    return base::ScopedFD();
  }
  if (stat_buf.st_size == 0) {
    // Also nothing to collect.
    return base::ScopedFD();
  }
  base::ScopedFD fd(open(filename.c_str(), mode));
  if (fd.get() < 0) {
    DPLOG(ERROR) << filename << ": cannot open";
    return base::ScopedFD();
  }

  return fd.Pass();
}


// Parses the contents of the metrics log file descriptor |fd| into |metrics|.
void ReadAllMetricsFromFd(int fd, ScopedVector<MetricSample>* metrics) {
  for (;;) {
    std::string message;

    if (!ReadMessage(fd, &message))
      break;

    scoped_ptr<MetricSample> sample = SerializationUtils::ParseSample(message);
    if (sample)
      metrics->push_back(sample.release());
  }
}

}  // namespace

scoped_ptr<MetricSample> SerializationUtils::ParseSample(
@@ -131,30 +175,27 @@ scoped_ptr<MetricSample> SerializationUtils::ParseSample(
  return scoped_ptr<MetricSample>();
}

void SerializationUtils::ReadAndTruncateMetricsFromFile(
void SerializationUtils::ReadMetricsFromFile(
    const std::string& filename,
    ScopedVector<MetricSample>* metrics) {
  struct stat stat_buf;
  int result;

  result = stat(filename.c_str(), &stat_buf);
  if (result < 0) {
    if (errno != ENOENT)
      DPLOG(ERROR) << filename << ": bad metrics file stat";

    // Nothing to collect---try later.
  base::ScopedFD fd(OpenMetricsFile(filename, O_RDONLY));
  if (!fd.is_valid()) {
    return;
  }
  if (stat_buf.st_size == 0) {
    // Also nothing to collect.
    return;

  // This processes all messages in the log.
  ReadAllMetricsFromFd(fd.get(), metrics);
}
  base::ScopedFD fd(open(filename.c_str(), O_RDWR));
  if (fd.get() < 0) {
    DPLOG(ERROR) << filename << ": cannot open";

void SerializationUtils::ReadAndTruncateMetricsFromFile(
    const std::string& filename,
    ScopedVector<MetricSample>* metrics) {
  base::ScopedFD fd(OpenMetricsFile(filename, O_RDWR));
  if (!fd.is_valid()) {
    return;
  }
  result = flock(fd.get(), LOCK_EX);

  int result = flock(fd.get(), LOCK_EX);
  if (result < 0) {
    DPLOG(ERROR) << filename << ": cannot lock";
    return;
@@ -162,16 +203,7 @@ void SerializationUtils::ReadAndTruncateMetricsFromFile(

  // This processes all messages in the log. When all messages are
  // read and processed, or an error occurs, truncate the file to zero size.
  for (;;) {
    std::string message;

    if (!ReadMessage(fd.get(), &message))
      break;

    scoped_ptr<MetricSample> sample = ParseSample(message);
    if (sample)
      metrics->push_back(sample.release());
  }
  ReadAllMetricsFromFd(fd.get(), metrics);

  result = ftruncate(fd.get(), 0);
  if (result < 0)
+5 −1
Original line number Diff line number Diff line
@@ -35,7 +35,11 @@ namespace SerializationUtils {
// deserialization was successful) or a NULL scoped_ptr.
scoped_ptr<MetricSample> ParseSample(const std::string& sample);

// Reads all samples from a file and truncate the file when done.
// Reads all samples from a file. The file contents remain unchanged.
void ReadMetricsFromFile(const std::string& filename,
                         ScopedVector<MetricSample>* metrics);

// Reads all samples from a file and truncates the file when done.
void ReadAndTruncateMetricsFromFile(const std::string& filename,
                                    ScopedVector<MetricSample>* metrics);