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
diff --git a/metricsd/metrics_client.cc b/metricsd/metrics_client.cc
index 57e96c2..f658b22 100644
--- a/metricsd/metrics_client.cc
+++ b/metricsd/metrics_client.cc
@@ -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 @@
" 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 @@
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 @@
return HasConsent();
case kModeIsGuestMode:
return IsGuestMode();
+ case kModeDumpLogs:
+ return DumpLogs();
default:
ShowUsage();
return 0;
diff --git a/metricsd/serialization/serialization_utils.cc b/metricsd/serialization/serialization_utils.cc
index 6dd8258..102c940 100644
--- a/metricsd/serialization/serialization_utils.cc
+++ b/metricsd/serialization/serialization_utils.cc
@@ -96,6 +96,50 @@
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 @@
return scoped_ptr<MetricSample>();
}
+void SerializationUtils::ReadMetricsFromFile(
+ const std::string& filename,
+ ScopedVector<MetricSample>* metrics) {
+ base::ScopedFD fd(OpenMetricsFile(filename, O_RDONLY));
+ if (!fd.is_valid()) {
+ return;
+ }
+
+ // This processes all messages in the log.
+ ReadAllMetricsFromFd(fd.get(), metrics);
+}
+
void SerializationUtils::ReadAndTruncateMetricsFromFile(
const std::string& filename,
ScopedVector<MetricSample>* metrics) {
- struct stat stat_buf;
- int result;
+ base::ScopedFD fd(OpenMetricsFile(filename, O_RDWR));
+ if (!fd.is_valid()) {
+ return;
+ }
- 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;
- }
- if (stat_buf.st_size == 0) {
- // Also nothing to collect.
- return;
- }
- base::ScopedFD fd(open(filename.c_str(), O_RDWR));
- if (fd.get() < 0) {
- DPLOG(ERROR) << filename << ": cannot open";
- 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 @@
// 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)
diff --git a/metricsd/serialization/serialization_utils.h b/metricsd/serialization/serialization_utils.h
index 67d4675..655652d 100644
--- a/metricsd/serialization/serialization_utils.h
+++ b/metricsd/serialization/serialization_utils.h
@@ -35,7 +35,11 @@
// 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);