diff --git a/metricsd/metrics_client.cc b/metricsd/metrics_client.cc index 57e96c29e..f658b22a7 100644 --- a/metricsd/metrics_client.cc +++ b/metricsd/metrics_client.cc @@ -17,9 +17,15 @@ #include #include +#include + +#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; + metrics::SerializationUtils::ReadMetricsFromFile( + metrics::kMetricsEventsFilePath, &metrics); + + for (ScopedVector::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; diff --git a/metricsd/serialization/serialization_utils.cc b/metricsd/serialization/serialization_utils.cc index 6dd8258d2..102c9404a 100644 --- a/metricsd/serialization/serialization_utils.cc +++ b/metricsd/serialization/serialization_utils.cc @@ -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* metrics) { + for (;;) { + std::string message; + + if (!ReadMessage(fd, &message)) + break; + + scoped_ptr sample = SerializationUtils::ParseSample(message); + if (sample) + metrics->push_back(sample.release()); + } +} + } // namespace scoped_ptr SerializationUtils::ParseSample( @@ -131,30 +175,27 @@ scoped_ptr SerializationUtils::ParseSample( return scoped_ptr(); } +void SerializationUtils::ReadMetricsFromFile( + const std::string& filename, + ScopedVector* 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* 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 @@ 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 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 67d46759f..655652d50 100644 --- a/metricsd/serialization/serialization_utils.h +++ b/metricsd/serialization/serialization_utils.h @@ -35,7 +35,11 @@ namespace SerializationUtils { // deserialization was successful) or a NULL scoped_ptr. scoped_ptr 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* metrics); + +// Reads all samples from a file and truncates the file when done. void ReadAndTruncateMetricsFromFile(const std::string& filename, ScopedVector* metrics);