From 65b014683927aca25d5d7e62b4bdd46e19d46bf6 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 14 Apr 2010 13:32:20 -0700 Subject: [PATCH 001/200] Unify metrics_collection and metrics_daemon into metrics. Tested new binaries on the target. Tested incremental build. Tested arm-generic build. Review URL: http://codereview.chromium.org/1650006 --- metrics/Makefile | 91 +++++++++++++++ metrics/README | 8 ++ metrics/marshal_void__string_boxed.list | 1 + metrics/metrics_client.cc | 73 ++++++++++++ metrics/metrics_daemon.cc | 144 ++++++++++++++++++++++++ metrics/metrics_daemon.h | 88 +++++++++++++++ metrics/metrics_daemon_main.cc | 16 +++ metrics/metrics_daemon_unittest.cc | 10 ++ metrics/metrics_library.cc | 100 ++++++++++++++++ metrics/metrics_library.h | 33 ++++++ metrics/network_states.h | 34 ++++++ metrics/omaha_tracker.sh | 0 metrics/syslog_parser.sh | 69 ++++++++++++ 13 files changed, 667 insertions(+) create mode 100644 metrics/Makefile create mode 100644 metrics/README create mode 100644 metrics/marshal_void__string_boxed.list create mode 100644 metrics/metrics_client.cc create mode 100644 metrics/metrics_daemon.cc create mode 100644 metrics/metrics_daemon.h create mode 100644 metrics/metrics_daemon_main.cc create mode 100644 metrics/metrics_daemon_unittest.cc create mode 100644 metrics/metrics_library.cc create mode 100644 metrics/metrics_library.h create mode 100644 metrics/network_states.h create mode 100644 metrics/omaha_tracker.sh create mode 100755 metrics/syslog_parser.sh diff --git a/metrics/Makefile b/metrics/Makefile new file mode 100644 index 000000000..0b047361b --- /dev/null +++ b/metrics/Makefile @@ -0,0 +1,91 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Makefile for metrics utilities -- library, client and daemon +# + +CCONFIG = $(shell $(PKG_CONFIG) --cflags dbus-1 glib-2.0 dbus-glib-1) +LDCONFIG = $(shell $(PKG_CONFIG) --libs dbus-1 glib-2.0 gthread-2.0 dbus-glib-1) + +CFLAGS = -Wall -Werror -I/usr/include -fpic -O2 $(CCONFIG) +CXXFLAGS = $(CFLAGS) -fno-exceptions + +CLIENT = metrics_client +DAEMON = metrics_daemon +TESTDAEMON = test_daemon +LIB = libmetrics.a +SHAREDLIB = libmetrics.so + +CLIENT_OBJS = \ + metrics_client.o +LIB_OBJS = \ + metrics_library.o +DAEMON_OBJS = \ + marshal_void__string_boxed.o \ + metrics_daemon.o \ + metrics_daemon_main.o +TESTDAEMON_OBJS = \ + marshal_void__string_boxed.o \ + metrics_daemon.o \ + metrics_daemon_unittest.o + +DAEMON_LDFLAGS = $(LDCONFIG) -lrt -lbase -lpthread -lgflags +TESTDAEMON_LIBS = -lgtest + +all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) $(TESTDAEMON) + +$(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) + $(CXX) $(LDFLAGS) $^ -o $@ + +$(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) + $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) + +$(TESTDAEMON): $(TESTDAEMON_OBJS) $(SHAREDLIB) + $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(TESTDAEMON_LIBS) + +$(LIB): $(LIB_OBJS) + ar rcs $@ $^ + +$(SHAREDLIB): $(LIB_OBJS) + $(CXX) $(LDFLAGS) -shared $^ -o $@ + +%.o: %.cc + $(CXX) $(CXXFLAGS) -c $< -o $@ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +%.c: %.list + glib-genmarshal --body --prefix=marshal $< > $@ + +%.h: %.list + glib-genmarshal --header --prefix=marshal $< > $@ + +# dependencies in addition to those defined by the rules + +metrics_daemon.o: \ + marshal_void__string_boxed.h \ + metrics_daemon.h \ + network_states.h +metrics_daemon_unittest.o: \ + marshal_void__string_boxed.h \ + metrics_daemon.h \ + network_states.h +marshal_void__string_boxed.o: \ + marshal_void__string_boxed.h + +.PRECIOUS: marshal_void__string_boxed.c # keep around for debugging + +install: + install $(CLIENT) $(DESTDIR)/usr/bin + install $(DAEMON) $(DESTDIR)/usr/bin + install $(LIB) $(DESTDIR)/usr/lib + install $(SHAREDLIB) $(DESTDIR)/usr/lib + install metrics_library.h $(DESTDIR)/usr/include + install syslog_parser.sh $(DESTDIR)/usr/bin + install omaha_tracker.sh $(DESTDIR)/usr/sbin + +clean: + rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) $(TESTDAEMON) + rm -f *.o marshal_void__string_boxed.[ch] diff --git a/metrics/README b/metrics/README new file mode 100644 index 000000000..d88992daf --- /dev/null +++ b/metrics/README @@ -0,0 +1,8 @@ +This packages contains all scripts and programs assoicated with metrics +collection for both Chrome's User Metrics Server and automated performance +metrics collection via Autotest. + +The package includes the metrics daemon for Chrome OS. This program +runs as a daemon and collects events by polling and listening for +d-bus signals. It then adds timing (if needed) and sends the events +to Chrome for transport to the UMA server at Google. diff --git a/metrics/marshal_void__string_boxed.list b/metrics/marshal_void__string_boxed.list new file mode 100644 index 000000000..e72aa4bc3 --- /dev/null +++ b/metrics/marshal_void__string_boxed.list @@ -0,0 +1 @@ +VOID:STRING,BOXED diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc new file mode 100644 index 000000000..cdea0124c --- /dev/null +++ b/metrics/metrics_client.cc @@ -0,0 +1,73 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include + +#include +#include + +#include "metrics_library.h" + +using namespace std; + +// Usage: metrics_client [-ab] metric_name metric_value +int main(int argc, char** argv) { + bool send_to_autotest = false; + bool send_to_chrome = true; + int metric_name_index = 1; + int metric_value_index = 2; + bool print_usage = false; + + if (argc >= 3) { + // Parse arguments + int flag; + while ((flag = getopt(argc, argv, "ab")) != -1) { + switch (flag) { + case 'a': + send_to_autotest = true; + send_to_chrome = false; + break; + case 'b': + send_to_chrome = true; + send_to_autotest = true; + break; + default: + print_usage = true; + break; + } + } + metric_name_index = optind; + metric_value_index = optind + 1; + } else { + print_usage = true; + } + + // Metrics value should be the last argument passed + if ((metric_value_index + 1) != argc) { + print_usage = true; + } + + if (print_usage) { + cerr << "Usage: metrics_client [-ab] name value" << endl; + cerr << endl; + cerr << " default: send metric to chrome only" << endl; + cerr << " -a: send metric to autotest only" << endl; + cerr << " -b: send metric to both chrome and autotest" << endl; + return 1; + } + + // Send metrics + if (send_to_autotest) { + MetricsLibrary::SendToAutotest(argv[metric_name_index], + argv[metric_value_index]); + } + if (send_to_chrome) { + MetricsLibrary::SendToChrome(argv[metric_name_index], + argv[metric_value_index]); + } + return 0; +} diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc new file mode 100644 index 000000000..a924b8ab4 --- /dev/null +++ b/metrics/metrics_daemon.cc @@ -0,0 +1,144 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics_daemon.h" +#include "metrics_library.h" + +#include + +extern "C" { +#include "marshal_void__string_boxed.h" +} + +#include + +#define SAFE_MESSAGE(e) ((e && e->message) ? e->message : "unknown error") + +MetricsDaemon::NetworkState +MetricsDaemon::network_states_[MetricsDaemon::kNumberNetworkStates] = { +#define STATE(name, capname) { #name, "Connman" # capname }, +#include "network_states.h" +}; + +void MetricsDaemon::Run(bool run_as_daemon, bool testing) { + Init(testing); + if (!run_as_daemon || daemon(0, 0) == 0) { + Loop(); + } +} + +void MetricsDaemon::Init(bool testing) { + testing_ = testing; + network_state_id_ = kUnknownNetworkStateId; + + ::g_thread_init(NULL); + ::g_type_init(); + ::dbus_g_thread_init(); + + ::GError* error = NULL; + ::DBusGConnection* dbc = ::dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); + // Note that LOG(FATAL) terminates the process; otherwise we'd have to worry + // about leaking |error|. + LOG_IF(FATAL, dbc == NULL) << + "cannot connect to dbus: " << SAFE_MESSAGE(error); + + ::DBusGProxy* net_proxy = ::dbus_g_proxy_new_for_name( + dbc, "org.moblin.connman", "/", "org.moblin.connman.Metrics"); + LOG_IF(FATAL, net_proxy == NULL) << "no dbus proxy for network"; + +#if 0 + // Unclear how soon one can call dbus_g_type_get_map(). Doing it before the + // call to dbus_g_bus_get() results in a (non-fatal) assertion failure. + // GetProperties returns a hash table. + hashtable_gtype = ::dbus_g_type_get_map("GHashTable", G_TYPE_STRING, + G_TYPE_VALUE); +#endif + + dbus_g_object_register_marshaller(marshal_VOID__STRING_BOXED, + G_TYPE_NONE, + G_TYPE_STRING, + G_TYPE_VALUE, + G_TYPE_INVALID); + ::dbus_g_proxy_add_signal(net_proxy, "ConnectionStateChanged", + G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); + ::dbus_g_proxy_connect_signal(net_proxy, "ConnectionStateChanged", + G_CALLBACK(&StaticNetSignalHandler), + this, NULL); +} + +void MetricsDaemon::Loop() { + ::GMainLoop* loop = ::g_main_loop_new(NULL, false); + ::g_main_loop_run(loop); +} + +void MetricsDaemon::StaticNetSignalHandler(::DBusGProxy* proxy, + const char* property, + const ::GValue* value, + void *data) { + (static_cast(data))->NetSignalHandler(proxy, property, value); +} + +void MetricsDaemon::NetSignalHandler(::DBusGProxy* proxy, + const char* property, + const ::GValue* value) { + if (strcmp("ConnectionState", property) != 0) { + return; + } + + const char* newstate = static_cast(g_value_get_string(value)); + LogNetworkStateChange(newstate); +} + +void MetricsDaemon::LogNetworkStateChange(const char* newstate) { + NetworkStateId new_id = GetNetworkStateId(newstate); + if (new_id == kUnknownNetworkStateId) { + LOG(WARNING) << "unknown network connection state " << newstate; + return; + } + NetworkStateId old_id = network_state_id_; + if (new_id == old_id) { // valid new state and no change + return; + } + struct timeval now; + if (gettimeofday(&now, NULL) != 0) { + PLOG(WARNING) << "gettimeofday"; + } + if (old_id != kUnknownNetworkStateId) { + struct timeval diff; + timersub(&now, &network_state_start_, &diff); + int diff_ms = diff.tv_usec / 1000 + diff.tv_sec * 1000; + // Saturates rather than overflowing. We expect this to be statistically + // insignificant, since INT_MAX milliseconds is 24.8 days. + if (diff.tv_sec >= INT_MAX / 1000) { + diff_ms = INT_MAX; + } + char buffer[100]; + snprintf(buffer, sizeof(buffer), "%d", diff_ms); + if (testing_) { + TestPublishMetric(network_states_[old_id].stat_name, buffer); + } else { + ChromePublishMetric(network_states_[old_id].stat_name, buffer); + } + } + network_state_id_ = new_id; + network_state_start_ = now; +} + +MetricsDaemon::NetworkStateId +MetricsDaemon::GetNetworkStateId(const char* state_name) { + for (int i = 0; i < kNumberNetworkStates; i++) { + if (strcmp(state_name, network_states_[i].name) == 0) { + return static_cast(i); + } + } + return static_cast(-1); +} + +void MetricsDaemon::ChromePublishMetric(const char* name, const char* value) { + MetricsLibrary::SendToChrome(name, value); +} + +void MetricsDaemon::TestPublishMetric(const char* name, const char* value) { + LOG(INFO) << "received metric: " << name << " " << value; +} diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h new file mode 100644 index 000000000..2ac1ea0f8 --- /dev/null +++ b/metrics/metrics_daemon.h @@ -0,0 +1,88 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_DAEMON_H_ +#define METRICS_DAEMON_H_ + +#include +#include +#include + +class MetricsDaemon { + + public: + MetricsDaemon() + : network_state_id_(kUnknownNetworkStateId) { + } + ~MetricsDaemon() {} + + // Does all the work. If |run_as_daemon| is true, daemonize by forking. If + // |testing| is true, log the stats instead of sending them to Chrome. + void Run(bool run_as_daemon, bool testing); + + private: + // Shared with Chrome for transport. + static const char* kMetricsFilePath; + static const int kMetricsMessageMaxLength = 4096; + + // The network states. See network_states.h. + typedef enum { + // Initial/unknown network state id. + kUnknownNetworkStateId = -1, +#define STATE(name, capname) kNetworkState ## capname, +#include "network_states.h" + kNumberNetworkStates + } NetworkStateId; + + typedef struct { + const char* name; + const char* stat_name; + } NetworkState; + + // Initializes. + void Init(bool testing); + + // Creates the event loop and enters it. + void Loop(); + + // Static callback for network events on DBus. + static void StaticNetSignalHandler(::DBusGProxy* proxy, const char* property, + const ::GValue* value, void* data); + + // Callback for network events on DBus. + void NetSignalHandler(::DBusGProxy* proxy, const char* property, + const ::GValue* value); + + // This is called at each network state change. The new state is identified + // by the string @newstate. As a side effect, this method ships to Chrome + // (or prints to stdout when testing) the name and duration of the state + // that has ended. + void LogNetworkStateChange(const char* newstate); + + // Given a string with the name of a state, returns the id for the state. + NetworkStateId GetNetworkStateId(const char* state_name); + + // Sends a stat to Chrome for transport to UMA. + void ChromePublishMetric(const char* name, const char* value); + + // Prints a stat for testing. + void TestPublishMetric(const char* name, const char* value); + +#if 0 + // Fetches a name-value hash table from DBus. + bool GetProperties(::DBusGProxy* proxy, ::GHashTable** table); + + // The type descriptor for a glib hash table. + GType hashtable_gtype; +#endif + + // Array of network states of interest. + static NetworkState network_states_[kNumberNetworkStates]; + + bool testing_; // just testing + NetworkStateId network_state_id_; // id of current state + struct timeval network_state_start_; // when current state was entered +}; + +#endif // METRICS_DAEMON_H_ diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc new file mode 100644 index 000000000..302bdcb5e --- /dev/null +++ b/metrics/metrics_daemon_main.cc @@ -0,0 +1,16 @@ +// Copyright (c) 2009 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include + +#include "metrics_daemon.h" + +DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); + +int main(int argc, char** argv) { + MetricsDaemon::MetricsDaemon d; + google::ParseCommandLineFlags(&argc, &argv, true); + d.Run(FLAGS_daemon, false); +} diff --git a/metrics/metrics_daemon_unittest.cc b/metrics/metrics_daemon_unittest.cc new file mode 100644 index 000000000..222cefab7 --- /dev/null +++ b/metrics/metrics_daemon_unittest.cc @@ -0,0 +1,10 @@ +// Copyright (c) 2009 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics_daemon.h" + +int main(int argc, char** argv) { + MetricsDaemon d; + d.Run(false, true); +} diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc new file mode 100644 index 000000000..99ad61640 --- /dev/null +++ b/metrics/metrics_library.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * metrics_library.cc + * + * Created on: Dec 1, 2009 + * Author: sosa + */ + +#include "metrics_library.h" + +#include +#include +#include +#include + +#define READ_WRITE_ALL_FILE_FLAGS \ + (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) + +static const char kAutotestPath[] = "/tmp/.chromeos-metrics-autotest"; +static const char kChromePath[] = "/tmp/.chromeos-metrics"; +static const int kBufferSize = 4096; + +using namespace std; + +// TODO(sosa@chromium.org) - use Chromium logger instead of stderr +void MetricsLibrary::PrintError(const char *message, const char *file, + int code) { + const char *kProgramName = "metrics_library"; + if (code == 0) { + fprintf(stderr, "%s: %s\n", kProgramName, message); + } else if (file == NULL) { + fprintf(stderr, "%s: ", kProgramName); + perror(message); + } else { + fprintf(stderr, "%s: %s: ", kProgramName, file); + perror(message); + } +} + +void MetricsLibrary::SendToAutotest(string name, string value) { + FILE *autotest_file = fopen(kAutotestPath, "a+"); + if (autotest_file == NULL) { + PrintError("fopen", kAutotestPath, errno); + return; + } + + fprintf(autotest_file, "%s=%s\n", name.c_str(), value.c_str()); + fclose(autotest_file); +} + +void MetricsLibrary::SendToChrome(string name, string value) { + int chrome_fd = open(kChromePath, + O_WRONLY | O_APPEND | O_CREAT, + READ_WRITE_ALL_FILE_FLAGS); + // If we failed to open it, return + if (chrome_fd < 0) { + PrintError("open", kChromePath, errno); + return; + } + + // Need to chmod because open flags are anded with umask. + if (fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS) < 0) { + PrintError("fchmod", kChromePath, errno); + close(chrome_fd); + return; + } + + // Grab an exclusive lock to protect Chrome from truncating underneath us + if (flock(chrome_fd, LOCK_EX) < 0) { + PrintError("flock", kChromePath, errno); + close(chrome_fd); + return; + } + + // Message format is: LENGTH (binary), NAME, VALUE + char message[kBufferSize]; + char *curr_ptr = message; + int32_t message_length = + name.length() + value.length() + 2 + sizeof(message_length); + if (message_length > static_cast(sizeof(message))) + PrintError("name/value too long", NULL, 0); + + // Make sure buffer is blanked + memset(message, 0, sizeof(message)); + memcpy(curr_ptr, &message_length, sizeof(message_length)); + curr_ptr += sizeof(message_length); + strncpy(curr_ptr, name.c_str(), name.length()); + curr_ptr += name.length() + 1; + strncpy(curr_ptr, value.c_str(), value.length()); + if (write(chrome_fd, message, message_length) != message_length) + PrintError("write", kChromePath, errno); + + // Release the file lock and close file + if (flock(chrome_fd, LOCK_UN) < 0) + PrintError("unlock", kChromePath, errno); + close(chrome_fd); +} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h new file mode 100644 index 000000000..f1268c971 --- /dev/null +++ b/metrics/metrics_library.h @@ -0,0 +1,33 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * metrics_library.h + * + * Created on: Dec 1, 2009 + * Author: sosa + */ + +#ifndef METRICS_LIBRARY_H_ +#define METRICS_LIBRARY_H_ + +#include +#include + +// TODO(sosa@chromium.org): Add testing for send methods + +// Library used to send metrics both Autotest and Chrome +class MetricsLibrary { + public: + // Sends histogram data to Chrome. + static void SendToChrome(std::string name, std::string value); + // Sends to Autotest. + static void SendToAutotest(std::string name, std::string value); + + private: + // Prints message to stderr + static void PrintError(const char *message, const char *file, int code); +}; + +#endif /* METRICS_LIBRARY_H_ */ diff --git a/metrics/network_states.h b/metrics/network_states.h new file mode 100644 index 000000000..69205ed64 --- /dev/null +++ b/metrics/network_states.h @@ -0,0 +1,34 @@ +// Copyright (c) 2009 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A table of network states, to be included when building tabular things. +// +// This file is used to construct two things: an enumerated type in +// metrics_daemon.h, and a table of structures with state names in +// metrics_daemon.cc. Including this file ensures that the two tables are +// always in sync (and saves typing). I don't know of other ways of achieving +// the same result in C/C++, but it doesn't mean there isn't one. + +// Before you include this file, define STATE to do something useful, or else +// if will be a no-op. STATE will be undefined on exit. Don't worry about +// collisions for the STATE macro (as long as it's a macro) because the +// compiler will flag them---in that case, just change the name. If someone is +// misguided enough to use STATE for something other than a macro, the error +// messages will be slightly more complicated. + + +#ifndef STATE +#define STATE(name, capname) +#endif + +STATE(association, Association) +STATE(configuration, Configuration) +STATE(disconnect, Disconnect) +STATE(failure, Failure) +STATE(idle, Idle) +STATE(offline, Offline) +STATE(online, Online) +STATE(ready, Ready) + +#undef STATE diff --git a/metrics/omaha_tracker.sh b/metrics/omaha_tracker.sh new file mode 100644 index 000000000..e69de29bb diff --git a/metrics/syslog_parser.sh b/metrics/syslog_parser.sh new file mode 100755 index 000000000..7f3003ff4 --- /dev/null +++ b/metrics/syslog_parser.sh @@ -0,0 +1,69 @@ +#! /bin/sh + +# This script parses /var/log/syslog for messages from programs that log +# uptime and disk stats (number of sectors read). It then outputs +# these stats in a format usable by the metrics collector, which forwards +# them to autotest and UMA. + +# To add a new metric add a line below, as PROGRAM_NAME METRIC_NAME. +# PROGRAM_NAME is the name of the job whose start time we +# are interested in. METRIC_NAME is the prefix we want to use for +# reporting to UMA and autotest. The script prepends "Time" and +# "Sectors" to METRIC_NAME for the two available measurements, uptime +# and number of sectors read thus far. + +# You will need to emit messages similar to the following in order to add a +# a metric using this process. You will need to emit both a start and stop +# time and the metric reported will be the difference in values + +# Nov 15 08:05 localhost PROGRAM_NAME[822]: start METRIC_NAME time 12 sectors 56 +# Nov 15 08:05 localhost PROGRAM_NAME[822]: stop METRIC_NAME time 24 sectors 68 + +# If you add metrics without a start, it is assumed you are requesting the +# time differece from system start + +# Metrics we are interested in measuring +METRICS=" +upstart start_x +" + +first=1 +program="" + +# Get the metrics for all things +for m in $METRICS +do + if [ $first -eq 1 ] + then + first=0 + program_name=$m + else + first=1 + metrics_name=$m + + # Example of line from /var/log/messages: + # Nov 15 08:05:42 localhost connmand[822]: start metric time 12 sectors 56 + # "upstart:" is $5, 1234 is $9, etc. + program="${program}/$program_name([[0-9]+]:|:) start $metrics_name/\ + { + metrics_start[\"${metrics_name}Time\"] = \$9; + metrics_start[\"${metrics_name}Sectors\"] = \$11; + }" + program="${program}/$program_name([[0-9]+]:|:) stop $metrics_name/\ + { + metrics_stop[\"${metrics_name}Time\"] = \$9; + metrics_stop[\"${metrics_name}Sectors\"] = \$11; + }" + fi +done + +# Do all the differencing here +program="${program}\ +END{ + for (i in metrics_stop) { + value_time = metrics_stop[i] - metrics_start[i]; + print i \"=\" value_time; + } +}" + +exec awk "$program" /var/log/syslog \ No newline at end of file From 4fcb2ac28fe7c7010c72844b911f869baaf798d9 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 15 Apr 2010 16:40:23 -0700 Subject: [PATCH 002/200] metrics cleanup and fixes. - value is int now. - add seconds to milliseconds option to metrics_client and use it - chmod chronos/root fix - style fixes Review URL: http://codereview.chromium.org/1649007 --- metrics/metrics_client.cc | 42 +++++++----- metrics/metrics_daemon.cc | 10 ++- metrics/metrics_daemon.h | 4 +- metrics/metrics_library.cc | 136 +++++++++++++++++++++++++------------ metrics/metrics_library.h | 13 ++-- 5 files changed, 125 insertions(+), 80 deletions(-) diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index cdea0124c..17f933cbb 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -2,22 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include -#include -#include -#include - +#include #include -#include #include "metrics_library.h" -using namespace std; - // Usage: metrics_client [-ab] metric_name metric_value int main(int argc, char** argv) { bool send_to_autotest = false; bool send_to_chrome = true; + bool secs_to_msecs = false; int metric_name_index = 1; int metric_value_index = 2; bool print_usage = false; @@ -25,7 +19,7 @@ int main(int argc, char** argv) { if (argc >= 3) { // Parse arguments int flag; - while ((flag = getopt(argc, argv, "ab")) != -1) { + while ((flag = getopt(argc, argv, "abt")) != -1) { switch (flag) { case 'a': send_to_autotest = true; @@ -35,6 +29,9 @@ int main(int argc, char** argv) { send_to_chrome = true; send_to_autotest = true; break; + case 't': + secs_to_msecs = true; + break; default: print_usage = true; break; @@ -52,22 +49,31 @@ int main(int argc, char** argv) { } if (print_usage) { - cerr << "Usage: metrics_client [-ab] name value" << endl; - cerr << endl; - cerr << " default: send metric to chrome only" << endl; - cerr << " -a: send metric to autotest only" << endl; - cerr << " -b: send metric to both chrome and autotest" << endl; + fprintf(stderr, + "Usage: metrics_client [-abt] name value\n" + "\n" + " default: send metric with integer value to chrome only\n" + " -a: send metric to autotest only\n" + " -b: send metric to both chrome and autotest\n" + " -t: convert value from float seconds to int milliseconds\n"); return 1; } + const char* name = argv[metric_name_index]; + int value; + if (secs_to_msecs) { + float secs = strtof(argv[metric_value_index], NULL); + value = static_cast(secs * 1000.0f); + } else { + value = atoi(argv[metric_value_index]); + } + // Send metrics if (send_to_autotest) { - MetricsLibrary::SendToAutotest(argv[metric_name_index], - argv[metric_value_index]); + MetricsLibrary::SendToAutotest(name, value); } if (send_to_chrome) { - MetricsLibrary::SendToChrome(argv[metric_name_index], - argv[metric_value_index]); + MetricsLibrary::SendToChrome(name, value); } return 0; } diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index a924b8ab4..9bb9c205f 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -113,12 +113,10 @@ void MetricsDaemon::LogNetworkStateChange(const char* newstate) { if (diff.tv_sec >= INT_MAX / 1000) { diff_ms = INT_MAX; } - char buffer[100]; - snprintf(buffer, sizeof(buffer), "%d", diff_ms); if (testing_) { - TestPublishMetric(network_states_[old_id].stat_name, buffer); + TestPublishMetric(network_states_[old_id].stat_name, diff_ms); } else { - ChromePublishMetric(network_states_[old_id].stat_name, buffer); + ChromePublishMetric(network_states_[old_id].stat_name, diff_ms); } } network_state_id_ = new_id; @@ -135,10 +133,10 @@ MetricsDaemon::GetNetworkStateId(const char* state_name) { return static_cast(-1); } -void MetricsDaemon::ChromePublishMetric(const char* name, const char* value) { +void MetricsDaemon::ChromePublishMetric(const char* name, int value) { MetricsLibrary::SendToChrome(name, value); } -void MetricsDaemon::TestPublishMetric(const char* name, const char* value) { +void MetricsDaemon::TestPublishMetric(const char* name, int value) { LOG(INFO) << "received metric: " << name << " " << value; } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 2ac1ea0f8..dc097b5e1 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -64,10 +64,10 @@ class MetricsDaemon { NetworkStateId GetNetworkStateId(const char* state_name); // Sends a stat to Chrome for transport to UMA. - void ChromePublishMetric(const char* name, const char* value); + void ChromePublishMetric(const char* name, int value); // Prints a stat for testing. - void TestPublishMetric(const char* name, const char* value); + void TestPublishMetric(const char* name, int value); #if 0 // Fetches a name-value hash table from DBus. diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 99ad61640..f482145a3 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -13,21 +13,23 @@ #include #include -#include -#include + +#include +#include +#include #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) static const char kAutotestPath[] = "/tmp/.chromeos-metrics-autotest"; static const char kChromePath[] = "/tmp/.chromeos-metrics"; -static const int kBufferSize = 4096; +static const int32_t kBufferSize = 1024; using namespace std; // TODO(sosa@chromium.org) - use Chromium logger instead of stderr -void MetricsLibrary::PrintError(const char *message, const char *file, - int code) { +static void PrintError(const char *message, const char *file, + int code) { const char *kProgramName = "metrics_library"; if (code == 0) { fprintf(stderr, "%s: %s\n", kProgramName, message); @@ -40,61 +42,105 @@ void MetricsLibrary::PrintError(const char *message, const char *file, } } -void MetricsLibrary::SendToAutotest(string name, string value) { - FILE *autotest_file = fopen(kAutotestPath, "a+"); - if (autotest_file == NULL) { - PrintError("fopen", kAutotestPath, errno); - return; - } - - fprintf(autotest_file, "%s=%s\n", name.c_str(), value.c_str()); - fclose(autotest_file); -} - -void MetricsLibrary::SendToChrome(string name, string value) { +// Sends message of size length to Chrome and returns true on success. +static bool SendMessageToChrome(int32_t length, const char *message) { int chrome_fd = open(kChromePath, O_WRONLY | O_APPEND | O_CREAT, READ_WRITE_ALL_FILE_FLAGS); - // If we failed to open it, return + // If we failed to open it, return. if (chrome_fd < 0) { PrintError("open", kChromePath, errno); - return; + return false; } - // Need to chmod because open flags are anded with umask. - if (fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS) < 0) { - PrintError("fchmod", kChromePath, errno); - close(chrome_fd); - return; - } + // Need to chmod because open flags are anded with umask. Ignore the + // exit code -- a chronos process may fail chmoding because the file + // has been created by a root process but that should be OK. + fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS); - // Grab an exclusive lock to protect Chrome from truncating underneath us + // Grab an exclusive lock to protect Chrome from truncating + // underneath us. Keep the file locked as briefly as possible. if (flock(chrome_fd, LOCK_EX) < 0) { PrintError("flock", kChromePath, errno); close(chrome_fd); - return; + return false; } - // Message format is: LENGTH (binary), NAME, VALUE - char message[kBufferSize]; - char *curr_ptr = message; - int32_t message_length = - name.length() + value.length() + 2 + sizeof(message_length); - if (message_length > static_cast(sizeof(message))) - PrintError("name/value too long", NULL, 0); - - // Make sure buffer is blanked - memset(message, 0, sizeof(message)); - memcpy(curr_ptr, &message_length, sizeof(message_length)); - curr_ptr += sizeof(message_length); - strncpy(curr_ptr, name.c_str(), name.length()); - curr_ptr += name.length() + 1; - strncpy(curr_ptr, value.c_str(), value.length()); - if (write(chrome_fd, message, message_length) != message_length) + bool success = true; + if (write(chrome_fd, message, length) != length) { PrintError("write", kChromePath, errno); + success = false; + } - // Release the file lock and close file - if (flock(chrome_fd, LOCK_UN) < 0) + // Release the file lock and close file. + if (flock(chrome_fd, LOCK_UN) < 0) { PrintError("unlock", kChromePath, errno); + success = false; + } close(chrome_fd); + return success; +} + +// Formats a name/value message for Chrome in buffer and returns the +// length of the message or a negative value on error. +// +// Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 | +// +// The arbitrary format argument covers the non-LENGTH portion of the +// message. The caller is responsible to store the \0 character +// between NAME and VALUE (e.g. "%s%c%d", name, '\0', value). +static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer, + const char *format, ...) { + int32_t message_length; + size_t len_size = sizeof(message_length); + + // Format the non-LENGTH contents in the buffer by leaving space for + // LENGTH at the start of the buffer. + va_list args; + va_start(args, format); + message_length = vsnprintf(&buffer[len_size], buffer_size - len_size, + format, args); + va_end(args); + + if (message_length < 0) { + PrintError("chrome message format error", NULL, 0); + return -1; + } + + // +1 to account for the trailing \0. + message_length += len_size + 1; + if (message_length > buffer_size) { + PrintError("chrome message too long", NULL, 0); + return -1; + } + + // Prepend LENGTH to the message. + memcpy(buffer, &message_length, len_size); + return message_length; +} + +bool MetricsLibrary::SendToAutotest(string name, int value) { + FILE *autotest_file = fopen(kAutotestPath, "a+"); + if (autotest_file == NULL) { + PrintError("fopen", kAutotestPath, errno); + return false; + } + + fprintf(autotest_file, "%s=%d\n", name.c_str(), value); + fclose(autotest_file); + return true; +} + +bool MetricsLibrary::SendToChrome(string name, int value) { + // Format the message. + char message[kBufferSize]; + int32_t message_length = + FormatChromeMessage(kBufferSize, message, + "%s%c%d", name.c_str(), '\0', value); + + if (message_length < 0) + return false; + + // Send the message. + return SendMessageToChrome(message_length, message); } diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index f1268c971..ebc972cae 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -12,7 +12,6 @@ #ifndef METRICS_LIBRARY_H_ #define METRICS_LIBRARY_H_ -#include #include // TODO(sosa@chromium.org): Add testing for send methods @@ -20,14 +19,10 @@ // Library used to send metrics both Autotest and Chrome class MetricsLibrary { public: - // Sends histogram data to Chrome. - static void SendToChrome(std::string name, std::string value); - // Sends to Autotest. - static void SendToAutotest(std::string name, std::string value); - - private: - // Prints message to stderr - static void PrintError(const char *message, const char *file, int code); + // Sends histogram data to Chrome and returns true on success. + static bool SendToChrome(std::string name, int value); + // Sends to Autotest and returns true on success. + static bool SendToAutotest(std::string name, int value); }; #endif /* METRICS_LIBRARY_H_ */ From c2526a1265ef8b4b61a76d7a49c8187a8e6656bf Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 21 Apr 2010 14:24:04 -0700 Subject: [PATCH 003/200] Update the libmetrics API to match the new Chrome interface. Review URL: http://codereview.chromium.org/1642018 --- metrics/metrics_client.cc | 34 ++++++++++++++++------------------ metrics/metrics_daemon.cc | 26 ++++++++++++++------------ metrics/metrics_daemon.h | 10 +++++----- metrics/metrics_library.cc | 22 ++++++++++++++-------- metrics/metrics_library.h | 20 ++++++++++++++++---- 5 files changed, 65 insertions(+), 47 deletions(-) diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index 17f933cbb..bb6769801 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -7,13 +7,11 @@ #include "metrics_library.h" -// Usage: metrics_client [-ab] metric_name metric_value int main(int argc, char** argv) { bool send_to_autotest = false; bool send_to_chrome = true; bool secs_to_msecs = false; - int metric_name_index = 1; - int metric_value_index = 2; + int name_index = 1; bool print_usage = false; if (argc >= 3) { @@ -37,43 +35,43 @@ int main(int argc, char** argv) { break; } } - metric_name_index = optind; - metric_value_index = optind + 1; + name_index = optind; } else { print_usage = true; } - // Metrics value should be the last argument passed - if ((metric_value_index + 1) != argc) { + if ((name_index + 5) != argc) { print_usage = true; } if (print_usage) { fprintf(stderr, - "Usage: metrics_client [-abt] name value\n" + "Usage: metrics_client [-abt] name sample min max nbuckets\n" "\n" - " default: send metric with integer value to chrome only\n" - " -a: send metric to autotest only\n" + " default: send metric with integer values to Chrome only\n" + " -a: send metric to autotest only (min/max/nbuckets ignored)\n" " -b: send metric to both chrome and autotest\n" - " -t: convert value from float seconds to int milliseconds\n"); + " -t: convert sample from double seconds to int milliseconds\n"); return 1; } - const char* name = argv[metric_name_index]; - int value; + const char* name = argv[name_index]; + int sample; if (secs_to_msecs) { - float secs = strtof(argv[metric_value_index], NULL); - value = static_cast(secs * 1000.0f); + sample = static_cast(atof(argv[name_index + 1]) * 1000.0); } else { - value = atoi(argv[metric_value_index]); + sample = atoi(argv[name_index + 1]); } + int min = atoi(argv[name_index + 2]); + int max = atoi(argv[name_index + 3]); + int nbuckets = atoi(argv[name_index + 4]); // Send metrics if (send_to_autotest) { - MetricsLibrary::SendToAutotest(name, value); + MetricsLibrary::SendToAutotest(name, sample); } if (send_to_chrome) { - MetricsLibrary::SendToChrome(name, value); + MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); } return 0; } diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 9bb9c205f..940085167 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -17,7 +17,7 @@ extern "C" { MetricsDaemon::NetworkState MetricsDaemon::network_states_[MetricsDaemon::kNumberNetworkStates] = { -#define STATE(name, capname) { #name, "Connman" # capname }, +#define STATE(name, capname) { #name, "Network.Connman" # capname }, #include "network_states.h" }; @@ -113,11 +113,11 @@ void MetricsDaemon::LogNetworkStateChange(const char* newstate) { if (diff.tv_sec >= INT_MAX / 1000) { diff_ms = INT_MAX; } - if (testing_) { - TestPublishMetric(network_states_[old_id].stat_name, diff_ms); - } else { - ChromePublishMetric(network_states_[old_id].stat_name, diff_ms); - } + PublishMetric(network_states_[old_id].stat_name, + diff_ms, + 1, + 8 * 60 * 60 * 1000, // 8 hours in milliseconds + 100); } network_state_id_ = new_id; network_state_start_ = now; @@ -133,10 +133,12 @@ MetricsDaemon::GetNetworkStateId(const char* state_name) { return static_cast(-1); } -void MetricsDaemon::ChromePublishMetric(const char* name, int value) { - MetricsLibrary::SendToChrome(name, value); -} - -void MetricsDaemon::TestPublishMetric(const char* name, int value) { - LOG(INFO) << "received metric: " << name << " " << value; +void MetricsDaemon::PublishMetric(const char* name, int sample, + int min, int max, int nbuckets) { + if (testing_) { + LOG(INFO) << "received metric: " << name << " " << sample << + " " << min << " " << max << " " << nbuckets; + } else { + MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); + } } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index dc097b5e1..b18e3667e 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -63,11 +63,11 @@ class MetricsDaemon { // Given a string with the name of a state, returns the id for the state. NetworkStateId GetNetworkStateId(const char* state_name); - // Sends a stat to Chrome for transport to UMA. - void ChromePublishMetric(const char* name, int value); - - // Prints a stat for testing. - void TestPublishMetric(const char* name, int value); + // Sends a stat to Chrome for transport to UMA (or prints it for + // testing). See MetricsLibrary::SendToChrome in metrics_library.h + // for a description of the arguments. + void PublishMetric(const char* name, int sample, + int min, int max, int nbuckets); #if 0 // Fetches a name-value hash table from DBus. diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index f482145a3..681cf96ab 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -21,8 +21,10 @@ #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) -static const char kAutotestPath[] = "/tmp/.chromeos-metrics-autotest"; -static const char kChromePath[] = "/tmp/.chromeos-metrics"; +static const char kAutotestPath[] = + "/var/log/metrics/autotest-events"; +static const char kChromePath[] = + "/var/log/metrics/uma-events"; static const int32_t kBufferSize = 1024; using namespace std; @@ -42,7 +44,7 @@ static void PrintError(const char *message, const char *file, } } -// Sends message of size length to Chrome and returns true on success. +// Sends message of size |length| to Chrome and returns true on success. static bool SendMessageToChrome(int32_t length, const char *message) { int chrome_fd = open(kChromePath, O_WRONLY | O_APPEND | O_CREAT, @@ -81,12 +83,12 @@ static bool SendMessageToChrome(int32_t length, const char *message) { return success; } -// Formats a name/value message for Chrome in buffer and returns the +// Formats a name/value message for Chrome in |buffer| and returns the // length of the message or a negative value on error. // // Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 | // -// The arbitrary format argument covers the non-LENGTH portion of the +// The arbitrary |format| argument covers the non-LENGTH portion of the // message. The caller is responsible to store the \0 character // between NAME and VALUE (e.g. "%s%c%d", name, '\0', value). static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer, @@ -119,7 +121,8 @@ static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer, return message_length; } -bool MetricsLibrary::SendToAutotest(string name, int value) { +// static +bool MetricsLibrary::SendToAutotest(const string& name, int value) { FILE *autotest_file = fopen(kAutotestPath, "a+"); if (autotest_file == NULL) { PrintError("fopen", kAutotestPath, errno); @@ -131,12 +134,15 @@ bool MetricsLibrary::SendToAutotest(string name, int value) { return true; } -bool MetricsLibrary::SendToChrome(string name, int value) { +// static +bool MetricsLibrary::SendToChrome(const string& name, int sample, + int min, int max, int nbuckets) { // Format the message. char message[kBufferSize]; int32_t message_length = FormatChromeMessage(kBufferSize, message, - "%s%c%d", name.c_str(), '\0', value); + "histogram%c%s %d %d %d %d", '\0', + name.c_str(), sample, min, max, nbuckets); if (message_length < 0) return false; diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index ebc972cae..5977e724c 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -16,13 +16,25 @@ // TODO(sosa@chromium.org): Add testing for send methods -// Library used to send metrics both Autotest and Chrome +// Library used to send metrics both Autotest and Chrome. class MetricsLibrary { public: - // Sends histogram data to Chrome and returns true on success. - static bool SendToChrome(std::string name, int value); + // Sends histogram data to Chrome for transport to UMA and returns + // true on success. This method results in the equivalent of an + // asynchronous non-blocking RPC to UMA_HISTOGRAM_CUSTOM_COUNTS + // inside Chrome (see base/histogram.h). + // + // |sample| is the sample value to be recorded (|min| <= |sample| < |max|). + // |min| is the minimum value of the histogram samples (|min| > 0). + // |max| is the maximum value of the histogram samples. + // |nbuckets| is the number of histogram buckets. + // [0,min) is the implicit underflow bucket. + // [|max|,infinity) is the implicit overflow bucket. + static bool SendToChrome(const std::string& name, int sample, + int min, int max, int nbuckets); + // Sends to Autotest and returns true on success. - static bool SendToAutotest(std::string name, int value); + static bool SendToAutotest(const std::string& name, int value); }; #endif /* METRICS_LIBRARY_H_ */ From 5b7dce1f609ca2a56928f88bedb31142c4b2d896 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 21 Apr 2010 15:45:10 -0700 Subject: [PATCH 004/200] Add support for linear/enumeration histograms. Review URL: http://codereview.chromium.org/1747008 --- metrics/metrics_client.cc | 33 ++++++++++++++++++++++++--------- metrics/metrics_library.cc | 17 +++++++++++++++++ metrics/metrics_library.h | 11 +++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index bb6769801..ad137be99 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -10,6 +10,7 @@ int main(int argc, char** argv) { bool send_to_autotest = false; bool send_to_chrome = true; + bool send_enum = false; bool secs_to_msecs = false; int name_index = 1; bool print_usage = false; @@ -17,7 +18,7 @@ int main(int argc, char** argv) { if (argc >= 3) { // Parse arguments int flag; - while ((flag = getopt(argc, argv, "abt")) != -1) { + while ((flag = getopt(argc, argv, "abet")) != -1) { switch (flag) { case 'a': send_to_autotest = true; @@ -27,6 +28,9 @@ int main(int argc, char** argv) { send_to_chrome = true; send_to_autotest = true; break; + case 'e': + send_enum = true; + break; case 't': secs_to_msecs = true; break; @@ -40,17 +44,22 @@ int main(int argc, char** argv) { print_usage = true; } - if ((name_index + 5) != argc) { + int num_args = send_enum ? 3 : 5; + if ((name_index + num_args) != argc || + (send_enum && secs_to_msecs)) { print_usage = true; } if (print_usage) { fprintf(stderr, - "Usage: metrics_client [-abt] name sample min max nbuckets\n" + "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n" + " metrics_client [-ab] -e name sample max\n" "\n" " default: send metric with integer values to Chrome only\n" - " -a: send metric to autotest only (min/max/nbuckets ignored)\n" - " -b: send metric to both chrome and autotest\n" + " |min| > 0, |min| <= sample < |max|\n" + " -a: send metric (name/sample) to Autotest only\n" + " -b: send metric to both Chrome and Autotest\n" + " -e: send linear/enumeration histogram data\n" " -t: convert sample from double seconds to int milliseconds\n"); return 1; } @@ -62,16 +71,22 @@ int main(int argc, char** argv) { } else { sample = atoi(argv[name_index + 1]); } - int min = atoi(argv[name_index + 2]); - int max = atoi(argv[name_index + 3]); - int nbuckets = atoi(argv[name_index + 4]); // Send metrics if (send_to_autotest) { MetricsLibrary::SendToAutotest(name, sample); } + if (send_to_chrome) { - MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); + if (send_enum) { + int max = atoi(argv[name_index + 2]); + MetricsLibrary::SendEnumToChrome(name, sample, max); + } else { + int min = atoi(argv[name_index + 2]); + int max = atoi(argv[name_index + 3]); + int nbuckets = atoi(argv[name_index + 4]); + MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); + } } return 0; } diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 681cf96ab..95e78b227 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -150,3 +150,20 @@ bool MetricsLibrary::SendToChrome(const string& name, int sample, // Send the message. return SendMessageToChrome(message_length, message); } + +//static +bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample, + int max) { + // Format the message. + char message[kBufferSize]; + int32_t message_length = + FormatChromeMessage(kBufferSize, message, + "linearhistogram%c%s %d %d", '\0', + name.c_str(), sample, max); + + if (message_length < 0) + return false; + + // Send the message. + return SendMessageToChrome(message_length, message); +} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 5977e724c..738396050 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -33,6 +33,17 @@ class MetricsLibrary { static bool SendToChrome(const std::string& name, int sample, int min, int max, int nbuckets); + // Sends linear histogram data to Chrome for transport to UMA and + // returns true on success. This method results in the equivalent of + // an asynchronous non-blocking RPC to UMA_HISTOGRAM_ENUMERATION + // inside Chrome (see base/histogram.h). + // + // |sample| is the sample value to be recorded (1 <= |sample| < |max|). + // |max| is the maximum value of the histogram samples. + // 0 is the implicit underflow bucket. + // [|max|,infinity) is the implicit overflow bucket. + static bool SendEnumToChrome(const std::string& name, int sample, int max); + // Sends to Autotest and returns true on success. static bool SendToAutotest(const std::string& name, int value); }; From 703ec97bdffff1a06162921aa8b7ecd0cea22afd Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Tue, 27 Apr 2010 11:02:18 -0700 Subject: [PATCH 005/200] Log time between network drops -- from online to offline. Rewrite most of metrics_daemon. Convert to low-level D-Bus API -- this simplifies the code a little and also allows us to catch the power state signal. I still suspect we may be abusing D-Bus a little but it seems to work. snanda@ -- please review the power state code specifically. BUG=none TEST=tested on target platform and arm-generic builds Review URL: http://codereview.chromium.org/1799001 --- metrics/Makefile | 25 +-- metrics/marshal_void__string_boxed.list | 1 - metrics/metrics_daemon.cc | 233 ++++++++++++++---------- metrics/metrics_daemon.h | 85 +++++---- metrics/network_states.h | 8 +- metrics/power_states.h | 28 +++ 6 files changed, 216 insertions(+), 164 deletions(-) delete mode 100644 metrics/marshal_void__string_boxed.list create mode 100644 metrics/power_states.h diff --git a/metrics/Makefile b/metrics/Makefile index 0b047361b..54b9f53bb 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -8,7 +8,7 @@ CCONFIG = $(shell $(PKG_CONFIG) --cflags dbus-1 glib-2.0 dbus-glib-1) LDCONFIG = $(shell $(PKG_CONFIG) --libs dbus-1 glib-2.0 gthread-2.0 dbus-glib-1) -CFLAGS = -Wall -Werror -I/usr/include -fpic -O2 $(CCONFIG) +CFLAGS = -Wall -Werror -I/usr/include -fPIC -O2 $(CCONFIG) CXXFLAGS = $(CFLAGS) -fno-exceptions CLIENT = metrics_client @@ -22,11 +22,9 @@ CLIENT_OBJS = \ LIB_OBJS = \ metrics_library.o DAEMON_OBJS = \ - marshal_void__string_boxed.o \ metrics_daemon.o \ metrics_daemon_main.o TESTDAEMON_OBJS = \ - marshal_void__string_boxed.o \ metrics_daemon.o \ metrics_daemon_unittest.o @@ -56,26 +54,16 @@ $(SHAREDLIB): $(LIB_OBJS) %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ -%.c: %.list - glib-genmarshal --body --prefix=marshal $< > $@ - -%.h: %.list - glib-genmarshal --header --prefix=marshal $< > $@ - # dependencies in addition to those defined by the rules metrics_daemon.o: \ - marshal_void__string_boxed.h \ metrics_daemon.h \ - network_states.h + network_states.h \ + power_states.h metrics_daemon_unittest.o: \ - marshal_void__string_boxed.h \ metrics_daemon.h \ - network_states.h -marshal_void__string_boxed.o: \ - marshal_void__string_boxed.h - -.PRECIOUS: marshal_void__string_boxed.c # keep around for debugging + network_states.h \ + power_states.h install: install $(CLIENT) $(DESTDIR)/usr/bin @@ -87,5 +75,4 @@ install: install omaha_tracker.sh $(DESTDIR)/usr/sbin clean: - rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) $(TESTDAEMON) - rm -f *.o marshal_void__string_boxed.[ch] + rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) $(TESTDAEMON) *.o diff --git a/metrics/marshal_void__string_boxed.list b/metrics/marshal_void__string_boxed.list deleted file mode 100644 index e72aa4bc3..000000000 --- a/metrics/marshal_void__string_boxed.list +++ /dev/null @@ -1 +0,0 @@ -VOID:STRING,BOXED diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 940085167..32581c1a1 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -5,22 +5,43 @@ #include "metrics_daemon.h" #include "metrics_library.h" -#include - -extern "C" { -#include "marshal_void__string_boxed.h" -} +#include #include -#define SAFE_MESSAGE(e) ((e && e->message) ? e->message : "unknown error") +#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") +#define DBUS_IFACE_CONNMAN_MANAGER "org.moblin.connman.Manager" +#define DBUS_IFACE_POWER_MANAGER "org.chromium.Power.Manager" -MetricsDaemon::NetworkState +// static +const char* +MetricsDaemon::dbus_matches_[] = { + "type='signal'," + "sender='org.moblin.connman'," + "interface='" DBUS_IFACE_CONNMAN_MANAGER "'," + "path='/'," + "member='StateChanged'", + + "type='signal'," + "interface='" DBUS_IFACE_POWER_MANAGER "'," + "path='/'," + "member='PowerStateChanged'", +}; + +// static +const char * MetricsDaemon::network_states_[MetricsDaemon::kNumberNetworkStates] = { -#define STATE(name, capname) { #name, "Network.Connman" # capname }, +#define STATE(name, capname) #name, #include "network_states.h" }; +// static +const char * +MetricsDaemon::power_states_[MetricsDaemon::kNumberPowerStates] = { +#define STATE(name, capname) #name, +#include "power_states.h" +}; + void MetricsDaemon::Run(bool run_as_daemon, bool testing) { Init(testing); if (!run_as_daemon || daemon(0, 0) == 0) { @@ -30,115 +51,139 @@ void MetricsDaemon::Run(bool run_as_daemon, bool testing) { void MetricsDaemon::Init(bool testing) { testing_ = testing; - network_state_id_ = kUnknownNetworkStateId; + network_state_ = kUnknownNetworkState; + network_state_changed_ = 0; + power_state_ = kUnknownPowerState; - ::g_thread_init(NULL); - ::g_type_init(); - ::dbus_g_thread_init(); + g_thread_init(NULL); + g_type_init(); + dbus_g_thread_init(); - ::GError* error = NULL; - ::DBusGConnection* dbc = ::dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); - // Note that LOG(FATAL) terminates the process; otherwise we'd have to worry - // about leaking |error|. - LOG_IF(FATAL, dbc == NULL) << - "cannot connect to dbus: " << SAFE_MESSAGE(error); + DBusError error; + dbus_error_init(&error); - ::DBusGProxy* net_proxy = ::dbus_g_proxy_new_for_name( - dbc, "org.moblin.connman", "/", "org.moblin.connman.Metrics"); - LOG_IF(FATAL, net_proxy == NULL) << "no dbus proxy for network"; + DBusConnection *connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + LOG_IF(FATAL, dbus_error_is_set(&error)) << + "No D-Bus connection: " << SAFE_MESSAGE(error); -#if 0 - // Unclear how soon one can call dbus_g_type_get_map(). Doing it before the - // call to dbus_g_bus_get() results in a (non-fatal) assertion failure. - // GetProperties returns a hash table. - hashtable_gtype = ::dbus_g_type_get_map("GHashTable", G_TYPE_STRING, - G_TYPE_VALUE); -#endif + dbus_connection_setup_with_g_main(connection, NULL); - dbus_g_object_register_marshaller(marshal_VOID__STRING_BOXED, - G_TYPE_NONE, - G_TYPE_STRING, - G_TYPE_VALUE, - G_TYPE_INVALID); - ::dbus_g_proxy_add_signal(net_proxy, "ConnectionStateChanged", - G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); - ::dbus_g_proxy_connect_signal(net_proxy, "ConnectionStateChanged", - G_CALLBACK(&StaticNetSignalHandler), - this, NULL); + // Registers D-Bus matches for the signals we would like to catch. + for (unsigned int m = 0; m < sizeof(dbus_matches_) / sizeof(char *); m++) { + const char* match = dbus_matches_[m]; + LOG(INFO) << "adding dbus match: " << match; + dbus_bus_add_match(connection, match, &error); + LOG_IF(FATAL, dbus_error_is_set(&error)) << + "unable to add a match: " << SAFE_MESSAGE(error); + } + + // Adds the D-Bus filter routine to be called back whenever one of + // the registered D-Bus matches is successful. The daemon is not + // activated for D-Bus messages that don't match. + CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL)); } void MetricsDaemon::Loop() { - ::GMainLoop* loop = ::g_main_loop_new(NULL, false); - ::g_main_loop_run(loop); + GMainLoop* loop = g_main_loop_new(NULL, false); + g_main_loop_run(loop); } -void MetricsDaemon::StaticNetSignalHandler(::DBusGProxy* proxy, - const char* property, - const ::GValue* value, - void *data) { - (static_cast(data))->NetSignalHandler(proxy, property, value); +// static +DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, + DBusMessage* message, + void* user_data) { + LOG(INFO) << "message filter"; + + int message_type = dbus_message_get_type(message); + if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) { + LOG(WARNING) << "unexpected message type " << message_type; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + // Signal messages always have interfaces. + const char* interface = dbus_message_get_interface(message); + CHECK(interface != NULL); + + MetricsDaemon* daemon = static_cast(user_data); + + DBusMessageIter iter; + dbus_message_iter_init(message, &iter); + if (strcmp(interface, DBUS_IFACE_CONNMAN_MANAGER) == 0) { + CHECK(strcmp(dbus_message_get_member(message), "StateChanged") == 0); + + char *state_name; + dbus_message_iter_get_basic(&iter, &state_name); + daemon->NetStateChanged(state_name); + } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) { + CHECK(strcmp(dbus_message_get_member(message), "PowerStateChanged") == 0); + + char *state_name; + dbus_message_iter_get_basic(&iter, &state_name); + daemon->PowerStateChanged(state_name); + } else { + LOG(WARNING) << "unexpected interface: " << interface; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + return DBUS_HANDLER_RESULT_HANDLED; } -void MetricsDaemon::NetSignalHandler(::DBusGProxy* proxy, - const char* property, - const ::GValue* value) { - if (strcmp("ConnectionState", property) != 0) { - return; +void MetricsDaemon::NetStateChanged(const char* state_name) { + LOG(INFO) << "network state: " << state_name; + + time_t now = time(NULL); + NetworkState state = LookupNetworkState(state_name); + + // Logs the time in seconds between the network going online to + // going offline in order to measure the mean time to network + // dropping. Going offline as part of suspend-to-RAM is not logged + // as network drop -- the assumption is that the message for + // suspend-to-RAM comes before the network offline message which + // seems to and should be the case. + if (state == kNetworkStateOffline && + network_state_ == kNetworkStateOnline && + power_state_ != kPowerStateMem) { + int online_time = static_cast(now - network_state_changed_); + PublishMetric("Network.TimeToDrop", online_time, + 1, 8 /* hours */ * 60 * 60, 50); } - const char* newstate = static_cast(g_value_get_string(value)); - LogNetworkStateChange(newstate); + network_state_ = state; + network_state_changed_ = now; } -void MetricsDaemon::LogNetworkStateChange(const char* newstate) { - NetworkStateId new_id = GetNetworkStateId(newstate); - if (new_id == kUnknownNetworkStateId) { - LOG(WARNING) << "unknown network connection state " << newstate; - return; - } - NetworkStateId old_id = network_state_id_; - if (new_id == old_id) { // valid new state and no change - return; - } - struct timeval now; - if (gettimeofday(&now, NULL) != 0) { - PLOG(WARNING) << "gettimeofday"; - } - if (old_id != kUnknownNetworkStateId) { - struct timeval diff; - timersub(&now, &network_state_start_, &diff); - int diff_ms = diff.tv_usec / 1000 + diff.tv_sec * 1000; - // Saturates rather than overflowing. We expect this to be statistically - // insignificant, since INT_MAX milliseconds is 24.8 days. - if (diff.tv_sec >= INT_MAX / 1000) { - diff_ms = INT_MAX; - } - PublishMetric(network_states_[old_id].stat_name, - diff_ms, - 1, - 8 * 60 * 60 * 1000, // 8 hours in milliseconds - 100); - } - network_state_id_ = new_id; - network_state_start_ = now; -} - -MetricsDaemon::NetworkStateId -MetricsDaemon::GetNetworkStateId(const char* state_name) { +MetricsDaemon::NetworkState +MetricsDaemon::LookupNetworkState(const char* state_name) { for (int i = 0; i < kNumberNetworkStates; i++) { - if (strcmp(state_name, network_states_[i].name) == 0) { - return static_cast(i); + if (strcmp(state_name, network_states_[i]) == 0) { + return static_cast(i); } } - return static_cast(-1); + LOG(WARNING) << "unknown network connection state: " << state_name; + return kUnknownNetworkState; +} + +void MetricsDaemon::PowerStateChanged(const char* state_name) { + LOG(INFO) << "power state: " << state_name; + power_state_ = LookupPowerState(state_name); +} + +MetricsDaemon::PowerState +MetricsDaemon::LookupPowerState(const char* state_name) { + for (int i = 0; i < kNumberPowerStates; i++) { + if (strcmp(state_name, power_states_[i]) == 0) { + return static_cast(i); + } + } + LOG(WARNING) << "unknown power state: " << state_name; + return kUnknownPowerState; } void MetricsDaemon::PublishMetric(const char* name, int sample, int min, int max, int nbuckets) { - if (testing_) { - LOG(INFO) << "received metric: " << name << " " << sample << - " " << min << " " << max << " " << nbuckets; - } else { + LOG(INFO) << "received metric: " << name << " " << sample << + " " << min << " " << max << " " << nbuckets; + if (!testing_) { MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); } } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index b18e3667e..23f0eb25f 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -5,40 +5,40 @@ #ifndef METRICS_DAEMON_H_ #define METRICS_DAEMON_H_ -#include -#include +#include #include class MetricsDaemon { public: MetricsDaemon() - : network_state_id_(kUnknownNetworkStateId) { - } + : testing_(false), + network_state_(kUnknownNetworkState), + network_state_changed_(0), + power_state_(kUnknownPowerState) {} ~MetricsDaemon() {} - // Does all the work. If |run_as_daemon| is true, daemonize by forking. If - // |testing| is true, log the stats instead of sending them to Chrome. + // Does all the work. If |run_as_daemon| is true, daemonizes by + // forking. If |testing| is true, logs the stats instead of sending + // them to Chrome. void Run(bool run_as_daemon, bool testing); private: - // Shared with Chrome for transport. - static const char* kMetricsFilePath; - static const int kMetricsMessageMaxLength = 4096; - - // The network states. See network_states.h. - typedef enum { - // Initial/unknown network state id. - kUnknownNetworkStateId = -1, + // The network states (see network_states.h). + enum NetworkState { + kUnknownNetworkState = -1, // Initial/unknown network state. #define STATE(name, capname) kNetworkState ## capname, #include "network_states.h" kNumberNetworkStates - } NetworkStateId; + }; - typedef struct { - const char* name; - const char* stat_name; - } NetworkState; + // The power states (see power_states.h). + enum PowerState { + kUnknownPowerState = -1, // Initial/unknown power state. +#define STATE(name, capname) kPowerState ## capname, +#include "power_states.h" + kNumberPowerStates + }; // Initializes. void Init(bool testing); @@ -46,22 +46,22 @@ class MetricsDaemon { // Creates the event loop and enters it. void Loop(); - // Static callback for network events on DBus. - static void StaticNetSignalHandler(::DBusGProxy* proxy, const char* property, - const ::GValue* value, void* data); + // D-Bus filter callback. + static DBusHandlerResult MessageFilter(DBusConnection* connection, + DBusMessage* message, + void* user_data); - // Callback for network events on DBus. - void NetSignalHandler(::DBusGProxy* proxy, const char* property, - const ::GValue* value); + // Processes network state change. + void NetStateChanged(const char* state_name); - // This is called at each network state change. The new state is identified - // by the string @newstate. As a side effect, this method ships to Chrome - // (or prints to stdout when testing) the name and duration of the state - // that has ended. - void LogNetworkStateChange(const char* newstate); + // Given the state name, returns the state id. + NetworkState LookupNetworkState(const char* state_name); - // Given a string with the name of a state, returns the id for the state. - NetworkStateId GetNetworkStateId(const char* state_name); + // Processes power state change. + void PowerStateChanged(const char* state_name); + + // Given the state name, returns the state id. + PowerState LookupPowerState(const char* state_name); // Sends a stat to Chrome for transport to UMA (or prints it for // testing). See MetricsLibrary::SendToChrome in metrics_library.h @@ -69,20 +69,19 @@ class MetricsDaemon { void PublishMetric(const char* name, int sample, int min, int max, int nbuckets); -#if 0 - // Fetches a name-value hash table from DBus. - bool GetProperties(::DBusGProxy* proxy, ::GHashTable** table); + // D-Bus message match strings. + static const char* dbus_matches_[]; - // The type descriptor for a glib hash table. - GType hashtable_gtype; -#endif + // Array of network states. + static const char* network_states_[kNumberNetworkStates]; - // Array of network states of interest. - static NetworkState network_states_[kNumberNetworkStates]; + // Array of power states. + static const char* power_states_[kNumberPowerStates]; - bool testing_; // just testing - NetworkStateId network_state_id_; // id of current state - struct timeval network_state_start_; // when current state was entered + bool testing_; // just testing + NetworkState network_state_; // current network state + time_t network_state_changed_; // timestamp last net state change + PowerState power_state_; // current power state }; #endif // METRICS_DAEMON_H_ diff --git a/metrics/network_states.h b/metrics/network_states.h index 69205ed64..b02f64a59 100644 --- a/metrics/network_states.h +++ b/metrics/network_states.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -22,13 +22,7 @@ #define STATE(name, capname) #endif -STATE(association, Association) -STATE(configuration, Configuration) -STATE(disconnect, Disconnect) -STATE(failure, Failure) -STATE(idle, Idle) STATE(offline, Offline) STATE(online, Online) -STATE(ready, Ready) #undef STATE diff --git a/metrics/power_states.h b/metrics/power_states.h new file mode 100644 index 000000000..413085ef6 --- /dev/null +++ b/metrics/power_states.h @@ -0,0 +1,28 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A table of power states, to be included when building tabular things. +// +// This file is used to construct two things: an enumerated type in +// metrics_daemon.h, and a table of structures with state names in +// metrics_daemon.cc. Including this file ensures that the two tables are +// always in sync (and saves typing). I don't know of other ways of achieving +// the same result in C/C++, but it doesn't mean there isn't one. + +// Before you include this file, define STATE to do something useful, or else +// if will be a no-op. STATE will be undefined on exit. Don't worry about +// collisions for the STATE macro (as long as it's a macro) because the +// compiler will flag them---in that case, just change the name. If someone is +// misguided enough to use STATE for something other than a macro, the error +// messages will be slightly more complicated. + + +#ifndef STATE +#define STATE(name, capname) +#endif + +STATE(on, On) +STATE(mem, Mem) + +#undef STATE From 41e0623cab97c7a008f862c673459dd4169bd6b9 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Mon, 3 May 2010 16:45:37 -0700 Subject: [PATCH 006/200] ... will look into some unit and integration testing for all metrics code next in a separate CL. Review URL: http://codereview.chromium.org/1863002 --- metrics/Makefile | 6 +- metrics/metrics_daemon.cc | 300 +++++++++++++++++++++++++++++++---- metrics/metrics_daemon.h | 145 +++++++++++++++-- metrics/power_states.h | 13 +- metrics/screensaver_states.h | 17 ++ metrics/session_states.h | 17 ++ 6 files changed, 433 insertions(+), 65 deletions(-) create mode 100644 metrics/screensaver_states.h create mode 100644 metrics/session_states.h diff --git a/metrics/Makefile b/metrics/Makefile index 54b9f53bb..784ff91e8 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -8,8 +8,7 @@ CCONFIG = $(shell $(PKG_CONFIG) --cflags dbus-1 glib-2.0 dbus-glib-1) LDCONFIG = $(shell $(PKG_CONFIG) --libs dbus-1 glib-2.0 gthread-2.0 dbus-glib-1) -CFLAGS = -Wall -Werror -I/usr/include -fPIC -O2 $(CCONFIG) -CXXFLAGS = $(CFLAGS) -fno-exceptions +CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions $(CCONFIG) CLIENT = metrics_client DAEMON = metrics_daemon @@ -51,9 +50,6 @@ $(SHAREDLIB): $(LIB_OBJS) %.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ -%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ - # dependencies in addition to those defined by the rules metrics_daemon.o: \ diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 32581c1a1..ef973a236 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -6,16 +6,39 @@ #include "metrics_library.h" #include +#include +#include #include #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") #define DBUS_IFACE_CONNMAN_MANAGER "org.moblin.connman.Manager" #define DBUS_IFACE_POWER_MANAGER "org.chromium.Power.Manager" +#define DBUS_IFACE_SCREENSAVER_MANAGER "org.chromium.ScreenSaver.Manager" +#define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface" + +// File to aggregate daily usage before sending to UMA. +// TODO(petkov): This file should probably live in a user-specific stateful +// location, e.g., /home/chronos/user. +static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage"; + +static const int kSecondsPerMinute = 60; +static const int kMinutesPerHour = 60; +static const int kHoursPerDay = 24; +static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour; +static const int kSecondsPerDay = kMinutesPerDay * kSecondsPerMinute; + +// The daily use monitor is scheduled to a 1-minute interval after +// initial user activity and then it's exponentially backed off to +// 10-minute intervals. Although not required, the back off is +// implemented because the histogram buckets are spaced exponentially +// anyway and to avoid too frequent metrics daemon process wake-ups +// and file I/O. +static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute; +static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute; // static -const char* -MetricsDaemon::dbus_matches_[] = { +const char* MetricsDaemon::kDBusMatches_[] = { "type='signal'," "sender='org.moblin.connman'," "interface='" DBUS_IFACE_CONNMAN_MANAGER "'," @@ -26,22 +49,43 @@ MetricsDaemon::dbus_matches_[] = { "interface='" DBUS_IFACE_POWER_MANAGER "'," "path='/'," "member='PowerStateChanged'", + + "type='signal'," + "interface='" DBUS_IFACE_SCREENSAVER_MANAGER "'," + "path='/'," + "member='LockStateChanged'", + + "type='signal'," + "sender='org.chromium.SessionManager'," + "interface='" DBUS_IFACE_SESSION_MANAGER "'," + "path='/org/chromium/SessionManager'," + "member='SessionStateChanged'", }; // static -const char * -MetricsDaemon::network_states_[MetricsDaemon::kNumberNetworkStates] = { +const char* MetricsDaemon::kNetworkStates_[] = { #define STATE(name, capname) #name, #include "network_states.h" }; // static -const char * -MetricsDaemon::power_states_[MetricsDaemon::kNumberPowerStates] = { +const char* MetricsDaemon::kPowerStates_[] = { #define STATE(name, capname) #name, #include "power_states.h" }; +// static +const char* MetricsDaemon::kScreenSaverStates_[] = { +#define STATE(name, capname) #name, +#include "screensaver_states.h" +}; + +// static +const char* MetricsDaemon::kSessionStates_[] = { +#define STATE(name, capname) #name, +#include "session_states.h" +}; + void MetricsDaemon::Run(bool run_as_daemon, bool testing) { Init(testing); if (!run_as_daemon || daemon(0, 0) == 0) { @@ -51,9 +95,6 @@ void MetricsDaemon::Run(bool run_as_daemon, bool testing) { void MetricsDaemon::Init(bool testing) { testing_ = testing; - network_state_ = kUnknownNetworkState; - network_state_changed_ = 0; - power_state_ = kUnknownPowerState; g_thread_init(NULL); g_type_init(); @@ -69,9 +110,9 @@ void MetricsDaemon::Init(bool testing) { dbus_connection_setup_with_g_main(connection, NULL); // Registers D-Bus matches for the signals we would like to catch. - for (unsigned int m = 0; m < sizeof(dbus_matches_) / sizeof(char *); m++) { - const char* match = dbus_matches_[m]; - LOG(INFO) << "adding dbus match: " << match; + for (unsigned int m = 0; m < sizeof(kDBusMatches_) / sizeof(char *); m++) { + const char* match = kDBusMatches_[m]; + DLOG(INFO) << "adding dbus match: " << match; dbus_bus_add_match(connection, match, &error); LOG_IF(FATAL, dbus_error_is_set(&error)) << "unable to add a match: " << SAFE_MESSAGE(error); @@ -92,11 +133,12 @@ void MetricsDaemon::Loop() { DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, DBusMessage* message, void* user_data) { - LOG(INFO) << "message filter"; + time_t now = time(NULL); + DLOG(INFO) << "message intercepted @ " << now; int message_type = dbus_message_get_type(message); if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) { - LOG(WARNING) << "unexpected message type " << message_type; + DLOG(WARNING) << "unexpected message type " << message_type; return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -109,29 +151,44 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, DBusMessageIter iter; dbus_message_iter_init(message, &iter); if (strcmp(interface, DBUS_IFACE_CONNMAN_MANAGER) == 0) { - CHECK(strcmp(dbus_message_get_member(message), "StateChanged") == 0); + CHECK(strcmp(dbus_message_get_member(message), + "StateChanged") == 0); char *state_name; dbus_message_iter_get_basic(&iter, &state_name); - daemon->NetStateChanged(state_name); + daemon->NetStateChanged(state_name, now); } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) { - CHECK(strcmp(dbus_message_get_member(message), "PowerStateChanged") == 0); + CHECK(strcmp(dbus_message_get_member(message), + "PowerStateChanged") == 0); char *state_name; dbus_message_iter_get_basic(&iter, &state_name); - daemon->PowerStateChanged(state_name); + daemon->PowerStateChanged(state_name, now); + } else if (strcmp(interface, DBUS_IFACE_SCREENSAVER_MANAGER) == 0) { + CHECK(strcmp(dbus_message_get_member(message), + "LockStateChanged") == 0); + + char *state_name; + dbus_message_iter_get_basic(&iter, &state_name); + daemon->ScreenSaverStateChanged(state_name, now); + } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) { + CHECK(strcmp(dbus_message_get_member(message), + "SessionStateChanged") == 0); + + char *state_name; + dbus_message_iter_get_basic(&iter, &state_name); + daemon->SessionStateChanged(state_name, now); } else { - LOG(WARNING) << "unexpected interface: " << interface; + DLOG(WARNING) << "unexpected interface: " << interface; return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } return DBUS_HANDLER_RESULT_HANDLED; } -void MetricsDaemon::NetStateChanged(const char* state_name) { - LOG(INFO) << "network state: " << state_name; +void MetricsDaemon::NetStateChanged(const char* state_name, time_t now) { + DLOG(INFO) << "network state: " << state_name; - time_t now = time(NULL); NetworkState state = LookupNetworkState(state_name); // Logs the time in seconds between the network going online to @@ -143,46 +200,225 @@ void MetricsDaemon::NetStateChanged(const char* state_name) { if (state == kNetworkStateOffline && network_state_ == kNetworkStateOnline && power_state_ != kPowerStateMem) { - int online_time = static_cast(now - network_state_changed_); + int online_time = static_cast(now - network_state_last_); PublishMetric("Network.TimeToDrop", online_time, 1, 8 /* hours */ * 60 * 60, 50); } network_state_ = state; - network_state_changed_ = now; + network_state_last_ = now; } MetricsDaemon::NetworkState MetricsDaemon::LookupNetworkState(const char* state_name) { for (int i = 0; i < kNumberNetworkStates; i++) { - if (strcmp(state_name, network_states_[i]) == 0) { + if (strcmp(state_name, kNetworkStates_[i]) == 0) { return static_cast(i); } } - LOG(WARNING) << "unknown network connection state: " << state_name; + DLOG(WARNING) << "unknown network connection state: " << state_name; return kUnknownNetworkState; } -void MetricsDaemon::PowerStateChanged(const char* state_name) { - LOG(INFO) << "power state: " << state_name; +void MetricsDaemon::PowerStateChanged(const char* state_name, time_t now) { + DLOG(INFO) << "power state: " << state_name; power_state_ = LookupPowerState(state_name); + + if (power_state_ != kPowerStateOn) + SetUserActiveState(false, now); } MetricsDaemon::PowerState MetricsDaemon::LookupPowerState(const char* state_name) { for (int i = 0; i < kNumberPowerStates; i++) { - if (strcmp(state_name, power_states_[i]) == 0) { + if (strcmp(state_name, kPowerStates_[i]) == 0) { return static_cast(i); } } - LOG(WARNING) << "unknown power state: " << state_name; + DLOG(WARNING) << "unknown power state: " << state_name; return kUnknownPowerState; } +void MetricsDaemon::ScreenSaverStateChanged(const char* state_name, + time_t now) { + DLOG(INFO) << "screen-saver state: " << state_name; + screensaver_state_ = LookupScreenSaverState(state_name); + SetUserActiveState(screensaver_state_ == kScreenSaverStateUnlocked, now); +} + +MetricsDaemon::ScreenSaverState +MetricsDaemon::LookupScreenSaverState(const char* state_name) { + for (int i = 0; i < kNumberScreenSaverStates; i++) { + if (strcmp(state_name, kScreenSaverStates_[i]) == 0) { + return static_cast(i); + } + } + DLOG(WARNING) << "unknown screen-saver state: " << state_name; + return kUnknownScreenSaverState; +} + +void MetricsDaemon::SessionStateChanged(const char* state_name, + time_t now) { + DLOG(INFO) << "user session state: " << state_name; + session_state_ = LookupSessionState(state_name); + SetUserActiveState(session_state_ == kSessionStateStarted, now); +} + +MetricsDaemon::SessionState +MetricsDaemon::LookupSessionState(const char* state_name) { + for (int i = 0; i < kNumberSessionStates; i++) { + if (strcmp(state_name, kSessionStates_[i]) == 0) { + return static_cast(i); + } + } + DLOG(WARNING) << "unknown user session state: " << state_name; + return kUnknownSessionState; +} + +void MetricsDaemon::SetUserActiveState(bool active, time_t now) { + DLOG(INFO) << "user: " << (active ? "active" : "inactive"); + + // Calculates the seconds of active use since the last update and + // the day since Epoch, and logs the usage data. + int seconds = user_active_ ? (now - user_active_last_) : 0; + int day = now / kSecondsPerDay; + LogDailyUseRecord(day, seconds); + + // Schedules a use monitor on inactive->active transitions and + // unschedules it on active->inactive transitions. + if (!user_active_ && active) + ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false); + else if (user_active_ && !active) + UnscheduleUseMonitor(); + + // Remembers the current active state and the time of the last + // activity update. + user_active_ = active; + user_active_last_ = now; +} + +void MetricsDaemon::LogDailyUseRecord(int day, int seconds) { + // If there's no new active use today and the last record in the + // usage aggregation file is today, there's nothing to do. + if (seconds == 0 && day == daily_use_day_last_) + return; + + DLOG(INFO) << "day: " << day << " usage: " << seconds << " seconds"; + int fd = HANDLE_EINTR(open(kDailyUseRecordFile, + O_RDWR | O_CREAT, + S_IRUSR | S_IWUSR)); + if (fd < 0) { + DLOG(WARNING) << "Unable to open the daily use file."; + return; + } + + bool same_day = false; + UseRecord record; + if (HANDLE_EINTR(read(fd, &record, sizeof(record))) == sizeof(record)) { + if (record.day_ == day) { + // If there's an existing record for today, aggregates the usage + // time. + same_day = true; + record.seconds_ += seconds; + } else { + // If there's an existing record for a day in the past, rounds + // the usage to the nearest minute and sends it to UMA. + int minutes = + (record.seconds_ + kSecondsPerMinute / 2) / kSecondsPerMinute; + PublishMetric("Logging.DailyUseTime", + minutes, 1, kMinutesPerDay, 50); + + // Truncates the usage file to ensure that no duplicate usage is + // sent to UMA. + LOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0); + } + } + + // Updates the use record in the daily usage file if there's new + // usage today. + if (seconds > 0) { + if (!same_day) { + record.day_ = day; + record.seconds_ = seconds; + } + // else an already existing record for the same day will be + // overwritten with updated usage below. + + LOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0); + LOG_IF(WARNING, + HANDLE_EINTR(write(fd, &record, sizeof(record))) != sizeof(record)); + } + + HANDLE_EINTR(close(fd)); + + // Remembers the day of the use record in the usage aggregation file + // to reduce file I/O. This is not really useful now but potentially + // allows frequent LogDailyUseRecord calls with no unnecessary I/O + // overhead. + daily_use_day_last_ = day; +} + +// static +gboolean MetricsDaemon::UseMonitorStatic(gpointer data) { + return static_cast(data)->UseMonitor() ? TRUE : FALSE; +} + +bool MetricsDaemon::UseMonitor() { + SetUserActiveState(user_active_, time(NULL)); + + // If a new monitor source/instance is scheduled, returns false to + // tell GLib to destroy this monitor source/instance. Returns true + // otherwise to keep calling back this monitor. + return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true); +} + +bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff) +{ + // Caps the interval -- the bigger the interval, the more active use + // time will be potentially dropped on system shutdown. + if (interval > kUseMonitorIntervalMax) + interval = kUseMonitorIntervalMax; + + if (backoff) { + // Back-off mode is used by the use monitor to reschedule itself + // with exponential back-off in time. This mode doesn't create a + // new timeout source if the new interval is the same as the old + // one. Also, if a new timeout source is created, the old one is + // not destroyed explicitly here -- it will be destroyed by GLib + // when the monitor returns FALSE (see UseMonitor and + // UseMonitorStatic). + if (interval == usemon_interval_) + return false; + } else { + UnscheduleUseMonitor(); + } + + // Schedules a new use monitor for |interval| seconds from now. + DLOG(INFO) << "scheduling use monitor in " << interval << " seconds"; + usemon_source_ = g_timeout_source_new_seconds(interval); + g_source_set_callback(usemon_source_, UseMonitorStatic, this, + NULL); // No destroy notification. + g_source_attach(usemon_source_, + NULL); // Default context. + usemon_interval_ = interval; + return true; +} + +void MetricsDaemon::UnscheduleUseMonitor() { + // If there's a use monitor scheduled already, destroys it. + if (usemon_source_ == NULL) + return; + + DLOG(INFO) << "destroying use monitor"; + g_source_destroy(usemon_source_); + usemon_source_ = NULL; + usemon_interval_ = 0; +} + void MetricsDaemon::PublishMetric(const char* name, int sample, int min, int max, int nbuckets) { - LOG(INFO) << "received metric: " << name << " " << sample << - " " << min << " " << max << " " << nbuckets; + DLOG(INFO) << "received metric: " << name << " " << sample << " " + << min << " " << max << " " << nbuckets; if (!testing_) { MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 23f0eb25f..99f76d087 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -6,16 +6,23 @@ #define METRICS_DAEMON_H_ #include +#include #include class MetricsDaemon { public: MetricsDaemon() - : testing_(false), - network_state_(kUnknownNetworkState), - network_state_changed_(0), - power_state_(kUnknownPowerState) {} + : network_state_(kUnknownNetworkState), + network_state_last_(0), + power_state_(kUnknownPowerState), + screensaver_state_(kUnknownScreenSaverState), + session_state_(kUnknownSessionState), + user_active_(false), + user_active_last_(0), + daily_use_day_last_(0), + usemon_interval_(0), + usemon_source_(NULL) {} ~MetricsDaemon() {} // Does all the work. If |run_as_daemon| is true, daemonizes by @@ -40,6 +47,45 @@ class MetricsDaemon { kNumberPowerStates }; + // The screen-saver states (see screensaver_states.h). + enum ScreenSaverState { + kUnknownScreenSaverState = -1, // Initial/unknown screen-saver state. +#define STATE(name, capname) kScreenSaverState ## capname, +#include "screensaver_states.h" + kNumberScreenSaverStates + }; + + // The user session states (see session_states.h). + enum SessionState { + kUnknownSessionState = -1, // Initial/unknown user session state. +#define STATE(name, capname) kSessionState ## capname, +#include "session_states.h" + kNumberSessionStates + }; + + // Data record for aggregating daily usage. + class UseRecord { + public: + UseRecord() : day_(0), seconds_(0) {} + int day_; + int seconds_; + }; + + // D-Bus message match strings. + static const char* kDBusMatches_[]; + + // Array of network states. + static const char* kNetworkStates_[kNumberNetworkStates]; + + // Array of power states. + static const char* kPowerStates_[kNumberPowerStates]; + + // Array of screen-saver states. + static const char* kScreenSaverStates_[kNumberScreenSaverStates]; + + // Array of user session states. + static const char* kSessionStates_[kNumberSessionStates]; + // Initializes. void Init(bool testing); @@ -52,36 +98,103 @@ class MetricsDaemon { void* user_data); // Processes network state change. - void NetStateChanged(const char* state_name); + void NetStateChanged(const char* state_name, time_t now); // Given the state name, returns the state id. NetworkState LookupNetworkState(const char* state_name); // Processes power state change. - void PowerStateChanged(const char* state_name); + void PowerStateChanged(const char* state_name, time_t now); // Given the state name, returns the state id. PowerState LookupPowerState(const char* state_name); + // Processes screen-saver state change. + void ScreenSaverStateChanged(const char* state_name, time_t now); + + // Given the state name, returns the state id. + ScreenSaverState LookupScreenSaverState(const char* state_name); + + // Processes user session state change. + void SessionStateChanged(const char* state_name, time_t now); + + // Given the state name, returns the state id. + SessionState LookupSessionState(const char* state_name); + + // Updates the user-active state to |active| and logs the usage data + // since the last update. If the user has just become active, + // reschedule the daily use monitor for more frequent updates -- + // this is followed by an exponential back-off (see UseMonitor). + void SetUserActiveState(bool active, time_t now); + + // Updates the daily usage file, if necessary, by adding |seconds| + // of active use to the |day| since Epoch. If there's usage data for + // day in the past in the usage file, that data is sent to UMA and + // removed from the file. If there's already usage data for |day| in + // the usage file, the |seconds| are accumulated. + void LogDailyUseRecord(int day, int seconds); + + // Callbacks for the daily use monitor. The daily use monitor uses + // LogDailyUseRecord to aggregate current usage data and send it to + // UMA, if necessary. It also reschedules itself using an + // exponentially bigger interval (up to a certain maximum) -- so + // usage is monitored less frequently with longer active use. + static gboolean UseMonitorStatic(gpointer data); + bool UseMonitor(); + + // Schedules or reschedules a daily use monitor for |interval| + // seconds from now. |backoff| mode is used by the use monitor to + // reschedule itself. If there's a monitor scheduled already and + // |backoff| is false, unschedules it first. Doesn't schedule a + // monitor for more than kUseMonitorIntervalMax seconds in the + // future (see metrics_daemon.cc). Returns true if a new use monitor + // was scheduled, false otherwise (note that if |backoff| is false a + // new use monitor will always be scheduled). + bool ScheduleUseMonitor(int interval, bool backoff); + + // Unschedules a scheduled use monitor, if any. + void UnscheduleUseMonitor(); + // Sends a stat to Chrome for transport to UMA (or prints it for // testing). See MetricsLibrary::SendToChrome in metrics_library.h // for a description of the arguments. void PublishMetric(const char* name, int sample, int min, int max, int nbuckets); - // D-Bus message match strings. - static const char* dbus_matches_[]; + // Testing mode. + bool testing_; - // Array of network states. - static const char* network_states_[kNumberNetworkStates]; + // Current network state. + NetworkState network_state_; - // Array of power states. - static const char* power_states_[kNumberPowerStates]; + // Timestamps last network state update. + time_t network_state_last_; - bool testing_; // just testing - NetworkState network_state_; // current network state - time_t network_state_changed_; // timestamp last net state change - PowerState power_state_; // current power state + // Current power state. + PowerState power_state_; + + // Current screen-saver state. + ScreenSaverState screensaver_state_; + + // Current user session state. + SessionState session_state_; + + // Is the user currently active: power is on, user session has + // started, screen is not locked. + bool user_active_; + + // Timestamps last user active update. + time_t user_active_last_; + + // Last stored daily use day (since epoch). + int daily_use_day_last_; + + // Sleep period until the next daily usage aggregation performed by + // the daily use monitor (see ScheduleUseMonitor). + int usemon_interval_; + + // Scheduled daily use monitor source (see ScheduleUseMonitor). + GSource* usemon_source_; }; #endif // METRICS_DAEMON_H_ diff --git a/metrics/power_states.h b/metrics/power_states.h index 413085ef6..74fbfecdd 100644 --- a/metrics/power_states.h +++ b/metrics/power_states.h @@ -4,18 +4,7 @@ // A table of power states, to be included when building tabular things. // -// This file is used to construct two things: an enumerated type in -// metrics_daemon.h, and a table of structures with state names in -// metrics_daemon.cc. Including this file ensures that the two tables are -// always in sync (and saves typing). I don't know of other ways of achieving -// the same result in C/C++, but it doesn't mean there isn't one. - -// Before you include this file, define STATE to do something useful, or else -// if will be a no-op. STATE will be undefined on exit. Don't worry about -// collisions for the STATE macro (as long as it's a macro) because the -// compiler will flag them---in that case, just change the name. If someone is -// misguided enough to use STATE for something other than a macro, the error -// messages will be slightly more complicated. +// See network_states.h for details. #ifndef STATE diff --git a/metrics/screensaver_states.h b/metrics/screensaver_states.h new file mode 100644 index 000000000..340142eaa --- /dev/null +++ b/metrics/screensaver_states.h @@ -0,0 +1,17 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A table of screen-saver states, to be included when building tabular things. +// +// See network_states.h for details. + + +#ifndef STATE +#define STATE(name, capname) +#endif + +STATE(locked, Locked) +STATE(unlocked, Unlocked) + +#undef STATE diff --git a/metrics/session_states.h b/metrics/session_states.h new file mode 100644 index 000000000..293dbd995 --- /dev/null +++ b/metrics/session_states.h @@ -0,0 +1,17 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A table of user session states, to be included when building tabular things. +// +// See network_states.h for details. + + +#ifndef STATE +#define STATE(name, capname) +#endif + +STATE(started, Started) +STATE(stopped, Stopped) + +#undef STATE From 2ccef01626f9e97cb577b2968d307a2710b9cabe Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 5 May 2010 16:06:37 -0700 Subject: [PATCH 007/200] Add some basic tests for metrics_daemon. A separate CL adds a test stanza to the metrics ebuild. More tests (specifically, for the D-Bus MessageFilter) will also come in a separate CL. Review URL: http://codereview.chromium.org/1919005 --- metrics/Makefile | 14 +- metrics/make_tests.sh | 12 + metrics/metrics_daemon.cc | 67 +++-- metrics/metrics_daemon.h | 37 ++- metrics/metrics_daemon_main.cc | 2 +- metrics/metrics_daemon_test.cc | 459 +++++++++++++++++++++++++++++ metrics/metrics_daemon_unittest.cc | 10 - 7 files changed, 559 insertions(+), 42 deletions(-) create mode 100755 metrics/make_tests.sh create mode 100644 metrics/metrics_daemon_test.cc delete mode 100644 metrics/metrics_daemon_unittest.cc diff --git a/metrics/Makefile b/metrics/Makefile index 784ff91e8..cb85fb9ee 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -12,7 +12,7 @@ CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions $(CCONFIG) CLIENT = metrics_client DAEMON = metrics_daemon -TESTDAEMON = test_daemon +DAEMON_TEST = metrics_daemon_test LIB = libmetrics.a SHAREDLIB = libmetrics.so @@ -25,12 +25,14 @@ DAEMON_OBJS = \ metrics_daemon_main.o TESTDAEMON_OBJS = \ metrics_daemon.o \ - metrics_daemon_unittest.o + metrics_daemon_test.o DAEMON_LDFLAGS = $(LDCONFIG) -lrt -lbase -lpthread -lgflags TESTDAEMON_LIBS = -lgtest -all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) $(TESTDAEMON) +all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) + +tests: $(DAEMON_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) $(CXX) $(LDFLAGS) $^ -o $@ @@ -38,11 +40,11 @@ $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) $(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) -$(TESTDAEMON): $(TESTDAEMON_OBJS) $(SHAREDLIB) +$(DAEMON_TEST): $(TESTDAEMON_OBJS) $(SHAREDLIB) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(TESTDAEMON_LIBS) $(LIB): $(LIB_OBJS) - ar rcs $@ $^ + $(AR) rcs $@ $^ $(SHAREDLIB): $(LIB_OBJS) $(CXX) $(LDFLAGS) -shared $^ -o $@ @@ -56,7 +58,7 @@ metrics_daemon.o: \ metrics_daemon.h \ network_states.h \ power_states.h -metrics_daemon_unittest.o: \ +metrics_daemon_test.o: \ metrics_daemon.h \ network_states.h \ power_states.h diff --git a/metrics/make_tests.sh b/metrics/make_tests.sh new file mode 100755 index 000000000..9dcc80475 --- /dev/null +++ b/metrics/make_tests.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# Builds tests. + +set -e +make tests +mkdir -p "${OUT_DIR}" +cp *_test "${OUT_DIR}" diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index ef973a236..da96dc2c6 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -37,6 +37,20 @@ static const int kSecondsPerDay = kMinutesPerDay * kSecondsPerMinute; static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute; static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute; +// static metrics parameters. +const char MetricsDaemon::kMetricDailyUseTimeName[] = + "Logging.DailyUseTime"; +const int MetricsDaemon::kMetricDailyUseTimeMin = 1; +const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay; +const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50; + +const char MetricsDaemon::kMetricTimeToNetworkDropName[] = + "Network.TimeToDrop"; +const int MetricsDaemon::kMetricTimeToNetworkDropMin = 1; +const int MetricsDaemon::kMetricTimeToNetworkDropMax = + 8 /* hours */ * kMinutesPerHour * kSecondsPerMinute; +const int MetricsDaemon::kMetricTimeToNetworkDropBuckets = 50; + // static const char* MetricsDaemon::kDBusMatches_[] = { "type='signal'," @@ -86,8 +100,8 @@ const char* MetricsDaemon::kSessionStates_[] = { #include "session_states.h" }; -void MetricsDaemon::Run(bool run_as_daemon, bool testing) { - Init(testing); +void MetricsDaemon::Run(bool run_as_daemon) { + Init(false); if (!run_as_daemon || daemon(0, 0) == 0) { Loop(); } @@ -95,6 +109,11 @@ void MetricsDaemon::Run(bool run_as_daemon, bool testing) { void MetricsDaemon::Init(bool testing) { testing_ = testing; + daily_use_record_file_ = kDailyUseRecordFile; + + // Don't setup D-Bus and GLib in test mode. + if (testing) + return; g_thread_init(NULL); g_type_init(); @@ -192,17 +211,19 @@ void MetricsDaemon::NetStateChanged(const char* state_name, time_t now) { NetworkState state = LookupNetworkState(state_name); // Logs the time in seconds between the network going online to - // going offline in order to measure the mean time to network - // dropping. Going offline as part of suspend-to-RAM is not logged - // as network drop -- the assumption is that the message for - // suspend-to-RAM comes before the network offline message which - // seems to and should be the case. - if (state == kNetworkStateOffline && + // going offline (or, more precisely, going not online) in order to + // measure the mean time to network dropping. Going offline as part + // of suspend-to-RAM is not logged as network drop -- the assumption + // is that the message for suspend-to-RAM comes before the network + // offline message which seems to and should be the case. + if (state != kNetworkStateOnline && network_state_ == kNetworkStateOnline && power_state_ != kPowerStateMem) { int online_time = static_cast(now - network_state_last_); - PublishMetric("Network.TimeToDrop", online_time, - 1, 8 /* hours */ * 60 * 60, 50); + PublishMetric(kMetricTimeToNetworkDropName, online_time, + kMetricTimeToNetworkDropMin, + kMetricTimeToNetworkDropMax, + kMetricTimeToNetworkDropBuckets); } network_state_ = state; @@ -304,11 +325,11 @@ void MetricsDaemon::LogDailyUseRecord(int day, int seconds) { return; DLOG(INFO) << "day: " << day << " usage: " << seconds << " seconds"; - int fd = HANDLE_EINTR(open(kDailyUseRecordFile, + int fd = HANDLE_EINTR(open(daily_use_record_file_, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); if (fd < 0) { - DLOG(WARNING) << "Unable to open the daily use file."; + PLOG(WARNING) << "Unable to open the daily use file"; return; } @@ -325,12 +346,14 @@ void MetricsDaemon::LogDailyUseRecord(int day, int seconds) { // the usage to the nearest minute and sends it to UMA. int minutes = (record.seconds_ + kSecondsPerMinute / 2) / kSecondsPerMinute; - PublishMetric("Logging.DailyUseTime", - minutes, 1, kMinutesPerDay, 50); + PublishMetric(kMetricDailyUseTimeName, minutes, + kMetricDailyUseTimeMin, + kMetricDailyUseTimeMax, + kMetricDailyUseTimeBuckets); // Truncates the usage file to ensure that no duplicate usage is // sent to UMA. - LOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0); + PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0); } } @@ -344,9 +367,10 @@ void MetricsDaemon::LogDailyUseRecord(int day, int seconds) { // else an already existing record for the same day will be // overwritten with updated usage below. - LOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0); - LOG_IF(WARNING, - HANDLE_EINTR(write(fd, &record, sizeof(record))) != sizeof(record)); + PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0); + PLOG_IF(WARNING, + HANDLE_EINTR(write(fd, &record, sizeof(record))) != + sizeof(record)); } HANDLE_EINTR(close(fd)); @@ -374,6 +398,9 @@ bool MetricsDaemon::UseMonitor() { bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff) { + if (testing_) + return false; + // Caps the interval -- the bigger the interval, the more active use // time will be potentially dropped on system shutdown. if (interval > kUseMonitorIntervalMax) @@ -417,8 +444,8 @@ void MetricsDaemon::UnscheduleUseMonitor() { void MetricsDaemon::PublishMetric(const char* name, int sample, int min, int max, int nbuckets) { - DLOG(INFO) << "received metric: " << name << " " << sample << " " - << min << " " << max << " " << nbuckets; + LOG(INFO) << "received metric: " << name << " " << sample << " " + << min << " " << max << " " << nbuckets; if (!testing_) { MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 99f76d087..028525a3d 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -9,11 +9,14 @@ #include #include +#include // for FRIEND_TEST + class MetricsDaemon { public: MetricsDaemon() - : network_state_(kUnknownNetworkState), + : daily_use_record_file_(NULL), + network_state_(kUnknownNetworkState), network_state_last_(0), power_state_(kUnknownPowerState), screensaver_state_(kUnknownScreenSaverState), @@ -26,11 +29,23 @@ class MetricsDaemon { ~MetricsDaemon() {} // Does all the work. If |run_as_daemon| is true, daemonizes by - // forking. If |testing| is true, logs the stats instead of sending - // them to Chrome. - void Run(bool run_as_daemon, bool testing); + // forking. + void Run(bool run_as_daemon); private: + friend class MetricsDaemonTest; + FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecord); + FRIEND_TEST(MetricsDaemonTest, LookupNetworkState); + FRIEND_TEST(MetricsDaemonTest, LookupPowerState); + FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); + FRIEND_TEST(MetricsDaemonTest, LookupSessionState); + FRIEND_TEST(MetricsDaemonTest, NetStateChanged); + FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); + FRIEND_TEST(MetricsDaemonTest, PublishMetric); + FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); + FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); + FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); + // The network states (see network_states.h). enum NetworkState { kUnknownNetworkState = -1, // Initial/unknown network state. @@ -71,6 +86,16 @@ class MetricsDaemon { int seconds_; }; + // Metric parameters. + static const char kMetricDailyUseTimeName[]; + static const int kMetricDailyUseTimeMin; + static const int kMetricDailyUseTimeMax; + static const int kMetricDailyUseTimeBuckets; + static const char kMetricTimeToNetworkDropName[]; + static const int kMetricTimeToNetworkDropMin; + static const int kMetricTimeToNetworkDropMax; + static const int kMetricTimeToNetworkDropBuckets; + // D-Bus message match strings. static const char* kDBusMatches_[]; @@ -161,9 +186,11 @@ class MetricsDaemon { void PublishMetric(const char* name, int sample, int min, int max, int nbuckets); - // Testing mode. + // Test mode. bool testing_; + const char* daily_use_record_file_; + // Current network state. NetworkState network_state_; diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 302bdcb5e..7ace3334d 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -12,5 +12,5 @@ DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); int main(int argc, char** argv) { MetricsDaemon::MetricsDaemon d; google::ParseCommandLineFlags(&argc, &argv, true); - d.Run(FLAGS_daemon, false); + d.Run(FLAGS_daemon); } diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc new file mode 100644 index 000000000..043be7249 --- /dev/null +++ b/metrics/metrics_daemon_test.cc @@ -0,0 +1,459 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics_daemon.h" + +#include + +#include +#include +#include +#include +#include + +static const char kTestDailyUseRecordFile[] = "/tmp/daily-usage-test"; +static const char kDoesNotExistFile[] = "/does/not/exist"; + +static const int kSecondsPerDay = 24 * 60 * 60; + +class MetricsDaemonTest : public testing::Test { + protected: + virtual void SetUp() { + daemon_.Init(true); + daemon_.daily_use_record_file_ = kTestDailyUseRecordFile; + + // The test fixture object will be used by the log message handler. + daemon_test_ = this; + logging::SetLogMessageHandler(LogMessageHandler); + } + + virtual void TearDown() { + logging::SetLogMessageHandler(NULL); + daemon_test_ = NULL; + file_util::Delete(FilePath(kTestDailyUseRecordFile), false); + } + + // Collects log messages in the |daemon_log_| member string so that + // they can be analyzed for errors and expected behavior. + static bool LogMessageHandler(int severity, const std::string& str) { + daemon_test_->daemon_log_.append(str); + daemon_test_->daemon_log_.append("\n"); + + // Returning true would mute the log. + return false; + } + + // Returns true if the daemon log contains |pattern|, false otherwise. + bool LogContains(const std::string& pattern) { + return daemon_log_.find(pattern) != std::string::npos; + } + + // Resets the daemon log history to empty. + void LogReset() { + daemon_log_.clear(); + } + + // Returns true if the specified metric is found in the generated + // log so far, false otherwise. + bool AssertMetricGenerated(const std::string& name, int sample, + int min, int max, int buckets) { + return LogContains(StringPrintf("received metric: %s %d %d %d %d", + name.c_str(), sample, min, max, buckets)); + } + + // Returns true if the specified daily use time metric is found in + // the generated log so far, false otherwise. + bool AssertDailyUseTimeMetric(int sample) { + return AssertMetricGenerated( + MetricsDaemon::kMetricDailyUseTimeName, sample, + MetricsDaemon::kMetricDailyUseTimeMin, + MetricsDaemon::kMetricDailyUseTimeMax, + MetricsDaemon::kMetricDailyUseTimeBuckets); + } + + // Returns true if the specified time to network drop metric is + // found in the generated log so far, false otherwise. + bool AssertTimeToNetworkDropMetric(int sample) { + return AssertMetricGenerated( + MetricsDaemon::kMetricTimeToNetworkDropName, sample, + MetricsDaemon::kMetricTimeToNetworkDropMin, + MetricsDaemon::kMetricTimeToNetworkDropMax, + MetricsDaemon::kMetricTimeToNetworkDropBuckets); + } + + // Returns true if no metric can be found in the generated log so + // far, false otherwise. + bool NoMetricGenerated() { + return !LogContains("received metric"); + } + + // Asserts that the daily use record file contains the specified + // contents. + testing::AssertionResult AssertDailyUseRecord(const char* expr_day, + const char* expr_seconds, + int expected_day, + int expected_seconds) { + int fd = HANDLE_EINTR(open(daemon_.daily_use_record_file_, O_RDONLY)); + if (fd < 0) { + testing::Message msg; + msg << "Unable to open " << daemon_.daily_use_record_file_; + return testing::AssertionFailure(msg); + } + + MetricsDaemon::UseRecord record; + if (!file_util::ReadFromFD(fd, reinterpret_cast(&record), + sizeof(record))) { + testing::Message msg; + msg << "Unable to read " << sizeof(record) << " bytes from " + << daemon_.daily_use_record_file_; + HANDLE_EINTR(close(fd)); + return testing::AssertionFailure(msg); + } + + if (record.day_ != expected_day || record.seconds_ != expected_seconds) { + testing::Message msg; + msg << "actual use record (" << record.day_ << ", " << record.seconds_ + << ") expected (" << expected_day << ", " << expected_seconds << ")"; + HANDLE_EINTR(close(fd)); + return testing::AssertionFailure(msg); + } + + HANDLE_EINTR(close(fd)); + return testing::AssertionSuccess(); + } + + bool NoOrEmptyUseRecordFile() { + FilePath record_file(daemon_.daily_use_record_file_); + int64 record_file_size; + return !file_util::PathExists(record_file) || + (file_util::GetFileSize(record_file, &record_file_size) && + record_file_size == 0); + } + + // Pointer to the current test fixture. + static MetricsDaemonTest* daemon_test_; + + // The MetricsDaemon under test. + MetricsDaemon daemon_; + + // The accumulated metrics daemon log. + std::string daemon_log_; +}; + +// static +MetricsDaemonTest* MetricsDaemonTest::daemon_test_ = NULL; + +TEST_F(MetricsDaemonTest, LogDailyUseRecord) { + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.LogDailyUseRecord(/* day */ 5, /* seconds */ 120); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 120); + EXPECT_EQ(5, daemon_.daily_use_day_last_); + + daemon_.LogDailyUseRecord(/* day */ 5, /* seconds */ 0); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 120); + EXPECT_EQ(5, daemon_.daily_use_day_last_); + + daemon_.LogDailyUseRecord(/* day */ 5, /* seconds */ 240); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 360); + EXPECT_EQ(5, daemon_.daily_use_day_last_); + + EXPECT_TRUE(NoMetricGenerated()); + + LogReset(); + daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 0); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 6)); + EXPECT_EQ(6, daemon_.daily_use_day_last_); + + // Tests rounding use time to the closest minute. + daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 90); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 90); + EXPECT_EQ(6, daemon_.daily_use_day_last_); + + LogReset(); + daemon_.LogDailyUseRecord(/* day */ 7, /* seconds */ 89); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 89); + EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 2)); + EXPECT_EQ(7, daemon_.daily_use_day_last_); + + LogReset(); + daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 15); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 15); + EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 1)); + EXPECT_EQ(6, daemon_.daily_use_day_last_); + + // Checks that the daemon doesn't die badly if the file can't be + // created. + LogReset(); + daemon_.daily_use_record_file_ = kDoesNotExistFile; + daemon_.LogDailyUseRecord(10, 20); + EXPECT_TRUE(LogContains("Unable to open the daily use file: " + "No such file or directory")); + EXPECT_EQ(6, daemon_.daily_use_day_last_); + file_util::Delete(FilePath(kDoesNotExistFile), false); +} + +TEST_F(MetricsDaemonTest, LookupNetworkState) { + EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, + daemon_.LookupNetworkState("online")); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, + daemon_.LookupNetworkState("offline")); + EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, + daemon_.LookupNetworkState("somestate")); +} + +TEST_F(MetricsDaemonTest, LookupPowerState) { + EXPECT_EQ(MetricsDaemon::kPowerStateOn, + daemon_.LookupPowerState("on")); + EXPECT_EQ(MetricsDaemon::kPowerStateMem, + daemon_.LookupPowerState("mem")); + EXPECT_EQ(MetricsDaemon::kUnknownPowerState, + daemon_.LookupPowerState("somestate")); +} + +TEST_F(MetricsDaemonTest, LookupScreenSaverState) { + EXPECT_EQ(MetricsDaemon::kScreenSaverStateLocked, + daemon_.LookupScreenSaverState("locked")); + EXPECT_EQ(MetricsDaemon::kScreenSaverStateUnlocked, + daemon_.LookupScreenSaverState("unlocked")); + EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, + daemon_.LookupScreenSaverState("somestate")); +} + +TEST_F(MetricsDaemonTest, LookupSessionState) { + EXPECT_EQ(MetricsDaemon::kSessionStateStarted, + daemon_.LookupSessionState("started")); + EXPECT_EQ(MetricsDaemon::kSessionStateStopped, + daemon_.LookupSessionState("stopped")); + EXPECT_EQ(MetricsDaemon::kUnknownSessionState, + daemon_.LookupSessionState("somestate")); +} + +TEST_F(MetricsDaemonTest, NetStateChanged) { + EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); + EXPECT_EQ(0, daemon_.network_state_last_); + EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); + + daemon_.NetStateChanged("online", /* now */ 10); + EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); + EXPECT_EQ(10, daemon_.network_state_last_); + + EXPECT_TRUE(NoMetricGenerated()); + + daemon_.NetStateChanged("offline", /* now */ 30); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); + EXPECT_EQ(30, daemon_.network_state_last_); + EXPECT_TRUE(AssertTimeToNetworkDropMetric(/* sample */ 20)); + + LogReset(); + daemon_.NetStateChanged("online", /* now */ 60); + EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); + EXPECT_EQ(60, daemon_.network_state_last_); + + daemon_.PowerStateChanged("mem", /* now */ 80); + EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); + EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); + EXPECT_EQ(60, daemon_.network_state_last_); + + daemon_.NetStateChanged("offline", /* now */ 85); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); + EXPECT_EQ(85, daemon_.network_state_last_); + + daemon_.NetStateChanged("somestate", /* now */ 90); + EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); + EXPECT_EQ(90, daemon_.network_state_last_); + + daemon_.NetStateChanged("offline", /* now */ 95); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); + EXPECT_EQ(95, daemon_.network_state_last_); + + daemon_.PowerStateChanged("on", /* now */ 100); + EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); + EXPECT_EQ(95, daemon_.network_state_last_); + + daemon_.NetStateChanged("online", /* now */ 105); + EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); + EXPECT_EQ(105, daemon_.network_state_last_); + + EXPECT_TRUE(NoMetricGenerated()); + + daemon_.NetStateChanged("offline", /* now */ 108); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); + EXPECT_EQ(108, daemon_.network_state_last_); + EXPECT_TRUE(AssertTimeToNetworkDropMetric(/* sample */ 3)); +} + +TEST_F(MetricsDaemonTest, PowerStateChanged) { + EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(0, daemon_.user_active_last_); + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.SetUserActiveState(/* active */ true, 7 * kSecondsPerDay + 15); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(7 * kSecondsPerDay + 15, daemon_.user_active_last_); + EXPECT_EQ(7, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.PowerStateChanged("mem", 7 * kSecondsPerDay + 45); + EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(7 * kSecondsPerDay + 45, daemon_.user_active_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); + + daemon_.PowerStateChanged("on", 7 * kSecondsPerDay + 85); + EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(7 * kSecondsPerDay + 45, daemon_.user_active_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); + + daemon_.PowerStateChanged("otherstate", 7 * kSecondsPerDay + 185); + EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(7 * kSecondsPerDay + 185, daemon_.user_active_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); + + EXPECT_TRUE(NoMetricGenerated()); +} + +TEST_F(MetricsDaemonTest, PublishMetric) { + daemon_.PublishMetric("Dummy.Metric", /* sample */ 3, + /* min */ 1, /* max */ 100, /* buckets */ 50); + EXPECT_TRUE(AssertMetricGenerated("Dummy.Metric", 3, 1, 100, 50)); +} + +TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { + EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, + daemon_.screensaver_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(0, daemon_.user_active_last_); + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.ScreenSaverStateChanged("locked", 5 * kSecondsPerDay + 10); + EXPECT_EQ(MetricsDaemon::kScreenSaverStateLocked, + daemon_.screensaver_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(5 * kSecondsPerDay + 10, daemon_.user_active_last_); + EXPECT_EQ(5, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.ScreenSaverStateChanged("unlocked", 5 * kSecondsPerDay + 100); + EXPECT_EQ(MetricsDaemon::kScreenSaverStateUnlocked, + daemon_.screensaver_state_); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(5 * kSecondsPerDay + 100, daemon_.user_active_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.ScreenSaverStateChanged("otherstate", 5 * kSecondsPerDay + 300); + EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, + daemon_.screensaver_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(5 * kSecondsPerDay + 300, daemon_.user_active_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 200); + + EXPECT_TRUE(NoMetricGenerated()); +} + +TEST_F(MetricsDaemonTest, SessionStateChanged) { + EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(0, daemon_.user_active_last_); + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.SessionStateChanged("started", 15 * kSecondsPerDay + 20); + EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(15 * kSecondsPerDay + 20, daemon_.user_active_last_); + EXPECT_EQ(15, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.SessionStateChanged("stopped", 15 * kSecondsPerDay + 150); + EXPECT_EQ(MetricsDaemon::kSessionStateStopped, daemon_.session_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(15 * kSecondsPerDay + 150, daemon_.user_active_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 15, /* seconds */ 130); + + daemon_.SessionStateChanged("otherstate", 15 * kSecondsPerDay + 300); + EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(15 * kSecondsPerDay + 300, daemon_.user_active_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 15, /* seconds */ 130); + + EXPECT_TRUE(NoMetricGenerated()); +} + +TEST_F(MetricsDaemonTest, SetUserActiveState) { + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(0, daemon_.user_active_last_); + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.SetUserActiveState(/* active */ false, 5 * kSecondsPerDay + 10); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(5 * kSecondsPerDay + 10, daemon_.user_active_last_); + EXPECT_EQ(5, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.SetUserActiveState(/* active */ true, 6 * kSecondsPerDay + 20); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(6 * kSecondsPerDay + 20, daemon_.user_active_last_); + EXPECT_EQ(6, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + + daemon_.SetUserActiveState(/* active */ true, 6 * kSecondsPerDay + 120); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(6 * kSecondsPerDay + 120, daemon_.user_active_last_); + EXPECT_EQ(6, daemon_.daily_use_day_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 100); + + daemon_.SetUserActiveState(/* active */ false, 6 * kSecondsPerDay + 220); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(6 * kSecondsPerDay + 220, daemon_.user_active_last_); + EXPECT_EQ(6, daemon_.daily_use_day_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 200); + + EXPECT_TRUE(NoMetricGenerated()); + + LogReset(); + daemon_.SetUserActiveState(/* active */ true, 8 * kSecondsPerDay - 300); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(8 * kSecondsPerDay - 300, daemon_.user_active_last_); + EXPECT_EQ(7, daemon_.daily_use_day_last_); + EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 3)); + + LogReset(); + daemon_.SetUserActiveState(/* active */ false, 8 * kSecondsPerDay + 300); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(8 * kSecondsPerDay + 300, daemon_.user_active_last_); + EXPECT_EQ(8, daemon_.daily_use_day_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 8, /* seconds */ 600); + + daemon_.SetUserActiveState(/* active */ true, 9 * kSecondsPerDay - 400); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(9 * kSecondsPerDay - 400, daemon_.user_active_last_); + EXPECT_EQ(8, daemon_.daily_use_day_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 8, /* seconds */ 600); + + EXPECT_TRUE(NoMetricGenerated()); + + LogReset(); + daemon_.SetUserActiveState(/* active */ true, 9 * kSecondsPerDay + 400); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(9 * kSecondsPerDay + 400, daemon_.user_active_last_); + EXPECT_EQ(9, daemon_.daily_use_day_last_); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 9, /* seconds */ 800); + EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 10)); +} + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/metrics/metrics_daemon_unittest.cc b/metrics/metrics_daemon_unittest.cc deleted file mode 100644 index 222cefab7..000000000 --- a/metrics/metrics_daemon_unittest.cc +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2009 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "metrics_daemon.h" - -int main(int argc, char** argv) { - MetricsDaemon d; - d.Run(false, true); -} From e579d66a369e0319949732c257081fba49aba209 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 5 May 2010 16:19:39 -0700 Subject: [PATCH 008/200] Add tests for the D-Bus MessageFilter. Review URL: http://codereview.chromium.org/1990001 --- metrics/metrics_daemon.h | 1 + metrics/metrics_daemon_test.cc | 90 +++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 028525a3d..3ab857a96 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -39,6 +39,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, LookupPowerState); FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); FRIEND_TEST(MetricsDaemonTest, LookupSessionState); + FRIEND_TEST(MetricsDaemonTest, MessageFilter); FRIEND_TEST(MetricsDaemonTest, NetStateChanged); FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); FRIEND_TEST(MetricsDaemonTest, PublishMetric); diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 043be7249..25b0855c7 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -12,7 +12,7 @@ #include #include -static const char kTestDailyUseRecordFile[] = "/tmp/daily-usage-test"; +static const char kTestDailyUseRecordFile[] = "daily-usage-test"; static const char kDoesNotExistFile[] = "/does/not/exist"; static const int kSecondsPerDay = 24 * 60 * 60; @@ -123,6 +123,8 @@ class MetricsDaemonTest : public testing::Test { return testing::AssertionSuccess(); } + // Returns true if the daily use record file does not exist or is + // empty, false otherwise. bool NoOrEmptyUseRecordFile() { FilePath record_file(daemon_.daily_use_record_file_); int64 record_file_size; @@ -131,6 +133,34 @@ class MetricsDaemonTest : public testing::Test { record_file_size == 0); } + // Creates a new DBus signal message with a single string + // argument. The message can be deallocated through + // DeleteDBusMessage. + // + // |path| is the object emitting the signal. + // |interface| is the interface the signal is emitted from. + // |name| is the name of the signal. + // |arg_value| is the value of the string argument. + DBusMessage* NewDBusSignalString(const std::string& path, + const std::string& interface, + const std::string& name, + const std::string& arg_value) { + DBusMessage* msg = dbus_message_new_signal(path.c_str(), + interface.c_str(), + name.c_str()); + DBusMessageIter iter; + dbus_message_iter_init_append(msg, &iter); + const char* arg_value_c = arg_value.c_str(); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &arg_value_c); + return msg; + } + + // Deallocates the DBus message |msg| previously allocated through + // dbus_message_new*. + void DeleteDBusMessage(DBusMessage* msg) { + dbus_message_unref(msg); + } + // Pointer to the current test fixture. static MetricsDaemonTest* daemon_test_; @@ -232,6 +262,64 @@ TEST_F(MetricsDaemonTest, LookupSessionState) { daemon_.LookupSessionState("somestate")); } +TEST_F(MetricsDaemonTest, MessageFilter) { + DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); + DBusHandlerResult res = + MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res); + DeleteDBusMessage(msg); + + msg = NewDBusSignalString("/", + "org.moblin.connman.Manager", + "StateChanged", + "online"); + EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); + res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); + EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); + DeleteDBusMessage(msg); + + msg = NewDBusSignalString("/", + "org.chromium.Power.Manager", + "PowerStateChanged", + "on"); + EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); + res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); + EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); + DeleteDBusMessage(msg); + + msg = NewDBusSignalString("/", + "org.chromium.ScreenSaver.Manager", + "LockStateChanged", + "unlocked"); + EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, + daemon_.screensaver_state_); + res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + EXPECT_EQ(MetricsDaemon::kScreenSaverStateUnlocked, + daemon_.screensaver_state_); + EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); + DeleteDBusMessage(msg); + + msg = NewDBusSignalString("/org/chromium/SessionManager", + "org.chromium.SessionManagerInterface", + "SessionStateChanged", + "started"); + EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); + res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); + EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); + DeleteDBusMessage(msg); + + msg = NewDBusSignalString("/", + "org.chromium.UnknownService.Manager", + "StateChanged", + "randomstate"); + res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res); + DeleteDBusMessage(msg); +} + TEST_F(MetricsDaemonTest, NetStateChanged) { EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); EXPECT_EQ(0, daemon_.network_state_last_); From 4fd6d3f1d049379704a8aef16177270649abb8a0 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Tue, 11 May 2010 09:47:43 -0700 Subject: [PATCH 009/200] First draft of the metrics doc. Review URL: http://codereview.chromium.org/1989004 --- metrics/README | 121 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 114 insertions(+), 7 deletions(-) diff --git a/metrics/README b/metrics/README index d88992daf..912af2713 100644 --- a/metrics/README +++ b/metrics/README @@ -1,8 +1,115 @@ -This packages contains all scripts and programs assoicated with metrics -collection for both Chrome's User Metrics Server and automated performance -metrics collection via Autotest. +Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. -The package includes the metrics daemon for Chrome OS. This program -runs as a daemon and collects events by polling and listening for -d-bus signals. It then adds timing (if needed) and sends the events -to Chrome for transport to the UMA server at Google. +The Chrome OS "metrics" package contains utilities for client-side +user metric collection. The collected data is sent to Chrome for +transport to the UMA server. + + +================================================================================ +The Metrics Library: libmetrics +================================================================================ + +libmetrics is a small library that implements the basic C++ API for +metrics collection. All metrics collection is funneled through this +library. The easiest and recommended way for a client-side module to +collect user metrics is to link libmetrics and use its APIs to send +metrics to Chrome for transport to UMA. In order to use the library in +a module, you need to do the following: + +- Add a dependence (DEPEND and RDEPEND) on chromeos-base/metrics to + the module's ebuild. + +- Link the module with libmetrics (for example, by passing -lmetrics + to the module's link command). Both libmetrics.so and libmetrics.a + are built and installed under $SYSROOT/usr/lib/. Note that by + default -lmetrics will link against libmetrics.so, which is + preferred. + +- To access the metrics library API in the module, include the + header file. The file is installed in + $SYSROOT/usr/include/ when the metrics library is built and + installed. + +- Currently, the API includes two static methods: + + bool MetricsLibrary::SendToChrome(const std::string& name, int sample, + int min, int max, int nbuckets) + sends a sample for a regular (exponential) histogram. + + bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample, + int max) + sends a sample for an enumeration (linear) histogram. + + See API documentation in metrics_library.h under + src/platform/metrics/. + + TODO: It might be better to convert the API to a dynamic object. + +- On the target platform, shortly after the sample is sent it should + be visible in Chrome through "about:histograms". + + +================================================================================ +Histogram Naming Convention +================================================================================ + +Use TrackerArea.MetricName. For example: + +Logging.CrashCounter +Network.TimeToDrop +Platform.BootTime + + +================================================================================ +Server Side +================================================================================ + +If the histogram data is visible in about:histograms, it will be sent +by an official Chrome build to UMA, assuming the user has opted into +metrics collection. To make the histogram visible on +"chromedashboard", the histogram wiki needs to be updated (steps 2 and +3 after following the "Details on how to add your own histograms" link +under the Histograms tab). + +The UMA server logs and keeps the collected field data even if the +metric's name is not added to the histogram wiki. However, the +dashboard histogram for that metric will show field data as of the +histogram wiki update date; it will not include data for older +dates. If past data needs to be displayed, manual server-side +intervention is required. In other words, one should assume that field +data collection starts only after the histogram wiki has been updated. + + +================================================================================ +The Metrics Client: metrics_client +================================================================================ + +metrics_client is a simple shell command-line utility for sending +histogram samples. It's installed under /usr/bin on the target +platform and uses libmetrics to send the data to Chrome. The utility +is useful for generating metrics from shell scripts. + +For usage information and command-line options, run "metrics_client" +on the target platform or look for "Usage:" in metrics_client.cc. + + +================================================================================ +The Metrics Daemon: metrics_daemon +================================================================================ + +metrics_daemon is a daemon that runs in the background on the target +platform and is intended for passive or ongoing metrics collection, or +metrics collection requiring feedback from multiple modules. For +example, it listens to D-Bus signals related to the user session and +screen saver states to determine if the user is actively using the +device or not and generates the corresponding data. The metrics daemon +uses libmetrics to send the data to Chrome. + +The recommended way to generate metrics data from a module is to link +and use libmetrics directly. However, the module could instead send +signals to or communicate in some alternative way with the metrics +daemon. Then the metrics daemon needs to monitor for the relevant +events and take appropriate action -- for example, aggregate data and +send the histogram samples. From fc91b42a5e9b2813f9614fbcd3b5fe97a6a01781 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 12 May 2010 13:05:45 -0700 Subject: [PATCH 010/200] Start transition the metrics library to non-static API. Use gmock in tests. Review URL: http://codereview.chromium.org/2049007 --- metrics/Makefile | 2 +- metrics/README | 19 +-- metrics/metrics_daemon.cc | 17 +-- metrics/metrics_daemon.h | 18 ++- metrics/metrics_daemon_test.cc | 210 ++++++++++++++++++--------------- metrics/metrics_library.cc | 13 ++ metrics/metrics_library.h | 23 +++- metrics/metrics_library_mock.h | 23 ++++ 8 files changed, 206 insertions(+), 119 deletions(-) create mode 100644 metrics/metrics_library_mock.h diff --git a/metrics/Makefile b/metrics/Makefile index cb85fb9ee..16e1d9f4d 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -28,7 +28,7 @@ TESTDAEMON_OBJS = \ metrics_daemon_test.o DAEMON_LDFLAGS = $(LDCONFIG) -lrt -lbase -lpthread -lgflags -TESTDAEMON_LIBS = -lgtest +TESTDAEMON_LIBS = -lgmock -lgtest all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) diff --git a/metrics/README b/metrics/README index 912af2713..d8f17c444 100644 --- a/metrics/README +++ b/metrics/README @@ -32,21 +32,26 @@ a module, you need to do the following: $SYSROOT/usr/include/ when the metrics library is built and installed. -- Currently, the API includes two static methods: +- The API includes two methods: + + bool MetricsLibrary::SendToUMA(const std::string& name, int sample, + int min, int max, int nbuckets) + sends a sample for a regular (exponential) histogram. + + bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, + int max) + sends a sample for an enumeration (linear) histogram. + + Currently, the API also includes two deprecated static methods: bool MetricsLibrary::SendToChrome(const std::string& name, int sample, int min, int max, int nbuckets) - sends a sample for a regular (exponential) histogram. - bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample, int max) - sends a sample for an enumeration (linear) histogram. - See API documentation in metrics_library.h under + See the API documentation in metrics_library.h under src/platform/metrics/. - TODO: It might be better to convert the API to a dynamic object. - - On the target platform, shortly after the sample is sent it should be visible in Chrome through "about:histograms". diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index da96dc2c6..a66b570ca 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "metrics_daemon.h" -#include "metrics_library.h" #include #include @@ -101,14 +100,18 @@ const char* MetricsDaemon::kSessionStates_[] = { }; void MetricsDaemon::Run(bool run_as_daemon) { - Init(false); + MetricsLibrary metrics_lib; + metrics_lib.Init(); + Init(false, &metrics_lib); if (!run_as_daemon || daemon(0, 0) == 0) { Loop(); } } -void MetricsDaemon::Init(bool testing) { +void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { testing_ = testing; + DCHECK(metrics_lib != NULL); + metrics_lib_ = metrics_lib; daily_use_record_file_ = kDailyUseRecordFile; // Don't setup D-Bus and GLib in test mode. @@ -444,9 +447,7 @@ void MetricsDaemon::UnscheduleUseMonitor() { void MetricsDaemon::PublishMetric(const char* name, int sample, int min, int max, int nbuckets) { - LOG(INFO) << "received metric: " << name << " " << sample << " " - << min << " " << max << " " << nbuckets; - if (!testing_) { - MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); - } + DLOG(INFO) << "received metric: " << name << " " << sample << " " + << min << " " << max << " " << nbuckets; + metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 3ab857a96..3aeaaaaf9 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -9,6 +9,8 @@ #include #include +#include "metrics_library.h" + #include // for FRIEND_TEST class MetricsDaemon { @@ -34,18 +36,23 @@ class MetricsDaemon { private: friend class MetricsDaemonTest; - FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecord); + FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecordBadFileLocation); + FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecordOnLogin); + FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecordRoundDown); + FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecordRoundUp); FRIEND_TEST(MetricsDaemonTest, LookupNetworkState); FRIEND_TEST(MetricsDaemonTest, LookupPowerState); FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); FRIEND_TEST(MetricsDaemonTest, LookupSessionState); FRIEND_TEST(MetricsDaemonTest, MessageFilter); - FRIEND_TEST(MetricsDaemonTest, NetStateChanged); + FRIEND_TEST(MetricsDaemonTest, NetStateChangedSimpleDrop); + FRIEND_TEST(MetricsDaemonTest, NetStateChangedSuspend); FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); FRIEND_TEST(MetricsDaemonTest, PublishMetric); FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); - FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); + FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateSendOnLogin); + FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateSendOnMonitor); // The network states (see network_states.h). enum NetworkState { @@ -113,7 +120,7 @@ class MetricsDaemon { static const char* kSessionStates_[kNumberSessionStates]; // Initializes. - void Init(bool testing); + void Init(bool testing, MetricsLibraryInterface* metrics_lib); // Creates the event loop and enters it. void Loop(); @@ -190,6 +197,9 @@ class MetricsDaemon { // Test mode. bool testing_; + // The metrics library handle. + MetricsLibraryInterface* metrics_lib_; + const char* daily_use_record_file_; // Current network state. diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 25b0855c7..8180b143c 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "metrics_daemon.h" +#include "metrics_library_mock.h" #include @@ -12,6 +13,10 @@ #include #include +using ::testing::Mock; +using ::testing::Return; +using ::testing::StrictMock; + static const char kTestDailyUseRecordFile[] = "daily-usage-test"; static const char kDoesNotExistFile[] = "/does/not/exist"; @@ -20,12 +25,12 @@ static const int kSecondsPerDay = 24 * 60 * 60; class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { - daemon_.Init(true); + daemon_.Init(true, &metrics_lib_); daemon_.daily_use_record_file_ = kTestDailyUseRecordFile; // The test fixture object will be used by the log message handler. daemon_test_ = this; - logging::SetLogMessageHandler(LogMessageHandler); + logging::SetLogMessageHandler(HandleLogMessages); } virtual void TearDown() { @@ -36,7 +41,7 @@ class MetricsDaemonTest : public testing::Test { // Collects log messages in the |daemon_log_| member string so that // they can be analyzed for errors and expected behavior. - static bool LogMessageHandler(int severity, const std::string& str) { + static bool HandleLogMessages(int severity, const std::string& str) { daemon_test_->daemon_log_.append(str); daemon_test_->daemon_log_.append("\n"); @@ -49,43 +54,32 @@ class MetricsDaemonTest : public testing::Test { return daemon_log_.find(pattern) != std::string::npos; } - // Resets the daemon log history to empty. - void LogReset() { - daemon_log_.clear(); + // Adds a metrics library mock expectation that the specified metric + // will be generated. + void ExpectMetric(const std::string& name, int sample, + int min, int max, int buckets) { + EXPECT_CALL(metrics_lib_, SendToUMA(name, sample, min, max, buckets)) + .Times(1) + .WillOnce(Return(true)) + .RetiresOnSaturation(); } - // Returns true if the specified metric is found in the generated - // log so far, false otherwise. - bool AssertMetricGenerated(const std::string& name, int sample, - int min, int max, int buckets) { - return LogContains(StringPrintf("received metric: %s %d %d %d %d", - name.c_str(), sample, min, max, buckets)); + // Adds a metrics library mock expectation that the specified daily + // use time metric will be generated. + void ExpectDailyUseTimeMetric(int sample) { + ExpectMetric(MetricsDaemon::kMetricDailyUseTimeName, sample, + MetricsDaemon::kMetricDailyUseTimeMin, + MetricsDaemon::kMetricDailyUseTimeMax, + MetricsDaemon::kMetricDailyUseTimeBuckets); } - // Returns true if the specified daily use time metric is found in - // the generated log so far, false otherwise. - bool AssertDailyUseTimeMetric(int sample) { - return AssertMetricGenerated( - MetricsDaemon::kMetricDailyUseTimeName, sample, - MetricsDaemon::kMetricDailyUseTimeMin, - MetricsDaemon::kMetricDailyUseTimeMax, - MetricsDaemon::kMetricDailyUseTimeBuckets); - } - - // Returns true if the specified time to network drop metric is - // found in the generated log so far, false otherwise. - bool AssertTimeToNetworkDropMetric(int sample) { - return AssertMetricGenerated( - MetricsDaemon::kMetricTimeToNetworkDropName, sample, - MetricsDaemon::kMetricTimeToNetworkDropMin, - MetricsDaemon::kMetricTimeToNetworkDropMax, - MetricsDaemon::kMetricTimeToNetworkDropBuckets); - } - - // Returns true if no metric can be found in the generated log so - // far, false otherwise. - bool NoMetricGenerated() { - return !LogContains("received metric"); + // Adds a metrics library mock expectation that the specified time + // to network dropping metric will be generated. + void ExpectTimeToNetworkDropMetric(int sample) { + ExpectMetric(MetricsDaemon::kMetricTimeToNetworkDropName, sample, + MetricsDaemon::kMetricTimeToNetworkDropMin, + MetricsDaemon::kMetricTimeToNetworkDropMax, + MetricsDaemon::kMetricTimeToNetworkDropBuckets); } // Asserts that the daily use record file contains the specified @@ -125,7 +119,7 @@ class MetricsDaemonTest : public testing::Test { // Returns true if the daily use record file does not exist or is // empty, false otherwise. - bool NoOrEmptyUseRecordFile() { + bool AssertNoOrEmptyUseRecordFile() { FilePath record_file(daemon_.daily_use_record_file_); int64 record_file_size; return !file_util::PathExists(record_file) || @@ -167,6 +161,10 @@ class MetricsDaemonTest : public testing::Test { // The MetricsDaemon under test. MetricsDaemon daemon_; + // Metrics library mock. It's a strict mock so that all unexpected + // metric generation calls are marked as failures. + StrictMock metrics_lib_; + // The accumulated metrics daemon log. std::string daemon_log_; }; @@ -174,9 +172,20 @@ class MetricsDaemonTest : public testing::Test { // static MetricsDaemonTest* MetricsDaemonTest::daemon_test_ = NULL; -TEST_F(MetricsDaemonTest, LogDailyUseRecord) { +TEST_F(MetricsDaemonTest, LogDailyUseRecordBadFileLocation) { + // Checks that the daemon doesn't die badly if the file can't be + // created. + daemon_.daily_use_record_file_ = kDoesNotExistFile; + daemon_.LogDailyUseRecord(10, 20); + EXPECT_TRUE(LogContains("Unable to open the daily use file: " + "No such file or directory")); EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + file_util::Delete(FilePath(kDoesNotExistFile), false); +} + +TEST_F(MetricsDaemonTest, LogDailyUseRecordOnLogin) { + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.LogDailyUseRecord(/* day */ 5, /* seconds */ 120); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 120); @@ -190,12 +199,31 @@ TEST_F(MetricsDaemonTest, LogDailyUseRecord) { EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 360); EXPECT_EQ(5, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoMetricGenerated()); - - LogReset(); + ExpectDailyUseTimeMetric(/* sample */ 6); + daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 0); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + EXPECT_EQ(6, daemon_.daily_use_day_last_); +} + +TEST_F(MetricsDaemonTest, LogDailyUseRecordRoundDown) { + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + + daemon_.LogDailyUseRecord(/* day */ 7, /* seconds */ 89); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 89); + EXPECT_EQ(7, daemon_.daily_use_day_last_); + + ExpectDailyUseTimeMetric(/* sample */ 1); + daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 15); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 15); + EXPECT_EQ(6, daemon_.daily_use_day_last_); +} + +TEST_F(MetricsDaemonTest, LogDailyUseRecordRoundUp) { + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 0); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); - EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 6)); EXPECT_EQ(6, daemon_.daily_use_day_last_); // Tests rounding use time to the closest minute. @@ -203,27 +231,10 @@ TEST_F(MetricsDaemonTest, LogDailyUseRecord) { EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 90); EXPECT_EQ(6, daemon_.daily_use_day_last_); - LogReset(); + ExpectDailyUseTimeMetric(/* sample */ 2); daemon_.LogDailyUseRecord(/* day */ 7, /* seconds */ 89); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 89); - EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 2)); EXPECT_EQ(7, daemon_.daily_use_day_last_); - - LogReset(); - daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 15); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 15); - EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 1)); - EXPECT_EQ(6, daemon_.daily_use_day_last_); - - // Checks that the daemon doesn't die badly if the file can't be - // created. - LogReset(); - daemon_.daily_use_record_file_ = kDoesNotExistFile; - daemon_.LogDailyUseRecord(10, 20); - EXPECT_TRUE(LogContains("Unable to open the daily use file: " - "No such file or directory")); - EXPECT_EQ(6, daemon_.daily_use_day_last_); - file_util::Delete(FilePath(kDoesNotExistFile), false); } TEST_F(MetricsDaemonTest, LookupNetworkState) { @@ -320,7 +331,7 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); } -TEST_F(MetricsDaemonTest, NetStateChanged) { +TEST_F(MetricsDaemonTest, NetStateChangedSimpleDrop) { EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); EXPECT_EQ(0, daemon_.network_state_last_); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); @@ -329,14 +340,21 @@ TEST_F(MetricsDaemonTest, NetStateChanged) { EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); EXPECT_EQ(10, daemon_.network_state_last_); - EXPECT_TRUE(NoMetricGenerated()); + ExpectTimeToNetworkDropMetric(20); + daemon_.NetStateChanged("offline", /* now */ 30); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); + EXPECT_EQ(30, daemon_.network_state_last_); +} + +TEST_F(MetricsDaemonTest, NetStateChangedSuspend) { + EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); + EXPECT_EQ(0, daemon_.network_state_last_); + EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); daemon_.NetStateChanged("offline", /* now */ 30); EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); EXPECT_EQ(30, daemon_.network_state_last_); - EXPECT_TRUE(AssertTimeToNetworkDropMetric(/* sample */ 20)); - LogReset(); daemon_.NetStateChanged("online", /* now */ 60); EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); EXPECT_EQ(60, daemon_.network_state_last_); @@ -367,12 +385,10 @@ TEST_F(MetricsDaemonTest, NetStateChanged) { EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); EXPECT_EQ(105, daemon_.network_state_last_); - EXPECT_TRUE(NoMetricGenerated()); - + ExpectTimeToNetworkDropMetric(3); daemon_.NetStateChanged("offline", /* now */ 108); EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); EXPECT_EQ(108, daemon_.network_state_last_); - EXPECT_TRUE(AssertTimeToNetworkDropMetric(/* sample */ 3)); } TEST_F(MetricsDaemonTest, PowerStateChanged) { @@ -380,13 +396,13 @@ TEST_F(MetricsDaemonTest, PowerStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(0, daemon_.user_active_last_); EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.SetUserActiveState(/* active */ true, 7 * kSecondsPerDay + 15); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(7 * kSecondsPerDay + 15, daemon_.user_active_last_); EXPECT_EQ(7, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.PowerStateChanged("mem", 7 * kSecondsPerDay + 45); EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); @@ -405,14 +421,12 @@ TEST_F(MetricsDaemonTest, PowerStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(7 * kSecondsPerDay + 185, daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); - - EXPECT_TRUE(NoMetricGenerated()); } TEST_F(MetricsDaemonTest, PublishMetric) { + ExpectMetric("Dummy.Metric", 3, 1, 100, 50); daemon_.PublishMetric("Dummy.Metric", /* sample */ 3, /* min */ 1, /* max */ 100, /* buckets */ 50); - EXPECT_TRUE(AssertMetricGenerated("Dummy.Metric", 3, 1, 100, 50)); } TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { @@ -421,7 +435,7 @@ TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(0, daemon_.user_active_last_); EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.ScreenSaverStateChanged("locked", 5 * kSecondsPerDay + 10); EXPECT_EQ(MetricsDaemon::kScreenSaverStateLocked, @@ -429,14 +443,14 @@ TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(5 * kSecondsPerDay + 10, daemon_.user_active_last_); EXPECT_EQ(5, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.ScreenSaverStateChanged("unlocked", 5 * kSecondsPerDay + 100); EXPECT_EQ(MetricsDaemon::kScreenSaverStateUnlocked, daemon_.screensaver_state_); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(5 * kSecondsPerDay + 100, daemon_.user_active_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.ScreenSaverStateChanged("otherstate", 5 * kSecondsPerDay + 300); EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, @@ -444,8 +458,6 @@ TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(5 * kSecondsPerDay + 300, daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 200); - - EXPECT_TRUE(NoMetricGenerated()); } TEST_F(MetricsDaemonTest, SessionStateChanged) { @@ -453,14 +465,14 @@ TEST_F(MetricsDaemonTest, SessionStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(0, daemon_.user_active_last_); EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.SessionStateChanged("started", 15 * kSecondsPerDay + 20); EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(15 * kSecondsPerDay + 20, daemon_.user_active_last_); EXPECT_EQ(15, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.SessionStateChanged("stopped", 15 * kSecondsPerDay + 150); EXPECT_EQ(MetricsDaemon::kSessionStateStopped, daemon_.session_state_); @@ -473,27 +485,25 @@ TEST_F(MetricsDaemonTest, SessionStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(15 * kSecondsPerDay + 300, daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 15, /* seconds */ 130); - - EXPECT_TRUE(NoMetricGenerated()); } -TEST_F(MetricsDaemonTest, SetUserActiveState) { +TEST_F(MetricsDaemonTest, SetUserActiveStateSendOnLogin) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(0, daemon_.user_active_last_); EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.SetUserActiveState(/* active */ false, 5 * kSecondsPerDay + 10); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(5 * kSecondsPerDay + 10, daemon_.user_active_last_); EXPECT_EQ(5, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.SetUserActiveState(/* active */ true, 6 * kSecondsPerDay + 20); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(6 * kSecondsPerDay + 20, daemon_.user_active_last_); EXPECT_EQ(6, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); daemon_.SetUserActiveState(/* active */ true, 6 * kSecondsPerDay + 120); EXPECT_TRUE(daemon_.user_active_); @@ -507,17 +517,26 @@ TEST_F(MetricsDaemonTest, SetUserActiveState) { EXPECT_EQ(6, daemon_.daily_use_day_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 200); - EXPECT_TRUE(NoMetricGenerated()); - - LogReset(); + ExpectDailyUseTimeMetric(/* sample */ 3); daemon_.SetUserActiveState(/* active */ true, 8 * kSecondsPerDay - 300); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(8 * kSecondsPerDay - 300, daemon_.user_active_last_); EXPECT_EQ(7, daemon_.daily_use_day_last_); - EXPECT_TRUE(NoOrEmptyUseRecordFile()); - EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 3)); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); +} + +TEST_F(MetricsDaemonTest, SetUserActiveStateSendOnMonitor) { + EXPECT_FALSE(daemon_.user_active_); + EXPECT_EQ(0, daemon_.user_active_last_); + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + + daemon_.SetUserActiveState(/* active */ true, 8 * kSecondsPerDay - 300); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(8 * kSecondsPerDay - 300, daemon_.user_active_last_); + EXPECT_EQ(7, daemon_.daily_use_day_last_); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - LogReset(); daemon_.SetUserActiveState(/* active */ false, 8 * kSecondsPerDay + 300); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(8 * kSecondsPerDay + 300, daemon_.user_active_last_); @@ -530,15 +549,12 @@ TEST_F(MetricsDaemonTest, SetUserActiveState) { EXPECT_EQ(8, daemon_.daily_use_day_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 8, /* seconds */ 600); - EXPECT_TRUE(NoMetricGenerated()); - - LogReset(); + ExpectDailyUseTimeMetric(/* sample */ 10); daemon_.SetUserActiveState(/* active */ true, 9 * kSecondsPerDay + 400); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(9 * kSecondsPerDay + 400, daemon_.user_active_last_); EXPECT_EQ(9, daemon_.daily_use_day_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 9, /* seconds */ 800); - EXPECT_TRUE(AssertDailyUseTimeMetric(/* sample */ 10)); } int main(int argc, char **argv) { diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 95e78b227..2accb1ae8 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -121,6 +121,9 @@ static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer, return message_length; } +void MetricsLibrary::Init() { +} + // static bool MetricsLibrary::SendToAutotest(const string& name, int value) { FILE *autotest_file = fopen(kAutotestPath, "a+"); @@ -151,6 +154,11 @@ bool MetricsLibrary::SendToChrome(const string& name, int sample, return SendMessageToChrome(message_length, message); } +bool MetricsLibrary::SendToUMA(const string& name, int sample, + int min, int max, int nbuckets) { + return SendToChrome(name, sample, min, max, nbuckets); +} + //static bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample, int max) { @@ -167,3 +175,8 @@ bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample, // Send the message. return SendMessageToChrome(message_length, message); } + +bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, + int max) { + return SendEnumToChrome(name, sample, max); +} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 738396050..e5e9024c0 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -16,9 +16,21 @@ // TODO(sosa@chromium.org): Add testing for send methods -// Library used to send metrics both Autotest and Chrome. -class MetricsLibrary { +class MetricsLibraryInterface { public: + virtual void Init() = 0; + virtual bool SendToUMA(const std::string& name, int sample, + int min, int max, int nbuckets) = 0; + virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0; + virtual ~MetricsLibraryInterface() {} +}; + +// Library used to send metrics to both Autotest and Chrome/UMA. +class MetricsLibrary : public MetricsLibraryInterface { + public: + // Initializes the library. + void Init(); + // Sends histogram data to Chrome for transport to UMA and returns // true on success. This method results in the equivalent of an // asynchronous non-blocking RPC to UMA_HISTOGRAM_CUSTOM_COUNTS @@ -30,6 +42,10 @@ class MetricsLibrary { // |nbuckets| is the number of histogram buckets. // [0,min) is the implicit underflow bucket. // [|max|,infinity) is the implicit overflow bucket. + bool SendToUMA(const std::string& name, int sample, + int min, int max, int nbuckets); + + // Deprecated. static bool SendToChrome(const std::string& name, int sample, int min, int max, int nbuckets); @@ -42,6 +58,9 @@ class MetricsLibrary { // |max| is the maximum value of the histogram samples. // 0 is the implicit underflow bucket. // [|max|,infinity) is the implicit overflow bucket. + bool SendEnumToUMA(const std::string& name, int sample, int max); + + // Deprecated. static bool SendEnumToChrome(const std::string& name, int sample, int max); // Sends to Autotest and returns true on success. diff --git a/metrics/metrics_library_mock.h b/metrics/metrics_library_mock.h new file mode 100644 index 000000000..ffc934295 --- /dev/null +++ b/metrics/metrics_library_mock.h @@ -0,0 +1,23 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_LIBRARY_MOCK_H_ +#define METRICS_LIBRARY_MOCK_H_ + +#include + +#include "metrics_library.h" + +#include + +class MetricsLibraryMock : public MetricsLibraryInterface { + public: + MOCK_METHOD0(Init, void()); + MOCK_METHOD5(SendToUMA, bool(const std::string& name, int sample, + int min, int max, int nbuckets)); + MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample, + int max)); +}; + +#endif /* METRICS_LIBRARY_MOCK_H_ */ From 21cd2c5a9fdbeec3cc312943cc373f8c2dce3b1f Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 12 May 2010 15:26:16 -0700 Subject: [PATCH 011/200] Remove the deprecated static metrics APIs. Review URL: http://codereview.chromium.org/2037011 --- metrics/README | 11 +++-------- metrics/metrics_client.cc | 6 ++++-- metrics/metrics_library.cc | 27 ++++----------------------- metrics/metrics_library.h | 14 -------------- 4 files changed, 11 insertions(+), 47 deletions(-) diff --git a/metrics/README b/metrics/README index d8f17c444..6d011dbfd 100644 --- a/metrics/README +++ b/metrics/README @@ -42,14 +42,9 @@ a module, you need to do the following: int max) sends a sample for an enumeration (linear) histogram. - Currently, the API also includes two deprecated static methods: - - bool MetricsLibrary::SendToChrome(const std::string& name, int sample, - int min, int max, int nbuckets) - bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample, - int max) - - See the API documentation in metrics_library.h under + Before using these methods, a MetricsLibrary object needs to be + constructed and initialized through its Init method. See the + complete API documentation in metrics_library.h under src/platform/metrics/. - On the target platform, shortly after the sample is sent it should diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index ad137be99..e85c4d473 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -78,14 +78,16 @@ int main(int argc, char** argv) { } if (send_to_chrome) { + MetricsLibrary metrics_lib; + metrics_lib.Init(); if (send_enum) { int max = atoi(argv[name_index + 2]); - MetricsLibrary::SendEnumToChrome(name, sample, max); + metrics_lib.SendEnumToUMA(name, sample, max); } else { int min = atoi(argv[name_index + 2]); int max = atoi(argv[name_index + 3]); int nbuckets = atoi(argv[name_index + 4]); - MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets); + metrics_lib.SendToUMA(name, sample, min, max, nbuckets); } } return 0; diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 2accb1ae8..98415a839 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -2,13 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/* - * metrics_library.cc - * - * Created on: Dec 1, 2009 - * Author: sosa - */ - #include "metrics_library.h" #include @@ -137,9 +130,8 @@ bool MetricsLibrary::SendToAutotest(const string& name, int value) { return true; } -// static -bool MetricsLibrary::SendToChrome(const string& name, int sample, - int min, int max, int nbuckets) { +bool MetricsLibrary::SendToUMA(const string& name, int sample, + int min, int max, int nbuckets) { // Format the message. char message[kBufferSize]; int32_t message_length = @@ -154,14 +146,8 @@ bool MetricsLibrary::SendToChrome(const string& name, int sample, return SendMessageToChrome(message_length, message); } -bool MetricsLibrary::SendToUMA(const string& name, int sample, - int min, int max, int nbuckets) { - return SendToChrome(name, sample, min, max, nbuckets); -} - -//static -bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample, - int max) { +bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, + int max) { // Format the message. char message[kBufferSize]; int32_t message_length = @@ -175,8 +161,3 @@ bool MetricsLibrary::SendEnumToChrome(const std::string& name, int sample, // Send the message. return SendMessageToChrome(message_length, message); } - -bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, - int max) { - return SendEnumToChrome(name, sample, max); -} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index e5e9024c0..2a6412cb4 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -2,13 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -/* - * metrics_library.h - * - * Created on: Dec 1, 2009 - * Author: sosa - */ - #ifndef METRICS_LIBRARY_H_ #define METRICS_LIBRARY_H_ @@ -45,10 +38,6 @@ class MetricsLibrary : public MetricsLibraryInterface { bool SendToUMA(const std::string& name, int sample, int min, int max, int nbuckets); - // Deprecated. - static bool SendToChrome(const std::string& name, int sample, - int min, int max, int nbuckets); - // Sends linear histogram data to Chrome for transport to UMA and // returns true on success. This method results in the equivalent of // an asynchronous non-blocking RPC to UMA_HISTOGRAM_ENUMERATION @@ -60,9 +49,6 @@ class MetricsLibrary : public MetricsLibraryInterface { // [|max|,infinity) is the implicit overflow bucket. bool SendEnumToUMA(const std::string& name, int sample, int max); - // Deprecated. - static bool SendEnumToChrome(const std::string& name, int sample, int max); - // Sends to Autotest and returns true on success. static bool SendToAutotest(const std::string& name, int value); }; From c80dd92f5d7280eed0c14972f38e9f6c73d21705 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Fri, 14 May 2010 09:12:36 -0700 Subject: [PATCH 012/200] Per kmixter's suggestion, install metrics headers under /usr/include/metrics. Also, move file installation to the metrics ebuild. Review URL: http://codereview.chromium.org/2087002 --- metrics/Makefile | 9 --------- metrics/README | 2 +- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 16e1d9f4d..5ce0caff8 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -63,14 +63,5 @@ metrics_daemon_test.o: \ network_states.h \ power_states.h -install: - install $(CLIENT) $(DESTDIR)/usr/bin - install $(DAEMON) $(DESTDIR)/usr/bin - install $(LIB) $(DESTDIR)/usr/lib - install $(SHAREDLIB) $(DESTDIR)/usr/lib - install metrics_library.h $(DESTDIR)/usr/include - install syslog_parser.sh $(DESTDIR)/usr/bin - install omaha_tracker.sh $(DESTDIR)/usr/sbin - clean: rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) $(TESTDAEMON) *.o diff --git a/metrics/README b/metrics/README index 6d011dbfd..2c7072da4 100644 --- a/metrics/README +++ b/metrics/README @@ -28,7 +28,7 @@ a module, you need to do the following: preferred. - To access the metrics library API in the module, include the - header file. The file is installed in + header file. The file is installed in $SYSROOT/usr/include/ when the metrics library is built and installed. From 11b8eb3cf14564630c8efd20d52d42796cf3cb0e Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Tue, 18 May 2010 11:00:59 -0700 Subject: [PATCH 013/200] Add metrics library tests. Some metrics daemon API cleanup. Refactor the metrics daemon API a little so that we don't need to link in libmetrics into the daemon test binary. Review URL: http://codereview.chromium.org/2079007 --- metrics/Makefile | 17 +++++-- metrics/metrics_daemon.cc | 23 ++++----- metrics/metrics_daemon.h | 18 +++---- metrics/metrics_daemon_main.cc | 7 ++- metrics/metrics_daemon_test.cc | 14 +++--- metrics/metrics_library.cc | 36 ++++++------- metrics/metrics_library.h | 29 ++++++++++- metrics/metrics_library_test.cc | 89 +++++++++++++++++++++++++++++++++ 8 files changed, 177 insertions(+), 56 deletions(-) create mode 100644 metrics/metrics_library_test.cc diff --git a/metrics/Makefile b/metrics/Makefile index 5ce0caff8..e0c37414a 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -15,11 +15,15 @@ DAEMON = metrics_daemon DAEMON_TEST = metrics_daemon_test LIB = libmetrics.a SHAREDLIB = libmetrics.so +LIB_TEST = metrics_library_test CLIENT_OBJS = \ metrics_client.o LIB_OBJS = \ metrics_library.o +TESTLIB_OBJS = \ + metrics_library.o \ + metrics_library_test.o DAEMON_OBJS = \ metrics_daemon.o \ metrics_daemon_main.o @@ -27,12 +31,13 @@ TESTDAEMON_OBJS = \ metrics_daemon.o \ metrics_daemon_test.o -DAEMON_LDFLAGS = $(LDCONFIG) -lrt -lbase -lpthread -lgflags +DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags TESTDAEMON_LIBS = -lgmock -lgtest +TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) -tests: $(DAEMON_TEST) +tests: $(DAEMON_TEST) $(LIB_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) $(CXX) $(LDFLAGS) $^ -o $@ @@ -40,7 +45,7 @@ $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) $(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) -$(DAEMON_TEST): $(TESTDAEMON_OBJS) $(SHAREDLIB) +$(DAEMON_TEST): $(TESTDAEMON_OBJS) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(TESTDAEMON_LIBS) $(LIB): $(LIB_OBJS) @@ -49,6 +54,9 @@ $(LIB): $(LIB_OBJS) $(SHAREDLIB): $(LIB_OBJS) $(CXX) $(LDFLAGS) -shared $^ -o $@ +$(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) + $(CXX) -o $@ $^ $(LDFLAGS) $(TESTLIB_LIBS) + %.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ @@ -64,4 +72,5 @@ metrics_daemon_test.o: \ power_states.h clean: - rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) $(TESTDAEMON) *.o + rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) *.o + rm -f $(DAEMON_TEST) $(LIB_TEST) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index a66b570ca..6e0193243 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -100,9 +100,6 @@ const char* MetricsDaemon::kSessionStates_[] = { }; void MetricsDaemon::Run(bool run_as_daemon) { - MetricsLibrary metrics_lib; - metrics_lib.Init(); - Init(false, &metrics_lib); if (!run_as_daemon || daemon(0, 0) == 0) { Loop(); } @@ -223,10 +220,10 @@ void MetricsDaemon::NetStateChanged(const char* state_name, time_t now) { network_state_ == kNetworkStateOnline && power_state_ != kPowerStateMem) { int online_time = static_cast(now - network_state_last_); - PublishMetric(kMetricTimeToNetworkDropName, online_time, - kMetricTimeToNetworkDropMin, - kMetricTimeToNetworkDropMax, - kMetricTimeToNetworkDropBuckets); + SendMetric(kMetricTimeToNetworkDropName, online_time, + kMetricTimeToNetworkDropMin, + kMetricTimeToNetworkDropMax, + kMetricTimeToNetworkDropBuckets); } network_state_ = state; @@ -349,10 +346,10 @@ void MetricsDaemon::LogDailyUseRecord(int day, int seconds) { // the usage to the nearest minute and sends it to UMA. int minutes = (record.seconds_ + kSecondsPerMinute / 2) / kSecondsPerMinute; - PublishMetric(kMetricDailyUseTimeName, minutes, - kMetricDailyUseTimeMin, - kMetricDailyUseTimeMax, - kMetricDailyUseTimeBuckets); + SendMetric(kMetricDailyUseTimeName, minutes, + kMetricDailyUseTimeMin, + kMetricDailyUseTimeMax, + kMetricDailyUseTimeBuckets); // Truncates the usage file to ensure that no duplicate usage is // sent to UMA. @@ -445,8 +442,8 @@ void MetricsDaemon::UnscheduleUseMonitor() { usemon_interval_ = 0; } -void MetricsDaemon::PublishMetric(const char* name, int sample, - int min, int max, int nbuckets) { +void MetricsDaemon::SendMetric(const std::string& name, int sample, + int min, int max, int nbuckets) { DLOG(INFO) << "received metric: " << name << " " << sample << " " << min << " " << max << " " << nbuckets; metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 3aeaaaaf9..ba9e13c84 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -30,6 +30,9 @@ class MetricsDaemon { usemon_source_(NULL) {} ~MetricsDaemon() {} + // Initializes. + void Init(bool testing, MetricsLibraryInterface* metrics_lib); + // Does all the work. If |run_as_daemon| is true, daemonizes by // forking. void Run(bool run_as_daemon); @@ -48,8 +51,8 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, NetStateChangedSimpleDrop); FRIEND_TEST(MetricsDaemonTest, NetStateChangedSuspend); FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); - FRIEND_TEST(MetricsDaemonTest, PublishMetric); FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); + FRIEND_TEST(MetricsDaemonTest, SendMetric); FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateSendOnLogin); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateSendOnMonitor); @@ -119,9 +122,6 @@ class MetricsDaemon { // Array of user session states. static const char* kSessionStates_[kNumberSessionStates]; - // Initializes. - void Init(bool testing, MetricsLibraryInterface* metrics_lib); - // Creates the event loop and enters it. void Loop(); @@ -188,11 +188,11 @@ class MetricsDaemon { // Unschedules a scheduled use monitor, if any. void UnscheduleUseMonitor(); - // Sends a stat to Chrome for transport to UMA (or prints it for - // testing). See MetricsLibrary::SendToChrome in metrics_library.h - // for a description of the arguments. - void PublishMetric(const char* name, int sample, - int min, int max, int nbuckets); + // Sends a regular (exponential) histogram sample to Chrome for + // transport to UMA. See MetricsLibrary::SendToUMA in + // metrics_library.h for a description of the arguments. + void SendMetric(const std::string& name, int sample, + int min, int max, int nbuckets); // Test mode. bool testing_; diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 7ace3334d..f3f071425 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -10,7 +10,10 @@ DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); int main(int argc, char** argv) { - MetricsDaemon::MetricsDaemon d; google::ParseCommandLineFlags(&argc, &argv, true); - d.Run(FLAGS_daemon); + MetricsLibrary metrics_lib; + metrics_lib.Init(); + MetricsDaemon daemon; + daemon.Init(false, &metrics_lib); + daemon.Run(FLAGS_daemon); } diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 8180b143c..2edbeda9e 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -25,7 +25,9 @@ static const int kSecondsPerDay = 24 * 60 * 60; class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { + EXPECT_EQ(NULL, daemon_.daily_use_record_file_); daemon_.Init(true, &metrics_lib_); + EXPECT_TRUE(NULL != daemon_.daily_use_record_file_); daemon_.daily_use_record_file_ = kTestDailyUseRecordFile; // The test fixture object will be used by the log message handler. @@ -423,12 +425,6 @@ TEST_F(MetricsDaemonTest, PowerStateChanged) { EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); } -TEST_F(MetricsDaemonTest, PublishMetric) { - ExpectMetric("Dummy.Metric", 3, 1, 100, 50); - daemon_.PublishMetric("Dummy.Metric", /* sample */ 3, - /* min */ 1, /* max */ 100, /* buckets */ 50); -} - TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, daemon_.screensaver_state_); @@ -460,6 +456,12 @@ TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 200); } +TEST_F(MetricsDaemonTest, SendMetric) { + ExpectMetric("Dummy.Metric", 3, 1, 100, 50); + daemon_.SendMetric("Dummy.Metric", /* sample */ 3, + /* min */ 1, /* max */ 100, /* buckets */ 50); +} + TEST_F(MetricsDaemonTest, SessionStateChanged) { EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); EXPECT_FALSE(daemon_.user_active_); diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 98415a839..6ec72262d 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -16,16 +16,16 @@ static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; -static const char kChromePath[] = +static const char kUMAEventsPath[] = "/var/log/metrics/uma-events"; static const int32_t kBufferSize = 1024; using namespace std; // TODO(sosa@chromium.org) - use Chromium logger instead of stderr -static void PrintError(const char *message, const char *file, +static void PrintError(const char* message, const char* file, int code) { - const char *kProgramName = "metrics_library"; + static const char *kProgramName = "libmetrics"; if (code == 0) { fprintf(stderr, "%s: %s\n", kProgramName, message); } else if (file == NULL) { @@ -37,14 +37,16 @@ static void PrintError(const char *message, const char *file, } } -// Sends message of size |length| to Chrome and returns true on success. -static bool SendMessageToChrome(int32_t length, const char *message) { - int chrome_fd = open(kChromePath, +MetricsLibrary::MetricsLibrary() + : uma_events_file_(NULL) {} + +bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { + int chrome_fd = open(uma_events_file_, O_WRONLY | O_APPEND | O_CREAT, READ_WRITE_ALL_FILE_FLAGS); // If we failed to open it, return. if (chrome_fd < 0) { - PrintError("open", kChromePath, errno); + PrintError("open", uma_events_file_, errno); return false; } @@ -56,36 +58,28 @@ static bool SendMessageToChrome(int32_t length, const char *message) { // Grab an exclusive lock to protect Chrome from truncating // underneath us. Keep the file locked as briefly as possible. if (flock(chrome_fd, LOCK_EX) < 0) { - PrintError("flock", kChromePath, errno); + PrintError("flock", uma_events_file_, errno); close(chrome_fd); return false; } bool success = true; if (write(chrome_fd, message, length) != length) { - PrintError("write", kChromePath, errno); + PrintError("write", uma_events_file_, errno); success = false; } // Release the file lock and close file. if (flock(chrome_fd, LOCK_UN) < 0) { - PrintError("unlock", kChromePath, errno); + PrintError("unlock", uma_events_file_, errno); success = false; } close(chrome_fd); return success; } -// Formats a name/value message for Chrome in |buffer| and returns the -// length of the message or a negative value on error. -// -// Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 | -// -// The arbitrary |format| argument covers the non-LENGTH portion of the -// message. The caller is responsible to store the \0 character -// between NAME and VALUE (e.g. "%s%c%d", name, '\0', value). -static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer, - const char *format, ...) { +int32_t MetricsLibrary::FormatChromeMessage(int32_t buffer_size, char* buffer, + const char* format, ...) { int32_t message_length; size_t len_size = sizeof(message_length); @@ -115,9 +109,9 @@ static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer, } void MetricsLibrary::Init() { + uma_events_file_ = kUMAEventsPath; } -// static bool MetricsLibrary::SendToAutotest(const string& name, int value) { FILE *autotest_file = fopen(kAutotestPath, "a+"); if (autotest_file == NULL) { diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 2a6412cb4..8ae3f5b8c 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -5,9 +5,10 @@ #ifndef METRICS_LIBRARY_H_ #define METRICS_LIBRARY_H_ +#include #include -// TODO(sosa@chromium.org): Add testing for send methods +#include // for FRIEND_TEST class MetricsLibraryInterface { public: @@ -21,6 +22,8 @@ class MetricsLibraryInterface { // Library used to send metrics to both Autotest and Chrome/UMA. class MetricsLibrary : public MetricsLibraryInterface { public: + MetricsLibrary(); + // Initializes the library. void Init(); @@ -51,6 +54,30 @@ class MetricsLibrary : public MetricsLibraryInterface { // Sends to Autotest and returns true on success. static bool SendToAutotest(const std::string& name, int value); + + private: + friend class MetricsLibraryTest; + FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage); + FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong); + FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome); + FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation); + + // Sends message of size |length| to Chrome for transport to UMA and + // returns true on success. + bool SendMessageToChrome(int32_t length, const char* message); + + // Formats a name/value message for Chrome in |buffer| and returns the + // length of the message or a negative value on error. + // + // Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 | + // + // The arbitrary |format| argument covers the non-LENGTH portion of the + // message. The caller is responsible to store the \0 character + // between NAME and VALUE (e.g. "%s%c%d", name, '\0', value). + int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, + const char *format, ...); + + const char* uma_events_file_; }; #endif /* METRICS_LIBRARY_H_ */ diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc new file mode 100644 index 000000000..f585d3507 --- /dev/null +++ b/metrics/metrics_library_test.cc @@ -0,0 +1,89 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics_library.h" + +#include + +#include +#include + +static const FilePath kTestUMAEventsFile("test-uma-events"); + +class MetricsLibraryTest : public testing::Test { + protected: + virtual void SetUp() { + EXPECT_EQ(NULL, lib_.uma_events_file_); + lib_.Init(); + EXPECT_TRUE(NULL != lib_.uma_events_file_); + lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str(); + } + + virtual void TearDown() { + file_util::Delete(kTestUMAEventsFile, false); + } + + MetricsLibrary lib_; +}; + +TEST_F(MetricsLibraryTest, FormatChromeMessage) { + char buf[7]; + const int kLen = 6; + EXPECT_EQ(kLen, lib_.FormatChromeMessage(7, buf, "%d", 1)); + + char exp[kLen]; + sprintf(exp, "%c%c%c%c1", kLen, 0, 0, 0); + EXPECT_EQ(0, memcmp(exp, buf, kLen)); +} + +TEST_F(MetricsLibraryTest, FormatChromeMessageTooLong) { + char buf[7]; + EXPECT_EQ(-1, lib_.FormatChromeMessage(7, buf, "test")); +} + +TEST_F(MetricsLibraryTest, SendEnumToUMA) { + char buf[100]; + const int kLen = 40; + EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); + EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + + char exp[kLen]; + sprintf(exp, "%c%c%c%clinearhistogram%cTest.EnumMetric 1 3", + kLen, 0, 0, 0, 0); + EXPECT_EQ(0, memcmp(exp, buf, kLen)); +} + +TEST_F(MetricsLibraryTest, SendMessageToChrome) { + EXPECT_TRUE(lib_.SendMessageToChrome(4, "test")); + EXPECT_TRUE(lib_.SendMessageToChrome(7, "content")); + std::string uma_events; + EXPECT_TRUE(file_util::ReadFileToString(kTestUMAEventsFile, &uma_events)); + EXPECT_EQ("testcontent", uma_events); +} + +TEST_F(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation) { + // Checks that the library doesn't die badly if the file can't be + // created. + static const char kDoesNotExistFile[] = "/does/not/exist"; + lib_.uma_events_file_ = kDoesNotExistFile; + static const char kDummyMessage[] = "Dummy Message"; + EXPECT_FALSE(lib_.SendMessageToChrome(strlen(kDummyMessage), kDummyMessage)); + file_util::Delete(FilePath(kDoesNotExistFile), false); +} + +TEST_F(MetricsLibraryTest, SendToUMA) { + char buf[100]; + const int kLen = 37; + EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); + EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + + char exp[kLen]; + sprintf(exp, "%c%c%c%chistogram%cTest.Metric 2 1 100 50", kLen, 0, 0, 0, 0); + EXPECT_EQ(0, memcmp(exp, buf, kLen)); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 51a83dbe1f75e0ba72696027bf00732a6cb34fb5 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 26 May 2010 15:37:25 -0700 Subject: [PATCH 014/200] A script to print the hardware class (e.g., hwqual ID) of the device. This script will be used be Chrome's UMA service to add the "hardwareclass" field to the XML to be uploaded. Review URL: http://codereview.chromium.org/2289001 --- metrics/hardware_class | 56 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 metrics/hardware_class diff --git a/metrics/hardware_class b/metrics/hardware_class new file mode 100755 index 000000000..c99db9b9e --- /dev/null +++ b/metrics/hardware_class @@ -0,0 +1,56 @@ +#!/bin/sh + +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# This script prints the hardware class (e.g., the hardware +# qualification ID) of this device, or "unknown" if it can't determine +# the hardware class. + +# TODO(petkov): The hardware qualification ID is not available on +# systems yet, so the script uses alternative ways to identify +# different system classes (e.g., the WiFi adapter PCI vendor and +# device IDs). Switch the script to use real hardware qualification ID +# when that becomes available. + +# Appends a new component ID to the hardware class. Separates IDs with +# dashes. +append_class() { + [ -n "$HARDWARE_CLASS" ] && HARDWARE_CLASS="${HARDWARE_CLASS}-" + HARDWARE_CLASS="${HARDWARE_CLASS}$1" +} + +# Adds the CPU family, model and stepping info, if available, to the +# class. +cpu() { + [ -r /proc/cpuinfo ] || return + FAMILY=`grep -m1 '^cpu family' /proc/cpuinfo \ + | sed 's/cpu family\s\+:\s\+\([0-9]\+\)$/\1/'` + MODEL=`grep -m1 '^model' /proc/cpuinfo \ + | sed 's/model\s\+:\s\+\([0-9]\+\)$/\1/'` + STEPPING=`grep -m1 '^stepping' /proc/cpuinfo \ + | sed 's/stepping\s\+:\s\+\([0-9]\+\)$/\1/'` + if [ -n "$FAMILY" ] && [ -n "$MODEL" ] && [ -n "$STEPPING" ]; then + append_class "cpu/$FAMILY:$MODEL:$STEPPING" + fi +} + +# Adds the wlan0 PCI vendor and device ID, if available, to the class. +wlan() { + WLAN_DEV=/sys/class/net/wlan0/device + if [ -r $WLAN_DEV/vendor ] && [ -r $WLAN_DEV/device ]; then + WLAN_ID=`paste -d ':' $WLAN_DEV/vendor $WLAN_DEV/device | sed s/0x//g` + append_class "wlan0/$WLAN_ID" + fi +} + + +HARDWARE_CLASS= + +cpu +wlan + +[ -z "$HARDWARE_CLASS" ] && HARDWARE_CLASS=unknown + +echo $HARDWARE_CLASS From f1172ff67e7e8748b465cfc6c98c3bb97b4ee539 Mon Sep 17 00:00:00 2001 From: Chris Sosa Date: Thu, 27 May 2010 14:17:38 -0700 Subject: [PATCH 015/200] Add script to collect logs, dumps and other relevant information. Review URL: http://codereview.chromium.org/2103005 --- metrics/generate_logs | 82 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100755 metrics/generate_logs diff --git a/metrics/generate_logs b/metrics/generate_logs new file mode 100755 index 000000000..dfcdeea63 --- /dev/null +++ b/metrics/generate_logs @@ -0,0 +1,82 @@ +#!/bin/sh +# +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# Copies all logs, screen-shots, crash dumps to Downloads folder. + +# Help information about script usage +if ([ "$1" = -h ] || [ "$1" = -? ]); then + echo "Usage of generate_logs script:" + echo "To collect logs, dumps and screenshots: sh $0" + echo "To collect data and clear up folders: sh $0 --delete" + echo "Collected data is zipped to tar.bz2 file with timestamp in Downloads" + exit 1 +fi + +echo "Collecting logs for $USER user" +home_dir=/home/$USER/user + +# Creating folder to copy logs, screenshots and dumps +log_dir=$home_dir/Downloads/diagnostic_logs +mkdir -p $log_dir +sudo rm -rf $log_dir/* +echo "Created log folder in Downloads" + +# Copying file with current timestamp and date +date > $log_dir/timestamp.txt +echo "Copied current timestamp" + +# Copying lsb-release file +sudo cp /etc/lsb-release $log_dir +echo "Copied lsb-release file with version information" + +# Copying logs- windows manager, screen-locker, messages, session_manager +mkdir $log_dir/system_level_logs +cp -r /var/log/messages \ + /var/log/session_manager \ + /var/log/softwareupdate.log \ + /var/log/update_engine.log \ + /var/log/window_manager \ + $log_dir/system_level_logs +cp -rf $home_dir/log $log_dir/user_level_logs +echo "Copied relevant logs" + +# Copying screen-shots +cp -rf $home_dir/Downloads/Screenshots $log_dir/screenshots 2> /dev/null && \ + echo "Copied screen-shots" + +# Copying crash dumps and deleting from original location +cp -rf $home_dir/.config/google-chrome/Crash\ Reports $log_dir/crashdumps 2> \ + /dev/null && echo "Copied crash dumps" + +# Compressing the log folder with all collected files +currentdate=$(date +%m%d%y-%H%M%S) +tar cjfP $home_dir/Downloads/log-$currentdate.tar.bz2 $log_dir/ +sudo rm -rf $log_dir/* +echo "Files zipped to folder under Downloads : log-$currentdate.tar.bz2" + +# Deleting logs, dumps, screenshots from original location with flag -delete +if [ "$1" = --delete ]; then + echo "Deleting all logs " + sudo rm -rf /var/log/messages \ + /var/log/session_manager \ + /var/log/softwareupdate.log \ + /var/log/update_engine.log \ + /var/log/window_manager/* + sudo rm -rf $home_dir/log/* + + echo "Deleting crash dumps" + sudo rm -rf $home_dir/.config/google-chrome/Crash\ Reports/* + + echo "Deleting screen-shots" + sudo rm -rf $home_dir/Downloads/Screenshots/* + + # Reboot after deleting all files + echo "Rebooting system after clean up in 1 minute" + sudo shutdown -r 1 +else + echo "Logs and dumps are copied but not deleted." + echo "To clear all logs, run the script with --delete flag" +fi From f27f036d3149e130376b59458d7eb6c1a10a9bb7 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Fri, 4 Jun 2010 13:14:19 -0700 Subject: [PATCH 016/200] Update metrics_daemon to use base/time.h instead of time_t directly. Also, guard against time jumps in the active daily use metric collection. BUG=none TEST=unit tests; gmerge'd on the target device and inspected the logs. Review URL: http://codereview.chromium.org/2576005 --- metrics/metrics_daemon.cc | 47 +++--- metrics/metrics_daemon.h | 32 +++-- metrics/metrics_daemon_test.cc | 256 ++++++++++++++++++--------------- 3 files changed, 189 insertions(+), 146 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 6e0193243..ecbea052d 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -10,6 +10,10 @@ #include #include +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") #define DBUS_IFACE_CONNMAN_MANAGER "org.moblin.connman.Manager" #define DBUS_IFACE_POWER_MANAGER "org.chromium.Power.Manager" @@ -25,7 +29,6 @@ static const int kSecondsPerMinute = 60; static const int kMinutesPerHour = 60; static const int kHoursPerDay = 24; static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour; -static const int kSecondsPerDay = kMinutesPerDay * kSecondsPerMinute; // The daily use monitor is scheduled to a 1-minute interval after // initial user activity and then it's exponentially backed off to @@ -152,8 +155,9 @@ void MetricsDaemon::Loop() { DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, DBusMessage* message, void* user_data) { - time_t now = time(NULL); - DLOG(INFO) << "message intercepted @ " << now; + Time now = Time::Now(); + TimeTicks ticks = TimeTicks::Now(); + DLOG(INFO) << "message intercepted @ " << now.ToInternalValue(); int message_type = dbus_message_get_type(message); if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) { @@ -175,7 +179,7 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, char *state_name; dbus_message_iter_get_basic(&iter, &state_name); - daemon->NetStateChanged(state_name, now); + daemon->NetStateChanged(state_name, ticks); } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) { CHECK(strcmp(dbus_message_get_member(message), "PowerStateChanged") == 0); @@ -205,7 +209,7 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, return DBUS_HANDLER_RESULT_HANDLED; } -void MetricsDaemon::NetStateChanged(const char* state_name, time_t now) { +void MetricsDaemon::NetStateChanged(const char* state_name, TimeTicks ticks) { DLOG(INFO) << "network state: " << state_name; NetworkState state = LookupNetworkState(state_name); @@ -219,7 +223,8 @@ void MetricsDaemon::NetStateChanged(const char* state_name, time_t now) { if (state != kNetworkStateOnline && network_state_ == kNetworkStateOnline && power_state_ != kPowerStateMem) { - int online_time = static_cast(now - network_state_last_); + TimeDelta since_online = ticks - network_state_last_; + int online_time = static_cast(since_online.InSeconds()); SendMetric(kMetricTimeToNetworkDropName, online_time, kMetricTimeToNetworkDropMin, kMetricTimeToNetworkDropMax, @@ -227,7 +232,7 @@ void MetricsDaemon::NetStateChanged(const char* state_name, time_t now) { } network_state_ = state; - network_state_last_ = now; + network_state_last_ = ticks; } MetricsDaemon::NetworkState @@ -241,7 +246,7 @@ MetricsDaemon::LookupNetworkState(const char* state_name) { return kUnknownNetworkState; } -void MetricsDaemon::PowerStateChanged(const char* state_name, time_t now) { +void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) { DLOG(INFO) << "power state: " << state_name; power_state_ = LookupPowerState(state_name); @@ -260,8 +265,7 @@ MetricsDaemon::LookupPowerState(const char* state_name) { return kUnknownPowerState; } -void MetricsDaemon::ScreenSaverStateChanged(const char* state_name, - time_t now) { +void MetricsDaemon::ScreenSaverStateChanged(const char* state_name, Time now) { DLOG(INFO) << "screen-saver state: " << state_name; screensaver_state_ = LookupScreenSaverState(state_name); SetUserActiveState(screensaver_state_ == kScreenSaverStateUnlocked, now); @@ -278,8 +282,7 @@ MetricsDaemon::LookupScreenSaverState(const char* state_name) { return kUnknownScreenSaverState; } -void MetricsDaemon::SessionStateChanged(const char* state_name, - time_t now) { +void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) { DLOG(INFO) << "user session state: " << state_name; session_state_ = LookupSessionState(state_name); SetUserActiveState(session_state_ == kSessionStateStarted, now); @@ -296,13 +299,23 @@ MetricsDaemon::LookupSessionState(const char* state_name) { return kUnknownSessionState; } -void MetricsDaemon::SetUserActiveState(bool active, time_t now) { +void MetricsDaemon::SetUserActiveState(bool active, Time now) { DLOG(INFO) << "user: " << (active ? "active" : "inactive"); // Calculates the seconds of active use since the last update and - // the day since Epoch, and logs the usage data. - int seconds = user_active_ ? (now - user_active_last_) : 0; - int day = now / kSecondsPerDay; + // the day since Epoch, and logs the usage data. Guards against the + // time jumping back and forth due to the user changing it by + // discarding the new use time. + int seconds = 0; + if (user_active_ && now > user_active_last_) { + TimeDelta since_active = now - user_active_last_; + if (since_active < TimeDelta::FromSeconds( + kUseMonitorIntervalMax + kSecondsPerMinute)) { + seconds = static_cast(since_active.InSeconds()); + } + } + TimeDelta since_epoch = now - Time(); + int day = since_epoch.InDays(); LogDailyUseRecord(day, seconds); // Schedules a use monitor on inactive->active transitions and @@ -388,7 +401,7 @@ gboolean MetricsDaemon::UseMonitorStatic(gpointer data) { } bool MetricsDaemon::UseMonitor() { - SetUserActiveState(user_active_, time(NULL)); + SetUserActiveState(user_active_, Time::Now()); // If a new monitor source/instance is scheduled, returns false to // tell GLib to destroy this monitor source/instance. Returns true diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index ba9e13c84..835aca36a 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -7,11 +7,11 @@ #include #include -#include #include "metrics_library.h" #include // for FRIEND_TEST +#include class MetricsDaemon { @@ -19,12 +19,10 @@ class MetricsDaemon { MetricsDaemon() : daily_use_record_file_(NULL), network_state_(kUnknownNetworkState), - network_state_last_(0), power_state_(kUnknownPowerState), screensaver_state_(kUnknownScreenSaverState), session_state_(kUnknownSessionState), user_active_(false), - user_active_last_(0), daily_use_day_last_(0), usemon_interval_(0), usemon_source_(NULL) {} @@ -56,6 +54,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateSendOnLogin); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateSendOnMonitor); + FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateTimeJump); // The network states (see network_states.h). enum NetworkState { @@ -131,25 +130,25 @@ class MetricsDaemon { void* user_data); // Processes network state change. - void NetStateChanged(const char* state_name, time_t now); + void NetStateChanged(const char* state_name, base::TimeTicks ticks); // Given the state name, returns the state id. NetworkState LookupNetworkState(const char* state_name); // Processes power state change. - void PowerStateChanged(const char* state_name, time_t now); + void PowerStateChanged(const char* state_name, base::Time now); // Given the state name, returns the state id. PowerState LookupPowerState(const char* state_name); // Processes screen-saver state change. - void ScreenSaverStateChanged(const char* state_name, time_t now); + void ScreenSaverStateChanged(const char* state_name, base::Time now); // Given the state name, returns the state id. ScreenSaverState LookupScreenSaverState(const char* state_name); // Processes user session state change. - void SessionStateChanged(const char* state_name, time_t now); + void SessionStateChanged(const char* state_name, base::Time now); // Given the state name, returns the state id. SessionState LookupSessionState(const char* state_name); @@ -158,7 +157,10 @@ class MetricsDaemon { // since the last update. If the user has just become active, // reschedule the daily use monitor for more frequent updates -- // this is followed by an exponential back-off (see UseMonitor). - void SetUserActiveState(bool active, time_t now); + // While in active use, this method should be called at intervals no + // longer than kUseMonitorIntervalMax otherwise new use time will be + // discarded. + void SetUserActiveState(bool active, base::Time now); // Updates the daily usage file, if necessary, by adding |seconds| // of active use to the |day| since Epoch. If there's usage data for @@ -205,8 +207,10 @@ class MetricsDaemon { // Current network state. NetworkState network_state_; - // Timestamps last network state update. - time_t network_state_last_; + // Timestamps last network state update. This timestamp is used to + // sample the time from the network going online to going offline so + // TimeTicks ensures a monotonically increasing TimeDelta. + base::TimeTicks network_state_last_; // Current power state. PowerState power_state_; @@ -221,10 +225,12 @@ class MetricsDaemon { // started, screen is not locked. bool user_active_; - // Timestamps last user active update. - time_t user_active_last_; + // Timestamps last user active update. Active use time is + // aggregated each day before sending to UMA so using time since the + // epoch as the timestamp. + base::Time user_active_last_; - // Last stored daily use day (since epoch). + // Last stored daily use day (since the epoch). int daily_use_day_last_; // Sleep period until the next daily usage aggregation performed by diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 2edbeda9e..79be3dd58 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -13,6 +13,8 @@ #include #include +using base::Time; +using base::TimeTicks; using ::testing::Mock; using ::testing::Return; using ::testing::StrictMock; @@ -22,13 +24,45 @@ static const char kDoesNotExistFile[] = "/does/not/exist"; static const int kSecondsPerDay = 24 * 60 * 60; +// This class allows a TimeTicks object to be initialized with seconds +// (rather than microseconds) through the protected TimeTicks(int64) +// constructor. +class TestTicks : public TimeTicks { + public: + TestTicks(int64 seconds) + : TimeTicks(seconds * Time::kMicrosecondsPerSecond) {} +}; + +// Overloaded for test failure printing purposes. +static std::ostream& operator<<(std::ostream& o, const TimeTicks& ticks) { + o << ticks.ToInternalValue() << "us"; + return o; +}; + +// Overloaded for test failure printing purposes. +static std::ostream& operator<<(std::ostream& o, const Time& time) { + o << time.ToInternalValue() << "us"; + return o; +}; + class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { EXPECT_EQ(NULL, daemon_.daily_use_record_file_); daemon_.Init(true, &metrics_lib_); + + // Tests constructor initialization. Switches to a test daily use + // record file. EXPECT_TRUE(NULL != daemon_.daily_use_record_file_); daemon_.daily_use_record_file_ = kTestDailyUseRecordFile; + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + EXPECT_EQ(0, daemon_.daily_use_day_last_); + EXPECT_FALSE(daemon_.user_active_); + EXPECT_TRUE(daemon_.user_active_last_.is_null()); + EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); + EXPECT_TRUE(daemon_.network_state_last_.is_null()); + EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); + EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); // The test fixture object will be used by the log message handler. daemon_test_ = this; @@ -84,6 +118,10 @@ class MetricsDaemonTest : public testing::Test { MetricsDaemon::kMetricTimeToNetworkDropBuckets); } + Time TestTime(int64 seconds) { + return Time::FromInternalValue(seconds * Time::kMicrosecondsPerSecond); + } + // Asserts that the daily use record file contains the specified // contents. testing::AssertionResult AssertDailyUseRecord(const char* expr_day, @@ -186,9 +224,6 @@ TEST_F(MetricsDaemonTest, LogDailyUseRecordBadFileLocation) { } TEST_F(MetricsDaemonTest, LogDailyUseRecordOnLogin) { - EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.LogDailyUseRecord(/* day */ 5, /* seconds */ 120); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 120); EXPECT_EQ(5, daemon_.daily_use_day_last_); @@ -208,9 +243,6 @@ TEST_F(MetricsDaemonTest, LogDailyUseRecordOnLogin) { } TEST_F(MetricsDaemonTest, LogDailyUseRecordRoundDown) { - EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.LogDailyUseRecord(/* day */ 7, /* seconds */ 89); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 89); EXPECT_EQ(7, daemon_.daily_use_day_last_); @@ -222,9 +254,6 @@ TEST_F(MetricsDaemonTest, LogDailyUseRecordRoundDown) { } TEST_F(MetricsDaemonTest, LogDailyUseRecordRoundUp) { - EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 0); EXPECT_EQ(6, daemon_.daily_use_day_last_); @@ -334,125 +363,103 @@ TEST_F(MetricsDaemonTest, MessageFilter) { } TEST_F(MetricsDaemonTest, NetStateChangedSimpleDrop) { - EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); - EXPECT_EQ(0, daemon_.network_state_last_); - EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); - - daemon_.NetStateChanged("online", /* now */ 10); + daemon_.NetStateChanged("online", TestTicks(10)); EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); - EXPECT_EQ(10, daemon_.network_state_last_); + EXPECT_EQ(TestTicks(10), daemon_.network_state_last_); ExpectTimeToNetworkDropMetric(20); - daemon_.NetStateChanged("offline", /* now */ 30); + daemon_.NetStateChanged("offline", TestTicks(30)); EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(30, daemon_.network_state_last_); + EXPECT_EQ(TestTicks(30), daemon_.network_state_last_); } TEST_F(MetricsDaemonTest, NetStateChangedSuspend) { + daemon_.NetStateChanged("offline", TestTicks(30)); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); + EXPECT_EQ(TestTicks(30), daemon_.network_state_last_); + + daemon_.NetStateChanged("online", TestTicks(60)); + EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); + EXPECT_EQ(TestTicks(60), daemon_.network_state_last_); + + daemon_.power_state_ = MetricsDaemon::kPowerStateMem; + daemon_.NetStateChanged("offline", TestTicks(85)); + EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); + EXPECT_EQ(TestTicks(85), daemon_.network_state_last_); + + daemon_.NetStateChanged("somestate", TestTicks(90)); EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); - EXPECT_EQ(0, daemon_.network_state_last_); - EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); + EXPECT_EQ(TestTicks(90), daemon_.network_state_last_); - daemon_.NetStateChanged("offline", /* now */ 30); + daemon_.NetStateChanged("offline", TestTicks(95)); EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(30, daemon_.network_state_last_); + EXPECT_EQ(TestTicks(95), daemon_.network_state_last_); - daemon_.NetStateChanged("online", /* now */ 60); + daemon_.power_state_ = MetricsDaemon::kPowerStateOn; + daemon_.NetStateChanged("online", TestTicks(105)); EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); - EXPECT_EQ(60, daemon_.network_state_last_); - - daemon_.PowerStateChanged("mem", /* now */ 80); - EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); - EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); - EXPECT_EQ(60, daemon_.network_state_last_); - - daemon_.NetStateChanged("offline", /* now */ 85); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(85, daemon_.network_state_last_); - - daemon_.NetStateChanged("somestate", /* now */ 90); - EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); - EXPECT_EQ(90, daemon_.network_state_last_); - - daemon_.NetStateChanged("offline", /* now */ 95); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(95, daemon_.network_state_last_); - - daemon_.PowerStateChanged("on", /* now */ 100); - EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(95, daemon_.network_state_last_); - - daemon_.NetStateChanged("online", /* now */ 105); - EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); - EXPECT_EQ(105, daemon_.network_state_last_); + EXPECT_EQ(TestTicks(105), daemon_.network_state_last_); ExpectTimeToNetworkDropMetric(3); - daemon_.NetStateChanged("offline", /* now */ 108); + daemon_.NetStateChanged("offline", TestTicks(108)); EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(108, daemon_.network_state_last_); + EXPECT_EQ(TestTicks(108), daemon_.network_state_last_); } TEST_F(MetricsDaemonTest, PowerStateChanged) { - EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(0, daemon_.user_active_last_); - EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - - daemon_.SetUserActiveState(/* active */ true, 7 * kSecondsPerDay + 15); + daemon_.SetUserActiveState(/* active */ true, + TestTime(7 * kSecondsPerDay + 15)); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(7 * kSecondsPerDay + 15, daemon_.user_active_last_); + EXPECT_EQ(TestTime(7 * kSecondsPerDay + 15), daemon_.user_active_last_); EXPECT_EQ(7, daemon_.daily_use_day_last_); EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.PowerStateChanged("mem", 7 * kSecondsPerDay + 45); + daemon_.PowerStateChanged("mem", TestTime(7 * kSecondsPerDay + 45)); EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(7 * kSecondsPerDay + 45, daemon_.user_active_last_); + EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); - daemon_.PowerStateChanged("on", 7 * kSecondsPerDay + 85); + daemon_.PowerStateChanged("on", TestTime(7 * kSecondsPerDay + 85)); EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(7 * kSecondsPerDay + 45, daemon_.user_active_last_); + EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); - daemon_.PowerStateChanged("otherstate", 7 * kSecondsPerDay + 185); + daemon_.PowerStateChanged("otherstate", TestTime(7 * kSecondsPerDay + 185)); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(7 * kSecondsPerDay + 185, daemon_.user_active_last_); + EXPECT_EQ(TestTime(7 * kSecondsPerDay + 185), daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); } TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, daemon_.screensaver_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(0, daemon_.user_active_last_); - EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.ScreenSaverStateChanged("locked", 5 * kSecondsPerDay + 10); + daemon_.ScreenSaverStateChanged("locked", + TestTime(5 * kSecondsPerDay + 10)); EXPECT_EQ(MetricsDaemon::kScreenSaverStateLocked, daemon_.screensaver_state_); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(5 * kSecondsPerDay + 10, daemon_.user_active_last_); + EXPECT_EQ(TestTime(5 * kSecondsPerDay + 10), daemon_.user_active_last_); EXPECT_EQ(5, daemon_.daily_use_day_last_); EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.ScreenSaverStateChanged("unlocked", 5 * kSecondsPerDay + 100); + daemon_.ScreenSaverStateChanged("unlocked", + TestTime(5 * kSecondsPerDay + 100)); EXPECT_EQ(MetricsDaemon::kScreenSaverStateUnlocked, daemon_.screensaver_state_); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(5 * kSecondsPerDay + 100, daemon_.user_active_last_); + EXPECT_EQ(TestTime(5 * kSecondsPerDay + 100), daemon_.user_active_last_); EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.ScreenSaverStateChanged("otherstate", 5 * kSecondsPerDay + 300); + daemon_.ScreenSaverStateChanged("otherstate", + TestTime(5 * kSecondsPerDay + 300)); EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, daemon_.screensaver_state_); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(5 * kSecondsPerDay + 300, daemon_.user_active_last_); + EXPECT_EQ(TestTime(5 * kSecondsPerDay + 300), daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 200); } @@ -463,100 +470,117 @@ TEST_F(MetricsDaemonTest, SendMetric) { } TEST_F(MetricsDaemonTest, SessionStateChanged) { - EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(0, daemon_.user_active_last_); - EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - - daemon_.SessionStateChanged("started", 15 * kSecondsPerDay + 20); + daemon_.SessionStateChanged("started", TestTime(15 * kSecondsPerDay + 20)); EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(15 * kSecondsPerDay + 20, daemon_.user_active_last_); + EXPECT_EQ(TestTime(15 * kSecondsPerDay + 20), daemon_.user_active_last_); EXPECT_EQ(15, daemon_.daily_use_day_last_); EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.SessionStateChanged("stopped", 15 * kSecondsPerDay + 150); + daemon_.SessionStateChanged("stopped", TestTime(15 * kSecondsPerDay + 150)); EXPECT_EQ(MetricsDaemon::kSessionStateStopped, daemon_.session_state_); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(15 * kSecondsPerDay + 150, daemon_.user_active_last_); + EXPECT_EQ(TestTime(15 * kSecondsPerDay + 150), daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 15, /* seconds */ 130); - daemon_.SessionStateChanged("otherstate", 15 * kSecondsPerDay + 300); + daemon_.SessionStateChanged("otherstate", + TestTime(15 * kSecondsPerDay + 300)); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(15 * kSecondsPerDay + 300, daemon_.user_active_last_); + EXPECT_EQ(TestTime(15 * kSecondsPerDay + 300), daemon_.user_active_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 15, /* seconds */ 130); } TEST_F(MetricsDaemonTest, SetUserActiveStateSendOnLogin) { + daemon_.SetUserActiveState(/* active */ false, + TestTime(5 * kSecondsPerDay + 10)); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(0, daemon_.user_active_last_); - EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - - daemon_.SetUserActiveState(/* active */ false, 5 * kSecondsPerDay + 10); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(5 * kSecondsPerDay + 10, daemon_.user_active_last_); + EXPECT_EQ(TestTime(5 * kSecondsPerDay + 10), daemon_.user_active_last_); EXPECT_EQ(5, daemon_.daily_use_day_last_); EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.SetUserActiveState(/* active */ true, 6 * kSecondsPerDay + 20); + daemon_.SetUserActiveState(/* active */ true, + TestTime(6 * kSecondsPerDay + 20)); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(6 * kSecondsPerDay + 20, daemon_.user_active_last_); + EXPECT_EQ(TestTime(6 * kSecondsPerDay + 20), daemon_.user_active_last_); EXPECT_EQ(6, daemon_.daily_use_day_last_); EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.SetUserActiveState(/* active */ true, 6 * kSecondsPerDay + 120); + daemon_.SetUserActiveState(/* active */ true, + TestTime(6 * kSecondsPerDay + 120)); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(6 * kSecondsPerDay + 120, daemon_.user_active_last_); + EXPECT_EQ(TestTime(6 * kSecondsPerDay + 120), daemon_.user_active_last_); EXPECT_EQ(6, daemon_.daily_use_day_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 100); - daemon_.SetUserActiveState(/* active */ false, 6 * kSecondsPerDay + 220); + daemon_.SetUserActiveState(/* active */ false, + TestTime(6 * kSecondsPerDay + 220)); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(6 * kSecondsPerDay + 220, daemon_.user_active_last_); + EXPECT_EQ(TestTime(6 * kSecondsPerDay + 220), daemon_.user_active_last_); EXPECT_EQ(6, daemon_.daily_use_day_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 200); ExpectDailyUseTimeMetric(/* sample */ 3); - daemon_.SetUserActiveState(/* active */ true, 8 * kSecondsPerDay - 300); + daemon_.SetUserActiveState(/* active */ true, + TestTime(8 * kSecondsPerDay - 300)); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(8 * kSecondsPerDay - 300, daemon_.user_active_last_); + EXPECT_EQ(TestTime(8 * kSecondsPerDay - 300), daemon_.user_active_last_); EXPECT_EQ(7, daemon_.daily_use_day_last_); EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); } TEST_F(MetricsDaemonTest, SetUserActiveStateSendOnMonitor) { - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(0, daemon_.user_active_last_); - EXPECT_EQ(0, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - - daemon_.SetUserActiveState(/* active */ true, 8 * kSecondsPerDay - 300); + daemon_.SetUserActiveState(/* active */ true, + TestTime(8 * kSecondsPerDay - 300)); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(8 * kSecondsPerDay - 300, daemon_.user_active_last_); + EXPECT_EQ(TestTime(8 * kSecondsPerDay - 300), daemon_.user_active_last_); EXPECT_EQ(7, daemon_.daily_use_day_last_); EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - daemon_.SetUserActiveState(/* active */ false, 8 * kSecondsPerDay + 300); + daemon_.SetUserActiveState(/* active */ false, + TestTime(8 * kSecondsPerDay + 300)); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(8 * kSecondsPerDay + 300, daemon_.user_active_last_); + EXPECT_EQ(TestTime(8 * kSecondsPerDay + 300), daemon_.user_active_last_); EXPECT_EQ(8, daemon_.daily_use_day_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 8, /* seconds */ 600); - daemon_.SetUserActiveState(/* active */ true, 9 * kSecondsPerDay - 400); + daemon_.SetUserActiveState(/* active */ true, + TestTime(9 * kSecondsPerDay - 200)); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(9 * kSecondsPerDay - 400, daemon_.user_active_last_); + EXPECT_EQ(TestTime(9 * kSecondsPerDay - 200), daemon_.user_active_last_); EXPECT_EQ(8, daemon_.daily_use_day_last_); EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 8, /* seconds */ 600); ExpectDailyUseTimeMetric(/* sample */ 10); - daemon_.SetUserActiveState(/* active */ true, 9 * kSecondsPerDay + 400); + daemon_.SetUserActiveState(/* active */ true, + TestTime(9 * kSecondsPerDay + 200)); EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(9 * kSecondsPerDay + 400, daemon_.user_active_last_); + EXPECT_EQ(TestTime(9 * kSecondsPerDay + 200), daemon_.user_active_last_); EXPECT_EQ(9, daemon_.daily_use_day_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 9, /* seconds */ 800); + EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 9, /* seconds */ 400); +} + +TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) { + daemon_.SetUserActiveState(/* active */ true, + TestTime(10 * kSecondsPerDay + 500)); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(TestTime(10 * kSecondsPerDay + 500), daemon_.user_active_last_); + EXPECT_EQ(10, daemon_.daily_use_day_last_); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + + daemon_.SetUserActiveState(/* active */ true, + TestTime(10 * kSecondsPerDay + 300)); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(TestTime(10 * kSecondsPerDay + 300), daemon_.user_active_last_); + EXPECT_EQ(10, daemon_.daily_use_day_last_); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + + daemon_.SetUserActiveState(/* active */ true, + TestTime(10 * kSecondsPerDay + 1000)); + EXPECT_TRUE(daemon_.user_active_); + EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_); + EXPECT_EQ(10, daemon_.daily_use_day_last_); + EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); } int main(int argc, char **argv) { From e334840fb37dd8067b785408eb03dffe485edb47 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Fri, 4 Jun 2010 14:07:41 -0700 Subject: [PATCH 017/200] s/org.moblin.connman/org.chromium.flimflam/ Review URL: http://codereview.chromium.org/2664003 --- metrics/metrics_daemon.cc | 8 ++++---- metrics/metrics_daemon_test.cc | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index ecbea052d..523d8bd21 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -15,7 +15,7 @@ using base::TimeDelta; using base::TimeTicks; #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") -#define DBUS_IFACE_CONNMAN_MANAGER "org.moblin.connman.Manager" +#define DBUS_IFACE_FLIMFLAM_MANAGER "org.chromium.flimflam.Manager" #define DBUS_IFACE_POWER_MANAGER "org.chromium.Power.Manager" #define DBUS_IFACE_SCREENSAVER_MANAGER "org.chromium.ScreenSaver.Manager" #define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface" @@ -56,8 +56,8 @@ const int MetricsDaemon::kMetricTimeToNetworkDropBuckets = 50; // static const char* MetricsDaemon::kDBusMatches_[] = { "type='signal'," - "sender='org.moblin.connman'," - "interface='" DBUS_IFACE_CONNMAN_MANAGER "'," + "sender='org.chromium.flimflam'," + "interface='" DBUS_IFACE_FLIMFLAM_MANAGER "'," "path='/'," "member='StateChanged'", @@ -173,7 +173,7 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, DBusMessageIter iter; dbus_message_iter_init(message, &iter); - if (strcmp(interface, DBUS_IFACE_CONNMAN_MANAGER) == 0) { + if (strcmp(interface, DBUS_IFACE_FLIMFLAM_MANAGER) == 0) { CHECK(strcmp(dbus_message_get_member(message), "StateChanged") == 0); diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 79be3dd58..5614eed0c 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -312,7 +312,7 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); msg = NewDBusSignalString("/", - "org.moblin.connman.Manager", + "org.chromium.flimflam.Manager", "StateChanged", "online"); EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); From 3b3add5ad213936f0f78dde7e2f2239086db6d50 Mon Sep 17 00:00:00 2001 From: David James Date: Fri, 4 Jun 2010 15:01:19 -0700 Subject: [PATCH 018/200] Cleanup style nits in metrics daemon. - Remove trailing spaces. - Convert 'char *' to 'char*' - Fix other minor style nits - Many of these issues were pointed out by tfarina. BUG=none TEST=Checked that it compiles and passes tests. Review URL: http://codereview.chromium.org/2693001 --- metrics/metrics_daemon.cc | 12 ++++++------ metrics/metrics_daemon_test.cc | 2 +- metrics/metrics_library.cc | 6 +++--- metrics/metrics_library.h | 4 ++-- metrics/syslog_parser.sh | 16 ++++++++-------- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 523d8bd21..04ad68631 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -125,14 +125,14 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { DBusError error; dbus_error_init(&error); - DBusConnection *connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); LOG_IF(FATAL, dbus_error_is_set(&error)) << "No D-Bus connection: " << SAFE_MESSAGE(error); dbus_connection_setup_with_g_main(connection, NULL); // Registers D-Bus matches for the signals we would like to catch. - for (unsigned int m = 0; m < sizeof(kDBusMatches_) / sizeof(char *); m++) { + for (unsigned int m = 0; m < sizeof(kDBusMatches_) / sizeof(char*); m++) { const char* match = kDBusMatches_[m]; DLOG(INFO) << "adding dbus match: " << match; dbus_bus_add_match(connection, match, &error); @@ -177,28 +177,28 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, CHECK(strcmp(dbus_message_get_member(message), "StateChanged") == 0); - char *state_name; + char* state_name; dbus_message_iter_get_basic(&iter, &state_name); daemon->NetStateChanged(state_name, ticks); } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) { CHECK(strcmp(dbus_message_get_member(message), "PowerStateChanged") == 0); - char *state_name; + char* state_name; dbus_message_iter_get_basic(&iter, &state_name); daemon->PowerStateChanged(state_name, now); } else if (strcmp(interface, DBUS_IFACE_SCREENSAVER_MANAGER) == 0) { CHECK(strcmp(dbus_message_get_member(message), "LockStateChanged") == 0); - char *state_name; + char* state_name; dbus_message_iter_get_basic(&iter, &state_name); daemon->ScreenSaverStateChanged(state_name, now); } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) { CHECK(strcmp(dbus_message_get_member(message), "SessionStateChanged") == 0); - char *state_name; + char* state_name; dbus_message_iter_get_basic(&iter, &state_name); daemon->SessionStateChanged(state_name, now); } else { diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 5614eed0c..31f079d45 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -583,7 +583,7 @@ TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) { EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); } -int main(int argc, char **argv) { +int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 6ec72262d..e4087ef37 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -20,12 +20,12 @@ static const char kUMAEventsPath[] = "/var/log/metrics/uma-events"; static const int32_t kBufferSize = 1024; -using namespace std; +using std::string; // TODO(sosa@chromium.org) - use Chromium logger instead of stderr static void PrintError(const char* message, const char* file, int code) { - static const char *kProgramName = "libmetrics"; + static const char kProgramName[] = "libmetrics"; if (code == 0) { fprintf(stderr, "%s: %s\n", kProgramName, message); } else if (file == NULL) { @@ -113,7 +113,7 @@ void MetricsLibrary::Init() { } bool MetricsLibrary::SendToAutotest(const string& name, int value) { - FILE *autotest_file = fopen(kAutotestPath, "a+"); + FILE* autotest_file = fopen(kAutotestPath, "a+"); if (autotest_file == NULL) { PrintError("fopen", kAutotestPath, errno); return false; diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 8ae3f5b8c..a9ac0bb10 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -75,9 +75,9 @@ class MetricsLibrary : public MetricsLibraryInterface { // message. The caller is responsible to store the \0 character // between NAME and VALUE (e.g. "%s%c%d", name, '\0', value). int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, - const char *format, ...); + const char* format, ...); const char* uma_events_file_; }; -#endif /* METRICS_LIBRARY_H_ */ +#endif // METRICS_LIBRARY_H_ diff --git a/metrics/syslog_parser.sh b/metrics/syslog_parser.sh index 7f3003ff4..7d064be96 100755 --- a/metrics/syslog_parser.sh +++ b/metrics/syslog_parser.sh @@ -12,7 +12,7 @@ # "Sectors" to METRIC_NAME for the two available measurements, uptime # and number of sectors read thus far. -# You will need to emit messages similar to the following in order to add a +# You will need to emit messages similar to the following in order to add a # a metric using this process. You will need to emit both a start and stop # time and the metric reported will be the difference in values @@ -36,25 +36,25 @@ do if [ $first -eq 1 ] then first=0 - program_name=$m + program_name=$m else first=1 - metrics_name=$m - + metrics_name=$m + # Example of line from /var/log/messages: # Nov 15 08:05:42 localhost connmand[822]: start metric time 12 sectors 56 # "upstart:" is $5, 1234 is $9, etc. program="${program}/$program_name([[0-9]+]:|:) start $metrics_name/\ { metrics_start[\"${metrics_name}Time\"] = \$9; - metrics_start[\"${metrics_name}Sectors\"] = \$11; + metrics_start[\"${metrics_name}Sectors\"] = \$11; }" program="${program}/$program_name([[0-9]+]:|:) stop $metrics_name/\ - { + { metrics_stop[\"${metrics_name}Time\"] = \$9; metrics_stop[\"${metrics_name}Sectors\"] = \$11; }" - fi + fi done # Do all the differencing here @@ -66,4 +66,4 @@ END{ } }" -exec awk "$program" /var/log/syslog \ No newline at end of file +exec awk "$program" /var/log/syslog From 55188f5f9fde971c44cf9a2fa4eca034b12da764 Mon Sep 17 00:00:00 2001 From: Anush Elangovan Date: Sat, 5 Jun 2010 13:12:30 -0700 Subject: [PATCH 019/200] Setup code review inheritance Change-Id: I068e0f67cb89140526cc4df6f868d0d74b9c1bee --- metrics/inherit-review-settings-ok | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 metrics/inherit-review-settings-ok diff --git a/metrics/inherit-review-settings-ok b/metrics/inherit-review-settings-ok new file mode 100644 index 000000000..e69de29bb From 6bf6e2530d1739a33b43ac8d8e654087391e6afa Mon Sep 17 00:00:00 2001 From: David James Date: Sun, 6 Jun 2010 18:52:40 -0700 Subject: [PATCH 020/200] Update metrics daemon to use new power manager signals for locking (4 of 7). Co-dependent with the following change: - Issue 2685003: Update XScreenSaver to use new power manager signals for locking (3 of 7). TEST=Ran test suite. BUG=chromium-os:3694 Review URL: http://codereview.chromium.org/2698002 --- metrics/metrics_daemon.cc | 57 +++++++--------------------------- metrics/metrics_daemon.h | 21 ------------- metrics/metrics_daemon_test.cc | 53 ++++--------------------------- metrics/screensaver_states.h | 17 ---------- 4 files changed, 18 insertions(+), 130 deletions(-) delete mode 100644 metrics/screensaver_states.h diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 04ad68631..5ccc69487 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -16,8 +16,7 @@ using base::TimeTicks; #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") #define DBUS_IFACE_FLIMFLAM_MANAGER "org.chromium.flimflam.Manager" -#define DBUS_IFACE_POWER_MANAGER "org.chromium.Power.Manager" -#define DBUS_IFACE_SCREENSAVER_MANAGER "org.chromium.ScreenSaver.Manager" +#define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager" #define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface" // File to aggregate daily usage before sending to UMA. @@ -63,13 +62,7 @@ const char* MetricsDaemon::kDBusMatches_[] = { "type='signal'," "interface='" DBUS_IFACE_POWER_MANAGER "'," - "path='/'," - "member='PowerStateChanged'", - - "type='signal'," - "interface='" DBUS_IFACE_SCREENSAVER_MANAGER "'," - "path='/'," - "member='LockStateChanged'", + "path='/'" "type='signal'," "sender='org.chromium.SessionManager'," @@ -90,12 +83,6 @@ const char* MetricsDaemon::kPowerStates_[] = { #include "power_states.h" }; -// static -const char* MetricsDaemon::kScreenSaverStates_[] = { -#define STATE(name, capname) #name, -#include "screensaver_states.h" -}; - // static const char* MetricsDaemon::kSessionStates_[] = { #define STATE(name, capname) #name, @@ -181,19 +168,16 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, dbus_message_iter_get_basic(&iter, &state_name); daemon->NetStateChanged(state_name, ticks); } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) { - CHECK(strcmp(dbus_message_get_member(message), - "PowerStateChanged") == 0); - - char* state_name; - dbus_message_iter_get_basic(&iter, &state_name); - daemon->PowerStateChanged(state_name, now); - } else if (strcmp(interface, DBUS_IFACE_SCREENSAVER_MANAGER) == 0) { - CHECK(strcmp(dbus_message_get_member(message), - "LockStateChanged") == 0); - - char* state_name; - dbus_message_iter_get_basic(&iter, &state_name); - daemon->ScreenSaverStateChanged(state_name, now); + const char* member = dbus_message_get_member(message); + if (strcmp(member, "ScreenIsLocked") == 0) { + daemon->SetUserActiveState(false, now); + } else if (strcmp(member, "ScreenIsUnlocked") == 0) { + daemon->SetUserActiveState(true, now); + } else if (strcmp(member, "PowerStateChanged") == 0) { + char* state_name; + dbus_message_iter_get_basic(&iter, &state_name); + daemon->PowerStateChanged(state_name, now); + } } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) { CHECK(strcmp(dbus_message_get_member(message), "SessionStateChanged") == 0); @@ -265,23 +249,6 @@ MetricsDaemon::LookupPowerState(const char* state_name) { return kUnknownPowerState; } -void MetricsDaemon::ScreenSaverStateChanged(const char* state_name, Time now) { - DLOG(INFO) << "screen-saver state: " << state_name; - screensaver_state_ = LookupScreenSaverState(state_name); - SetUserActiveState(screensaver_state_ == kScreenSaverStateUnlocked, now); -} - -MetricsDaemon::ScreenSaverState -MetricsDaemon::LookupScreenSaverState(const char* state_name) { - for (int i = 0; i < kNumberScreenSaverStates; i++) { - if (strcmp(state_name, kScreenSaverStates_[i]) == 0) { - return static_cast(i); - } - } - DLOG(WARNING) << "unknown screen-saver state: " << state_name; - return kUnknownScreenSaverState; -} - void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) { DLOG(INFO) << "user session state: " << state_name; session_state_ = LookupSessionState(state_name); diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 835aca36a..ea4771fc7 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -20,7 +20,6 @@ class MetricsDaemon { : daily_use_record_file_(NULL), network_state_(kUnknownNetworkState), power_state_(kUnknownPowerState), - screensaver_state_(kUnknownScreenSaverState), session_state_(kUnknownSessionState), user_active_(false), daily_use_day_last_(0), @@ -72,14 +71,6 @@ class MetricsDaemon { kNumberPowerStates }; - // The screen-saver states (see screensaver_states.h). - enum ScreenSaverState { - kUnknownScreenSaverState = -1, // Initial/unknown screen-saver state. -#define STATE(name, capname) kScreenSaverState ## capname, -#include "screensaver_states.h" - kNumberScreenSaverStates - }; - // The user session states (see session_states.h). enum SessionState { kUnknownSessionState = -1, // Initial/unknown user session state. @@ -115,9 +106,6 @@ class MetricsDaemon { // Array of power states. static const char* kPowerStates_[kNumberPowerStates]; - // Array of screen-saver states. - static const char* kScreenSaverStates_[kNumberScreenSaverStates]; - // Array of user session states. static const char* kSessionStates_[kNumberSessionStates]; @@ -141,12 +129,6 @@ class MetricsDaemon { // Given the state name, returns the state id. PowerState LookupPowerState(const char* state_name); - // Processes screen-saver state change. - void ScreenSaverStateChanged(const char* state_name, base::Time now); - - // Given the state name, returns the state id. - ScreenSaverState LookupScreenSaverState(const char* state_name); - // Processes user session state change. void SessionStateChanged(const char* state_name, base::Time now); @@ -215,9 +197,6 @@ class MetricsDaemon { // Current power state. PowerState power_state_; - // Current screen-saver state. - ScreenSaverState screensaver_state_; - // Current user session state. SessionState session_state_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 31f079d45..49acd3164 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -286,15 +286,6 @@ TEST_F(MetricsDaemonTest, LookupPowerState) { daemon_.LookupPowerState("somestate")); } -TEST_F(MetricsDaemonTest, LookupScreenSaverState) { - EXPECT_EQ(MetricsDaemon::kScreenSaverStateLocked, - daemon_.LookupScreenSaverState("locked")); - EXPECT_EQ(MetricsDaemon::kScreenSaverStateUnlocked, - daemon_.LookupScreenSaverState("unlocked")); - EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, - daemon_.LookupScreenSaverState("somestate")); -} - TEST_F(MetricsDaemonTest, LookupSessionState) { EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.LookupSessionState("started")); @@ -322,7 +313,7 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); msg = NewDBusSignalString("/", - "org.chromium.Power.Manager", + "org.chromium.PowerManager", "PowerStateChanged", "on"); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); @@ -332,14 +323,12 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); msg = NewDBusSignalString("/", - "org.chromium.ScreenSaver.Manager", - "LockStateChanged", - "unlocked"); - EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, - daemon_.screensaver_state_); + "org.chromium.PowerManager", + "ScreenIsUnlocked", + ""); + EXPECT_FALSE(daemon_.user_active_); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); - EXPECT_EQ(MetricsDaemon::kScreenSaverStateUnlocked, - daemon_.screensaver_state_); + EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); @@ -433,36 +422,6 @@ TEST_F(MetricsDaemonTest, PowerStateChanged) { EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); } -TEST_F(MetricsDaemonTest, ScreenSaverStateChanged) { - EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, - daemon_.screensaver_state_); - - daemon_.ScreenSaverStateChanged("locked", - TestTime(5 * kSecondsPerDay + 10)); - EXPECT_EQ(MetricsDaemon::kScreenSaverStateLocked, - daemon_.screensaver_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(5 * kSecondsPerDay + 10), daemon_.user_active_last_); - EXPECT_EQ(5, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - - daemon_.ScreenSaverStateChanged("unlocked", - TestTime(5 * kSecondsPerDay + 100)); - EXPECT_EQ(MetricsDaemon::kScreenSaverStateUnlocked, - daemon_.screensaver_state_); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(5 * kSecondsPerDay + 100), daemon_.user_active_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - - daemon_.ScreenSaverStateChanged("otherstate", - TestTime(5 * kSecondsPerDay + 300)); - EXPECT_EQ(MetricsDaemon::kUnknownScreenSaverState, - daemon_.screensaver_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(5 * kSecondsPerDay + 300), daemon_.user_active_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 200); -} - TEST_F(MetricsDaemonTest, SendMetric) { ExpectMetric("Dummy.Metric", 3, 1, 100, 50); daemon_.SendMetric("Dummy.Metric", /* sample */ 3, diff --git a/metrics/screensaver_states.h b/metrics/screensaver_states.h deleted file mode 100644 index 340142eaa..000000000 --- a/metrics/screensaver_states.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// A table of screen-saver states, to be included when building tabular things. -// -// See network_states.h for details. - - -#ifndef STATE -#define STATE(name, capname) -#endif - -STATE(locked, Locked) -STATE(unlocked, Unlocked) - -#undef STATE From 3fec7f67e4d752f8bdd1ccb8590cbbba9b297a85 Mon Sep 17 00:00:00 2001 From: Anush Elangovan Date: Tue, 8 Jun 2010 01:33:22 -0700 Subject: [PATCH 021/200] Update Watchlists Change-Id: If8849909b7641df022f32726d3ca2b31ad828fe0 --- metrics/WATCHLISTS | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 metrics/WATCHLISTS diff --git a/metrics/WATCHLISTS b/metrics/WATCHLISTS new file mode 100644 index 000000000..a051f350f --- /dev/null +++ b/metrics/WATCHLISTS @@ -0,0 +1,16 @@ +# See http://dev.chromium.org/developers/contributing-code/watchlists for +# a description of this file's format. +# Please keep these keys in alphabetical order. + +{ + 'WATCHLIST_DEFINITIONS': { + 'all': { + 'filepath': '.', + }, + }, + 'WATCHLISTS': { + 'all': ['petkov@chromium.org', + 'semenzato@chromium.org', + 'sosa@chromium.org'] + }, +} From 53faeb0d6cedf0e2459979f296a0d7531228d96e Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Tue, 8 Jun 2010 15:59:13 -0700 Subject: [PATCH 022/200] Fixed KDBusMatches bug. Missing comma in array definition caused metrics_daemon to fail. Metrics_daemon would use a lot of CPU when broken. Review URL: http://codereview.chromium.org/2767003 --- metrics/metrics_daemon.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 5ccc69487..70c0748d5 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -62,7 +62,7 @@ const char* MetricsDaemon::kDBusMatches_[] = { "type='signal'," "interface='" DBUS_IFACE_POWER_MANAGER "'," - "path='/'" + "path='/'", "type='signal'," "sender='org.chromium.SessionManager'," From 99c64a0dd83ad1e14ab0a72ed7c721d7d19dad61 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 10 Jun 2010 15:46:39 -0700 Subject: [PATCH 023/200] Recommend including "Chrome OS" in the histogram description. Review URL: http://codereview.chromium.org/2715005 --- metrics/README | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/metrics/README b/metrics/README index 2c7072da4..6fec766f0 100644 --- a/metrics/README +++ b/metrics/README @@ -71,7 +71,9 @@ by an official Chrome build to UMA, assuming the user has opted into metrics collection. To make the histogram visible on "chromedashboard", the histogram wiki needs to be updated (steps 2 and 3 after following the "Details on how to add your own histograms" link -under the Histograms tab). +under the Histograms tab). Include the string "Chrome OS" in the +histogram description so that it's easier to distinguish Chrome OS +specific metrics from general Chrome histograms. The UMA server logs and keeps the collected field data even if the metric's name is not added to the histogram wiki. However, the From f1e85e49b3017a670237f550cbfe07a9721fdf5d Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 10 Jun 2010 15:59:53 -0700 Subject: [PATCH 024/200] Implement a persistent storage aggregation counter class. This class is currently used to aggregate the active daily use time but can also be used to aggregate other data (e.g., active use time between crashes) before sending to UMA. Abstracting this in a separate class also simplifies the daemon unit tests. An alternative design would store the data on shutdown (but may slow down shutdown a little). This should do it for now. BUG=none TEST=gmerged on device,inspected logs,about:histograms,etc. Review URL: http://codereview.chromium.org/2731008 --- metrics/Makefile | 26 +++- metrics/counter.cc | 176 ++++++++++++++++++++++ metrics/counter.h | 151 +++++++++++++++++++ metrics/counter_mock.h | 26 ++++ metrics/counter_test.cc | 262 +++++++++++++++++++++++++++++++++ metrics/metrics_daemon.cc | 93 ++++-------- metrics/metrics_daemon.h | 34 ++--- metrics/metrics_daemon_test.cc | 260 +++++++------------------------- metrics/metrics_library_mock.h | 2 +- 9 files changed, 723 insertions(+), 307 deletions(-) create mode 100644 metrics/counter.cc create mode 100644 metrics/counter.h create mode 100644 metrics/counter_mock.h create mode 100644 metrics/counter_test.cc diff --git a/metrics/Makefile b/metrics/Makefile index e0c37414a..c8ba54517 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -16,32 +16,42 @@ DAEMON_TEST = metrics_daemon_test LIB = libmetrics.a SHAREDLIB = libmetrics.so LIB_TEST = metrics_library_test +COUNTER_TEST = counter_test +TESTCOUNTER_OBJS = \ + counter.o \ + counter_test.o CLIENT_OBJS = \ metrics_client.o +DAEMON_OBJS = \ + counter.o \ + metrics_daemon.o \ + metrics_daemon_main.o +TESTDAEMON_OBJS = \ + counter.o \ + metrics_daemon.o \ + metrics_daemon_test.o LIB_OBJS = \ metrics_library.o TESTLIB_OBJS = \ metrics_library.o \ metrics_library_test.o -DAEMON_OBJS = \ - metrics_daemon.o \ - metrics_daemon_main.o -TESTDAEMON_OBJS = \ - metrics_daemon.o \ - metrics_daemon_test.o +TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags TESTDAEMON_LIBS = -lgmock -lgtest TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) -tests: $(DAEMON_TEST) $(LIB_TEST) +tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) $(CXX) $(LDFLAGS) $^ -o $@ +$(COUNTER_TEST): $(TESTCOUNTER_OBJS) + $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) + $(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) @@ -73,4 +83,4 @@ metrics_daemon_test.o: \ clean: rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) *.o - rm -f $(DAEMON_TEST) $(LIB_TEST) + rm -f $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) diff --git a/metrics/counter.cc b/metrics/counter.cc new file mode 100644 index 000000000..34fcf7116 --- /dev/null +++ b/metrics/counter.cc @@ -0,0 +1,176 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "counter.h" + +#include + +#include +#include + +namespace chromeos_metrics { + +// TaggedCounter::Record implementation. +void TaggedCounter::Record::Init(int tag, int count) { + tag_ = tag; + count_ = (count > 0) ? count : 0; +} + +void TaggedCounter::Record::Add(int count) { + if (count <= 0) + return; + + count_ += count; + + // Saturates on postive overflow. + if (count_ < 0) { + count_ = INT_MAX; + } +} + +// TaggedCounter implementation. +TaggedCounter::TaggedCounter() + : filename_(NULL), reporter_(NULL), reporter_handle_(NULL), + record_state_(kRecordInvalid) {} + +TaggedCounter::~TaggedCounter() {} + +void TaggedCounter::Init(const char* filename, + Reporter reporter, void* reporter_handle) { + DCHECK(filename); + filename_ = filename; + reporter_ = reporter; + reporter_handle_ = reporter_handle; + record_state_ = kRecordInvalid; +} + +void TaggedCounter::Update(int tag, int count) { + UpdateInternal(tag, + count, + false); // No flush. +} + +void TaggedCounter::Flush() { + UpdateInternal(0, // tag + 0, // count + true); // Do flush. +} + +void TaggedCounter::UpdateInternal(int tag, int count, bool flush) { + // If there's no new data and the last record in the aggregation + // file is with the same tag, there's nothing to do. + if (!flush && count <= 0 && + record_state_ == kRecordValid && record_.tag() == tag) + return; + + DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush; + DCHECK(filename_); + + // NOTE: The assumption is that this TaggedCounter object is the + // sole owner of the persistent storage file so no locking is + // necessary. + int fd = HANDLE_EINTR(open(filename_, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + if (fd < 0) { + PLOG(WARNING) << "Unable to open the persistent counter file"; + return; + } + + ReadRecord(fd); + ReportRecord(tag, flush); + UpdateRecord(tag, count); + WriteRecord(fd); + + HANDLE_EINTR(close(fd)); +} + +void TaggedCounter::ReadRecord(int fd) { + if (record_state_ != kRecordInvalid) + return; + + if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) { + if (record_.count() > 0) { + record_state_ = kRecordValid; + return; + } + // This shouldn't happen normally unless somebody messed with the + // persistent storage file. + NOTREACHED(); + record_state_ = kRecordNullDirty; + return; + } + + record_state_ = kRecordNull; +} + +void TaggedCounter::ReportRecord(int tag, bool flush) { + // If no valid record, there's nothing to report. + if (record_state_ != kRecordValid) { + DCHECK(record_state_ == kRecordNull); + return; + } + + // If the current record has the same tag as the new tag, it's not + // ready to be reported yet. + if (!flush && record_.tag() == tag) + return; + + if (reporter_) { + reporter_(reporter_handle_, record_.tag(), record_.count()); + } + record_state_ = kRecordNullDirty; +} + +void TaggedCounter::UpdateRecord(int tag, int count) { + if (count <= 0) + return; + + switch (record_state_) { + case kRecordNull: + case kRecordNullDirty: + // Current record is null, starting a new record. + record_.Init(tag, count); + record_state_ = kRecordValidDirty; + break; + + case kRecordValid: + // If there's an existing record for the current tag, + // accumulates the counts. + DCHECK_EQ(record_.tag(), tag); + record_.Add(count); + record_state_ = kRecordValidDirty; + break; + + default: + NOTREACHED(); + } +} + +void TaggedCounter::WriteRecord(int fd) { + switch (record_state_) { + case kRecordNullDirty: + // Truncates the aggregation file to discard the record. + PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0); + record_state_ = kRecordNull; + break; + + case kRecordValidDirty: + // Updates the accumulator record in the file if there's new data. + PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0); + PLOG_IF(WARNING, + HANDLE_EINTR(write(fd, &record_, sizeof(record_))) != + sizeof(record_)); + record_state_ = kRecordValid; + break; + + case kRecordNull: + case kRecordValid: + // Nothing to do. + break; + + default: + NOTREACHED(); + } +} + +} // namespace chromeos_metrics diff --git a/metrics/counter.h b/metrics/counter.h new file mode 100644 index 000000000..aac00af73 --- /dev/null +++ b/metrics/counter.h @@ -0,0 +1,151 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_COUNTER_H_ +#define METRICS_COUNTER_H_ + +#include // for FRIEND_TEST + +namespace chromeos_metrics { + +// TaggedCounter maintains a persistent storage (i.e., a file) +// aggregation counter for a given tag (e.g., day, hour) that survives +// system shutdowns, reboots and crashes, as well as daemon process +// restarts. The counter object is initialized by pointing to the +// persistent storage file and providing a callback used for reporting +// aggregated data. The counter can then be updated with additional +// event counts. The aggregated count is reported through the +// callback when the counter is explicitly flushed or when data for a +// new tag arrives. +class TaggedCounterInterface { + public: + // Callback type used for reporting aggregated or flushed data. + // Once this callback is invoked by the counter, the reported + // aggregated data is discarded. Only aggregated data with positive + // counts is reported. + // + // |handle| is the |reporter_handle| pointer passed through Init. + // |tag| is the tag associated with the aggregated count. + // |count| is aggregated count. + typedef void (*Reporter)(void* handle, int tag, int count); + + virtual ~TaggedCounterInterface() {} + + // Initializes the counter by providing the persistent storage + // location |filename| and a |reporter| callback for reporting + // aggregated counts. |reporter_handle| is sent to the |reporter| + // along with the aggregated counts. + // + // NOTE: The assumption is that this object is the sole owner of the + // persistent storage file so no locking is currently implemented. + virtual void Init(const char* filename, + Reporter reporter, void* reporter_handle) = 0; + + // Adds |count| of events for the given |tag|. If there's an + // existing aggregated count for a different tag, it's reported + // through the reporter callback and discarded. + virtual void Update(int tag, int count) = 0; + + // Reports the current aggregated count (if any) through the + // reporter callback and discards it. + virtual void Flush() = 0; +}; + +class TaggedCounter : public TaggedCounterInterface { + public: + TaggedCounter(); + ~TaggedCounter(); + + // Implementation of interface methods. + void Init(const char* filename, Reporter reporter, void* reporter_handle); + void Update(int tag, int count); + void Flush(); + + private: + friend class RecordTest; + friend class TaggedCounterTest; + FRIEND_TEST(TaggedCounterTest, BadFileLocation); + FRIEND_TEST(TaggedCounterTest, Flush); + FRIEND_TEST(TaggedCounterTest, InitFromFile); + FRIEND_TEST(TaggedCounterTest, Update); + + // The current tag/count record is cached by the counter object to + // avoid potentially unnecessary I/O. The cached record can be in + // one of the following states: + enum RecordState { + kRecordInvalid, // Invalid record, sync from persistent storage needed. + kRecordNull, // No current record, persistent storage synced. + kRecordNullDirty, // No current record, persistent storage is invalid. + kRecordValid, // Current record valid, persistent storage synced. + kRecordValidDirty // Current record valid, persistent storage is invalid. + }; + + // Defines the tag/count record. Objects of this class are synced + // with the persistent storage through binary reads/writes. + class Record { + public: + // Creates a new Record with |tag_| and |count_| reset to 0. + Record() : tag_(0), count_(0) {} + + // Initializes with |tag| and |count|. If |count| is negative, + // |count_| is set to 0. + void Init(int tag, int count); + + // Adds |count| to the current |count_|. Negative |count| is + // ignored. In case of positive overflow, |count_| is saturated to + // INT_MAX. + void Add(int count); + + int tag() const { return tag_; } + int count() const { return count_; } + + private: + int tag_; + int count_; + }; + + // Implementation of the Update and Flush methods. Goes through the + // necessary steps to read, report, update, and sync the aggregated + // record. + void UpdateInternal(int tag, int count, bool flush); + + // If the current cached record is invalid, reads it from persistent + // storage specified through file descriptor |fd| and updates the + // cached record state to either null, or valid depending on the + // persistent storage contents. + void ReadRecord(int fd); + + // If there's an existing valid record and either |flush| is true, + // or the new |tag| is different than the old one, reports the + // aggregated data through the reporter callback and resets the + // cached record. + void ReportRecord(int tag, bool flush); + + // Updates the cached record given the new |tag| and |count|. This + // method expects either a null cached record, or a valid cached + // record with the same tag as |tag|. + void UpdateRecord(int tag, int count); + + // If the cached record state is dirty, updates the persistent + // storage specified through file descriptor |fd| and switches the + // record state to non-dirty. + void WriteRecord(int fd); + + // Persistent storage file path. + const char* filename_; + + // Aggregated data reporter callback and handle to pass-through. + Reporter reporter_; + void* reporter_handle_; + + // Current cached aggregation record. + Record record_; + + // Current cached aggregation record state. + RecordState record_state_; +}; + +} // namespace chromeos_metrics + +#endif // METRICS_COUNTER_H_ diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h new file mode 100644 index 000000000..22327ac29 --- /dev/null +++ b/metrics/counter_mock.h @@ -0,0 +1,26 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_COUNTER_MOCK_H_ +#define METRICS_COUNTER_MOCK_H_ + +#include + +#include + +#include "counter.h" + +namespace chromeos_metrics { + +class TaggedCounterMock : public TaggedCounterInterface { + public: + MOCK_METHOD3(Init, void(const char* filename, + Reporter reporter, void* reporter_handle)); + MOCK_METHOD2(Update, void(int tag, int count)); + MOCK_METHOD0(Flush, void()); +}; + +} // namespace chromeos_metrics + +#endif // METRICS_COUNTER_MOCK_H_ diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc new file mode 100644 index 000000000..f9a191f15 --- /dev/null +++ b/metrics/counter_test.cc @@ -0,0 +1,262 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include +#include +#include +#include +#include +#include + +#include "counter.h" + +using ::testing::_; +using ::testing::MockFunction; +using ::testing::StrictMock; + +namespace chromeos_metrics { + +static const char kTestRecordFile[] = "record-file"; +static const char kDoesNotExistFile[] = "/does/not/exist"; + +class RecordTest : public testing::Test { + protected: + virtual void SetUp() { + EXPECT_EQ(0, record_.tag()); + EXPECT_EQ(0, record_.count()); + } + + // The record under test. + TaggedCounter::Record record_; +}; + +class TaggedCounterTest : public testing::Test { + protected: + virtual void SetUp() { + EXPECT_EQ(NULL, counter_.filename_); + EXPECT_TRUE(NULL == counter_.reporter_); + EXPECT_EQ(NULL, counter_.reporter_handle_); + EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); + + counter_.Init(kTestRecordFile, &Reporter, this); + EXPECT_TRUE(AssertNoOrEmptyRecordFile()); + EXPECT_EQ(kTestRecordFile, counter_.filename_); + EXPECT_TRUE(Reporter == counter_.reporter_); + EXPECT_EQ(this, counter_.reporter_handle_); + EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); + + // The test fixture object will be used by the log message handler. + test_ = this; + logging::SetLogMessageHandler(HandleLogMessages); + } + + virtual void TearDown() { + logging::SetLogMessageHandler(NULL); + test_ = NULL; + file_util::Delete(FilePath(kTestRecordFile), false); + } + + // Asserts that the record file contains the specified contents. + testing::AssertionResult AssertRecord(const char* expr_tag, + const char* expr_count, + int expected_tag, + int expected_count) { + int fd = HANDLE_EINTR(open(kTestRecordFile, O_RDONLY)); + if (fd < 0) { + testing::Message msg; + msg << "Unable to open " << kTestRecordFile; + return testing::AssertionFailure(msg); + } + + TaggedCounter::Record record; + if (!file_util::ReadFromFD(fd, reinterpret_cast(&record), + sizeof(record))) { + testing::Message msg; + msg << "Unable to read " << sizeof(record) << " bytes from " + << kTestRecordFile; + HANDLE_EINTR(close(fd)); + return testing::AssertionFailure(msg); + } + + if (record.tag() != expected_tag || record.count() != expected_count) { + testing::Message msg; + msg << "actual record (" << record.tag() << ", " << record.count() + << ") expected (" << expected_tag << ", " << expected_count << ")"; + HANDLE_EINTR(close(fd)); + return testing::AssertionFailure(msg); + } + + HANDLE_EINTR(close(fd)); + return testing::AssertionSuccess(); + } + + // Returns true if the persistent record file does not exist or is + // empty, false otherwise. + bool AssertNoOrEmptyRecordFile() { + FilePath record_file(counter_.filename_); + int64 record_file_size; + return !file_util::PathExists(record_file) || + (file_util::GetFileSize(record_file, &record_file_size) && + record_file_size == 0); + } + + // Adds a reporter call expectation that the specified tag/count + // callback will be generated. + void ExpectReporterCall(int tag, int count) { + EXPECT_CALL(reporter_, Call(_, tag, count)) + .Times(1) + .RetiresOnSaturation(); + } + + // The reporter callback forwards the call to the reporter mock so + // that we can set call expectations. + static void Reporter(void* handle, int tag, int count) { + TaggedCounterTest* test = static_cast(handle); + ASSERT_FALSE(NULL == test); + test->reporter_.Call(handle, tag, count); + } + + // Collects log messages in the |log_| member string so that they + // can be analyzed for errors and expected behavior. + static bool HandleLogMessages(int severity, const std::string& str) { + test_->log_.append(str); + test_->log_.append("\n"); + + // Returning true would mute the log. + return false; + } + + // Returns true if the counter log contains |pattern|, false otherwise. + bool LogContains(const std::string& pattern) { + return log_.find(pattern) != std::string::npos; + } + + // The TaggedCounter object under test. + TaggedCounter counter_; + + // The accumulated counter log. + std::string log_; + + // Reporter mock to set callback expectations on. + StrictMock > reporter_; + + // Pointer to the current test fixture. + static TaggedCounterTest* test_; +}; + +// static +TaggedCounterTest* TaggedCounterTest::test_ = NULL; + +TEST_F(RecordTest, Init) { + record_.Init(/* tag */ 5, /* count */ -1); + EXPECT_EQ(5, record_.tag()); + EXPECT_EQ(0, record_.count()); + + record_.Init(/* tag */ -2, /* count */ 10); + EXPECT_EQ(-2, record_.tag()); + EXPECT_EQ(10, record_.count()); +} + +TEST_F(RecordTest, Add) { + record_.Add(/* count */ -1); + EXPECT_EQ(0, record_.count()); + + record_.Add(/* count */ 5); + EXPECT_EQ(5, record_.count()); + + record_.Add(/* count */ 10); + EXPECT_EQ(15, record_.count()); + + record_.Add(/* count */ -2); + EXPECT_EQ(15, record_.count()); + + record_.Add(/* count */ INT_MAX); + EXPECT_EQ(INT_MAX, record_.count()); + + record_.Add(/* count */ 1); + EXPECT_EQ(INT_MAX, record_.count()); +} + +TEST_F(TaggedCounterTest, BadFileLocation) { + // Checks that the counter doesn't die badly if the file can't be + // created. + counter_.Init(kDoesNotExistFile, + /* reporter */ NULL, /* reporter_handle */ NULL); + counter_.Update(/* tag */ 10, /* count */ 20); + EXPECT_TRUE(LogContains("Unable to open the persistent counter file: " + "No such file or directory")); + EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); + file_util::Delete(FilePath(kDoesNotExistFile), false); +} + +TEST_F(TaggedCounterTest, Flush) { + counter_.Flush(); + EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); + + counter_.Update(/* tag */ 40, /* count */ 60); + ExpectReporterCall(/* tag */ 40, /* count */ 60); + counter_.Flush(); + EXPECT_TRUE(AssertNoOrEmptyRecordFile()); + EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); + + counter_.Update(/* tag */ 41, /* count */ 70); + counter_.record_.Init(/* tag */ 0, /* count */ 0); + counter_.record_state_ = TaggedCounter::kRecordInvalid; + ExpectReporterCall(/* tag */ 41, /* count */ 70); + counter_.Flush(); + EXPECT_TRUE(AssertNoOrEmptyRecordFile()); + EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); +} + +TEST_F(TaggedCounterTest, InitFromFile) { + counter_.Update(/* tag */ 30, /* count */ 50); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 50); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); + + counter_.Init(kTestRecordFile, &Reporter, this); + counter_.Update(/* tag */ 30, /* count */ 40); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 90); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); + + counter_.Init(kTestRecordFile, &Reporter, this); + ExpectReporterCall(/* tag */ 30, /* count */ 90); + counter_.Update(/* tag */ 31, /* count */ 60); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 31, /* seconds */ 60); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); + + ExpectReporterCall(/* tag */ 31, /* count */ 60); + counter_.Init(kTestRecordFile, &Reporter, this); + counter_.Update(/* tag */ 32, /* count */ 0); + EXPECT_TRUE(AssertNoOrEmptyRecordFile()); + EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); +} + +TEST_F(TaggedCounterTest, Update) { + counter_.Update(/* tag */ 20, /* count */ 30); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 30); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); + + counter_.Update(/* tag */ 20, /* count */ 40); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 70); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); + + ExpectReporterCall(/* tag */ 20, /* count */ 70); + counter_.Update(/* tag */ 21, /* count */ 15); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 21, /* seconds */ 15); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); + + ExpectReporterCall(/* tag */ 21, /* count */ 15); + counter_.Update(/* tag */ 22, /* count */ 0); + EXPECT_TRUE(AssertNoOrEmptyRecordFile()); + EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); +} + +} // namespace chromeos_metrics + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 70c0748d5..e93c9a818 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -5,11 +5,11 @@ #include "metrics_daemon.h" #include -#include -#include #include +#include "counter.h" + using base::Time; using base::TimeDelta; using base::TimeTicks; @@ -89,6 +89,16 @@ const char* MetricsDaemon::kSessionStates_[] = { #include "session_states.h" }; +MetricsDaemon::MetricsDaemon() + : network_state_(kUnknownNetworkState), + power_state_(kUnknownPowerState), + session_state_(kUnknownSessionState), + user_active_(false), + usemon_interval_(0), + usemon_source_(NULL) {} + +MetricsDaemon::~MetricsDaemon() {} + void MetricsDaemon::Run(bool run_as_daemon) { if (!run_as_daemon || daemon(0, 0) == 0) { Loop(); @@ -99,7 +109,8 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; - daily_use_record_file_ = kDailyUseRecordFile; + daily_use_.reset(new chromeos_metrics::TaggedCounter()); + daily_use_->Init(kDailyUseRecordFile, &DailyUseReporter, this); // Don't setup D-Bus and GLib in test mode. if (testing) @@ -283,7 +294,7 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { } TimeDelta since_epoch = now - Time(); int day = since_epoch.InDays(); - LogDailyUseRecord(day, seconds); + daily_use_->Update(day, seconds); // Schedules a use monitor on inactive->active transitions and // unschedules it on active->inactive transitions. @@ -298,70 +309,6 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { user_active_last_ = now; } -void MetricsDaemon::LogDailyUseRecord(int day, int seconds) { - // If there's no new active use today and the last record in the - // usage aggregation file is today, there's nothing to do. - if (seconds == 0 && day == daily_use_day_last_) - return; - - DLOG(INFO) << "day: " << day << " usage: " << seconds << " seconds"; - int fd = HANDLE_EINTR(open(daily_use_record_file_, - O_RDWR | O_CREAT, - S_IRUSR | S_IWUSR)); - if (fd < 0) { - PLOG(WARNING) << "Unable to open the daily use file"; - return; - } - - bool same_day = false; - UseRecord record; - if (HANDLE_EINTR(read(fd, &record, sizeof(record))) == sizeof(record)) { - if (record.day_ == day) { - // If there's an existing record for today, aggregates the usage - // time. - same_day = true; - record.seconds_ += seconds; - } else { - // If there's an existing record for a day in the past, rounds - // the usage to the nearest minute and sends it to UMA. - int minutes = - (record.seconds_ + kSecondsPerMinute / 2) / kSecondsPerMinute; - SendMetric(kMetricDailyUseTimeName, minutes, - kMetricDailyUseTimeMin, - kMetricDailyUseTimeMax, - kMetricDailyUseTimeBuckets); - - // Truncates the usage file to ensure that no duplicate usage is - // sent to UMA. - PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0); - } - } - - // Updates the use record in the daily usage file if there's new - // usage today. - if (seconds > 0) { - if (!same_day) { - record.day_ = day; - record.seconds_ = seconds; - } - // else an already existing record for the same day will be - // overwritten with updated usage below. - - PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0); - PLOG_IF(WARNING, - HANDLE_EINTR(write(fd, &record, sizeof(record))) != - sizeof(record)); - } - - HANDLE_EINTR(close(fd)); - - // Remembers the day of the use record in the usage aggregation file - // to reduce file I/O. This is not really useful now but potentially - // allows frequent LogDailyUseRecord calls with no unnecessary I/O - // overhead. - daily_use_day_last_ = day; -} - // static gboolean MetricsDaemon::UseMonitorStatic(gpointer data) { return static_cast(data)->UseMonitor() ? TRUE : FALSE; @@ -422,6 +369,16 @@ void MetricsDaemon::UnscheduleUseMonitor() { usemon_interval_ = 0; } +// static +void MetricsDaemon::DailyUseReporter(void* handle, int tag, int count) { + MetricsDaemon* daemon = static_cast(handle); + int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; + daemon->SendMetric(kMetricDailyUseTimeName, minutes, + kMetricDailyUseTimeMin, + kMetricDailyUseTimeMax, + kMetricDailyUseTimeBuckets); +} + void MetricsDaemon::SendMetric(const std::string& name, int sample, int min, int max, int nbuckets) { DLOG(INFO) << "received metric: " << name << " " << sample << " " diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index ea4771fc7..ee8059620 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -8,24 +8,19 @@ #include #include +#include +#include +#include // for FRIEND_TEST + #include "metrics_library.h" -#include // for FRIEND_TEST -#include +namespace chromeos_metrics { class TaggedCounterInterface; } class MetricsDaemon { public: - MetricsDaemon() - : daily_use_record_file_(NULL), - network_state_(kUnknownNetworkState), - power_state_(kUnknownPowerState), - session_state_(kUnknownSessionState), - user_active_(false), - daily_use_day_last_(0), - usemon_interval_(0), - usemon_source_(NULL) {} - ~MetricsDaemon() {} + MetricsDaemon(); + ~MetricsDaemon(); // Initializes. void Init(bool testing, MetricsLibraryInterface* metrics_lib); @@ -36,10 +31,7 @@ class MetricsDaemon { private: friend class MetricsDaemonTest; - FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecordBadFileLocation); - FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecordOnLogin); - FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecordRoundDown); - FRIEND_TEST(MetricsDaemonTest, LogDailyUseRecordRoundUp); + FRIEND_TEST(MetricsDaemonTest, DailyUseReporter); FRIEND_TEST(MetricsDaemonTest, LookupNetworkState); FRIEND_TEST(MetricsDaemonTest, LookupPowerState); FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); @@ -51,8 +43,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); FRIEND_TEST(MetricsDaemonTest, SendMetric); FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); - FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateSendOnLogin); - FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateSendOnMonitor); + FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateTimeJump); // The network states (see network_states.h). @@ -178,14 +169,14 @@ class MetricsDaemon { void SendMetric(const std::string& name, int sample, int min, int max, int nbuckets); + static void DailyUseReporter(void* data, int tag, int count); + // Test mode. bool testing_; // The metrics library handle. MetricsLibraryInterface* metrics_lib_; - const char* daily_use_record_file_; - // Current network state. NetworkState network_state_; @@ -209,8 +200,7 @@ class MetricsDaemon { // epoch as the timestamp. base::Time user_active_last_; - // Last stored daily use day (since the epoch). - int daily_use_day_last_; + scoped_ptr daily_use_; // Sleep period until the next daily usage aggregation performed by // the daily use monitor (see ScheduleUseMonitor). diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 49acd3164..7119c8506 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -2,26 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + +#include "counter_mock.h" #include "metrics_daemon.h" #include "metrics_library_mock.h" -#include - -#include -#include -#include -#include -#include - using base::Time; using base::TimeTicks; -using ::testing::Mock; +using chromeos_metrics::TaggedCounterMock; +using ::testing::_; using ::testing::Return; using ::testing::StrictMock; -static const char kTestDailyUseRecordFile[] = "daily-usage-test"; -static const char kDoesNotExistFile[] = "/does/not/exist"; - static const int kSecondsPerDay = 24 * 60 * 60; // This class allows a TimeTicks object to be initialized with seconds @@ -48,46 +41,29 @@ static std::ostream& operator<<(std::ostream& o, const Time& time) { class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { - EXPECT_EQ(NULL, daemon_.daily_use_record_file_); + EXPECT_EQ(NULL, daemon_.daily_use_.get()); daemon_.Init(true, &metrics_lib_); - // Tests constructor initialization. Switches to a test daily use - // record file. - EXPECT_TRUE(NULL != daemon_.daily_use_record_file_); - daemon_.daily_use_record_file_ = kTestDailyUseRecordFile; - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - EXPECT_EQ(0, daemon_.daily_use_day_last_); + // Tests constructor initialization. Switches to mock counters. + EXPECT_TRUE(NULL != daemon_.daily_use_.get()); + daily_use_ = new StrictMock(); + daemon_.daily_use_.reset(daily_use_); // Transfers ownership. EXPECT_FALSE(daemon_.user_active_); EXPECT_TRUE(daemon_.user_active_last_.is_null()); EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); EXPECT_TRUE(daemon_.network_state_last_.is_null()); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); - - // The test fixture object will be used by the log message handler. - daemon_test_ = this; - logging::SetLogMessageHandler(HandleLogMessages); } - virtual void TearDown() { - logging::SetLogMessageHandler(NULL); - daemon_test_ = NULL; - file_util::Delete(FilePath(kTestDailyUseRecordFile), false); - } + virtual void TearDown() {} - // Collects log messages in the |daemon_log_| member string so that - // they can be analyzed for errors and expected behavior. - static bool HandleLogMessages(int severity, const std::string& str) { - daemon_test_->daemon_log_.append(str); - daemon_test_->daemon_log_.append("\n"); - - // Returning true would mute the log. - return false; - } - - // Returns true if the daemon log contains |pattern|, false otherwise. - bool LogContains(const std::string& pattern) { - return daemon_log_.find(pattern) != std::string::npos; + // Adds a daily use aggregation counter expectation that the + // specified tag/count update will be generated. + void ExpectDailyUseUpdate(int tag, int count) { + EXPECT_CALL(*daily_use_, Update(tag, count)) + .Times(1) + .RetiresOnSaturation(); } // Adds a metrics library mock expectation that the specified metric @@ -118,55 +94,11 @@ class MetricsDaemonTest : public testing::Test { MetricsDaemon::kMetricTimeToNetworkDropBuckets); } + // Converts from seconds to a Time object. Time TestTime(int64 seconds) { return Time::FromInternalValue(seconds * Time::kMicrosecondsPerSecond); } - // Asserts that the daily use record file contains the specified - // contents. - testing::AssertionResult AssertDailyUseRecord(const char* expr_day, - const char* expr_seconds, - int expected_day, - int expected_seconds) { - int fd = HANDLE_EINTR(open(daemon_.daily_use_record_file_, O_RDONLY)); - if (fd < 0) { - testing::Message msg; - msg << "Unable to open " << daemon_.daily_use_record_file_; - return testing::AssertionFailure(msg); - } - - MetricsDaemon::UseRecord record; - if (!file_util::ReadFromFD(fd, reinterpret_cast(&record), - sizeof(record))) { - testing::Message msg; - msg << "Unable to read " << sizeof(record) << " bytes from " - << daemon_.daily_use_record_file_; - HANDLE_EINTR(close(fd)); - return testing::AssertionFailure(msg); - } - - if (record.day_ != expected_day || record.seconds_ != expected_seconds) { - testing::Message msg; - msg << "actual use record (" << record.day_ << ", " << record.seconds_ - << ") expected (" << expected_day << ", " << expected_seconds << ")"; - HANDLE_EINTR(close(fd)); - return testing::AssertionFailure(msg); - } - - HANDLE_EINTR(close(fd)); - return testing::AssertionSuccess(); - } - - // Returns true if the daily use record file does not exist or is - // empty, false otherwise. - bool AssertNoOrEmptyUseRecordFile() { - FilePath record_file(daemon_.daily_use_record_file_); - int64 record_file_size; - return !file_util::PathExists(record_file) || - (file_util::GetFileSize(record_file, &record_file_size) && - record_file_size == 0); - } - // Creates a new DBus signal message with a single string // argument. The message can be deallocated through // DeleteDBusMessage. @@ -195,9 +127,6 @@ class MetricsDaemonTest : public testing::Test { dbus_message_unref(msg); } - // Pointer to the current test fixture. - static MetricsDaemonTest* daemon_test_; - // The MetricsDaemon under test. MetricsDaemon daemon_; @@ -205,67 +134,19 @@ class MetricsDaemonTest : public testing::Test { // metric generation calls are marked as failures. StrictMock metrics_lib_; - // The accumulated metrics daemon log. - std::string daemon_log_; + // Daily use time aggregation counter mock. It's a strict mock so + // that all unexpected update calls are marked as failures. It's a + // pointer so that it can replace the scoped_ptr allocated by the + // daemon. + StrictMock* daily_use_; }; -// static -MetricsDaemonTest* MetricsDaemonTest::daemon_test_ = NULL; - -TEST_F(MetricsDaemonTest, LogDailyUseRecordBadFileLocation) { - // Checks that the daemon doesn't die badly if the file can't be - // created. - daemon_.daily_use_record_file_ = kDoesNotExistFile; - daemon_.LogDailyUseRecord(10, 20); - EXPECT_TRUE(LogContains("Unable to open the daily use file: " - "No such file or directory")); - EXPECT_EQ(0, daemon_.daily_use_day_last_); - file_util::Delete(FilePath(kDoesNotExistFile), false); -} - -TEST_F(MetricsDaemonTest, LogDailyUseRecordOnLogin) { - daemon_.LogDailyUseRecord(/* day */ 5, /* seconds */ 120); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 120); - EXPECT_EQ(5, daemon_.daily_use_day_last_); - - daemon_.LogDailyUseRecord(/* day */ 5, /* seconds */ 0); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 120); - EXPECT_EQ(5, daemon_.daily_use_day_last_); - - daemon_.LogDailyUseRecord(/* day */ 5, /* seconds */ 240); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 5, /* seconds */ 360); - EXPECT_EQ(5, daemon_.daily_use_day_last_); - - ExpectDailyUseTimeMetric(/* sample */ 6); - daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 0); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); - EXPECT_EQ(6, daemon_.daily_use_day_last_); -} - -TEST_F(MetricsDaemonTest, LogDailyUseRecordRoundDown) { - daemon_.LogDailyUseRecord(/* day */ 7, /* seconds */ 89); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 89); - EXPECT_EQ(7, daemon_.daily_use_day_last_); +TEST_F(MetricsDaemonTest, DailyUseReporter) { + ExpectDailyUseTimeMetric(/* sample */ 2); + MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 20, /* count */ 90); ExpectDailyUseTimeMetric(/* sample */ 1); - daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 15); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 15); - EXPECT_EQ(6, daemon_.daily_use_day_last_); -} - -TEST_F(MetricsDaemonTest, LogDailyUseRecordRoundUp) { - daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 0); - EXPECT_EQ(6, daemon_.daily_use_day_last_); - - // Tests rounding use time to the closest minute. - daemon_.LogDailyUseRecord(/* day */ 6, /* seconds */ 90); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 90); - EXPECT_EQ(6, daemon_.daily_use_day_last_); - - ExpectDailyUseTimeMetric(/* sample */ 2); - daemon_.LogDailyUseRecord(/* day */ 7, /* seconds */ 89); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 89); - EXPECT_EQ(7, daemon_.daily_use_day_last_); + MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 23, /* count */ 89); } TEST_F(MetricsDaemonTest, LookupNetworkState) { @@ -322,6 +203,9 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); + EXPECT_CALL(*daily_use_, Update(_, 0)) + .Times(1) + .RetiresOnSaturation(); msg = NewDBusSignalString("/", "org.chromium.PowerManager", "ScreenIsUnlocked", @@ -332,6 +216,9 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); + EXPECT_CALL(*daily_use_, Update(_, 0)) + .Times(1) + .RetiresOnSaturation(); msg = NewDBusSignalString("/org/chromium/SessionManager", "org.chromium.SessionManagerInterface", "SessionStateChanged", @@ -396,30 +283,28 @@ TEST_F(MetricsDaemonTest, NetStateChangedSuspend) { } TEST_F(MetricsDaemonTest, PowerStateChanged) { + ExpectDailyUseUpdate(7, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(7 * kSecondsPerDay + 15)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 15), daemon_.user_active_last_); - EXPECT_EQ(7, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + ExpectDailyUseUpdate(7, 30); daemon_.PowerStateChanged("mem", TestTime(7 * kSecondsPerDay + 45)); EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); daemon_.PowerStateChanged("on", TestTime(7 * kSecondsPerDay + 85)); EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); + ExpectDailyUseUpdate(7, 0); daemon_.PowerStateChanged("otherstate", TestTime(7 * kSecondsPerDay + 185)); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 185), daemon_.user_active_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 7, /* seconds */ 30); } TEST_F(MetricsDaemonTest, SendMetric) { @@ -429,117 +314,76 @@ TEST_F(MetricsDaemonTest, SendMetric) { } TEST_F(MetricsDaemonTest, SessionStateChanged) { + ExpectDailyUseUpdate(15, 0); daemon_.SessionStateChanged("started", TestTime(15 * kSecondsPerDay + 20)); EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(15 * kSecondsPerDay + 20), daemon_.user_active_last_); - EXPECT_EQ(15, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + ExpectDailyUseUpdate(15, 130); daemon_.SessionStateChanged("stopped", TestTime(15 * kSecondsPerDay + 150)); EXPECT_EQ(MetricsDaemon::kSessionStateStopped, daemon_.session_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(15 * kSecondsPerDay + 150), daemon_.user_active_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 15, /* seconds */ 130); + ExpectDailyUseUpdate(15, 0); daemon_.SessionStateChanged("otherstate", TestTime(15 * kSecondsPerDay + 300)); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(15 * kSecondsPerDay + 300), daemon_.user_active_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 15, /* seconds */ 130); } -TEST_F(MetricsDaemonTest, SetUserActiveStateSendOnLogin) { +TEST_F(MetricsDaemonTest, SetUserActiveState) { + ExpectDailyUseUpdate(5, 0); daemon_.SetUserActiveState(/* active */ false, TestTime(5 * kSecondsPerDay + 10)); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(5 * kSecondsPerDay + 10), daemon_.user_active_last_); - EXPECT_EQ(5, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + ExpectDailyUseUpdate(6, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(6 * kSecondsPerDay + 20)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(6 * kSecondsPerDay + 20), daemon_.user_active_last_); - EXPECT_EQ(6, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + ExpectDailyUseUpdate(6, 100); daemon_.SetUserActiveState(/* active */ true, TestTime(6 * kSecondsPerDay + 120)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(6 * kSecondsPerDay + 120), daemon_.user_active_last_); - EXPECT_EQ(6, daemon_.daily_use_day_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 100); + ExpectDailyUseUpdate(6, 110); daemon_.SetUserActiveState(/* active */ false, - TestTime(6 * kSecondsPerDay + 220)); + TestTime(6 * kSecondsPerDay + 230)); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(6 * kSecondsPerDay + 220), daemon_.user_active_last_); - EXPECT_EQ(6, daemon_.daily_use_day_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 6, /* seconds */ 200); - - ExpectDailyUseTimeMetric(/* sample */ 3); - daemon_.SetUserActiveState(/* active */ true, - TestTime(8 * kSecondsPerDay - 300)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(8 * kSecondsPerDay - 300), daemon_.user_active_last_); - EXPECT_EQ(7, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); -} - -TEST_F(MetricsDaemonTest, SetUserActiveStateSendOnMonitor) { - daemon_.SetUserActiveState(/* active */ true, - TestTime(8 * kSecondsPerDay - 300)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(8 * kSecondsPerDay - 300), daemon_.user_active_last_); - EXPECT_EQ(7, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + EXPECT_EQ(TestTime(6 * kSecondsPerDay + 230), daemon_.user_active_last_); + ExpectDailyUseUpdate(6, 0); daemon_.SetUserActiveState(/* active */ false, - TestTime(8 * kSecondsPerDay + 300)); + TestTime(6 * kSecondsPerDay + 260)); EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(8 * kSecondsPerDay + 300), daemon_.user_active_last_); - EXPECT_EQ(8, daemon_.daily_use_day_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 8, /* seconds */ 600); - - daemon_.SetUserActiveState(/* active */ true, - TestTime(9 * kSecondsPerDay - 200)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(9 * kSecondsPerDay - 200), daemon_.user_active_last_); - EXPECT_EQ(8, daemon_.daily_use_day_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 8, /* seconds */ 600); - - ExpectDailyUseTimeMetric(/* sample */ 10); - daemon_.SetUserActiveState(/* active */ true, - TestTime(9 * kSecondsPerDay + 200)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(9 * kSecondsPerDay + 200), daemon_.user_active_last_); - EXPECT_EQ(9, daemon_.daily_use_day_last_); - EXPECT_PRED_FORMAT2(AssertDailyUseRecord, /* day */ 9, /* seconds */ 400); + EXPECT_EQ(TestTime(6 * kSecondsPerDay + 260), daemon_.user_active_last_); } TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) { + ExpectDailyUseUpdate(10, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 500)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 500), daemon_.user_active_last_); - EXPECT_EQ(10, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + ExpectDailyUseUpdate(10, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 300)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 300), daemon_.user_active_last_); - EXPECT_EQ(10, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); + ExpectDailyUseUpdate(10, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 1000)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_); - EXPECT_EQ(10, daemon_.daily_use_day_last_); - EXPECT_TRUE(AssertNoOrEmptyUseRecordFile()); } int main(int argc, char** argv) { diff --git a/metrics/metrics_library_mock.h b/metrics/metrics_library_mock.h index ffc934295..ba59c8f15 100644 --- a/metrics/metrics_library_mock.h +++ b/metrics/metrics_library_mock.h @@ -20,4 +20,4 @@ class MetricsLibraryMock : public MetricsLibraryInterface { int max)); }; -#endif /* METRICS_LIBRARY_MOCK_H_ */ +#endif // METRICS_LIBRARY_MOCK_H_ From 1bb904ed4dc0a7030cf0458aae67fc00565bbe36 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 16 Jun 2010 15:58:06 -0700 Subject: [PATCH 025/200] Measure and report time between user-space process crashes. BUG=none TEST=unit tests, gmerged on the device and inspected logs, about:histograms,etc. Review URL: http://codereview.chromium.org/2736008 --- metrics/counter.cc | 33 +++++++---- metrics/counter.h | 8 +-- metrics/counter_test.cc | 8 +-- metrics/metrics_daemon.cc | 50 +++++++++++++++- metrics/metrics_daemon.h | 26 +++++++- metrics/metrics_daemon_test.cc | 105 ++++++++++++++++++++++++--------- 6 files changed, 177 insertions(+), 53 deletions(-) diff --git a/metrics/counter.cc b/metrics/counter.cc index 34fcf7116..58dbae457 100644 --- a/metrics/counter.cc +++ b/metrics/counter.cc @@ -58,11 +58,16 @@ void TaggedCounter::Flush() { } void TaggedCounter::UpdateInternal(int tag, int count, bool flush) { - // If there's no new data and the last record in the aggregation - // file is with the same tag, there's nothing to do. - if (!flush && count <= 0 && - record_state_ == kRecordValid && record_.tag() == tag) - return; + if (flush) { + // Flushing but record is null, so nothing to do. + if (record_state_ == kRecordNull) + return; + } else { + // If there's no new data and the last record in the aggregation + // file is with the same tag, there's nothing to do. + if (count <= 0 && record_state_ == kRecordValid && record_.tag() == tag) + return; + } DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush; DCHECK(filename_); @@ -78,7 +83,7 @@ void TaggedCounter::UpdateInternal(int tag, int count, bool flush) { ReadRecord(fd); ReportRecord(tag, flush); - UpdateRecord(tag, count); + UpdateRecord(tag, count, flush); WriteRecord(fd); HANDLE_EINTR(close(fd)); @@ -89,7 +94,7 @@ void TaggedCounter::ReadRecord(int fd) { return; if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) { - if (record_.count() > 0) { + if (record_.count() >= 0) { record_state_ = kRecordValid; return; } @@ -106,7 +111,7 @@ void TaggedCounter::ReadRecord(int fd) { void TaggedCounter::ReportRecord(int tag, bool flush) { // If no valid record, there's nothing to report. if (record_state_ != kRecordValid) { - DCHECK(record_state_ == kRecordNull); + DCHECK_EQ(record_state_, kRecordNull); return; } @@ -121,9 +126,11 @@ void TaggedCounter::ReportRecord(int tag, bool flush) { record_state_ = kRecordNullDirty; } -void TaggedCounter::UpdateRecord(int tag, int count) { - if (count <= 0) +void TaggedCounter::UpdateRecord(int tag, int count, bool flush) { + if (flush) { + DCHECK(record_state_ == kRecordNull || record_state_ == kRecordNullDirty); return; + } switch (record_state_) { case kRecordNull: @@ -137,8 +144,10 @@ void TaggedCounter::UpdateRecord(int tag, int count) { // If there's an existing record for the current tag, // accumulates the counts. DCHECK_EQ(record_.tag(), tag); - record_.Add(count); - record_state_ = kRecordValidDirty; + if (count > 0) { + record_.Add(count); + record_state_ = kRecordValidDirty; + } break; default: diff --git a/metrics/counter.h b/metrics/counter.h index aac00af73..876b10732 100644 --- a/metrics/counter.h +++ b/metrics/counter.h @@ -22,8 +22,7 @@ class TaggedCounterInterface { public: // Callback type used for reporting aggregated or flushed data. // Once this callback is invoked by the counter, the reported - // aggregated data is discarded. Only aggregated data with positive - // counts is reported. + // aggregated data is discarded. // // |handle| is the |reporter_handle| pointer passed through Init. // |tag| is the tag associated with the aggregated count. @@ -124,8 +123,9 @@ class TaggedCounter : public TaggedCounterInterface { // Updates the cached record given the new |tag| and |count|. This // method expects either a null cached record, or a valid cached - // record with the same tag as |tag|. - void UpdateRecord(int tag, int count); + // record with the same tag as |tag|. If |flush| is true, the method + // asserts that the cached record is null and returns. + void UpdateRecord(int tag, int count, bool flush); // If the cached record state is dirty, updates the persistent // storage specified through file descriptor |fd| and switches the diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc index f9a191f15..605e859dc 100644 --- a/metrics/counter_test.cc +++ b/metrics/counter_test.cc @@ -230,8 +230,8 @@ TEST_F(TaggedCounterTest, InitFromFile) { ExpectReporterCall(/* tag */ 31, /* count */ 60); counter_.Init(kTestRecordFile, &Reporter, this); counter_.Update(/* tag */ 32, /* count */ 0); - EXPECT_TRUE(AssertNoOrEmptyRecordFile()); - EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 32, /* seconds */ 0); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); } TEST_F(TaggedCounterTest, Update) { @@ -250,8 +250,8 @@ TEST_F(TaggedCounterTest, Update) { ExpectReporterCall(/* tag */ 21, /* count */ 15); counter_.Update(/* tag */ 22, /* count */ 0); - EXPECT_TRUE(AssertNoOrEmptyRecordFile()); - EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 22, /* seconds */ 0); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); } } // namespace chromeos_metrics diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index e93c9a818..6cbe8e812 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -15,6 +15,7 @@ using base::TimeDelta; using base::TimeTicks; #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") +#define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter" #define DBUS_IFACE_FLIMFLAM_MANAGER "org.chromium.flimflam.Manager" #define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager" #define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface" @@ -23,11 +24,16 @@ using base::TimeTicks; // TODO(petkov): This file should probably live in a user-specific stateful // location, e.g., /home/chronos/user. static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage"; +static const char kUserCrashIntervalRecordFile[] = + "/var/log/metrics/user-crash-interval"; static const int kSecondsPerMinute = 60; static const int kMinutesPerHour = 60; static const int kHoursPerDay = 24; static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour; +static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay; +static const int kDaysPerWeek = 7; +static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek; // The daily use monitor is scheduled to a 1-minute interval after // initial user activity and then it's exponentially backed off to @@ -52,8 +58,19 @@ const int MetricsDaemon::kMetricTimeToNetworkDropMax = 8 /* hours */ * kMinutesPerHour * kSecondsPerMinute; const int MetricsDaemon::kMetricTimeToNetworkDropBuckets = 50; +const char MetricsDaemon::kMetricUserCrashIntervalName[] = + "Logging.UserCrashInterval"; +const int MetricsDaemon::kMetricUserCrashIntervalMin = 1; +const int MetricsDaemon::kMetricUserCrashIntervalMax = 4 * kSecondsPerWeek; +const int MetricsDaemon::kMetricUserCrashIntervalBuckets = 50; + // static const char* MetricsDaemon::kDBusMatches_[] = { + "type='signal'," + "interface='" DBUS_IFACE_CRASH_REPORTER "'," + "path='/'," + "member='UserCrash'", + "type='signal'," "sender='org.chromium.flimflam'," "interface='" DBUS_IFACE_FLIMFLAM_MANAGER "'," @@ -111,6 +128,9 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { metrics_lib_ = metrics_lib; daily_use_.reset(new chromeos_metrics::TaggedCounter()); daily_use_->Init(kDailyUseRecordFile, &DailyUseReporter, this); + user_crash_interval_.reset(new chromeos_metrics::TaggedCounter()); + user_crash_interval_->Init(kUserCrashIntervalRecordFile, + &UserCrashIntervalReporter, this); // Don't setup D-Bus and GLib in test mode. if (testing) @@ -130,7 +150,7 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { dbus_connection_setup_with_g_main(connection, NULL); // Registers D-Bus matches for the signals we would like to catch. - for (unsigned int m = 0; m < sizeof(kDBusMatches_) / sizeof(char*); m++) { + for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) { const char* match = kDBusMatches_[m]; DLOG(INFO) << "adding dbus match: " << match; dbus_bus_add_match(connection, match, &error); @@ -171,7 +191,11 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, DBusMessageIter iter; dbus_message_iter_init(message, &iter); - if (strcmp(interface, DBUS_IFACE_FLIMFLAM_MANAGER) == 0) { + if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) { + CHECK(strcmp(dbus_message_get_member(message), + "UserCrash") == 0); + daemon->ProcessUserCrash(); + } else if (strcmp(interface, DBUS_IFACE_FLIMFLAM_MANAGER) == 0) { CHECK(strcmp(dbus_message_get_member(message), "StateChanged") == 0); @@ -295,6 +319,7 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { TimeDelta since_epoch = now - Time(); int day = since_epoch.InDays(); daily_use_->Update(day, seconds); + user_crash_interval_->Update(0, seconds); // Schedules a use monitor on inactive->active transitions and // unschedules it on active->inactive transitions. @@ -309,6 +334,14 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { user_active_last_ = now; } +void MetricsDaemon::ProcessUserCrash() { + // Counts the active use time up to now. + SetUserActiveState(user_active_, Time::Now()); + + // Reports the active use time since the last crash and resets it. + user_crash_interval_->Flush(); +} + // static gboolean MetricsDaemon::UseMonitorStatic(gpointer data) { return static_cast(data)->UseMonitor() ? TRUE : FALSE; @@ -371,6 +404,9 @@ void MetricsDaemon::UnscheduleUseMonitor() { // static void MetricsDaemon::DailyUseReporter(void* handle, int tag, int count) { + if (count <= 0) + return; + MetricsDaemon* daemon = static_cast(handle); int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; daemon->SendMetric(kMetricDailyUseTimeName, minutes, @@ -379,6 +415,16 @@ void MetricsDaemon::DailyUseReporter(void* handle, int tag, int count) { kMetricDailyUseTimeBuckets); } +// static +void MetricsDaemon::UserCrashIntervalReporter(void* handle, + int tag, int count) { + MetricsDaemon* daemon = static_cast(handle); + daemon->SendMetric(kMetricUserCrashIntervalName, count, + kMetricUserCrashIntervalMin, + kMetricUserCrashIntervalMax, + kMetricUserCrashIntervalBuckets); +} + void MetricsDaemon::SendMetric(const std::string& name, int sample, int min, int max, int nbuckets) { DLOG(INFO) << "received metric: " << name << " " << sample << " " diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index ee8059620..50958b8d9 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -40,11 +40,13 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, NetStateChangedSimpleDrop); FRIEND_TEST(MetricsDaemonTest, NetStateChangedSuspend); FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); + FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash); FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); FRIEND_TEST(MetricsDaemonTest, SendMetric); FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateTimeJump); + FRIEND_TEST(MetricsDaemonTest, UserCrashIntervalReporter); // The network states (see network_states.h). enum NetworkState { @@ -87,6 +89,10 @@ class MetricsDaemon { static const int kMetricTimeToNetworkDropMin; static const int kMetricTimeToNetworkDropMax; static const int kMetricTimeToNetworkDropBuckets; + static const char kMetricUserCrashIntervalName[]; + static const int kMetricUserCrashIntervalMin; + static const int kMetricUserCrashIntervalMax; + static const int kMetricUserCrashIntervalBuckets; // D-Bus message match strings. static const char* kDBusMatches_[]; @@ -142,6 +148,10 @@ class MetricsDaemon { // the usage file, the |seconds| are accumulated. void LogDailyUseRecord(int day, int seconds); + // Updates the active use time and logs time between user-space + // process crashes. + void ProcessUserCrash(); + // Callbacks for the daily use monitor. The daily use monitor uses // LogDailyUseRecord to aggregate current usage data and send it to // UMA, if necessary. It also reschedules itself using an @@ -169,8 +179,14 @@ class MetricsDaemon { void SendMetric(const std::string& name, int sample, int min, int max, int nbuckets); + // TaggedCounter callback to process aggregated daily usage data and + // send to UMA. static void DailyUseReporter(void* data, int tag, int count); + // TaggedCounter callback to process time between user-space process + // crashes and send to UMA. + static void UserCrashIntervalReporter(void* data, int tag, int count); + // Test mode. bool testing_; @@ -195,13 +211,17 @@ class MetricsDaemon { // started, screen is not locked. bool user_active_; - // Timestamps last user active update. Active use time is - // aggregated each day before sending to UMA so using time since the - // epoch as the timestamp. + // Timestamps last user active update. Active use time is aggregated + // each day before sending to UMA so using time since the epoch as + // the timestamp. base::Time user_active_last_; + // Daily active use time in seconds. scoped_ptr daily_use_; + // Active use time between user-space process crashes. + scoped_ptr user_crash_interval_; + // Sleep period until the next daily usage aggregation performed by // the daily use monitor (see ScheduleUseMonitor). int usemon_interval_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 7119c8506..1f2c0fa45 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -42,12 +42,19 @@ class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { EXPECT_EQ(NULL, daemon_.daily_use_.get()); + EXPECT_EQ(NULL, daemon_.user_crash_interval_.get()); daemon_.Init(true, &metrics_lib_); // Tests constructor initialization. Switches to mock counters. EXPECT_TRUE(NULL != daemon_.daily_use_.get()); + EXPECT_TRUE(NULL != daemon_.user_crash_interval_.get()); + + // Allocates mock counter and transfers ownership. daily_use_ = new StrictMock(); - daemon_.daily_use_.reset(daily_use_); // Transfers ownership. + daemon_.daily_use_.reset(daily_use_); + user_crash_interval_ = new StrictMock(); + daemon_.user_crash_interval_.reset(user_crash_interval_); + EXPECT_FALSE(daemon_.user_active_); EXPECT_TRUE(daemon_.user_active_last_.is_null()); EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); @@ -58,10 +65,24 @@ class MetricsDaemonTest : public testing::Test { virtual void TearDown() {} - // Adds a daily use aggregation counter expectation that the + // Adds active use aggregation counters update expectations that the // specified tag/count update will be generated. - void ExpectDailyUseUpdate(int tag, int count) { - EXPECT_CALL(*daily_use_, Update(tag, count)) + void ExpectActiveUseUpdate(int daily_tag, int count) { + EXPECT_CALL(*daily_use_, Update(daily_tag, count)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*user_crash_interval_, Update(0, count)) + .Times(1) + .RetiresOnSaturation(); + } + + // Adds active use aggregation counters update expectations that + // ignore the update arguments. + void IgnoreActiveUseUpdate() { + EXPECT_CALL(*daily_use_, Update(_, _)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*user_crash_interval_, Update(_, _)) .Times(1) .RetiresOnSaturation(); } @@ -134,11 +155,11 @@ class MetricsDaemonTest : public testing::Test { // metric generation calls are marked as failures. StrictMock metrics_lib_; - // Daily use time aggregation counter mock. It's a strict mock so - // that all unexpected update calls are marked as failures. It's a - // pointer so that it can replace the scoped_ptr allocated by the - // daemon. + // Counter mocks. They are strict mocks so that all unexpected + // update calls are marked as failures. They are pointers so that + // they can replace the scoped_ptr's allocated by the daemon. StrictMock* daily_use_; + StrictMock* user_crash_interval_; }; TEST_F(MetricsDaemonTest, DailyUseReporter) { @@ -147,6 +168,10 @@ TEST_F(MetricsDaemonTest, DailyUseReporter) { ExpectDailyUseTimeMetric(/* sample */ 1); MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 23, /* count */ 89); + + // There should be no metrics generated for the calls below. + MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 50, /* count */ 0); + MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 60, /* count */ -5); } TEST_F(MetricsDaemonTest, LookupNetworkState) { @@ -183,6 +208,18 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res); DeleteDBusMessage(msg); + IgnoreActiveUseUpdate(); + EXPECT_CALL(*user_crash_interval_, Flush()) + .Times(1) + .RetiresOnSaturation(); + msg = NewDBusSignalString("/", + "org.chromium.CrashReporter", + "UserCrash", + ""); + res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); + DeleteDBusMessage(msg); + msg = NewDBusSignalString("/", "org.chromium.flimflam.Manager", "StateChanged", @@ -203,9 +240,7 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); - EXPECT_CALL(*daily_use_, Update(_, 0)) - .Times(1) - .RetiresOnSaturation(); + IgnoreActiveUseUpdate(); msg = NewDBusSignalString("/", "org.chromium.PowerManager", "ScreenIsUnlocked", @@ -216,9 +251,7 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); - EXPECT_CALL(*daily_use_, Update(_, 0)) - .Times(1) - .RetiresOnSaturation(); + IgnoreActiveUseUpdate(); msg = NewDBusSignalString("/org/chromium/SessionManager", "org.chromium.SessionManagerInterface", "SessionStateChanged", @@ -283,13 +316,13 @@ TEST_F(MetricsDaemonTest, NetStateChangedSuspend) { } TEST_F(MetricsDaemonTest, PowerStateChanged) { - ExpectDailyUseUpdate(7, 0); + ExpectActiveUseUpdate(7, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(7 * kSecondsPerDay + 15)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 15), daemon_.user_active_last_); - ExpectDailyUseUpdate(7, 30); + ExpectActiveUseUpdate(7, 30); daemon_.PowerStateChanged("mem", TestTime(7 * kSecondsPerDay + 45)); EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); @@ -300,13 +333,21 @@ TEST_F(MetricsDaemonTest, PowerStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_); - ExpectDailyUseUpdate(7, 0); + ExpectActiveUseUpdate(7, 0); daemon_.PowerStateChanged("otherstate", TestTime(7 * kSecondsPerDay + 185)); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 185), daemon_.user_active_last_); } +TEST_F(MetricsDaemonTest, ProcessUserCrash) { + IgnoreActiveUseUpdate(); + EXPECT_CALL(*user_crash_interval_, Flush()) + .Times(1) + .RetiresOnSaturation(); + daemon_.ProcessUserCrash(); +} + TEST_F(MetricsDaemonTest, SendMetric) { ExpectMetric("Dummy.Metric", 3, 1, 100, 50); daemon_.SendMetric("Dummy.Metric", /* sample */ 3, @@ -314,19 +355,19 @@ TEST_F(MetricsDaemonTest, SendMetric) { } TEST_F(MetricsDaemonTest, SessionStateChanged) { - ExpectDailyUseUpdate(15, 0); + ExpectActiveUseUpdate(15, 0); daemon_.SessionStateChanged("started", TestTime(15 * kSecondsPerDay + 20)); EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(15 * kSecondsPerDay + 20), daemon_.user_active_last_); - ExpectDailyUseUpdate(15, 130); + ExpectActiveUseUpdate(15, 130); daemon_.SessionStateChanged("stopped", TestTime(15 * kSecondsPerDay + 150)); EXPECT_EQ(MetricsDaemon::kSessionStateStopped, daemon_.session_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(15 * kSecondsPerDay + 150), daemon_.user_active_last_); - ExpectDailyUseUpdate(15, 0); + ExpectActiveUseUpdate(15, 0); daemon_.SessionStateChanged("otherstate", TestTime(15 * kSecondsPerDay + 300)); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); @@ -335,31 +376,31 @@ TEST_F(MetricsDaemonTest, SessionStateChanged) { } TEST_F(MetricsDaemonTest, SetUserActiveState) { - ExpectDailyUseUpdate(5, 0); + ExpectActiveUseUpdate(5, 0); daemon_.SetUserActiveState(/* active */ false, TestTime(5 * kSecondsPerDay + 10)); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(5 * kSecondsPerDay + 10), daemon_.user_active_last_); - ExpectDailyUseUpdate(6, 0); + ExpectActiveUseUpdate(6, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(6 * kSecondsPerDay + 20)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(6 * kSecondsPerDay + 20), daemon_.user_active_last_); - ExpectDailyUseUpdate(6, 100); + ExpectActiveUseUpdate(6, 100); daemon_.SetUserActiveState(/* active */ true, TestTime(6 * kSecondsPerDay + 120)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(6 * kSecondsPerDay + 120), daemon_.user_active_last_); - ExpectDailyUseUpdate(6, 110); + ExpectActiveUseUpdate(6, 110); daemon_.SetUserActiveState(/* active */ false, TestTime(6 * kSecondsPerDay + 230)); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(6 * kSecondsPerDay + 230), daemon_.user_active_last_); - ExpectDailyUseUpdate(6, 0); + ExpectActiveUseUpdate(6, 0); daemon_.SetUserActiveState(/* active */ false, TestTime(6 * kSecondsPerDay + 260)); EXPECT_FALSE(daemon_.user_active_); @@ -367,25 +408,33 @@ TEST_F(MetricsDaemonTest, SetUserActiveState) { } TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) { - ExpectDailyUseUpdate(10, 0); + ExpectActiveUseUpdate(10, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 500)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 500), daemon_.user_active_last_); - ExpectDailyUseUpdate(10, 0); + ExpectActiveUseUpdate(10, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 300)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 300), daemon_.user_active_last_); - ExpectDailyUseUpdate(10, 0); + ExpectActiveUseUpdate(10, 0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 1000)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_); } +TEST_F(MetricsDaemonTest, UserCrashIntervalReporter) { + ExpectMetric(MetricsDaemon::kMetricUserCrashIntervalName, 50, + MetricsDaemon::kMetricUserCrashIntervalMin, + MetricsDaemon::kMetricUserCrashIntervalMax, + MetricsDaemon::kMetricUserCrashIntervalBuckets); + MetricsDaemon::UserCrashIntervalReporter(&daemon_, 0, 50); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 10b301da5759282417e2e94594838070e3ebacf5 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Thu, 17 Jun 2010 14:22:43 -0700 Subject: [PATCH 026/200] add C wrapper for libmetrics TEST=unit tests + build&test crosmetrics plugin for flimflam Review URL: http://codereview.chromium.org/2832008 --- metrics/Makefile | 1 + metrics/c_metrics_library.cc | 44 ++++++++++++++++++++++++++++++ metrics/c_metrics_library.h | 33 +++++++++++++++++++++++ metrics/metrics_library.h | 1 + metrics/metrics_library_test.cc | 47 +++++++++++++++++++++++++++++++-- 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 metrics/c_metrics_library.cc create mode 100644 metrics/c_metrics_library.h diff --git a/metrics/Makefile b/metrics/Makefile index c8ba54517..fa68a401c 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -32,6 +32,7 @@ TESTDAEMON_OBJS = \ metrics_daemon.o \ metrics_daemon_test.o LIB_OBJS = \ + c_metrics_library.o \ metrics_library.o TESTLIB_OBJS = \ metrics_library.o \ diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc new file mode 100644 index 000000000..91e9ca6e3 --- /dev/null +++ b/metrics/c_metrics_library.cc @@ -0,0 +1,44 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// +// C wrapper to libmetrics +// + +#include "c_metrics_library.h" +#include "metrics_library.h" + +extern "C" CMetricsLibrary CMetricsLibraryNew(void) { + MetricsLibrary* lib = new MetricsLibrary; + return reinterpret_cast(lib); +} + +extern "C" void CMetricsLibraryDelete(CMetricsLibrary handle) { + MetricsLibrary* lib = reinterpret_cast(handle); + delete lib; +} + +extern "C" void CMetricsLibraryInit(CMetricsLibrary handle) { + MetricsLibrary* lib = reinterpret_cast(handle); + if (lib != NULL) + lib->Init(); +} + +extern "C" int CMetricsLibrarySendToUMA(CMetricsLibrary handle, + const char* name, int sample, + int min, int max, int nbuckets) { + MetricsLibrary* lib = reinterpret_cast(handle); + if (lib == NULL) + return 0; + return lib->SendToUMA(std::string(name), sample, min, max, nbuckets); +} + +extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, + const char* name, int sample, + int max) { + MetricsLibrary* lib = reinterpret_cast(handle); + if (lib == NULL) + return 0; + return lib->SendEnumToUMA(std::string(name), sample, max); +} diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h new file mode 100644 index 000000000..6d1e6f907 --- /dev/null +++ b/metrics/c_metrics_library.h @@ -0,0 +1,33 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef C_METRICS_LIBRARY_H_ +#define C_METRICS_LIBRARY_H_ + +#if defined(__cplusplus) +extern "C" { +#endif +typedef struct CMetricsLibraryOpaque* CMetricsLibrary; + +// C wrapper for MetricsLibrary::MetricsLibrary. +CMetricsLibrary CMetricsLibraryNew(void); + +// C wrapper for MetricsLibrary::~MetricsLibrary. +void CMetricsLibraryDelete(CMetricsLibrary handle); + +// C wrapper for MetricsLibrary::Init. +void CMetricsLibraryInit(CMetricsLibrary handle); + +// C wrapper for MetricsLibrary::SendToUMA. +int CMetricsLibrarySendToUMA(CMetricsLibrary handle, + const char* name, int sample, + int min, int max, int nbuckets); + +// C wrapper for MetricsLibrary::SendEnumToUMA. +int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, + const char* name, int sample, int max); +#if defined(__cplusplus) +} +#endif +#endif // C_METRICS_LIBRARY_H_ diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index a9ac0bb10..80bea70a3 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -56,6 +56,7 @@ class MetricsLibrary : public MetricsLibraryInterface { static bool SendToAutotest(const std::string& name, int value); private: + friend class CMetricsLibraryTest; friend class MetricsLibraryTest; FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage); FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong); diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index f585d3507..596abd3e6 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -2,13 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "metrics_library.h" - #include #include #include +#include "c_metrics_library.h" +#include "metrics_library.h" + static const FilePath kTestUMAEventsFile("test-uma-events"); class MetricsLibraryTest : public testing::Test { @@ -83,6 +84,48 @@ TEST_F(MetricsLibraryTest, SendToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } +class CMetricsLibraryTest : public testing::Test { + protected: + virtual void SetUp() { + lib_ = CMetricsLibraryNew(); + MetricsLibrary& ml = *reinterpret_cast(lib_); + EXPECT_EQ(NULL, ml.uma_events_file_); + CMetricsLibraryInit(lib_); + EXPECT_TRUE(NULL != ml.uma_events_file_); + ml.uma_events_file_ = kTestUMAEventsFile.value().c_str(); + } + + virtual void TearDown() { + CMetricsLibraryDelete(lib_); + file_util::Delete(kTestUMAEventsFile, false); + } + + CMetricsLibrary lib_; +}; + +TEST_F(CMetricsLibraryTest, SendEnumToUMA) { + char buf[100]; + const int kLen = 40; + EXPECT_TRUE(CMetricsLibrarySendEnumToUMA(lib_, "Test.EnumMetric", 1, 3)); + EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + + char exp[kLen]; + sprintf(exp, "%c%c%c%clinearhistogram%cTest.EnumMetric 1 3", + kLen, 0, 0, 0, 0); + EXPECT_EQ(0, memcmp(exp, buf, kLen)); +} + +TEST_F(CMetricsLibraryTest, SendToUMA) { + char buf[100]; + const int kLen = 37; + EXPECT_TRUE(CMetricsLibrarySendToUMA(lib_, "Test.Metric", 2, 1, 100, 50)); + EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + + char exp[kLen]; + sprintf(exp, "%c%c%c%chistogram%cTest.Metric 2 1 100 50", kLen, 0, 0, 0, 0); + EXPECT_EQ(0, memcmp(exp, buf, kLen)); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 32e1df9d6ce846ce194a43bce9e8c28936ea3a1b Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 17 Jun 2010 17:05:06 -0700 Subject: [PATCH 027/200] Update the README to mention that a C API is also available. Review URL: http://codereview.chromium.org/2819008 --- metrics/README | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/metrics/README b/metrics/README index 6fec766f0..42193796e 100644 --- a/metrics/README +++ b/metrics/README @@ -11,10 +11,10 @@ transport to the UMA server. The Metrics Library: libmetrics ================================================================================ -libmetrics is a small library that implements the basic C++ API for -metrics collection. All metrics collection is funneled through this -library. The easiest and recommended way for a client-side module to -collect user metrics is to link libmetrics and use its APIs to send +libmetrics is a small library that implements the basic C and C++ API +for metrics collection. All metrics collection is funneled through +this library. The easiest and recommended way for a client-side module +to collect user metrics is to link libmetrics and use its APIs to send metrics to Chrome for transport to UMA. In order to use the library in a module, you need to do the following: @@ -47,6 +47,8 @@ a module, you need to do the following: complete API documentation in metrics_library.h under src/platform/metrics/. + For more information on the C API see c_metrics_library.h. + - On the target platform, shortly after the sample is sent it should be visible in Chrome through "about:histograms". @@ -59,7 +61,6 @@ Use TrackerArea.MetricName. For example: Logging.CrashCounter Network.TimeToDrop -Platform.BootTime ================================================================================ From c2bf95fd8be2848d343cea52474bfe960499ba1d Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Mon, 21 Jun 2010 16:27:52 -0700 Subject: [PATCH 028/200] Update libmetrics docs to cover some recent questions and issues. Review URL: http://codereview.chromium.org/2828017 --- metrics/README | 33 +++++++++++++++++++++++++++++++++ metrics/metrics_library.h | 11 +++++++++++ 2 files changed, 44 insertions(+) diff --git a/metrics/README b/metrics/README index 42193796e..677e6b75c 100644 --- a/metrics/README +++ b/metrics/README @@ -116,3 +116,36 @@ signals to or communicate in some alternative way with the metrics daemon. Then the metrics daemon needs to monitor for the relevant events and take appropriate action -- for example, aggregate data and send the histogram samples. + + +================================================================================ +FAQ +================================================================================ + +Q. What should my histogram's |min| and |max| values be set at? + +A. You should set the values to a range that covers the vast majority + of samples that would appear in the field. Note that samples below + the |min| will still be collected in the underflow bucket and + samples above the |max| will end up in the overflow bucket. Also, + the reported mean of the data will be correct regardless of the + range. + +Q. How many buckets should I use in my histogram? + +A. You should allocate as many buckets as necessary to perform proper + analysis on the collected data. Note, however, that the memory + allocated in Chrome for each histogram is proportional to the + number of buckets. Therefore, it is strongly recommended to keep + this number low (e.g., 50 is normal, while 100 is probably high). + +Q. When should I use an enumeration (linear) histogram vs. a regular + (exponential) histogram? + +A. Enumeration histograms should really be used only for sampling + enumerated events and, in some cases, percentages. Normally, you + should use a regular histogram with exponential bucket layout that + provides higher resolution at the low end of the range and lower + resolution at the high end. Regular histograms are generally used + for collecting performance data (e.g., timing, memory usage, power) + as well as aggregated event counts. diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 80bea70a3..76fa451ea 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -38,6 +38,11 @@ class MetricsLibrary : public MetricsLibraryInterface { // |nbuckets| is the number of histogram buckets. // [0,min) is the implicit underflow bucket. // [|max|,infinity) is the implicit overflow bucket. + // + // Note that the memory allocated in Chrome for each histogram is + // proportional to the number of buckets. Therefore, it is strongly + // recommended to keep this number low (e.g., 50 is normal, while + // 100 is high). bool SendToUMA(const std::string& name, int sample, int min, int max, int nbuckets); @@ -50,6 +55,12 @@ class MetricsLibrary : public MetricsLibraryInterface { // |max| is the maximum value of the histogram samples. // 0 is the implicit underflow bucket. // [|max|,infinity) is the implicit overflow bucket. + // + // An enumaration histogram requires |max| + 1 number of + // buckets. Note that the memory allocated in Chrome for each + // histogram is proportional to the number of buckets. Therefore, it + // is strongly recommended to keep this number low (e.g., 50 is + // normal, while 100 is high). bool SendEnumToUMA(const std::string& name, int sample, int max); // Sends to Autotest and returns true on success. From cd8c3174531f782d73f6dabae333eb279febc4a4 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 24 Jun 2010 10:13:54 -0700 Subject: [PATCH 029/200] Readability review. Review URL: http://codereview.chromium.org/2729018 --- metrics/counter.cc | 31 ++++++++++++++++--------------- metrics/counter.h | 32 +++++++++++++++++++------------- metrics/counter_mock.h | 1 + metrics/counter_test.cc | 19 ++++++++++--------- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/metrics/counter.cc b/metrics/counter.cc index 58dbae457..b81a0d1be 100644 --- a/metrics/counter.cc +++ b/metrics/counter.cc @@ -4,7 +4,7 @@ #include "counter.h" -#include +#include #include #include @@ -12,26 +12,28 @@ namespace chromeos_metrics { // TaggedCounter::Record implementation. -void TaggedCounter::Record::Init(int tag, int count) { +void TaggedCounter::Record::Init(int32 tag, int32 count) { tag_ = tag; count_ = (count > 0) ? count : 0; } -void TaggedCounter::Record::Add(int count) { +void TaggedCounter::Record::Add(int32 count) { if (count <= 0) return; - count_ += count; - - // Saturates on postive overflow. - if (count_ < 0) { - count_ = INT_MAX; - } + // Saturates on positive overflow. + int64 new_count = static_cast(count_) + static_cast(count); + if (new_count > kint32max) + count_ = kint32max; + else + count_ = static_cast(new_count); } // TaggedCounter implementation. TaggedCounter::TaggedCounter() - : filename_(NULL), reporter_(NULL), reporter_handle_(NULL), + : filename_(NULL), + reporter_(NULL), + reporter_handle_(NULL), record_state_(kRecordInvalid) {} TaggedCounter::~TaggedCounter() {} @@ -45,7 +47,7 @@ void TaggedCounter::Init(const char* filename, record_state_ = kRecordInvalid; } -void TaggedCounter::Update(int tag, int count) { +void TaggedCounter::Update(int32 tag, int32 count) { UpdateInternal(tag, count, false); // No flush. @@ -57,7 +59,7 @@ void TaggedCounter::Flush() { true); // Do flush. } -void TaggedCounter::UpdateInternal(int tag, int count, bool flush) { +void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) { if (flush) { // Flushing but record is null, so nothing to do. if (record_state_ == kRecordNull) @@ -104,11 +106,10 @@ void TaggedCounter::ReadRecord(int fd) { record_state_ = kRecordNullDirty; return; } - record_state_ = kRecordNull; } -void TaggedCounter::ReportRecord(int tag, bool flush) { +void TaggedCounter::ReportRecord(int32 tag, bool flush) { // If no valid record, there's nothing to report. if (record_state_ != kRecordValid) { DCHECK_EQ(record_state_, kRecordNull); @@ -126,7 +127,7 @@ void TaggedCounter::ReportRecord(int tag, bool flush) { record_state_ = kRecordNullDirty; } -void TaggedCounter::UpdateRecord(int tag, int count, bool flush) { +void TaggedCounter::UpdateRecord(int32 tag, int32 count, bool flush) { if (flush) { DCHECK(record_state_ == kRecordNull || record_state_ == kRecordNullDirty); return; diff --git a/metrics/counter.h b/metrics/counter.h index 876b10732..1cfcb51d4 100644 --- a/metrics/counter.h +++ b/metrics/counter.h @@ -5,6 +5,7 @@ #ifndef METRICS_COUNTER_H_ #define METRICS_COUNTER_H_ +#include #include // for FRIEND_TEST namespace chromeos_metrics { @@ -18,6 +19,11 @@ namespace chromeos_metrics { // event counts. The aggregated count is reported through the // callback when the counter is explicitly flushed or when data for a // new tag arrives. +// +// The primary reason for using an interface is to allow easier unit +// testing in clients through mocking thus avoiding file access and +// callbacks. Of course, it also enables alternative implementations +// of the counter with additional features. class TaggedCounterInterface { public: // Callback type used for reporting aggregated or flushed data. @@ -27,7 +33,7 @@ class TaggedCounterInterface { // |handle| is the |reporter_handle| pointer passed through Init. // |tag| is the tag associated with the aggregated count. // |count| is aggregated count. - typedef void (*Reporter)(void* handle, int tag, int count); + typedef void (*Reporter)(void* handle, int32 tag, int32 count); virtual ~TaggedCounterInterface() {} @@ -44,7 +50,7 @@ class TaggedCounterInterface { // Adds |count| of events for the given |tag|. If there's an // existing aggregated count for a different tag, it's reported // through the reporter callback and discarded. - virtual void Update(int tag, int count) = 0; + virtual void Update(int32 tag, int32 count) = 0; // Reports the current aggregated count (if any) through the // reporter callback and discards it. @@ -58,7 +64,7 @@ class TaggedCounter : public TaggedCounterInterface { // Implementation of interface methods. void Init(const char* filename, Reporter reporter, void* reporter_handle); - void Update(int tag, int count); + void Update(int32 tag, int32 count); void Flush(); private: @@ -89,25 +95,25 @@ class TaggedCounter : public TaggedCounterInterface { // Initializes with |tag| and |count|. If |count| is negative, // |count_| is set to 0. - void Init(int tag, int count); + void Init(int32 tag, int32 count); // Adds |count| to the current |count_|. Negative |count| is // ignored. In case of positive overflow, |count_| is saturated to - // INT_MAX. - void Add(int count); + // kint32max. + void Add(int32 count); - int tag() const { return tag_; } - int count() const { return count_; } + int32 tag() const { return tag_; } + int32 count() const { return count_; } private: - int tag_; - int count_; + int32 tag_; + int32 count_; }; // Implementation of the Update and Flush methods. Goes through the // necessary steps to read, report, update, and sync the aggregated // record. - void UpdateInternal(int tag, int count, bool flush); + void UpdateInternal(int32 tag, int32 count, bool flush); // If the current cached record is invalid, reads it from persistent // storage specified through file descriptor |fd| and updates the @@ -119,13 +125,13 @@ class TaggedCounter : public TaggedCounterInterface { // or the new |tag| is different than the old one, reports the // aggregated data through the reporter callback and resets the // cached record. - void ReportRecord(int tag, bool flush); + void ReportRecord(int32 tag, bool flush); // Updates the cached record given the new |tag| and |count|. This // method expects either a null cached record, or a valid cached // record with the same tag as |tag|. If |flush| is true, the method // asserts that the cached record is null and returns. - void UpdateRecord(int tag, int count, bool flush); + void UpdateRecord(int32 tag, int32 count, bool flush); // If the cached record state is dirty, updates the persistent // storage specified through file descriptor |fd| and switches the diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h index 22327ac29..baa97b0bf 100644 --- a/metrics/counter_mock.h +++ b/metrics/counter_mock.h @@ -24,3 +24,4 @@ class TaggedCounterMock : public TaggedCounterInterface { } // namespace chromeos_metrics #endif // METRICS_COUNTER_MOCK_H_ + diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc index 605e859dc..fd5a38939 100644 --- a/metrics/counter_test.cc +++ b/metrics/counter_test.cc @@ -62,8 +62,8 @@ class TaggedCounterTest : public testing::Test { // Asserts that the record file contains the specified contents. testing::AssertionResult AssertRecord(const char* expr_tag, const char* expr_count, - int expected_tag, - int expected_count) { + int32 expected_tag, + int32 expected_count) { int fd = HANDLE_EINTR(open(kTestRecordFile, O_RDONLY)); if (fd < 0) { testing::Message msg; @@ -105,7 +105,7 @@ class TaggedCounterTest : public testing::Test { // Adds a reporter call expectation that the specified tag/count // callback will be generated. - void ExpectReporterCall(int tag, int count) { + void ExpectReporterCall(int32 tag, int32 count) { EXPECT_CALL(reporter_, Call(_, tag, count)) .Times(1) .RetiresOnSaturation(); @@ -113,7 +113,7 @@ class TaggedCounterTest : public testing::Test { // The reporter callback forwards the call to the reporter mock so // that we can set call expectations. - static void Reporter(void* handle, int tag, int count) { + static void Reporter(void* handle, int32 tag, int32 count) { TaggedCounterTest* test = static_cast(handle); ASSERT_FALSE(NULL == test); test->reporter_.Call(handle, tag, count); @@ -130,7 +130,7 @@ class TaggedCounterTest : public testing::Test { } // Returns true if the counter log contains |pattern|, false otherwise. - bool LogContains(const std::string& pattern) { + bool LogContains(const std::string& pattern) const { return log_.find(pattern) != std::string::npos; } @@ -141,7 +141,8 @@ class TaggedCounterTest : public testing::Test { std::string log_; // Reporter mock to set callback expectations on. - StrictMock > reporter_; + StrictMock > reporter_; // Pointer to the current test fixture. static TaggedCounterTest* test_; @@ -173,11 +174,11 @@ TEST_F(RecordTest, Add) { record_.Add(/* count */ -2); EXPECT_EQ(15, record_.count()); - record_.Add(/* count */ INT_MAX); - EXPECT_EQ(INT_MAX, record_.count()); + record_.Add(/* count */ kint32max); + EXPECT_EQ(kint32max, record_.count()); record_.Add(/* count */ 1); - EXPECT_EQ(INT_MAX, record_.count()); + EXPECT_EQ(kint32max, record_.count()); } TEST_F(TaggedCounterTest, BadFileLocation) { From 38d5cb09ef3e4436ade3f236806ac1cf31357af4 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 24 Jun 2010 12:10:26 -0700 Subject: [PATCH 030/200] Log active use time between kernel crashes. Also, initialize the network state from flimflam, just in case -- now that the metrics daemon process starts a bit late, BUG=none TEST=unit tests, ran on the device, emerged arm-generic Review URL: http://codereview.chromium.org/2864009 --- metrics/Makefile | 12 ---- metrics/metrics_daemon.cc | 121 ++++++++++++++++++++++++++++++--- metrics/metrics_daemon.h | 22 ++++++ metrics/metrics_daemon_test.cc | 42 ++++++++++++ 4 files changed, 174 insertions(+), 23 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index fa68a401c..298607876 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -35,7 +35,6 @@ LIB_OBJS = \ c_metrics_library.o \ metrics_library.o TESTLIB_OBJS = \ - metrics_library.o \ metrics_library_test.o TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread @@ -71,17 +70,6 @@ $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) %.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ -# dependencies in addition to those defined by the rules - -metrics_daemon.o: \ - metrics_daemon.h \ - network_states.h \ - power_states.h -metrics_daemon_test.o: \ - metrics_daemon.h \ - network_states.h \ - power_states.h - clean: rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) *.o rm -f $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 6cbe8e812..30d3da8ae 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -6,6 +6,7 @@ #include +#include #include #include "counter.h" @@ -13,6 +14,7 @@ using base::Time; using base::TimeDelta; using base::TimeTicks; +using std::string; #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") #define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter" @@ -20,13 +22,6 @@ using base::TimeTicks; #define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager" #define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface" -// File to aggregate daily usage before sending to UMA. -// TODO(petkov): This file should probably live in a user-specific stateful -// location, e.g., /home/chronos/user. -static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage"; -static const char kUserCrashIntervalRecordFile[] = - "/var/log/metrics/user-crash-interval"; - static const int kSecondsPerMinute = 60; static const int kMinutesPerHour = 60; static const int kHoursPerDay = 24; @@ -51,6 +46,12 @@ const int MetricsDaemon::kMetricDailyUseTimeMin = 1; const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay; const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50; +const char MetricsDaemon::kMetricKernelCrashIntervalName[] = + "Logging.KernelCrashInterval"; +const int MetricsDaemon::kMetricKernelCrashIntervalMin = 1; +const int MetricsDaemon::kMetricKernelCrashIntervalMax = 4 * kSecondsPerWeek; +const int MetricsDaemon::kMetricKernelCrashIntervalBuckets = 50; + const char MetricsDaemon::kMetricTimeToNetworkDropName[] = "Network.TimeToDrop"; const int MetricsDaemon::kMetricTimeToNetworkDropMin = 1; @@ -106,6 +107,53 @@ const char* MetricsDaemon::kSessionStates_[] = { #include "session_states.h" }; +// Invokes a remote method over D-Bus that takes no input arguments +// and returns a string result. The method call is issued with a 2 +// second blocking timeout. Returns an empty string on failure or +// timeout. +static string DBusGetString(DBusConnection* connection, + const string& destination, + const string& path, + const string& interface, + const string& method) { + DBusMessage* message = + dbus_message_new_method_call(destination.c_str(), + path.c_str(), + interface.c_str(), + method.c_str()); + if (!message) { + DLOG(WARNING) << "DBusGetString: unable to allocate a message"; + return ""; + } + + DBusError error; + dbus_error_init(&error); + const int kTimeout = 2000; // ms + DLOG(INFO) << "DBusGetString: dest=" << destination << " path=" << path + << " iface=" << interface << " method=" << method; + DBusMessage* reply = + dbus_connection_send_with_reply_and_block(connection, message, kTimeout, + &error); + dbus_message_unref(message); + if (dbus_error_is_set(&error) || !reply) { + DLOG(WARNING) << "DBusGetString: call failed"; + return ""; + } + DBusMessageIter iter; + dbus_message_iter_init(reply, &iter); + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { + NOTREACHED(); + dbus_message_unref(reply); + return ""; + } + const char* c_result = ""; + dbus_message_iter_get_basic(&iter, &c_result); + string result = c_result; + DLOG(INFO) << "DBusGetString: result=" << result; + dbus_message_unref(reply); + return result; +} + MetricsDaemon::MetricsDaemon() : network_state_(kUnknownNetworkState), power_state_(kUnknownPowerState), @@ -117,21 +165,35 @@ MetricsDaemon::MetricsDaemon() MetricsDaemon::~MetricsDaemon() {} void MetricsDaemon::Run(bool run_as_daemon) { - if (!run_as_daemon || daemon(0, 0) == 0) { - Loop(); - } + if (run_as_daemon && daemon(0, 0) != 0) + return; + + static const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; + CheckKernelCrash(kKernelCrashDetectedFile); + Loop(); } void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; + + static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage"; daily_use_.reset(new chromeos_metrics::TaggedCounter()); daily_use_->Init(kDailyUseRecordFile, &DailyUseReporter, this); + + static const char kUserCrashIntervalRecordFile[] = + "/var/log/metrics/user-crash-interval"; user_crash_interval_.reset(new chromeos_metrics::TaggedCounter()); user_crash_interval_->Init(kUserCrashIntervalRecordFile, &UserCrashIntervalReporter, this); + static const char kKernelCrashIntervalRecordFile[] = + "/var/log/metrics/kernel-crash-interval"; + kernel_crash_interval_.reset(new chromeos_metrics::TaggedCounter()); + kernel_crash_interval_->Init(kKernelCrashIntervalRecordFile, + &KernelCrashIntervalReporter, this); + // Don't setup D-Bus and GLib in test mode. if (testing) return; @@ -162,6 +224,11 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { // the registered D-Bus matches is successful. The daemon is not // activated for D-Bus messages that don't match. CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL)); + + // Initializes the current network state by retrieving it from flimflam. + string state_name = DBusGetString(connection, "org.chromium.flimflam", "/", + DBUS_IFACE_FLIMFLAM_MANAGER, "GetState"); + NetStateChanged(state_name.c_str(), TimeTicks::Now()); } void MetricsDaemon::Loop() { @@ -320,6 +387,7 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { int day = since_epoch.InDays(); daily_use_->Update(day, seconds); user_crash_interval_->Update(0, seconds); + kernel_crash_interval_->Update(0, seconds); // Schedules a use monitor on inactive->active transitions and // unschedules it on active->inactive transitions. @@ -342,6 +410,27 @@ void MetricsDaemon::ProcessUserCrash() { user_crash_interval_->Flush(); } +void MetricsDaemon::ProcessKernelCrash() { + // Counts the active use time up to now. + SetUserActiveState(user_active_, Time::Now()); + + // Reports the active use time since the last crash and resets it. + kernel_crash_interval_->Flush(); +} + +void MetricsDaemon::CheckKernelCrash(const std::string& crash_file) { + FilePath crash_detected(crash_file); + if (!file_util::PathExists(crash_detected)) + return; + + ProcessKernelCrash(); + + // Deletes the crash-detected file so that the daemon doesn't report + // another kernel crash in case it's restarted. + file_util::Delete(crash_detected, + false); // recursive +} + // static gboolean MetricsDaemon::UseMonitorStatic(gpointer data) { return static_cast(data)->UseMonitor() ? TRUE : FALSE; @@ -425,7 +514,17 @@ void MetricsDaemon::UserCrashIntervalReporter(void* handle, kMetricUserCrashIntervalBuckets); } -void MetricsDaemon::SendMetric(const std::string& name, int sample, +// static +void MetricsDaemon::KernelCrashIntervalReporter(void* handle, + int tag, int count) { + MetricsDaemon* daemon = static_cast(handle); + daemon->SendMetric(kMetricKernelCrashIntervalName, count, + kMetricKernelCrashIntervalMin, + kMetricKernelCrashIntervalMax, + kMetricKernelCrashIntervalBuckets); +} + +void MetricsDaemon::SendMetric(const string& name, int sample, int min, int max, int nbuckets) { DLOG(INFO) << "received metric: " << name << " " << sample << " " << min << " " << max << " " << nbuckets; diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 50958b8d9..437cafd0e 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -31,7 +31,9 @@ class MetricsDaemon { private: friend class MetricsDaemonTest; + FRIEND_TEST(MetricsDaemonTest, CheckKernelCrash); FRIEND_TEST(MetricsDaemonTest, DailyUseReporter); + FRIEND_TEST(MetricsDaemonTest, KernelCrashIntervalReporter); FRIEND_TEST(MetricsDaemonTest, LookupNetworkState); FRIEND_TEST(MetricsDaemonTest, LookupPowerState); FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); @@ -40,6 +42,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, NetStateChangedSimpleDrop); FRIEND_TEST(MetricsDaemonTest, NetStateChangedSuspend); FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); + FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash); FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash); FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); FRIEND_TEST(MetricsDaemonTest, SendMetric); @@ -85,6 +88,10 @@ class MetricsDaemon { static const int kMetricDailyUseTimeMin; static const int kMetricDailyUseTimeMax; static const int kMetricDailyUseTimeBuckets; + static const char kMetricKernelCrashIntervalName[]; + static const int kMetricKernelCrashIntervalMin; + static const int kMetricKernelCrashIntervalMax; + static const int kMetricKernelCrashIntervalBuckets; static const char kMetricTimeToNetworkDropName[]; static const int kMetricTimeToNetworkDropMin; static const int kMetricTimeToNetworkDropMax; @@ -152,6 +159,14 @@ class MetricsDaemon { // process crashes. void ProcessUserCrash(); + // Updates the active use time and logs time between kernel crashes. + void ProcessKernelCrash(); + + // Checks if a kernel crash has been detected and processes if so. + // The method assumes that a kernel crash has happened if + // |crash_file| exists. + void CheckKernelCrash(const std::string& crash_file); + // Callbacks for the daily use monitor. The daily use monitor uses // LogDailyUseRecord to aggregate current usage data and send it to // UMA, if necessary. It also reschedules itself using an @@ -187,6 +202,10 @@ class MetricsDaemon { // crashes and send to UMA. static void UserCrashIntervalReporter(void* data, int tag, int count); + // TaggedCounter callback to process time between kernel crashes and + // send to UMA. + static void KernelCrashIntervalReporter(void* data, int tag, int count); + // Test mode. bool testing_; @@ -222,6 +241,9 @@ class MetricsDaemon { // Active use time between user-space process crashes. scoped_ptr user_crash_interval_; + // Active use time between kernel crashes. + scoped_ptr kernel_crash_interval_; + // Sleep period until the next daily usage aggregation performed by // the daily use monitor (see ScheduleUseMonitor). int usemon_interval_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 1f2c0fa45..e75c1616c 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include #include #include "counter_mock.h" @@ -42,16 +43,20 @@ class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { EXPECT_EQ(NULL, daemon_.daily_use_.get()); + EXPECT_EQ(NULL, daemon_.kernel_crash_interval_.get()); EXPECT_EQ(NULL, daemon_.user_crash_interval_.get()); daemon_.Init(true, &metrics_lib_); // Tests constructor initialization. Switches to mock counters. EXPECT_TRUE(NULL != daemon_.daily_use_.get()); + EXPECT_TRUE(NULL != daemon_.kernel_crash_interval_.get()); EXPECT_TRUE(NULL != daemon_.user_crash_interval_.get()); // Allocates mock counter and transfers ownership. daily_use_ = new StrictMock(); daemon_.daily_use_.reset(daily_use_); + kernel_crash_interval_ = new StrictMock(); + daemon_.kernel_crash_interval_.reset(kernel_crash_interval_); user_crash_interval_ = new StrictMock(); daemon_.user_crash_interval_.reset(user_crash_interval_); @@ -71,6 +76,9 @@ class MetricsDaemonTest : public testing::Test { EXPECT_CALL(*daily_use_, Update(daily_tag, count)) .Times(1) .RetiresOnSaturation(); + EXPECT_CALL(*kernel_crash_interval_, Update(0, count)) + .Times(1) + .RetiresOnSaturation(); EXPECT_CALL(*user_crash_interval_, Update(0, count)) .Times(1) .RetiresOnSaturation(); @@ -82,6 +90,9 @@ class MetricsDaemonTest : public testing::Test { EXPECT_CALL(*daily_use_, Update(_, _)) .Times(1) .RetiresOnSaturation(); + EXPECT_CALL(*kernel_crash_interval_, Update(_, _)) + .Times(1) + .RetiresOnSaturation(); EXPECT_CALL(*user_crash_interval_, Update(_, _)) .Times(1) .RetiresOnSaturation(); @@ -159,9 +170,24 @@ class MetricsDaemonTest : public testing::Test { // update calls are marked as failures. They are pointers so that // they can replace the scoped_ptr's allocated by the daemon. StrictMock* daily_use_; + StrictMock* kernel_crash_interval_; StrictMock* user_crash_interval_; }; +TEST_F(MetricsDaemonTest, CheckKernelCrash) { + static const char kKernelCrashDetected[] = "test-kernel-crash-detected"; + daemon_.CheckKernelCrash(kKernelCrashDetected); + + FilePath crash_detected(kKernelCrashDetected); + file_util::WriteFile(crash_detected, "", 0); + IgnoreActiveUseUpdate(); + EXPECT_CALL(*kernel_crash_interval_, Flush()) + .Times(1) + .RetiresOnSaturation(); + daemon_.CheckKernelCrash(kKernelCrashDetected); + file_util::Delete(crash_detected, false); +} + TEST_F(MetricsDaemonTest, DailyUseReporter) { ExpectDailyUseTimeMetric(/* sample */ 2); MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 20, /* count */ 90); @@ -174,6 +200,14 @@ TEST_F(MetricsDaemonTest, DailyUseReporter) { MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 60, /* count */ -5); } +TEST_F(MetricsDaemonTest, KernelCrashIntervalReporter) { + ExpectMetric(MetricsDaemon::kMetricKernelCrashIntervalName, 50, + MetricsDaemon::kMetricKernelCrashIntervalMin, + MetricsDaemon::kMetricKernelCrashIntervalMax, + MetricsDaemon::kMetricKernelCrashIntervalBuckets); + MetricsDaemon::KernelCrashIntervalReporter(&daemon_, 0, 50); +} + TEST_F(MetricsDaemonTest, LookupNetworkState) { EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.LookupNetworkState("online")); @@ -340,6 +374,14 @@ TEST_F(MetricsDaemonTest, PowerStateChanged) { EXPECT_EQ(TestTime(7 * kSecondsPerDay + 185), daemon_.user_active_last_); } +TEST_F(MetricsDaemonTest, ProcessKernelCrash) { + IgnoreActiveUseUpdate(); + EXPECT_CALL(*kernel_crash_interval_, Flush()) + .Times(1) + .RetiresOnSaturation(); + daemon_.ProcessKernelCrash(); +} + TEST_F(MetricsDaemonTest, ProcessUserCrash) { IgnoreActiveUseUpdate(); EXPECT_CALL(*user_crash_interval_, Flush()) From d2f284bfe2fc9dfad40cab4e19f2a2f587a0b42e Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 30 Jun 2010 09:47:16 -0700 Subject: [PATCH 031/200] Add crash reporting to metrics_daemon. BUG=none TEST=unit tests,gmerge'd on device, verified it runs OK, pkill -SEGV generated minidump. Review URL: http://codereview.chromium.org/2806043 --- metrics/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/Makefile b/metrics/Makefile index 298607876..480da220a 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -53,7 +53,7 @@ $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) $(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) + $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) -lcrash $(DAEMON_TEST): $(TESTDAEMON_OBJS) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(TESTDAEMON_LIBS) From 1c2b4c8125c6fe6ba76bb8b0928847199140f787 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Wed, 30 Jun 2010 10:30:27 -0700 Subject: [PATCH 032/200] Use the HWID from the firmware as the hardware class, if available. BUG=3089 TEST=gmerge'd on device, ran with and without an hwid file. Review URL: http://codereview.chromium.org/2824039 --- metrics/hardware_class | 68 +++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/metrics/hardware_class b/metrics/hardware_class index c99db9b9e..913607666 100755 --- a/metrics/hardware_class +++ b/metrics/hardware_class @@ -8,49 +8,69 @@ # qualification ID) of this device, or "unknown" if it can't determine # the hardware class. -# TODO(petkov): The hardware qualification ID is not available on -# systems yet, so the script uses alternative ways to identify -# different system classes (e.g., the WiFi adapter PCI vendor and -# device IDs). Switch the script to use real hardware qualification ID -# when that becomes available. +# TODO(petkov): If the hardware qualification ID is not available on +# the systems, the script uses alternative ways to identify different +# system classes (e.g., the WiFi adapter PCI vendor and device +# IDs). Switch the script to use only real hardware qualification ID +# when that becomes available on all systems. + +HARDWARE_CLASS= +readonly HWID_PATH=/sys/bus/platform/devices/chromeos_acpi/HWID # Appends a new component ID to the hardware class. Separates IDs with # dashes. append_class() { + [ -n "$1" ] || return [ -n "$HARDWARE_CLASS" ] && HARDWARE_CLASS="${HARDWARE_CLASS}-" HARDWARE_CLASS="${HARDWARE_CLASS}$1" } +hwid() { + [ -r "$HWID_PATH" ] || return + local acpihwid + acpihwid=$(cat "$HWID_PATH") + [ -n "$acpihwid" ] || return + append_class "$acpihwid" +} + # Adds the CPU family, model and stepping info, if available, to the # class. cpu() { [ -r /proc/cpuinfo ] || return - FAMILY=`grep -m1 '^cpu family' /proc/cpuinfo \ - | sed 's/cpu family\s\+:\s\+\([0-9]\+\)$/\1/'` - MODEL=`grep -m1 '^model' /proc/cpuinfo \ - | sed 's/model\s\+:\s\+\([0-9]\+\)$/\1/'` - STEPPING=`grep -m1 '^stepping' /proc/cpuinfo \ - | sed 's/stepping\s\+:\s\+\([0-9]\+\)$/\1/'` - if [ -n "$FAMILY" ] && [ -n "$MODEL" ] && [ -n "$STEPPING" ]; then - append_class "cpu/$FAMILY:$MODEL:$STEPPING" + local family + family=$(grep -m1 '^cpu family' /proc/cpuinfo \ + | sed 's/cpu family\s\+:\s\+\([0-9]\+\)$/\1/') + local model + model=$(grep -m1 '^model' /proc/cpuinfo \ + | sed 's/model\s\+:\s\+\([0-9]\+\)$/\1/') + local stepping + stepping=$(grep -m1 '^stepping' /proc/cpuinfo \ + | sed 's/stepping\s\+:\s\+\([0-9]\+\)$/\1/') + if [ -n "$family" ] && [ -n "$model" ] && [ -n "$stepping" ]; then + append_class "cpu/$family:$model:$stepping" fi } # Adds the wlan0 PCI vendor and device ID, if available, to the class. wlan() { - WLAN_DEV=/sys/class/net/wlan0/device - if [ -r $WLAN_DEV/vendor ] && [ -r $WLAN_DEV/device ]; then - WLAN_ID=`paste -d ':' $WLAN_DEV/vendor $WLAN_DEV/device | sed s/0x//g` - append_class "wlan0/$WLAN_ID" + local dev=/sys/class/net/wlan0/device + if [ -r $dev/vendor ] && [ -r $dev/device ]; then + local id + id=$(paste -d ':' $dev/vendor $dev/device | sed s/0x//g) + append_class "wlan0/$id" fi } +main() { + hwid + # If the HWID is not available, use system component IDs. + if [ -z "$HARDWARE_CLASS" ]; then + cpu + wlan + [ -z "$HARDWARE_CLASS" ] && HARDWARE_CLASS=unknown + fi -HARDWARE_CLASS= + echo $HARDWARE_CLASS +} -cpu -wlan - -[ -z "$HARDWARE_CLASS" ] && HARDWARE_CLASS=unknown - -echo $HARDWARE_CLASS +main $@ From 07ae1f87ca9aa7d77550ef10b6dc97defc08b625 Mon Sep 17 00:00:00 2001 From: Chris Sosa Date: Tue, 13 Jul 2010 16:14:27 -0700 Subject: [PATCH 033/200] Remove generate_logs since it's been moved to workarounds in issue 2861045 TEST=Emerged metrics and workarounds after change. Review URL: http://codereview.chromium.org/2978006 --- metrics/generate_logs | 82 ------------------------------------------- 1 file changed, 82 deletions(-) delete mode 100755 metrics/generate_logs diff --git a/metrics/generate_logs b/metrics/generate_logs deleted file mode 100755 index dfcdeea63..000000000 --- a/metrics/generate_logs +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -# -# Copies all logs, screen-shots, crash dumps to Downloads folder. - -# Help information about script usage -if ([ "$1" = -h ] || [ "$1" = -? ]); then - echo "Usage of generate_logs script:" - echo "To collect logs, dumps and screenshots: sh $0" - echo "To collect data and clear up folders: sh $0 --delete" - echo "Collected data is zipped to tar.bz2 file with timestamp in Downloads" - exit 1 -fi - -echo "Collecting logs for $USER user" -home_dir=/home/$USER/user - -# Creating folder to copy logs, screenshots and dumps -log_dir=$home_dir/Downloads/diagnostic_logs -mkdir -p $log_dir -sudo rm -rf $log_dir/* -echo "Created log folder in Downloads" - -# Copying file with current timestamp and date -date > $log_dir/timestamp.txt -echo "Copied current timestamp" - -# Copying lsb-release file -sudo cp /etc/lsb-release $log_dir -echo "Copied lsb-release file with version information" - -# Copying logs- windows manager, screen-locker, messages, session_manager -mkdir $log_dir/system_level_logs -cp -r /var/log/messages \ - /var/log/session_manager \ - /var/log/softwareupdate.log \ - /var/log/update_engine.log \ - /var/log/window_manager \ - $log_dir/system_level_logs -cp -rf $home_dir/log $log_dir/user_level_logs -echo "Copied relevant logs" - -# Copying screen-shots -cp -rf $home_dir/Downloads/Screenshots $log_dir/screenshots 2> /dev/null && \ - echo "Copied screen-shots" - -# Copying crash dumps and deleting from original location -cp -rf $home_dir/.config/google-chrome/Crash\ Reports $log_dir/crashdumps 2> \ - /dev/null && echo "Copied crash dumps" - -# Compressing the log folder with all collected files -currentdate=$(date +%m%d%y-%H%M%S) -tar cjfP $home_dir/Downloads/log-$currentdate.tar.bz2 $log_dir/ -sudo rm -rf $log_dir/* -echo "Files zipped to folder under Downloads : log-$currentdate.tar.bz2" - -# Deleting logs, dumps, screenshots from original location with flag -delete -if [ "$1" = --delete ]; then - echo "Deleting all logs " - sudo rm -rf /var/log/messages \ - /var/log/session_manager \ - /var/log/softwareupdate.log \ - /var/log/update_engine.log \ - /var/log/window_manager/* - sudo rm -rf $home_dir/log/* - - echo "Deleting crash dumps" - sudo rm -rf $home_dir/.config/google-chrome/Crash\ Reports/* - - echo "Deleting screen-shots" - sudo rm -rf $home_dir/Downloads/Screenshots/* - - # Reboot after deleting all files - echo "Rebooting system after clean up in 1 minute" - sudo shutdown -r 1 -else - echo "Logs and dumps are copied but not deleted." - echo "To clear all logs, run the script with --delete flag" -fi From 6c0260a1bdc75b7c50b957583efb9664f7def9d0 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Mon, 19 Jul 2010 10:54:27 -0700 Subject: [PATCH 034/200] Link libcrash conditionally. This enables building metrics for the host thus enabling using metrics in the current update_engine without breaking the host dev flow. BUG=4852 TEST=emerged with and without USE=-crash Review URL: http://codereview.chromium.org/3032004 --- metrics/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/metrics/Makefile b/metrics/Makefile index 480da220a..f3dddd873 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -18,6 +18,8 @@ SHAREDLIB = libmetrics.so LIB_TEST = metrics_library_test COUNTER_TEST = counter_test +LCRASH ?= -lcrash + TESTCOUNTER_OBJS = \ counter.o \ counter_test.o @@ -53,7 +55,7 @@ $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) $(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) -lcrash + $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(LCRASH) $(DAEMON_TEST): $(TESTDAEMON_OBJS) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(TESTDAEMON_LIBS) From 9ce4fa3f60654775da215dc75a2a334c920d082b Mon Sep 17 00:00:00 2001 From: "J. Richard Barnette" Date: Thu, 5 Aug 2010 14:13:48 -0700 Subject: [PATCH 035/200] Add LICENSE file --- metrics/LICENSE | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 metrics/LICENSE diff --git a/metrics/LICENSE b/metrics/LICENSE new file mode 100644 index 000000000..d25149653 --- /dev/null +++ b/metrics/LICENSE @@ -0,0 +1,27 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 3fd74748a7f42ef3d4073514ce9e5b3c6197dd24 Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Thu, 12 Aug 2010 12:19:25 -0700 Subject: [PATCH 036/200] [metrics] Fix tests after libbase roll broke them Change-Id: Ie26189e05b8aaa637614ed098d8b215c6a436216 Review URL: http://codereview.chromium.org/3138013 --- metrics/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index f3dddd873..deb529498 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -39,10 +39,10 @@ LIB_OBJS = \ TESTLIB_OBJS = \ metrics_library_test.o -TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags +TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 +DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags -lglib-2.0 TESTDAEMON_LIBS = -lgmock -lgtest -TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread +TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread -lglib-2.0 all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) From ccd84c03d2bd43df4f6913179f50d4a58cfa2903 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Mon, 16 Aug 2010 19:57:13 -0700 Subject: [PATCH 037/200] Add # daily crashes metrics and separate kernel crashes out. BUG=5340 Review URL: http://codereview.chromium.org/3181015 --- metrics/counter.cc | 28 +++++ metrics/counter.h | 46 ++++++++ metrics/counter_mock.h | 10 +- metrics/counter_test.cc | 59 +++++++++++ metrics/metrics_daemon.cc | 187 +++++++++++++++++++++++++++------ metrics/metrics_daemon.h | 104 +++++++++++++----- metrics/metrics_daemon_test.cc | 97 +++++++++++++---- 7 files changed, 452 insertions(+), 79 deletions(-) diff --git a/metrics/counter.cc b/metrics/counter.cc index b81a0d1be..4de1db105 100644 --- a/metrics/counter.cc +++ b/metrics/counter.cc @@ -183,4 +183,32 @@ void TaggedCounter::WriteRecord(int fd) { } } +FrequencyCounter::FrequencyCounter() : cycle_duration_(1) { +} + +FrequencyCounter::~FrequencyCounter() { +} + +void FrequencyCounter::Init(const char* filename, + TaggedCounterInterface::Reporter reporter, + void* reporter_handle, + time_t cycle_duration) { + // Allow tests to inject tagged_counter_ dependency. + if (tagged_counter_.get() == NULL) { + tagged_counter_.reset(new TaggedCounter()); + } + tagged_counter_->Init(filename, reporter, reporter_handle); + DCHECK(cycle_duration > 0); + cycle_duration_ = cycle_duration; +} + +void FrequencyCounter::UpdateInternal(int32 count, time_t now) { + DCHECK(tagged_counter_.get() != NULL); + tagged_counter_->Update(GetCycleNumber(now), count); +} + +int32 FrequencyCounter::GetCycleNumber(time_t now) { + return now / cycle_duration_; +} + } // namespace chromeos_metrics diff --git a/metrics/counter.h b/metrics/counter.h index 1cfcb51d4..a5e5302f2 100644 --- a/metrics/counter.h +++ b/metrics/counter.h @@ -5,11 +5,18 @@ #ifndef METRICS_COUNTER_H_ #define METRICS_COUNTER_H_ +#include + #include +#include #include // for FRIEND_TEST namespace chromeos_metrics { +// Constants useful for frequency statistics. +const int kSecondsPerDay = 60 * 60 * 24; +const int kSecondsPerWeek = kSecondsPerDay * 7; + // TaggedCounter maintains a persistent storage (i.e., a file) // aggregation counter for a given tag (e.g., day, hour) that survives // system shutdowns, reboots and crashes, as well as daemon process @@ -152,6 +159,45 @@ class TaggedCounter : public TaggedCounterInterface { RecordState record_state_; }; +// FrequencyCounter uses TaggedCounter to maintain a persistent +// storage of the number of events that occur in a given cycle +// duration (in other words, a frequency count). For example, to +// count the number of blips per day, initialize |cycle_duration| to +// chromeos_metrics::kSecondsPerDay, and call Update with the number +// of blips that happen concurrently (usually 1). Reporting of the +// value is done through TaggedCounter's reporter function. +class FrequencyCounter { + public: + // Create a new frequency counter. + FrequencyCounter(); + virtual ~FrequencyCounter(); + + // Initialize a frequency counter, which is necessary before first use. + // |filename|, |reporter|, and |reporter_handle| are used as in + // TaggedCounter::Init. |cycle_duration| is the number of seconds + // in a cycle. + virtual void Init(const char* filename, + TaggedCounterInterface::Reporter reporter, + void* reporter_handle, + time_t cycle_duration); + // Record that an event occurred. |count| is the number of concurrent + // events that have occurred. The time is implicitly assumed to be the + // time of the call. + virtual void Update(int32 count) { + UpdateInternal(count, time(NULL)); + } + + private: + friend class FrequencyCounterTest; + FRIEND_TEST(FrequencyCounterTest, UpdateInternal); + + void UpdateInternal(int32 count, time_t now); + int32 GetCycleNumber(time_t now); + + time_t cycle_duration_; + scoped_ptr tagged_counter_; +}; + } // namespace chromeos_metrics #endif // METRICS_COUNTER_H_ diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h index baa97b0bf..701cce48e 100644 --- a/metrics/counter_mock.h +++ b/metrics/counter_mock.h @@ -21,7 +21,15 @@ class TaggedCounterMock : public TaggedCounterInterface { MOCK_METHOD0(Flush, void()); }; +class FrequencyCounterMock : public FrequencyCounter { + public: + MOCK_METHOD4(Init, void(const char* filename, + TaggedCounterInterface::Reporter reporter, + void* reporter_handle, + time_t cycle_duration)); + MOCK_METHOD1(Update, void(int32 count)); +}; + } // namespace chromeos_metrics #endif // METRICS_COUNTER_MOCK_H_ - diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc index fd5a38939..eb68b2acb 100644 --- a/metrics/counter_test.cc +++ b/metrics/counter_test.cc @@ -12,6 +12,7 @@ #include #include "counter.h" +#include "counter_mock.h" // For TaggedCounterMock. using ::testing::_; using ::testing::MockFunction; @@ -255,6 +256,64 @@ TEST_F(TaggedCounterTest, Update) { EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); } +class FrequencyCounterTest : public testing::Test { + protected: + virtual void SetUp() { + tagged_counter_ = new StrictMock; + frequency_counter_.tagged_counter_.reset(tagged_counter_); + } + + static void FakeReporter(void *, int32, int32) { + } + + void CheckInit(int32 cycle_duration); + void CheckCycleNumber(int32 cycle_duration); + + FrequencyCounter frequency_counter_; + StrictMock* tagged_counter_; + + TaggedCounter::Reporter reporter_; +}; + +void FrequencyCounterTest::CheckInit(int32 cycle_duration) { + EXPECT_CALL(*tagged_counter_, Init(kTestRecordFile, FakeReporter, this)) + .Times(1) + .RetiresOnSaturation(); + frequency_counter_.Init(kTestRecordFile, + FakeReporter, + this, + cycle_duration); + EXPECT_EQ(cycle_duration, frequency_counter_.cycle_duration_); +} + +TEST_F(FrequencyCounterTest, Init) { + CheckInit(100); +} + +void FrequencyCounterTest::CheckCycleNumber(int32 cycle_duration) { + CheckInit(cycle_duration); + EXPECT_EQ(150, frequency_counter_.GetCycleNumber(cycle_duration * 150)); + EXPECT_EQ(150, frequency_counter_.GetCycleNumber(cycle_duration * 150 + + cycle_duration - 1)); + EXPECT_EQ(151, frequency_counter_.GetCycleNumber(cycle_duration * 151 + 1)); + EXPECT_EQ(0, frequency_counter_.GetCycleNumber(0)); +} + + +TEST_F(FrequencyCounterTest, GetCycleNumberForWeek) { + CheckCycleNumber(kSecondsPerWeek); +} + +TEST_F(FrequencyCounterTest, GetCycleNumberForDay) { + CheckCycleNumber(kSecondsPerDay); +} + +TEST_F(FrequencyCounterTest, UpdateInternal) { + CheckInit(kSecondsPerWeek); + EXPECT_CALL(*tagged_counter_, Update(150, 2)); + frequency_counter_.UpdateInternal(2, kSecondsPerWeek * 150); +} + } // namespace chromeos_metrics int main(int argc, char** argv) { diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 30d3da8ae..0171fdec9 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -39,6 +39,10 @@ static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek; static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute; static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute; +const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; +static const char kUncleanShutdownDetectedFile[] = + "/tmp/unclean-shutdown-detected"; + // static metrics parameters. const char MetricsDaemon::kMetricDailyUseTimeName[] = "Logging.DailyUseTime"; @@ -46,12 +50,6 @@ const int MetricsDaemon::kMetricDailyUseTimeMin = 1; const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay; const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50; -const char MetricsDaemon::kMetricKernelCrashIntervalName[] = - "Logging.KernelCrashInterval"; -const int MetricsDaemon::kMetricKernelCrashIntervalMin = 1; -const int MetricsDaemon::kMetricKernelCrashIntervalMax = 4 * kSecondsPerWeek; -const int MetricsDaemon::kMetricKernelCrashIntervalBuckets = 50; - const char MetricsDaemon::kMetricTimeToNetworkDropName[] = "Network.TimeToDrop"; const int MetricsDaemon::kMetricTimeToNetworkDropMin = 1; @@ -59,11 +57,33 @@ const int MetricsDaemon::kMetricTimeToNetworkDropMax = 8 /* hours */ * kMinutesPerHour * kSecondsPerMinute; const int MetricsDaemon::kMetricTimeToNetworkDropBuckets = 50; +// crash interval metrics +const char MetricsDaemon::kMetricKernelCrashIntervalName[] = + "Logging.KernelCrashInterval"; +const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] = + "Logging.UncleanShutdownInterval"; const char MetricsDaemon::kMetricUserCrashIntervalName[] = "Logging.UserCrashInterval"; -const int MetricsDaemon::kMetricUserCrashIntervalMin = 1; -const int MetricsDaemon::kMetricUserCrashIntervalMax = 4 * kSecondsPerWeek; -const int MetricsDaemon::kMetricUserCrashIntervalBuckets = 50; + +const int MetricsDaemon::kMetricCrashIntervalMin = 1; +const int MetricsDaemon::kMetricCrashIntervalMax = + 4 * kSecondsPerWeek; +const int MetricsDaemon::kMetricCrashIntervalBuckets = 50; + +// crash frequency metrics +const char MetricsDaemon::kMetricAnyCrashesDailyName[] = + "Logging.AnyCrashesDaily"; +const char MetricsDaemon::kMetricKernelCrashesDailyName[] = + "Logging.KernelCrashesDaily"; +const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] = + "Logging.UncleanShutdownsDaily"; +const char MetricsDaemon::kMetricUserCrashesDailyName[] = + "Logging.UserCrashesDaily"; +const char MetricsDaemon::kMetricCrashesDailyMin = 1; +const char MetricsDaemon::kMetricCrashesDailyMax = 100; +const char MetricsDaemon::kMetricCrashesDailyBuckets = 50; + + // static const char* MetricsDaemon::kDBusMatches_[] = { @@ -168,8 +188,14 @@ void MetricsDaemon::Run(bool run_as_daemon) { if (run_as_daemon && daemon(0, 0) != 0) return; - static const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; - CheckKernelCrash(kKernelCrashDetectedFile); + if (CheckSystemCrash(kKernelCrashDetectedFile)) { + ProcessKernelCrash(); + } + + if (CheckSystemCrash(kUncleanShutdownDetectedFile)) { + ProcessUncleanShutdown(); + } + Loop(); } @@ -180,19 +206,57 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage"; daily_use_.reset(new chromeos_metrics::TaggedCounter()); - daily_use_->Init(kDailyUseRecordFile, &DailyUseReporter, this); + daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this); static const char kUserCrashIntervalRecordFile[] = "/var/log/metrics/user-crash-interval"; user_crash_interval_.reset(new chromeos_metrics::TaggedCounter()); user_crash_interval_->Init(kUserCrashIntervalRecordFile, - &UserCrashIntervalReporter, this); + &ReportUserCrashInterval, this); static const char kKernelCrashIntervalRecordFile[] = "/var/log/metrics/kernel-crash-interval"; kernel_crash_interval_.reset(new chromeos_metrics::TaggedCounter()); kernel_crash_interval_->Init(kKernelCrashIntervalRecordFile, - &KernelCrashIntervalReporter, this); + &ReportKernelCrashInterval, this); + + static const char kUncleanShutdownDetectedFile[] = + "/var/log/metrics/unclean-shutdown-interval"; + unclean_shutdown_interval_.reset(new chromeos_metrics::TaggedCounter()); + unclean_shutdown_interval_->Init(kUncleanShutdownDetectedFile, + &ReportUncleanShutdownInterval, this); + + static const char kUserCrashesDailyRecordFile[] = + "/var/log/metrics/user-crashes-daily"; + user_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter()); + user_crashes_daily_->Init(kUserCrashesDailyRecordFile, + &ReportUserCrashesDaily, + this, + chromeos_metrics::kSecondsPerDay); + + static const char kKernelCrashesDailyRecordFile[] = + "/var/log/metrics/kernel-crashes-daily"; + kernel_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter()); + kernel_crashes_daily_->Init(kKernelCrashesDailyRecordFile, + &ReportKernelCrashesDaily, + this, + chromeos_metrics::kSecondsPerDay); + + static const char kUncleanShutdownsDailyRecordFile[] = + "/var/log/metrics/unclean-shutdowns-daily"; + unclean_shutdowns_daily_.reset(new chromeos_metrics::FrequencyCounter()); + unclean_shutdowns_daily_->Init(kUncleanShutdownsDailyRecordFile, + &ReportUncleanShutdownsDaily, + this, + chromeos_metrics::kSecondsPerDay); + + static const char kAnyCrashesUserCrashDailyRecordFile[] = + "/var/log/metrics/any-crashes-daily"; + any_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter()); + any_crashes_daily_->Init(kAnyCrashesUserCrashDailyRecordFile, + &ReportAnyCrashesDaily, + this, + chromeos_metrics::kSecondsPerDay); // Don't setup D-Bus and GLib in test mode. if (testing) @@ -408,6 +472,9 @@ void MetricsDaemon::ProcessUserCrash() { // Reports the active use time since the last crash and resets it. user_crash_interval_->Flush(); + + user_crashes_daily_->Update(1); + any_crashes_daily_->Update(1); } void MetricsDaemon::ProcessKernelCrash() { @@ -416,19 +483,32 @@ void MetricsDaemon::ProcessKernelCrash() { // Reports the active use time since the last crash and resets it. kernel_crash_interval_->Flush(); + + kernel_crashes_daily_->Update(1); + any_crashes_daily_->Update(1); } -void MetricsDaemon::CheckKernelCrash(const std::string& crash_file) { +void MetricsDaemon::ProcessUncleanShutdown() { + // Counts the active use time up to now. + SetUserActiveState(user_active_, Time::Now()); + + // Reports the active use time since the last crash and resets it. + unclean_shutdown_interval_->Flush(); + + unclean_shutdowns_daily_->Update(1); + any_crashes_daily_->Update(1); +} + +bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) { FilePath crash_detected(crash_file); if (!file_util::PathExists(crash_detected)) - return; - - ProcessKernelCrash(); + return false; // Deletes the crash-detected file so that the daemon doesn't report // another kernel crash in case it's restarted. file_util::Delete(crash_detected, false); // recursive + return true; } // static @@ -492,7 +572,7 @@ void MetricsDaemon::UnscheduleUseMonitor() { } // static -void MetricsDaemon::DailyUseReporter(void* handle, int tag, int count) { +void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { if (count <= 0) return; @@ -505,25 +585,68 @@ void MetricsDaemon::DailyUseReporter(void* handle, int tag, int count) { } // static -void MetricsDaemon::UserCrashIntervalReporter(void* handle, - int tag, int count) { +void MetricsDaemon::ReportCrashInterval(const char* histogram_name, + void* handle, int count) { MetricsDaemon* daemon = static_cast(handle); - daemon->SendMetric(kMetricUserCrashIntervalName, count, - kMetricUserCrashIntervalMin, - kMetricUserCrashIntervalMax, - kMetricUserCrashIntervalBuckets); + daemon->SendMetric(histogram_name, count, + kMetricCrashIntervalMin, + kMetricCrashIntervalMax, + kMetricCrashIntervalBuckets); } // static -void MetricsDaemon::KernelCrashIntervalReporter(void* handle, - int tag, int count) { - MetricsDaemon* daemon = static_cast(handle); - daemon->SendMetric(kMetricKernelCrashIntervalName, count, - kMetricKernelCrashIntervalMin, - kMetricKernelCrashIntervalMax, - kMetricKernelCrashIntervalBuckets); +void MetricsDaemon::ReportUserCrashInterval(void* handle, + int tag, int count) { + ReportCrashInterval(kMetricUserCrashIntervalName, handle, count); } +// static +void MetricsDaemon::ReportKernelCrashInterval(void* handle, + int tag, int count) { + ReportCrashInterval(kMetricKernelCrashIntervalName, handle, count); +} + +// static +void MetricsDaemon::ReportUncleanShutdownInterval(void* handle, + int tag, int count) { + ReportCrashInterval(kMetricUncleanShutdownIntervalName, handle, count); +} + +// static +void MetricsDaemon::ReportCrashesDailyFrequency(const char* histogram_name, + void* handle, + int count) { + MetricsDaemon* daemon = static_cast(handle); + daemon->SendMetric(histogram_name, count, + kMetricCrashesDailyMin, + kMetricCrashesDailyMax, + kMetricCrashesDailyBuckets); +} + +// static +void MetricsDaemon::ReportUserCrashesDaily(void* handle, + int tag, int count) { + ReportCrashesDailyFrequency(kMetricUserCrashesDailyName, handle, count); +} + +// static +void MetricsDaemon::ReportKernelCrashesDaily(void* handle, + int tag, int count) { + ReportCrashesDailyFrequency(kMetricKernelCrashesDailyName, handle, count); +} + +// static +void MetricsDaemon::ReportUncleanShutdownsDaily(void* handle, + int tag, int count) { + ReportCrashesDailyFrequency(kMetricUncleanShutdownsDailyName, handle, count); +} + +// static +void MetricsDaemon::ReportAnyCrashesDaily(void* handle, int tag, int count) { + ReportCrashesDailyFrequency(kMetricAnyCrashesDailyName, handle, count); +} + + void MetricsDaemon::SendMetric(const string& name, int sample, int min, int max, int nbuckets) { DLOG(INFO) << "received metric: " << name << " " << sample << " " diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 437cafd0e..f98c34c4f 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -14,7 +14,10 @@ #include "metrics_library.h" -namespace chromeos_metrics { class TaggedCounterInterface; } +namespace chromeos_metrics { +class FrequencyCounter; +class TaggedCounterInterface; +} class MetricsDaemon { @@ -31,9 +34,7 @@ class MetricsDaemon { private: friend class MetricsDaemonTest; - FRIEND_TEST(MetricsDaemonTest, CheckKernelCrash); - FRIEND_TEST(MetricsDaemonTest, DailyUseReporter); - FRIEND_TEST(MetricsDaemonTest, KernelCrashIntervalReporter); + FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash); FRIEND_TEST(MetricsDaemonTest, LookupNetworkState); FRIEND_TEST(MetricsDaemonTest, LookupPowerState); FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); @@ -43,13 +44,18 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, NetStateChangedSuspend); FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash); + FRIEND_TEST(MetricsDaemonTest, ProcessUncleanShutdown); FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash); + FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency); + FRIEND_TEST(MetricsDaemonTest, ReportDailyUse); + FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval); + FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval); + FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval); FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); FRIEND_TEST(MetricsDaemonTest, SendMetric); FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateTimeJump); - FRIEND_TEST(MetricsDaemonTest, UserCrashIntervalReporter); // The network states (see network_states.h). enum NetworkState { @@ -84,22 +90,27 @@ class MetricsDaemon { }; // Metric parameters. + static const char kMetricAnyCrashesDailyName[]; + static const char kMetricCrashesDailyBuckets; + static const char kMetricCrashesDailyMax; + static const char kMetricCrashesDailyMin; + static const int kMetricCrashIntervalBuckets; + static const int kMetricCrashIntervalMax; + static const int kMetricCrashIntervalMin; + static const int kMetricDailyUseTimeBuckets; + static const int kMetricDailyUseTimeMax; + static const int kMetricDailyUseTimeMin; static const char kMetricDailyUseTimeName[]; - static const int kMetricDailyUseTimeMin; - static const int kMetricDailyUseTimeMax; - static const int kMetricDailyUseTimeBuckets; + static const char kMetricKernelCrashesDailyName[]; static const char kMetricKernelCrashIntervalName[]; - static const int kMetricKernelCrashIntervalMin; - static const int kMetricKernelCrashIntervalMax; - static const int kMetricKernelCrashIntervalBuckets; + static const int kMetricTimeToNetworkDropBuckets; + static const int kMetricTimeToNetworkDropMax; + static const int kMetricTimeToNetworkDropMin; static const char kMetricTimeToNetworkDropName[]; - static const int kMetricTimeToNetworkDropMin; - static const int kMetricTimeToNetworkDropMax; - static const int kMetricTimeToNetworkDropBuckets; + static const char kMetricUncleanShutdownIntervalName[]; + static const char kMetricUncleanShutdownsDailyName[]; + static const char kMetricUserCrashesDailyName[]; static const char kMetricUserCrashIntervalName[]; - static const int kMetricUserCrashIntervalMin; - static const int kMetricUserCrashIntervalMax; - static const int kMetricUserCrashIntervalBuckets; // D-Bus message match strings. static const char* kDBusMatches_[]; @@ -162,10 +173,14 @@ class MetricsDaemon { // Updates the active use time and logs time between kernel crashes. void ProcessKernelCrash(); - // Checks if a kernel crash has been detected and processes if so. - // The method assumes that a kernel crash has happened if - // |crash_file| exists. - void CheckKernelCrash(const std::string& crash_file); + // Updates the active use time and logs time between unclean shutdowns. + void ProcessUncleanShutdown(); + + // Checks if a kernel crash has been detected and returns true if + // so. The method assumes that a kernel crash has happened if + // |crash_file| exists. It removes the file immediately if it + // exists, so it must not be called more than once. + bool CheckSystemCrash(const std::string& crash_file); // Callbacks for the daily use monitor. The daily use monitor uses // LogDailyUseRecord to aggregate current usage data and send it to @@ -196,15 +211,39 @@ class MetricsDaemon { // TaggedCounter callback to process aggregated daily usage data and // send to UMA. - static void DailyUseReporter(void* data, int tag, int count); + static void ReportDailyUse(void* data, int tag, int count); + + // Helper to report a crash interval to UMA. + static void ReportCrashInterval(const char* histogram_name, + void* handle, int count); // TaggedCounter callback to process time between user-space process // crashes and send to UMA. - static void UserCrashIntervalReporter(void* data, int tag, int count); + static void ReportUserCrashInterval(void* data, int tag, int count); // TaggedCounter callback to process time between kernel crashes and // send to UMA. - static void KernelCrashIntervalReporter(void* data, int tag, int count); + static void ReportKernelCrashInterval(void* data, int tag, int count); + + // TaggedCounter callback to process time between unclean shutdowns and + // send to UMA. + static void ReportUncleanShutdownInterval(void* data, int tag, int count); + + // Helper to report a daily crash frequency to UMA. + static void ReportCrashesDailyFrequency(const char* histogram_name, + void* handle, int count); + + // TaggedCounter callback to report daily crash frequency to UMA. + static void ReportUserCrashesDaily(void* handle, int tag, int count); + + // TaggedCounter callback to report kernel crash frequency to UMA. + static void ReportKernelCrashesDaily(void* handle, int tag, int count); + + // TaggedCounter callback to report unclean shutdown frequency to UMA. + static void ReportUncleanShutdownsDaily(void* handle, int tag, int count); + + // TaggedCounter callback to report frequency of any crashes to UMA. + static void ReportAnyCrashesDaily(void* handle, int tag, int count); // Test mode. bool testing_; @@ -244,6 +283,23 @@ class MetricsDaemon { // Active use time between kernel crashes. scoped_ptr kernel_crash_interval_; + // Active use time between unclean shutdowns crashes. + scoped_ptr + unclean_shutdown_interval_; + + // Daily count of user-space process crashes. + scoped_ptr user_crashes_daily_; + + // Daily count of kernel crashes. + scoped_ptr kernel_crashes_daily_; + + // Daily count of unclean shutdowns. + scoped_ptr unclean_shutdowns_daily_; + + // Daily count of any crashes (user-space processes, kernel, or + // unclean shutdowns). + scoped_ptr any_crashes_daily_; + // Sleep period until the next daily usage aggregation performed by // the daily use monitor (see ScheduleUseMonitor). int usemon_interval_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index e75c1616c..e551cf990 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -11,6 +11,7 @@ using base::Time; using base::TimeTicks; +using chromeos_metrics::FrequencyCounterMock; using chromeos_metrics::TaggedCounterMock; using ::testing::_; using ::testing::Return; @@ -59,6 +60,16 @@ class MetricsDaemonTest : public testing::Test { daemon_.kernel_crash_interval_.reset(kernel_crash_interval_); user_crash_interval_ = new StrictMock(); daemon_.user_crash_interval_.reset(user_crash_interval_); + unclean_shutdown_interval_ = new StrictMock(); + daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_); + kernel_crashes_daily_ = new StrictMock(); + daemon_.kernel_crashes_daily_.reset(kernel_crashes_daily_); + user_crashes_daily_ = new StrictMock(); + daemon_.user_crashes_daily_.reset(user_crashes_daily_); + unclean_shutdowns_daily_ = new StrictMock(); + daemon_.unclean_shutdowns_daily_.reset(unclean_shutdowns_daily_); + any_crashes_daily_ = new StrictMock(); + daemon_.any_crashes_daily_.reset(any_crashes_daily_); EXPECT_FALSE(daemon_.user_active_); EXPECT_TRUE(daemon_.user_active_last_.is_null()); @@ -172,40 +183,54 @@ class MetricsDaemonTest : public testing::Test { StrictMock* daily_use_; StrictMock* kernel_crash_interval_; StrictMock* user_crash_interval_; + StrictMock* unclean_shutdown_interval_; + + StrictMock* kernel_crashes_daily_; + StrictMock* user_crashes_daily_; + StrictMock* unclean_shutdowns_daily_; + StrictMock* any_crashes_daily_; }; -TEST_F(MetricsDaemonTest, CheckKernelCrash) { +TEST_F(MetricsDaemonTest, CheckSystemCrash) { static const char kKernelCrashDetected[] = "test-kernel-crash-detected"; - daemon_.CheckKernelCrash(kKernelCrashDetected); + EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected)); FilePath crash_detected(kKernelCrashDetected); file_util::WriteFile(crash_detected, "", 0); - IgnoreActiveUseUpdate(); - EXPECT_CALL(*kernel_crash_interval_, Flush()) - .Times(1) - .RetiresOnSaturation(); - daemon_.CheckKernelCrash(kKernelCrashDetected); + EXPECT_TRUE(file_util::PathExists(crash_detected)); + EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected)); + EXPECT_FALSE(file_util::PathExists(crash_detected)); + EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected)); + EXPECT_FALSE(file_util::PathExists(crash_detected)); file_util::Delete(crash_detected, false); } -TEST_F(MetricsDaemonTest, DailyUseReporter) { +TEST_F(MetricsDaemonTest, ReportDailyUse) { ExpectDailyUseTimeMetric(/* sample */ 2); - MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 20, /* count */ 90); + MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 20, /* count */ 90); ExpectDailyUseTimeMetric(/* sample */ 1); - MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 23, /* count */ 89); + MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 23, /* count */ 89); // There should be no metrics generated for the calls below. - MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 50, /* count */ 0); - MetricsDaemon::DailyUseReporter(&daemon_, /* tag */ 60, /* count */ -5); + MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 50, /* count */ 0); + MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 60, /* count */ -5); } -TEST_F(MetricsDaemonTest, KernelCrashIntervalReporter) { +TEST_F(MetricsDaemonTest, ReportKernelCrashInterval) { ExpectMetric(MetricsDaemon::kMetricKernelCrashIntervalName, 50, - MetricsDaemon::kMetricKernelCrashIntervalMin, - MetricsDaemon::kMetricKernelCrashIntervalMax, - MetricsDaemon::kMetricKernelCrashIntervalBuckets); - MetricsDaemon::KernelCrashIntervalReporter(&daemon_, 0, 50); + MetricsDaemon::kMetricCrashIntervalMin, + MetricsDaemon::kMetricCrashIntervalMax, + MetricsDaemon::kMetricCrashIntervalBuckets); + MetricsDaemon::ReportKernelCrashInterval(&daemon_, 0, 50); +} + +TEST_F(MetricsDaemonTest, ReportUncleanShutdownInterval) { + ExpectMetric(MetricsDaemon::kMetricUncleanShutdownIntervalName, 50, + MetricsDaemon::kMetricCrashIntervalMin, + MetricsDaemon::kMetricCrashIntervalMax, + MetricsDaemon::kMetricCrashIntervalBuckets); + MetricsDaemon::ReportUncleanShutdownInterval(&daemon_, 0, 50); } TEST_F(MetricsDaemonTest, LookupNetworkState) { @@ -246,6 +271,12 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_CALL(*user_crash_interval_, Flush()) .Times(1) .RetiresOnSaturation(); + EXPECT_CALL(*user_crashes_daily_, Update(1)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*any_crashes_daily_, Update(1)) + .Times(1) + .RetiresOnSaturation(); msg = NewDBusSignalString("/", "org.chromium.CrashReporter", "UserCrash", @@ -379,14 +410,28 @@ TEST_F(MetricsDaemonTest, ProcessKernelCrash) { EXPECT_CALL(*kernel_crash_interval_, Flush()) .Times(1) .RetiresOnSaturation(); + EXPECT_CALL(*kernel_crashes_daily_, Update(1)); + EXPECT_CALL(*any_crashes_daily_, Update(1)); daemon_.ProcessKernelCrash(); } +TEST_F(MetricsDaemonTest, ProcessUncleanShutdown) { + IgnoreActiveUseUpdate(); + EXPECT_CALL(*unclean_shutdown_interval_, Flush()) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(*unclean_shutdowns_daily_, Update(1)); + EXPECT_CALL(*any_crashes_daily_, Update(1)); + daemon_.ProcessUncleanShutdown(); +} + TEST_F(MetricsDaemonTest, ProcessUserCrash) { IgnoreActiveUseUpdate(); EXPECT_CALL(*user_crash_interval_, Flush()) .Times(1) .RetiresOnSaturation(); + EXPECT_CALL(*user_crashes_daily_, Update(1)); + EXPECT_CALL(*any_crashes_daily_, Update(1)); daemon_.ProcessUserCrash(); } @@ -469,12 +514,20 @@ TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) { EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_); } -TEST_F(MetricsDaemonTest, UserCrashIntervalReporter) { +TEST_F(MetricsDaemonTest, ReportUserCrashInterval) { ExpectMetric(MetricsDaemon::kMetricUserCrashIntervalName, 50, - MetricsDaemon::kMetricUserCrashIntervalMin, - MetricsDaemon::kMetricUserCrashIntervalMax, - MetricsDaemon::kMetricUserCrashIntervalBuckets); - MetricsDaemon::UserCrashIntervalReporter(&daemon_, 0, 50); + MetricsDaemon::kMetricCrashIntervalMin, + MetricsDaemon::kMetricCrashIntervalMax, + MetricsDaemon::kMetricCrashIntervalBuckets); + MetricsDaemon::ReportUserCrashInterval(&daemon_, 0, 50); +} + +TEST_F(MetricsDaemonTest, ReportCrashesDailyFrequency) { + ExpectMetric("foobar", 50, + MetricsDaemon::kMetricCrashesDailyMin, + MetricsDaemon::kMetricCrashesDailyMax, + MetricsDaemon::kMetricCrashesDailyBuckets); + MetricsDaemon::ReportCrashesDailyFrequency("foobar", &daemon_, 50); } int main(int argc, char** argv) { From 4c5daa47942e4d891c74ac56417dd815b25e6e3d Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 26 Aug 2010 18:35:06 -0700 Subject: [PATCH 038/200] Add weekly crash counters, refactor metrics_daemon, respect opt-in in library. BUG=5340,5814 Change-Id: I2c207055f1ebe48051193395e2dbe38d9140b025 Review URL: http://codereview.chromium.org/3171023 --- metrics/c_metrics_library.cc | 15 ++- metrics/c_metrics_library.h | 12 +- metrics/counter.cc | 61 +++++++-- metrics/counter.h | 129 ++++++++++++++---- metrics/counter_mock.h | 14 +- metrics/counter_test.cc | 108 ++++++++++++--- metrics/metrics_daemon.cc | 224 ++++++++++++++------------------ metrics/metrics_daemon.h | 98 ++++++-------- metrics/metrics_daemon_test.cc | 188 ++++++++++++++++++--------- metrics/metrics_library.cc | 28 +++- metrics/metrics_library.h | 11 ++ metrics/metrics_library_test.cc | 81 ++++++++++++ 12 files changed, 663 insertions(+), 306 deletions(-) diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc index 91e9ca6e3..e97f9ac26 100644 --- a/metrics/c_metrics_library.cc +++ b/metrics/c_metrics_library.cc @@ -26,8 +26,8 @@ extern "C" void CMetricsLibraryInit(CMetricsLibrary handle) { } extern "C" int CMetricsLibrarySendToUMA(CMetricsLibrary handle, - const char* name, int sample, - int min, int max, int nbuckets) { + const char* name, int sample, + int min, int max, int nbuckets) { MetricsLibrary* lib = reinterpret_cast(handle); if (lib == NULL) return 0; @@ -35,10 +35,17 @@ extern "C" int CMetricsLibrarySendToUMA(CMetricsLibrary handle, } extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, - const char* name, int sample, - int max) { + const char* name, int sample, + int max) { MetricsLibrary* lib = reinterpret_cast(handle); if (lib == NULL) return 0; return lib->SendEnumToUMA(std::string(name), sample, max); } + +extern "C" int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle) { + MetricsLibrary* lib = reinterpret_cast(handle); + if (lib == NULL) + return 0; + return lib->AreMetricsEnabled(); +} diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h index 6d1e6f907..e691ad66e 100644 --- a/metrics/c_metrics_library.h +++ b/metrics/c_metrics_library.h @@ -21,13 +21,17 @@ void CMetricsLibraryInit(CMetricsLibrary handle); // C wrapper for MetricsLibrary::SendToUMA. int CMetricsLibrarySendToUMA(CMetricsLibrary handle, - const char* name, int sample, - int min, int max, int nbuckets); + const char* name, int sample, + int min, int max, int nbuckets); // C wrapper for MetricsLibrary::SendEnumToUMA. int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, - const char* name, int sample, int max); + const char* name, int sample, int max); + +// C wrapper for MetricsLibrary::AreMetricsEnabled. +int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle); + #if defined(__cplusplus) } #endif -#endif // C_METRICS_LIBRARY_H_ +#endif // C_METRICS_LIBRARY_H_ diff --git a/metrics/counter.cc b/metrics/counter.cc index 4de1db105..95d1faf87 100644 --- a/metrics/counter.cc +++ b/metrics/counter.cc @@ -8,6 +8,7 @@ #include #include +#include "metrics_library.h" namespace chromeos_metrics { @@ -31,8 +32,7 @@ void TaggedCounter::Record::Add(int32 count) { // TaggedCounter implementation. TaggedCounter::TaggedCounter() - : filename_(NULL), - reporter_(NULL), + : reporter_(NULL), reporter_handle_(NULL), record_state_(kRecordInvalid) {} @@ -72,12 +72,13 @@ void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) { } DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush; - DCHECK(filename_); + DCHECK(!filename_.empty()); // NOTE: The assumption is that this TaggedCounter object is the // sole owner of the persistent storage file so no locking is // necessary. - int fd = HANDLE_EINTR(open(filename_, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); + int fd = HANDLE_EINTR(open(filename_.c_str(), + O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); if (fd < 0) { PLOG(WARNING) << "Unable to open the persistent counter file"; return; @@ -183,21 +184,57 @@ void TaggedCounter::WriteRecord(int fd) { } } +MetricsLibraryInterface* TaggedCounterReporter::metrics_lib_ = NULL; + +TaggedCounterReporter::TaggedCounterReporter() + : tagged_counter_(new TaggedCounter()), + min_(0), + max_(0), + buckets_(0) { +} + +TaggedCounterReporter::~TaggedCounterReporter() { +} + +void TaggedCounterReporter::Init(const char* filename, + const char* histogram_name, + int min, + int max, + int buckets) { + tagged_counter_->Init(filename, Report, this); + histogram_name_ = histogram_name; + min_ = min; + max_ = max; + buckets_ = buckets; + CHECK(min_ >= 0); + CHECK(max_ > min_); + CHECK(buckets_ > 0); +} + +void TaggedCounterReporter::Report(void* handle, int32 tag, int32 count) { + TaggedCounterReporter* this_reporter = + reinterpret_cast(handle); + DLOG(INFO) << "received metric: " << this_reporter->histogram_name_ + << " " << count << " " << this_reporter->min_ << " " + << this_reporter->max_ << " " << this_reporter->buckets_; + CHECK(metrics_lib_ != NULL); + CHECK(this_reporter->buckets_ > 0); + metrics_lib_->SendToUMA(this_reporter->histogram_name_, + count, + this_reporter->min_, + this_reporter->max_, + this_reporter->buckets_); +} + FrequencyCounter::FrequencyCounter() : cycle_duration_(1) { } FrequencyCounter::~FrequencyCounter() { } -void FrequencyCounter::Init(const char* filename, - TaggedCounterInterface::Reporter reporter, - void* reporter_handle, +void FrequencyCounter::Init(TaggedCounterInterface* tagged_counter, time_t cycle_duration) { - // Allow tests to inject tagged_counter_ dependency. - if (tagged_counter_.get() == NULL) { - tagged_counter_.reset(new TaggedCounter()); - } - tagged_counter_->Init(filename, reporter, reporter_handle); + tagged_counter_.reset(tagged_counter); DCHECK(cycle_duration > 0); cycle_duration_ = cycle_duration; } diff --git a/metrics/counter.h b/metrics/counter.h index a5e5302f2..e55cdedac 100644 --- a/metrics/counter.h +++ b/metrics/counter.h @@ -5,12 +5,15 @@ #ifndef METRICS_COUNTER_H_ #define METRICS_COUNTER_H_ +#include #include #include #include #include // for FRIEND_TEST +class MetricsLibraryInterface; + namespace chromeos_metrics { // Constants useful for frequency statistics. @@ -44,16 +47,6 @@ class TaggedCounterInterface { virtual ~TaggedCounterInterface() {} - // Initializes the counter by providing the persistent storage - // location |filename| and a |reporter| callback for reporting - // aggregated counts. |reporter_handle| is sent to the |reporter| - // along with the aggregated counts. - // - // NOTE: The assumption is that this object is the sole owner of the - // persistent storage file so no locking is currently implemented. - virtual void Init(const char* filename, - Reporter reporter, void* reporter_handle) = 0; - // Adds |count| of events for the given |tag|. If there's an // existing aggregated count for a different tag, it's reported // through the reporter callback and discarded. @@ -67,12 +60,21 @@ class TaggedCounterInterface { class TaggedCounter : public TaggedCounterInterface { public: TaggedCounter(); - ~TaggedCounter(); + virtual ~TaggedCounter(); + + // Initializes the counter by providing the persistent storage + // location |filename| and a |reporter| callback for reporting + // aggregated counts. |reporter_handle| is sent to the |reporter| + // along with the aggregated counts. + // + // NOTE: The assumption is that this object is the sole owner of the + // persistent storage file so no locking is currently implemented. + virtual void Init(const char* filename, + Reporter reporter, void* reporter_handle); // Implementation of interface methods. - void Init(const char* filename, Reporter reporter, void* reporter_handle); - void Update(int32 tag, int32 count); - void Flush(); + virtual void Update(int32 tag, int32 count); + virtual void Flush(); private: friend class RecordTest; @@ -146,7 +148,7 @@ class TaggedCounter : public TaggedCounterInterface { void WriteRecord(int fd); // Persistent storage file path. - const char* filename_; + std::string filename_; // Aggregated data reporter callback and handle to pass-through. Reporter reporter_; @@ -159,6 +161,71 @@ class TaggedCounter : public TaggedCounterInterface { RecordState record_state_; }; +// TaggedCounterReporter provides a TaggedCounterInterface which both +// counts tagged events and reports them up through the metrics +// library to UMA. +class TaggedCounterReporter : public TaggedCounterInterface { + public: + TaggedCounterReporter(); + virtual ~TaggedCounterReporter(); + + // Set the metrics library used by all TaggedCounterReporter + // instances. We assume there is only one metrics library + // shared amongst all reporters. + static void SetMetricsLibraryInterface(MetricsLibraryInterface* metrics_lib) { + metrics_lib_ = metrics_lib; + } + + // Initializes the counter by providing the persistent storage + // location |filename|, a |histogram_name| (a linear histogram) to + // report to with |min|, |max|, and |buckets| attributes for the + // histogram. + virtual void Init(const char* filename, + const char* histogram_name, + int min, + int max, + int buckets); + + // Implementation of interface method. + virtual void Update(int32 tag, int32 count) { + tagged_counter_->Update(tag, count); + } + // Implementation of interface method. + virtual void Flush() { + tagged_counter_->Flush(); + } + + // Accessor functions. + const std::string& histogram_name() const { + return histogram_name_; + } + + int min() const { + return min_; + } + + int max() const { + return max_; + } + + int buckets() const { + return buckets_; + } + + protected: + friend class TaggedCounterReporterTest; + FRIEND_TEST(TaggedCounterReporterTest, Report); + + static void Report(void* handle, int32 tag, int32 count); + + static MetricsLibraryInterface* metrics_lib_; + scoped_ptr tagged_counter_; + std::string histogram_name_; + int min_; + int max_; + int buckets_; +}; + // FrequencyCounter uses TaggedCounter to maintain a persistent // storage of the number of events that occur in a given cycle // duration (in other words, a frequency count). For example, to @@ -172,13 +239,11 @@ class FrequencyCounter { FrequencyCounter(); virtual ~FrequencyCounter(); - // Initialize a frequency counter, which is necessary before first use. - // |filename|, |reporter|, and |reporter_handle| are used as in - // TaggedCounter::Init. |cycle_duration| is the number of seconds - // in a cycle. - virtual void Init(const char* filename, - TaggedCounterInterface::Reporter reporter, - void* reporter_handle, + // Initialize a frequency counter, which is necessary before first + // use. |tagged_counter| is used to store the counts, its memory + // will be managed by this FrequencyCounter. |cycle_duration| is + // the number of seconds in a cycle. + virtual void Init(TaggedCounterInterface* tagged_counter, time_t cycle_duration); // Record that an event occurred. |count| is the number of concurrent // events that have occurred. The time is implicitly assumed to be the @@ -187,9 +252,29 @@ class FrequencyCounter { UpdateInternal(count, time(NULL)); } + // Update the frequency counter based on the current time. If a + // cycle has finished, this will have the effect of flushing the + // cycle's count, without first requiring another update to the + // frequency counter. The more often this is called, the lower the + // latency to have a new sample submitted. + virtual void FlushFinishedCycles() { + Update(0); + } + + // Accessor function. + const TaggedCounterInterface& tagged_counter() const { + return *tagged_counter_; + } + + time_t cycle_duration() const { + return cycle_duration_; + } + private: friend class FrequencyCounterTest; FRIEND_TEST(FrequencyCounterTest, UpdateInternal); + FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForWeek); + FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForDay); void UpdateInternal(int32 count, time_t now); int32 GetCycleNumber(time_t now); diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h index 701cce48e..cf2a486c1 100644 --- a/metrics/counter_mock.h +++ b/metrics/counter_mock.h @@ -13,7 +13,7 @@ namespace chromeos_metrics { -class TaggedCounterMock : public TaggedCounterInterface { +class TaggedCounterMock : public TaggedCounter { public: MOCK_METHOD3(Init, void(const char* filename, Reporter reporter, void* reporter_handle)); @@ -21,6 +21,17 @@ class TaggedCounterMock : public TaggedCounterInterface { MOCK_METHOD0(Flush, void()); }; +class TaggedCounterReporterMock : public TaggedCounterReporter { + public: + MOCK_METHOD5(Init, void(const char* filename, + const char* histogram_name, + int min, + int max, + int nbuckets)); + MOCK_METHOD2(Update, void(int32 tag, int32 count)); + MOCK_METHOD0(Flush, void()); +}; + class FrequencyCounterMock : public FrequencyCounter { public: MOCK_METHOD4(Init, void(const char* filename, @@ -28,6 +39,7 @@ class FrequencyCounterMock : public FrequencyCounter { void* reporter_handle, time_t cycle_duration)); MOCK_METHOD1(Update, void(int32 count)); + MOCK_METHOD0(FlushFinishedCycles, void()); }; } // namespace chromeos_metrics diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc index eb68b2acb..358051668 100644 --- a/metrics/counter_test.cc +++ b/metrics/counter_test.cc @@ -13,6 +13,7 @@ #include "counter.h" #include "counter_mock.h" // For TaggedCounterMock. +#include "metrics_library_mock.h" using ::testing::_; using ::testing::MockFunction; @@ -37,7 +38,7 @@ class RecordTest : public testing::Test { class TaggedCounterTest : public testing::Test { protected: virtual void SetUp() { - EXPECT_EQ(NULL, counter_.filename_); + EXPECT_TRUE(counter_.filename_.empty()); EXPECT_TRUE(NULL == counter_.reporter_); EXPECT_EQ(NULL, counter_.reporter_handle_); EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); @@ -256,14 +257,85 @@ TEST_F(TaggedCounterTest, Update) { EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); } +static const char kTestFilename[] = "test_filename"; +static const char kTestHistogram[] = "test_histogram"; +const int kHistogramMin = 15; +const int kHistogramMax = 1024; +const int kHistogramBuckets = 23; + +class TaggedCounterReporterTest : public testing::Test { + protected: + virtual void SetUp() { + tagged_counter_ = new StrictMock(); + reporter_.tagged_counter_.reset(tagged_counter_); + metrics_lib_.reset(new StrictMock); + reporter_.SetMetricsLibraryInterface(metrics_lib_.get()); + ASSERT_TRUE(metrics_lib_.get() == reporter_.metrics_lib_); + } + virtual void TearDown() { + reporter_.SetMetricsLibraryInterface(NULL); + } + + void DoInit(); + StrictMock* tagged_counter_; + TaggedCounterReporter reporter_; + scoped_ptr metrics_lib_; +}; + +void TaggedCounterReporterTest::DoInit() { + EXPECT_CALL(*tagged_counter_, + Init(kTestFilename, + TaggedCounterReporter::Report, + &reporter_)) + .Times(1) + .RetiresOnSaturation(); + reporter_.Init(kTestFilename, + kTestHistogram, + kHistogramMin, + kHistogramMax, + kHistogramBuckets); + EXPECT_EQ(kTestHistogram, reporter_.histogram_name_); + EXPECT_EQ(kHistogramBuckets, reporter_.buckets_); + EXPECT_EQ(kHistogramMax, reporter_.max_); + EXPECT_EQ(kHistogramMin, reporter_.min_); +} + +TEST_F(TaggedCounterReporterTest, Init) { + DoInit(); +} + +TEST_F(TaggedCounterReporterTest, Update) { + DoInit(); + EXPECT_CALL(*tagged_counter_, Update(1, 2)) + .Times(1) + .RetiresOnSaturation(); + reporter_.Update(1, 2); +} + +TEST_F(TaggedCounterReporterTest, Flush) { + DoInit(); + EXPECT_CALL(*tagged_counter_, Flush()) + .Times(1) + .RetiresOnSaturation(); + reporter_.Flush(); +} + +TEST_F(TaggedCounterReporterTest, Report) { + DoInit(); + EXPECT_CALL(*metrics_lib_, SendToUMA(kTestHistogram, + 301, + kHistogramMin, + kHistogramMax, + kHistogramBuckets)) + .Times(1) + .RetiresOnSaturation(); + reporter_.Report(&reporter_, 127, 301); +} + class FrequencyCounterTest : public testing::Test { protected: virtual void SetUp() { - tagged_counter_ = new StrictMock; - frequency_counter_.tagged_counter_.reset(tagged_counter_); - } - - static void FakeReporter(void *, int32, int32) { + tagged_counter_ = NULL; } void CheckInit(int32 cycle_duration); @@ -276,14 +348,10 @@ class FrequencyCounterTest : public testing::Test { }; void FrequencyCounterTest::CheckInit(int32 cycle_duration) { - EXPECT_CALL(*tagged_counter_, Init(kTestRecordFile, FakeReporter, this)) - .Times(1) - .RetiresOnSaturation(); - frequency_counter_.Init(kTestRecordFile, - FakeReporter, - this, - cycle_duration); + tagged_counter_ = new StrictMock; + frequency_counter_.Init(tagged_counter_, cycle_duration); EXPECT_EQ(cycle_duration, frequency_counter_.cycle_duration_); + EXPECT_EQ(tagged_counter_, frequency_counter_.tagged_counter_.get()); } TEST_F(FrequencyCounterTest, Init) { @@ -292,10 +360,12 @@ TEST_F(FrequencyCounterTest, Init) { void FrequencyCounterTest::CheckCycleNumber(int32 cycle_duration) { CheckInit(cycle_duration); - EXPECT_EQ(150, frequency_counter_.GetCycleNumber(cycle_duration * 150)); - EXPECT_EQ(150, frequency_counter_.GetCycleNumber(cycle_duration * 150 + - cycle_duration - 1)); - EXPECT_EQ(151, frequency_counter_.GetCycleNumber(cycle_duration * 151 + 1)); + EXPECT_EQ(150, frequency_counter_.GetCycleNumber( + cycle_duration * 150)); + EXPECT_EQ(150, frequency_counter_.GetCycleNumber( + cycle_duration * 150 + cycle_duration - 1)); + EXPECT_EQ(151, frequency_counter_.GetCycleNumber( + cycle_duration * 151 + 1)); EXPECT_EQ(0, frequency_counter_.GetCycleNumber(0)); } @@ -310,7 +380,9 @@ TEST_F(FrequencyCounterTest, GetCycleNumberForDay) { TEST_F(FrequencyCounterTest, UpdateInternal) { CheckInit(kSecondsPerWeek); - EXPECT_CALL(*tagged_counter_, Update(150, 2)); + EXPECT_CALL(*tagged_counter_, Update(150, 2)) + .Times(1) + .RetiresOnSaturation(); frequency_counter_.UpdateInternal(2, kSecondsPerWeek * 150); } diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 0171fdec9..4d50cb5e5 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -4,10 +4,11 @@ #include "metrics_daemon.h" -#include +#include #include #include +#include #include "counter.h" @@ -43,7 +44,7 @@ const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; static const char kUncleanShutdownDetectedFile[] = "/tmp/unclean-shutdown-detected"; -// static metrics parameters. +// static metrics parameters const char MetricsDaemon::kMetricDailyUseTimeName[] = "Logging.DailyUseTime"; const int MetricsDaemon::kMetricDailyUseTimeMin = 1; @@ -73,16 +74,26 @@ const int MetricsDaemon::kMetricCrashIntervalBuckets = 50; // crash frequency metrics const char MetricsDaemon::kMetricAnyCrashesDailyName[] = "Logging.AnyCrashesDaily"; +const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] = + "Logging.AnyCrashesWeekly"; const char MetricsDaemon::kMetricKernelCrashesDailyName[] = "Logging.KernelCrashesDaily"; +const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] = + "Logging.KernelCrashesWeekly"; const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] = "Logging.UncleanShutdownsDaily"; +const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] = + "Logging.UncleanShutdownsWeekly"; const char MetricsDaemon::kMetricUserCrashesDailyName[] = "Logging.UserCrashesDaily"; -const char MetricsDaemon::kMetricCrashesDailyMin = 1; -const char MetricsDaemon::kMetricCrashesDailyMax = 100; -const char MetricsDaemon::kMetricCrashesDailyBuckets = 50; +const char MetricsDaemon::kMetricUserCrashesWeeklyName[] = + "Logging.UserCrashesWeekly"; +const char MetricsDaemon::kMetricCrashFrequencyMin = 1; +const char MetricsDaemon::kMetricCrashFrequencyMax = 100; +const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50; +// persistent metrics path +const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; // static @@ -182,7 +193,17 @@ MetricsDaemon::MetricsDaemon() usemon_interval_(0), usemon_source_(NULL) {} -MetricsDaemon::~MetricsDaemon() {} +MetricsDaemon::~MetricsDaemon() { + DeleteFrequencyCounters(); +} + +void MetricsDaemon::DeleteFrequencyCounters() { + for (FrequencyCounters::iterator i = frequency_counters_.begin(); + i != frequency_counters_.end(); ++i) { + delete i->second; + i->second = NULL; + } +} void MetricsDaemon::Run(bool run_as_daemon) { if (run_as_daemon && daemon(0, 0) != 0) @@ -199,64 +220,72 @@ void MetricsDaemon::Run(bool run_as_daemon) { Loop(); } +FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) { + return FilePath(kMetricsPath).Append(histogram_name); +} + +void MetricsDaemon::ConfigureCrashIntervalReporter( + const char* histogram_name, + scoped_ptr* reporter) { + reporter->reset(new chromeos_metrics::TaggedCounterReporter()); + FilePath file_path = GetHistogramPath(histogram_name); + (*reporter)->Init(file_path.value().c_str(), + histogram_name, + kMetricCrashIntervalMin, + kMetricCrashIntervalMax, + kMetricCrashIntervalBuckets); +} + +void MetricsDaemon::ConfigureCrashFrequencyReporter( + const char* histogram_name) { + scoped_ptr reporter( + new chromeos_metrics::TaggedCounterReporter()); + FilePath file_path = GetHistogramPath(histogram_name); + reporter->Init(file_path.value().c_str(), + histogram_name, + kMetricCrashFrequencyMin, + kMetricCrashFrequencyMax, + kMetricCrashFrequencyBuckets); + scoped_ptr new_counter( + new chromeos_metrics::FrequencyCounter()); + time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ? + chromeos_metrics::kSecondsPerWeek : + chromeos_metrics::kSecondsPerDay; + new_counter->Init( + static_cast( + reporter.release()), + cycle_duration); + frequency_counters_[histogram_name] = new_counter.release(); +} + void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; + chromeos_metrics::TaggedCounterReporter:: + SetMetricsLibraryInterface(metrics_lib); static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage"; daily_use_.reset(new chromeos_metrics::TaggedCounter()); daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this); - static const char kUserCrashIntervalRecordFile[] = - "/var/log/metrics/user-crash-interval"; - user_crash_interval_.reset(new chromeos_metrics::TaggedCounter()); - user_crash_interval_->Init(kUserCrashIntervalRecordFile, - &ReportUserCrashInterval, this); + ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName, + &kernel_crash_interval_); + ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName, + &unclean_shutdown_interval_); + ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName, + &user_crash_interval_); - static const char kKernelCrashIntervalRecordFile[] = - "/var/log/metrics/kernel-crash-interval"; - kernel_crash_interval_.reset(new chromeos_metrics::TaggedCounter()); - kernel_crash_interval_->Init(kKernelCrashIntervalRecordFile, - &ReportKernelCrashInterval, this); - - static const char kUncleanShutdownDetectedFile[] = - "/var/log/metrics/unclean-shutdown-interval"; - unclean_shutdown_interval_.reset(new chromeos_metrics::TaggedCounter()); - unclean_shutdown_interval_->Init(kUncleanShutdownDetectedFile, - &ReportUncleanShutdownInterval, this); - - static const char kUserCrashesDailyRecordFile[] = - "/var/log/metrics/user-crashes-daily"; - user_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter()); - user_crashes_daily_->Init(kUserCrashesDailyRecordFile, - &ReportUserCrashesDaily, - this, - chromeos_metrics::kSecondsPerDay); - - static const char kKernelCrashesDailyRecordFile[] = - "/var/log/metrics/kernel-crashes-daily"; - kernel_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter()); - kernel_crashes_daily_->Init(kKernelCrashesDailyRecordFile, - &ReportKernelCrashesDaily, - this, - chromeos_metrics::kSecondsPerDay); - - static const char kUncleanShutdownsDailyRecordFile[] = - "/var/log/metrics/unclean-shutdowns-daily"; - unclean_shutdowns_daily_.reset(new chromeos_metrics::FrequencyCounter()); - unclean_shutdowns_daily_->Init(kUncleanShutdownsDailyRecordFile, - &ReportUncleanShutdownsDaily, - this, - chromeos_metrics::kSecondsPerDay); - - static const char kAnyCrashesUserCrashDailyRecordFile[] = - "/var/log/metrics/any-crashes-daily"; - any_crashes_daily_.reset(new chromeos_metrics::FrequencyCounter()); - any_crashes_daily_->Init(kAnyCrashesUserCrashDailyRecordFile, - &ReportAnyCrashesDaily, - this, - chromeos_metrics::kSecondsPerDay); + DeleteFrequencyCounters(); + ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName); + ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName); + ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName); + ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName); + ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName); + ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName); + ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName); + ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); + ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); // Don't setup D-Bus and GLib in test mode. if (testing) @@ -453,6 +482,12 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { user_crash_interval_->Update(0, seconds); kernel_crash_interval_->Update(0, seconds); + // Flush finished cycles of all frequency counters. + for (FrequencyCounters::iterator i = frequency_counters_.begin(); + i != frequency_counters_.end(); ++i) { + i->second->FlushFinishedCycles(); + } + // Schedules a use monitor on inactive->active transitions and // unschedules it on active->inactive transitions. if (!user_active_ && active) @@ -473,8 +508,10 @@ void MetricsDaemon::ProcessUserCrash() { // Reports the active use time since the last crash and resets it. user_crash_interval_->Flush(); - user_crashes_daily_->Update(1); - any_crashes_daily_->Update(1); + frequency_counters_[kMetricUserCrashesDailyName]->Update(1); + frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1); + frequency_counters_[kMetricAnyCrashesDailyName]->Update(1); + frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1); } void MetricsDaemon::ProcessKernelCrash() { @@ -484,8 +521,10 @@ void MetricsDaemon::ProcessKernelCrash() { // Reports the active use time since the last crash and resets it. kernel_crash_interval_->Flush(); - kernel_crashes_daily_->Update(1); - any_crashes_daily_->Update(1); + frequency_counters_[kMetricKernelCrashesDailyName]->Update(1); + frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1); + frequency_counters_[kMetricAnyCrashesDailyName]->Update(1); + frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1); } void MetricsDaemon::ProcessUncleanShutdown() { @@ -495,8 +534,10 @@ void MetricsDaemon::ProcessUncleanShutdown() { // Reports the active use time since the last crash and resets it. unclean_shutdown_interval_->Flush(); - unclean_shutdowns_daily_->Update(1); - any_crashes_daily_->Update(1); + frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1); + frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1); + frequency_counters_[kMetricAnyCrashesDailyName]->Update(1); + frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1); } bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) { @@ -584,69 +625,6 @@ void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { kMetricDailyUseTimeBuckets); } -// static -void MetricsDaemon::ReportCrashInterval(const char* histogram_name, - void* handle, int count) { - MetricsDaemon* daemon = static_cast(handle); - daemon->SendMetric(histogram_name, count, - kMetricCrashIntervalMin, - kMetricCrashIntervalMax, - kMetricCrashIntervalBuckets); -} - -// static -void MetricsDaemon::ReportUserCrashInterval(void* handle, - int tag, int count) { - ReportCrashInterval(kMetricUserCrashIntervalName, handle, count); -} - -// static -void MetricsDaemon::ReportKernelCrashInterval(void* handle, - int tag, int count) { - ReportCrashInterval(kMetricKernelCrashIntervalName, handle, count); -} - -// static -void MetricsDaemon::ReportUncleanShutdownInterval(void* handle, - int tag, int count) { - ReportCrashInterval(kMetricUncleanShutdownIntervalName, handle, count); -} - -// static -void MetricsDaemon::ReportCrashesDailyFrequency(const char* histogram_name, - void* handle, - int count) { - MetricsDaemon* daemon = static_cast(handle); - daemon->SendMetric(histogram_name, count, - kMetricCrashesDailyMin, - kMetricCrashesDailyMax, - kMetricCrashesDailyBuckets); -} - -// static -void MetricsDaemon::ReportUserCrashesDaily(void* handle, - int tag, int count) { - ReportCrashesDailyFrequency(kMetricUserCrashesDailyName, handle, count); -} - -// static -void MetricsDaemon::ReportKernelCrashesDaily(void* handle, - int tag, int count) { - ReportCrashesDailyFrequency(kMetricKernelCrashesDailyName, handle, count); -} - -// static -void MetricsDaemon::ReportUncleanShutdownsDaily(void* handle, - int tag, int count) { - ReportCrashesDailyFrequency(kMetricUncleanShutdownsDailyName, handle, count); -} - -// static -void MetricsDaemon::ReportAnyCrashesDaily(void* handle, int tag, int count) { - ReportCrashesDailyFrequency(kMetricAnyCrashesDailyName, handle, count); -} - - void MetricsDaemon::SendMetric(const string& name, int sample, int min, int max, int nbuckets) { DLOG(INFO) << "received metric: " << name << " " << sample << " " diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index f98c34c4f..d400bf0f0 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -7,7 +7,9 @@ #include #include +#include +#include #include #include #include // for FRIEND_TEST @@ -16,7 +18,8 @@ namespace chromeos_metrics { class FrequencyCounter; -class TaggedCounterInterface; +class TaggedCounter; +class TaggedCounterReporter; } class MetricsDaemon { @@ -35,6 +38,10 @@ class MetricsDaemon { private: friend class MetricsDaemonTest; FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash); + FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoCurrent); + FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast); + FRIEND_TEST(MetricsDaemonTest, GetHistogramPath); + FRIEND_TEST(MetricsDaemonTest, IsNewEpoch); FRIEND_TEST(MetricsDaemonTest, LookupNetworkState); FRIEND_TEST(MetricsDaemonTest, LookupPowerState); FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); @@ -89,11 +96,15 @@ class MetricsDaemon { int seconds_; }; + typedef std::map + FrequencyCounters; + // Metric parameters. static const char kMetricAnyCrashesDailyName[]; - static const char kMetricCrashesDailyBuckets; - static const char kMetricCrashesDailyMax; - static const char kMetricCrashesDailyMin; + static const char kMetricAnyCrashesWeeklyName[]; + static const char kMetricCrashFrequencyBuckets; + static const char kMetricCrashFrequencyMax; + static const char kMetricCrashFrequencyMin; static const int kMetricCrashIntervalBuckets; static const int kMetricCrashIntervalMax; static const int kMetricCrashIntervalMin; @@ -102,14 +113,18 @@ class MetricsDaemon { static const int kMetricDailyUseTimeMin; static const char kMetricDailyUseTimeName[]; static const char kMetricKernelCrashesDailyName[]; + static const char kMetricKernelCrashesWeeklyName[]; static const char kMetricKernelCrashIntervalName[]; + static const char kMetricsPath[]; static const int kMetricTimeToNetworkDropBuckets; static const int kMetricTimeToNetworkDropMax; static const int kMetricTimeToNetworkDropMin; static const char kMetricTimeToNetworkDropName[]; static const char kMetricUncleanShutdownIntervalName[]; static const char kMetricUncleanShutdownsDailyName[]; + static const char kMetricUncleanShutdownsWeeklyName[]; static const char kMetricUserCrashesDailyName[]; + static const char kMetricUserCrashesWeeklyName[]; static const char kMetricUserCrashIntervalName[]; // D-Bus message match strings. @@ -124,6 +139,20 @@ class MetricsDaemon { // Array of user session states. static const char* kSessionStates_[kNumberSessionStates]; + // Clears and deletes the data contained in frequency_counters_. + void DeleteFrequencyCounters(); + + // Configures the given crash interval reporter. + void ConfigureCrashIntervalReporter( + const char* histogram_name, + scoped_ptr* reporter); + + // Configures the given frequency counter reporter. + void ConfigureCrashFrequencyReporter(const char* histogram_name); + + // Returns file path to persistent file for generating given histogram. + FilePath GetHistogramPath(const char* histogram_name); + // Creates the event loop and enters it. void Loop(); @@ -203,48 +232,15 @@ class MetricsDaemon { // Unschedules a scheduled use monitor, if any. void UnscheduleUseMonitor(); + // Report daily use through UMA. + static void ReportDailyUse(void* handle, int tag, int count); + // Sends a regular (exponential) histogram sample to Chrome for // transport to UMA. See MetricsLibrary::SendToUMA in // metrics_library.h for a description of the arguments. void SendMetric(const std::string& name, int sample, int min, int max, int nbuckets); - // TaggedCounter callback to process aggregated daily usage data and - // send to UMA. - static void ReportDailyUse(void* data, int tag, int count); - - // Helper to report a crash interval to UMA. - static void ReportCrashInterval(const char* histogram_name, - void* handle, int count); - - // TaggedCounter callback to process time between user-space process - // crashes and send to UMA. - static void ReportUserCrashInterval(void* data, int tag, int count); - - // TaggedCounter callback to process time between kernel crashes and - // send to UMA. - static void ReportKernelCrashInterval(void* data, int tag, int count); - - // TaggedCounter callback to process time between unclean shutdowns and - // send to UMA. - static void ReportUncleanShutdownInterval(void* data, int tag, int count); - - // Helper to report a daily crash frequency to UMA. - static void ReportCrashesDailyFrequency(const char* histogram_name, - void* handle, int count); - - // TaggedCounter callback to report daily crash frequency to UMA. - static void ReportUserCrashesDaily(void* handle, int tag, int count); - - // TaggedCounter callback to report kernel crash frequency to UMA. - static void ReportKernelCrashesDaily(void* handle, int tag, int count); - - // TaggedCounter callback to report unclean shutdown frequency to UMA. - static void ReportUncleanShutdownsDaily(void* handle, int tag, int count); - - // TaggedCounter callback to report frequency of any crashes to UMA. - static void ReportAnyCrashesDaily(void* handle, int tag, int count); - // Test mode. bool testing_; @@ -275,30 +271,20 @@ class MetricsDaemon { base::Time user_active_last_; // Daily active use time in seconds. - scoped_ptr daily_use_; + scoped_ptr daily_use_; // Active use time between user-space process crashes. - scoped_ptr user_crash_interval_; + scoped_ptr user_crash_interval_; // Active use time between kernel crashes. - scoped_ptr kernel_crash_interval_; + scoped_ptr kernel_crash_interval_; // Active use time between unclean shutdowns crashes. - scoped_ptr + scoped_ptr unclean_shutdown_interval_; - // Daily count of user-space process crashes. - scoped_ptr user_crashes_daily_; - - // Daily count of kernel crashes. - scoped_ptr kernel_crashes_daily_; - - // Daily count of unclean shutdowns. - scoped_ptr unclean_shutdowns_daily_; - - // Daily count of any crashes (user-space processes, kernel, or - // unclean shutdowns). - scoped_ptr any_crashes_daily_; + // Map of all frequency counters, to simplify flushing them. + FrequencyCounters frequency_counters_; // Sleep period until the next daily usage aggregation performed by // the daily use monitor (see ScheduleUseMonitor). diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index e551cf990..9ba63e80f 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include #include @@ -11,14 +13,21 @@ using base::Time; using base::TimeTicks; +using chromeos_metrics::FrequencyCounter; using chromeos_metrics::FrequencyCounterMock; using chromeos_metrics::TaggedCounterMock; +using chromeos_metrics::TaggedCounterReporter; +using chromeos_metrics::TaggedCounterReporterMock; using ::testing::_; using ::testing::Return; using ::testing::StrictMock; static const int kSecondsPerDay = 24 * 60 * 60; +static const char kTestDir[] = "test"; +static const char kLastFile[] = "test/last"; +static const char kCurrentFile[] = "test/current"; + // This class allows a TimeTicks object to be initialized with seconds // (rather than microseconds) through the protected TimeTicks(int64) // constructor. @@ -48,6 +57,38 @@ class MetricsDaemonTest : public testing::Test { EXPECT_EQ(NULL, daemon_.user_crash_interval_.get()); daemon_.Init(true, &metrics_lib_); + // Check configuration of a few histograms. + FrequencyCounter* frequency_counter = + daemon_.frequency_counters_[MetricsDaemon::kMetricAnyCrashesDailyName]; + const TaggedCounterReporter* reporter = GetReporter(frequency_counter); + EXPECT_EQ(MetricsDaemon::kMetricAnyCrashesDailyName, + reporter->histogram_name()); + EXPECT_EQ(chromeos_metrics::kSecondsPerDay, + frequency_counter->cycle_duration()); + EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyMin, reporter->min()); + EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyMax, reporter->max()); + EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyBuckets, reporter->buckets()); + + frequency_counter = + daemon_.frequency_counters_[MetricsDaemon::kMetricAnyCrashesWeeklyName]; + reporter = GetReporter(frequency_counter); + EXPECT_EQ(MetricsDaemon::kMetricAnyCrashesWeeklyName, + reporter->histogram_name()); + EXPECT_EQ(chromeos_metrics::kSecondsPerWeek, + frequency_counter->cycle_duration()); + + EXPECT_EQ(MetricsDaemon::kMetricKernelCrashIntervalName, + daemon_.kernel_crash_interval_->histogram_name()); + EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalMin, + daemon_.kernel_crash_interval_->min()); + EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalMax, + daemon_.kernel_crash_interval_->max()); + EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalBuckets, + daemon_.kernel_crash_interval_->buckets()); + + EXPECT_EQ(MetricsDaemon::kMetricUncleanShutdownIntervalName, + daemon_.unclean_shutdown_interval_->histogram_name()); + // Tests constructor initialization. Switches to mock counters. EXPECT_TRUE(NULL != daemon_.daily_use_.get()); EXPECT_TRUE(NULL != daemon_.kernel_crash_interval_.get()); @@ -56,20 +97,20 @@ class MetricsDaemonTest : public testing::Test { // Allocates mock counter and transfers ownership. daily_use_ = new StrictMock(); daemon_.daily_use_.reset(daily_use_); - kernel_crash_interval_ = new StrictMock(); + kernel_crash_interval_ = new StrictMock(); daemon_.kernel_crash_interval_.reset(kernel_crash_interval_); - user_crash_interval_ = new StrictMock(); + user_crash_interval_ = new StrictMock(); daemon_.user_crash_interval_.reset(user_crash_interval_); - unclean_shutdown_interval_ = new StrictMock(); + unclean_shutdown_interval_ = new StrictMock(); daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_); - kernel_crashes_daily_ = new StrictMock(); - daemon_.kernel_crashes_daily_.reset(kernel_crashes_daily_); - user_crashes_daily_ = new StrictMock(); - daemon_.user_crashes_daily_.reset(user_crashes_daily_); - unclean_shutdowns_daily_ = new StrictMock(); - daemon_.unclean_shutdowns_daily_.reset(unclean_shutdowns_daily_); - any_crashes_daily_ = new StrictMock(); - daemon_.any_crashes_daily_.reset(any_crashes_daily_); + + // Reset all frequency counter reporters to mocks for further testing. + MetricsDaemon::FrequencyCounters::iterator i; + for (i = daemon_.frequency_counters_.begin(); + i != daemon_.frequency_counters_.end(); ++i) { + delete i->second; + i->second = new StrictMock(); + } EXPECT_FALSE(daemon_.user_active_); EXPECT_TRUE(daemon_.user_active_last_.is_null()); @@ -77,10 +118,29 @@ class MetricsDaemonTest : public testing::Test { EXPECT_TRUE(daemon_.network_state_last_.is_null()); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); + + file_util::Delete(FilePath(kTestDir), true); + file_util::CreateDirectory(FilePath(kTestDir)); } virtual void TearDown() {} + const TaggedCounterReporter* + GetReporter(FrequencyCounter* frequency_counter) const { + return static_cast( + &frequency_counter->tagged_counter()); + } + + void ExpectFrequencyFlushCalls() { + MetricsDaemon::FrequencyCounters::iterator i; + for (i = daemon_.frequency_counters_.begin(); + i != daemon_.frequency_counters_.end(); ++i) { + FrequencyCounterMock* mock = + static_cast(i->second); + EXPECT_CALL(*mock, FlushFinishedCycles()); + } + } + // Adds active use aggregation counters update expectations that the // specified tag/count update will be generated. void ExpectActiveUseUpdate(int daily_tag, int count) { @@ -93,6 +153,7 @@ class MetricsDaemonTest : public testing::Test { EXPECT_CALL(*user_crash_interval_, Update(0, count)) .Times(1) .RetiresOnSaturation(); + ExpectFrequencyFlushCalls(); } // Adds active use aggregation counters update expectations that @@ -107,6 +168,7 @@ class MetricsDaemonTest : public testing::Test { EXPECT_CALL(*user_crash_interval_, Update(_, _)) .Times(1) .RetiresOnSaturation(); + ExpectFrequencyFlushCalls(); } // Adds a metrics library mock expectation that the specified metric @@ -170,6 +232,12 @@ class MetricsDaemonTest : public testing::Test { dbus_message_unref(msg); } + // Get the frequency counter for the given name. + FrequencyCounterMock& GetFrequencyMock(const char* histogram_name) { + return *static_cast( + daemon_.frequency_counters_[histogram_name]); + } + // The MetricsDaemon under test. MetricsDaemon daemon_; @@ -181,14 +249,9 @@ class MetricsDaemonTest : public testing::Test { // update calls are marked as failures. They are pointers so that // they can replace the scoped_ptr's allocated by the daemon. StrictMock* daily_use_; - StrictMock* kernel_crash_interval_; - StrictMock* user_crash_interval_; - StrictMock* unclean_shutdown_interval_; - - StrictMock* kernel_crashes_daily_; - StrictMock* user_crashes_daily_; - StrictMock* unclean_shutdowns_daily_; - StrictMock* any_crashes_daily_; + StrictMock* kernel_crash_interval_; + StrictMock* user_crash_interval_; + StrictMock* unclean_shutdown_interval_; }; TEST_F(MetricsDaemonTest, CheckSystemCrash) { @@ -217,22 +280,6 @@ TEST_F(MetricsDaemonTest, ReportDailyUse) { MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 60, /* count */ -5); } -TEST_F(MetricsDaemonTest, ReportKernelCrashInterval) { - ExpectMetric(MetricsDaemon::kMetricKernelCrashIntervalName, 50, - MetricsDaemon::kMetricCrashIntervalMin, - MetricsDaemon::kMetricCrashIntervalMax, - MetricsDaemon::kMetricCrashIntervalBuckets); - MetricsDaemon::ReportKernelCrashInterval(&daemon_, 0, 50); -} - -TEST_F(MetricsDaemonTest, ReportUncleanShutdownInterval) { - ExpectMetric(MetricsDaemon::kMetricUncleanShutdownIntervalName, 50, - MetricsDaemon::kMetricCrashIntervalMin, - MetricsDaemon::kMetricCrashIntervalMax, - MetricsDaemon::kMetricCrashIntervalBuckets); - MetricsDaemon::ReportUncleanShutdownInterval(&daemon_, 0, 50); -} - TEST_F(MetricsDaemonTest, LookupNetworkState) { EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.LookupNetworkState("online")); @@ -268,15 +315,25 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); IgnoreActiveUseUpdate(); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName), + Update(1)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName), + Update(1)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesDailyName), + Update(1)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesWeeklyName), + Update(1)) + .Times(1) + .RetiresOnSaturation(); EXPECT_CALL(*user_crash_interval_, Flush()) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*user_crashes_daily_, Update(1)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*any_crashes_daily_, Update(1)) - .Times(1) - .RetiresOnSaturation(); msg = NewDBusSignalString("/", "org.chromium.CrashReporter", "UserCrash", @@ -410,8 +467,14 @@ TEST_F(MetricsDaemonTest, ProcessKernelCrash) { EXPECT_CALL(*kernel_crash_interval_, Flush()) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*kernel_crashes_daily_, Update(1)); - EXPECT_CALL(*any_crashes_daily_, Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName), + Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName), + Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricKernelCrashesDailyName), + Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricKernelCrashesWeeklyName), + Update(1)); daemon_.ProcessKernelCrash(); } @@ -420,8 +483,15 @@ TEST_F(MetricsDaemonTest, ProcessUncleanShutdown) { EXPECT_CALL(*unclean_shutdown_interval_, Flush()) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*unclean_shutdowns_daily_, Update(1)); - EXPECT_CALL(*any_crashes_daily_, Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName), + Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName), + Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUncleanShutdownsDailyName), + Update(1)); + EXPECT_CALL( + GetFrequencyMock(MetricsDaemon::kMetricUncleanShutdownsWeeklyName), + Update(1)); daemon_.ProcessUncleanShutdown(); } @@ -430,8 +500,14 @@ TEST_F(MetricsDaemonTest, ProcessUserCrash) { EXPECT_CALL(*user_crash_interval_, Flush()) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*user_crashes_daily_, Update(1)); - EXPECT_CALL(*any_crashes_daily_, Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName), + Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName), + Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesDailyName), + Update(1)); + EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesWeeklyName), + Update(1)); daemon_.ProcessUserCrash(); } @@ -514,20 +590,10 @@ TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) { EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_); } -TEST_F(MetricsDaemonTest, ReportUserCrashInterval) { - ExpectMetric(MetricsDaemon::kMetricUserCrashIntervalName, 50, - MetricsDaemon::kMetricCrashIntervalMin, - MetricsDaemon::kMetricCrashIntervalMax, - MetricsDaemon::kMetricCrashIntervalBuckets); - MetricsDaemon::ReportUserCrashInterval(&daemon_, 0, 50); -} - -TEST_F(MetricsDaemonTest, ReportCrashesDailyFrequency) { - ExpectMetric("foobar", 50, - MetricsDaemon::kMetricCrashesDailyMin, - MetricsDaemon::kMetricCrashesDailyMax, - MetricsDaemon::kMetricCrashesDailyBuckets); - MetricsDaemon::ReportCrashesDailyFrequency("foobar", &daemon_, 50); +TEST_F(MetricsDaemonTest, GetHistogramPath) { + EXPECT_EQ("/var/log/metrics/Logging.AnyCrashesDaily", + daemon_.GetHistogramPath( + MetricsDaemon::kMetricAnyCrashesDailyName).value()); } int main(int argc, char** argv) { diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index e4087ef37..3e3987d75 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -14,12 +15,14 @@ #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) -static const char kAutotestPath[] = - "/var/log/metrics/autotest-events"; -static const char kUMAEventsPath[] = - "/var/log/metrics/uma-events"; +static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; +static const char kUMAEventsPath[] = "/var/log/metrics/uma-events"; +static const char kConsentFile[] = "/home/chronos/Consent To Send Stats"; static const int32_t kBufferSize = 1024; +time_t MetricsLibrary::cached_enabled_time_ = 0; +bool MetricsLibrary::cached_enabled_ = false; + using std::string; // TODO(sosa@chromium.org) - use Chromium logger instead of stderr @@ -38,9 +41,24 @@ static void PrintError(const char* message, const char* file, } MetricsLibrary::MetricsLibrary() - : uma_events_file_(NULL) {} + : uma_events_file_(NULL), + consent_file_(kConsentFile) {} + +bool MetricsLibrary::AreMetricsEnabled() { + static struct stat stat_buffer; + time_t this_check_time = time(NULL); + + if (this_check_time != cached_enabled_time_) { + cached_enabled_time_ = this_check_time; + cached_enabled_ = (stat(consent_file_, &stat_buffer) >= 0); + } + return cached_enabled_; +} bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { + if (!AreMetricsEnabled()) + return true; + int chrome_fd = open(uma_events_file_, O_WRONLY | O_APPEND | O_CREAT, READ_WRITE_ALL_FILE_FLAGS); diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 76fa451ea..52da94d27 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -27,6 +27,9 @@ class MetricsLibrary : public MetricsLibraryInterface { // Initializes the library. void Init(); + // Returns whether or not metrics collection is enabled. + bool AreMetricsEnabled(); + // Sends histogram data to Chrome for transport to UMA and returns // true on success. This method results in the equivalent of an // asynchronous non-blocking RPC to UMA_HISTOGRAM_CUSTOM_COUNTS @@ -69,6 +72,7 @@ class MetricsLibrary : public MetricsLibraryInterface { private: friend class CMetricsLibraryTest; friend class MetricsLibraryTest; + FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled); FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage); FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong); FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome); @@ -89,7 +93,14 @@ class MetricsLibrary : public MetricsLibraryInterface { int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, const char* format, ...); + // Time at which we last checked if metrics were enabled. + static time_t cached_enabled_time_; + + // Cached state of whether or not metrics were enabled. + static bool cached_enabled_; + const char* uma_events_file_; + const char* consent_file_; }; #endif // METRICS_LIBRARY_H_ diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 596abd3e6..cbf4cce24 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -12,6 +12,15 @@ static const FilePath kTestUMAEventsFile("test-uma-events"); +static const char kTestConsent[] = "test-consent"; + +static void SetMetricsEnabled(bool enabled) { + if (enabled) + ASSERT_EQ(1, file_util::WriteFile(FilePath(kTestConsent) , "0", 1)); + else + file_util::Delete(FilePath(kTestConsent), false); +} + class MetricsLibraryTest : public testing::Test { protected: virtual void SetUp() { @@ -19,15 +28,63 @@ class MetricsLibraryTest : public testing::Test { lib_.Init(); EXPECT_TRUE(NULL != lib_.uma_events_file_); lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str(); + SetMetricsEnabled(true); + // Defeat metrics enabled caching between tests. + lib_.cached_enabled_time_ = 0; + lib_.consent_file_ = kTestConsent; } virtual void TearDown() { file_util::Delete(kTestUMAEventsFile, false); } + void VerifyEnabledCacheHit(bool to_value); + void VerifyEnabledCacheEviction(bool to_value); + MetricsLibrary lib_; }; +TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) { + SetMetricsEnabled(false); + EXPECT_FALSE(lib_.AreMetricsEnabled()); +} + +TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) { + EXPECT_TRUE(lib_.AreMetricsEnabled()); +} + +void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { + // We might step from one second to the next one time, but not 100 + // times in a row. + for (int i = 0; i < 100; ++i) { + lib_.cached_enabled_time_ = 0; + SetMetricsEnabled(!to_value); + ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); + SetMetricsEnabled(to_value); + if (lib_.AreMetricsEnabled() == !to_value) + return; + } + ADD_FAILURE() << "Did not see evidence of caching"; +} + +void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) { + lib_.cached_enabled_time_ = 0; + SetMetricsEnabled(!to_value); + ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); + SetMetricsEnabled(to_value); + ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5); + // Sleep one second (or cheat to be faster). + --lib_.cached_enabled_time_; + ASSERT_EQ(to_value, lib_.AreMetricsEnabled()); +} + +TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) { + VerifyEnabledCacheHit(false); + VerifyEnabledCacheHit(true); + VerifyEnabledCacheEviction(false); + VerifyEnabledCacheEviction(true); +} + TEST_F(MetricsLibraryTest, FormatChromeMessage) { char buf[7]; const int kLen = 6; @@ -55,6 +112,12 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } +TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) { + SetMetricsEnabled(false); + EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); + EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); +} + TEST_F(MetricsLibraryTest, SendMessageToChrome) { EXPECT_TRUE(lib_.SendMessageToChrome(4, "test")); EXPECT_TRUE(lib_.SendMessageToChrome(7, "content")); @@ -84,6 +147,12 @@ TEST_F(MetricsLibraryTest, SendToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } +TEST_F(MetricsLibraryTest, SendToUMANotEnabled) { + SetMetricsEnabled(false); + EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); + EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); +} + class CMetricsLibraryTest : public testing::Test { protected: virtual void SetUp() { @@ -93,6 +162,9 @@ class CMetricsLibraryTest : public testing::Test { CMetricsLibraryInit(lib_); EXPECT_TRUE(NULL != ml.uma_events_file_); ml.uma_events_file_ = kTestUMAEventsFile.value().c_str(); + SetMetricsEnabled(true); + reinterpret_cast(lib_)->cached_enabled_time_ = 0; + reinterpret_cast(lib_)->consent_file_ = kTestConsent; } virtual void TearDown() { @@ -103,6 +175,15 @@ class CMetricsLibraryTest : public testing::Test { CMetricsLibrary lib_; }; +TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) { + SetMetricsEnabled(false); + EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_)); +} + +TEST_F(CMetricsLibraryTest, AreMetricsEnabledTrue) { + EXPECT_TRUE(CMetricsLibraryAreMetricsEnabled(lib_)); +} + TEST_F(CMetricsLibraryTest, SendEnumToUMA) { char buf[100]; const int kLen = 40; From 239b826808860a10a8120b99bd31b5e7bdbb9764 Mon Sep 17 00:00:00 2001 From: Sam Leffler Date: Mon, 30 Aug 2010 08:56:58 -0700 Subject: [PATCH 039/200] remove TimeToDrop support Now that flimflam has native support this can be purged. TEST=run FEATURES=test emerge-x86-generic metrics; verify TimeToDrop is still being recorded w/ about:histograms/Network Review URL: http://codereview.chromium.org/3233004 --- metrics/metrics_daemon.cc | 119 +-------------------------------- metrics/metrics_daemon.h | 27 -------- metrics/metrics_daemon_test.cc | 80 ---------------------- metrics/network_states.h | 28 -------- 4 files changed, 1 insertion(+), 253 deletions(-) delete mode 100644 metrics/network_states.h diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 4d50cb5e5..2f62330a1 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -19,7 +19,6 @@ using std::string; #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") #define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter" -#define DBUS_IFACE_FLIMFLAM_MANAGER "org.chromium.flimflam.Manager" #define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager" #define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface" @@ -51,13 +50,6 @@ const int MetricsDaemon::kMetricDailyUseTimeMin = 1; const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay; const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50; -const char MetricsDaemon::kMetricTimeToNetworkDropName[] = - "Network.TimeToDrop"; -const int MetricsDaemon::kMetricTimeToNetworkDropMin = 1; -const int MetricsDaemon::kMetricTimeToNetworkDropMax = - 8 /* hours */ * kMinutesPerHour * kSecondsPerMinute; -const int MetricsDaemon::kMetricTimeToNetworkDropBuckets = 50; - // crash interval metrics const char MetricsDaemon::kMetricKernelCrashIntervalName[] = "Logging.KernelCrashInterval"; @@ -103,12 +95,6 @@ const char* MetricsDaemon::kDBusMatches_[] = { "path='/'," "member='UserCrash'", - "type='signal'," - "sender='org.chromium.flimflam'," - "interface='" DBUS_IFACE_FLIMFLAM_MANAGER "'," - "path='/'," - "member='StateChanged'", - "type='signal'," "interface='" DBUS_IFACE_POWER_MANAGER "'," "path='/'", @@ -120,12 +106,6 @@ const char* MetricsDaemon::kDBusMatches_[] = { "member='SessionStateChanged'", }; -// static -const char* MetricsDaemon::kNetworkStates_[] = { -#define STATE(name, capname) #name, -#include "network_states.h" -}; - // static const char* MetricsDaemon::kPowerStates_[] = { #define STATE(name, capname) #name, @@ -138,56 +118,8 @@ const char* MetricsDaemon::kSessionStates_[] = { #include "session_states.h" }; -// Invokes a remote method over D-Bus that takes no input arguments -// and returns a string result. The method call is issued with a 2 -// second blocking timeout. Returns an empty string on failure or -// timeout. -static string DBusGetString(DBusConnection* connection, - const string& destination, - const string& path, - const string& interface, - const string& method) { - DBusMessage* message = - dbus_message_new_method_call(destination.c_str(), - path.c_str(), - interface.c_str(), - method.c_str()); - if (!message) { - DLOG(WARNING) << "DBusGetString: unable to allocate a message"; - return ""; - } - - DBusError error; - dbus_error_init(&error); - const int kTimeout = 2000; // ms - DLOG(INFO) << "DBusGetString: dest=" << destination << " path=" << path - << " iface=" << interface << " method=" << method; - DBusMessage* reply = - dbus_connection_send_with_reply_and_block(connection, message, kTimeout, - &error); - dbus_message_unref(message); - if (dbus_error_is_set(&error) || !reply) { - DLOG(WARNING) << "DBusGetString: call failed"; - return ""; - } - DBusMessageIter iter; - dbus_message_iter_init(reply, &iter); - if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) { - NOTREACHED(); - dbus_message_unref(reply); - return ""; - } - const char* c_result = ""; - dbus_message_iter_get_basic(&iter, &c_result); - string result = c_result; - DLOG(INFO) << "DBusGetString: result=" << result; - dbus_message_unref(reply); - return result; -} - MetricsDaemon::MetricsDaemon() - : network_state_(kUnknownNetworkState), - power_state_(kUnknownPowerState), + : power_state_(kUnknownPowerState), session_state_(kUnknownSessionState), user_active_(false), usemon_interval_(0), @@ -317,11 +249,6 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { // the registered D-Bus matches is successful. The daemon is not // activated for D-Bus messages that don't match. CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL)); - - // Initializes the current network state by retrieving it from flimflam. - string state_name = DBusGetString(connection, "org.chromium.flimflam", "/", - DBUS_IFACE_FLIMFLAM_MANAGER, "GetState"); - NetStateChanged(state_name.c_str(), TimeTicks::Now()); } void MetricsDaemon::Loop() { @@ -355,13 +282,6 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, CHECK(strcmp(dbus_message_get_member(message), "UserCrash") == 0); daemon->ProcessUserCrash(); - } else if (strcmp(interface, DBUS_IFACE_FLIMFLAM_MANAGER) == 0) { - CHECK(strcmp(dbus_message_get_member(message), - "StateChanged") == 0); - - char* state_name; - dbus_message_iter_get_basic(&iter, &state_name); - daemon->NetStateChanged(state_name, ticks); } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) { const char* member = dbus_message_get_member(message); if (strcmp(member, "ScreenIsLocked") == 0) { @@ -388,43 +308,6 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, return DBUS_HANDLER_RESULT_HANDLED; } -void MetricsDaemon::NetStateChanged(const char* state_name, TimeTicks ticks) { - DLOG(INFO) << "network state: " << state_name; - - NetworkState state = LookupNetworkState(state_name); - - // Logs the time in seconds between the network going online to - // going offline (or, more precisely, going not online) in order to - // measure the mean time to network dropping. Going offline as part - // of suspend-to-RAM is not logged as network drop -- the assumption - // is that the message for suspend-to-RAM comes before the network - // offline message which seems to and should be the case. - if (state != kNetworkStateOnline && - network_state_ == kNetworkStateOnline && - power_state_ != kPowerStateMem) { - TimeDelta since_online = ticks - network_state_last_; - int online_time = static_cast(since_online.InSeconds()); - SendMetric(kMetricTimeToNetworkDropName, online_time, - kMetricTimeToNetworkDropMin, - kMetricTimeToNetworkDropMax, - kMetricTimeToNetworkDropBuckets); - } - - network_state_ = state; - network_state_last_ = ticks; -} - -MetricsDaemon::NetworkState -MetricsDaemon::LookupNetworkState(const char* state_name) { - for (int i = 0; i < kNumberNetworkStates; i++) { - if (strcmp(state_name, kNetworkStates_[i]) == 0) { - return static_cast(i); - } - } - DLOG(WARNING) << "unknown network connection state: " << state_name; - return kUnknownNetworkState; -} - void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) { DLOG(INFO) << "power state: " << state_name; power_state_ = LookupPowerState(state_name); diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index d400bf0f0..5f2e7868a 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -42,13 +42,10 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast); FRIEND_TEST(MetricsDaemonTest, GetHistogramPath); FRIEND_TEST(MetricsDaemonTest, IsNewEpoch); - FRIEND_TEST(MetricsDaemonTest, LookupNetworkState); FRIEND_TEST(MetricsDaemonTest, LookupPowerState); FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); FRIEND_TEST(MetricsDaemonTest, LookupSessionState); FRIEND_TEST(MetricsDaemonTest, MessageFilter); - FRIEND_TEST(MetricsDaemonTest, NetStateChangedSimpleDrop); - FRIEND_TEST(MetricsDaemonTest, NetStateChangedSuspend); FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash); FRIEND_TEST(MetricsDaemonTest, ProcessUncleanShutdown); @@ -64,14 +61,6 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateTimeJump); - // The network states (see network_states.h). - enum NetworkState { - kUnknownNetworkState = -1, // Initial/unknown network state. -#define STATE(name, capname) kNetworkState ## capname, -#include "network_states.h" - kNumberNetworkStates - }; - // The power states (see power_states.h). enum PowerState { kUnknownPowerState = -1, // Initial/unknown power state. @@ -116,10 +105,6 @@ class MetricsDaemon { static const char kMetricKernelCrashesWeeklyName[]; static const char kMetricKernelCrashIntervalName[]; static const char kMetricsPath[]; - static const int kMetricTimeToNetworkDropBuckets; - static const int kMetricTimeToNetworkDropMax; - static const int kMetricTimeToNetworkDropMin; - static const char kMetricTimeToNetworkDropName[]; static const char kMetricUncleanShutdownIntervalName[]; static const char kMetricUncleanShutdownsDailyName[]; static const char kMetricUncleanShutdownsWeeklyName[]; @@ -130,9 +115,6 @@ class MetricsDaemon { // D-Bus message match strings. static const char* kDBusMatches_[]; - // Array of network states. - static const char* kNetworkStates_[kNumberNetworkStates]; - // Array of power states. static const char* kPowerStates_[kNumberPowerStates]; @@ -161,12 +143,6 @@ class MetricsDaemon { DBusMessage* message, void* user_data); - // Processes network state change. - void NetStateChanged(const char* state_name, base::TimeTicks ticks); - - // Given the state name, returns the state id. - NetworkState LookupNetworkState(const char* state_name); - // Processes power state change. void PowerStateChanged(const char* state_name, base::Time now); @@ -247,9 +223,6 @@ class MetricsDaemon { // The metrics library handle. MetricsLibraryInterface* metrics_lib_; - // Current network state. - NetworkState network_state_; - // Timestamps last network state update. This timestamp is used to // sample the time from the network going online to going offline so // TimeTicks ensures a monotonically increasing TimeDelta. diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 9ba63e80f..40edf1fe3 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -37,12 +37,6 @@ class TestTicks : public TimeTicks { : TimeTicks(seconds * Time::kMicrosecondsPerSecond) {} }; -// Overloaded for test failure printing purposes. -static std::ostream& operator<<(std::ostream& o, const TimeTicks& ticks) { - o << ticks.ToInternalValue() << "us"; - return o; -}; - // Overloaded for test failure printing purposes. static std::ostream& operator<<(std::ostream& o, const Time& time) { o << time.ToInternalValue() << "us"; @@ -114,8 +108,6 @@ class MetricsDaemonTest : public testing::Test { EXPECT_FALSE(daemon_.user_active_); EXPECT_TRUE(daemon_.user_active_last_.is_null()); - EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); - EXPECT_TRUE(daemon_.network_state_last_.is_null()); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); @@ -190,15 +182,6 @@ class MetricsDaemonTest : public testing::Test { MetricsDaemon::kMetricDailyUseTimeBuckets); } - // Adds a metrics library mock expectation that the specified time - // to network dropping metric will be generated. - void ExpectTimeToNetworkDropMetric(int sample) { - ExpectMetric(MetricsDaemon::kMetricTimeToNetworkDropName, sample, - MetricsDaemon::kMetricTimeToNetworkDropMin, - MetricsDaemon::kMetricTimeToNetworkDropMax, - MetricsDaemon::kMetricTimeToNetworkDropBuckets); - } - // Converts from seconds to a Time object. Time TestTime(int64 seconds) { return Time::FromInternalValue(seconds * Time::kMicrosecondsPerSecond); @@ -280,15 +263,6 @@ TEST_F(MetricsDaemonTest, ReportDailyUse) { MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 60, /* count */ -5); } -TEST_F(MetricsDaemonTest, LookupNetworkState) { - EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, - daemon_.LookupNetworkState("online")); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, - daemon_.LookupNetworkState("offline")); - EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, - daemon_.LookupNetworkState("somestate")); -} - TEST_F(MetricsDaemonTest, LookupPowerState) { EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.LookupPowerState("on")); @@ -342,16 +316,6 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); - msg = NewDBusSignalString("/", - "org.chromium.flimflam.Manager", - "StateChanged", - "online"); - EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); - res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); - EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); - EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); - DeleteDBusMessage(msg); - msg = NewDBusSignalString("/", "org.chromium.PowerManager", "PowerStateChanged", @@ -393,50 +357,6 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); } -TEST_F(MetricsDaemonTest, NetStateChangedSimpleDrop) { - daemon_.NetStateChanged("online", TestTicks(10)); - EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); - EXPECT_EQ(TestTicks(10), daemon_.network_state_last_); - - ExpectTimeToNetworkDropMetric(20); - daemon_.NetStateChanged("offline", TestTicks(30)); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(TestTicks(30), daemon_.network_state_last_); -} - -TEST_F(MetricsDaemonTest, NetStateChangedSuspend) { - daemon_.NetStateChanged("offline", TestTicks(30)); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(TestTicks(30), daemon_.network_state_last_); - - daemon_.NetStateChanged("online", TestTicks(60)); - EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); - EXPECT_EQ(TestTicks(60), daemon_.network_state_last_); - - daemon_.power_state_ = MetricsDaemon::kPowerStateMem; - daemon_.NetStateChanged("offline", TestTicks(85)); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(TestTicks(85), daemon_.network_state_last_); - - daemon_.NetStateChanged("somestate", TestTicks(90)); - EXPECT_EQ(MetricsDaemon::kUnknownNetworkState, daemon_.network_state_); - EXPECT_EQ(TestTicks(90), daemon_.network_state_last_); - - daemon_.NetStateChanged("offline", TestTicks(95)); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(TestTicks(95), daemon_.network_state_last_); - - daemon_.power_state_ = MetricsDaemon::kPowerStateOn; - daemon_.NetStateChanged("online", TestTicks(105)); - EXPECT_EQ(MetricsDaemon::kNetworkStateOnline, daemon_.network_state_); - EXPECT_EQ(TestTicks(105), daemon_.network_state_last_); - - ExpectTimeToNetworkDropMetric(3); - daemon_.NetStateChanged("offline", TestTicks(108)); - EXPECT_EQ(MetricsDaemon::kNetworkStateOffline, daemon_.network_state_); - EXPECT_EQ(TestTicks(108), daemon_.network_state_last_); -} - TEST_F(MetricsDaemonTest, PowerStateChanged) { ExpectActiveUseUpdate(7, 0); daemon_.SetUserActiveState(/* active */ true, diff --git a/metrics/network_states.h b/metrics/network_states.h deleted file mode 100644 index b02f64a59..000000000 --- a/metrics/network_states.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// A table of network states, to be included when building tabular things. -// -// This file is used to construct two things: an enumerated type in -// metrics_daemon.h, and a table of structures with state names in -// metrics_daemon.cc. Including this file ensures that the two tables are -// always in sync (and saves typing). I don't know of other ways of achieving -// the same result in C/C++, but it doesn't mean there isn't one. - -// Before you include this file, define STATE to do something useful, or else -// if will be a no-op. STATE will be undefined on exit. Don't worry about -// collisions for the STATE macro (as long as it's a macro) because the -// compiler will flag them---in that case, just change the name. If someone is -// misguided enough to use STATE for something other than a macro, the error -// messages will be slightly more complicated. - - -#ifndef STATE -#define STATE(name, capname) -#endif - -STATE(offline, Offline) -STATE(online, Online) - -#undef STATE From e4fb0af5c5a6f1740d3ca86b0dbc67c000fc6a92 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Tue, 14 Sep 2010 18:09:20 -0700 Subject: [PATCH 040/200] Deprecate libcrash.a Change-Id: Ic8dd69478bcc32908c47ed38f61534fefb8e053d BUG=5870 TEST=build_packages Review URL: http://codereview.chromium.org/3413005 --- metrics/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index deb529498..5e3ed14bb 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -18,8 +18,6 @@ SHAREDLIB = libmetrics.so LIB_TEST = metrics_library_test COUNTER_TEST = counter_test -LCRASH ?= -lcrash - TESTCOUNTER_OBJS = \ counter.o \ counter_test.o @@ -55,7 +53,7 @@ $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) $(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(LCRASH) + $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(DAEMON_TEST): $(TESTDAEMON_OBJS) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(TESTDAEMON_LIBS) From dd6a8db06e649fcdd000e1a92620f64502244706 Mon Sep 17 00:00:00 2001 From: Chris Sosa Date: Thu, 30 Sep 2010 16:39:32 -0700 Subject: [PATCH 041/200] Remove blank tracking script. Change-Id: Iffe8e4233a3f7c100d44502baffdc9515cbdf426 BUG=5905 TEST=Emerged metrics package Review URL: http://codereview.chromium.org/3597004 --- metrics/omaha_tracker.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 metrics/omaha_tracker.sh diff --git a/metrics/omaha_tracker.sh b/metrics/omaha_tracker.sh deleted file mode 100644 index e69de29bb..000000000 From fd55798dc07ea8c8f0ee1c3dbd829371aa8eac67 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Fri, 1 Oct 2010 15:11:44 -0700 Subject: [PATCH 042/200] Metrics: Update README to reflect recent changes. The updates include: - Mention the need for "Consent To Send Stats" to exists and the new AreMetricsEnabled API. - Replace wiki with XML references (need to add an internal only document somewhere too). - Reformat to take full advantage of 80-character lines. BUG=none TEST=none Change-Id: Idbe06e70d5d15f1f8dbbb9475f38a1b0becbca32 Review URL: http://codereview.chromium.org/3573007 --- metrics/README | 146 ++++++++++++++++++++++++------------------------- 1 file changed, 70 insertions(+), 76 deletions(-) diff --git a/metrics/README b/metrics/README index 677e6b75c..151964837 100644 --- a/metrics/README +++ b/metrics/README @@ -2,35 +2,32 @@ Copyright (c) 2010 The Chromium OS Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. -The Chrome OS "metrics" package contains utilities for client-side -user metric collection. The collected data is sent to Chrome for -transport to the UMA server. +The Chrome OS "metrics" package contains utilities for client-side user metric +collection. The collected data is sent to Chrome for transport to the UMA +server. ================================================================================ The Metrics Library: libmetrics ================================================================================ -libmetrics is a small library that implements the basic C and C++ API -for metrics collection. All metrics collection is funneled through -this library. The easiest and recommended way for a client-side module -to collect user metrics is to link libmetrics and use its APIs to send -metrics to Chrome for transport to UMA. In order to use the library in -a module, you need to do the following: +libmetrics is a small library that implements the basic C and C++ API for +metrics collection. All metrics collection is funneled through this library. The +easiest and recommended way for a client-side module to collect user metrics is +to link libmetrics and use its APIs to send metrics to Chrome for transport to +UMA. In order to use the library in a module, you need to do the following: -- Add a dependence (DEPEND and RDEPEND) on chromeos-base/metrics to - the module's ebuild. +- Add a dependence (DEPEND and RDEPEND) on chromeos-base/metrics to the module's + ebuild. -- Link the module with libmetrics (for example, by passing -lmetrics - to the module's link command). Both libmetrics.so and libmetrics.a - are built and installed under $SYSROOT/usr/lib/. Note that by - default -lmetrics will link against libmetrics.so, which is - preferred. +- Link the module with libmetrics (for example, by passing -lmetrics to the + module's link command). Both libmetrics.so and libmetrics.a are built and + installed under $SYSROOT/usr/lib/. Note that by default -lmetrics will link + against libmetrics.so, which is preferred. - To access the metrics library API in the module, include the header file. The file is installed in - $SYSROOT/usr/include/ when the metrics library is built and - installed. + $SYSROOT/usr/include/ when the metrics library is built and installed. - The API includes two methods: @@ -42,15 +39,18 @@ a module, you need to do the following: int max) sends a sample for an enumeration (linear) histogram. - Before using these methods, a MetricsLibrary object needs to be - constructed and initialized through its Init method. See the - complete API documentation in metrics_library.h under - src/platform/metrics/. + Before using these methods, a MetricsLibrary object needs to be constructed + and initialized through its Init method. See the complete API documentation in + metrics_library.h under src/platform/metrics/. For more information on the C API see c_metrics_library.h. -- On the target platform, shortly after the sample is sent it should - be visible in Chrome through "about:histograms". +- Samples are sent to Chrome only if the "/home/chronos/Consent To Send Stats" + file exists (see the AreMetricsEnabled API method). Normally, this file is + created when the user opts into metrics collection. + +- On the target platform, shortly after the sample is sent, it should be visible + in Chrome through "about:histograms". ================================================================================ @@ -67,55 +67,51 @@ Network.TimeToDrop Server Side ================================================================================ -If the histogram data is visible in about:histograms, it will be sent -by an official Chrome build to UMA, assuming the user has opted into -metrics collection. To make the histogram visible on -"chromedashboard", the histogram wiki needs to be updated (steps 2 and -3 after following the "Details on how to add your own histograms" link -under the Histograms tab). Include the string "Chrome OS" in the -histogram description so that it's easier to distinguish Chrome OS -specific metrics from general Chrome histograms. +If the histogram data is visible in about:histograms, it will be sent by an +official Chrome build to UMA, assuming the user has opted into metrics +collection. To make the histogram visible on "chromedashboard", the histogram +description XML file needs to be updated (steps 2 and 3 after following the +"Details on how to add your own histograms" link under the Histograms tab). +Include the string "Chrome OS" in the histogram description so that it's easier +to distinguish Chrome OS specific metrics from general Chrome histograms. -The UMA server logs and keeps the collected field data even if the -metric's name is not added to the histogram wiki. However, the -dashboard histogram for that metric will show field data as of the -histogram wiki update date; it will not include data for older -dates. If past data needs to be displayed, manual server-side -intervention is required. In other words, one should assume that field -data collection starts only after the histogram wiki has been updated. +The UMA server logs and keeps the collected field data even if the metric's name +is not added to the histogram XML. However, the dashboard histogram for that +metric will show field data as of the histogram XML update date; it will not +include data for older dates. If past data needs to be displayed, manual +server-side intervention is required. In other words, one should assume that +field data collection starts only after the histogram XML has been updated. ================================================================================ The Metrics Client: metrics_client ================================================================================ -metrics_client is a simple shell command-line utility for sending -histogram samples. It's installed under /usr/bin on the target -platform and uses libmetrics to send the data to Chrome. The utility -is useful for generating metrics from shell scripts. +metrics_client is a simple shell command-line utility for sending histogram +samples. It's installed under /usr/bin on the target platform and uses +libmetrics to send the data to Chrome. The utility is useful for generating +metrics from shell scripts. -For usage information and command-line options, run "metrics_client" -on the target platform or look for "Usage:" in metrics_client.cc. +For usage information and command-line options, run "metrics_client" on the +target platform or look for "Usage:" in metrics_client.cc. ================================================================================ The Metrics Daemon: metrics_daemon ================================================================================ -metrics_daemon is a daemon that runs in the background on the target -platform and is intended for passive or ongoing metrics collection, or -metrics collection requiring feedback from multiple modules. For -example, it listens to D-Bus signals related to the user session and -screen saver states to determine if the user is actively using the -device or not and generates the corresponding data. The metrics daemon -uses libmetrics to send the data to Chrome. +metrics_daemon is a daemon that runs in the background on the target platform +and is intended for passive or ongoing metrics collection, or metrics collection +requiring feedback from multiple modules. For example, it listens to D-Bus +signals related to the user session and screen saver states to determine if the +user is actively using the device or not and generates the corresponding +data. The metrics daemon uses libmetrics to send the data to Chrome. -The recommended way to generate metrics data from a module is to link -and use libmetrics directly. However, the module could instead send -signals to or communicate in some alternative way with the metrics -daemon. Then the metrics daemon needs to monitor for the relevant -events and take appropriate action -- for example, aggregate data and -send the histogram samples. +The recommended way to generate metrics data from a module is to link and use +libmetrics directly. However, the module could instead send signals to or +communicate in some alternative way with the metrics daemon. Then the metrics +daemon needs to monitor for the relevant events and take appropriate action -- +for example, aggregate data and send the histogram samples. ================================================================================ @@ -124,28 +120,26 @@ FAQ Q. What should my histogram's |min| and |max| values be set at? -A. You should set the values to a range that covers the vast majority - of samples that would appear in the field. Note that samples below - the |min| will still be collected in the underflow bucket and - samples above the |max| will end up in the overflow bucket. Also, - the reported mean of the data will be correct regardless of the - range. +A. You should set the values to a range that covers the vast majority of samples + that would appear in the field. Note that samples below the |min| will still + be collected in the underflow bucket and samples above the |max| will end up + in the overflow bucket. Also, the reported mean of the data will be correct + regardless of the range. Q. How many buckets should I use in my histogram? -A. You should allocate as many buckets as necessary to perform proper - analysis on the collected data. Note, however, that the memory - allocated in Chrome for each histogram is proportional to the - number of buckets. Therefore, it is strongly recommended to keep - this number low (e.g., 50 is normal, while 100 is probably high). +A. You should allocate as many buckets as necessary to perform proper analysis + on the collected data. Note, however, that the memory allocated in Chrome for + each histogram is proportional to the number of buckets. Therefore, it is + strongly recommended to keep this number low (e.g., 50 is normal, while 100 + is probably high). Q. When should I use an enumeration (linear) histogram vs. a regular (exponential) histogram? -A. Enumeration histograms should really be used only for sampling - enumerated events and, in some cases, percentages. Normally, you - should use a regular histogram with exponential bucket layout that - provides higher resolution at the low end of the range and lower - resolution at the high end. Regular histograms are generally used - for collecting performance data (e.g., timing, memory usage, power) - as well as aggregated event counts. +A. Enumeration histograms should really be used only for sampling enumerated + events and, in some cases, percentages. Normally, you should use a regular + histogram with exponential bucket layout that provides higher resolution at + the low end of the range and lower resolution at the high end. Regular + histograms are generally used for collecting performance data (e.g., timing, + memory usage, power) as well as aggregated event counts. From eafbbdf3df81cdbdb50b4c676e7d55dbdd9b30a3 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Fri, 1 Oct 2010 15:38:42 -0700 Subject: [PATCH 043/200] metrics: Add guest mode detection to metrics library and client Change-Id: I2c27bd999330395ba3568820ea76198b202bd7f4 BUG=7203 TEST=Verify metrics_client -c and -g toggling consent and guest mode. Review URL: http://codereview.chromium.org/3571009 --- metrics/metrics_client.cc | 164 +++++++++++++++++++++----------- metrics/metrics_daemon.cc | 1 - metrics/metrics_library.cc | 73 +++++++++++++- metrics/metrics_library.h | 12 +++ metrics/metrics_library_test.cc | 63 +++++++++++- 5 files changed, 254 insertions(+), 59 deletions(-) diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index e85c4d473..ce22e987e 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -7,63 +7,29 @@ #include "metrics_library.h" -int main(int argc, char** argv) { - bool send_to_autotest = false; - bool send_to_chrome = true; - bool send_enum = false; - bool secs_to_msecs = false; - int name_index = 1; - bool print_usage = false; - - if (argc >= 3) { - // Parse arguments - int flag; - while ((flag = getopt(argc, argv, "abet")) != -1) { - switch (flag) { - case 'a': - send_to_autotest = true; - send_to_chrome = false; - break; - case 'b': - send_to_chrome = true; - send_to_autotest = true; - break; - case 'e': - send_enum = true; - break; - case 't': - secs_to_msecs = true; - break; - default: - print_usage = true; - break; - } - } - name_index = optind; - } else { - print_usage = true; - } - - int num_args = send_enum ? 3 : 5; - if ((name_index + num_args) != argc || - (send_enum && secs_to_msecs)) { - print_usage = true; - } - - if (print_usage) { - fprintf(stderr, - "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n" - " metrics_client [-ab] -e name sample max\n" - "\n" - " default: send metric with integer values to Chrome only\n" - " |min| > 0, |min| <= sample < |max|\n" - " -a: send metric (name/sample) to Autotest only\n" - " -b: send metric to both Chrome and Autotest\n" - " -e: send linear/enumeration histogram data\n" - " -t: convert sample from double seconds to int milliseconds\n"); - return 1; - } +void ShowUsage() { + fprintf(stderr, + "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n" + " metrics_client [-ab] -e name sample max\n" + " metrics_client [-cg]\n" + "\n" + " default: send metric with integer values to Chrome only\n" + " |min| > 0, |min| <= sample < |max|\n" + " -a: send metric (name/sample) to Autotest only\n" + " -b: send metric to both Chrome and Autotest\n" + " -c: return exit status 0 if user consents to stats, 1 otherwise\n" + " -e: send linear/enumeration histogram data\n" + " -g: return exit status 0 if machine in guest mode, 1 otherwise\n" + " -t: convert sample from double seconds to int milliseconds\n"); + exit(1); +} +static int SendStats(char* argv[], + int name_index, + bool send_enum, + bool secs_to_msecs, + bool send_to_autotest, + bool send_to_chrome) { const char* name = argv[name_index]; int sample; if (secs_to_msecs) { @@ -92,3 +58,89 @@ int main(int argc, char** argv) { } return 0; } + +static int HasConsent() { + MetricsLibrary metrics_lib; + metrics_lib.Init(); + return metrics_lib.AreMetricsEnabled() ? 0 : 1; +} + +static int IsGuestMode() { + MetricsLibrary metrics_lib; + metrics_lib.Init(); + return metrics_lib.IsGuestMode() ? 0 : 1; +} + +int main(int argc, char** argv) { + enum Mode { + kModeSendStats, + kModeHasConsent, + kModeIsGuestMode + } mode = kModeSendStats; + bool send_to_autotest = false; + bool send_to_chrome = true; + bool send_enum = false; + bool secs_to_msecs = false; + bool print_usage = false; + + // Parse arguments + int flag; + while ((flag = getopt(argc, argv, "abcegt")) != -1) { + switch (flag) { + case 'a': + mode = kModeSendStats; + send_to_autotest = true; + send_to_chrome = false; + break; + case 'b': + mode = kModeSendStats; + send_to_chrome = true; + send_to_autotest = true; + break; + case 'c': + mode = kModeHasConsent; + break; + case 'e': + send_enum = true; + break; + case 'g': + mode = kModeIsGuestMode; + break; + case 't': + secs_to_msecs = true; + break; + default: + print_usage = true; + break; + } + } + int name_index = optind; + + int expected_args = 0; + if (mode == kModeSendStats) + expected_args = send_enum ? 3 : 5; + + if ((name_index + expected_args) != argc) { + ShowUsage(); + } + + switch(mode) { + case kModeSendStats: + if (send_enum && secs_to_msecs) { + ShowUsage(); + } + return SendStats(argv, + name_index, + send_enum, + secs_to_msecs, + send_to_autotest, + send_to_chrome); + case kModeHasConsent: + return HasConsent(); + case kModeIsGuestMode: + return IsGuestMode(); + default: + ShowUsage(); + return 0; + } +} diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 2f62330a1..a5059d983 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -210,7 +210,6 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { DeleteFrequencyCounters(); ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName); - ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName); ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName); ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName); ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName); diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 3e3987d75..f720972c2 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -44,13 +44,84 @@ MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), consent_file_(kConsentFile) {} +// We take buffer and buffer_size as parameters in order to simplify testing +// of various alignments of the |device_name| with |buffer_size|. +bool MetricsLibrary::IsDeviceMounted(const char* device_name, + const char* mounts_file, + char* buffer, + int buffer_size, + bool* result) { + if (buffer == NULL || buffer_size < 1) + return false; + int mounts_fd = open(mounts_file, O_RDONLY); + if (mounts_fd < 0) + return false; + // match_offset describes: + // -1 -- not beginning of line + // 0..strlen(device_name)-1 -- this offset in device_name is next to match + // strlen(device_name) -- matched full name, just need a space. + int match_offset = 0; + bool match = false; + while (!match) { + int read_size = read(mounts_fd, buffer, buffer_size); + if (read_size <= 0) { + if (errno == -EINTR) + continue; + break; + } + for (int i = 0; i < read_size; ++i) { + if (buffer[i] == '\n') { + match_offset = 0; + continue; + } + if (match_offset < 0) { + continue; + } + if (device_name[match_offset] == '\0') { + if (buffer[i] == ' ') { + match = true; + break; + } + match_offset = -1; + continue; + } + + if (buffer[i] == device_name[match_offset]) { + ++match_offset; + } else { + match_offset = -1; + } + } + } + close(mounts_fd); + *result = match; + return true; +} + +bool MetricsLibrary::IsGuestMode() { + char buffer[256]; + bool result = false; + if (!IsDeviceMounted("guestfs", + "/proc/mounts", + buffer, + sizeof(buffer), + &result)) { + return false; + } + return result; +} + bool MetricsLibrary::AreMetricsEnabled() { static struct stat stat_buffer; time_t this_check_time = time(NULL); if (this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; - cached_enabled_ = (stat(consent_file_, &stat_buffer) >= 0); + if (stat(consent_file_, &stat_buffer) >= 0 && + !IsGuestMode()) + cached_enabled_ = true; + else + cached_enabled_ = false; } return cached_enabled_; } diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 52da94d27..fb31c20f6 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -27,6 +27,9 @@ class MetricsLibrary : public MetricsLibraryInterface { // Initializes the library. void Init(); + // Returns whether or not the machine is running in guest mode. + bool IsGuestMode(); + // Returns whether or not metrics collection is enabled. bool AreMetricsEnabled(); @@ -75,9 +78,18 @@ class MetricsLibrary : public MetricsLibraryInterface { FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled); FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage); FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong); + FRIEND_TEST(MetricsLibraryTest, IsDeviceMounted); FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome); FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation); + // Sets |*result| to whether or not the |mounts_file| indicates that + // the |device_name| is currently mounted. Uses |buffer| of + // |buffer_size| to read the file. Returns false if any error. + bool IsDeviceMounted(const char* device_name, + const char* mounts_file, + char* buffer, int buffer_size, + bool* result); + // Sends message of size |length| to Chrome for transport to UMA and // returns true on success. bool SendMessageToChrome(int32_t length, const char* message); diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index cbf4cce24..0cd695b1d 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -11,8 +11,8 @@ #include "metrics_library.h" static const FilePath kTestUMAEventsFile("test-uma-events"); - static const char kTestConsent[] = "test-consent"; +static const char kTestMounts[] = "test-mounts"; static void SetMetricsEnabled(bool enabled) { if (enabled) @@ -35,6 +35,8 @@ class MetricsLibraryTest : public testing::Test { } virtual void TearDown() { + file_util::Delete(FilePath(kTestConsent), false); + file_util::Delete(FilePath(kTestMounts), false); file_util::Delete(kTestUMAEventsFile, false); } @@ -44,6 +46,65 @@ class MetricsLibraryTest : public testing::Test { MetricsLibrary lib_; }; +TEST_F(MetricsLibraryTest, IsDeviceMounted) { + static const char kTestContents[] = + "0123456789abcde 0123456789abcde\nguestfs foo bar\n"; + char buffer[1024]; + int block_sizes[] = { 1, 2, 3, 4, 5, 6, 8, 12, 14, 16, 32, 1024 }; + bool result; + EXPECT_FALSE(lib_.IsDeviceMounted("guestfs", + "nonexistent", + buffer, + 1, + &result)); + ASSERT_TRUE(file_util::WriteFile(FilePath(kTestMounts), + kTestContents, + strlen(kTestContents))); + EXPECT_FALSE(lib_.IsDeviceMounted("guestfs", + kTestMounts, + buffer, + 0, + &result)); + for (size_t i = 0; i < arraysize(block_sizes); ++i) { + EXPECT_TRUE(lib_.IsDeviceMounted("0123456789abcde", + kTestMounts, + buffer, + block_sizes[i], + &result)); + EXPECT_TRUE(result); + EXPECT_TRUE(lib_.IsDeviceMounted("guestfs", + kTestMounts, + buffer, + block_sizes[i], + &result)); + EXPECT_TRUE(result); + EXPECT_TRUE(lib_.IsDeviceMounted("0123456", + kTestMounts, + buffer, + block_sizes[i], + &result)); + EXPECT_FALSE(result); + EXPECT_TRUE(lib_.IsDeviceMounted("9abcde", + kTestMounts, + buffer, + block_sizes[i], + &result)); + EXPECT_FALSE(result); + EXPECT_TRUE(lib_.IsDeviceMounted("foo", + kTestMounts, + buffer, + block_sizes[i], + &result)); + EXPECT_FALSE(result); + EXPECT_TRUE(lib_.IsDeviceMounted("bar", + kTestMounts, + buffer, + block_sizes[i], + &result)); + EXPECT_FALSE(result); + } +} + TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) { SetMetricsEnabled(false); EXPECT_FALSE(lib_.AreMetricsEnabled()); From b9b05e604742b2dacaafe0f6d12409201985d7ba Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Tue, 9 Nov 2010 12:18:51 -0800 Subject: [PATCH 044/200] metrics: Update tests to send user in session state change. This updates the signal to match the changes that I'm making in http://codereview.chromium.org/4718001/show. Change-Id: Id414187a62f209f2e8145c75bfd982b357d04e98 BUG=chromium-os:8123 TEST=ran it Review URL: http://codereview.chromium.org/4749001 --- metrics/metrics_daemon_test.cc | 49 +++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 40edf1fe3..530f357b6 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -4,6 +4,9 @@ #include +#include +#include + #include #include @@ -18,6 +21,8 @@ using chromeos_metrics::FrequencyCounterMock; using chromeos_metrics::TaggedCounterMock; using chromeos_metrics::TaggedCounterReporter; using chromeos_metrics::TaggedCounterReporterMock; +using std::string; +using std::vector; using ::testing::_; using ::testing::Return; using ::testing::StrictMock; @@ -165,7 +170,7 @@ class MetricsDaemonTest : public testing::Test { // Adds a metrics library mock expectation that the specified metric // will be generated. - void ExpectMetric(const std::string& name, int sample, + void ExpectMetric(const string& name, int sample, int min, int max, int buckets) { EXPECT_CALL(metrics_lib_, SendToUMA(name, sample, min, max, buckets)) .Times(1) @@ -187,25 +192,27 @@ class MetricsDaemonTest : public testing::Test { return Time::FromInternalValue(seconds * Time::kMicrosecondsPerSecond); } - // Creates a new DBus signal message with a single string - // argument. The message can be deallocated through - // DeleteDBusMessage. + // Creates a new DBus signal message with zero or more string arguments. + // The message can be deallocated through DeleteDBusMessage. // // |path| is the object emitting the signal. // |interface| is the interface the signal is emitted from. // |name| is the name of the signal. - // |arg_value| is the value of the string argument. - DBusMessage* NewDBusSignalString(const std::string& path, - const std::string& interface, - const std::string& name, - const std::string& arg_value) { + // |arg_values| contains the values of the string arguments. + DBusMessage* NewDBusSignalString(const string& path, + const string& interface, + const string& name, + const vector& arg_values) { DBusMessage* msg = dbus_message_new_signal(path.c_str(), interface.c_str(), name.c_str()); DBusMessageIter iter; dbus_message_iter_init_append(msg, &iter); - const char* arg_value_c = arg_value.c_str(); - dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &arg_value_c); + for (vector::const_iterator it = arg_values.begin(); + it != arg_values.end(); ++it) { + const char* str_value = it->c_str(); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &str_value); + } return msg; } @@ -308,29 +315,33 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_CALL(*user_crash_interval_, Flush()) .Times(1) .RetiresOnSaturation(); + vector signal_args; msg = NewDBusSignalString("/", "org.chromium.CrashReporter", "UserCrash", - ""); + signal_args); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); + signal_args.clear(); + signal_args.push_back("on"); msg = NewDBusSignalString("/", "org.chromium.PowerManager", "PowerStateChanged", - "on"); + signal_args); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); + signal_args.clear(); IgnoreActiveUseUpdate(); msg = NewDBusSignalString("/", "org.chromium.PowerManager", "ScreenIsUnlocked", - ""); + signal_args); EXPECT_FALSE(daemon_.user_active_); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); EXPECT_TRUE(daemon_.user_active_); @@ -338,20 +349,26 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); IgnoreActiveUseUpdate(); + signal_args.clear(); + signal_args.push_back("started"); + signal_args.push_back("bob"); // arbitrary username msg = NewDBusSignalString("/org/chromium/SessionManager", "org.chromium.SessionManagerInterface", "SessionStateChanged", - "started"); + signal_args); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); + signal_args.clear(); + signal_args.push_back("randomstate"); + signal_args.push_back("bob"); // arbitrary username msg = NewDBusSignalString("/", "org.chromium.UnknownService.Manager", "StateChanged", - "randomstate"); + signal_args); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res); DeleteDBusMessage(msg); From ed82485c3be8fcab94cfb312c771b28896c4f30e Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 6 Jan 2011 10:51:47 -0800 Subject: [PATCH 045/200] Add support for user actions to the metrics library and the metrics clients. BUG=10696 TEST=unit tests, tested on the device through metrics_client and inspecting the uma-events file. Change-Id: Ie39dd8b5ab968c328993076369a4ba14cb7fcd81 Review URL: http://codereview.chromium.org/6094010 --- metrics/README | 22 ++++++---------------- metrics/c_metrics_library.cc | 8 ++++++++ metrics/c_metrics_library.h | 4 ++++ metrics/metrics_client.cc | 28 +++++++++++++++++++++++----- metrics/metrics_library.cc | 15 +++++++++++++-- metrics/metrics_library.h | 9 +++++++++ metrics/metrics_library_test.cc | 28 ++++++++++++++++++++++++++++ 7 files changed, 91 insertions(+), 23 deletions(-) diff --git a/metrics/README b/metrics/README index 151964837..2ecec5f42 100644 --- a/metrics/README +++ b/metrics/README @@ -29,19 +29,9 @@ UMA. In order to use the library in a module, you need to do the following: header file. The file is installed in $SYSROOT/usr/include/ when the metrics library is built and installed. -- The API includes two methods: - - bool MetricsLibrary::SendToUMA(const std::string& name, int sample, - int min, int max, int nbuckets) - sends a sample for a regular (exponential) histogram. - - bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, - int max) - sends a sample for an enumeration (linear) histogram. - - Before using these methods, a MetricsLibrary object needs to be constructed - and initialized through its Init method. See the complete API documentation in - metrics_library.h under src/platform/metrics/. +- The API is documented in metrics_library.h under src/platform/metrics/. Before + using the API methods, a MetricsLibrary object needs to be constructed and + initialized through its Init method. For more information on the C API see c_metrics_library.h. @@ -88,9 +78,9 @@ The Metrics Client: metrics_client ================================================================================ metrics_client is a simple shell command-line utility for sending histogram -samples. It's installed under /usr/bin on the target platform and uses -libmetrics to send the data to Chrome. The utility is useful for generating -metrics from shell scripts. +samples and user actions. It's installed under /usr/bin on the target platform +and uses libmetrics to send the data to Chrome. The utility is useful for +generating metrics from shell scripts. For usage information and command-line options, run "metrics_client" on the target platform or look for "Usage:" in metrics_client.cc. diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc index e97f9ac26..3450918c4 100644 --- a/metrics/c_metrics_library.cc +++ b/metrics/c_metrics_library.cc @@ -43,6 +43,14 @@ extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, return lib->SendEnumToUMA(std::string(name), sample, max); } +extern "C" int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle, + const char* action) { + MetricsLibrary* lib = reinterpret_cast(handle); + if (lib == NULL) + return 0; + return lib->SendUserActionToUMA(std::string(action)); +} + extern "C" int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle) { MetricsLibrary* lib = reinterpret_cast(handle); if (lib == NULL) diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h index e691ad66e..9500aade5 100644 --- a/metrics/c_metrics_library.h +++ b/metrics/c_metrics_library.h @@ -28,6 +28,10 @@ int CMetricsLibrarySendToUMA(CMetricsLibrary handle, int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, const char* name, int sample, int max); +// C wrapper for MetricsLibrary::SendUserActionToUMA. +int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle, + const char* action); + // C wrapper for MetricsLibrary::AreMetricsEnabled. int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle); diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index ce22e987e..5aedd8b26 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -11,6 +11,7 @@ void ShowUsage() { fprintf(stderr, "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n" " metrics_client [-ab] -e name sample max\n" + " metrics_client -u action\n" " metrics_client [-cg]\n" "\n" " default: send metric with integer values to Chrome only\n" @@ -20,7 +21,8 @@ void ShowUsage() { " -c: return exit status 0 if user consents to stats, 1 otherwise\n" " -e: send linear/enumeration histogram data\n" " -g: return exit status 0 if machine in guest mode, 1 otherwise\n" - " -t: convert sample from double seconds to int milliseconds\n"); + " -t: convert sample from double seconds to int milliseconds\n" + " -u: send a user action to Chrome\n"); exit(1); } @@ -59,6 +61,14 @@ static int SendStats(char* argv[], return 0; } +static int SendUserAction(char* argv[], int action_index) { + const char* action = argv[action_index]; + MetricsLibrary metrics_lib; + metrics_lib.Init(); + metrics_lib.SendUserActionToUMA(action); + return 0; +} + static int HasConsent() { MetricsLibrary metrics_lib; metrics_lib.Init(); @@ -74,6 +84,7 @@ static int IsGuestMode() { int main(int argc, char** argv) { enum Mode { kModeSendStats, + kModeSendUserAction, kModeHasConsent, kModeIsGuestMode } mode = kModeSendStats; @@ -85,7 +96,7 @@ int main(int argc, char** argv) { // Parse arguments int flag; - while ((flag = getopt(argc, argv, "abcegt")) != -1) { + while ((flag = getopt(argc, argv, "abcegtu")) != -1) { switch (flag) { case 'a': mode = kModeSendStats; @@ -109,18 +120,23 @@ int main(int argc, char** argv) { case 't': secs_to_msecs = true; break; + case 'u': + mode = kModeSendUserAction; + break; default: print_usage = true; break; } } - int name_index = optind; + int arg_index = optind; int expected_args = 0; if (mode == kModeSendStats) expected_args = send_enum ? 3 : 5; + else if (mode == kModeSendUserAction) + expected_args = 1; - if ((name_index + expected_args) != argc) { + if ((arg_index + expected_args) != argc) { ShowUsage(); } @@ -130,11 +146,13 @@ int main(int argc, char** argv) { ShowUsage(); } return SendStats(argv, - name_index, + arg_index, send_enum, secs_to_msecs, send_to_autotest, send_to_chrome); + case kModeSendUserAction: + return SendUserAction(argv, arg_index); case kModeHasConsent: return HasConsent(); case kModeIsGuestMode: diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index f720972c2..05e63acff 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -221,7 +221,6 @@ bool MetricsLibrary::SendToUMA(const string& name, int sample, FormatChromeMessage(kBufferSize, message, "histogram%c%s %d %d %d %d", '\0', name.c_str(), sample, min, max, nbuckets); - if (message_length < 0) return false; @@ -237,7 +236,19 @@ bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, FormatChromeMessage(kBufferSize, message, "linearhistogram%c%s %d %d", '\0', name.c_str(), sample, max); - + if (message_length < 0) + return false; + + // Send the message. + return SendMessageToChrome(message_length, message); +} + +bool MetricsLibrary::SendUserActionToUMA(const std::string& action) { + // Format the message. + char message[kBufferSize]; + int32_t message_length = + FormatChromeMessage(kBufferSize, message, + "useraction%c%s", '\0', action.c_str()); if (message_length < 0) return false; diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index fb31c20f6..b9b817a9c 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -69,6 +69,15 @@ class MetricsLibrary : public MetricsLibraryInterface { // normal, while 100 is high). bool SendEnumToUMA(const std::string& name, int sample, int max); + // Sends a user action to Chrome for transport to UMA and returns true on + // success. This method results in the equivalent of an asynchronous + // non-blocking RPC to UserMetrics::RecordAction (see the comments in + // chrome/browser/chromeos/external_metrics.cc and + // chrome/browser/metrics/user_metrics.h on how to register new user actions). + // + // |action| is the user-generated event (e.g., "MuteKeyPressed"). + bool SendUserActionToUMA(const std::string& action); + // Sends to Autotest and returns true on success. static bool SendToAutotest(const std::string& name, int value); diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 0cd695b1d..3e49f6925 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -214,6 +214,23 @@ TEST_F(MetricsLibraryTest, SendToUMANotEnabled) { EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } +TEST_F(MetricsLibraryTest, SendUserActionToUMA) { + char buf[100]; + const int kLen = 30; + EXPECT_TRUE(lib_.SendUserActionToUMA("SomeKeyPressed")); + EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + + char exp[kLen]; + sprintf(exp, "%c%c%c%cuseraction%cSomeKeyPressed", kLen, 0, 0, 0, 0); + EXPECT_EQ(0, memcmp(exp, buf, kLen)); +} + +TEST_F(MetricsLibraryTest, SendUserActionToUMANotEnabled) { + SetMetricsEnabled(false); + EXPECT_TRUE(lib_.SendUserActionToUMA("SomeOtherKeyPressed")); + EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); +} + class CMetricsLibraryTest : public testing::Test { protected: virtual void SetUp() { @@ -268,6 +285,17 @@ TEST_F(CMetricsLibraryTest, SendToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } +TEST_F(CMetricsLibraryTest, SendUserActionToUMA) { + char buf[100]; + const int kLen = 30; + EXPECT_TRUE(CMetricsLibrarySendUserActionToUMA(lib_, "SomeKeyPressed")); + EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + + char exp[kLen]; + sprintf(exp, "%c%c%c%cuseraction%cSomeKeyPressed", kLen, 0, 0, 0, 0); + EXPECT_EQ(0, memcmp(exp, buf, kLen)); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From ca90d8b40bffb9f265a57673b272a78405408bd0 Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Thu, 6 Jan 2011 15:46:00 -0800 Subject: [PATCH 046/200] metrics: Add SendUserActionToUMA() to MetricsLibraryMock. The metrics daemon doesn't support sending user actions, but I need this exposed through the mock library so I can use SendUserActionToUMA() in the power manager. Change-Id: Ie9e3995df9978768477fe46dcb9ebec4d1fff1d5 BUG=chromium-os:10696 TEST=built for x86-mario with FEATURES=test; checked that the power manager can now use SendUserActionToUMA() Review URL: http://codereview.chromium.org/6130003 --- metrics/metrics_library.h | 1 + metrics/metrics_library_mock.h | 1 + 2 files changed, 2 insertions(+) diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index b9b817a9c..4bbd0d3a1 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -16,6 +16,7 @@ class MetricsLibraryInterface { virtual bool SendToUMA(const std::string& name, int sample, int min, int max, int nbuckets) = 0; virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0; + virtual bool SendUserActionToUMA(const std::string& action) = 0; virtual ~MetricsLibraryInterface() {} }; diff --git a/metrics/metrics_library_mock.h b/metrics/metrics_library_mock.h index ba59c8f15..9a5d59e63 100644 --- a/metrics/metrics_library_mock.h +++ b/metrics/metrics_library_mock.h @@ -18,6 +18,7 @@ class MetricsLibraryMock : public MetricsLibraryInterface { int min, int max, int nbuckets)); MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample, int max)); + MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action)); }; #endif // METRICS_LIBRARY_MOCK_H_ From 6c35d7c1b6fb78b95e9033062089fdd876e7ecf4 Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Fri, 21 Jan 2011 11:25:45 -0800 Subject: [PATCH 047/200] metrics: Update comment about adding user actions. This points people at chrome/tools/extract_actions.py, where new actions can be registered without needing to modify C++ code (once http://codereview.chromium.org/6266011/ is committed). Change-Id: If7ceaa38939ab9c1594aacd999e0ec86c4541d41 BUG=chromium-os:10696 TEST=none Review URL: http://codereview.chromium.org/6320009 --- metrics/metrics_library.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 4bbd0d3a1..ed8386202 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -72,9 +72,12 @@ class MetricsLibrary : public MetricsLibraryInterface { // Sends a user action to Chrome for transport to UMA and returns true on // success. This method results in the equivalent of an asynchronous - // non-blocking RPC to UserMetrics::RecordAction (see the comments in - // chrome/browser/chromeos/external_metrics.cc and - // chrome/browser/metrics/user_metrics.h on how to register new user actions). + // non-blocking RPC to UserMetrics::RecordAction. The new metric must be + // added to chrome/tools/extract_actions.py in the Chromium repository, which + // should then be run to generate a hash for the new action. + // + // Until http://crosbug.com/11125 is fixed, the metric must also be added to + // chrome/browser/chromeos/external_metrics.cc. // // |action| is the user-generated event (e.g., "MuteKeyPressed"). bool SendUserActionToUMA(const std::string& action); From be2e13b32b9d079bed76019a63ff89bfb7a94f35 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Sat, 22 Jan 2011 06:15:56 -0800 Subject: [PATCH 048/200] metrics: Send ability to notify chrome of system crashes Change-Id: I11df903c020141a8123055620f9ad23fedc06c7d BUG=9352 TEST= 1) UserCrash 2) Crash random process and verify Chrome indicates "other user" crashes occurred in its stability UMA data. Review URL: http://codereview.chromium.org/6211001 --- metrics/c_metrics_library.cc | 8 ++++++++ metrics/c_metrics_library.h | 4 ++++ metrics/metrics_library.cc | 14 ++++++++++++++ metrics/metrics_library.h | 4 ++++ metrics/metrics_library_test.cc | 28 ++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+) diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc index 3450918c4..8946699c3 100644 --- a/metrics/c_metrics_library.cc +++ b/metrics/c_metrics_library.cc @@ -51,6 +51,14 @@ extern "C" int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle, return lib->SendUserActionToUMA(std::string(action)); } +extern "C" int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle, + const char* crash_kind) { + MetricsLibrary* lib = reinterpret_cast(handle); + if (lib == NULL) + return 0; + return lib->SendCrashToUMA(crash_kind); +} + extern "C" int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle) { MetricsLibrary* lib = reinterpret_cast(handle); if (lib == NULL) diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h index 9500aade5..5c7003d9e 100644 --- a/metrics/c_metrics_library.h +++ b/metrics/c_metrics_library.h @@ -32,6 +32,10 @@ int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle, const char* action); +// C wrapper for MetricsLibrary::SendCrashToUMA. +int CMetricsLibrarySendCrashToUMA(CMetricsLibrary handle, + const char* crash_kind); + // C wrapper for MetricsLibrary::AreMetricsEnabled. int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle); diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 05e63acff..f2046a35e 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -255,3 +255,17 @@ bool MetricsLibrary::SendUserActionToUMA(const std::string& action) { // Send the message. return SendMessageToChrome(message_length, message); } + +bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { + // Format the message. + char message[kBufferSize]; + int32_t message_length = + FormatChromeMessage(kBufferSize, message, + "crash%c%s", '\0', crash_kind); + + if (message_length < 0) + return false; + + // Send the message. + return SendMessageToChrome(message_length, message); +} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index ed8386202..3f860eb18 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -82,6 +82,10 @@ class MetricsLibrary : public MetricsLibraryInterface { // |action| is the user-generated event (e.g., "MuteKeyPressed"). bool SendUserActionToUMA(const std::string& action); + // Sends a signal to UMA that a crash of the given |crash_kind| + // has occurred. Used by UMA to generate stability statistics. + bool SendCrashToUMA(const char *crash_kind); + // Sends to Autotest and returns true on success. static bool SendToAutotest(const std::string& name, int value); diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 3e49f6925..762be4a79 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -231,6 +231,23 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMANotEnabled) { EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } +TEST_F(MetricsLibraryTest, SendCrashToUMAEnabled) { + EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); + char exp[100]; + int len = sprintf(exp, "%c%c%c%ccrash%ckernel", + 0, 0, 0, 0, 0) + 1; + exp[0] = len; + char buf[100]; + EXPECT_EQ(len, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(0, memcmp(exp, buf, len)); +} + +TEST_F(MetricsLibraryTest, SendCrashToUMANotEnabled) { + SetMetricsEnabled(false); + EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); + EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); +} + class CMetricsLibraryTest : public testing::Test { protected: virtual void SetUp() { @@ -296,6 +313,17 @@ TEST_F(CMetricsLibraryTest, SendUserActionToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } +TEST_F(CMetricsLibraryTest, SendCrashToUMA) { + char buf[100]; + char exp[100]; + int len = sprintf(exp, "%c%c%c%ccrash%cuser", 0, 0, 0, 0, 0) + 1; + exp[0] = len; + EXPECT_TRUE(CMetricsLibrarySendCrashToUMA(lib_, "user")); + EXPECT_EQ(len, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + + EXPECT_EQ(0, memcmp(exp, buf, len)); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From c88e42dea9826857c8c9a966b7669b733ba40c7b Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 17 Feb 2011 10:21:16 -0800 Subject: [PATCH 049/200] Collect some disk statistics. Change-Id: Id30f4b7e5d121f2632592ebacf47a18ea1d89fec BUG=chromium-os:12171 TEST=ran on target and observed that stats are generated Review URL: http://codereview.chromium.org/6486021 --- metrics/metrics_daemon.cc | 125 ++++++++++++++++++++++++++++++++- metrics/metrics_daemon.h | 42 ++++++++++- metrics/metrics_daemon_main.cc | 5 +- metrics/metrics_daemon_test.cc | 51 +++++++++++++- 4 files changed, 216 insertions(+), 7 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index a5059d983..1e15e1dde 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -4,6 +4,7 @@ #include "metrics_daemon.h" +#include #include #include @@ -84,6 +85,28 @@ const char MetricsDaemon::kMetricCrashFrequencyMin = 1; const char MetricsDaemon::kMetricCrashFrequencyMax = 100; const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50; +// disk stats metrics + +// The {Read,Write}Sectors numbers are in sectors/second. +// A sector is usually 512 bytes. + +const char MetricsDaemon::kMetricReadSectorsLongName[] = + "Platform.ReadSectorsLong"; +const char MetricsDaemon::kMetricWriteSectorsLongName[] = + "Platform.WriteSectorsLong"; +const char MetricsDaemon::kMetricReadSectorsShortName[] = + "Platform.ReadSectorsShort"; +const char MetricsDaemon::kMetricWriteSectorsShortName[] = + "Platform.WriteSectorsShort"; + +const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds +const int MetricsDaemon::kMetricDiskStatsLongInterval = 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 +const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets + // persistent metrics path const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; @@ -123,7 +146,8 @@ MetricsDaemon::MetricsDaemon() session_state_(kUnknownSessionState), user_active_(false), usemon_interval_(0), - usemon_source_(NULL) {} + usemon_source_(NULL), + diskstats_path_(NULL) {} MetricsDaemon::~MetricsDaemon() { DeleteFrequencyCounters(); @@ -190,7 +214,8 @@ void MetricsDaemon::ConfigureCrashFrequencyReporter( frequency_counters_[histogram_name] = new_counter.release(); } -void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { +void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, + const char* diskstats_path) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; @@ -218,6 +243,9 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib) { ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); + diskstats_path_ = diskstats_path; + DiskStatsReporterInit(); + // Don't setup D-Bus and GLib in test mode. if (testing) return; @@ -494,6 +522,99 @@ void MetricsDaemon::UnscheduleUseMonitor() { usemon_interval_ = 0; } +void MetricsDaemon::DiskStatsReporterInit() { + DiskStatsReadStats(&read_sectors_, &write_sectors_); + // The first time around just run the long stat, so we don't delay boot. + diskstats_state_ = kDiskStatsLong; + ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval); +} + +void MetricsDaemon::ScheduleDiskStatsCallback(int wait) { + if (testing_) { + return; + } + g_timeout_add_seconds(wait, DiskStatsCallbackStatic, this); +} + +void MetricsDaemon::DiskStatsReadStats(long int* read_sectors, + long int* write_sectors) { + int nchars; + int nitems; + char line[200]; + int file = HANDLE_EINTR(open(diskstats_path_, O_RDONLY)); + if (file < 0) { + PLOG(WARNING) << "cannot open " << diskstats_path_; + return; + } + nchars = HANDLE_EINTR(read(file, line, sizeof(line))); + if (nchars < 0) { + PLOG(WARNING) << "cannot read from " << diskstats_path_; + } else { + LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in " + << diskstats_path_; + line[nchars] = '\0'; + nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld", + read_sectors, write_sectors); + LOG_IF(WARNING, nitems != 2) << "found " << nitems << " items in " + << diskstats_path_ << ", expected 2"; + } + HANDLE_EINTR(close(file)); +} + +// static +gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) { + (static_cast(handle))->DiskStatsCallback(); + return false; // one-time callback +} + +void MetricsDaemon::DiskStatsCallback() { + long int read_sectors_now, write_sectors_now; + DiskStatsReadStats(&read_sectors_now, &write_sectors_now); + + switch (diskstats_state_) { + case kDiskStatsShort: + SendMetric(kMetricReadSectorsShortName, + (int) (read_sectors_now - read_sectors_) / + kMetricDiskStatsShortInterval, + 1, + kMetricSectorsIOMax, + kMetricSectorsBuckets); + SendMetric(kMetricWriteSectorsShortName, + (int) (write_sectors_now - write_sectors_) / + kMetricDiskStatsShortInterval, + 1, + kMetricSectorsIOMax, + kMetricSectorsBuckets); + // Schedule long callback. + diskstats_state_ = kDiskStatsLong; + ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval - + kMetricDiskStatsShortInterval); + break; + case kDiskStatsLong: + SendMetric(kMetricReadSectorsLongName, + (int) (read_sectors_now - read_sectors_) / + kMetricDiskStatsLongInterval, + 1, + kMetricSectorsIOMax, + kMetricSectorsBuckets); + SendMetric(kMetricWriteSectorsLongName, + (int) (write_sectors_now - write_sectors_) / + kMetricDiskStatsLongInterval, + 1, + kMetricSectorsIOMax, + kMetricSectorsBuckets); + // Reset sector counters + read_sectors_ = read_sectors_now; + write_sectors_ = write_sectors_now; + // Schedule short callback. + diskstats_state_ = kDiskStatsShort; + ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval); + break; + default: + LOG(FATAL) << "Invalid disk stats state"; + } +} + // static void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { if (count <= 0) diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 5f2e7868a..dd613227d 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -29,7 +29,8 @@ class MetricsDaemon { ~MetricsDaemon(); // Initializes. - void Init(bool testing, MetricsLibraryInterface* metrics_lib); + void Init(bool testing, MetricsLibraryInterface* metrics_lib, + const char* diskstats_path); // Does all the work. If |run_as_daemon| is true, daemonizes by // forking. @@ -52,6 +53,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash); FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency); FRIEND_TEST(MetricsDaemonTest, ReportDailyUse); + FRIEND_TEST(MetricsDaemonTest, ReportDiskStats); FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval); FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval); FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval); @@ -77,6 +79,12 @@ class MetricsDaemon { kNumberSessionStates }; + // State for disk stats collector callback. + enum DiskStatsState { + kDiskStatsShort, // short wait before short interval collection + kDiskStatsLong, // final wait before new collection + }; + // Data record for aggregating daily usage. class UseRecord { public: @@ -111,6 +119,15 @@ class MetricsDaemon { static const char kMetricUserCrashesDailyName[]; static const char kMetricUserCrashesWeeklyName[]; static const char kMetricUserCrashIntervalName[]; + static const char kMetricReadSectorsLongName[]; + static const char kMetricReadSectorsShortName[]; + static const char kMetricWriteSectorsLongName[]; + static const char kMetricWriteSectorsShortName[]; + static const int kMetricDiskStatsShortInterval; + static const int kMetricDiskStatsLongInterval; + static const int kMetricSectorsIOMax; + static const int kMetricSectorsBuckets; + static const char kMetricsDiskStatsPath[]; // D-Bus message match strings. static const char* kDBusMatches_[]; @@ -217,6 +234,22 @@ class MetricsDaemon { void SendMetric(const std::string& name, int sample, int min, int max, int nbuckets); + // Initializes disk stats reporting. + void DiskStatsReporterInit(); + + // Schedules a callback for the next disk stats collection. + void ScheduleDiskStatsCallback(int wait); + + // Reads cumulative disk statistics from sysfs. + void DiskStatsReadStats(long int* read_sectors, long int* write_sectors); + + // Reports disk statistics (static version for glib). Arguments are a glib + // artifact. + static gboolean DiskStatsCallbackStatic(void* handle); + + // Reports disk statistics. + void DiskStatsCallback(); + // Test mode. bool testing_; @@ -265,6 +298,13 @@ class MetricsDaemon { // Scheduled daily use monitor source (see ScheduleUseMonitor). GSource* usemon_source_; + + // Contains the most recent disk stats. + long int read_sectors_; + long int write_sectors_; + + DiskStatsState diskstats_state_; + const char* diskstats_path_; }; #endif // METRICS_DAEMON_H_ diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index f3f071425..899256cff 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -9,11 +9,14 @@ DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); +// Path to disk stats. This may be system dependent. +const char kMetricsMainDiskStatsPath[] = "/sys/class/block/sda/stat"; + int main(int argc, char** argv) { google::ParseCommandLineFlags(&argc, &argv, true); MetricsLibrary metrics_lib; metrics_lib.Init(); MetricsDaemon daemon; - daemon.Init(false, &metrics_lib); + daemon.Init(false, &metrics_lib, kMetricsMainDiskStatsPath); daemon.Run(FLAGS_daemon); } diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 530f357b6..208d8e5bf 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include "counter_mock.h" @@ -32,6 +33,14 @@ static const int kSecondsPerDay = 24 * 60 * 60; static const char kTestDir[] = "test"; static const char kLastFile[] = "test/last"; static const char kCurrentFile[] = "test/current"; +static const char kFakeDiskStatsPath[] = "fake-disk-stats"; +static const char kFakeDiskStatsFormat[] = + " 1793 1788 %d 105580 " + " 196 175 %d 30290 " + " 0 44060 135850\n"; +static string kFakeDiskStats[2]; +static const int kFakeReadSectors[] = {80000, 100000}; +static const int kFakeWriteSectors[] = {3000, 4000}; // This class allows a TimeTicks object to be initialized with seconds // (rather than microseconds) through the protected TimeTicks(int64) @@ -54,7 +63,12 @@ class MetricsDaemonTest : public testing::Test { EXPECT_EQ(NULL, daemon_.daily_use_.get()); EXPECT_EQ(NULL, daemon_.kernel_crash_interval_.get()); EXPECT_EQ(NULL, daemon_.user_crash_interval_.get()); - daemon_.Init(true, &metrics_lib_); + kFakeDiskStats[0] = StringPrintf(kFakeDiskStatsFormat, + kFakeReadSectors[0], kFakeWriteSectors[0]); + kFakeDiskStats[1] = StringPrintf(kFakeDiskStatsFormat, + kFakeReadSectors[1], kFakeWriteSectors[1]); + CreateFakeDiskStatsFile(kFakeDiskStats[0].c_str()); + daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath); // Check configuration of a few histograms. FrequencyCounter* frequency_counter = @@ -120,7 +134,9 @@ class MetricsDaemonTest : public testing::Test { file_util::CreateDirectory(FilePath(kTestDir)); } - virtual void TearDown() {} + virtual void TearDown() { + EXPECT_EQ(unlink(kFakeDiskStatsPath), 0); + } const TaggedCounterReporter* GetReporter(FrequencyCounter* frequency_counter) const { @@ -222,12 +238,22 @@ class MetricsDaemonTest : public testing::Test { dbus_message_unref(msg); } - // Get the frequency counter for the given name. + // Gets the frequency counter for the given name. FrequencyCounterMock& GetFrequencyMock(const char* histogram_name) { return *static_cast( daemon_.frequency_counters_[histogram_name]); } + // Creates or overwrites an input file containing fake disk stats. + void CreateFakeDiskStatsFile(const char* fake_stats) { + if (unlink(kFakeDiskStatsPath) < 0) { + EXPECT_EQ(errno, ENOENT); + } + FILE* f = fopen(kFakeDiskStatsPath, "w"); + EXPECT_EQ(1, fwrite(fake_stats, strlen(fake_stats), 1, f)); + EXPECT_EQ(0, fclose(f)); + } + // The MetricsDaemon under test. MetricsDaemon daemon_; @@ -533,6 +559,25 @@ TEST_F(MetricsDaemonTest, GetHistogramPath) { MetricsDaemon::kMetricAnyCrashesDailyName).value()); } +TEST_F(MetricsDaemonTest, ReportDiskStats) { + long int read_sectors_now, write_sectors_now; + + CreateFakeDiskStatsFile(kFakeDiskStats[1].c_str()); + daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now); + EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]); + EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]); + + MetricsDaemon::DiskStatsState ds_state = daemon_.diskstats_state_; + EXPECT_CALL(metrics_lib_, + SendToUMA(_, (kFakeReadSectors[1] - kFakeReadSectors[0]) / 30, + _, _, _)); + EXPECT_CALL(metrics_lib_, + SendToUMA(_, (kFakeWriteSectors[1] - kFakeWriteSectors[0]) / 30, + _, _, _)); + daemon_.DiskStatsCallback(); + EXPECT_TRUE(ds_state != daemon_.diskstats_state_); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 8842c8c42d194d0c69e35280b24bafa890325514 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Thu, 24 Feb 2011 12:48:30 -0800 Subject: [PATCH 050/200] libmetrics -- release the shared file lock when closing, handle EINTR. BUG=chromium-os:11125 TEST=unit tests, tested on device Change-Id: I126af62f77e4fd0f098d441038f8dc94c0020ac2 Review URL: http://codereview.chromium.org/6576048 --- metrics/metrics_library.cc | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index f2046a35e..3bc800716 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -12,6 +12,8 @@ #include #include +#include + #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) @@ -130,9 +132,9 @@ bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { if (!AreMetricsEnabled()) return true; - int chrome_fd = open(uma_events_file_, - O_WRONLY | O_APPEND | O_CREAT, - READ_WRITE_ALL_FILE_FLAGS); + int chrome_fd = HANDLE_EINTR(open(uma_events_file_, + O_WRONLY | O_APPEND | O_CREAT, + READ_WRITE_ALL_FILE_FLAGS)); // If we failed to open it, return. if (chrome_fd < 0) { PrintError("open", uma_events_file_, errno); @@ -146,24 +148,20 @@ bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { // Grab an exclusive lock to protect Chrome from truncating // underneath us. Keep the file locked as briefly as possible. - if (flock(chrome_fd, LOCK_EX) < 0) { + if (HANDLE_EINTR(flock(chrome_fd, LOCK_EX)) < 0) { PrintError("flock", uma_events_file_, errno); - close(chrome_fd); + HANDLE_EINTR(close(chrome_fd)); return false; } bool success = true; - if (write(chrome_fd, message, length) != length) { + if (HANDLE_EINTR(write(chrome_fd, message, length)) != length) { PrintError("write", uma_events_file_, errno); success = false; } - // Release the file lock and close file. - if (flock(chrome_fd, LOCK_UN) < 0) { - PrintError("unlock", uma_events_file_, errno); - success = false; - } - close(chrome_fd); + // Close the file and release the lock. + HANDLE_EINTR(close(chrome_fd)); return success; } From 8d3305eb9200f7e2f2b0317b0a67babe81f989ad Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Fri, 25 Feb 2011 14:19:30 -0800 Subject: [PATCH 051/200] libmetrics: Support partial writes. BUG=chromium-os:11125 TEST=unit tests, tested on device Change-Id: If9066988b86f61cb5bae413b7250d5426854f31b Review URL: http://codereview.chromium.org/6592019 --- metrics/metrics_library.cc | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 3bc800716..8c98696c1 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -12,7 +12,7 @@ #include #include -#include +#include "base/eintr_wrapper.h" // HANDLE_EINTR macro, no libbase required. #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) @@ -42,6 +42,22 @@ static void PrintError(const char* message, const char* file, } } +// Copied from libbase to avoid pulling in all of libbase just for libmetrics. +static int WriteFileDescriptor(const int fd, const char* data, int size) { + // Allow for partial writes. + ssize_t bytes_written_total = 0; + for (ssize_t bytes_written_partial = 0; bytes_written_total < size; + bytes_written_total += bytes_written_partial) { + bytes_written_partial = + HANDLE_EINTR(write(fd, data + bytes_written_total, + size - bytes_written_total)); + if (bytes_written_partial < 0) + return -1; + } + + return bytes_written_total; +} + MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), consent_file_(kConsentFile) {} @@ -155,7 +171,7 @@ bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { } bool success = true; - if (HANDLE_EINTR(write(chrome_fd, message, length)) != length) { + if (WriteFileDescriptor(chrome_fd, message, length) != length) { PrintError("write", uma_events_file_, errno); success = false; } From 0f132bba6fbf1b8ce5a3e0927f39ba41ec4aebd1 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 28 Feb 2011 11:17:43 -0800 Subject: [PATCH 052/200] Find device-dependent disk stats file, and skip disk stats if not available. Change-Id: I03afb85e3357dd4c2cf5effd98b194c71d77c71d BUG=12171 TEST=unit tested Review URL: http://codereview.chromium.org/6541007 --- metrics/Makefile | 3 ++- metrics/metrics_daemon.cc | 14 ++++++++------ metrics/metrics_daemon.h | 4 ++-- metrics/metrics_daemon_main.cc | 30 +++++++++++++++++++++++++++--- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 5e3ed14bb..032ea73bf 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -38,7 +38,8 @@ TESTLIB_OBJS = \ metrics_library_test.o TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 -DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags -lglib-2.0 +DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags \ + -lglib-2.0 -lrootdev TESTDAEMON_LIBS = -lgmock -lgtest TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread -lglib-2.0 diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 1e15e1dde..4820590d1 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -146,8 +146,7 @@ MetricsDaemon::MetricsDaemon() session_state_(kUnknownSessionState), user_active_(false), usemon_interval_(0), - usemon_source_(NULL), - diskstats_path_(NULL) {} + usemon_source_(NULL) {} MetricsDaemon::~MetricsDaemon() { DeleteFrequencyCounters(); @@ -215,7 +214,7 @@ void MetricsDaemon::ConfigureCrashFrequencyReporter( } void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, - const char* diskstats_path) { + string diskstats_path) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; @@ -243,8 +242,11 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); - diskstats_path_ = diskstats_path; - DiskStatsReporterInit(); + // Don't attempt to collect disk stats if there is no disk stats file. + if (!diskstats_path.empty()) { + diskstats_path_ = diskstats_path; + DiskStatsReporterInit(); + } // Don't setup D-Bus and GLib in test mode. if (testing) @@ -541,7 +543,7 @@ void MetricsDaemon::DiskStatsReadStats(long int* read_sectors, int nchars; int nitems; char line[200]; - int file = HANDLE_EINTR(open(diskstats_path_, O_RDONLY)); + int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY)); if (file < 0) { PLOG(WARNING) << "cannot open " << diskstats_path_; return; diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index dd613227d..525251829 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -30,7 +30,7 @@ class MetricsDaemon { // Initializes. void Init(bool testing, MetricsLibraryInterface* metrics_lib, - const char* diskstats_path); + std::string diskstats_path); // Does all the work. If |run_as_daemon| is true, daemonizes by // forking. @@ -304,7 +304,7 @@ class MetricsDaemon { long int write_sectors_; DiskStatsState diskstats_state_; - const char* diskstats_path_; + std::string diskstats_path_; }; #endif // METRICS_DAEMON_H_ diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 899256cff..1ee061177 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -3,20 +3,44 @@ // found in the LICENSE file. +#include +#include #include +#include #include "metrics_daemon.h" DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); -// Path to disk stats. This may be system dependent. -const char kMetricsMainDiskStatsPath[] = "/sys/class/block/sda/stat"; +// Return the path to the disk stats in the sysfs. +static +const std::string MetricsMainDiskStatsPath() { + char dev_path_cstr[PATH_MAX]; + std::string dev_prefix = "/dev/"; + std::string dev_path; + std::string dev_name; + + int ret = rootdev(dev_path_cstr, sizeof(dev_path_cstr), true, true); + if (ret != 0) { + LOG(WARNING) << "error " << ret << " determining root device"; + return ""; + } + dev_path = dev_path_cstr; + // Check that rootdev begins with "/dev/". + if (!StartsWithASCII(dev_path, dev_prefix, false)) { + LOG(WARNING) << "unexpected root device " << dev_path; + return ""; + } + // Get the device name, e.g. "sda" from "/dev/sda". + dev_name = dev_path.substr(dev_prefix.length()); + return "/sys/class/block/" + dev_name + "/stat"; +} int main(int argc, char** argv) { google::ParseCommandLineFlags(&argc, &argv, true); MetricsLibrary metrics_lib; metrics_lib.Init(); MetricsDaemon daemon; - daemon.Init(false, &metrics_lib, kMetricsMainDiskStatsPath); + daemon.Init(false, &metrics_lib, MetricsMainDiskStatsPath()); daemon.Run(FLAGS_daemon); } From 29c7ef9e6a9ef322079f3f0eb1444aa12909ebbf Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 12 Apr 2011 14:12:35 -0700 Subject: [PATCH 053/200] 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 --- metrics/metrics_daemon.cc | 134 +++++++++++++++++++++++++++++++++ metrics/metrics_daemon.h | 23 ++++++ metrics/metrics_daemon_test.cc | 61 +++++++++++++++ 3 files changed, 218 insertions(+) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 4820590d1..273fa60a6 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -9,6 +9,7 @@ #include #include +#include #include #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(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 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 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(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); +} diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 525251829..e0614452b 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -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_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 208d8e5bf..476bf1cca 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -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(); From 8032dd0c6f5342d0823d9dbec3b95927b9cec0f1 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Mon, 9 May 2011 16:33:19 -0700 Subject: [PATCH 054/200] metrics: Fixed compile-time warnings due to unused variables. BUG=chromium-os:15040 TEST=built with gcc-4.6 Change-Id: Idf95c986e1eda79be735eeb4ff7c79e9a7eede60 Reviewed-on: http://gerrit.chromium.org/gerrit/569 Tested-by: Darin Petkov Reviewed-by: Chris Sosa --- metrics/metrics_client.cc | 5 ++--- metrics/metrics_daemon.cc | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index 5aedd8b26..86bec731f 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -92,7 +92,6 @@ int main(int argc, char** argv) { bool send_to_chrome = true; bool send_enum = false; bool secs_to_msecs = false; - bool print_usage = false; // Parse arguments int flag; @@ -124,7 +123,7 @@ int main(int argc, char** argv) { mode = kModeSendUserAction; break; default: - print_usage = true; + ShowUsage(); break; } } diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 273fa60a6..e9031ab7a 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -296,7 +296,6 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, DBusMessage* message, void* user_data) { Time now = Time::Now(); - TimeTicks ticks = TimeTicks::Now(); DLOG(INFO) << "message intercepted @ " << now.ToInternalValue(); int message_type = dbus_message_get_type(message); From 817016a81f9c6dd8d3f96619f50ff243bd2a0c55 Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Thu, 12 May 2011 14:14:48 -0700 Subject: [PATCH 055/200] [metrics] Roll forward to new libchrome BUG=chromium-os:14304 TEST=build, unit tests Change-Id: I92fd96b2c14df847efe47735c0f76d99023ec254 Reviewed-on: http://gerrit.chromium.org/gerrit/819 Reviewed-by: Darin Petkov Tested-by: Chris Masone --- metrics/counter.h | 2 +- metrics/counter_test.cc | 6 +++++- metrics/metrics_daemon.h | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/metrics/counter.h b/metrics/counter.h index e55cdedac..a360e1f7b 100644 --- a/metrics/counter.h +++ b/metrics/counter.h @@ -9,7 +9,7 @@ #include #include -#include +#include #include // for FRIEND_TEST class MetricsLibraryInterface; diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc index 358051668..71fefd6c5 100644 --- a/metrics/counter_test.cc +++ b/metrics/counter_test.cc @@ -123,7 +123,11 @@ class TaggedCounterTest : public testing::Test { // Collects log messages in the |log_| member string so that they // can be analyzed for errors and expected behavior. - static bool HandleLogMessages(int severity, const std::string& str) { + static bool HandleLogMessages(int severity, + const char* file, + int line, + size_t message_start, + const std::string& str) { test_->log_.append(str); test_->log_.append("\n"); diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index e0614452b..2bbdb4fd0 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include // for FRIEND_TEST From b7780ce4a49344ed62b90fdc6717e97a98d28698 Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Fri, 20 May 2011 15:49:59 -0700 Subject: [PATCH 056/200] Remove obsolete hardware_class script. BUG=chromium-os:15257 TEST=emerge-x86-mario metrics Change-Id: I496121c47394dc81b7f414655940dddc8d5cc992 Reviewed-on: http://gerrit.chromium.org/gerrit/1314 Tested-by: Darin Petkov Reviewed-by: Chris Sosa --- metrics/hardware_class | 76 ------------------------------------------ 1 file changed, 76 deletions(-) delete mode 100755 metrics/hardware_class diff --git a/metrics/hardware_class b/metrics/hardware_class deleted file mode 100755 index 913607666..000000000 --- a/metrics/hardware_class +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/sh - -# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# This script prints the hardware class (e.g., the hardware -# qualification ID) of this device, or "unknown" if it can't determine -# the hardware class. - -# TODO(petkov): If the hardware qualification ID is not available on -# the systems, the script uses alternative ways to identify different -# system classes (e.g., the WiFi adapter PCI vendor and device -# IDs). Switch the script to use only real hardware qualification ID -# when that becomes available on all systems. - -HARDWARE_CLASS= -readonly HWID_PATH=/sys/bus/platform/devices/chromeos_acpi/HWID - -# Appends a new component ID to the hardware class. Separates IDs with -# dashes. -append_class() { - [ -n "$1" ] || return - [ -n "$HARDWARE_CLASS" ] && HARDWARE_CLASS="${HARDWARE_CLASS}-" - HARDWARE_CLASS="${HARDWARE_CLASS}$1" -} - -hwid() { - [ -r "$HWID_PATH" ] || return - local acpihwid - acpihwid=$(cat "$HWID_PATH") - [ -n "$acpihwid" ] || return - append_class "$acpihwid" -} - -# Adds the CPU family, model and stepping info, if available, to the -# class. -cpu() { - [ -r /proc/cpuinfo ] || return - local family - family=$(grep -m1 '^cpu family' /proc/cpuinfo \ - | sed 's/cpu family\s\+:\s\+\([0-9]\+\)$/\1/') - local model - model=$(grep -m1 '^model' /proc/cpuinfo \ - | sed 's/model\s\+:\s\+\([0-9]\+\)$/\1/') - local stepping - stepping=$(grep -m1 '^stepping' /proc/cpuinfo \ - | sed 's/stepping\s\+:\s\+\([0-9]\+\)$/\1/') - if [ -n "$family" ] && [ -n "$model" ] && [ -n "$stepping" ]; then - append_class "cpu/$family:$model:$stepping" - fi -} - -# Adds the wlan0 PCI vendor and device ID, if available, to the class. -wlan() { - local dev=/sys/class/net/wlan0/device - if [ -r $dev/vendor ] && [ -r $dev/device ]; then - local id - id=$(paste -d ':' $dev/vendor $dev/device | sed s/0x//g) - append_class "wlan0/$id" - fi -} - -main() { - hwid - # If the HWID is not available, use system component IDs. - if [ -z "$HARDWARE_CLASS" ]; then - cpu - wlan - [ -z "$HARDWARE_CLASS" ] && HARDWARE_CLASS=unknown - fi - - echo $HARDWARE_CLASS -} - -main $@ From 8accd33d920d99bd1521ae6937952bdece4301a3 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 17 May 2011 16:37:18 -0700 Subject: [PATCH 057/200] Add memuse stats and fix disk I/O computations. This changes adds memory usage stats after 1 5, 30, 150, and 750 minutes of active use (approximated by wall clock time minus sleep time). We log only the anonymous memory usage (active + inactive) as we expect that other types of allocations would not convey much additional information. This also fixes the disk I/O computation to use actual active time elapsed between callbacks, instead of assuming the expected interval has elapsed. The existing code causes errors in both directions when a suspend/resume cycle occurs between callbacks. BUG=14209 TEST=verify that about:histograms page contains Platform.MemuseAnon1 after 1 minute and Platform.MemuseAnon2 after 5 minutes. Change-Id: Ib32d915fac7766a9fca7125105224889ea93050e Reviewed-on: http://gerrit.chromium.org/gerrit/1132 Reviewed-by: Darin Petkov Tested-by: Luigi Semenzato --- metrics/metrics_daemon.cc | 269 ++++++++++++++++++++++++--------- metrics/metrics_daemon.h | 53 ++++++- metrics/metrics_daemon_test.cc | 20 ++- 3 files changed, 257 insertions(+), 85 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index e9031ab7a..7ce28580a 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -5,7 +5,9 @@ #include "metrics_daemon.h" #include +#include #include +#include #include #include @@ -17,7 +19,10 @@ using base::Time; using base::TimeDelta; using base::TimeTicks; +using std::map; using std::string; +using std::vector; + #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") #define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter" @@ -144,17 +149,46 @@ const char* MetricsDaemon::kSessionStates_[] = { #include "session_states.h" }; +// Memory use stats collection intervals. We collect some memory use interval +// at these intervals after boot, and we stop collecting after the last one, +// with the assumption that in most cases the memory use won't change much +// after that. +static const int kMemuseIntervals[] = { + 1 * kSecondsPerMinute, // 1 minute mark + 4 * kSecondsPerMinute, // 5 minute mark + 25 * kSecondsPerMinute, // 0.5 hour mark + 120 * kSecondsPerMinute, // 2.5 hour mark + 600 * kSecondsPerMinute, // 12.5 hour mark +}; + MetricsDaemon::MetricsDaemon() : power_state_(kUnknownPowerState), session_state_(kUnknownSessionState), user_active_(false), usemon_interval_(0), - usemon_source_(NULL) {} + usemon_source_(NULL), + memuse_initial_time_(0), + memuse_interval_index_(0), + read_sectors_(0), + write_sectors_(0), + diskstats_state_(kDiskStatsShort), + diskstats_initial_time_(0) {} MetricsDaemon::~MetricsDaemon() { DeleteFrequencyCounters(); } +double MetricsDaemon::GetActiveTime() { + struct timespec ts; + int r = clock_gettime(CLOCK_MONOTONIC, &ts); + if (r < 0) { + PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed"; + return 0; + } else { + return ts.tv_sec + ((double) ts.tv_nsec) / (1000 * 1000 * 1000); + } +} + void MetricsDaemon::DeleteFrequencyCounters() { for (FrequencyCounters::iterator i = frequency_counters_.begin(); i != frequency_counters_.end(); ++i) { @@ -217,7 +251,7 @@ void MetricsDaemon::ConfigureCrashFrequencyReporter( } void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, - string diskstats_path) { + const string& diskstats_path) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; @@ -253,6 +287,7 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, // Start collecting meminfo stats. ScheduleMeminfoCallback(kMetricMeminfoInterval); + ScheduleMemuseCallback(true, 0); // Don't setup D-Bus and GLib in test mode. if (testing) @@ -457,7 +492,7 @@ void MetricsDaemon::ProcessUncleanShutdown() { frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1); } -bool MetricsDaemon::CheckSystemCrash(const std::string& crash_file) { +bool MetricsDaemon::CheckSystemCrash(const string& crash_file) { FilePath crash_detected(crash_file); if (!file_util::PathExists(crash_detected)) return false; @@ -533,7 +568,12 @@ void MetricsDaemon::DiskStatsReporterInit() { DiskStatsReadStats(&read_sectors_, &write_sectors_); // The first time around just run the long stat, so we don't delay boot. diskstats_state_ = kDiskStatsLong; - ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval); + diskstats_initial_time_ = GetActiveTime(); + if (diskstats_initial_time_ < 0) { + LOG(WARNING) << "not collecting disk stats"; + } else { + ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval); + } } void MetricsDaemon::ScheduleDiskStatsCallback(int wait) { @@ -574,21 +614,32 @@ gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) { return false; // one-time callback } +// Collects disk stats alternating over a short and a long interval. + void MetricsDaemon::DiskStatsCallback() { long int read_sectors_now, write_sectors_now; + double time_now = GetActiveTime(); + double delta_time = time_now - diskstats_initial_time_; + if (testing_) { + // Fake the time when testing. + delta_time = diskstats_state_ == kDiskStatsShort ? + kMetricDiskStatsShortInterval : kMetricDiskStatsLongInterval; + } DiskStatsReadStats(&read_sectors_now, &write_sectors_now); + int delta_read = read_sectors_now - read_sectors_; + int delta_write = write_sectors_now - write_sectors_; + int read_sectors_per_second = delta_read / delta_time; + int write_sectors_per_second = delta_write / delta_time; switch (diskstats_state_) { case kDiskStatsShort: SendMetric(kMetricReadSectorsShortName, - (int) (read_sectors_now - read_sectors_) / - kMetricDiskStatsShortInterval, + read_sectors_per_second, 1, kMetricSectorsIOMax, kMetricSectorsBuckets); SendMetric(kMetricWriteSectorsShortName, - (int) (write_sectors_now - write_sectors_) / - kMetricDiskStatsShortInterval, + write_sectors_per_second, 1, kMetricSectorsIOMax, kMetricSectorsBuckets); @@ -599,20 +650,20 @@ void MetricsDaemon::DiskStatsCallback() { break; case kDiskStatsLong: SendMetric(kMetricReadSectorsLongName, - (int) (read_sectors_now - read_sectors_) / - kMetricDiskStatsLongInterval, + read_sectors_per_second, 1, kMetricSectorsIOMax, kMetricSectorsBuckets); SendMetric(kMetricWriteSectorsLongName, - (int) (write_sectors_now - write_sectors_) / - kMetricDiskStatsLongInterval, + write_sectors_per_second, 1, kMetricSectorsIOMax, kMetricSectorsBuckets); - // Reset sector counters + // Reset sector counters. read_sectors_ = read_sectors_now; write_sectors_ = write_sectors_now; + // Set start time for new cycle. + diskstats_initial_time_ = time_now; // Schedule short callback. diskstats_state_ = kDiskStatsShort; ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval); @@ -635,24 +686,17 @@ gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) { } gboolean MetricsDaemon::MeminfoCallback() { - std::string meminfo; + string meminfo_raw; const FilePath meminfo_path("/proc/meminfo"); - if (!file_util::ReadFileToString(meminfo_path, &meminfo)) { + if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) { LOG(WARNING) << "cannot read " << meminfo_path.value().c_str(); return false; } - return ProcessMeminfo(meminfo); + return ProcessMeminfo(meminfo_raw); } -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[] = { +gboolean MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { + static const MeminfoRecord fields_array[] = { { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory { "MemFree", "MemFree" }, { "Buffers", "Buffers" }, @@ -677,67 +721,144 @@ gboolean MetricsDaemon::ProcessMeminfo(std::string meminfo) { // { "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 lines; - int nlines = Tokenize(meminfo, "\n", &lines); + vector fields(fields_array, + fields_array + arraysize(fields_array)); + if (!FillMeminfo(meminfo_raw, &fields)) { + return false; + } + int total_memory = fields[0].value; + if (total_memory == 0) { + // this "cannot happen" + LOG(WARNING) << "borked meminfo parser"; + return false; + } + // Send all fields retrieved, except total memory. + for (unsigned int i = 1; i < fields.size(); i++) { + string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name); + if (fields[i].log_scale) { + // report value in kbytes, log scale, 4Gb max + SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100); + } else { + // report value as percent of total memory + int percent = fields[i].value * 100 / total_memory; + SendLinearMetric(metrics_name, percent, 100, 101); + } + } + return true; +} + +gboolean MetricsDaemon::FillMeminfo(const string& meminfo_raw, + vector* fields) { + vector lines; + unsigned int nlines = Tokenize(meminfo_raw, "\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 tokens; + unsigned int ifield = 0; + for (unsigned int iline = 0; + iline < nlines && ifield < fields->size(); + iline++) { + vector 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]; + if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) { + // Name matches. Parse value and save. char* rest; - meminfo_value = static_cast(strtol(tokens[1].c_str(), &rest, 10)); + (*fields)[ifield].value = + static_cast(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++; + ifield++; } - iline++; } + if (ifield < fields->size()) { + // End of input reached while scanning. + LOG(WARNING) << "cannot find field " << (*fields)[ifield].match + << " and following"; + return false; + } + return true; +} + +void MetricsDaemon::ScheduleMemuseCallback(gboolean new_callback, + double time_elapsed) { + if (testing_) { + return; + } + int interval = kMemuseIntervals[memuse_interval_index_]; + int wait; + if (new_callback) { + memuse_initial_time_ = GetActiveTime(); + wait = interval; + } else { + wait = ceil(interval - time_elapsed); // round up + } + g_timeout_add_seconds(wait, MemuseCallbackStatic, this); +} + +// static +gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) { + MetricsDaemon* daemon = static_cast(handle); + daemon->MemuseCallback(); + return false; +} + +void MetricsDaemon::MemuseCallback() { + // Since we only care about active time (i.e. uptime minus sleep time) but + // the callbacks are driven by real time (uptime), we check if we should + // reschedule this callback due to intervening sleep periods. + double now = GetActiveTime(); + double active_time = now - memuse_initial_time_; + if (active_time < kMemuseIntervals[memuse_interval_index_]) { + // Not enough active time has passed. Reschedule the callback. + ScheduleMemuseCallback(false, active_time); + } else { + // Enough active time has passed. Do the work, and (if we succeed) see if + // we need to do more. + if (MemuseCallbackWork() && + memuse_interval_index_ < arraysize(kMemuseIntervals)) { + memuse_interval_index_++; + ScheduleMemuseCallback(true, 0); + } + } +} + +gboolean MetricsDaemon::MemuseCallbackWork() { + string meminfo_raw; + const FilePath meminfo_path("/proc/meminfo"); + if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) { + LOG(WARNING) << "cannot read " << meminfo_path.value().c_str(); + return false; + } + return ProcessMemuse(meminfo_raw); +} + +gboolean MetricsDaemon::ProcessMemuse(const string& meminfo_raw) { + static const MeminfoRecord fields_array[] = { + { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory + { "ActiveAnon", "Active(anon)" }, + { "InactiveAnon", "Inactive(anon)" }, + }; + vector fields(fields_array, + fields_array + arraysize(fields_array)); + if (!FillMeminfo(meminfo_raw, &fields)) { + return false; + } + int total = fields[0].value; + int active_anon = fields[1].value; + int inactive_anon = fields[2].value; + if (total == 0) { + // this "cannot happen" + LOG(WARNING) << "borked meminfo parser"; + return false; + } + string metrics_name = StringPrintf("Platform.MemuseAnon%d", + memuse_interval_index_); + SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total, + 100, 101); + return true; } // static diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 2bbdb4fd0..68646bcb2 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -30,7 +30,7 @@ class MetricsDaemon { // Initializes. void Init(bool testing, MetricsLibraryInterface* metrics_lib, - std::string diskstats_path); + const std::string& diskstats_path); // Does all the work. If |run_as_daemon| is true, daemonizes by // forking. @@ -95,6 +95,14 @@ class MetricsDaemon { int seconds_; }; + // Record for retrieving and reporting values from /proc/meminfo. + struct MeminfoRecord { + 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 + int value; // value from /proc/meminfo + }; + typedef std::map FrequencyCounters; @@ -141,6 +149,9 @@ class MetricsDaemon { // Array of user session states. static const char* kSessionStates_[kNumberSessionStates]; + // Returns the active time since boot (uptime minus sleep time) in seconds. + double GetActiveTime(); + // Clears and deletes the data contained in frequency_counters_. void DeleteFrequencyCounters(); @@ -270,8 +281,37 @@ class MetricsDaemon { gboolean MeminfoCallback(); // Parses content of /proc/meminfo and sends fields of interest to UMA. - // Returns false on errors. - gboolean ProcessMeminfo(std::string meminfo); + // Returns false on errors. |meminfo_raw| contains the content of + // /proc/meminfo. + gboolean ProcessMeminfo(const std::string& meminfo_raw); + + // Parses meminfo data from |meminfo_raw|. |fields| is a vector containing + // the fields of interest. The order of the fields must be the same in which + // /proc/meminfo prints them. The result of parsing fields[i] is placed in + // fields[i].value. + gboolean FillMeminfo(const std::string& meminfo_raw, + std::vector* fields); + + // Schedule a memory use callback. |new_callback| is true when this callback + // is scheduled for the first time. When |new_callback| is false, + // |time_elapsed| is the active (non-sleep) time that has passed between now + // and the original callback scheduling time. We use it to reschedule a + // callback that fired too early because we slept. + void ScheduleMemuseCallback(gboolean new_callback, double time_elapsed); + + // Static wrapper for MemuseCallback. Always returns false. + static gboolean MemuseCallbackStatic(void* handle); + + // Calls MemuseCallbackWork, and possibly schedules next callback, if enough + // active time has passed. Otherwise reschedules itself to simulate active + // time callbacks (i.e. wall clock time minus sleep time). + void MemuseCallback(); + + // Reads /proc/meminfo and sends total anonymous memory usage to UMA. + gboolean MemuseCallbackWork(); + + // Parse meminfo data and send to UMA. + gboolean ProcessMemuse(const std::string& meminfo_raw); // Test mode. bool testing_; @@ -322,12 +362,19 @@ class MetricsDaemon { // Scheduled daily use monitor source (see ScheduleUseMonitor). GSource* usemon_source_; + // Time of initial scheduling of memuse callback + double memuse_initial_time_; + + // Selects the wait time for the next memory use callback. + unsigned int memuse_interval_index_; + // Contains the most recent disk stats. long int read_sectors_; long int write_sectors_; DiskStatsState diskstats_state_; std::string diskstats_path_; + double diskstats_initial_time_; }; #endif // METRICS_DAEMON_H_ diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 476bf1cca..df95a5d31 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -580,10 +580,10 @@ TEST_F(MetricsDaemonTest, ReportDiskStats) { } TEST_F(MetricsDaemonTest, ProcessMeminfo) { - const char* meminfo = "\ + string meminfo = "\ MemTotal: 2000000 kB\n\ -MemFree: 1000000 kB\n\ -Buffers: 10492 kB\n\ +MemFree: 500000 kB\n\ +Buffers: 1000000 kB\n\ Cached: 213652 kB\n\ SwapCached: 0 kB\n\ Active: 133400 kB\n\ @@ -617,25 +617,29 @@ VmallocChunk: 103824 kB\n\ DirectMap4k: 9636 kB\n\ DirectMap2M: 1955840 kB\n\ "; + // All enum calls must report percents. EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, 100)) .Times(AtLeast(1)); + // Check that MemFree is correctly computed at 25%. + EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMemFree", 25, 100)) + .Times(AtLeast(1)); + // Check that we call SendToUma at least once (log histogram). EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)) .Times(AtLeast(1)); - EXPECT_CALL(metrics_lib_, SendToUMA("NFS_Unstable", _, _, _, _)) + // Make sure we don't report fields not in the list. + EXPECT_CALL(metrics_lib_, SendToUMA("Platform.MeminfoMlocked", _, _, _, _)) .Times(0); - EXPECT_CALL(metrics_lib_, SendEnumToUMA("NFS_Unstable", _, _)) + EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMlocked", _, _)) .Times(0); EXPECT_TRUE(daemon_.ProcessMeminfo(meminfo)); } TEST_F(MetricsDaemonTest, ProcessMeminfo2) { - const char* meminfo = "\ + string 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)); } From 4fdcf19a629798b95b26f2f971438004dc29744c Mon Sep 17 00:00:00 2001 From: Arkaitz Ruiz Alvarez Date: Wed, 25 May 2011 14:41:11 -0700 Subject: [PATCH 058/200] Determine guest mode status for metrics collection Modification to the function IsGuestMode. It returns true only if the guestfs partition is mounted and the file /var/run/state/logged-in exits. Previously it did not check for this file existence. BUG=chromium-os:15763 TEST=Log in in to guest mode right after installing recovery image and check the absence of /home/chronos/prev_stats. Log out and log in as a user and confirm the existence of this directory. Change-Id: I29c66642ba590e882e6ec7a02732e818c87ac2ad Reviewed-on: http://gerrit.chromium.org/gerrit/1586 Reviewed-by: Darin Petkov Tested-by: Arkaitz Ruiz Alvarez --- metrics/metrics_library.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 8c98696c1..0f2a7d900 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -126,7 +126,7 @@ bool MetricsLibrary::IsGuestMode() { &result)) { return false; } - return result; + return result && access("/var/run/state/logged-in", F_OK) == 0; } bool MetricsLibrary::AreMetricsEnabled() { From 9f1a774c671a03c1849c9bf1b7c31f9fe7995f73 Mon Sep 17 00:00:00 2001 From: Arkaitz Ruiz Alvarez Date: Thu, 26 May 2011 12:22:22 -0700 Subject: [PATCH 059/200] Determine guest mode status for metrics collection Modification to the function IsGuestMode. It returns true only if the guestfs partition is mounted and the file /var/run/state/logged-in exits. Previously it did not check for this file existence. BUG=chromium-os:15763 TEST=Log in in to guest mode right after installing recovery image and check the absence of /home/chronos/prev_stats. Log out and log in as a user and confirm the existence of this directory. Change-Id: I7b0e7e8844332cca3fa67611b90fada3c5ba0eeb Reviewed-on: http://gerrit.chromium.org/gerrit/1647 Tested-by: Arkaitz Ruiz Alvarez Reviewed-by: Darin Petkov --- metrics/metrics_library.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 0f2a7d900..d2c1b0fd1 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -126,7 +126,7 @@ bool MetricsLibrary::IsGuestMode() { &result)) { return false; } - return result && access("/var/run/state/logged-in", F_OK) == 0; + return result && (access("/var/run/state/logged-in", F_OK) == 0); } bool MetricsLibrary::AreMetricsEnabled() { From a2ce30fdde1c3ffa0ef6171139898b80c09b361f Mon Sep 17 00:00:00 2001 From: Julian Pastarmov Date: Mon, 18 Jul 2011 17:41:17 +0200 Subject: [PATCH 060/200] Make the metrics library respect the policy settings instead of the consent file. BUG=chromium-os:17012 TEST=metrics_library_test Change-Id: I133b19a672f52d5ba4f150aa75d1b32af2896c3f Reviewed-on: http://gerrit.chromium.org/gerrit/3865 Reviewed-by: Ken Mixter Tested-by: Ken Mixter --- metrics/Makefile | 11 ++--- metrics/metrics_library.cc | 22 ++++++++-- metrics/metrics_library.h | 8 ++++ metrics/metrics_library_test.cc | 73 +++++++++++++++++++++++---------- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 032ea73bf..2920547ea 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -39,16 +39,17 @@ TESTLIB_OBJS = \ TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags \ - -lglib-2.0 -lrootdev + -lglib-2.0 -lrootdev -lpolicy TESTDAEMON_LIBS = -lgmock -lgtest -TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread -lglib-2.0 +TESTLIB_LIBS = -lgtest -lgmock -lbase -lrt -lpthread -lglib-2.0 +POLICY_LIBS = -lpolicy all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) - $(CXX) $(LDFLAGS) $^ -o $@ + $(CXX) $(LDFLAGS) $(POLICY_LIBS) -lrt $^ -o $@ $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) @@ -63,10 +64,10 @@ $(LIB): $(LIB_OBJS) $(AR) rcs $@ $^ $(SHAREDLIB): $(LIB_OBJS) - $(CXX) $(LDFLAGS) -shared $^ -o $@ + $(CXX) $(LDFLAGS) $(POLICY_LIBS) -shared $^ -o $@ $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(LDFLAGS) $(TESTLIB_LIBS) + $(CXX) -o $@ $^ $(LDFLAGS) $(POLICY_LIBS) $(TESTLIB_LIBS) %.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index d2c1b0fd1..ae6c8e0db 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -13,6 +13,7 @@ #include #include "base/eintr_wrapper.h" // HANDLE_EINTR macro, no libbase required. +#include "policy/device_policy.h" #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) @@ -60,7 +61,8 @@ static int WriteFileDescriptor(const int fd, const char* data, int size) { MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), - consent_file_(kConsentFile) {} + consent_file_(kConsentFile), + policy_provider_(NULL) {} // We take buffer and buffer_size as parameters in order to simplify testing // of various alignments of the |device_name| with |buffer_size|. @@ -130,13 +132,21 @@ bool MetricsLibrary::IsGuestMode() { } bool MetricsLibrary::AreMetricsEnabled() { - static struct stat stat_buffer; time_t this_check_time = time(NULL); + if (!policy_provider_.get()) + policy_provider_.reset(new policy::PolicyProvider()); + else + policy_provider_->Reload(); + + // We initialize with the default value which is false and will be preserved + // if the policy is not set. + bool enabled = false; + if (policy_provider_->device_policy_is_loaded()) + policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled); if (this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; - if (stat(consent_file_, &stat_buffer) >= 0 && - !IsGuestMode()) + if (enabled && !IsGuestMode()) cached_enabled_ = true; else cached_enabled_ = false; @@ -283,3 +293,7 @@ bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { // Send the message. return SendMessageToChrome(message_length, message); } + +void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { + policy_provider_.reset(provider); +} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 3f860eb18..456610fad 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -8,8 +8,11 @@ #include #include +#include #include // for FRIEND_TEST +#include "policy/libpolicy.h" + class MetricsLibraryInterface { public: virtual void Init() = 0; @@ -122,6 +125,9 @@ class MetricsLibrary : public MetricsLibraryInterface { int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, const char* format, ...); + // This function is used by tests only to mock the device policies. + void SetPolicyProvider(policy::PolicyProvider* provider); + // Time at which we last checked if metrics were enabled. static time_t cached_enabled_time_; @@ -130,6 +136,8 @@ class MetricsLibrary : public MetricsLibraryInterface { const char* uma_events_file_; const char* consent_file_; + + scoped_ptr policy_provider_; }; #endif // METRICS_LIBRARY_H_ diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 762be4a79..3b3dd95ae 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -5,20 +5,24 @@ #include #include +#include #include +#include +#include #include "c_metrics_library.h" #include "metrics_library.h" static const FilePath kTestUMAEventsFile("test-uma-events"); -static const char kTestConsent[] = "test-consent"; static const char kTestMounts[] = "test-mounts"; -static void SetMetricsEnabled(bool enabled) { - if (enabled) - ASSERT_EQ(1, file_util::WriteFile(FilePath(kTestConsent) , "0", 1)); - else - file_util::Delete(FilePath(kTestConsent), false); +using ::testing::_; +using ::testing::Return; +using ::testing::AnyNumber; + +ACTION_P(SetMetricsPolicy, enabled) { + *arg0 = enabled; + return true; } class MetricsLibraryTest : public testing::Test { @@ -28,14 +32,20 @@ class MetricsLibraryTest : public testing::Test { lib_.Init(); EXPECT_TRUE(NULL != lib_.uma_events_file_); lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - SetMetricsEnabled(true); + device_policy_ = new policy::MockDevicePolicy(); + EXPECT_CALL(*device_policy_, LoadPolicy()) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .Times(AnyNumber()) + .WillRepeatedly(SetMetricsPolicy(true)); + provider_ = new policy::PolicyProvider(device_policy_); + lib_.SetPolicyProvider(provider_); // Defeat metrics enabled caching between tests. lib_.cached_enabled_time_ = 0; - lib_.consent_file_ = kTestConsent; } virtual void TearDown() { - file_util::Delete(FilePath(kTestConsent), false); file_util::Delete(FilePath(kTestMounts), false); file_util::Delete(kTestUMAEventsFile, false); } @@ -44,6 +54,8 @@ class MetricsLibraryTest : public testing::Test { void VerifyEnabledCacheEviction(bool to_value); MetricsLibrary lib_; + policy::MockDevicePolicy* device_policy_; + policy::PolicyProvider* provider_; }; TEST_F(MetricsLibraryTest, IsDeviceMounted) { @@ -106,7 +118,8 @@ TEST_F(MetricsLibraryTest, IsDeviceMounted) { } TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_FALSE(lib_.AreMetricsEnabled()); } @@ -119,9 +132,11 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { // times in a row. for (int i = 0; i < 100; ++i) { lib_.cached_enabled_time_ = 0; - SetMetricsEnabled(!to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(!to_value)); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - SetMetricsEnabled(to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(to_value)); if (lib_.AreMetricsEnabled() == !to_value) return; } @@ -130,9 +145,11 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) { lib_.cached_enabled_time_ = 0; - SetMetricsEnabled(!to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(!to_value)); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - SetMetricsEnabled(to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(to_value)); ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5); // Sleep one second (or cheat to be faster). --lib_.cached_enabled_time_; @@ -174,7 +191,8 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { } TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -209,7 +227,8 @@ TEST_F(MetricsLibraryTest, SendToUMA) { } TEST_F(MetricsLibraryTest, SendToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -226,7 +245,8 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMA) { } TEST_F(MetricsLibraryTest, SendUserActionToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendUserActionToUMA("SomeOtherKeyPressed")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -243,7 +263,8 @@ TEST_F(MetricsLibraryTest, SendCrashToUMAEnabled) { } TEST_F(MetricsLibraryTest, SendCrashToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -257,9 +278,16 @@ class CMetricsLibraryTest : public testing::Test { CMetricsLibraryInit(lib_); EXPECT_TRUE(NULL != ml.uma_events_file_); ml.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - SetMetricsEnabled(true); + device_policy_ = new policy::MockDevicePolicy(); + EXPECT_CALL(*device_policy_, LoadPolicy()) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .Times(AnyNumber()) + .WillRepeatedly(SetMetricsPolicy(true)); + provider_ = new policy::PolicyProvider(device_policy_); + ml.SetPolicyProvider(provider_); reinterpret_cast(lib_)->cached_enabled_time_ = 0; - reinterpret_cast(lib_)->consent_file_ = kTestConsent; } virtual void TearDown() { @@ -268,10 +296,13 @@ class CMetricsLibraryTest : public testing::Test { } CMetricsLibrary lib_; + policy::MockDevicePolicy* device_policy_; + policy::PolicyProvider* provider_; }; TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_)); } From ec380e84c5acce3a0a81710eaaaf85da31608345 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Thu, 21 Jul 2011 14:33:54 -0700 Subject: [PATCH 061/200] Revert "Make the metrics library respect the policy settings instead of the consent file." This reverts commit 6a294b4a3e9aa69dc9c429ed398e9dd7615029f2 Change-Id: I7d80b119d4e2b880f75c2293770e073785764e7d Reviewed-on: http://gerrit.chromium.org/gerrit/4509 Reviewed-by: Ken Mixter Tested-by: Ken Mixter --- metrics/Makefile | 11 +++-- metrics/metrics_library.cc | 22 ++-------- metrics/metrics_library.h | 8 ---- metrics/metrics_library_test.cc | 73 ++++++++++----------------------- 4 files changed, 30 insertions(+), 84 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 2920547ea..032ea73bf 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -39,17 +39,16 @@ TESTLIB_OBJS = \ TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags \ - -lglib-2.0 -lrootdev -lpolicy + -lglib-2.0 -lrootdev TESTDAEMON_LIBS = -lgmock -lgtest -TESTLIB_LIBS = -lgtest -lgmock -lbase -lrt -lpthread -lglib-2.0 -POLICY_LIBS = -lpolicy +TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread -lglib-2.0 all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) - $(CXX) $(LDFLAGS) $(POLICY_LIBS) -lrt $^ -o $@ + $(CXX) $(LDFLAGS) $^ -o $@ $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) @@ -64,10 +63,10 @@ $(LIB): $(LIB_OBJS) $(AR) rcs $@ $^ $(SHAREDLIB): $(LIB_OBJS) - $(CXX) $(LDFLAGS) $(POLICY_LIBS) -shared $^ -o $@ + $(CXX) $(LDFLAGS) -shared $^ -o $@ $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(LDFLAGS) $(POLICY_LIBS) $(TESTLIB_LIBS) + $(CXX) -o $@ $^ $(LDFLAGS) $(TESTLIB_LIBS) %.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index ae6c8e0db..d2c1b0fd1 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -13,7 +13,6 @@ #include #include "base/eintr_wrapper.h" // HANDLE_EINTR macro, no libbase required. -#include "policy/device_policy.h" #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) @@ -61,8 +60,7 @@ static int WriteFileDescriptor(const int fd, const char* data, int size) { MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), - consent_file_(kConsentFile), - policy_provider_(NULL) {} + consent_file_(kConsentFile) {} // We take buffer and buffer_size as parameters in order to simplify testing // of various alignments of the |device_name| with |buffer_size|. @@ -132,21 +130,13 @@ bool MetricsLibrary::IsGuestMode() { } bool MetricsLibrary::AreMetricsEnabled() { + static struct stat stat_buffer; time_t this_check_time = time(NULL); - if (!policy_provider_.get()) - policy_provider_.reset(new policy::PolicyProvider()); - else - policy_provider_->Reload(); - - // We initialize with the default value which is false and will be preserved - // if the policy is not set. - bool enabled = false; - if (policy_provider_->device_policy_is_loaded()) - policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled); if (this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; - if (enabled && !IsGuestMode()) + if (stat(consent_file_, &stat_buffer) >= 0 && + !IsGuestMode()) cached_enabled_ = true; else cached_enabled_ = false; @@ -293,7 +283,3 @@ bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { // Send the message. return SendMessageToChrome(message_length, message); } - -void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { - policy_provider_.reset(provider); -} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 456610fad..3f860eb18 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -8,11 +8,8 @@ #include #include -#include #include // for FRIEND_TEST -#include "policy/libpolicy.h" - class MetricsLibraryInterface { public: virtual void Init() = 0; @@ -125,9 +122,6 @@ class MetricsLibrary : public MetricsLibraryInterface { int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, const char* format, ...); - // This function is used by tests only to mock the device policies. - void SetPolicyProvider(policy::PolicyProvider* provider); - // Time at which we last checked if metrics were enabled. static time_t cached_enabled_time_; @@ -136,8 +130,6 @@ class MetricsLibrary : public MetricsLibraryInterface { const char* uma_events_file_; const char* consent_file_; - - scoped_ptr policy_provider_; }; #endif // METRICS_LIBRARY_H_ diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 3b3dd95ae..762be4a79 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -5,24 +5,20 @@ #include #include -#include #include -#include -#include #include "c_metrics_library.h" #include "metrics_library.h" static const FilePath kTestUMAEventsFile("test-uma-events"); +static const char kTestConsent[] = "test-consent"; static const char kTestMounts[] = "test-mounts"; -using ::testing::_; -using ::testing::Return; -using ::testing::AnyNumber; - -ACTION_P(SetMetricsPolicy, enabled) { - *arg0 = enabled; - return true; +static void SetMetricsEnabled(bool enabled) { + if (enabled) + ASSERT_EQ(1, file_util::WriteFile(FilePath(kTestConsent) , "0", 1)); + else + file_util::Delete(FilePath(kTestConsent), false); } class MetricsLibraryTest : public testing::Test { @@ -32,20 +28,14 @@ class MetricsLibraryTest : public testing::Test { lib_.Init(); EXPECT_TRUE(NULL != lib_.uma_events_file_); lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - device_policy_ = new policy::MockDevicePolicy(); - EXPECT_CALL(*device_policy_, LoadPolicy()) - .Times(AnyNumber()) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .Times(AnyNumber()) - .WillRepeatedly(SetMetricsPolicy(true)); - provider_ = new policy::PolicyProvider(device_policy_); - lib_.SetPolicyProvider(provider_); + SetMetricsEnabled(true); // Defeat metrics enabled caching between tests. lib_.cached_enabled_time_ = 0; + lib_.consent_file_ = kTestConsent; } virtual void TearDown() { + file_util::Delete(FilePath(kTestConsent), false); file_util::Delete(FilePath(kTestMounts), false); file_util::Delete(kTestUMAEventsFile, false); } @@ -54,8 +44,6 @@ class MetricsLibraryTest : public testing::Test { void VerifyEnabledCacheEviction(bool to_value); MetricsLibrary lib_; - policy::MockDevicePolicy* device_policy_; - policy::PolicyProvider* provider_; }; TEST_F(MetricsLibraryTest, IsDeviceMounted) { @@ -118,8 +106,7 @@ TEST_F(MetricsLibraryTest, IsDeviceMounted) { } TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_FALSE(lib_.AreMetricsEnabled()); } @@ -132,11 +119,9 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { // times in a row. for (int i = 0; i < 100; ++i) { lib_.cached_enabled_time_ = 0; - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(!to_value)); + SetMetricsEnabled(!to_value); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(to_value)); + SetMetricsEnabled(to_value); if (lib_.AreMetricsEnabled() == !to_value) return; } @@ -145,11 +130,9 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) { lib_.cached_enabled_time_ = 0; - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(!to_value)); + SetMetricsEnabled(!to_value); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(to_value)); + SetMetricsEnabled(to_value); ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5); // Sleep one second (or cheat to be faster). --lib_.cached_enabled_time_; @@ -191,8 +174,7 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { } TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -227,8 +209,7 @@ TEST_F(MetricsLibraryTest, SendToUMA) { } TEST_F(MetricsLibraryTest, SendToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -245,8 +226,7 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMA) { } TEST_F(MetricsLibraryTest, SendUserActionToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_TRUE(lib_.SendUserActionToUMA("SomeOtherKeyPressed")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -263,8 +243,7 @@ TEST_F(MetricsLibraryTest, SendCrashToUMAEnabled) { } TEST_F(MetricsLibraryTest, SendCrashToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -278,16 +257,9 @@ class CMetricsLibraryTest : public testing::Test { CMetricsLibraryInit(lib_); EXPECT_TRUE(NULL != ml.uma_events_file_); ml.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - device_policy_ = new policy::MockDevicePolicy(); - EXPECT_CALL(*device_policy_, LoadPolicy()) - .Times(AnyNumber()) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .Times(AnyNumber()) - .WillRepeatedly(SetMetricsPolicy(true)); - provider_ = new policy::PolicyProvider(device_policy_); - ml.SetPolicyProvider(provider_); + SetMetricsEnabled(true); reinterpret_cast(lib_)->cached_enabled_time_ = 0; + reinterpret_cast(lib_)->consent_file_ = kTestConsent; } virtual void TearDown() { @@ -296,13 +268,10 @@ class CMetricsLibraryTest : public testing::Test { } CMetricsLibrary lib_; - policy::MockDevicePolicy* device_policy_; - policy::PolicyProvider* provider_; }; TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_)); } From ff3eb194b0b012961d9454a93589cd030ba72c64 Mon Sep 17 00:00:00 2001 From: Julian Pastarmov Date: Fri, 22 Jul 2011 08:57:55 -0700 Subject: [PATCH 062/200] Make the metrics library respect the policy settings instead of the consent file. BUG=chromium-os:17012 TEST=metrics_library_test This is a second try at committing http://gerrit.chromium.org/gerrit/#change,3865 Change-Id: I8c874ca26dd0d07471cfc66ded527ad5c3a1cd20 Reviewed-on: http://gerrit.chromium.org/gerrit/4578 Reviewed-by: Julian Pastarmov Tested-by: Julian Pastarmov --- metrics/Makefile | 11 ++--- metrics/metrics_library.cc | 22 ++++++++-- metrics/metrics_library.h | 8 ++++ metrics/metrics_library_test.cc | 73 +++++++++++++++++++++++---------- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 032ea73bf..2920547ea 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -39,16 +39,17 @@ TESTLIB_OBJS = \ TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags \ - -lglib-2.0 -lrootdev + -lglib-2.0 -lrootdev -lpolicy TESTDAEMON_LIBS = -lgmock -lgtest -TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread -lglib-2.0 +TESTLIB_LIBS = -lgtest -lgmock -lbase -lrt -lpthread -lglib-2.0 +POLICY_LIBS = -lpolicy all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) - $(CXX) $(LDFLAGS) $^ -o $@ + $(CXX) $(LDFLAGS) $(POLICY_LIBS) -lrt $^ -o $@ $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) @@ -63,10 +64,10 @@ $(LIB): $(LIB_OBJS) $(AR) rcs $@ $^ $(SHAREDLIB): $(LIB_OBJS) - $(CXX) $(LDFLAGS) -shared $^ -o $@ + $(CXX) $(LDFLAGS) $(POLICY_LIBS) -shared $^ -o $@ $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(LDFLAGS) $(TESTLIB_LIBS) + $(CXX) -o $@ $^ $(LDFLAGS) $(POLICY_LIBS) $(TESTLIB_LIBS) %.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index d2c1b0fd1..ae6c8e0db 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -13,6 +13,7 @@ #include #include "base/eintr_wrapper.h" // HANDLE_EINTR macro, no libbase required. +#include "policy/device_policy.h" #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) @@ -60,7 +61,8 @@ static int WriteFileDescriptor(const int fd, const char* data, int size) { MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), - consent_file_(kConsentFile) {} + consent_file_(kConsentFile), + policy_provider_(NULL) {} // We take buffer and buffer_size as parameters in order to simplify testing // of various alignments of the |device_name| with |buffer_size|. @@ -130,13 +132,21 @@ bool MetricsLibrary::IsGuestMode() { } bool MetricsLibrary::AreMetricsEnabled() { - static struct stat stat_buffer; time_t this_check_time = time(NULL); + if (!policy_provider_.get()) + policy_provider_.reset(new policy::PolicyProvider()); + else + policy_provider_->Reload(); + + // We initialize with the default value which is false and will be preserved + // if the policy is not set. + bool enabled = false; + if (policy_provider_->device_policy_is_loaded()) + policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled); if (this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; - if (stat(consent_file_, &stat_buffer) >= 0 && - !IsGuestMode()) + if (enabled && !IsGuestMode()) cached_enabled_ = true; else cached_enabled_ = false; @@ -283,3 +293,7 @@ bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { // Send the message. return SendMessageToChrome(message_length, message); } + +void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { + policy_provider_.reset(provider); +} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 3f860eb18..456610fad 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -8,8 +8,11 @@ #include #include +#include #include // for FRIEND_TEST +#include "policy/libpolicy.h" + class MetricsLibraryInterface { public: virtual void Init() = 0; @@ -122,6 +125,9 @@ class MetricsLibrary : public MetricsLibraryInterface { int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, const char* format, ...); + // This function is used by tests only to mock the device policies. + void SetPolicyProvider(policy::PolicyProvider* provider); + // Time at which we last checked if metrics were enabled. static time_t cached_enabled_time_; @@ -130,6 +136,8 @@ class MetricsLibrary : public MetricsLibraryInterface { const char* uma_events_file_; const char* consent_file_; + + scoped_ptr policy_provider_; }; #endif // METRICS_LIBRARY_H_ diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 762be4a79..3b3dd95ae 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -5,20 +5,24 @@ #include #include +#include #include +#include +#include #include "c_metrics_library.h" #include "metrics_library.h" static const FilePath kTestUMAEventsFile("test-uma-events"); -static const char kTestConsent[] = "test-consent"; static const char kTestMounts[] = "test-mounts"; -static void SetMetricsEnabled(bool enabled) { - if (enabled) - ASSERT_EQ(1, file_util::WriteFile(FilePath(kTestConsent) , "0", 1)); - else - file_util::Delete(FilePath(kTestConsent), false); +using ::testing::_; +using ::testing::Return; +using ::testing::AnyNumber; + +ACTION_P(SetMetricsPolicy, enabled) { + *arg0 = enabled; + return true; } class MetricsLibraryTest : public testing::Test { @@ -28,14 +32,20 @@ class MetricsLibraryTest : public testing::Test { lib_.Init(); EXPECT_TRUE(NULL != lib_.uma_events_file_); lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - SetMetricsEnabled(true); + device_policy_ = new policy::MockDevicePolicy(); + EXPECT_CALL(*device_policy_, LoadPolicy()) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .Times(AnyNumber()) + .WillRepeatedly(SetMetricsPolicy(true)); + provider_ = new policy::PolicyProvider(device_policy_); + lib_.SetPolicyProvider(provider_); // Defeat metrics enabled caching between tests. lib_.cached_enabled_time_ = 0; - lib_.consent_file_ = kTestConsent; } virtual void TearDown() { - file_util::Delete(FilePath(kTestConsent), false); file_util::Delete(FilePath(kTestMounts), false); file_util::Delete(kTestUMAEventsFile, false); } @@ -44,6 +54,8 @@ class MetricsLibraryTest : public testing::Test { void VerifyEnabledCacheEviction(bool to_value); MetricsLibrary lib_; + policy::MockDevicePolicy* device_policy_; + policy::PolicyProvider* provider_; }; TEST_F(MetricsLibraryTest, IsDeviceMounted) { @@ -106,7 +118,8 @@ TEST_F(MetricsLibraryTest, IsDeviceMounted) { } TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_FALSE(lib_.AreMetricsEnabled()); } @@ -119,9 +132,11 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { // times in a row. for (int i = 0; i < 100; ++i) { lib_.cached_enabled_time_ = 0; - SetMetricsEnabled(!to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(!to_value)); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - SetMetricsEnabled(to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(to_value)); if (lib_.AreMetricsEnabled() == !to_value) return; } @@ -130,9 +145,11 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) { lib_.cached_enabled_time_ = 0; - SetMetricsEnabled(!to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(!to_value)); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - SetMetricsEnabled(to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(to_value)); ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5); // Sleep one second (or cheat to be faster). --lib_.cached_enabled_time_; @@ -174,7 +191,8 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { } TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -209,7 +227,8 @@ TEST_F(MetricsLibraryTest, SendToUMA) { } TEST_F(MetricsLibraryTest, SendToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -226,7 +245,8 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMA) { } TEST_F(MetricsLibraryTest, SendUserActionToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendUserActionToUMA("SomeOtherKeyPressed")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -243,7 +263,8 @@ TEST_F(MetricsLibraryTest, SendCrashToUMAEnabled) { } TEST_F(MetricsLibraryTest, SendCrashToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -257,9 +278,16 @@ class CMetricsLibraryTest : public testing::Test { CMetricsLibraryInit(lib_); EXPECT_TRUE(NULL != ml.uma_events_file_); ml.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - SetMetricsEnabled(true); + device_policy_ = new policy::MockDevicePolicy(); + EXPECT_CALL(*device_policy_, LoadPolicy()) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .Times(AnyNumber()) + .WillRepeatedly(SetMetricsPolicy(true)); + provider_ = new policy::PolicyProvider(device_policy_); + ml.SetPolicyProvider(provider_); reinterpret_cast(lib_)->cached_enabled_time_ = 0; - reinterpret_cast(lib_)->consent_file_ = kTestConsent; } virtual void TearDown() { @@ -268,10 +296,13 @@ class CMetricsLibraryTest : public testing::Test { } CMetricsLibrary lib_; + policy::MockDevicePolicy* device_policy_; + policy::PolicyProvider* provider_; }; TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_)); } From a3df284fe19807430cdd54d2274ff198bec120d5 Mon Sep 17 00:00:00 2001 From: Julian Pastarmov Date: Fri, 22 Jul 2011 10:20:38 -0700 Subject: [PATCH 063/200] Revert "Make the metrics library respect the policy settings instead of the consent file." This reverts commit 8c874ca26dd0d07471cfc66ded527ad5c3a1cd20 Change-Id: Iaa91d046ac27a9bdc96e6cd9438741d98f1cef66 Reviewed-on: http://gerrit.chromium.org/gerrit/4583 Reviewed-by: Julian Pastarmov Tested-by: Julian Pastarmov --- metrics/Makefile | 11 +++-- metrics/metrics_library.cc | 22 ++-------- metrics/metrics_library.h | 8 ---- metrics/metrics_library_test.cc | 73 ++++++++++----------------------- 4 files changed, 30 insertions(+), 84 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 2920547ea..032ea73bf 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -39,17 +39,16 @@ TESTLIB_OBJS = \ TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags \ - -lglib-2.0 -lrootdev -lpolicy + -lglib-2.0 -lrootdev TESTDAEMON_LIBS = -lgmock -lgtest -TESTLIB_LIBS = -lgtest -lgmock -lbase -lrt -lpthread -lglib-2.0 -POLICY_LIBS = -lpolicy +TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread -lglib-2.0 all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) - $(CXX) $(LDFLAGS) $(POLICY_LIBS) -lrt $^ -o $@ + $(CXX) $(LDFLAGS) $^ -o $@ $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) @@ -64,10 +63,10 @@ $(LIB): $(LIB_OBJS) $(AR) rcs $@ $^ $(SHAREDLIB): $(LIB_OBJS) - $(CXX) $(LDFLAGS) $(POLICY_LIBS) -shared $^ -o $@ + $(CXX) $(LDFLAGS) -shared $^ -o $@ $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(LDFLAGS) $(POLICY_LIBS) $(TESTLIB_LIBS) + $(CXX) -o $@ $^ $(LDFLAGS) $(TESTLIB_LIBS) %.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index ae6c8e0db..d2c1b0fd1 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -13,7 +13,6 @@ #include #include "base/eintr_wrapper.h" // HANDLE_EINTR macro, no libbase required. -#include "policy/device_policy.h" #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) @@ -61,8 +60,7 @@ static int WriteFileDescriptor(const int fd, const char* data, int size) { MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), - consent_file_(kConsentFile), - policy_provider_(NULL) {} + consent_file_(kConsentFile) {} // We take buffer and buffer_size as parameters in order to simplify testing // of various alignments of the |device_name| with |buffer_size|. @@ -132,21 +130,13 @@ bool MetricsLibrary::IsGuestMode() { } bool MetricsLibrary::AreMetricsEnabled() { + static struct stat stat_buffer; time_t this_check_time = time(NULL); - if (!policy_provider_.get()) - policy_provider_.reset(new policy::PolicyProvider()); - else - policy_provider_->Reload(); - - // We initialize with the default value which is false and will be preserved - // if the policy is not set. - bool enabled = false; - if (policy_provider_->device_policy_is_loaded()) - policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled); if (this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; - if (enabled && !IsGuestMode()) + if (stat(consent_file_, &stat_buffer) >= 0 && + !IsGuestMode()) cached_enabled_ = true; else cached_enabled_ = false; @@ -293,7 +283,3 @@ bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { // Send the message. return SendMessageToChrome(message_length, message); } - -void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { - policy_provider_.reset(provider); -} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 456610fad..3f860eb18 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -8,11 +8,8 @@ #include #include -#include #include // for FRIEND_TEST -#include "policy/libpolicy.h" - class MetricsLibraryInterface { public: virtual void Init() = 0; @@ -125,9 +122,6 @@ class MetricsLibrary : public MetricsLibraryInterface { int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, const char* format, ...); - // This function is used by tests only to mock the device policies. - void SetPolicyProvider(policy::PolicyProvider* provider); - // Time at which we last checked if metrics were enabled. static time_t cached_enabled_time_; @@ -136,8 +130,6 @@ class MetricsLibrary : public MetricsLibraryInterface { const char* uma_events_file_; const char* consent_file_; - - scoped_ptr policy_provider_; }; #endif // METRICS_LIBRARY_H_ diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 3b3dd95ae..762be4a79 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -5,24 +5,20 @@ #include #include -#include #include -#include -#include #include "c_metrics_library.h" #include "metrics_library.h" static const FilePath kTestUMAEventsFile("test-uma-events"); +static const char kTestConsent[] = "test-consent"; static const char kTestMounts[] = "test-mounts"; -using ::testing::_; -using ::testing::Return; -using ::testing::AnyNumber; - -ACTION_P(SetMetricsPolicy, enabled) { - *arg0 = enabled; - return true; +static void SetMetricsEnabled(bool enabled) { + if (enabled) + ASSERT_EQ(1, file_util::WriteFile(FilePath(kTestConsent) , "0", 1)); + else + file_util::Delete(FilePath(kTestConsent), false); } class MetricsLibraryTest : public testing::Test { @@ -32,20 +28,14 @@ class MetricsLibraryTest : public testing::Test { lib_.Init(); EXPECT_TRUE(NULL != lib_.uma_events_file_); lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - device_policy_ = new policy::MockDevicePolicy(); - EXPECT_CALL(*device_policy_, LoadPolicy()) - .Times(AnyNumber()) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .Times(AnyNumber()) - .WillRepeatedly(SetMetricsPolicy(true)); - provider_ = new policy::PolicyProvider(device_policy_); - lib_.SetPolicyProvider(provider_); + SetMetricsEnabled(true); // Defeat metrics enabled caching between tests. lib_.cached_enabled_time_ = 0; + lib_.consent_file_ = kTestConsent; } virtual void TearDown() { + file_util::Delete(FilePath(kTestConsent), false); file_util::Delete(FilePath(kTestMounts), false); file_util::Delete(kTestUMAEventsFile, false); } @@ -54,8 +44,6 @@ class MetricsLibraryTest : public testing::Test { void VerifyEnabledCacheEviction(bool to_value); MetricsLibrary lib_; - policy::MockDevicePolicy* device_policy_; - policy::PolicyProvider* provider_; }; TEST_F(MetricsLibraryTest, IsDeviceMounted) { @@ -118,8 +106,7 @@ TEST_F(MetricsLibraryTest, IsDeviceMounted) { } TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_FALSE(lib_.AreMetricsEnabled()); } @@ -132,11 +119,9 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { // times in a row. for (int i = 0; i < 100; ++i) { lib_.cached_enabled_time_ = 0; - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(!to_value)); + SetMetricsEnabled(!to_value); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(to_value)); + SetMetricsEnabled(to_value); if (lib_.AreMetricsEnabled() == !to_value) return; } @@ -145,11 +130,9 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) { lib_.cached_enabled_time_ = 0; - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(!to_value)); + SetMetricsEnabled(!to_value); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(to_value)); + SetMetricsEnabled(to_value); ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5); // Sleep one second (or cheat to be faster). --lib_.cached_enabled_time_; @@ -191,8 +174,7 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { } TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -227,8 +209,7 @@ TEST_F(MetricsLibraryTest, SendToUMA) { } TEST_F(MetricsLibraryTest, SendToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -245,8 +226,7 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMA) { } TEST_F(MetricsLibraryTest, SendUserActionToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_TRUE(lib_.SendUserActionToUMA("SomeOtherKeyPressed")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -263,8 +243,7 @@ TEST_F(MetricsLibraryTest, SendCrashToUMAEnabled) { } TEST_F(MetricsLibraryTest, SendCrashToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -278,16 +257,9 @@ class CMetricsLibraryTest : public testing::Test { CMetricsLibraryInit(lib_); EXPECT_TRUE(NULL != ml.uma_events_file_); ml.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - device_policy_ = new policy::MockDevicePolicy(); - EXPECT_CALL(*device_policy_, LoadPolicy()) - .Times(AnyNumber()) - .WillRepeatedly(Return(true)); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .Times(AnyNumber()) - .WillRepeatedly(SetMetricsPolicy(true)); - provider_ = new policy::PolicyProvider(device_policy_); - ml.SetPolicyProvider(provider_); + SetMetricsEnabled(true); reinterpret_cast(lib_)->cached_enabled_time_ = 0; + reinterpret_cast(lib_)->consent_file_ = kTestConsent; } virtual void TearDown() { @@ -296,13 +268,10 @@ class CMetricsLibraryTest : public testing::Test { } CMetricsLibrary lib_; - policy::MockDevicePolicy* device_policy_; - policy::PolicyProvider* provider_; }; TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); + SetMetricsEnabled(false); EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_)); } From b2f170970ebea7d7b6200761a5243224af536874 Mon Sep 17 00:00:00 2001 From: Ken Mixter Date: Fri, 22 Jul 2011 14:59:51 -0700 Subject: [PATCH 064/200] Revert "Revert "Make the metrics library respect the policy settings instead of the consent file."" This reverts commit aa91d046ac27a9bdc96e6cd9438741d98f1cef66 Change-Id: I1c84fb86d6eb56a5a8e88136c98104394be697b2 Reviewed-on: http://gerrit.chromium.org/gerrit/4614 Reviewed-by: Darin Petkov Reviewed-by: Ken Mixter Tested-by: Ken Mixter --- metrics/Makefile | 11 ++--- metrics/metrics_library.cc | 22 ++++++++-- metrics/metrics_library.h | 8 ++++ metrics/metrics_library_test.cc | 73 +++++++++++++++++++++++---------- 4 files changed, 84 insertions(+), 30 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 032ea73bf..2920547ea 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -39,16 +39,17 @@ TESTLIB_OBJS = \ TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags \ - -lglib-2.0 -lrootdev + -lglib-2.0 -lrootdev -lpolicy TESTDAEMON_LIBS = -lgmock -lgtest -TESTLIB_LIBS = -lgtest -lbase -lrt -lpthread -lglib-2.0 +TESTLIB_LIBS = -lgtest -lgmock -lbase -lrt -lpthread -lglib-2.0 +POLICY_LIBS = -lpolicy all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) - $(CXX) $(LDFLAGS) $^ -o $@ + $(CXX) $(LDFLAGS) $(POLICY_LIBS) -lrt $^ -o $@ $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) @@ -63,10 +64,10 @@ $(LIB): $(LIB_OBJS) $(AR) rcs $@ $^ $(SHAREDLIB): $(LIB_OBJS) - $(CXX) $(LDFLAGS) -shared $^ -o $@ + $(CXX) $(LDFLAGS) $(POLICY_LIBS) -shared $^ -o $@ $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(LDFLAGS) $(TESTLIB_LIBS) + $(CXX) -o $@ $^ $(LDFLAGS) $(POLICY_LIBS) $(TESTLIB_LIBS) %.o: %.cc $(CXX) $(CXXFLAGS) -c $< -o $@ diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index d2c1b0fd1..ae6c8e0db 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -13,6 +13,7 @@ #include #include "base/eintr_wrapper.h" // HANDLE_EINTR macro, no libbase required. +#include "policy/device_policy.h" #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) @@ -60,7 +61,8 @@ static int WriteFileDescriptor(const int fd, const char* data, int size) { MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), - consent_file_(kConsentFile) {} + consent_file_(kConsentFile), + policy_provider_(NULL) {} // We take buffer and buffer_size as parameters in order to simplify testing // of various alignments of the |device_name| with |buffer_size|. @@ -130,13 +132,21 @@ bool MetricsLibrary::IsGuestMode() { } bool MetricsLibrary::AreMetricsEnabled() { - static struct stat stat_buffer; time_t this_check_time = time(NULL); + if (!policy_provider_.get()) + policy_provider_.reset(new policy::PolicyProvider()); + else + policy_provider_->Reload(); + + // We initialize with the default value which is false and will be preserved + // if the policy is not set. + bool enabled = false; + if (policy_provider_->device_policy_is_loaded()) + policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled); if (this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; - if (stat(consent_file_, &stat_buffer) >= 0 && - !IsGuestMode()) + if (enabled && !IsGuestMode()) cached_enabled_ = true; else cached_enabled_ = false; @@ -283,3 +293,7 @@ bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { // Send the message. return SendMessageToChrome(message_length, message); } + +void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { + policy_provider_.reset(provider); +} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 3f860eb18..456610fad 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -8,8 +8,11 @@ #include #include +#include #include // for FRIEND_TEST +#include "policy/libpolicy.h" + class MetricsLibraryInterface { public: virtual void Init() = 0; @@ -122,6 +125,9 @@ class MetricsLibrary : public MetricsLibraryInterface { int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, const char* format, ...); + // This function is used by tests only to mock the device policies. + void SetPolicyProvider(policy::PolicyProvider* provider); + // Time at which we last checked if metrics were enabled. static time_t cached_enabled_time_; @@ -130,6 +136,8 @@ class MetricsLibrary : public MetricsLibraryInterface { const char* uma_events_file_; const char* consent_file_; + + scoped_ptr policy_provider_; }; #endif // METRICS_LIBRARY_H_ diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 762be4a79..3b3dd95ae 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -5,20 +5,24 @@ #include #include +#include #include +#include +#include #include "c_metrics_library.h" #include "metrics_library.h" static const FilePath kTestUMAEventsFile("test-uma-events"); -static const char kTestConsent[] = "test-consent"; static const char kTestMounts[] = "test-mounts"; -static void SetMetricsEnabled(bool enabled) { - if (enabled) - ASSERT_EQ(1, file_util::WriteFile(FilePath(kTestConsent) , "0", 1)); - else - file_util::Delete(FilePath(kTestConsent), false); +using ::testing::_; +using ::testing::Return; +using ::testing::AnyNumber; + +ACTION_P(SetMetricsPolicy, enabled) { + *arg0 = enabled; + return true; } class MetricsLibraryTest : public testing::Test { @@ -28,14 +32,20 @@ class MetricsLibraryTest : public testing::Test { lib_.Init(); EXPECT_TRUE(NULL != lib_.uma_events_file_); lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - SetMetricsEnabled(true); + device_policy_ = new policy::MockDevicePolicy(); + EXPECT_CALL(*device_policy_, LoadPolicy()) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .Times(AnyNumber()) + .WillRepeatedly(SetMetricsPolicy(true)); + provider_ = new policy::PolicyProvider(device_policy_); + lib_.SetPolicyProvider(provider_); // Defeat metrics enabled caching between tests. lib_.cached_enabled_time_ = 0; - lib_.consent_file_ = kTestConsent; } virtual void TearDown() { - file_util::Delete(FilePath(kTestConsent), false); file_util::Delete(FilePath(kTestMounts), false); file_util::Delete(kTestUMAEventsFile, false); } @@ -44,6 +54,8 @@ class MetricsLibraryTest : public testing::Test { void VerifyEnabledCacheEviction(bool to_value); MetricsLibrary lib_; + policy::MockDevicePolicy* device_policy_; + policy::PolicyProvider* provider_; }; TEST_F(MetricsLibraryTest, IsDeviceMounted) { @@ -106,7 +118,8 @@ TEST_F(MetricsLibraryTest, IsDeviceMounted) { } TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_FALSE(lib_.AreMetricsEnabled()); } @@ -119,9 +132,11 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { // times in a row. for (int i = 0; i < 100; ++i) { lib_.cached_enabled_time_ = 0; - SetMetricsEnabled(!to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(!to_value)); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - SetMetricsEnabled(to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(to_value)); if (lib_.AreMetricsEnabled() == !to_value) return; } @@ -130,9 +145,11 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) { lib_.cached_enabled_time_ = 0; - SetMetricsEnabled(!to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(!to_value)); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - SetMetricsEnabled(to_value); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(to_value)); ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5); // Sleep one second (or cheat to be faster). --lib_.cached_enabled_time_; @@ -174,7 +191,8 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { } TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -209,7 +227,8 @@ TEST_F(MetricsLibraryTest, SendToUMA) { } TEST_F(MetricsLibraryTest, SendToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -226,7 +245,8 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMA) { } TEST_F(MetricsLibraryTest, SendUserActionToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendUserActionToUMA("SomeOtherKeyPressed")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -243,7 +263,8 @@ TEST_F(MetricsLibraryTest, SendCrashToUMAEnabled) { } TEST_F(MetricsLibraryTest, SendCrashToUMANotEnabled) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); } @@ -257,9 +278,16 @@ class CMetricsLibraryTest : public testing::Test { CMetricsLibraryInit(lib_); EXPECT_TRUE(NULL != ml.uma_events_file_); ml.uma_events_file_ = kTestUMAEventsFile.value().c_str(); - SetMetricsEnabled(true); + device_policy_ = new policy::MockDevicePolicy(); + EXPECT_CALL(*device_policy_, LoadPolicy()) + .Times(AnyNumber()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .Times(AnyNumber()) + .WillRepeatedly(SetMetricsPolicy(true)); + provider_ = new policy::PolicyProvider(device_policy_); + ml.SetPolicyProvider(provider_); reinterpret_cast(lib_)->cached_enabled_time_ = 0; - reinterpret_cast(lib_)->consent_file_ = kTestConsent; } virtual void TearDown() { @@ -268,10 +296,13 @@ class CMetricsLibraryTest : public testing::Test { } CMetricsLibrary lib_; + policy::MockDevicePolicy* device_policy_; + policy::PolicyProvider* provider_; }; TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) { - SetMetricsEnabled(false); + EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillOnce(SetMetricsPolicy(false)); EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_)); } From 70b7abd84fc7b101d46a3f01d543b3fb3a9c268a Mon Sep 17 00:00:00 2001 From: Julian Pastarmov Date: Tue, 2 Aug 2011 16:10:49 +0200 Subject: [PATCH 065/200] Respect the metrics file if no policy value is set (for legacy migration). BUG=chromium-os:18430 TEST=Update chromeos and check whether the consent for stats is preserved. Change-Id: I86d4710ef9f381abd9f1eac728040878e6fc5b71 Reviewed-on: http://gerrit.chromium.org/gerrit/5139 Reviewed-by: Ken Mixter Tested-by: Julian Pastarmov --- metrics/metrics_library.cc | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index ae6c8e0db..707ea6874 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -132,20 +132,30 @@ bool MetricsLibrary::IsGuestMode() { } bool MetricsLibrary::AreMetricsEnabled() { + static struct stat stat_buffer; time_t this_check_time = time(NULL); - if (!policy_provider_.get()) - policy_provider_.reset(new policy::PolicyProvider()); - else - policy_provider_->Reload(); - - // We initialize with the default value which is false and will be preserved - // if the policy is not set. - bool enabled = false; - if (policy_provider_->device_policy_is_loaded()) - policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled); - if (this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; + + if (!policy_provider_.get()) + policy_provider_.reset(new policy::PolicyProvider()); + else + policy_provider_->Reload(); + // We initialize with the default value which is false and will be preserved + // if the policy is not set. + bool enabled = false; + bool has_policy = false; + if (policy_provider_->device_policy_is_loaded()) { + has_policy = + policy_provider_->GetDevicePolicy().GetMetricsEnabled(&enabled); + } + // If policy couldn't be loaded or the metrics policy is not set we should + // still respect the consent file if it is present for migration purposes. + // TODO(pastarmovj) + if (!has_policy) { + enabled = stat(consent_file_, &stat_buffer) >= 0; + } + if (enabled && !IsGuestMode()) cached_enabled_ = true; else From 0e5debf2d5f8e929dea4c35915174b77a86fbdce Mon Sep 17 00:00:00 2001 From: Julian Pastarmov Date: Thu, 4 Aug 2011 11:15:13 +0200 Subject: [PATCH 066/200] Fixed a test where a method was expected to be called but it wasn't. A recent optimization in the code made that second call unneeded as so our expectation is not met anymore (for good so). BUG=chromium-os:18430 TEST=Metrics unit tests should pass. Change-Id: I1162dc657156ee9207b904ab544b0f151eec0453 Reviewed-on: http://gerrit.chromium.org/gerrit/5302 Reviewed-by: Julian Pastarmov Tested-by: Julian Pastarmov --- metrics/metrics_library_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 3b3dd95ae..0e58e0190 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -135,8 +135,8 @@ void MetricsLibraryTest::VerifyEnabledCacheHit(bool to_value) { EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) .WillOnce(SetMetricsPolicy(!to_value)); ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(to_value)); + ON_CALL(*device_policy_, GetMetricsEnabled(_)) + .WillByDefault(SetMetricsPolicy(to_value)); if (lib_.AreMetricsEnabled() == !to_value) return; } From be388f301b9fef2e4ecd0cd9612dfe9216b5b266 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Tue, 2 Aug 2011 12:40:17 -0700 Subject: [PATCH 067/200] Added a timer-based report to the metrics library. Timer encapsulates a timer with basic functionality. TimerReporter subclasses it for also sending UMA reports. BUG=chromium-os:18800 TEST=Unit test 'timer_test' has been included Change-Id: I9de9a2a7388721ba1476fe706a8d12788d2176ad Reviewed-on: http://gerrit.chromium.org/gerrit/5161 Reviewed-by: Gaurav Shah Reviewed-by: Darin Petkov Tested-by: Bruno Pontes Soares Rocha Reviewed-by: Bruno Pontes Soares Rocha --- metrics/Makefile | 17 +++-- metrics/timer.cc | 77 ++++++++++++++++++++ metrics/timer.h | 140 ++++++++++++++++++++++++++++++++++++ metrics/timer_mock.h | 45 ++++++++++++ metrics/timer_test.cc | 162 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 437 insertions(+), 4 deletions(-) create mode 100644 metrics/timer.cc create mode 100644 metrics/timer.h create mode 100644 metrics/timer_mock.h create mode 100644 metrics/timer_test.cc diff --git a/metrics/Makefile b/metrics/Makefile index 2920547ea..072fb048e 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -1,4 +1,4 @@ -# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # @@ -17,10 +17,14 @@ LIB = libmetrics.a SHAREDLIB = libmetrics.so LIB_TEST = metrics_library_test COUNTER_TEST = counter_test +TIMER_TEST = timer_test TESTCOUNTER_OBJS = \ counter.o \ counter_test.o +TESTTIMER_OBJS = \ + timer.o \ + timer_test.o CLIENT_OBJS = \ metrics_client.o DAEMON_OBJS = \ @@ -33,11 +37,13 @@ TESTDAEMON_OBJS = \ metrics_daemon_test.o LIB_OBJS = \ c_metrics_library.o \ - metrics_library.o + metrics_library.o \ + timer.o TESTLIB_OBJS = \ metrics_library_test.o TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 +TESTTIMER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags \ -lglib-2.0 -lrootdev -lpolicy TESTDAEMON_LIBS = -lgmock -lgtest @@ -46,7 +52,7 @@ POLICY_LIBS = -lpolicy all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) -tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) +tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(TIMER_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) $(CXX) $(LDFLAGS) $(POLICY_LIBS) -lrt $^ -o $@ @@ -54,6 +60,9 @@ $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) +$(TIMER_TEST): $(TESTTIMER_OBJS) + $(CXX) -o $@ $^ $(TESTTIMER_LIBS) + $(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) @@ -74,4 +83,4 @@ $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) clean: rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) *.o - rm -f $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) + rm -f $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(TIMER_TEST) diff --git a/metrics/timer.cc b/metrics/timer.cc new file mode 100644 index 000000000..67b0fd14b --- /dev/null +++ b/metrics/timer.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "timer.h" + +#include + +#include +#include + +#include "metrics_library.h" + +namespace chromeos_metrics { + +base::TimeTicks ClockWrapper::GetCurrentTime() const { + return base::TimeTicks::Now(); +} + +Timer::Timer() + : is_started_(false), + clock_wrapper_(new ClockWrapper()) {} + +bool Timer::Start() { + start_time_ = clock_wrapper_->GetCurrentTime(); + is_started_ = true; + return true; +} + +bool Timer::Stop() { + // Check if the timer has been started. + if (!is_started_) return false; + is_started_ = false; + elapsed_time_ = clock_wrapper_->GetCurrentTime() - start_time_; + return true; +} + +bool Timer::Reset() { + is_started_ = false; + return true; +} + +bool Timer::HasStarted() const { + return is_started_; +} + +bool Timer::GetElapsedTime(base::TimeDelta* elapsed_time) const { + if (start_time_.is_null() || !elapsed_time) return false; + if (is_started_) { + *elapsed_time = clock_wrapper_->GetCurrentTime() - start_time_; + } else { + *elapsed_time = elapsed_time_; + } + return true; +} + +// static +MetricsLibraryInterface* TimerReporter::metrics_lib_ = NULL; + +TimerReporter::TimerReporter(const std::string& histogram_name, int min, + int max, int num_buckets) + : histogram_name_(histogram_name), + min_(min), + max_(max), + num_buckets_(num_buckets) {} + +bool TimerReporter::ReportMilliseconds() const { + base::TimeDelta elapsed_time; + if (!metrics_lib_ || !GetElapsedTime(&elapsed_time)) return false; + return metrics_lib_->SendToUMA(histogram_name_, + elapsed_time.InMilliseconds(), + min_, + max_, + num_buckets_); +} + +} // namespace chromeos_metrics diff --git a/metrics/timer.h b/metrics/timer.h new file mode 100644 index 000000000..1d3197672 --- /dev/null +++ b/metrics/timer.h @@ -0,0 +1,140 @@ +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Timer - class that provides timer tracking. + +#ifndef METRICS_TIMER_H_ +#define METRICS_TIMER_H_ + +#include + +#include +#include +#include // for FRIEND_TEST + +class MetricsLibraryInterface; + +namespace chromeos_metrics { + +class TimerInterface { + public: + virtual ~TimerInterface() {} + + virtual bool Start() = 0; + virtual bool Stop() = 0; + virtual bool Reset() = 0; + virtual bool HasStarted() const = 0; +}; + +// Wrapper for calls to the system clock. +class ClockWrapper { + public: + ClockWrapper() {} + virtual ~ClockWrapper() {} + + // Returns the current time from the system. + virtual base::TimeTicks GetCurrentTime() const; + + private: + DISALLOW_COPY_AND_ASSIGN(ClockWrapper); +}; + +// Implements a Timer. +class Timer : public TimerInterface { + public: + Timer(); + virtual ~Timer() {} + + // Starts the timer. If a timer is already running, also resets current + // timer. Always returns true. + virtual bool Start(); + + // Stops the timer and calculates the total time elapsed between now and when + // Start() was called. Note that this method needs a prior call to Start(). + // Otherwise, it fails (returns false). + virtual bool Stop(); + + // Resets the timer, erasing the current duration being tracked. Always + // returns true. + virtual bool Reset(); + + // Returns whether the timer has started or not. + virtual bool HasStarted() const; + + // Stores the current elapsed time in |elapsed_time|. If timer is stopped, + // stores the elapsed time from when Stop() was last called. Otherwise, + // calculates and stores the elapsed time since the last Start(). + // Returns false if the timer was never Start()'ed or if called with a null + // pointer argument. + virtual bool GetElapsedTime(base::TimeDelta* elapsed_time) const; + + private: + friend class TimerTest; + friend class TimerReporterTest; + FRIEND_TEST(TimerTest, StartStop); + FRIEND_TEST(TimerTest, ReStart); + FRIEND_TEST(TimerTest, Reset); + FRIEND_TEST(TimerTest, SeparatedTimers); + FRIEND_TEST(TimerTest, InvalidStop); + FRIEND_TEST(TimerTest, InvalidElapsedTime); + FRIEND_TEST(TimerReporterTest, StartStopReport); + + // Elapsed time of the last use of the timer. + base::TimeDelta elapsed_time_; + + // Starting time value. + base::TimeTicks start_time_; + + // Whether the timer has started or not. + bool is_started_; + + // Wrapper for the calls to the system clock. + scoped_ptr clock_wrapper_; + + DISALLOW_COPY_AND_ASSIGN(Timer); +}; + +// Extends the Timer class to report the elapsed time in milliseconds through +// the UMA metrics library. +class TimerReporter : public Timer { + public: + // Initializes the timer by providing a |histogram_name| to report to with + // |min|, |max| and |num_buckets| attributes for the histogram. + TimerReporter(const std::string& histogram_name, int min, int max, + int num_buckets); + virtual ~TimerReporter() {} + + // Sets the metrics library used by all instances of this class. + static void set_metrics_lib(MetricsLibraryInterface* metrics_lib) { + metrics_lib_ = metrics_lib; + } + + // Reports the current duration to UMA, in milliseconds. Returns false if + // there is nothing to report, i.e. the timer was not started or a metrics + // library is not set. + virtual bool ReportMilliseconds() const; + + // Accessor methods. + const std::string& histogram_name() const { return histogram_name_; } + int min() const { return min_; } + int max() const { return max_; } + int num_buckets() const { return num_buckets_; } + + private: + friend class TimerReporterTest; + FRIEND_TEST(TimerReporterTest, StartStopReport); + FRIEND_TEST(TimerReporterTest, InvalidReport); + + static MetricsLibraryInterface* metrics_lib_; + std::string histogram_name_; + int min_; + int max_; + int num_buckets_; + + DISALLOW_COPY_AND_ASSIGN(TimerReporter); +}; + +} // namespace chromeos_metrics + +#endif // METRICS_TIMER_H_ diff --git a/metrics/timer_mock.h b/metrics/timer_mock.h new file mode 100644 index 000000000..ef29191a2 --- /dev/null +++ b/metrics/timer_mock.h @@ -0,0 +1,45 @@ +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_TIMER_MOCK_H_ +#define METRICS_TIMER_MOCK_H_ + + +#include + +#include +#include +#include + +#include "timer.h" + +namespace chromeos_metrics { + +class TimerMock : public Timer { + public: + MOCK_METHOD0(Start, bool()); + MOCK_METHOD0(Stop, bool()); + MOCK_METHOD0(Reset, bool()); + MOCK_CONST_METHOD0(HasStarted, bool()); + MOCK_CONST_METHOD1(GetElapsedTime, bool(base::TimeDelta* elapsed_time)); +}; + +class TimerReporterMock : public TimerReporter { + public: + MOCK_METHOD0(Start, bool()); + MOCK_METHOD0(Stop, bool()); + MOCK_METHOD0(Reset, bool()); + MOCK_CONST_METHOD0(HasStarted, bool()); + MOCK_CONST_METHOD0(GetElapsedTime, base::TimeDelta()); + MOCK_CONST_METHOD0(ReportMilliseconds, bool()); +}; + +class ClockWrapperMock : public ClockWrapper { + public: + MOCK_CONST_METHOD0(GetCurrentTime, base::TimeTicks()); +}; + +} // namespace chromeos_metrics + +#endif // METRICS_TIMER_MOCK_H_ diff --git a/metrics/timer_test.cc b/metrics/timer_test.cc new file mode 100644 index 000000000..445aeeb0e --- /dev/null +++ b/metrics/timer_test.cc @@ -0,0 +1,162 @@ +// Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include + +#include "metrics_library_mock.h" +#include "timer.h" +#include "timer_mock.h" + +using ::testing::_; +using ::testing::Return; + +namespace chromeos_metrics { + +class TimerTest : public testing::Test { + public: + TimerTest() : clock_wrapper_mock_(new ClockWrapperMock()) {} + + protected: + virtual void SetUp() { + EXPECT_FALSE(timer_.is_started_); + stime += base::TimeDelta::FromMilliseconds(1500); + etime += base::TimeDelta::FromMilliseconds(3000); + } + + virtual void TearDown() {} + + Timer timer_; + scoped_ptr clock_wrapper_mock_; + base::TimeTicks stime, etime; +}; + +TEST_F(TimerTest, StartStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 1500); + ASSERT_FALSE(timer_.HasStarted()); +} + +TEST_F(TimerTest, ReStart) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + timer_.Start(); + base::TimeTicks buffer = timer_.start_time_; + timer_.Start(); + ASSERT_FALSE(timer_.start_time_ == buffer); +} + +TEST_F(TimerTest, Reset) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + timer_.Start(); + ASSERT_TRUE(timer_.Reset()); + ASSERT_FALSE(timer_.HasStarted()); +} + +TEST_F(TimerTest, SeparatedTimers) { + base::TimeTicks stime2, etime2; + stime2 += base::TimeDelta::FromMilliseconds(4200); + etime2 += base::TimeDelta::FromMilliseconds(5000); + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)) + .WillOnce(Return(stime2)) + .WillOnce(Return(etime2)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 1500); + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.start_time_ == stime2); + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 800); + ASSERT_FALSE(timer_.HasStarted()); +} + +TEST_F(TimerTest, InvalidStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_FALSE(timer_.Stop()); + // Now we try it again, but after a valid start/stop. + timer_.Start(); + timer_.Stop(); + base::TimeDelta elapsed_time = timer_.elapsed_time_; + ASSERT_FALSE(timer_.Stop()); + ASSERT_TRUE(elapsed_time == timer_.elapsed_time_); +} + +TEST_F(TimerTest, InvalidElapsedTime) { + base::TimeDelta elapsed_time; + ASSERT_FALSE(timer_.GetElapsedTime(&elapsed_time)); +} + +static const char kMetricName[] = "test-timer"; +static const int kMinSample = 0; +static const int kMaxSample = 120 * 1E6; +static const int kNumBuckets = 50; + +class TimerReporterTest : public testing::Test { + public: + TimerReporterTest() : timer_reporter_(kMetricName, kMinSample, kMaxSample, + kNumBuckets), + clock_wrapper_mock_(new ClockWrapperMock()) {} + + protected: + virtual void SetUp() { + timer_reporter_.set_metrics_lib(&lib_); + EXPECT_EQ(timer_reporter_.histogram_name_, kMetricName); + EXPECT_EQ(timer_reporter_.min_, kMinSample); + EXPECT_EQ(timer_reporter_.max_, kMaxSample); + EXPECT_EQ(timer_reporter_.num_buckets_, kNumBuckets); + stime += base::TimeDelta::FromMilliseconds(1500); + etime += base::TimeDelta::FromMilliseconds(3000); + } + + virtual void TearDown() { + timer_reporter_.set_metrics_lib(NULL); + } + + TimerReporter timer_reporter_; + MetricsLibraryMock lib_; + scoped_ptr clock_wrapper_mock_; + base::TimeTicks stime, etime; +}; + +TEST_F(TimerReporterTest, StartStopReport) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)); + timer_reporter_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + EXPECT_CALL(lib_, SendToUMA(kMetricName, 1500, kMinSample, kMaxSample, + kNumBuckets)).WillOnce(Return(true)); + ASSERT_TRUE(timer_reporter_.Start()); + ASSERT_TRUE(timer_reporter_.Stop()); + ASSERT_TRUE(timer_reporter_.ReportMilliseconds()); +} + +TEST_F(TimerReporterTest, InvalidReport) { + ASSERT_FALSE(timer_reporter_.ReportMilliseconds()); +} + +} // namespace chromeos_metrics + +int main(int argc, char **argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From e3bdf0f5596602809185ec3ee0321b2eb8144693 Mon Sep 17 00:00:00 2001 From: Bruno Rocha Date: Thu, 18 Aug 2011 11:28:30 -0700 Subject: [PATCH 068/200] Fixed wrong method signatures in TimerReporterMock. TimerReporterMock had some wrong method signatures, and was lacking signatures for its accessor methods. BUG=chromium-os:18800 TEST=None Change-Id: I08d832857c1ae3df907f5470433117f7a03fe125 Reviewed-on: http://gerrit.chromium.org/gerrit/6230 Reviewed-by: Gaurav Shah Reviewed-by: Bruno Pontes Soares Rocha Tested-by: Bruno Pontes Soares Rocha --- metrics/timer_mock.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/metrics/timer_mock.h b/metrics/timer_mock.h index ef29191a2..1f0c9de60 100644 --- a/metrics/timer_mock.h +++ b/metrics/timer_mock.h @@ -27,12 +27,17 @@ class TimerMock : public Timer { class TimerReporterMock : public TimerReporter { public: + TimerReporterMock() : TimerReporter("",0,0,0) {} MOCK_METHOD0(Start, bool()); MOCK_METHOD0(Stop, bool()); MOCK_METHOD0(Reset, bool()); MOCK_CONST_METHOD0(HasStarted, bool()); - MOCK_CONST_METHOD0(GetElapsedTime, base::TimeDelta()); + MOCK_CONST_METHOD1(GetElapsedTime, bool(base::TimeDelta* elapsed_time)); MOCK_CONST_METHOD0(ReportMilliseconds, bool()); + MOCK_CONST_METHOD0(histogram_name, std::string&()); + MOCK_CONST_METHOD0(min, int()); + MOCK_CONST_METHOD0(max, int()); + MOCK_CONST_METHOD0(num_buckets, int()); }; class ClockWrapperMock : public ClockWrapper { From 5bd764faf30e33e5e90a9c633d96f83739b26693 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 14 Oct 2011 12:03:35 -0700 Subject: [PATCH 069/200] Add page fault statistics. BUG=chromium-os:20624 TEST=observe that Platform.PageFaultXXX appear in about:histograms. Change-Id: Ifc281d31e05102dc4133d133f732b737e19891f1 Reviewed-on: http://gerrit.chromium.org/gerrit/10143 Commit-Ready: Luigi Semenzato Tested-by: Luigi Semenzato Reviewed-by: Darin Petkov --- metrics/metrics_daemon.cc | 241 +++++++++++++++++++++++---------- metrics/metrics_daemon.h | 70 ++++++---- metrics/metrics_daemon_main.cc | 5 +- metrics/metrics_daemon_test.cc | 17 ++- 4 files changed, 228 insertions(+), 105 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 7ce28580a..1684f814f 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -105,8 +105,8 @@ const char MetricsDaemon::kMetricReadSectorsShortName[] = const char MetricsDaemon::kMetricWriteSectorsShortName[] = "Platform.WriteSectorsShort"; -const int MetricsDaemon::kMetricDiskStatsShortInterval = 1; // seconds -const int MetricsDaemon::kMetricDiskStatsLongInterval = 30; // seconds +const int MetricsDaemon::kMetricStatsShortInterval = 1; // seconds +const int MetricsDaemon::kMetricStatsLongInterval = 30; // seconds const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds @@ -114,6 +114,17 @@ const int MetricsDaemon::kMetricMeminfoInterval = 30; // seconds // sectors. const int MetricsDaemon::kMetricSectorsIOMax = 500000; // sectors/second const int MetricsDaemon::kMetricSectorsBuckets = 50; // buckets +// Page size is 4k, sector size is 0.5k. We're not interested in page fault +// rates that the disk cannot sustain. +const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8; +const int MetricsDaemon::kMetricPageFaultsBuckets = 50; + +// Major page faults, i.e. the ones that require data to be read from disk. + +const char MetricsDaemon::kMetricPageFaultsLongName[] = + "Platform.PageFaultsLong"; +const char MetricsDaemon::kMetricPageFaultsShortName[] = + "Platform.PageFaultsShort"; // persistent metrics path const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; @@ -171,8 +182,9 @@ MetricsDaemon::MetricsDaemon() memuse_interval_index_(0), read_sectors_(0), write_sectors_(0), - diskstats_state_(kDiskStatsShort), - diskstats_initial_time_(0) {} + page_faults_(0), + stats_state_(kStatsShort), + stats_initial_time_(0) {} MetricsDaemon::~MetricsDaemon() { DeleteFrequencyCounters(); @@ -251,7 +263,8 @@ void MetricsDaemon::ConfigureCrashFrequencyReporter( } void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, - const string& diskstats_path) { + const string& diskstats_path, + const string& vmstats_path) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; @@ -279,11 +292,9 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); - // Don't attempt to collect disk stats if there is no disk stats file. - if (!diskstats_path.empty()) { - diskstats_path_ = diskstats_path; - DiskStatsReporterInit(); - } + diskstats_path_ = diskstats_path; + vmstats_path_ = vmstats_path; + StatsReporterInit(); // Start collecting meminfo stats. ScheduleMeminfoCallback(kMetricMeminfoInterval); @@ -564,112 +575,198 @@ void MetricsDaemon::UnscheduleUseMonitor() { usemon_interval_ = 0; } -void MetricsDaemon::DiskStatsReporterInit() { +void MetricsDaemon::StatsReporterInit() { DiskStatsReadStats(&read_sectors_, &write_sectors_); + VmStatsReadStats(&page_faults_); // The first time around just run the long stat, so we don't delay boot. - diskstats_state_ = kDiskStatsLong; - diskstats_initial_time_ = GetActiveTime(); - if (diskstats_initial_time_ < 0) { + stats_state_ = kStatsLong; + stats_initial_time_ = GetActiveTime(); + if (stats_initial_time_ < 0) { LOG(WARNING) << "not collecting disk stats"; } else { - ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval); + ScheduleStatsCallback(kMetricStatsLongInterval); } } -void MetricsDaemon::ScheduleDiskStatsCallback(int wait) { +void MetricsDaemon::ScheduleStatsCallback(int wait) { if (testing_) { return; } - g_timeout_add_seconds(wait, DiskStatsCallbackStatic, this); + g_timeout_add_seconds(wait, StatsCallbackStatic, this); } -void MetricsDaemon::DiskStatsReadStats(long int* read_sectors, +bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors, long int* write_sectors) { int nchars; int nitems; + bool success = false; char line[200]; + if (diskstats_path_.empty()) { + return false; + } int file = HANDLE_EINTR(open(diskstats_path_.c_str(), O_RDONLY)); if (file < 0) { PLOG(WARNING) << "cannot open " << diskstats_path_; - return; + return false; } nchars = HANDLE_EINTR(read(file, line, sizeof(line))); if (nchars < 0) { PLOG(WARNING) << "cannot read from " << diskstats_path_; + return false; } else { - LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in " - << diskstats_path_; + LOG_IF(WARNING, nchars == sizeof(line)) + << "line too long in " << diskstats_path_; line[nchars] = '\0'; nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld", read_sectors, write_sectors); - LOG_IF(WARNING, nitems != 2) << "found " << nitems << " items in " - << diskstats_path_ << ", expected 2"; + if (nitems == 2) { + success = true; + } else { + LOG(WARNING) << "found " << nitems << " items in " + << diskstats_path_ << ", expected 2"; + } } HANDLE_EINTR(close(file)); + return success; +} + +bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) { + static const char kPageFaultSearchString[] = "\npgmajfault "; + bool success = false; + /* Each line in the file has the form + * + * for instance: + * nr_free_pages 213427 + */ + char* s = strstr(stats, kPageFaultSearchString); + if (s == NULL) { + LOG(WARNING) << "cannot find page fault entry in vmstats"; + } else { + char* endp; + /* Skip and space. Don't count the terminating null. */ + s += sizeof(kPageFaultSearchString) - 1; + *page_faults = strtol(s, &endp, 10); + if (*endp == '\n') { + success = true; + } else { + LOG(WARNING) << "error parsing vmstats"; + } + } + return success; +} + +bool MetricsDaemon::VmStatsReadStats(long int* page_faults) { + char buffer[4000]; + int nchars; + int success = false; + if (testing_) { + return false; + } + int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY)); + if (file < 0) { + PLOG(WARNING) << "cannot open " << vmstats_path_; + return false; + } + nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1)); + LOG_IF(WARNING, nchars == sizeof(buffer) - 1) + << "file too large in " << vmstats_path_; + if (nchars < 0) { + PLOG(WARNING) << "cannot read from " << vmstats_path_; + } else if (nchars == 0) { + LOG(WARNING) << vmstats_path_ << " is empty"; + } else { + buffer[nchars] = '\0'; + success = VmStatsParseStats(buffer, page_faults); + } + HANDLE_EINTR(close(file)); + return success; } // static -gboolean MetricsDaemon::DiskStatsCallbackStatic(void* handle) { - (static_cast(handle))->DiskStatsCallback(); +gboolean MetricsDaemon::StatsCallbackStatic(void* handle) { + (static_cast(handle))->StatsCallback(); return false; // one-time callback } -// Collects disk stats alternating over a short and a long interval. +// Collects disk and vm stats alternating over a short and a long interval. -void MetricsDaemon::DiskStatsCallback() { - long int read_sectors_now, write_sectors_now; +void MetricsDaemon::StatsCallback() { + long int read_sectors_now, write_sectors_now, page_faults_now; double time_now = GetActiveTime(); - double delta_time = time_now - diskstats_initial_time_; + double delta_time = time_now - stats_initial_time_; if (testing_) { // Fake the time when testing. - delta_time = diskstats_state_ == kDiskStatsShort ? - kMetricDiskStatsShortInterval : kMetricDiskStatsLongInterval; + delta_time = stats_state_ == kStatsShort ? + kMetricStatsShortInterval : kMetricStatsLongInterval; } - DiskStatsReadStats(&read_sectors_now, &write_sectors_now); + bool diskstats_success = DiskStatsReadStats(&read_sectors_now, + &write_sectors_now); int delta_read = read_sectors_now - read_sectors_; int delta_write = write_sectors_now - write_sectors_; int read_sectors_per_second = delta_read / delta_time; int write_sectors_per_second = delta_write / delta_time; + bool vmstats_success = VmStatsReadStats(&page_faults_now); + int delta_faults = page_faults_now - page_faults_; + int page_faults_per_second = delta_faults / delta_time; - switch (diskstats_state_) { - case kDiskStatsShort: - SendMetric(kMetricReadSectorsShortName, - read_sectors_per_second, - 1, - kMetricSectorsIOMax, - kMetricSectorsBuckets); - SendMetric(kMetricWriteSectorsShortName, - write_sectors_per_second, - 1, - kMetricSectorsIOMax, - kMetricSectorsBuckets); + switch (stats_state_) { + case kStatsShort: + if (diskstats_success) { + SendMetric(kMetricReadSectorsShortName, + read_sectors_per_second, + 1, + kMetricSectorsIOMax, + kMetricSectorsBuckets); + SendMetric(kMetricWriteSectorsShortName, + write_sectors_per_second, + 1, + kMetricSectorsIOMax, + kMetricSectorsBuckets); + } + if (vmstats_success) { + SendMetric(kMetricPageFaultsShortName, + page_faults_per_second, + 1, + kMetricPageFaultsMax, + kMetricPageFaultsBuckets); + } // Schedule long callback. - diskstats_state_ = kDiskStatsLong; - ScheduleDiskStatsCallback(kMetricDiskStatsLongInterval - - kMetricDiskStatsShortInterval); + stats_state_ = kStatsLong; + ScheduleStatsCallback(kMetricStatsLongInterval - + kMetricStatsShortInterval); break; - case kDiskStatsLong: - SendMetric(kMetricReadSectorsLongName, - read_sectors_per_second, - 1, - kMetricSectorsIOMax, - kMetricSectorsBuckets); - SendMetric(kMetricWriteSectorsLongName, - write_sectors_per_second, - 1, - kMetricSectorsIOMax, - kMetricSectorsBuckets); - // Reset sector counters. - read_sectors_ = read_sectors_now; - write_sectors_ = write_sectors_now; + case kStatsLong: + if (diskstats_success) { + SendMetric(kMetricReadSectorsLongName, + read_sectors_per_second, + 1, + kMetricSectorsIOMax, + kMetricSectorsBuckets); + SendMetric(kMetricWriteSectorsLongName, + write_sectors_per_second, + 1, + kMetricSectorsIOMax, + kMetricSectorsBuckets); + // Reset sector counters. + read_sectors_ = read_sectors_now; + write_sectors_ = write_sectors_now; + } + if (vmstats_success) { + SendMetric(kMetricPageFaultsLongName, + page_faults_per_second, + 1, + kMetricPageFaultsMax, + kMetricPageFaultsBuckets); + page_faults_ = page_faults_now; + } // Set start time for new cycle. - diskstats_initial_time_ = time_now; + stats_initial_time_ = time_now; // Schedule short callback. - diskstats_state_ = kDiskStatsShort; - ScheduleDiskStatsCallback(kMetricDiskStatsShortInterval); + stats_state_ = kStatsShort; + ScheduleStatsCallback(kMetricStatsShortInterval); break; default: - LOG(FATAL) << "Invalid disk stats state"; + LOG(FATAL) << "Invalid stats state"; } } @@ -685,7 +782,7 @@ gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) { return (static_cast(handle))->MeminfoCallback(); } -gboolean MetricsDaemon::MeminfoCallback() { +bool MetricsDaemon::MeminfoCallback() { string meminfo_raw; const FilePath meminfo_path("/proc/meminfo"); if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) { @@ -695,7 +792,7 @@ gboolean MetricsDaemon::MeminfoCallback() { return ProcessMeminfo(meminfo_raw); } -gboolean MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { +bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { static const MeminfoRecord fields_array[] = { { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory { "MemFree", "MemFree" }, @@ -747,8 +844,8 @@ gboolean MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { return true; } -gboolean MetricsDaemon::FillMeminfo(const string& meminfo_raw, - vector* fields) { +bool MetricsDaemon::FillMeminfo(const string& meminfo_raw, + vector* fields) { vector lines; unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines); @@ -782,7 +879,7 @@ gboolean MetricsDaemon::FillMeminfo(const string& meminfo_raw, return true; } -void MetricsDaemon::ScheduleMemuseCallback(gboolean new_callback, +void MetricsDaemon::ScheduleMemuseCallback(bool new_callback, double time_elapsed) { if (testing_) { return; @@ -825,7 +922,7 @@ void MetricsDaemon::MemuseCallback() { } } -gboolean MetricsDaemon::MemuseCallbackWork() { +bool MetricsDaemon::MemuseCallbackWork() { string meminfo_raw; const FilePath meminfo_path("/proc/meminfo"); if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) { @@ -835,7 +932,7 @@ gboolean MetricsDaemon::MemuseCallbackWork() { return ProcessMemuse(meminfo_raw); } -gboolean MetricsDaemon::ProcessMemuse(const string& meminfo_raw) { +bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) { static const MeminfoRecord fields_array[] = { { "MemTotal", "MemTotal" }, // SPECIAL CASE: total system memory { "ActiveAnon", "Active(anon)" }, diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 68646bcb2..0197bd163 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -30,7 +30,8 @@ class MetricsDaemon { // Initializes. void Init(bool testing, MetricsLibraryInterface* metrics_lib, - const std::string& diskstats_path); + const std::string& diskstats_path, + const std::string& vmstats_path); // Does all the work. If |run_as_daemon| is true, daemonizes by // forking. @@ -47,6 +48,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); FRIEND_TEST(MetricsDaemonTest, LookupSessionState); FRIEND_TEST(MetricsDaemonTest, MessageFilter); + FRIEND_TEST(MetricsDaemonTest, ParseVmStats); FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash); FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo); @@ -82,9 +84,9 @@ class MetricsDaemon { }; // State for disk stats collector callback. - enum DiskStatsState { - kDiskStatsShort, // short wait before short interval collection - kDiskStatsLong, // final wait before new collection + enum StatsState { + kStatsShort, // short wait before short interval collection + kStatsLong, // final wait before new collection }; // Data record for aggregating daily usage. @@ -133,12 +135,17 @@ class MetricsDaemon { static const char kMetricReadSectorsShortName[]; static const char kMetricWriteSectorsLongName[]; static const char kMetricWriteSectorsShortName[]; - static const int kMetricDiskStatsShortInterval; - static const int kMetricDiskStatsLongInterval; + static const char kMetricPageFaultsShortName[]; + static const char kMetricPageFaultsLongName[]; + static const int kMetricStatsShortInterval; + static const int kMetricStatsLongInterval; static const int kMetricMeminfoInterval; static const int kMetricSectorsIOMax; static const int kMetricSectorsBuckets; + static const int kMetricPageFaultsMax; + static const int kMetricPageFaultsBuckets; static const char kMetricsDiskStatsPath[]; + static const char kMetricsVmStatsPath[]; // D-Bus message match strings. static const char* kDBusMatches_[]; @@ -254,21 +261,27 @@ class MetricsDaemon { void SendLinearMetric(const std::string& name, int sample, int max, int nbuckets); - // Initializes disk stats reporting. - void DiskStatsReporterInit(); + // Initializes vm and disk stats reporting. + void StatsReporterInit(); - // Schedules a callback for the next disk stats collection. - void ScheduleDiskStatsCallback(int wait); + // Schedules a callback for the next vm and disk stats collection. + void ScheduleStatsCallback(int wait); - // Reads cumulative disk statistics from sysfs. - void DiskStatsReadStats(long int* read_sectors, long int* write_sectors); + // Reads cumulative disk statistics from sysfs. Returns true for success. + bool DiskStatsReadStats(long int* read_sectors, long int* write_sectors); - // Reports disk statistics (static version for glib). Arguments are a glib - // artifact. - static gboolean DiskStatsCallbackStatic(void* handle); + // Reads cumulative vm statistics from procfs. Returns true for success. + bool VmStatsReadStats(long int* page_faults); - // Reports disk statistics. - void DiskStatsCallback(); + // Parse cumulative vm statistics from a C string. Returns true for success. + bool VmStatsParseStats(char* stats, long int* page_faults); + + // Reports disk and vm statistics (static version for glib). Arguments are a + // glib artifact. + static gboolean StatsCallbackStatic(void* handle); + + // Reports disk and vm statistics. + void StatsCallback(); // Schedules meminfo collection callback. void ScheduleMeminfoCallback(int wait); @@ -278,26 +291,26 @@ class MetricsDaemon { static gboolean MeminfoCallbackStatic(void* handle); // Reports memory statistics. Returns false on failure. - gboolean MeminfoCallback(); + bool MeminfoCallback(); // Parses content of /proc/meminfo and sends fields of interest to UMA. // Returns false on errors. |meminfo_raw| contains the content of // /proc/meminfo. - gboolean ProcessMeminfo(const std::string& meminfo_raw); + bool ProcessMeminfo(const std::string& meminfo_raw); // Parses meminfo data from |meminfo_raw|. |fields| is a vector containing // the fields of interest. The order of the fields must be the same in which // /proc/meminfo prints them. The result of parsing fields[i] is placed in // fields[i].value. - gboolean FillMeminfo(const std::string& meminfo_raw, - std::vector* fields); + bool FillMeminfo(const std::string& meminfo_raw, + std::vector* fields); // Schedule a memory use callback. |new_callback| is true when this callback // is scheduled for the first time. When |new_callback| is false, // |time_elapsed| is the active (non-sleep) time that has passed between now // and the original callback scheduling time. We use it to reschedule a // callback that fired too early because we slept. - void ScheduleMemuseCallback(gboolean new_callback, double time_elapsed); + void ScheduleMemuseCallback(bool new_callback, double time_elapsed); // Static wrapper for MemuseCallback. Always returns false. static gboolean MemuseCallbackStatic(void* handle); @@ -308,10 +321,10 @@ class MetricsDaemon { void MemuseCallback(); // Reads /proc/meminfo and sends total anonymous memory usage to UMA. - gboolean MemuseCallbackWork(); + bool MemuseCallbackWork(); // Parse meminfo data and send to UMA. - gboolean ProcessMemuse(const std::string& meminfo_raw); + bool ProcessMemuse(const std::string& meminfo_raw); // Test mode. bool testing_; @@ -368,13 +381,16 @@ class MetricsDaemon { // Selects the wait time for the next memory use callback. unsigned int memuse_interval_index_; - // Contains the most recent disk stats. + // Contain the most recent disk and vm cumulative stats. long int read_sectors_; long int write_sectors_; + long int page_faults_; + + StatsState stats_state_; + double stats_initial_time_; - DiskStatsState diskstats_state_; std::string diskstats_path_; - double diskstats_initial_time_; + std::string vmstats_path_; }; #endif // METRICS_DAEMON_H_ diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 1ee061177..d194e5442 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -12,7 +12,8 @@ DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); -// Return the path to the disk stats in the sysfs. +// Returns the path to the disk stats in the sysfs. Returns the null string if +// it cannot find the disk stats file. static const std::string MetricsMainDiskStatsPath() { char dev_path_cstr[PATH_MAX]; @@ -41,6 +42,6 @@ int main(int argc, char** argv) { MetricsLibrary metrics_lib; metrics_lib.Init(); MetricsDaemon daemon; - daemon.Init(false, &metrics_lib, MetricsMainDiskStatsPath()); + daemon.Init(false, &metrics_lib, MetricsMainDiskStatsPath(), "/proc/vmstat"); daemon.Run(FLAGS_daemon); } diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index df95a5d31..d7ce735bb 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -43,6 +43,8 @@ static string kFakeDiskStats[2]; static const int kFakeReadSectors[] = {80000, 100000}; static const int kFakeWriteSectors[] = {3000, 4000}; +static const char kFakeVmStatsPath[] = "fake-vm-stats"; + // This class allows a TimeTicks object to be initialized with seconds // (rather than microseconds) through the protected TimeTicks(int64) // constructor. @@ -69,7 +71,7 @@ class MetricsDaemonTest : public testing::Test { kFakeDiskStats[1] = StringPrintf(kFakeDiskStatsFormat, kFakeReadSectors[1], kFakeWriteSectors[1]); CreateFakeDiskStatsFile(kFakeDiskStats[0].c_str()); - daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath); + daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath, kFakeVmStatsPath); // Check configuration of a few histograms. FrequencyCounter* frequency_counter = @@ -568,15 +570,15 @@ TEST_F(MetricsDaemonTest, ReportDiskStats) { EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]); EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]); - MetricsDaemon::DiskStatsState ds_state = daemon_.diskstats_state_; + MetricsDaemon::StatsState s_state = daemon_.stats_state_; EXPECT_CALL(metrics_lib_, SendToUMA(_, (kFakeReadSectors[1] - kFakeReadSectors[0]) / 30, _, _, _)); EXPECT_CALL(metrics_lib_, SendToUMA(_, (kFakeWriteSectors[1] - kFakeWriteSectors[0]) / 30, _, _, _)); - daemon_.DiskStatsCallback(); - EXPECT_TRUE(ds_state != daemon_.diskstats_state_); + daemon_.StatsCallback(); + EXPECT_TRUE(s_state != daemon_.stats_state_); } TEST_F(MetricsDaemonTest, ProcessMeminfo) { @@ -643,6 +645,13 @@ MemFree: 1000000 kB\n\ EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo)); } +TEST_F(MetricsDaemonTest, ParseVmStats) { + static char kVmStats[] = "foo 100\nbar 200\npgmajfault 42\netcetc 300\n"; + long int page_faults = 0; + EXPECT_TRUE(daemon_.VmStatsParseStats(kVmStats, &page_faults)); + EXPECT_EQ(page_faults, 42); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From df3e4529cacb8da52d7d16c23215f0d8ae9c0d2d Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 14 Oct 2011 15:31:32 -0700 Subject: [PATCH 070/200] Sends metrics to Chrome even when user did not consent to send stats. BUG=chromium-os:21660 TEST=observed that Platform.* stats appear in about:histograms also when consent is turned off. Change-Id: Id70f0cca468ec39fdfe687d89a6f0748a2a07828 Reviewed-on: http://gerrit.chromium.org/gerrit/10147 Commit-Ready: Luigi Semenzato Tested-by: Luigi Semenzato Reviewed-by: Darin Petkov --- metrics/metrics_library.cc | 2 -- metrics/metrics_library_test.cc | 30 +----------------------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 707ea6874..99e0deb9f 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -165,8 +165,6 @@ bool MetricsLibrary::AreMetricsEnabled() { } bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { - if (!AreMetricsEnabled()) - return true; int chrome_fd = HANDLE_EINTR(open(uma_events_file_, O_WRONLY | O_APPEND | O_CREAT, diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 0e58e0190..3a5edcd30 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -190,13 +190,6 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } -TEST_F(MetricsLibraryTest, SendEnumToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); - EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); - EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); -} - TEST_F(MetricsLibraryTest, SendMessageToChrome) { EXPECT_TRUE(lib_.SendMessageToChrome(4, "test")); EXPECT_TRUE(lib_.SendMessageToChrome(7, "content")); @@ -226,13 +219,6 @@ TEST_F(MetricsLibraryTest, SendToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } -TEST_F(MetricsLibraryTest, SendToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); - EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); - EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); -} - TEST_F(MetricsLibraryTest, SendUserActionToUMA) { char buf[100]; const int kLen = 30; @@ -244,14 +230,7 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } -TEST_F(MetricsLibraryTest, SendUserActionToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); - EXPECT_TRUE(lib_.SendUserActionToUMA("SomeOtherKeyPressed")); - EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); -} - -TEST_F(MetricsLibraryTest, SendCrashToUMAEnabled) { +TEST_F(MetricsLibraryTest, SendCrashToUMA) { EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); char exp[100]; int len = sprintf(exp, "%c%c%c%ccrash%ckernel", @@ -262,13 +241,6 @@ TEST_F(MetricsLibraryTest, SendCrashToUMAEnabled) { EXPECT_EQ(0, memcmp(exp, buf, len)); } -TEST_F(MetricsLibraryTest, SendCrashToUMANotEnabled) { - EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) - .WillOnce(SetMetricsPolicy(false)); - EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); - EXPECT_FALSE(file_util::PathExists(kTestUMAEventsFile)); -} - class CMetricsLibraryTest : public testing::Test { protected: virtual void SetUp() { From 8df4995721f92512407c2c9165499a731dec34cc Mon Sep 17 00:00:00 2001 From: Elly Jones Date: Wed, 11 Jan 2012 17:27:01 -0500 Subject: [PATCH 071/200] [metrics] use libchromeos.pc BUG=chromium-os:24959 TEST=build Change-Id: I7038f5173c7b0949cbf88e6a9213d75e96ae142d Signed-off-by: Elly Jones Reviewed-on: https://gerrit.chromium.org/gerrit/14012 Reviewed-by: Mike Frysinger --- metrics/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 072fb048e..46711c8e6 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -5,8 +5,10 @@ # Makefile for metrics utilities -- library, client and daemon # -CCONFIG = $(shell $(PKG_CONFIG) --cflags dbus-1 glib-2.0 dbus-glib-1) -LDCONFIG = $(shell $(PKG_CONFIG) --libs dbus-1 glib-2.0 gthread-2.0 dbus-glib-1) +PC_DEPS = dbus-1 glib-2.0 dbus-glib-1 libchromeos + +CCONFIG = $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) +LDCONFIG = $(shell $(PKG_CONFIG) --libs $(PC_DEPS) gthread-2.0) CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions $(CCONFIG) From f53e106bbb4fddb58076630c4f0e8560a5770c93 Mon Sep 17 00:00:00 2001 From: Scott James Remnant Date: Thu, 12 Jan 2012 17:58:52 -0800 Subject: [PATCH 072/200] Revert "[metrics] use libchromeos.pc" This reverts commit 6ed1d63d859c37e5a3290d4786862d46927aa5c2 Change-Id: If32cadcdb918f66c9cfada8809f2001122a35d69 Reviewed-on: https://gerrit.chromium.org/gerrit/14118 Reviewed-by: Scott James Remnant Tested-by: Scott James Remnant --- metrics/Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 46711c8e6..072fb048e 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -5,10 +5,8 @@ # Makefile for metrics utilities -- library, client and daemon # -PC_DEPS = dbus-1 glib-2.0 dbus-glib-1 libchromeos - -CCONFIG = $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) -LDCONFIG = $(shell $(PKG_CONFIG) --libs $(PC_DEPS) gthread-2.0) +CCONFIG = $(shell $(PKG_CONFIG) --cflags dbus-1 glib-2.0 dbus-glib-1) +LDCONFIG = $(shell $(PKG_CONFIG) --libs dbus-1 glib-2.0 gthread-2.0 dbus-glib-1) CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions $(CCONFIG) From 4abef11971b46dbde07cfc2a8ca213350fc4adce Mon Sep 17 00:00:00 2001 From: Elly Jones Date: Wed, 11 Jan 2012 17:27:01 -0500 Subject: [PATCH 073/200] [metrics] use libchromeos.pc BUG=chromium-os:24959 TEST=build Signed-off-by: Elly Jones Reviewed-on: https://gerrit.chromium.org/gerrit/14012 Reviewed-by: Mike Frysinger (cherry picked from commit 0ea8151d4628ccf500da82206384b8b492a95a0b) Change-Id: I0c51d49cc7182a19b16d3cb123c43e29be890cbe Reviewed-on: https://gerrit.chromium.org/gerrit/14505 Reviewed-by: Kees Cook Commit-Ready: Elly Jones Tested-by: Elly Jones --- metrics/Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 072fb048e..f2baa963a 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -5,8 +5,10 @@ # Makefile for metrics utilities -- library, client and daemon # -CCONFIG = $(shell $(PKG_CONFIG) --cflags dbus-1 glib-2.0 dbus-glib-1) -LDCONFIG = $(shell $(PKG_CONFIG) --libs dbus-1 glib-2.0 gthread-2.0 dbus-glib-1) +PC_DEPS = dbus-1 glib-2.0 dbus-glib-1 libchrome libchromeos + +CCONFIG = $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) +LDCONFIG = $(shell $(PKG_CONFIG) --libs $(PC_DEPS) gthread-2.0) CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions $(CCONFIG) @@ -44,7 +46,7 @@ TESTLIB_OBJS = \ TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 TESTTIMER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 -DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lrt -lbase -lpthread -lgflags \ +DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lpthread -lgflags \ -lglib-2.0 -lrootdev -lpolicy TESTDAEMON_LIBS = -lgmock -lgtest TESTLIB_LIBS = -lgtest -lgmock -lbase -lrt -lpthread -lglib-2.0 From 113535524665aac8e43319fc26b6e3a9ca10d047 Mon Sep 17 00:00:00 2001 From: Eric Shienbrood Date: Wed, 29 Feb 2012 14:52:29 -0500 Subject: [PATCH 074/200] Update metrics_library.h to find scoped_ptr.h in . This is in preparation to the move to a newer libchrome, where scoped_ptr.h no longer appears in base, just in base/memory. BUG=chromium-os:16623 TEST=Do the emerge, and then examine /usr/include/metrics/metrics_library.h Change-Id: I95cd9b52eee216316dd8ea21ecb84101d7a95edf Reviewed-on: https://gerrit.chromium.org/gerrit/17073 Reviewed-by: Darin Petkov Commit-Ready: Eric Shienbrood Reviewed-by: Eric Shienbrood Tested-by: Eric Shienbrood --- metrics/metrics_library.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 456610fad..f8ce6c747 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -8,7 +8,7 @@ #include #include -#include +#include #include // for FRIEND_TEST #include "policy/libpolicy.h" From 8b8dd207944e5cb3fb5d128b23dd77a6992e97a3 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 21 Feb 2012 13:15:09 -0500 Subject: [PATCH 075/200] touchup pkg-config/library handling Make sure we execute pkg-config twice in the ebuild and not once per object compile/link. Fix the order of libraries when linking libmetric -- we need them to come after the objects, and we need to link in libbase since we use symbols from it. (Things happen to be working now because libpolicy accidentally exports some symbols from libbase.) BUG=chromium-os:26658 TEST=`emerge-x86-alex metrics` still works Change-Id: Ic8381ce9cb3e8a6d81d45d613ac66fea8a12b669 Reviewed-on: https://gerrit.chromium.org/gerrit/16290 Reviewed-by: Julian Pastarmov Commit-Ready: Mike Frysinger Tested-by: Mike Frysinger --- metrics/Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index f2baa963a..56226ef42 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -5,10 +5,10 @@ # Makefile for metrics utilities -- library, client and daemon # +PKG_CONFIG ?= pkg-config PC_DEPS = dbus-1 glib-2.0 dbus-glib-1 libchrome libchromeos - -CCONFIG = $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) -LDCONFIG = $(shell $(PKG_CONFIG) --libs $(PC_DEPS) gthread-2.0) +CCONFIG := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) +LDCONFIG := $(shell $(PKG_CONFIG) --libs $(PC_DEPS) gthread-2.0) CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions $(CCONFIG) @@ -50,14 +50,14 @@ DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lpthread -lgflags \ -lglib-2.0 -lrootdev -lpolicy TESTDAEMON_LIBS = -lgmock -lgtest TESTLIB_LIBS = -lgtest -lgmock -lbase -lrt -lpthread -lglib-2.0 -POLICY_LIBS = -lpolicy +POLICY_LIBS := -lpolicy $(shell $(PKG_CONFIG) --libs libchrome) all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(TIMER_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) - $(CXX) $(LDFLAGS) $(POLICY_LIBS) -lrt $^ -o $@ + $(CXX) $(LDFLAGS) $^ -o $@ $(POLICY_LIBS) -lrt $(COUNTER_TEST): $(TESTCOUNTER_OBJS) $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) @@ -75,7 +75,7 @@ $(LIB): $(LIB_OBJS) $(AR) rcs $@ $^ $(SHAREDLIB): $(LIB_OBJS) - $(CXX) $(LDFLAGS) $(POLICY_LIBS) -shared $^ -o $@ + $(CXX) $(LDFLAGS) -shared $^ -o $@ $(POLICY_LIBS) $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) $(CXX) -o $@ $^ $(LDFLAGS) $(POLICY_LIBS) $(TESTLIB_LIBS) From 71ebf98645568101e49cdc35104499abd5207533 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 7 Mar 2012 10:35:29 -0500 Subject: [PATCH 076/200] include base/stringprintf.h directly This code uses StringPrintf, but doesn't include the header. Older versions of libbase would implicitly include it, but newer ones do not. So include it ourselves to fix building with newer versions. BUG=chromium-os:25872 TEST=`emerge-x86-alex metrics` works (with newer and older libbase) Change-Id: I7859f67767958f70853a40e8df4a33a64b36d010 Reviewed-on: https://gerrit.chromium.org/gerrit/17487 Reviewed-by: Darin Petkov Tested-by: Mike Frysinger Commit-Ready: Mike Frysinger --- metrics/metrics_daemon.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 1684f814f..7d564d4db 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "counter.h" From f06d7b1213101d1af9e6d0c3cbd946d1efc9bda8 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 9 Mar 2012 10:59:56 -0500 Subject: [PATCH 077/200] clean up build & convert to SLOT-ed libbase This includes a couple of fixes: - use the right compiler flag names - execute pkg-config twice total, and not once per compile - build against SLOT-ed libbase - drop link info that was just for libbase as that handles things all by itself now without bothering external users BUG=chromium-os:16623 TEST=`emerge-x86-alex metrics` still works TEST=`cros_run_unit_tests --board x86-alex -p metrics` passes Change-Id: I40b9216c9e2a1edef476f9369d524e6a4bf26012 Reviewed-on: https://gerrit.chromium.org/gerrit/17704 Reviewed-by: Luigi Semenzato Reviewed-by: Darin Petkov Commit-Ready: Mike Frysinger Tested-by: Mike Frysinger --- metrics/Makefile | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 56226ef42..0cf8b7eaf 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -1,16 +1,19 @@ -# Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # # Makefile for metrics utilities -- library, client and daemon # +BASE_VER = 85268 PKG_CONFIG ?= pkg-config -PC_DEPS = dbus-1 glib-2.0 dbus-glib-1 libchrome libchromeos -CCONFIG := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) -LDCONFIG := $(shell $(PKG_CONFIG) --libs $(PC_DEPS) gthread-2.0) +PC_DEPS = dbus-1 glib-2.0 dbus-glib-1 \ + libchrome-$(BASE_VER) libchromeos-$(BASE_VER) +PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) +LDLIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) -CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions $(CCONFIG) +CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions +CPPFLAGS += $(PC_CFLAGS) CLIENT = metrics_client DAEMON = metrics_daemon @@ -44,44 +47,42 @@ LIB_OBJS = \ TESTLIB_OBJS = \ metrics_library_test.o -TESTCOUNTER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 -TESTTIMER_LIBS = -lgmock -lgtest -lbase -lrt -lpthread -lglib-2.0 -DAEMON_LDFLAGS = $(LDFLAGS) $(LDCONFIG) -lpthread -lgflags \ - -lglib-2.0 -lrootdev -lpolicy -TESTDAEMON_LIBS = -lgmock -lgtest -TESTLIB_LIBS = -lgtest -lgmock -lbase -lrt -lpthread -lglib-2.0 -POLICY_LIBS := -lpolicy $(shell $(PKG_CONFIG) --libs libchrome) +POLICY_LIBS = -lpolicy-$(BASE_VER) +TEST_LIBS = -lgmock -lgtest +DAEMON_LIBS = -lgflags -lrootdev + +LINK = $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(TIMER_TEST) $(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) - $(CXX) $(LDFLAGS) $^ -o $@ $(POLICY_LIBS) -lrt + $(LINK) $(COUNTER_TEST): $(TESTCOUNTER_OBJS) - $(CXX) -o $@ $^ $(TESTCOUNTER_LIBS) + $(LINK) $(TEST_LIBS) $(TIMER_TEST): $(TESTTIMER_OBJS) - $(CXX) -o $@ $^ $(TESTTIMER_LIBS) + $(LINK) $(TEST_LIBS) $(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) + $(LINK) $(DAEMON_LIBS) $(DAEMON_TEST): $(TESTDAEMON_OBJS) - $(CXX) -o $@ $^ $(DAEMON_LDFLAGS) $(TESTDAEMON_LIBS) + $(LINK) $(DAEMON_LIBS) $(TEST_LIBS) $(LIB): $(LIB_OBJS) $(AR) rcs $@ $^ $(SHAREDLIB): $(LIB_OBJS) - $(CXX) $(LDFLAGS) -shared $^ -o $@ $(POLICY_LIBS) + $(LINK) -shared $(POLICY_LIBS) $(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) - $(CXX) -o $@ $^ $(LDFLAGS) $(POLICY_LIBS) $(TESTLIB_LIBS) + $(LINK) $(POLICY_LIBS) $(TEST_LIBS) %.o: %.cc - $(CXX) $(CXXFLAGS) -c $< -o $@ + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ clean: rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) *.o From f040f1324dcd511c54d2f87d13b37e3f7d806252 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 6 Apr 2012 16:34:09 -0400 Subject: [PATCH 078/200] update to newer libbase, and allow setting via env Other packages have this, and it makes managing the ebuild easier, so let the env set the libbase version to use. BUG=chromium-os:25872 TEST=`emerge-x86-alex metrics` works TEST=`cros_run_unit_tests --board=x86-alex -p metrics` pass TEST=build_images+build_packages for x86-alex boots up Change-Id: I2f417a13f8220d653aa2bcf6c89f519fb4f2e3df Reviewed-on: https://gerrit.chromium.org/gerrit/20198 Reviewed-by: Darin Petkov Commit-Ready: Mike Frysinger Tested-by: Mike Frysinger --- metrics/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/Makefile b/metrics/Makefile index 0cf8b7eaf..c146b02b6 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -5,7 +5,7 @@ # Makefile for metrics utilities -- library, client and daemon # -BASE_VER = 85268 +BASE_VER ?= 125070 PKG_CONFIG ?= pkg-config PC_DEPS = dbus-1 glib-2.0 dbus-glib-1 \ libchrome-$(BASE_VER) libchromeos-$(BASE_VER) From 564c69f4f1d55b95f4593accde37358d8dcac7ec Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Wed, 2 May 2012 14:24:04 -0700 Subject: [PATCH 079/200] Add unisdt.h to make gcc 4.7 compile it BUG=None TEST=compile passed Change-Id: I3c52e0c7b60572058d277c05ea05471327149601 Reviewed-on: https://gerrit.chromium.org/gerrit/21672 Reviewed-by: Darin Petkov Commit-Ready: Yunlian Jiang Reviewed-by: Yunlian Jiang Tested-by: Yunlian Jiang --- metrics/metrics_library.h | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index f8ce6c747..2539f02e5 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -7,6 +7,7 @@ #include #include +#include #include #include // for FRIEND_TEST From 9cd8b8cbab630b7b0ac7c90d5783d755be6aab54 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Thu, 6 Sep 2012 22:28:47 -0700 Subject: [PATCH 080/200] Add missing gthread-2.0 linkage. gthread-2.0 linkage is required for g_thread_init when compiling with glib 2.32.4. BUG=chromium-os:34103 TEST=`emerge metrics` with glib 2.30.2 and 2.32.4 Change-Id: Id6ef58ee568989123fc563af47e2670066d01b93 Reviewed-on: https://gerrit.chromium.org/gerrit/32507 Reviewed-by: Darin Petkov Commit-Ready: Ben Chan Tested-by: Ben Chan --- metrics/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/Makefile b/metrics/Makefile index c146b02b6..54d927e7f 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -7,7 +7,7 @@ BASE_VER ?= 125070 PKG_CONFIG ?= pkg-config -PC_DEPS = dbus-1 glib-2.0 dbus-glib-1 \ +PC_DEPS = dbus-1 glib-2.0 gthread-2.0 dbus-glib-1 \ libchrome-$(BASE_VER) libchromeos-$(BASE_VER) PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) LDLIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) From d605a005d6aa403e73ab148529f355ee5dd37ee9 Mon Sep 17 00:00:00 2001 From: Julian Pastarmov Date: Mon, 4 Feb 2013 17:58:14 +0100 Subject: [PATCH 081/200] Make policy reloads explicit. BUG=chromium-os:38541 TEST=unit tests pass CQ-DEPEND=I20461078ca890c6ec2f81ad5383c06c4d75a64cd Change-Id: I042e9d1cfbefa479ff18c0b1bc170a15cec705bc Reviewed-on: https://gerrit.chromium.org/gerrit/42538 Reviewed-by: Will Drewry Commit-Queue: Julian Pastarmov Tested-by: Julian Pastarmov --- metrics/metrics_library.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 99e0deb9f..ff040e4fc 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -139,8 +139,7 @@ bool MetricsLibrary::AreMetricsEnabled() { if (!policy_provider_.get()) policy_provider_.reset(new policy::PolicyProvider()); - else - policy_provider_->Reload(); + policy_provider_->Reload(); // We initialize with the default value which is false and will be preserved // if the policy is not set. bool enabled = false; From 3ccca065fd626027e51d25d3b2e29eb77fd04084 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 4 Feb 2013 19:50:45 -0800 Subject: [PATCH 082/200] Add meminfo swap metrics. The current metrics don't show how much swap we're using at a given time. We only have indirect measures (page faults rate for instance). We also need to add one type of histogram scale, since compressed swap can exceed total RAM, and all current histograms are on a scale from zero to total RAM. BUG=chromium-os:38583 TEST=stared at the code really hard and compiled it BRANCH=none Change-Id: Icb9dce5efe5dbd78123aad51ffd369cb46721096 Reviewed-on: https://gerrit.chromium.org/gerrit/42598 Reviewed-by: Sonny Rao Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_daemon.cc | 30 +++++++++++++++++++----------- metrics/metrics_daemon.h | 18 ++++++++++++++---- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 7d564d4db..d7c12c42b 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -806,16 +806,16 @@ bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { { "InactiveAnon", "Inactive(anon)" }, { "ActiveFile" , "Active(file)" }, { "InactiveFile", "Inactive(file)" }, - { "Unevictable", "Unevictable", 1 }, + { "Unevictable", "Unevictable", kMeminfoScaleLog }, // { "Mlocked", "Mlocked" }, // { "SwapTotal", "SwapTotal" }, - // { "SwapFree", "SwapFree" }, + { "SwapFree", "SwapFree", kMeminfoScaleLogLarge }, // { "Dirty", "Dirty" }, // { "Writeback", "Writeback" }, { "AnonPages", "AnonPages" }, { "Mapped", "Mapped" }, - { "Shmem", "Shmem", 1 }, - { "Slab", "Slab", 1 }, + { "Shmem", "Shmem", kMeminfoScaleLog }, + { "Slab", "Slab", kMeminfoScaleLog }, // { "SReclaimable", "SReclaimable" }, // { "SUnreclaim", "SUnreclaim" }, }; @@ -833,13 +833,21 @@ bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { // Send all fields retrieved, except total memory. for (unsigned int i = 1; i < fields.size(); i++) { string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name); - if (fields[i].log_scale) { - // report value in kbytes, log scale, 4Gb max - SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100); - } else { - // report value as percent of total memory - int percent = fields[i].value * 100 / total_memory; - SendLinearMetric(metrics_name, percent, 100, 101); + int percent; + switch (fields[i].scale) { + case kMeminfoScalePercent: + // report value as percent of total memory + percent = fields[i].value * 100 / total_memory; + SendLinearMetric(metrics_name, percent, 100, 101); + break; + case kMeminfoScaleLog: + // report value in kbytes, log scale, 4Gb max + SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100); + break; + case kMeminfoScaleLogLarge: + // report value in kbytes, log scale, 8Gb max + SendMetric(metrics_name, fields[i].value, 1, 8 * 1000 * 1000, 100); + break; } } return true; diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 0197bd163..b93a2bcaf 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -97,12 +97,22 @@ class MetricsDaemon { int seconds_; }; + // Type of scale to use for meminfo histograms. For most of them we use + // percent of total RAM, but for some we use absolute numbers, usually in + // megabytes, on a log scale from 0 to 4000, and 0 to 8000 for compressed + // swap (since it can be larger than total RAM). + enum MeminfoScaleType { + kMeminfoScalePercent = 0, + kMeminfoScaleLog, + kMeminfoScaleLogLarge, + }; + // Record for retrieving and reporting values from /proc/meminfo. struct MeminfoRecord { - 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 - int value; // value from /proc/meminfo + const char* name; // print name + const char* match; // string to match in output of /proc/meminfo + MeminfoScaleType scale; // histogram scale selector + int value; // value from /proc/meminfo }; typedef std::map From 942cbabb1cdf05d9d190a47da61ec8f109f8e212 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 12 Feb 2013 13:17:07 -0800 Subject: [PATCH 083/200] Modify swap metrics to be more useful. We need to record the amount of swap used, and correlate it with the total amount of swap. The metrics added are Platform.MeminfoSwapUsed and Platform.MeminfoSwapUsedPercent. BUG=chromium-os:38583 TEST=not really BRANCH=none Change-Id: Iaf26c917e3c4d23f3f58f436047f8dd165177960 Reviewed-on: https://gerrit.chromium.org/gerrit/43123 Tested-by: Luigi Semenzato Reviewed-by: Sonny Rao Commit-Queue: Luigi Semenzato --- metrics/metrics_daemon.cc | 32 +++++++++++++++++++++----------- metrics/metrics_daemon.h | 11 ++++++----- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index d7c12c42b..798d0228d 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -806,16 +806,16 @@ bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { { "InactiveAnon", "Inactive(anon)" }, { "ActiveFile" , "Active(file)" }, { "InactiveFile", "Inactive(file)" }, - { "Unevictable", "Unevictable", kMeminfoScaleLog }, + { "Unevictable", "Unevictable", kMeminfoOp_HistLog }, // { "Mlocked", "Mlocked" }, - // { "SwapTotal", "SwapTotal" }, - { "SwapFree", "SwapFree", kMeminfoScaleLogLarge }, + { "SwapTotal", "SwapTotal", kMeminfoOp_SwapTotal }, + { "SwapFree", "SwapFree", kMeminfoOp_SwapFree }, // { "Dirty", "Dirty" }, // { "Writeback", "Writeback" }, { "AnonPages", "AnonPages" }, { "Mapped", "Mapped" }, - { "Shmem", "Shmem", kMeminfoScaleLog }, - { "Slab", "Slab", kMeminfoScaleLog }, + { "Shmem", "Shmem", kMeminfoOp_HistLog }, + { "Slab", "Slab", kMeminfoOp_HistLog }, // { "SReclaimable", "SReclaimable" }, // { "SUnreclaim", "SUnreclaim" }, }; @@ -830,26 +830,36 @@ bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { LOG(WARNING) << "borked meminfo parser"; return false; } + int swap_total = 0; + int swap_free = 0; // Send all fields retrieved, except total memory. for (unsigned int i = 1; i < fields.size(); i++) { string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name); int percent; - switch (fields[i].scale) { - case kMeminfoScalePercent: + switch (fields[i].op) { + case kMeminfoOp_HistPercent: // report value as percent of total memory percent = fields[i].value * 100 / total_memory; SendLinearMetric(metrics_name, percent, 100, 101); break; - case kMeminfoScaleLog: + case kMeminfoOp_HistLog: // report value in kbytes, log scale, 4Gb max SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100); break; - case kMeminfoScaleLogLarge: - // report value in kbytes, log scale, 8Gb max - SendMetric(metrics_name, fields[i].value, 1, 8 * 1000 * 1000, 100); + case kMeminfoOp_SwapTotal: + swap_total = fields[i].value; + case kMeminfoOp_SwapFree: + swap_free = fields[i].value; break; } } + if (swap_total > 0) { + int swap_used = swap_total - swap_free; + int swap_used_percent = swap_used * 100 / swap_total; + SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100); + SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent, + 100, 101); + } return true; } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index b93a2bcaf..0bebbc726 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -101,17 +101,18 @@ class MetricsDaemon { // percent of total RAM, but for some we use absolute numbers, usually in // megabytes, on a log scale from 0 to 4000, and 0 to 8000 for compressed // swap (since it can be larger than total RAM). - enum MeminfoScaleType { - kMeminfoScalePercent = 0, - kMeminfoScaleLog, - kMeminfoScaleLogLarge, + enum MeminfoOp { + kMeminfoOp_HistPercent = 0, + kMeminfoOp_HistLog, + kMeminfoOp_SwapTotal, + kMeminfoOp_SwapFree, }; // Record for retrieving and reporting values from /proc/meminfo. struct MeminfoRecord { const char* name; // print name const char* match; // string to match in output of /proc/meminfo - MeminfoScaleType scale; // histogram scale selector + MeminfoOp op; // histogram scale selector, or other operator int value; // value from /proc/meminfo }; From e10b548cf5e0f51f0de08e0c984b23b38640e0f9 Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Thu, 14 Feb 2013 12:15:35 -0800 Subject: [PATCH 084/200] [metrics] Update metrics to build against libchrome-180609 base/eintr_wrapper.h moved to base/posix/eintr_wrapper.h CQ-DEPEND=Ib19c1382ab28ae7632728aa672478da2feb3950e BUG=chromium-os:38941 TEST=emerge the metrics package with tests enabled. STATUS=Fixed Change-Id: I5d72d8934930ce394786ee151fd8f390b5caf2e1 Reviewed-on: https://gerrit.chromium.org/gerrit/43297 Tested-by: Chris Masone Reviewed-by: Luigi Semenzato Commit-Queue: Chris Masone --- metrics/Makefile | 2 +- metrics/counter.cc | 3 ++- metrics/counter_test.cc | 2 +- metrics/metrics_library.cc | 4 +++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/metrics/Makefile b/metrics/Makefile index 54d927e7f..0774a24d9 100644 --- a/metrics/Makefile +++ b/metrics/Makefile @@ -5,7 +5,7 @@ # Makefile for metrics utilities -- library, client and daemon # -BASE_VER ?= 125070 +BASE_VER ?= 180609 PKG_CONFIG ?= pkg-config PC_DEPS = dbus-1 glib-2.0 gthread-2.0 dbus-glib-1 \ libchrome-$(BASE_VER) libchromeos-$(BASE_VER) diff --git a/metrics/counter.cc b/metrics/counter.cc index 95d1faf87..9d94597db 100644 --- a/metrics/counter.cc +++ b/metrics/counter.cc @@ -6,8 +6,9 @@ #include -#include #include +#include + #include "metrics_library.h" namespace chromeos_metrics { diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc index 71fefd6c5..73a50db4a 100644 --- a/metrics/counter_test.cc +++ b/metrics/counter_test.cc @@ -4,9 +4,9 @@ #include -#include #include #include +#include #include #include #include diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index ff040e4fc..fe481228e 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -12,7 +12,9 @@ #include #include -#include "base/eintr_wrapper.h" // HANDLE_EINTR macro, no libbase required. +// HANDLE_EINTR macro, no libbase required. +#include + #include "policy/device_policy.h" #define READ_WRITE_ALL_FILE_FLAGS \ From 326842224051fd332b60c8039b7a43aa95f52120 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Wed, 13 Mar 2013 10:53:55 -0700 Subject: [PATCH 085/200] Add generic "event of interest" enumerated histogram. This change aims to simplify the addition of events we may want to track in the field, more specifically hard-to-reproduce bugs. Before this change, this requires creating a new histogram and registering it via histograms.xml, which is not in the Chrome OS repositories. With this change, a new event just requires claiming an event name (such as ModemManagerCommandSendFailure) in metrics_library.cc, and running "metrics_client -v " or calling SendCrosEventToUMA(event_name). I can make up a bug for this. Or not. BUG=none TEST=compiled Change-Id: I9c56b58310f0d22e77624edee7fe6149abd60a49 Reviewed-on: https://gerrit.chromium.org/gerrit/45322 Commit-Queue: Luigi Semenzato Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_client.cc | 28 ++++++++++++++++++++++++++-- metrics/metrics_library.cc | 18 ++++++++++++++++++ metrics/metrics_library.h | 8 ++++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index 86bec731f..6ffc13b8c 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -11,6 +11,7 @@ void ShowUsage() { fprintf(stderr, "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n" " metrics_client [-ab] -e name sample max\n" + " metrics_client [-ab] -v event\n" " metrics_client -u action\n" " metrics_client [-cg]\n" "\n" @@ -22,7 +23,9 @@ void ShowUsage() { " -e: send linear/enumeration histogram data\n" " -g: return exit status 0 if machine in guest mode, 1 otherwise\n" " -t: convert sample from double seconds to int milliseconds\n" - " -u: send a user action to Chrome\n"); + " -u: send a user action to Chrome\n" + " -v: send a Platform.CrOSEvent enum histogram sample\n" + ); exit(1); } @@ -69,6 +72,19 @@ static int SendUserAction(char* argv[], int action_index) { return 0; } +static int SendCrosEvent(char* argv[], int action_index) { + const char* event = argv[action_index]; + bool result; + MetricsLibrary metrics_lib; + metrics_lib.Init(); + result = metrics_lib.SendCrosEventToUMA(event); + if (!result) { + fprintf(stderr, "metrics_client: could not send event %s\n", event); + return 1; + } + return 0; +} + static int HasConsent() { MetricsLibrary metrics_lib; metrics_lib.Init(); @@ -85,6 +101,7 @@ int main(int argc, char** argv) { enum Mode { kModeSendStats, kModeSendUserAction, + kModeSendCrosEvent, kModeHasConsent, kModeIsGuestMode } mode = kModeSendStats; @@ -95,7 +112,7 @@ int main(int argc, char** argv) { // Parse arguments int flag; - while ((flag = getopt(argc, argv, "abcegtu")) != -1) { + while ((flag = getopt(argc, argv, "abcegtuv")) != -1) { switch (flag) { case 'a': mode = kModeSendStats; @@ -122,6 +139,9 @@ int main(int argc, char** argv) { case 'u': mode = kModeSendUserAction; break; + case 'v': + mode = kModeSendCrosEvent; + break; default: ShowUsage(); break; @@ -134,6 +154,8 @@ int main(int argc, char** argv) { expected_args = send_enum ? 3 : 5; else if (mode == kModeSendUserAction) expected_args = 1; + else if (mode == kModeSendCrosEvent) + expected_args = 1; if ((arg_index + expected_args) != argc) { ShowUsage(); @@ -152,6 +174,8 @@ int main(int argc, char** argv) { send_to_chrome); case kModeSendUserAction: return SendUserAction(argv, arg_index); + case kModeSendCrosEvent: + return SendCrosEvent(argv, arg_index); case kModeHasConsent: return HasConsent(); case kModeIsGuestMode: diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index fe481228e..a2227ac1b 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -24,6 +24,8 @@ static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; static const char kUMAEventsPath[] = "/var/log/metrics/uma-events"; static const char kConsentFile[] = "/home/chronos/Consent To Send Stats"; static const int32_t kBufferSize = 1024; +static const char kCrosEventHistogramName[] = "Platform.CrOSEvent"; +static const int kCrosEventHistogramMax = 100; time_t MetricsLibrary::cached_enabled_time_ = 0; bool MetricsLibrary::cached_enabled_ = false; @@ -306,3 +308,19 @@ bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { policy_provider_.reset(provider); } + +bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) { + int n; + /* Add new events here. + * + * Whoever adds the second event (if anybody) please be so kind to change + * this to a map lookup. (Or at least change "second" above to "third", + * etc.) + */ + if (event.compare("ModemManagerCommandSendFailure") == 0) { + n = 0; + } else { + return false; + } + return SendEnumToUMA(kCrosEventHistogramName, n, kCrosEventHistogramMax); +} diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 2539f02e5..ce6985f6c 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -90,6 +90,14 @@ class MetricsLibrary : public MetricsLibraryInterface { // has occurred. Used by UMA to generate stability statistics. bool SendCrashToUMA(const char *crash_kind); + // Sends a "generic Chrome OS event" to UMA. This is an event name + // that is translated into an enumerated histogram entry. Event names + // are added to metrics_library.cc. Optionally, they can be added + // to histograms.xml---but part of the reason for this is to simplify + // the addition of events (at the cost of having to look them up by + // number in the histograms dashboard). + bool SendCrosEventToUMA(const std::string& event); + // Sends to Autotest and returns true on success. static bool SendToAutotest(const std::string& name, int value); From 1bc9ce0294a0107b6d087e73164eb786cc658704 Mon Sep 17 00:00:00 2001 From: repo sync Date: Fri, 22 Mar 2013 17:21:57 -0700 Subject: [PATCH 086/200] metrics: Fixes a comment for |TimerReporter::ReportMilliseconds|. It turns out that |TimerReporter::ReportMilliseconds| does report a value for a timer that hasn't been started (in shill, |Metrics::NotifyDevice*Finished| calls |Stop| immediately before |ReportMilliseconds| and it works just fine. This is perfectly OK, it's just that the comment didn't reflect this. This CL fixes that. BUG=None. TEST=None. Change-Id: I578d554106acdb92957bfe0197622a1054cfa9c9 Reviewed-on: https://gerrit.chromium.org/gerrit/46337 Reviewed-by: Darin Petkov Reviewed-by: Wade Guthrie Tested-by: Wade Guthrie Commit-Queue: Wade Guthrie --- metrics/timer.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metrics/timer.h b/metrics/timer.h index 1d3197672..e80950c1d 100644 --- a/metrics/timer.h +++ b/metrics/timer.h @@ -111,8 +111,7 @@ class TimerReporter : public Timer { } // Reports the current duration to UMA, in milliseconds. Returns false if - // there is nothing to report, i.e. the timer was not started or a metrics - // library is not set. + // there is nothing to report, e.g. a metrics library is not set. virtual bool ReportMilliseconds() const; // Accessor methods. From 4b5164bb9e32ef08de123ddf2ce773434ff48d18 Mon Sep 17 00:00:00 2001 From: Grant Grundler Date: Tue, 26 Mar 2013 17:13:49 -0700 Subject: [PATCH 087/200] metrics: add HwWatchdogReboot to CrosEvents Add HwWatchdogReboot to CrosEvents support. BUG=chromium:221000 TEST=none Change-Id: I7388c6d1ee93a73f930d591552b18ea7bba7743f Signed-off-by: Grant Grundler Reviewed-on: https://gerrit.chromium.org/gerrit/46593 Reviewed-by: Doug Anderson Reviewed-by: Luigi Semenzato --- metrics/metrics_library.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index a2227ac1b..075176fb2 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -319,6 +319,8 @@ bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) { */ if (event.compare("ModemManagerCommandSendFailure") == 0) { n = 0; + } else if (event.compare("HwWatchdogReboot") == 0) { + n = 1; } else { return false; } From 6844c06107726d45c76456c5e8fefe59a123fe79 Mon Sep 17 00:00:00 2001 From: Chih-Chung Chang Date: Mon, 1 Apr 2013 14:27:39 +0800 Subject: [PATCH 088/200] metrics: Add Cras.NoCodecsFoundAtBoot event to CrosEvents Also put event names into an array, so it's easier to add new event names. BUG=none TEST=run "strace -e write=3 metrics_client -v Cras.NoCodecsFoundAtBoot" and verify the event is written. Change-Id: I925b4ff3b6d362e099df62a11905c1df03fc9435 Reviewed-on: https://gerrit.chromium.org/gerrit/46998 Reviewed-by: Chih-Chung Chang Tested-by: Chih-Chung Chang Commit-Queue: Chih-Chung Chang --- metrics/metrics_library.cc | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 075176fb2..1c7c28da7 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -19,6 +19,7 @@ #define READ_WRITE_ALL_FILE_FLAGS \ (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; static const char kUMAEventsPath[] = "/var/log/metrics/uma-events"; @@ -27,6 +28,17 @@ static const int32_t kBufferSize = 1024; static const char kCrosEventHistogramName[] = "Platform.CrOSEvent"; static const int kCrosEventHistogramMax = 100; +/* Add new cros events here. + * + * The index of the event is sent in the message, so please do not + * reorder the names. + */ +static const char *kCrosEventNames[] = { + "ModemManagerCommandSendFailure", // 0 + "HwWatchdogReboot", // 1 + "Cras.NoCodecsFoundAtBoot", // 2 +}; + time_t MetricsLibrary::cached_enabled_time_ = 0; bool MetricsLibrary::cached_enabled_ = false; @@ -310,19 +322,10 @@ void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { } bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) { - int n; - /* Add new events here. - * - * Whoever adds the second event (if anybody) please be so kind to change - * this to a map lookup. (Or at least change "second" above to "third", - * etc.) - */ - if (event.compare("ModemManagerCommandSendFailure") == 0) { - n = 0; - } else if (event.compare("HwWatchdogReboot") == 0) { - n = 1; - } else { - return false; + for (size_t i = 0; i < ARRAY_SIZE(kCrosEventNames); i++) { + if (strcmp(event.c_str(), kCrosEventNames[i]) == 0) { + return SendEnumToUMA(kCrosEventHistogramName, i, kCrosEventHistogramMax); + } } - return SendEnumToUMA(kCrosEventHistogramName, n, kCrosEventHistogramMax); + return false; } From 40f2573a7b0e377b4c78597b226e068f13da20ef Mon Sep 17 00:00:00 2001 From: Darin Petkov Date: Mon, 29 Apr 2013 15:07:31 +0200 Subject: [PATCH 089/200] metrics: Listen to session manager for screen lock/unlock signals. This should fix a pretty broken DailyUseTime metric. Also, use contstants from service_constants.h where available. BUG=chromium:216382,chromium:234799 TEST=unit tests; stop metrics_daemon on the device, then ran metrics_daemon --nodaemon and inspected console log output while logging in/out as well as on suspend/resume. CQ-DEPEND=Ibfcc54c8dbf145cccd5ef871c7c9701b8312eb9e Change-Id: I69ca0ea9594a496453f0933147ec66b0fa334718 Reviewed-on: https://gerrit.chromium.org/gerrit/49468 Tested-by: Darin Petkov Reviewed-by: Chris Masone Commit-Queue: Darin Petkov --- metrics/metrics_daemon.cc | 80 +++++++++++++++++----------------- metrics/metrics_daemon.h | 3 -- metrics/metrics_daemon_test.cc | 19 ++++---- 3 files changed, 49 insertions(+), 53 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 798d0228d..a7865484b 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "counter.h" @@ -26,9 +27,9 @@ using std::vector; #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") -#define DBUS_IFACE_CRASH_REPORTER "org.chromium.CrashReporter" -#define DBUS_IFACE_POWER_MANAGER "org.chromium.PowerManager" -#define DBUS_IFACE_SESSION_MANAGER "org.chromium.SessionManagerInterface" + +static const char kCrashReporterInterface[] = "org.chromium.CrashReporter"; +static const char kCrashReporterUserCrashSignal[] = "UserCrash"; static const int kSecondsPerMinute = 60; static const int kMinutesPerHour = 60; @@ -130,25 +131,6 @@ const char MetricsDaemon::kMetricPageFaultsShortName[] = // persistent metrics path const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; - -// static -const char* MetricsDaemon::kDBusMatches_[] = { - "type='signal'," - "interface='" DBUS_IFACE_CRASH_REPORTER "'," - "path='/'," - "member='UserCrash'", - - "type='signal'," - "interface='" DBUS_IFACE_POWER_MANAGER "'," - "path='/'", - - "type='signal'," - "sender='org.chromium.SessionManager'," - "interface='" DBUS_IFACE_SESSION_MANAGER "'," - "path='/org/chromium/SessionManager'," - "member='SessionStateChanged'", -}; - // static const char* MetricsDaemon::kPowerStates_[] = { #define STATE(name, capname) #name, @@ -318,9 +300,26 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, dbus_connection_setup_with_g_main(connection, NULL); + vector matches; + matches.push_back( + StringPrintf("type='signal',interface='%s',path='/',member='%s'", + kCrashReporterInterface, + kCrashReporterUserCrashSignal)); + matches.push_back( + StringPrintf("type='signal',interface='%s',path='%s',member='%s'", + power_manager::kPowerManagerInterface, + power_manager::kPowerManagerServicePath, + power_manager::kPowerStateChangedSignal)); + matches.push_back( + StringPrintf("type='signal',sender='%s',interface='%s',path='%s'", + login_manager::kSessionManagerServiceName, + login_manager::kSessionManagerInterface, + login_manager::kSessionManagerServicePath)); + // Registers D-Bus matches for the signals we would like to catch. - for (unsigned int m = 0; m < arraysize(kDBusMatches_); m++) { - const char* match = kDBusMatches_[m]; + for (vector::const_iterator it = matches.begin(); + it != matches.end(); ++it) { + const char* match = it->c_str(); DLOG(INFO) << "adding dbus match: " << match; dbus_bus_add_match(connection, match, &error); LOG_IF(FATAL, dbus_error_is_set(&error)) << @@ -359,28 +358,27 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, DBusMessageIter iter; dbus_message_iter_init(message, &iter); - if (strcmp(interface, DBUS_IFACE_CRASH_REPORTER) == 0) { + if (strcmp(interface, kCrashReporterInterface) == 0) { CHECK(strcmp(dbus_message_get_member(message), - "UserCrash") == 0); + kCrashReporterUserCrashSignal) == 0); daemon->ProcessUserCrash(); - } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) { - const char* member = dbus_message_get_member(message); - if (strcmp(member, "ScreenIsLocked") == 0) { - daemon->SetUserActiveState(false, now); - } else if (strcmp(member, "ScreenIsUnlocked") == 0) { - daemon->SetUserActiveState(true, now); - } else if (strcmp(member, "PowerStateChanged") == 0) { - char* state_name; - dbus_message_iter_get_basic(&iter, &state_name); - daemon->PowerStateChanged(state_name, now); - } - } else if (strcmp(interface, DBUS_IFACE_SESSION_MANAGER) == 0) { + } else if (strcmp(interface, power_manager::kPowerManagerInterface) == 0) { CHECK(strcmp(dbus_message_get_member(message), - "SessionStateChanged") == 0); - + power_manager::kPowerStateChangedSignal) == 0); char* state_name; dbus_message_iter_get_basic(&iter, &state_name); - daemon->SessionStateChanged(state_name, now); + daemon->PowerStateChanged(state_name, now); + } else if (strcmp(interface, login_manager::kSessionManagerInterface) == 0) { + const char* member = dbus_message_get_member(message); + if (strcmp(member, login_manager::kScreenIsLockedSignal) == 0) { + daemon->SetUserActiveState(false, now); + } else if (strcmp(member, login_manager::kScreenIsUnlockedSignal) == 0) { + daemon->SetUserActiveState(true, now); + } else if (strcmp(member, login_manager::kSessionStateChangedSignal) == 0) { + char* state_name; + dbus_message_iter_get_basic(&iter, &state_name); + daemon->SessionStateChanged(state_name, now); + } } else { DLOG(WARNING) << "unexpected interface: " << interface; return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 0bebbc726..07eaa01ea 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -158,9 +158,6 @@ class MetricsDaemon { static const char kMetricsDiskStatsPath[]; static const char kMetricsVmStatsPath[]; - // D-Bus message match strings. - static const char* kDBusMatches_[]; - // Array of power states. static const char* kPowerStates_[kNumberPowerStates]; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index d7ce735bb..370634cd0 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -9,6 +9,7 @@ #include #include +#include #include #include "counter_mock.h" @@ -355,9 +356,9 @@ TEST_F(MetricsDaemonTest, MessageFilter) { signal_args.clear(); signal_args.push_back("on"); - msg = NewDBusSignalString("/", - "org.chromium.PowerManager", - "PowerStateChanged", + msg = NewDBusSignalString(power_manager::kPowerManagerServicePath, + power_manager::kPowerManagerInterface, + power_manager::kPowerStateChangedSignal, signal_args); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); @@ -367,9 +368,9 @@ TEST_F(MetricsDaemonTest, MessageFilter) { signal_args.clear(); IgnoreActiveUseUpdate(); - msg = NewDBusSignalString("/", - "org.chromium.PowerManager", - "ScreenIsUnlocked", + msg = NewDBusSignalString(login_manager::kSessionManagerServicePath, + login_manager::kSessionManagerInterface, + login_manager::kScreenIsUnlockedSignal, signal_args); EXPECT_FALSE(daemon_.user_active_); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); @@ -381,9 +382,9 @@ TEST_F(MetricsDaemonTest, MessageFilter) { signal_args.clear(); signal_args.push_back("started"); signal_args.push_back("bob"); // arbitrary username - msg = NewDBusSignalString("/org/chromium/SessionManager", - "org.chromium.SessionManagerInterface", - "SessionStateChanged", + msg = NewDBusSignalString(login_manager::kSessionManagerServicePath, + login_manager::kSessionManagerInterface, + login_manager::kSessionStateChangedSignal, signal_args); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); From a7ebeb3e8b2659e616b77989e4c8964d5188a8ec Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 19 Mar 2013 15:02:42 -0700 Subject: [PATCH 090/200] Add sparse histograms to the metrics library and metrics client. Samples to these histograms are any 32-bit int value. BUG=chromium:222189 TEST=manual Change-Id: Ic8d5773d05d717a275c4a4b5616e0e4c307337b8 Reviewed-on: https://gerrit.chromium.org/gerrit/45897 Tested-by: Luigi Semenzato Reviewed-by: Darin Petkov Commit-Queue: Luigi Semenzato Reviewed-by: Luigi Semenzato --- metrics/c_metrics_library.cc | 8 +++++ metrics/c_metrics_library.h | 4 +++ metrics/metrics_client.cc | 52 +++++++++++++++++++++------------ metrics/metrics_library.cc | 13 +++++++++ metrics/metrics_library.h | 7 +++++ metrics/metrics_library_mock.h | 1 + metrics/metrics_library_test.cc | 11 +++++++ 7 files changed, 77 insertions(+), 19 deletions(-) diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc index 8946699c3..5c7553c2c 100644 --- a/metrics/c_metrics_library.cc +++ b/metrics/c_metrics_library.cc @@ -43,6 +43,14 @@ extern "C" int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, return lib->SendEnumToUMA(std::string(name), sample, max); } +extern "C" int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle, + const char* name, int sample) { + MetricsLibrary* lib = reinterpret_cast(handle); + if (lib == NULL) + return 0; + return lib->SendSparseToUMA(std::string(name), sample); +} + extern "C" int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle, const char* action) { MetricsLibrary* lib = reinterpret_cast(handle); diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h index 5c7003d9e..28ae91600 100644 --- a/metrics/c_metrics_library.h +++ b/metrics/c_metrics_library.h @@ -28,6 +28,10 @@ int CMetricsLibrarySendToUMA(CMetricsLibrary handle, int CMetricsLibrarySendEnumToUMA(CMetricsLibrary handle, const char* name, int sample, int max); +// C wrapper for MetricsLibrary::SendSparseToUMA. +int CMetricsLibrarySendSparseToUMA(CMetricsLibrary handle, + const char* name, int sample); + // C wrapper for MetricsLibrary::SendUserActionToUMA. int CMetricsLibrarySendUserActionToUMA(CMetricsLibrary handle, const char* action); diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index 6ffc13b8c..cdf09a2ca 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -7,10 +7,21 @@ #include "metrics_library.h" +enum Mode { + kModeSendSample, + kModeSendEnumSample, + kModeSendSparseSample, + kModeSendUserAction, + kModeSendCrosEvent, + kModeHasConsent, + kModeIsGuestMode, +}; + void ShowUsage() { fprintf(stderr, "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n" " metrics_client [-ab] -e name sample max\n" + " metrics_client [-ab] -s name sample\n" " metrics_client [-ab] -v event\n" " metrics_client -u action\n" " metrics_client [-cg]\n" @@ -22,6 +33,7 @@ void ShowUsage() { " -c: return exit status 0 if user consents to stats, 1 otherwise\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" " -t: convert sample from double seconds to int milliseconds\n" " -u: send a user action to Chrome\n" " -v: send a Platform.CrOSEvent enum histogram sample\n" @@ -31,7 +43,7 @@ void ShowUsage() { static int SendStats(char* argv[], int name_index, - bool send_enum, + enum Mode mode, bool secs_to_msecs, bool send_to_autotest, bool send_to_chrome) { @@ -51,7 +63,9 @@ static int SendStats(char* argv[], if (send_to_chrome) { MetricsLibrary metrics_lib; metrics_lib.Init(); - if (send_enum) { + if (mode == kModeSendSparseSample) { + metrics_lib.SendSparseToUMA(name, sample); + } else if (mode == kModeSendEnumSample) { int max = atoi(argv[name_index + 2]); metrics_lib.SendEnumToUMA(name, sample, max); } else { @@ -98,29 +112,20 @@ static int IsGuestMode() { } int main(int argc, char** argv) { - enum Mode { - kModeSendStats, - kModeSendUserAction, - kModeSendCrosEvent, - kModeHasConsent, - kModeIsGuestMode - } mode = kModeSendStats; + enum Mode mode = kModeSendSample; bool send_to_autotest = false; bool send_to_chrome = true; - bool send_enum = false; bool secs_to_msecs = false; // Parse arguments int flag; - while ((flag = getopt(argc, argv, "abcegtuv")) != -1) { + while ((flag = getopt(argc, argv, "abcegstuv")) != -1) { switch (flag) { case 'a': - mode = kModeSendStats; send_to_autotest = true; send_to_chrome = false; break; case 'b': - mode = kModeSendStats; send_to_chrome = true; send_to_autotest = true; break; @@ -128,11 +133,14 @@ int main(int argc, char** argv) { mode = kModeHasConsent; break; case 'e': - send_enum = true; + mode = kModeSendEnumSample; break; case 'g': mode = kModeIsGuestMode; break; + case 's': + mode = kModeSendSparseSample; + break; case 't': secs_to_msecs = true; break; @@ -150,8 +158,12 @@ int main(int argc, char** argv) { int arg_index = optind; int expected_args = 0; - if (mode == kModeSendStats) - expected_args = send_enum ? 3 : 5; + if (mode == kModeSendSample) + expected_args = 5; + else if (mode == kModeSendEnumSample) + expected_args = 3; + else if (mode == kModeSendSparseSample) + expected_args = 2; else if (mode == kModeSendUserAction) expected_args = 1; else if (mode == kModeSendCrosEvent) @@ -162,13 +174,15 @@ int main(int argc, char** argv) { } switch(mode) { - case kModeSendStats: - if (send_enum && secs_to_msecs) { + case kModeSendSample: + case kModeSendEnumSample: + case kModeSendSparseSample: + if ((mode != kModeSendSample) && secs_to_msecs) { ShowUsage(); } return SendStats(argv, arg_index, - send_enum, + mode, secs_to_msecs, send_to_autotest, send_to_chrome); diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 1c7c28da7..e5aaae6db 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -290,6 +290,19 @@ bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, return SendMessageToChrome(message_length, message); } +bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) { + // Format the message. + char message[kBufferSize]; + int32_t message_length = + FormatChromeMessage(kBufferSize, message, "sparsehistogram%c%s %d", + '\0', name.c_str(), sample); + if (message_length < 0) + return false; + + // Send the message. + return SendMessageToChrome(message_length, message); +} + bool MetricsLibrary::SendUserActionToUMA(const std::string& action) { // Format the message. char message[kBufferSize]; diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index ce6985f6c..9a4c59bde 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -20,6 +20,7 @@ class MetricsLibraryInterface { virtual bool SendToUMA(const std::string& name, int sample, int min, int max, int nbuckets) = 0; virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0; + virtual bool SendSparseToUMA(const std::string& name, int sample) = 0; virtual bool SendUserActionToUMA(const std::string& action) = 0; virtual ~MetricsLibraryInterface() {} }; @@ -74,6 +75,12 @@ class MetricsLibrary : public MetricsLibraryInterface { // normal, while 100 is high). bool SendEnumToUMA(const std::string& name, int sample, int max); + // Sends sparse histogram sample to Chrome for transport to UMA. Returns + // true on success. + // + // |sample| is the 32-bit integer value to be recorded. + bool SendSparseToUMA(const std::string& name, int sample); + // Sends a user action to Chrome for transport to UMA and returns true on // success. This method results in the equivalent of an asynchronous // non-blocking RPC to UserMetrics::RecordAction. The new metric must be diff --git a/metrics/metrics_library_mock.h b/metrics/metrics_library_mock.h index 9a5d59e63..537f4e47b 100644 --- a/metrics/metrics_library_mock.h +++ b/metrics/metrics_library_mock.h @@ -18,6 +18,7 @@ class MetricsLibraryMock : public MetricsLibraryInterface { int min, int max, int nbuckets)); MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample, int max)); + MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample)); MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action)); }; diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 3a5edcd30..03ccd4b7b 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -230,6 +230,17 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMA) { EXPECT_EQ(0, memcmp(exp, buf, kLen)); } +TEST_F(MetricsLibraryTest, SendSparseToUMA) { + char buf[100]; + const int kLen = 4 + sizeof("sparsehistogram") + sizeof("Test.Sparse 1234"); + EXPECT_TRUE(lib_.SendSparseToUMA("Test.Sparse", 1234)); + EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + + char exp[kLen]; + sprintf(exp, "%c%c%c%csparsehistogram%cTest.Sparse 1234", kLen, 0, 0, 0, 0); + EXPECT_EQ(0, memcmp(exp, buf, kLen)); +} + TEST_F(MetricsLibraryTest, SendCrashToUMA) { EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); char exp[100]; From 3ebadf8785557efb6f1130c12df25eee454cba51 Mon Sep 17 00:00:00 2001 From: Liam McLoughlin Date: Tue, 1 Jan 2013 22:32:25 -0500 Subject: [PATCH 091/200] Add metrics GYP file BUG=chromium:220003 TEST=Run emerge- -v platform2 with and without platform2 USE flag on a range of boards plus the host Change-Id: I44f15c31a8ac3917f3262912c43419a93652c0d8 Reviewed-on: https://gerrit.chromium.org/gerrit/40316 Tested-by: Liam McLoughlin Reviewed-by: Mike Frysinger Commit-Queue: Liam McLoughlin --- metrics/metrics.gyp | 113 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 metrics/metrics.gyp diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp new file mode 100644 index 000000000..b2644ba23 --- /dev/null +++ b/metrics/metrics.gyp @@ -0,0 +1,113 @@ +{ + 'target_defaults': { + 'dependencies': [ + '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + ], + 'variables': { + 'deps': [ + 'dbus-1', + 'dbus-glib-1', + 'glib-2.0', + 'gobject-2.0', + 'gthread-2.0', + 'libchrome-<(libbase_ver)', + ] + }, + 'cflags_cc': [ + '-fno-exceptions', + ], + }, + 'targets': [ + { + 'target_name': 'metrics', + 'type': 'static_library', + 'sources': [ + 'c_metrics_library.cc', + 'metrics_library.cc', + 'timer.cc', + ], + }, + { + 'target_name': 'libmetrics', + 'type': 'shared_library', + 'sources': [ + 'c_metrics_library.cc', + 'metrics_library.cc', + 'timer.cc', + ], + }, + { + 'target_name': 'libmetrics_daemon', + 'type': 'static_library', + 'dependencies': ['libmetrics'], + 'link_settings': { + 'libraries': [ + '-lrootdev', + '-lgflags', + ], + }, + 'sources': [ + 'counter.cc', + 'metrics_daemon.cc', + 'metrics_daemon_main.cc', + ] + }, + { + 'target_name': 'metrics_daemon', + 'type': 'executable', + 'dependencies': ['libmetrics_daemon'], + }, + { + 'target_name': 'metrics_client', + 'type': 'executable', + 'dependencies': ['libmetrics'], + 'sources': [ + 'metrics_client.cc', + ] + }, + ], + 'conditions': [ + ['USE_test == 1', { + 'targets': [ + { + 'target_name': 'metrics_library_test', + 'type': 'executable', + 'dependencies': ['libmetrics'], + 'includes': ['../common-mk/common_test.gypi'], + 'sources': [ + 'metrics_library_test.cc', + ] + }, + { + 'target_name': 'metrics_daemon_test', + 'type': 'executable', + 'dependencies': [ + 'libmetrics_daemon', + ], + 'includes': ['../common-mk/common_test.gypi'], + 'sources': [ + 'metrics_daemon_test.cc', + ] + }, + { + 'target_name': 'counter_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'sources': [ + 'counter.cc', + 'counter_test.cc', + ] + }, + { + 'target_name': 'timer_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'sources': [ + 'timer.cc', + 'timer_test.cc', + ] + }, + ], + }], + ], +} From fb3a8211b5515937455d00fdb1991aa7af37f38b Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 7 May 2013 16:55:00 -0700 Subject: [PATCH 092/200] Collect CPU frequency thermal throttling stats This collects the max frequency every 30s and reports it as a percentage of the unthrottled frequency. The special value of 101% indicates that the CPU is using turbo. BUG=chromium:238890 TEST=ran manually, checked about:histograms Change-Id: Ia1c8a2344b81b8274f9045b854d2e6d35cf49339 Reviewed-on: https://gerrit.chromium.org/gerrit/50387 Reviewed-by: Darin Petkov Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_daemon.cc | 83 +++++++++++++++++++++++++++++++--- metrics/metrics_daemon.h | 17 ++++++- metrics/metrics_daemon_main.cc | 8 +++- metrics/metrics_daemon_test.cc | 52 +++++++++++++++++++-- 4 files changed, 147 insertions(+), 13 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index a7865484b..3b9e9dba5 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -128,6 +129,11 @@ const char MetricsDaemon::kMetricPageFaultsLongName[] = const char MetricsDaemon::kMetricPageFaultsShortName[] = "Platform.PageFaultsShort"; +// Thermal CPU throttling. + +const char MetricsDaemon::kMetricScaledCpuFrequencyName[] = + "Platform.CpuFrequencyThermalScaling"; + // persistent metrics path const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; @@ -247,7 +253,10 @@ void MetricsDaemon::ConfigureCrashFrequencyReporter( void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, const string& diskstats_path, - const string& vmstats_path) { + const string& vmstats_path, + const string& scaling_max_freq_path, + const string& cpuinfo_max_freq_path + ) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; @@ -277,6 +286,8 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, diskstats_path_ = diskstats_path; vmstats_path_ = vmstats_path; + scaling_max_freq_path_ = scaling_max_freq_path; + cpuinfo_max_freq_path_ = cpuinfo_max_freq_path; StatsReporterInit(); // Start collecting meminfo stats. @@ -632,17 +643,16 @@ bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors, bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) { static const char kPageFaultSearchString[] = "\npgmajfault "; bool success = false; - /* Each line in the file has the form - * - * for instance: - * nr_free_pages 213427 - */ + // Each line in the file has the form + // + // for instance: + // nr_free_pages 213427 char* s = strstr(stats, kPageFaultSearchString); if (s == NULL) { LOG(WARNING) << "cannot find page fault entry in vmstats"; } else { char* endp; - /* Skip and space. Don't count the terminating null. */ + // Skip and space. Don't count the terminating null. s += sizeof(kPageFaultSearchString) - 1; *page_faults = strtol(s, &endp, 10); if (*endp == '\n') { @@ -681,6 +691,64 @@ bool MetricsDaemon::VmStatsReadStats(long int* page_faults) { return success; } +bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) { + const string sysfs_dirpath(testing_ ? + "./" : "/sys/devices/system/cpu/cpu0/cpufreq/"); + const FilePath sysfs_path(sysfs_dirpath + sysfs_file_name); + string value_string; + if (!file_util::ReadFileToString(sysfs_path, &value_string)) { + LOG(WARNING) << "cannot read " << sysfs_path.value().c_str(); + return false; + } + if (!RemoveChars(value_string, "\n", &value_string)) { + LOG(WARNING) << "no newline in " << value_string; + // Continue even though the lack of newline is suspicious. + } + if (!base::StringToInt(value_string, value)) { + LOG(WARNING) << "cannot convert " << value_string << " to int"; + return false; + } + return true; +} + +void MetricsDaemon::SendCpuThrottleMetrics() { + // |max_freq| is 0 only the first time through. + static int max_freq = 0; + if (max_freq == -1) + // Give up, as sysfs did not report max_freq correctly. + return; + if (max_freq == 0 || testing_) { + // One-time initialization of max_freq. (Every time when testing.) + if (!ReadFreqToInt(cpuinfo_max_freq_path_, &max_freq)) { + max_freq = -1; + return; + } + if (max_freq == 0) { + LOG(WARNING) << "sysfs reports 0 max CPU frequency\n"; + max_freq = -1; + return; + } + if (max_freq % 10000 == 1000) { + // Special case: system has turbo mode, and max non-turbo frequency is + // max_freq - 1000. This relies on "normal" (non-turbo) frequencies + // being multiples of (at least) 10 MHz. Although there is no guarantee + // of this, it seems a fairly reasonable assumption. Otherwise we should + // read scaling_available_frequencies, sort the frequencies, compare the + // two highest ones, and check if they differ by 1000 (kHz) (and that's a + // hack too, no telling when it will change). + max_freq -= 1000; + } + } + int scaled_freq = 0; + if (!ReadFreqToInt(scaling_max_freq_path_, &scaled_freq)) + return; + // Frequencies are in kHz. If scaled_freq > max_freq, turbo is on, but + // scaled_freq is not the actual turbo frequency. We indicate this situation + // with a 101% value. + int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100); + SendLinearMetric(kMetricScaledCpuFrequencyName, percent, 101, 102); +} + // static gboolean MetricsDaemon::StatsCallbackStatic(void* handle) { (static_cast(handle))->StatsCallback(); @@ -758,6 +826,7 @@ void MetricsDaemon::StatsCallback() { kMetricPageFaultsBuckets); page_faults_ = page_faults_now; } + SendCpuThrottleMetrics(); // Set start time for new cycle. stats_initial_time_ = time_now; // Schedule short callback. diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 07eaa01ea..03c5f3ccd 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -31,7 +31,9 @@ class MetricsDaemon { // Initializes. void Init(bool testing, MetricsLibraryInterface* metrics_lib, const std::string& diskstats_path, - const std::string& vmstats_path); + const std::string& vmstats_path, + const std::string& cpuinfo_max_freq_path, + const std::string& scaling_max_freq_path); // Does all the work. If |run_as_daemon| is true, daemonizes by // forking. @@ -56,6 +58,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ProcessUncleanShutdown); FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash); FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency); + FRIEND_TEST(MetricsDaemonTest, ReadFreqToInt); FRIEND_TEST(MetricsDaemonTest, ReportDailyUse); FRIEND_TEST(MetricsDaemonTest, ReportDiskStats); FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval); @@ -63,6 +66,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval); FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); FRIEND_TEST(MetricsDaemonTest, SendMetric); + FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics); FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateTimeJump); @@ -148,6 +152,7 @@ class MetricsDaemon { static const char kMetricWriteSectorsShortName[]; static const char kMetricPageFaultsShortName[]; static const char kMetricPageFaultsLongName[]; + static const char kMetricScaledCpuFrequencyName[]; static const int kMetricStatsShortInterval; static const int kMetricStatsLongInterval; static const int kMetricMeminfoInterval; @@ -331,9 +336,15 @@ class MetricsDaemon { // Reads /proc/meminfo and sends total anonymous memory usage to UMA. bool MemuseCallbackWork(); - // Parse meminfo data and send to UMA. + // Parses meminfo data and sends it to UMA. bool ProcessMemuse(const std::string& meminfo_raw); + // Sends stats for thermal CPU throttling. + void SendCpuThrottleMetrics(); + + // Reads an integer CPU frequency value from sysfs. + bool ReadFreqToInt(const std::string& sysfs_file_name, int* value); + // Test mode. bool testing_; @@ -399,6 +410,8 @@ class MetricsDaemon { std::string diskstats_path_; std::string vmstats_path_; + std::string scaling_max_freq_path_; + std::string cpuinfo_max_freq_path_; }; #endif // METRICS_DAEMON_H_ diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index d194e5442..643a2bf35 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -10,6 +10,11 @@ #include "metrics_daemon.h" +const char kScalingMaxFreqPath[] = + "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"; +const char kCpuinfoMaxFreqPath[] = + "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; + DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); // Returns the path to the disk stats in the sysfs. Returns the null string if @@ -42,6 +47,7 @@ int main(int argc, char** argv) { MetricsLibrary metrics_lib; metrics_lib.Init(); MetricsDaemon daemon; - daemon.Init(false, &metrics_lib, MetricsMainDiskStatsPath(), "/proc/vmstat"); + daemon.Init(false, &metrics_lib, MetricsMainDiskStatsPath(), + "/proc/vmstat", kScalingMaxFreqPath, kCpuinfoMaxFreqPath); daemon.Run(FLAGS_daemon); } diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 370634cd0..92240a2fd 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -45,6 +45,8 @@ static const int kFakeReadSectors[] = {80000, 100000}; static const int kFakeWriteSectors[] = {3000, 4000}; static const char kFakeVmStatsPath[] = "fake-vm-stats"; +static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq"; +static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq"; // This class allows a TimeTicks object to be initialized with seconds // (rather than microseconds) through the protected TimeTicks(int64) @@ -72,7 +74,11 @@ class MetricsDaemonTest : public testing::Test { kFakeDiskStats[1] = StringPrintf(kFakeDiskStatsFormat, kFakeReadSectors[1], kFakeWriteSectors[1]); CreateFakeDiskStatsFile(kFakeDiskStats[0].c_str()); - daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath, kFakeVmStatsPath); + CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, 10000000); + CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 10000000); + + daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath, kFakeVmStatsPath, + kFakeScalingMaxFreqPath, kFakeCpuinfoMaxFreqPath); // Check configuration of a few histograms. FrequencyCounter* frequency_counter = @@ -139,7 +145,9 @@ class MetricsDaemonTest : public testing::Test { } virtual void TearDown() { - EXPECT_EQ(unlink(kFakeDiskStatsPath), 0); + EXPECT_EQ(0, unlink(kFakeDiskStatsPath)); + EXPECT_EQ(0, unlink(kFakeScalingMaxFreqPath)); + EXPECT_EQ(0, unlink(kFakeCpuinfoMaxFreqPath)); } const TaggedCounterReporter* @@ -258,6 +266,17 @@ class MetricsDaemonTest : public testing::Test { EXPECT_EQ(0, fclose(f)); } + // Creates or overwrites an input file containing a fake CPU frequency. + void CreateFakeCpuFrequencyFile(const char* filename, int frequency) { + FilePath path(filename); + file_util::Delete(path, false); + std::string frequency_string = StringPrintf("%d\n", frequency); + int frequency_string_length = frequency_string.length(); + EXPECT_EQ(frequency_string.length(), + file_util::WriteFile(path, frequency_string.c_str(), + frequency_string_length)); + } + // The MetricsDaemon under test. MetricsDaemon daemon_; @@ -578,6 +597,7 @@ TEST_F(MetricsDaemonTest, ReportDiskStats) { EXPECT_CALL(metrics_lib_, SendToUMA(_, (kFakeWriteSectors[1] - kFakeWriteSectors[0]) / 30, _, _, _)); + EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, _)); // SendCpuThrottleMetrics daemon_.StatsCallback(); EXPECT_TRUE(s_state != daemon_.stats_state_); } @@ -642,7 +662,7 @@ TEST_F(MetricsDaemonTest, ProcessMeminfo2) { MemTotal: 2000000 kB\n\ MemFree: 1000000 kB\n\ "; - /* Not enough fields */ + // Not enough fields. EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo)); } @@ -653,6 +673,32 @@ TEST_F(MetricsDaemonTest, ParseVmStats) { EXPECT_EQ(page_faults, 42); } +TEST_F(MetricsDaemonTest, ReadFreqToInt) { + const int fake_scaled_freq = 1666999; + const int fake_max_freq = 2000000; + int scaled_freq = 0; + int max_freq = 0; + CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, fake_scaled_freq); + CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, fake_max_freq); + EXPECT_TRUE(daemon_.testing_); + EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeScalingMaxFreqPath, &scaled_freq)); + EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeCpuinfoMaxFreqPath, &max_freq)); + EXPECT_EQ(fake_scaled_freq, scaled_freq); + EXPECT_EQ(fake_max_freq, max_freq); +} + +TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) { + CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, 2001000); + // Test the 101% and 100% cases. + CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 2001000); + EXPECT_TRUE(daemon_.testing_); + EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101)); + daemon_.SendCpuThrottleMetrics(); + CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 2000000); + EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 100, 101)); + daemon_.SendCpuThrottleMetrics(); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From e6fcdabe7394ac4a14ded21bdbbc7e8d66828843 Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Mon, 13 May 2013 10:31:45 -0700 Subject: [PATCH 093/200] metrics: fix clang syntax checking error. This fixes the two errors in the clang syntax. BUG=chromium:240325 TEST=FEATURES="test" emerge-lumpy metrics Change-Id: I57ab09fbd3dba2b394c606a3b2ba1ad799607939 Reviewed-on: https://gerrit.chromium.org/gerrit/50999 Reviewed-by: Darin Petkov Commit-Queue: Yunlian Jiang Tested-by: Yunlian Jiang --- metrics/counter_mock.h | 4 +--- metrics/metrics_daemon_test.cc | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h index cf2a486c1..267aaca99 100644 --- a/metrics/counter_mock.h +++ b/metrics/counter_mock.h @@ -34,9 +34,7 @@ class TaggedCounterReporterMock : public TaggedCounterReporter { class FrequencyCounterMock : public FrequencyCounter { public: - MOCK_METHOD4(Init, void(const char* filename, - TaggedCounterInterface::Reporter reporter, - void* reporter_handle, + MOCK_METHOD2(Init, void(TaggedCounterInterface* tagged_counter, time_t cycle_duration)); MOCK_METHOD1(Update, void(int32 count)); MOCK_METHOD0(FlushFinishedCycles, void()); diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 92240a2fd..d27597c0f 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -57,12 +57,6 @@ class TestTicks : public TimeTicks { : TimeTicks(seconds * Time::kMicrosecondsPerSecond) {} }; -// Overloaded for test failure printing purposes. -static std::ostream& operator<<(std::ostream& o, const Time& time) { - o << time.ToInternalValue() << "us"; - return o; -}; - class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { From 0672655fa66deb6dd772a584b82e2c5bbf4cf847 Mon Sep 17 00:00:00 2001 From: repo sync Date: Tue, 28 May 2013 14:19:53 -0700 Subject: [PATCH 094/200] metrics: Creates |Pause| and |Resume| methods for timers. BUG=chromium:244589 TEST=unittest Change-Id: I54e6dd7f5a8eb08ff42b1ee0e60df5cc895d0819 Reviewed-on: https://gerrit.chromium.org/gerrit/56849 Reviewed-by: Darin Petkov Reviewed-by: Wade Guthrie Tested-by: Wade Guthrie Commit-Queue: Wade Guthrie --- metrics/timer.cc | 58 ++++++-- metrics/timer.h | 34 +++-- metrics/timer_test.cc | 315 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 373 insertions(+), 34 deletions(-) diff --git a/metrics/timer.cc b/metrics/timer.cc index 67b0fd14b..c8e617612 100644 --- a/metrics/timer.cc +++ b/metrics/timer.cc @@ -18,38 +18,70 @@ base::TimeTicks ClockWrapper::GetCurrentTime() const { } Timer::Timer() - : is_started_(false), + : timer_state_(kTimerStopped), clock_wrapper_(new ClockWrapper()) {} bool Timer::Start() { + elapsed_time_ = base::TimeDelta(); // Sets elapsed_time_ to zero. start_time_ = clock_wrapper_->GetCurrentTime(); - is_started_ = true; + timer_state_ = kTimerRunning; return true; } bool Timer::Stop() { - // Check if the timer has been started. - if (!is_started_) return false; - is_started_ = false; - elapsed_time_ = clock_wrapper_->GetCurrentTime() - start_time_; + if (timer_state_ == kTimerStopped) + return false; + if (timer_state_ == kTimerRunning) + elapsed_time_ += clock_wrapper_->GetCurrentTime() - start_time_; + timer_state_ = kTimerStopped; return true; } +bool Timer::Pause() { + switch (timer_state_) { + case kTimerStopped: + if (!Start()) + return false; + timer_state_ = kTimerPaused; + return true; + case kTimerRunning: + timer_state_ = kTimerPaused; + elapsed_time_ += clock_wrapper_->GetCurrentTime() - start_time_; + return true; + default: + return false; + } +} + +bool Timer::Resume() { + switch (timer_state_) { + case kTimerStopped: + return Start(); + case kTimerPaused: + start_time_ = clock_wrapper_->GetCurrentTime(); + timer_state_ = kTimerRunning; + return true; + default: + return false; + } +} + bool Timer::Reset() { - is_started_ = false; + elapsed_time_ = base::TimeDelta(); // Sets elapsed_time_ to zero. + timer_state_ = kTimerStopped; return true; } bool Timer::HasStarted() const { - return is_started_; + return timer_state_ != kTimerStopped; } bool Timer::GetElapsedTime(base::TimeDelta* elapsed_time) const { - if (start_time_.is_null() || !elapsed_time) return false; - if (is_started_) { - *elapsed_time = clock_wrapper_->GetCurrentTime() - start_time_; - } else { - *elapsed_time = elapsed_time_; + if (start_time_.is_null() || !elapsed_time) + return false; + *elapsed_time = elapsed_time_; + if (timer_state_ == kTimerRunning) { + *elapsed_time += clock_wrapper_->GetCurrentTime() - start_time_; } return true; } diff --git a/metrics/timer.h b/metrics/timer.h index e80950c1d..b94977387 100644 --- a/metrics/timer.h +++ b/metrics/timer.h @@ -55,6 +55,14 @@ class Timer : public TimerInterface { // Otherwise, it fails (returns false). virtual bool Stop(); + // Pauses a timer. If the timer is stopped, this call starts the timer in + // the paused state. Fails (returns false) if the timer is already paused. + virtual bool Pause(); + + // Restarts a paused timer (or starts a stopped timer). This method fails + // (returns false) if the timer is already running; otherwise, returns true. + virtual bool Resume(); + // Resets the timer, erasing the current duration being tracked. Always // returns true. virtual bool Reset(); @@ -70,15 +78,25 @@ class Timer : public TimerInterface { virtual bool GetElapsedTime(base::TimeDelta* elapsed_time) const; private: + enum TimerState { kTimerStopped, kTimerRunning, kTimerPaused }; friend class TimerTest; friend class TimerReporterTest; - FRIEND_TEST(TimerTest, StartStop); - FRIEND_TEST(TimerTest, ReStart); - FRIEND_TEST(TimerTest, Reset); - FRIEND_TEST(TimerTest, SeparatedTimers); - FRIEND_TEST(TimerTest, InvalidStop); - FRIEND_TEST(TimerTest, InvalidElapsedTime); FRIEND_TEST(TimerReporterTest, StartStopReport); + FRIEND_TEST(TimerTest, InvalidElapsedTime); + FRIEND_TEST(TimerTest, InvalidStop); + FRIEND_TEST(TimerTest, PauseResumeStop); + FRIEND_TEST(TimerTest, PauseStartStopResume); + FRIEND_TEST(TimerTest, PauseStop); + FRIEND_TEST(TimerTest, Reset); + FRIEND_TEST(TimerTest, ReStart); + FRIEND_TEST(TimerTest, ResumeStartStopPause); + FRIEND_TEST(TimerTest, SeparatedTimers); + FRIEND_TEST(TimerTest, StartPauseResumePauseResumeStop); + FRIEND_TEST(TimerTest, StartPauseResumePauseStop); + FRIEND_TEST(TimerTest, StartPauseResumeStop); + FRIEND_TEST(TimerTest, StartPauseStop); + FRIEND_TEST(TimerTest, StartResumeStop); + FRIEND_TEST(TimerTest, StartStop); // Elapsed time of the last use of the timer. base::TimeDelta elapsed_time_; @@ -86,8 +104,8 @@ class Timer : public TimerInterface { // Starting time value. base::TimeTicks start_time_; - // Whether the timer has started or not. - bool is_started_; + // Whether the timer is running, stopped, or paused. + TimerState timer_state_; // Wrapper for the calls to the system clock. scoped_ptr clock_wrapper_; diff --git a/metrics/timer_test.cc b/metrics/timer_test.cc index 445aeeb0e..03475d33c 100644 --- a/metrics/timer_test.cc +++ b/metrics/timer_test.cc @@ -16,22 +16,40 @@ using ::testing::Return; namespace chromeos_metrics { +namespace { +const int64 kStime1MSec = 1400; +const int64 kEtime1MSec = 3000; +const int64 kDelta1MSec = 1600; + +const int64 kStime2MSec = 4200; +const int64 kEtime2MSec = 5000; +const int64 kDelta2MSec = 800; + +const int64 kStime3MSec = 6600; +const int64 kEtime3MSec = 6800; +const int64 kDelta3MSec = 200; +} // namespace + class TimerTest : public testing::Test { public: TimerTest() : clock_wrapper_mock_(new ClockWrapperMock()) {} protected: virtual void SetUp() { - EXPECT_FALSE(timer_.is_started_); - stime += base::TimeDelta::FromMilliseconds(1500); - etime += base::TimeDelta::FromMilliseconds(3000); + EXPECT_EQ(Timer::kTimerStopped, timer_.timer_state_); + stime += base::TimeDelta::FromMilliseconds(kStime1MSec); + etime += base::TimeDelta::FromMilliseconds(kEtime1MSec); + stime2 += base::TimeDelta::FromMilliseconds(kStime2MSec); + etime2 += base::TimeDelta::FromMilliseconds(kEtime2MSec); + stime3 += base::TimeDelta::FromMilliseconds(kStime3MSec); + etime3 += base::TimeDelta::FromMilliseconds(kEtime3MSec); } virtual void TearDown() {} Timer timer_; scoped_ptr clock_wrapper_mock_; - base::TimeTicks stime, etime; + base::TimeTicks stime, etime, stime2, etime2, stime3, etime3; }; TEST_F(TimerTest, StartStop) { @@ -43,7 +61,13 @@ TEST_F(TimerTest, StartStop) { ASSERT_TRUE(timer_.start_time_ == stime); ASSERT_TRUE(timer_.HasStarted()); ASSERT_TRUE(timer_.Stop()); - ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 1500); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec); + + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + ASSERT_FALSE(timer_.HasStarted()); } @@ -68,9 +92,6 @@ TEST_F(TimerTest, Reset) { } TEST_F(TimerTest, SeparatedTimers) { - base::TimeTicks stime2, etime2; - stime2 += base::TimeDelta::FromMilliseconds(4200); - etime2 += base::TimeDelta::FromMilliseconds(5000); EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) .WillOnce(Return(stime)) .WillOnce(Return(etime)) @@ -79,12 +100,17 @@ TEST_F(TimerTest, SeparatedTimers) { timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); ASSERT_TRUE(timer_.Start()); ASSERT_TRUE(timer_.Stop()); - ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 1500); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec); ASSERT_TRUE(timer_.Start()); ASSERT_TRUE(timer_.start_time_ == stime2); ASSERT_TRUE(timer_.Stop()); - ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 800); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec); ASSERT_FALSE(timer_.HasStarted()); + + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); } TEST_F(TimerTest, InvalidStop) { @@ -106,6 +132,269 @@ TEST_F(TimerTest, InvalidElapsedTime) { ASSERT_FALSE(timer_.GetElapsedTime(&elapsed_time)); } +TEST_F(TimerTest, PauseStartStopResume) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(stime2)) + .WillOnce(Return(etime2)) + .WillOnce(Return(stime3)) + .WillOnce(Return(etime3)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Pause()); // Starts timer paused. + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Start()); // Restarts timer. + ASSERT_TRUE(timer_.start_time_ == stime2); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec); + ASSERT_FALSE(timer_.HasStarted()); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Resume()); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(kDelta3MSec, elapsed_time.InMilliseconds()); +} + +TEST_F(TimerTest, ResumeStartStopPause) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(stime2)) + .WillOnce(Return(etime2)) + .WillOnce(Return(stime3)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Resume()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.start_time_ == stime2); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec); + ASSERT_FALSE(timer_.HasStarted()); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(0, elapsed_time.InMilliseconds()); +} + +TEST_F(TimerTest, StartResumeStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_FALSE(timer_.Resume()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec); + ASSERT_FALSE(timer_.HasStarted()); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); +} + +TEST_F(TimerTest, StartPauseStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec); + ASSERT_FALSE(timer_.HasStarted()); + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); +} + +TEST_F(TimerTest, StartPauseResumeStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)) + .WillOnce(Return(stime2)) + .WillOnce(Return(etime2)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Resume()); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec + kDelta2MSec); + ASSERT_FALSE(timer_.HasStarted()); + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); +} + +TEST_F(TimerTest, PauseStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 0); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), 0); + ASSERT_FALSE(timer_.HasStarted()); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); +} + +TEST_F(TimerTest, PauseResumeStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(stime2)) + .WillOnce(Return(etime2)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Resume()); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta2MSec); + ASSERT_FALSE(timer_.HasStarted()); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); +} + +TEST_F(TimerTest, StartPauseResumePauseStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)) + .WillOnce(Return(stime2)) + .WillOnce(Return(stime3)) + .WillOnce(Return(etime3)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Resume()); + ASSERT_TRUE(timer_.HasStarted()); + // Make sure GetElapsedTime works while we're running. + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(kDelta1MSec + kStime3MSec - kStime2MSec, + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + kDelta1MSec + kEtime3MSec - kStime2MSec); + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + kDelta1MSec + kEtime3MSec - kStime2MSec); + ASSERT_FALSE(timer_.HasStarted()); + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); +} + +TEST_F(TimerTest, StartPauseResumePauseResumeStop) { + EXPECT_CALL(*clock_wrapper_mock_, GetCurrentTime()) + .WillOnce(Return(stime)) + .WillOnce(Return(etime)) + .WillOnce(Return(stime2)) + .WillOnce(Return(etime2)) + .WillOnce(Return(stime3)) + .WillOnce(Return(etime3)); + timer_.clock_wrapper_.reset(clock_wrapper_mock_.release()); + ASSERT_TRUE(timer_.Start()); + ASSERT_TRUE(timer_.start_time_ == stime); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec); + base::TimeDelta elapsed_time; + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Resume()); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Pause()); + ASSERT_TRUE(timer_.HasStarted()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), kDelta1MSec + kDelta2MSec); + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); + + ASSERT_TRUE(timer_.Resume()); + ASSERT_TRUE(timer_.HasStarted()); + + ASSERT_TRUE(timer_.Stop()); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + kDelta1MSec + kDelta2MSec + kDelta3MSec); + ASSERT_FALSE(timer_.HasStarted()); + ASSERT_TRUE(timer_.GetElapsedTime(&elapsed_time)); + ASSERT_EQ(timer_.elapsed_time_.InMilliseconds(), + elapsed_time.InMilliseconds()); +} + static const char kMetricName[] = "test-timer"; static const int kMinSample = 0; static const int kMaxSample = 120 * 1E6; @@ -124,8 +413,8 @@ class TimerReporterTest : public testing::Test { EXPECT_EQ(timer_reporter_.min_, kMinSample); EXPECT_EQ(timer_reporter_.max_, kMaxSample); EXPECT_EQ(timer_reporter_.num_buckets_, kNumBuckets); - stime += base::TimeDelta::FromMilliseconds(1500); - etime += base::TimeDelta::FromMilliseconds(3000); + stime += base::TimeDelta::FromMilliseconds(kStime1MSec); + etime += base::TimeDelta::FromMilliseconds(kEtime1MSec); } virtual void TearDown() { @@ -143,7 +432,7 @@ TEST_F(TimerReporterTest, StartStopReport) { .WillOnce(Return(stime)) .WillOnce(Return(etime)); timer_reporter_.clock_wrapper_.reset(clock_wrapper_mock_.release()); - EXPECT_CALL(lib_, SendToUMA(kMetricName, 1500, kMinSample, kMaxSample, + EXPECT_CALL(lib_, SendToUMA(kMetricName, kDelta1MSec, kMinSample, kMaxSample, kNumBuckets)).WillOnce(Return(true)); ASSERT_TRUE(timer_reporter_.Start()); ASSERT_TRUE(timer_reporter_.Stop()); From d92d18c935ad8d90c56e6a0f568ef72f5c8cd752 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 4 Jun 2013 13:24:21 -0700 Subject: [PATCH 095/200] Fix wrong sysfs pathname. This bug was introduced while adding test code which was supposed to improve the reliability of this program. BUG=chromium:238890 TEST=verified that about:histograms contains samples Change-Id: I66323292f9261f5715760d7c884f91aca1f7e453 Reviewed-on: https://gerrit.chromium.org/gerrit/57501 Reviewed-by: Sonny Rao Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/metrics_daemon.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 3b9e9dba5..ad6bf945b 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -692,9 +692,7 @@ bool MetricsDaemon::VmStatsReadStats(long int* page_faults) { } bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) { - const string sysfs_dirpath(testing_ ? - "./" : "/sys/devices/system/cpu/cpu0/cpufreq/"); - const FilePath sysfs_path(sysfs_dirpath + sysfs_file_name); + const FilePath sysfs_path(sysfs_file_name); string value_string; if (!file_util::ReadFileToString(sysfs_path, &value_string)) { LOG(WARNING) << "cannot read " << sysfs_path.value().c_str(); From 6f59842fb198462300885e8a52ce6f3d5ed1716d Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Sat, 22 Jun 2013 06:29:36 -0700 Subject: [PATCH 096/200] Remove unnecessary call to the deprecated g_thread_init(). g_thread_init() has been deprecated since glib 2.32. This CL removes the unnecessary call to g_thread_init(), so that we can later migrate to glib 2.34. It also replaces dbus_g_thread_init(), which calls the deprecated g_thread_supported(), with dbus_threads_init_default() directly. BUG=chromium:253025 TEST=Tested the following: 1. Build and run unit tests. 2. chrome://histograms shows that CrOS metrics are being collected. Change-Id: I9c191d1926928caa8a704db03024f48f6c1cb383 Reviewed-on: https://gerrit.chromium.org/gerrit/59683 Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/metrics_daemon.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index ad6bf945b..e2c4b13fd 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -298,9 +298,8 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, if (testing) return; - g_thread_init(NULL); g_type_init(); - dbus_g_thread_init(); + dbus_threads_init_default(); DBusError error; dbus_error_init(&error); From 6e55c11537d0625e6d6a024db4b80ab942081022 Mon Sep 17 00:00:00 2001 From: Darren Krahn Date: Fri, 19 Jul 2013 14:09:50 -0700 Subject: [PATCH 097/200] Define cros event names for chaps database failures. BUG=chrome-os-partner:17610 TEST=emerge Change-Id: Ic12379645ad1c72388aa4a4758fa4bea5b2f554d Reviewed-on: https://gerrit.chromium.org/gerrit/62698 Reviewed-by: Gaurav Shah Commit-Queue: Darren Krahn Tested-by: Darren Krahn --- metrics/metrics_library.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index e5aaae6db..12ad226f2 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -37,6 +37,9 @@ static const char *kCrosEventNames[] = { "ModemManagerCommandSendFailure", // 0 "HwWatchdogReboot", // 1 "Cras.NoCodecsFoundAtBoot", // 2 + "Chaps.DatabaseCorrupted", // 3 + "Chaps.DatabaseRepairFailure", // 4 + "Chaps.DatabaseCreateFailure", // 5 }; time_t MetricsLibrary::cached_enabled_time_ = 0; From 4b8aebb0f452e5d2fc28cfa90a14ab14dc0db5f0 Mon Sep 17 00:00:00 2001 From: Sonny Rao Date: Wed, 31 Jul 2013 23:18:31 -0700 Subject: [PATCH 098/200] Add additional stats for swapping from /proc/vmstat This adds swap-in and swap-out rate calculation on the same intervals as major page fault collection. BUG=chromium:261965 TEST=observe Platform.SwapIn{Long,Short} and Platform.SwapOut{Long,Short} appear in histograms Change-Id: Ifcdba1088cdff355b8e132145ac79635b185663b Reviewed-on: https://gerrit.chromium.org/gerrit/64162 Reviewed-by: Luigi Semenzato Commit-Queue: Sonny Rao Tested-by: Sonny Rao --- metrics/metrics_daemon.cc | 140 ++++++++++++++++++++++----------- metrics/metrics_daemon.h | 17 +++- metrics/metrics_daemon_test.cc | 11 ++- 3 files changed, 116 insertions(+), 52 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index e2c4b13fd..1161f6c84 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -129,6 +130,18 @@ const char MetricsDaemon::kMetricPageFaultsLongName[] = const char MetricsDaemon::kMetricPageFaultsShortName[] = "Platform.PageFaultsShort"; +// Swap in and Swap out + +const char MetricsDaemon::kMetricSwapInLongName[] = + "Platform.SwapInLong"; +const char MetricsDaemon::kMetricSwapInShortName[] = + "Platform.SwapInShort"; + +const char MetricsDaemon::kMetricSwapOutLongName[] = + "Platform.SwapOutLong"; +const char MetricsDaemon::kMetricSwapOutShortName[] = + "Platform.SwapOutShort"; + // Thermal CPU throttling. const char MetricsDaemon::kMetricScaledCpuFrequencyName[] = @@ -171,7 +184,7 @@ MetricsDaemon::MetricsDaemon() memuse_interval_index_(0), read_sectors_(0), write_sectors_(0), - page_faults_(0), + vmstats_(), stats_state_(kStatsShort), stats_initial_time_(0) {} @@ -586,7 +599,7 @@ void MetricsDaemon::UnscheduleUseMonitor() { void MetricsDaemon::StatsReporterInit() { DiskStatsReadStats(&read_sectors_, &write_sectors_); - VmStatsReadStats(&page_faults_); + VmStatsReadStats(&vmstats_); // The first time around just run the long stat, so we don't delay boot. stats_state_ = kStatsLong; stats_initial_time_ = GetActiveTime(); @@ -639,55 +652,66 @@ bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors, return success; } -bool MetricsDaemon::VmStatsParseStats(char* stats, long int* page_faults) { - static const char kPageFaultSearchString[] = "\npgmajfault "; - bool success = false; +bool MetricsDaemon::VmStatsParseStats(const char* stats, + struct VmstatRecord* record) { + // a mapping of string name to field in VmstatRecord and whether we found it + struct mapping { + const string name; + uint64_t* value_p; + bool found; + } map[] = + { { .name = "pgmajfault", + .value_p = &record->page_faults_, + .found = false }, + { .name = "pswpin", + .value_p = &record->swap_in_, + .found = false }, + { .name = "pswpout", + .value_p = &record->swap_out_, + .found = false }, }; + // Each line in the file has the form // // for instance: // nr_free_pages 213427 - char* s = strstr(stats, kPageFaultSearchString); - if (s == NULL) { - LOG(WARNING) << "cannot find page fault entry in vmstats"; - } else { - char* endp; - // Skip and space. Don't count the terminating null. - s += sizeof(kPageFaultSearchString) - 1; - *page_faults = strtol(s, &endp, 10); - if (*endp == '\n') { - success = true; + vector lines; + Tokenize(stats, "\n", &lines); + for (vector::iterator it = lines.begin(); + it != lines.end(); ++it) { + vector tokens; + base::SplitString(*it, ' ', &tokens); + if (tokens.size() == 2) { + for (unsigned int i = 0; i < sizeof(map)/sizeof(struct mapping); i++) { + if (!tokens[0].compare(map[i].name)) { + if (!base::StringToUint64(tokens[1], map[i].value_p)) + return false; + map[i].found = true; + } + } } else { - LOG(WARNING) << "error parsing vmstats"; + LOG(WARNING) << "unexpected vmstat format"; } } - return success; + // make sure we got all the stats + for (unsigned i = 0; i < sizeof(map)/sizeof(struct mapping); i++) { + if (map[i].found == false) { + LOG(WARNING) << "vmstat missing " << map[i].name; + return false; + } + } + return true; } -bool MetricsDaemon::VmStatsReadStats(long int* page_faults) { - char buffer[4000]; - int nchars; - int success = false; - if (testing_) { +bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) { + string value_string; + FilePath* path = new FilePath(vmstats_path_); + if (!file_util::ReadFileToString(*path, &value_string)) { + delete path; + LOG(WARNING) << "cannot read " << vmstats_path_; return false; } - int file = HANDLE_EINTR(open(vmstats_path_.c_str(), O_RDONLY)); - if (file < 0) { - PLOG(WARNING) << "cannot open " << vmstats_path_; - return false; - } - nchars = HANDLE_EINTR(read(file, buffer, sizeof(buffer) - 1)); - LOG_IF(WARNING, nchars == sizeof(buffer) - 1) - << "file too large in " << vmstats_path_; - if (nchars < 0) { - PLOG(WARNING) << "cannot read from " << vmstats_path_; - } else if (nchars == 0) { - LOG(WARNING) << vmstats_path_ << " is empty"; - } else { - buffer[nchars] = '\0'; - success = VmStatsParseStats(buffer, page_faults); - } - HANDLE_EINTR(close(file)); - return success; + delete path; + return VmStatsParseStats(value_string.c_str(), stats); } bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) { @@ -755,7 +779,8 @@ gboolean MetricsDaemon::StatsCallbackStatic(void* handle) { // Collects disk and vm stats alternating over a short and a long interval. void MetricsDaemon::StatsCallback() { - long int read_sectors_now, write_sectors_now, page_faults_now; + long int read_sectors_now, write_sectors_now; + struct VmstatRecord vmstats_now; double time_now = GetActiveTime(); double delta_time = time_now - stats_initial_time_; if (testing_) { @@ -769,9 +794,13 @@ void MetricsDaemon::StatsCallback() { int delta_write = write_sectors_now - write_sectors_; int read_sectors_per_second = delta_read / delta_time; int write_sectors_per_second = delta_write / delta_time; - bool vmstats_success = VmStatsReadStats(&page_faults_now); - int delta_faults = page_faults_now - page_faults_; - int page_faults_per_second = delta_faults / delta_time; + bool vmstats_success = VmStatsReadStats(&vmstats_now); + uint64_t delta_faults = vmstats_now.page_faults_ - vmstats_.page_faults_; + uint64_t delta_swap_in = vmstats_now.swap_in_ - vmstats_.swap_in_; + uint64_t delta_swap_out = vmstats_now.swap_out_ - vmstats_.swap_out_; + uint64_t page_faults_per_second = delta_faults / delta_time; + uint64_t swap_in_per_second = delta_swap_in / delta_time; + uint64_t swap_out_per_second = delta_swap_out / delta_time; switch (stats_state_) { case kStatsShort: @@ -793,6 +822,16 @@ void MetricsDaemon::StatsCallback() { 1, kMetricPageFaultsMax, kMetricPageFaultsBuckets); + SendMetric(kMetricSwapInShortName, + swap_in_per_second, + 1, + kMetricPageFaultsMax, + kMetricPageFaultsBuckets); + SendMetric(kMetricSwapOutShortName, + swap_out_per_second, + 1, + kMetricPageFaultsMax, + kMetricPageFaultsBuckets); } // Schedule long callback. stats_state_ = kStatsLong; @@ -821,7 +860,18 @@ void MetricsDaemon::StatsCallback() { 1, kMetricPageFaultsMax, kMetricPageFaultsBuckets); - page_faults_ = page_faults_now; + SendMetric(kMetricSwapInLongName, + swap_in_per_second, + 1, + kMetricPageFaultsMax, + kMetricPageFaultsBuckets); + SendMetric(kMetricSwapOutLongName, + swap_out_per_second, + 1, + kMetricPageFaultsMax, + kMetricPageFaultsBuckets); + + vmstats_ = vmstats_now; } SendCpuThrottleMetrics(); // Set start time for new cycle. diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 03c5f3ccd..d3a4f72f3 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -120,6 +120,13 @@ class MetricsDaemon { int value; // value from /proc/meminfo }; + // Record for retrieving and reporting values from /proc/vmstat + struct VmstatRecord { + uint64_t page_faults_; // major faults + uint64_t swap_in_; // pages swapped in + uint64_t swap_out_; // pages swapped out + }; + typedef std::map FrequencyCounters; @@ -152,6 +159,10 @@ class MetricsDaemon { static const char kMetricWriteSectorsShortName[]; static const char kMetricPageFaultsShortName[]; static const char kMetricPageFaultsLongName[]; + static const char kMetricSwapInLongName[]; + static const char kMetricSwapInShortName[]; + static const char kMetricSwapOutLongName[]; + static const char kMetricSwapOutShortName[]; static const char kMetricScaledCpuFrequencyName[]; static const int kMetricStatsShortInterval; static const int kMetricStatsLongInterval; @@ -284,10 +295,10 @@ class MetricsDaemon { bool DiskStatsReadStats(long int* read_sectors, long int* write_sectors); // Reads cumulative vm statistics from procfs. Returns true for success. - bool VmStatsReadStats(long int* page_faults); + bool VmStatsReadStats(struct VmstatRecord* stats); // Parse cumulative vm statistics from a C string. Returns true for success. - bool VmStatsParseStats(char* stats, long int* page_faults); + bool VmStatsParseStats(const char* stats, struct VmstatRecord* record); // Reports disk and vm statistics (static version for glib). Arguments are a // glib artifact. @@ -403,7 +414,7 @@ class MetricsDaemon { // Contain the most recent disk and vm cumulative stats. long int read_sectors_; long int write_sectors_; - long int page_faults_; + struct VmstatRecord vmstats_; StatsState stats_state_; double stats_initial_time_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index d27597c0f..2d8b61a07 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -661,10 +661,13 @@ MemFree: 1000000 kB\n\ } TEST_F(MetricsDaemonTest, ParseVmStats) { - static char kVmStats[] = "foo 100\nbar 200\npgmajfault 42\netcetc 300\n"; - long int page_faults = 0; - EXPECT_TRUE(daemon_.VmStatsParseStats(kVmStats, &page_faults)); - EXPECT_EQ(page_faults, 42); + static char kVmStats[] = "pswpin 1345\npswpout 8896\n" + "foo 100\nbar 200\npgmajfault 42\netcetc 300\n"; + struct MetricsDaemon::VmstatRecord stats; + EXPECT_TRUE(daemon_.VmStatsParseStats(kVmStats, &stats)); + EXPECT_EQ(stats.page_faults_, 42); + EXPECT_EQ(stats.swap_in_, 1345); + EXPECT_EQ(stats.swap_out_, 8896); } TEST_F(MetricsDaemonTest, ReadFreqToInt) { From 86830baa179ee66b2551122dc587057dfd1cdd05 Mon Sep 17 00:00:00 2001 From: Darren Krahn Date: Fri, 26 Jul 2013 13:37:20 -0700 Subject: [PATCH 099/200] Added cros event name for attestation metric. BUG=chromium:260504 TEST=unit Change-Id: If1c7218a02bd6bac8193c4ac550866b06b475f2a Reviewed-on: https://gerrit.chromium.org/gerrit/63495 Reviewed-by: Gaurav Shah Tested-by: Darren Krahn Commit-Queue: Darren Krahn --- metrics/metrics_library.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 12ad226f2..de2ff93a5 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -40,6 +40,7 @@ static const char *kCrosEventNames[] = { "Chaps.DatabaseCorrupted", // 3 "Chaps.DatabaseRepairFailure", // 4 "Chaps.DatabaseCreateFailure", // 5 + "Attestation.OriginSpecificExhausted", // 6 }; time_t MetricsLibrary::cached_enabled_time_ = 0; From e57398a6ee0afc0185aabedc66d14c91c09d33de Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 11 Nov 2013 14:24:44 -0800 Subject: [PATCH 100/200] Define UMA events for interesting power adapter situations. This adds events to the Platform.CrOSEvent histogram to record specific situations we're interested in. The user of the events will be thermal.sh in the daisy overlay. BUG=chrome-os-partner:23973 TEST=tested manually and observed samples in about:histograms Change-Id: I1845ce1f90f5e77ecdc294834671328ce13c827b Reviewed-on: https://chromium-review.googlesource.com/176367 Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato Reviewed-by: Vincent Palatin --- metrics/metrics_library.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index de2ff93a5..ba8e9009a 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -41,6 +41,8 @@ static const char *kCrosEventNames[] = { "Chaps.DatabaseRepairFailure", // 4 "Chaps.DatabaseCreateFailure", // 5 "Attestation.OriginSpecificExhausted", // 6 + "SpringPowerSupply.Original.High", // 7 + "SpringPowerSupply.Other.High", // 8 }; time_t MetricsLibrary::cached_enabled_time_ = 0; From e8fd968af0f72e9d80f34081a75048d5fdfb14b0 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Wed, 13 Nov 2013 16:28:43 -0800 Subject: [PATCH 101/200] Add two more charger states to Platform.CrOSEvent. This adds SpringPowerSupply.Original.Low and SpringPowerSupply.ChargerIdle. BUG=chrome-os-partner:23973 TEST=manual Change-Id: I35478741e128891667bfd64c45b379c7c9b7d602 Reviewed-on: https://chromium-review.googlesource.com/176707 Reviewed-by: Vincent Palatin Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_library.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index ba8e9009a..3165561ce 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -43,6 +43,8 @@ static const char *kCrosEventNames[] = { "Attestation.OriginSpecificExhausted", // 6 "SpringPowerSupply.Original.High", // 7 "SpringPowerSupply.Other.High", // 8 + "SpringPowerSupply.Original.Low", // 9 + "SpringPowerSupply.ChargerIdle", // 10 }; time_t MetricsLibrary::cached_enabled_time_ = 0; From 33a1baef7a63fd5cc7c639d72188abc4cd8e2ef9 Mon Sep 17 00:00:00 2001 From: Chris Masone Date: Mon, 18 Nov 2013 14:35:09 -0800 Subject: [PATCH 102/200] Add passive_metrics USE flag to platform2 We want to be able to control the inclusion of the metrics_daemon, which passively gathers power stats and the like and sends them through the UMA pipeline iff the user has opted-in to metrics collection. CQ-DEPEND=CL:177244, CL:177291 BUG=None TEST=emerge platform2 with USE='-passive_metrics' on and off, with and without FEATURES=test Change-Id: Id6af84d3de585a156d8ebbcc23117df000de080d Reviewed-on: https://chromium-review.googlesource.com/177217 Reviewed-by: Chris Masone Tested-by: Chris Masone Commit-Queue: Chris Masone --- metrics/metrics.gyp | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index b2644ba23..67e7c379d 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -52,11 +52,6 @@ 'metrics_daemon_main.cc', ] }, - { - 'target_name': 'metrics_daemon', - 'type': 'executable', - 'dependencies': ['libmetrics_daemon'], - }, { 'target_name': 'metrics_client', 'type': 'executable', @@ -67,6 +62,15 @@ }, ], 'conditions': [ + ['USE_passive_metrics == 1', { + 'targets': [ + { + 'target_name': 'metrics_daemon', + 'type': 'executable', + 'dependencies': ['libmetrics_daemon'], + }, + ], + }], ['USE_test == 1', { 'targets': [ { @@ -78,17 +82,6 @@ 'metrics_library_test.cc', ] }, - { - 'target_name': 'metrics_daemon_test', - 'type': 'executable', - 'dependencies': [ - 'libmetrics_daemon', - ], - 'includes': ['../common-mk/common_test.gypi'], - 'sources': [ - 'metrics_daemon_test.cc', - ] - }, { 'target_name': 'counter_test', 'type': 'executable', @@ -109,5 +102,20 @@ }, ], }], + ['USE_passive_metrics == 1 and USE_test == 1', { + 'targets': [ + { + 'target_name': 'metrics_daemon_test', + 'type': 'executable', + 'dependencies': [ + 'libmetrics_daemon', + ], + 'includes': ['../common-mk/common_test.gypi'], + 'sources': [ + 'metrics_daemon_test.cc', + ] + }, + ], + }], ], } From d399bed72c51c063525f7656e6506efe7743b2cf Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Thu, 5 Dec 2013 13:34:51 -0800 Subject: [PATCH 103/200] metrics: fix global-buffer-overflow This fix the problem that the subscript of a global array overflows in some cases. BUG=chromium:326284 TEST=Unittest and suite:smoke passes. Change-Id: I6a19d3bb18a886ee8a29b9a4df5f2b136ce57c5c Reviewed-on: https://chromium-review.googlesource.com/178973 Reviewed-by: Mike Frysinger Commit-Queue: Yunlian Jiang Tested-by: Yunlian Jiang --- metrics/metrics_daemon.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 1161f6c84..1655fae63 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -1048,7 +1048,7 @@ void MetricsDaemon::MemuseCallback() { // Enough active time has passed. Do the work, and (if we succeed) see if // we need to do more. if (MemuseCallbackWork() && - memuse_interval_index_ < arraysize(kMemuseIntervals)) { + memuse_interval_index_ < arraysize(kMemuseIntervals) - 1) { memuse_interval_index_++; ScheduleMemuseCallback(true, 0); } From 0d9a9c961ec44d054d4922a0355938265c73ae95 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 5 Dec 2013 15:55:12 -0800 Subject: [PATCH 104/200] Fix out-of-bound index and clean up code. There was an out-of-bound index after something like 7.5 hours of active use. This probably caused the metrics daemon to restart and pollute some of the memuse statistics, the very ones responsible for the overflow. BUG=chromium:326824 TEST=ran manually and verified memuse is still reported Change-Id: I70709f8cbba63ea1ca5e277b2ab58729e072e127 Reviewed-on: https://chromium-review.googlesource.com/178976 Reviewed-by: Luigi Semenzato Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_daemon.cc | 37 +++++++++++++++---------------------- metrics/metrics_daemon.h | 12 ++++-------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 1655fae63..f3849eeef 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -180,7 +180,7 @@ MetricsDaemon::MetricsDaemon() user_active_(false), usemon_interval_(0), usemon_source_(NULL), - memuse_initial_time_(0), + memuse_final_time_(0), memuse_interval_index_(0), read_sectors_(0), write_sectors_(0), @@ -305,7 +305,8 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, // Start collecting meminfo stats. ScheduleMeminfoCallback(kMetricMeminfoInterval); - ScheduleMemuseCallback(true, 0); + memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0]; + ScheduleMemuseCallback(kMemuseIntervals[0]); // Don't setup D-Bus and GLib in test mode. if (testing) @@ -1012,20 +1013,11 @@ bool MetricsDaemon::FillMeminfo(const string& meminfo_raw, return true; } -void MetricsDaemon::ScheduleMemuseCallback(bool new_callback, - double time_elapsed) { +void MetricsDaemon::ScheduleMemuseCallback(double interval) { if (testing_) { return; } - int interval = kMemuseIntervals[memuse_interval_index_]; - int wait; - if (new_callback) { - memuse_initial_time_ = GetActiveTime(); - wait = interval; - } else { - wait = ceil(interval - time_elapsed); // round up - } - g_timeout_add_seconds(wait, MemuseCallbackStatic, this); + g_timeout_add_seconds(interval, MemuseCallbackStatic, this); } // static @@ -1040,17 +1032,18 @@ void MetricsDaemon::MemuseCallback() { // the callbacks are driven by real time (uptime), we check if we should // reschedule this callback due to intervening sleep periods. double now = GetActiveTime(); - double active_time = now - memuse_initial_time_; - if (active_time < kMemuseIntervals[memuse_interval_index_]) { - // Not enough active time has passed. Reschedule the callback. - ScheduleMemuseCallback(false, active_time); + // Avoid intervals of less than one second. + double remaining_time = ceil(memuse_final_time_ - now); + if (remaining_time > 0) { + ScheduleMemuseCallback(remaining_time); } else { - // Enough active time has passed. Do the work, and (if we succeed) see if - // we need to do more. + // Report stats and advance the measurement interval unless there are + // errors or we've completed the last interval. if (MemuseCallbackWork() && - memuse_interval_index_ < arraysize(kMemuseIntervals) - 1) { - memuse_interval_index_++; - ScheduleMemuseCallback(true, 0); + memuse_interval_index_ < arraysize(kMemuseIntervals)) { + double interval = kMemuseIntervals[memuse_interval_index_++]; + memuse_final_time_ = now + interval; + ScheduleMemuseCallback(interval); } } } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index d3a4f72f3..82372f832 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -329,12 +329,8 @@ class MetricsDaemon { bool FillMeminfo(const std::string& meminfo_raw, std::vector* fields); - // Schedule a memory use callback. |new_callback| is true when this callback - // is scheduled for the first time. When |new_callback| is false, - // |time_elapsed| is the active (non-sleep) time that has passed between now - // and the original callback scheduling time. We use it to reschedule a - // callback that fired too early because we slept. - void ScheduleMemuseCallback(bool new_callback, double time_elapsed); + // Schedule a memory use callback in |interval| seconds. + void ScheduleMemuseCallback(double interval); // Static wrapper for MemuseCallback. Always returns false. static gboolean MemuseCallbackStatic(void* handle); @@ -405,8 +401,8 @@ class MetricsDaemon { // Scheduled daily use monitor source (see ScheduleUseMonitor). GSource* usemon_source_; - // Time of initial scheduling of memuse callback - double memuse_initial_time_; + // End time of current memuse stat collection interval. + double memuse_final_time_; // Selects the wait time for the next memory use callback. unsigned int memuse_interval_index_; From b2c56e1ab0347fcac5c09977b09f873dc763f12f Mon Sep 17 00:00:00 2001 From: Liam McLoughlin Date: Tue, 5 Nov 2013 20:58:18 +0000 Subject: [PATCH 105/200] Add -fvisibility=default to metrics library BUG=chromium:315243 TEST=x86-generic-full and daisy-full trybots pass Change-Id: Ia1f2d41ddf0e54beee0a928bc62b67edd71ac706 Reviewed-on: https://chromium-review.googlesource.com/175770 Reviewed-by: Mike Frysinger Commit-Queue: Liam McLoughlin Tested-by: Liam McLoughlin --- metrics/metrics.gyp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 67e7c379d..9709c35b3 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -30,6 +30,9 @@ { 'target_name': 'libmetrics', 'type': 'shared_library', + 'cflags': [ + '-fvisibility=default', + ], 'sources': [ 'c_metrics_library.cc', 'metrics_library.cc', From 1c5708f69fdf629732df1799519936bf4f8e5b1b Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 17 Dec 2013 15:50:52 -0500 Subject: [PATCH 106/200] metrics_daemon_test: drop unused vars BUG=None TEST=`FEATURES=test emerge-x86-alex platform2` still works Change-Id: I86aa6a7380d226173c642880a3fc50faf3219d13 Reviewed-on: https://chromium-review.googlesource.com/180333 Reviewed-by: Yunlian Jiang Tested-by: Mike Frysinger Commit-Queue: Mike Frysinger --- metrics/metrics_daemon_test.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 2d8b61a07..9f07548bd 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -33,8 +33,6 @@ using ::testing::AtLeast; static const int kSecondsPerDay = 24 * 60 * 60; static const char kTestDir[] = "test"; -static const char kLastFile[] = "test/last"; -static const char kCurrentFile[] = "test/current"; static const char kFakeDiskStatsPath[] = "fake-disk-stats"; static const char kFakeDiskStatsFormat[] = " 1793 1788 %d 105580 " From 754dc924663cf3096062de6cd7d2e5017e8a92fc Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Thu, 19 Dec 2013 01:37:49 -0500 Subject: [PATCH 107/200] add printf attributes to printf func This fixes build warnings when using clang: .../metrics/metrics_library.cc:235:30: warning: format string is not a string literal [-Wformat-nonliteral] format, args); ^~~~~~ BUG=None TEST=`FEATURES=test emerge-x86-alex platform2` works & doesn't warn TEST=`FEATURES=test emerge-daisyplatform2` works & doesn't warn TEST=`FEATURES=test emerge-lumpy platform2` works & doesn't warn Change-Id: I03bfffe328eb1f1742de5b3fe722209e6e91f4bc Reviewed-on: https://chromium-review.googlesource.com/180930 Reviewed-by: Chris Sosa Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- metrics/metrics_library.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 9a4c59bde..4bba96dce 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -138,6 +138,10 @@ class MetricsLibrary : public MetricsLibraryInterface { // The arbitrary |format| argument covers the non-LENGTH portion of the // message. The caller is responsible to store the \0 character // between NAME and VALUE (e.g. "%s%c%d", name, '\0', value). + // + // Ideally we'd use "3, 4" here instead of "4, 5", but it seems clang/gcc + // have an implicit first arg ("this"). http://crbug.com/329356 + __attribute__((__format__(__printf__, 4, 5))) int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, const char* format, ...); From 09a15fa95486f7b6a3197366241827a23392918b Mon Sep 17 00:00:00 2001 From: Darren Krahn Date: Fri, 7 Feb 2014 16:51:15 -0800 Subject: [PATCH 108/200] Added a metric for tracking non-zero TPM dictionary-attack counters. BUG=chromium:341358 TEST=manual Change-Id: I99d3e4279c6d9185b9b515259d4fe511e0d79874 Reviewed-on: https://chromium-review.googlesource.com/185493 Reviewed-by: Luigi Semenzato Commit-Queue: Darren Krahn Tested-by: Darren Krahn --- metrics/metrics_library.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 3165561ce..12b0c7122 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -45,6 +45,7 @@ static const char *kCrosEventNames[] = { "SpringPowerSupply.Other.High", // 8 "SpringPowerSupply.Original.Low", // 9 "SpringPowerSupply.ChargerIdle", // 10 + "TPM.NonZeroDictionaryAttackCounter", // 11 }; time_t MetricsLibrary::cached_enabled_time_ = 0; From 2e6543ddad0b7d64697bb2326a5cfd1ab835a489 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 5 Feb 2014 23:26:25 -0800 Subject: [PATCH 109/200] Update to build against libchrome-242728. This CL updates metrics to build against libchrome-242728 and also converts libmetrics into slotted libraries (libmetrics-180609 and libmetrics-242728). BUG=chromium:341521 BUG=chromium:342866 CQ-DEPEND=CL:186027 CQ-DEPEND=CL:186026 CQ-DEPEND=CL:186037 CQ-DEPEND=CL:186092 CQ-DEPEND=CL:186028 CQ-DEPEND=CL:186029 CQ-DEPEND=CL:186038 CQ-DEPEND=CL:186093 CQ-DEPEND=CL:186100 CQ-DEPEND=CL:186039 TEST=Trybot run on paladin, release, and chromiumos-sdk builders. Change-Id: I09dc3d47cfe24a22864abf217658c63493b35cba Reviewed-on: https://chromium-review.googlesource.com/185187 Reviewed-by: Ben Chan Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/counter_test.cc | 17 +++++---- metrics/libmetrics-180609.gyp | 8 ++++ metrics/libmetrics-242728.gyp | 8 ++++ metrics/libmetrics.gypi | 29 ++++++++++++++ metrics/metrics.gyp | 68 ++++++++++++++------------------- metrics/metrics_daemon.cc | 26 +++++++------ metrics/metrics_daemon.h | 6 +-- metrics/metrics_daemon_main.cc | 2 +- metrics/metrics_daemon_test.cc | 18 +++++---- metrics/metrics_library.cc | 3 +- metrics/metrics_library_test.cc | 35 ++++++++--------- metrics/timer.cc | 1 - metrics/timer.h | 4 ++ metrics/timer_mock.h | 1 - metrics/timer_test.cc | 1 - 15 files changed, 133 insertions(+), 94 deletions(-) create mode 100644 metrics/libmetrics-180609.gyp create mode 100644 metrics/libmetrics-242728.gyp create mode 100644 metrics/libmetrics.gypi diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc index 73a50db4a..c0761e2e7 100644 --- a/metrics/counter_test.cc +++ b/metrics/counter_test.cc @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include @@ -15,6 +15,7 @@ #include "counter_mock.h" // For TaggedCounterMock. #include "metrics_library_mock.h" +using base::FilePath; using ::testing::_; using ::testing::MockFunction; using ::testing::StrictMock; @@ -58,7 +59,7 @@ class TaggedCounterTest : public testing::Test { virtual void TearDown() { logging::SetLogMessageHandler(NULL); test_ = NULL; - file_util::Delete(FilePath(kTestRecordFile), false); + base::DeleteFile(FilePath(kTestRecordFile), false); } // Asserts that the record file contains the specified contents. @@ -74,8 +75,8 @@ class TaggedCounterTest : public testing::Test { } TaggedCounter::Record record; - if (!file_util::ReadFromFD(fd, reinterpret_cast(&record), - sizeof(record))) { + if (!base::ReadFromFD(fd, reinterpret_cast(&record), + sizeof(record))) { testing::Message msg; msg << "Unable to read " << sizeof(record) << " bytes from " << kTestRecordFile; @@ -100,9 +101,9 @@ class TaggedCounterTest : public testing::Test { bool AssertNoOrEmptyRecordFile() { FilePath record_file(counter_.filename_); int64 record_file_size; - return !file_util::PathExists(record_file) || - (file_util::GetFileSize(record_file, &record_file_size) && - record_file_size == 0); + return !base::PathExists(record_file) || + (base::GetFileSize(record_file, &record_file_size) && + record_file_size == 0); } // Adds a reporter call expectation that the specified tag/count @@ -196,7 +197,7 @@ TEST_F(TaggedCounterTest, BadFileLocation) { EXPECT_TRUE(LogContains("Unable to open the persistent counter file: " "No such file or directory")); EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); - file_util::Delete(FilePath(kDoesNotExistFile), false); + base::DeleteFile(FilePath(kDoesNotExistFile), false); } TEST_F(TaggedCounterTest, Flush) { diff --git a/metrics/libmetrics-180609.gyp b/metrics/libmetrics-180609.gyp new file mode 100644 index 000000000..4226712c7 --- /dev/null +++ b/metrics/libmetrics-180609.gyp @@ -0,0 +1,8 @@ +{ + 'variables': { + 'libbase_ver': 180609, + }, + 'includes': [ + '../metrics/libmetrics.gypi', + ], +} diff --git a/metrics/libmetrics-242728.gyp b/metrics/libmetrics-242728.gyp new file mode 100644 index 000000000..a8b194840 --- /dev/null +++ b/metrics/libmetrics-242728.gyp @@ -0,0 +1,8 @@ +{ + 'variables': { + 'libbase_ver': 242728, + }, + 'includes': [ + '../metrics/libmetrics.gypi', + ], +} diff --git a/metrics/libmetrics.gypi b/metrics/libmetrics.gypi new file mode 100644 index 000000000..21af05d4c --- /dev/null +++ b/metrics/libmetrics.gypi @@ -0,0 +1,29 @@ +{ + 'target_defaults': { + 'dependencies': [ + '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + ], + 'variables': { + 'deps': [ + 'libchrome-<(libbase_ver)', + ] + }, + 'cflags_cc': [ + '-fno-exceptions', + ], + }, + 'targets': [ + { + 'target_name': 'libmetrics-<(libbase_ver)', + 'type': 'shared_library', + 'cflags': [ + '-fvisibility=default', + ], + 'sources': [ + 'c_metrics_library.cc', + 'metrics_library.cc', + 'timer.cc', + ], + }, + ], +} diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 9709c35b3..0a5ac48ba 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -1,48 +1,32 @@ { + 'variables': { + 'libbase_ver': 242728, + }, 'target_defaults': { - 'dependencies': [ - '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', - ], - 'variables': { - 'deps': [ - 'dbus-1', - 'dbus-glib-1', - 'glib-2.0', - 'gobject-2.0', - 'gthread-2.0', - 'libchrome-<(libbase_ver)', - ] - }, - 'cflags_cc': [ - '-fno-exceptions', - ], + 'dependencies': [ + '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + ], + 'variables': { + 'deps': [ + 'dbus-1', + 'dbus-glib-1', + 'glib-2.0', + 'gobject-2.0', + 'gthread-2.0', + 'libchrome-<(libbase_ver)', + ] + }, + 'cflags_cc': [ + '-fno-exceptions', + ], }, 'targets': [ - { - 'target_name': 'metrics', - 'type': 'static_library', - 'sources': [ - 'c_metrics_library.cc', - 'metrics_library.cc', - 'timer.cc', - ], - }, - { - 'target_name': 'libmetrics', - 'type': 'shared_library', - 'cflags': [ - '-fvisibility=default', - ], - 'sources': [ - 'c_metrics_library.cc', - 'metrics_library.cc', - 'timer.cc', - ], - }, { 'target_name': 'libmetrics_daemon', 'type': 'static_library', - 'dependencies': ['libmetrics'], + 'dependencies': [ + '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', + ], 'link_settings': { 'libraries': [ '-lrootdev', @@ -58,7 +42,9 @@ { 'target_name': 'metrics_client', 'type': 'executable', - 'dependencies': ['libmetrics'], + 'dependencies': [ + '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', + ], 'sources': [ 'metrics_client.cc', ] @@ -79,7 +65,9 @@ { 'target_name': 'metrics_library_test', 'type': 'executable', - 'dependencies': ['libmetrics'], + 'dependencies': [ + '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', + ], 'includes': ['../common-mk/common_test.gypi'], 'sources': [ 'metrics_library_test.cc', diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index f3849eeef..01fec167d 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -11,15 +11,17 @@ #include #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include "counter.h" +using base::FilePath; +using base::StringPrintf; using base::Time; using base::TimeDelta; using base::TimeTicks; @@ -528,13 +530,13 @@ void MetricsDaemon::ProcessUncleanShutdown() { bool MetricsDaemon::CheckSystemCrash(const string& crash_file) { FilePath crash_detected(crash_file); - if (!file_util::PathExists(crash_detected)) + if (!base::PathExists(crash_detected)) return false; // Deletes the crash-detected file so that the daemon doesn't report // another kernel crash in case it's restarted. - file_util::Delete(crash_detected, - false); // recursive + base::DeleteFile(crash_detected, + false); // recursive return true; } @@ -706,7 +708,7 @@ bool MetricsDaemon::VmStatsParseStats(const char* stats, bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) { string value_string; FilePath* path = new FilePath(vmstats_path_); - if (!file_util::ReadFileToString(*path, &value_string)) { + if (!base::ReadFileToString(*path, &value_string)) { delete path; LOG(WARNING) << "cannot read " << vmstats_path_; return false; @@ -718,11 +720,11 @@ bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) { bool MetricsDaemon::ReadFreqToInt(const string& sysfs_file_name, int* value) { const FilePath sysfs_path(sysfs_file_name); string value_string; - if (!file_util::ReadFileToString(sysfs_path, &value_string)) { + if (!base::ReadFileToString(sysfs_path, &value_string)) { LOG(WARNING) << "cannot read " << sysfs_path.value().c_str(); return false; } - if (!RemoveChars(value_string, "\n", &value_string)) { + if (!base::RemoveChars(value_string, "\n", &value_string)) { LOG(WARNING) << "no newline in " << value_string; // Continue even though the lack of newline is suspicious. } @@ -901,7 +903,7 @@ gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) { bool MetricsDaemon::MeminfoCallback() { string meminfo_raw; const FilePath meminfo_path("/proc/meminfo"); - if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) { + if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) { LOG(WARNING) << "cannot read " << meminfo_path.value().c_str(); return false; } @@ -1051,7 +1053,7 @@ void MetricsDaemon::MemuseCallback() { bool MetricsDaemon::MemuseCallbackWork() { string meminfo_raw; const FilePath meminfo_path("/proc/meminfo"); - if (!file_util::ReadFileToString(meminfo_path, &meminfo_raw)) { + if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) { LOG(WARNING) << "cannot read " << meminfo_path.value().c_str(); return false; } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 82372f832..6ceb13617 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -9,9 +9,9 @@ #include #include -#include +#include #include -#include +#include #include // for FRIEND_TEST #include "metrics_library.h" @@ -195,7 +195,7 @@ class MetricsDaemon { void ConfigureCrashFrequencyReporter(const char* histogram_name); // Returns file path to persistent file for generating given histogram. - FilePath GetHistogramPath(const char* histogram_name); + base::FilePath GetHistogramPath(const char* histogram_name); // Creates the event loop and enters it. void Loop(); diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 643a2bf35..5fb7507e7 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -4,7 +4,7 @@ #include -#include +#include #include #include diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 9f07548bd..7f98a1956 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -16,6 +16,8 @@ #include "metrics_daemon.h" #include "metrics_library_mock.h" +using base::FilePath; +using base::StringPrintf; using base::Time; using base::TimeTicks; using chromeos_metrics::FrequencyCounter; @@ -132,8 +134,8 @@ class MetricsDaemonTest : public testing::Test { EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); - file_util::Delete(FilePath(kTestDir), true); - file_util::CreateDirectory(FilePath(kTestDir)); + base::DeleteFile(FilePath(kTestDir), true); + base::CreateDirectory(FilePath(kTestDir)); } virtual void TearDown() { @@ -261,7 +263,7 @@ class MetricsDaemonTest : public testing::Test { // Creates or overwrites an input file containing a fake CPU frequency. void CreateFakeCpuFrequencyFile(const char* filename, int frequency) { FilePath path(filename); - file_util::Delete(path, false); + base::DeleteFile(path, false); std::string frequency_string = StringPrintf("%d\n", frequency); int frequency_string_length = frequency_string.length(); EXPECT_EQ(frequency_string.length(), @@ -291,12 +293,12 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) { FilePath crash_detected(kKernelCrashDetected); file_util::WriteFile(crash_detected, "", 0); - EXPECT_TRUE(file_util::PathExists(crash_detected)); + EXPECT_TRUE(base::PathExists(crash_detected)); EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected)); - EXPECT_FALSE(file_util::PathExists(crash_detected)); + EXPECT_FALSE(base::PathExists(crash_detected)); EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected)); - EXPECT_FALSE(file_util::PathExists(crash_detected)); - file_util::Delete(crash_detected, false); + EXPECT_FALSE(base::PathExists(crash_detected)); + base::DeleteFile(crash_detected, false); } TEST_F(MetricsDaemonTest, ReportDailyUse) { diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 12b0c7122..c53c16b12 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -86,8 +86,7 @@ static int WriteFileDescriptor(const int fd, const char* data, int size) { MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), - consent_file_(kConsentFile), - policy_provider_(NULL) {} + consent_file_(kConsentFile) {} // We take buffer and buffer_size as parameters in order to simplify testing // of various alignments of the |device_name| with |buffer_size|. diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 03ccd4b7b..a748ed5c3 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -13,13 +13,14 @@ #include "c_metrics_library.h" #include "metrics_library.h" -static const FilePath kTestUMAEventsFile("test-uma-events"); -static const char kTestMounts[] = "test-mounts"; - +using base::FilePath; using ::testing::_; using ::testing::Return; using ::testing::AnyNumber; +static const FilePath kTestUMAEventsFile("test-uma-events"); +static const char kTestMounts[] = "test-mounts"; + ACTION_P(SetMetricsPolicy, enabled) { *arg0 = enabled; return true; @@ -46,8 +47,8 @@ class MetricsLibraryTest : public testing::Test { } virtual void TearDown() { - file_util::Delete(FilePath(kTestMounts), false); - file_util::Delete(kTestUMAEventsFile, false); + base::DeleteFile(FilePath(kTestMounts), false); + base::DeleteFile(kTestUMAEventsFile, false); } void VerifyEnabledCacheHit(bool to_value); @@ -182,7 +183,7 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { char buf[100]; const int kLen = 40; EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); - EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); char exp[kLen]; sprintf(exp, "%c%c%c%clinearhistogram%cTest.EnumMetric 1 3", @@ -194,7 +195,7 @@ TEST_F(MetricsLibraryTest, SendMessageToChrome) { EXPECT_TRUE(lib_.SendMessageToChrome(4, "test")); EXPECT_TRUE(lib_.SendMessageToChrome(7, "content")); std::string uma_events; - EXPECT_TRUE(file_util::ReadFileToString(kTestUMAEventsFile, &uma_events)); + EXPECT_TRUE(base::ReadFileToString(kTestUMAEventsFile, &uma_events)); EXPECT_EQ("testcontent", uma_events); } @@ -205,14 +206,14 @@ TEST_F(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation) { lib_.uma_events_file_ = kDoesNotExistFile; static const char kDummyMessage[] = "Dummy Message"; EXPECT_FALSE(lib_.SendMessageToChrome(strlen(kDummyMessage), kDummyMessage)); - file_util::Delete(FilePath(kDoesNotExistFile), false); + base::DeleteFile(FilePath(kDoesNotExistFile), false); } TEST_F(MetricsLibraryTest, SendToUMA) { char buf[100]; const int kLen = 37; EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); - EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); char exp[kLen]; sprintf(exp, "%c%c%c%chistogram%cTest.Metric 2 1 100 50", kLen, 0, 0, 0, 0); @@ -223,7 +224,7 @@ TEST_F(MetricsLibraryTest, SendUserActionToUMA) { char buf[100]; const int kLen = 30; EXPECT_TRUE(lib_.SendUserActionToUMA("SomeKeyPressed")); - EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); char exp[kLen]; sprintf(exp, "%c%c%c%cuseraction%cSomeKeyPressed", kLen, 0, 0, 0, 0); @@ -234,7 +235,7 @@ TEST_F(MetricsLibraryTest, SendSparseToUMA) { char buf[100]; const int kLen = 4 + sizeof("sparsehistogram") + sizeof("Test.Sparse 1234"); EXPECT_TRUE(lib_.SendSparseToUMA("Test.Sparse", 1234)); - EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); char exp[kLen]; sprintf(exp, "%c%c%c%csparsehistogram%cTest.Sparse 1234", kLen, 0, 0, 0, 0); @@ -248,7 +249,7 @@ TEST_F(MetricsLibraryTest, SendCrashToUMA) { 0, 0, 0, 0, 0) + 1; exp[0] = len; char buf[100]; - EXPECT_EQ(len, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(len, base::ReadFile(kTestUMAEventsFile, buf, 100)); EXPECT_EQ(0, memcmp(exp, buf, len)); } @@ -275,7 +276,7 @@ class CMetricsLibraryTest : public testing::Test { virtual void TearDown() { CMetricsLibraryDelete(lib_); - file_util::Delete(kTestUMAEventsFile, false); + base::DeleteFile(kTestUMAEventsFile, false); } CMetricsLibrary lib_; @@ -297,7 +298,7 @@ TEST_F(CMetricsLibraryTest, SendEnumToUMA) { char buf[100]; const int kLen = 40; EXPECT_TRUE(CMetricsLibrarySendEnumToUMA(lib_, "Test.EnumMetric", 1, 3)); - EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); char exp[kLen]; sprintf(exp, "%c%c%c%clinearhistogram%cTest.EnumMetric 1 3", @@ -309,7 +310,7 @@ TEST_F(CMetricsLibraryTest, SendToUMA) { char buf[100]; const int kLen = 37; EXPECT_TRUE(CMetricsLibrarySendToUMA(lib_, "Test.Metric", 2, 1, 100, 50)); - EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); char exp[kLen]; sprintf(exp, "%c%c%c%chistogram%cTest.Metric 2 1 100 50", kLen, 0, 0, 0, 0); @@ -320,7 +321,7 @@ TEST_F(CMetricsLibraryTest, SendUserActionToUMA) { char buf[100]; const int kLen = 30; EXPECT_TRUE(CMetricsLibrarySendUserActionToUMA(lib_, "SomeKeyPressed")); - EXPECT_EQ(kLen, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); char exp[kLen]; sprintf(exp, "%c%c%c%cuseraction%cSomeKeyPressed", kLen, 0, 0, 0, 0); @@ -333,7 +334,7 @@ TEST_F(CMetricsLibraryTest, SendCrashToUMA) { int len = sprintf(exp, "%c%c%c%ccrash%cuser", 0, 0, 0, 0, 0) + 1; exp[0] = len; EXPECT_TRUE(CMetricsLibrarySendCrashToUMA(lib_, "user")); - EXPECT_EQ(len, file_util::ReadFile(kTestUMAEventsFile, buf, 100)); + EXPECT_EQ(len, base::ReadFile(kTestUMAEventsFile, buf, 100)); EXPECT_EQ(0, memcmp(exp, buf, len)); } diff --git a/metrics/timer.cc b/metrics/timer.cc index c8e617612..90948b5ca 100644 --- a/metrics/timer.cc +++ b/metrics/timer.cc @@ -7,7 +7,6 @@ #include #include -#include #include "metrics_library.h" diff --git a/metrics/timer.h b/metrics/timer.h index b94977387..ce8887303 100644 --- a/metrics/timer.h +++ b/metrics/timer.h @@ -10,7 +10,11 @@ #include #include +#if BASE_VER >= 242728 +#include +#else #include +#endif #include // for FRIEND_TEST class MetricsLibraryInterface; diff --git a/metrics/timer_mock.h b/metrics/timer_mock.h index 1f0c9de60..6034ee3c9 100644 --- a/metrics/timer_mock.h +++ b/metrics/timer_mock.h @@ -9,7 +9,6 @@ #include #include -#include #include #include "timer.h" diff --git a/metrics/timer_test.cc b/metrics/timer_test.cc index 03475d33c..d64beab3a 100644 --- a/metrics/timer_test.cc +++ b/metrics/timer_test.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include -#include #include #include From 46465a7cf2d7d71025a57aea6df7fa83ab7eff7c Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 12 Feb 2014 14:59:57 -0800 Subject: [PATCH 110/200] Remove unused Makefile. metrics is now part of platform2 and built using gyp. BUG=chromium:342154 TEST=Trybot runs on x86-mario,lumpy,daisy paladin and release builders. Change-Id: I69c5622399cab9d8508dc75310972ae638f0cac7 Reviewed-on: https://chromium-review.googlesource.com/186194 Commit-Queue: Ben Chan Tested-by: Ben Chan Reviewed-by: Mike Frysinger --- metrics/Makefile | 89 ------------------------------------------------ 1 file changed, 89 deletions(-) delete mode 100644 metrics/Makefile diff --git a/metrics/Makefile b/metrics/Makefile deleted file mode 100644 index 0774a24d9..000000000 --- a/metrics/Makefile +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -# -# Makefile for metrics utilities -- library, client and daemon -# - -BASE_VER ?= 180609 -PKG_CONFIG ?= pkg-config -PC_DEPS = dbus-1 glib-2.0 gthread-2.0 dbus-glib-1 \ - libchrome-$(BASE_VER) libchromeos-$(BASE_VER) -PC_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(PC_DEPS)) -LDLIBS := $(shell $(PKG_CONFIG) --libs $(PC_DEPS)) - -CXXFLAGS += -Wall -Werror -fPIC -fno-exceptions -CPPFLAGS += $(PC_CFLAGS) - -CLIENT = metrics_client -DAEMON = metrics_daemon -DAEMON_TEST = metrics_daemon_test -LIB = libmetrics.a -SHAREDLIB = libmetrics.so -LIB_TEST = metrics_library_test -COUNTER_TEST = counter_test -TIMER_TEST = timer_test - -TESTCOUNTER_OBJS = \ - counter.o \ - counter_test.o -TESTTIMER_OBJS = \ - timer.o \ - timer_test.o -CLIENT_OBJS = \ - metrics_client.o -DAEMON_OBJS = \ - counter.o \ - metrics_daemon.o \ - metrics_daemon_main.o -TESTDAEMON_OBJS = \ - counter.o \ - metrics_daemon.o \ - metrics_daemon_test.o -LIB_OBJS = \ - c_metrics_library.o \ - metrics_library.o \ - timer.o -TESTLIB_OBJS = \ - metrics_library_test.o - -POLICY_LIBS = -lpolicy-$(BASE_VER) -TEST_LIBS = -lgmock -lgtest -DAEMON_LIBS = -lgflags -lrootdev - -LINK = $(CXX) $(CXXFLAGS) $(LDFLAGS) $^ -o $@ $(LDLIBS) - -all: $(LIB) $(SHAREDLIB) $(CLIENT) $(DAEMON) - -tests: $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(TIMER_TEST) - -$(CLIENT): $(CLIENT_OBJS) $(SHAREDLIB) - $(LINK) - -$(COUNTER_TEST): $(TESTCOUNTER_OBJS) - $(LINK) $(TEST_LIBS) - -$(TIMER_TEST): $(TESTTIMER_OBJS) - $(LINK) $(TEST_LIBS) - -$(DAEMON): $(DAEMON_OBJS) $(SHAREDLIB) - $(LINK) $(DAEMON_LIBS) - -$(DAEMON_TEST): $(TESTDAEMON_OBJS) - $(LINK) $(DAEMON_LIBS) $(TEST_LIBS) - -$(LIB): $(LIB_OBJS) - $(AR) rcs $@ $^ - -$(SHAREDLIB): $(LIB_OBJS) - $(LINK) -shared $(POLICY_LIBS) - -$(LIB_TEST): $(TESTLIB_OBJS) $(SHAREDLIB) - $(LINK) $(POLICY_LIBS) $(TEST_LIBS) - -%.o: %.cc - $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ - -clean: - rm -f $(CLIENT) $(DAEMON) $(LIB) $(SHAREDLIB) *.o - rm -f $(COUNTER_TEST) $(DAEMON_TEST) $(LIB_TEST) $(TIMER_TEST) From c5a92347a5134d0320e66034b6eb7c4b2e663782 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 14 Feb 2014 15:05:51 -0800 Subject: [PATCH 111/200] metrics daemon: change location of flag files For security reasons, we changed the location of some flag files to /var/run from /tmp, but we didn't update this program to look for the files in their new location. This corrects the problem. BUG=chromium:200181 TEST=manually verified that kernel crash counters increase BRANCH=none Change-Id: I03efe6179a1ad8dfd4c8cbb1eccf24c7a8494058 Reviewed-on: https://chromium-review.googlesource.com/186692 Reviewed-by: Chris Masone Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_daemon.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 01fec167d..44d8d4ff2 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -52,9 +52,9 @@ static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek; static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute; static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute; -const char kKernelCrashDetectedFile[] = "/tmp/kernel-crash-detected"; +const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected"; static const char kUncleanShutdownDetectedFile[] = - "/tmp/unclean-shutdown-detected"; + "/var/run/unclean-shutdown-detected"; // static metrics parameters const char MetricsDaemon::kMetricDailyUseTimeName[] = From 859b3f0ad350dcb6f6f7184c6efdc42038046780 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Wed, 5 Feb 2014 15:33:19 -0800 Subject: [PATCH 112/200] metrics: add per-version cumulative counters This adds counters that accumulate some measurement across an OS version; they are reset at version updates, but reported more frequently (for instance, daily). Such counts could be obtained by pseudonymous dremel queries, but this is more convenient. The code replaces the "tag" datum in the counters with two tags: a "report tag" and a "reset tag". When the report tag changes, the count is reported but not reset. When the reset tag changes, the count is both reported and reset. This also adds one usage of the new counter which tracks the total number of kernel crashes since the most recent OS version update. The state machine in counter.cc changes a bit because it's no longer true that a counter is reset after reporting it. That logic is still rather confusing, and could use a rewrite. BUG=chromium:339588 TEST=ran on target under various situations BRANCH=none Change-Id: I5f83731e1a3d6e055b6d0f89111c9ffc60ccfcb9 Reviewed-on: https://chromium-review.googlesource.com/185081 Reviewed-by: Daniel Erat Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/counter.cc | 109 ++++++++++++++++++------ metrics/counter.h | 129 +++++++++++++++++++++-------- metrics/counter_mock.h | 4 +- metrics/counter_test.cc | 142 +++++++++++++++++++++++--------- metrics/metrics_daemon.cc | 107 ++++++++++++++++++------ metrics/metrics_daemon.h | 25 +++++- metrics/metrics_daemon_test.cc | 36 ++++---- metrics/metrics_library_test.cc | 2 +- 8 files changed, 406 insertions(+), 148 deletions(-) diff --git a/metrics/counter.cc b/metrics/counter.cc index 9d94597db..f71b4fbf6 100644 --- a/metrics/counter.cc +++ b/metrics/counter.cc @@ -14,8 +14,11 @@ namespace chromeos_metrics { // TaggedCounter::Record implementation. -void TaggedCounter::Record::Init(int32 tag, int32 count) { - tag_ = tag; +void TaggedCounter::Record::Init(uint32 report_tag, + uint32 reset_tag, + int32 count) { + report_tag_ = report_tag; + reset_tag_ = reset_tag; count_ = (count > 0) ? count : 0; } @@ -48,19 +51,24 @@ void TaggedCounter::Init(const char* filename, record_state_ = kRecordInvalid; } -void TaggedCounter::Update(int32 tag, int32 count) { - UpdateInternal(tag, +void TaggedCounter::Update(uint32 report_tag, uint32 reset_tag, int32 count) { + UpdateInternal(report_tag, + reset_tag, count, false); // No flush. } void TaggedCounter::Flush() { - UpdateInternal(0, // tag + UpdateInternal(0, // report_tag + 0, // reset_tag 0, // count true); // Do flush. } -void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) { +void TaggedCounter::UpdateInternal(uint32 report_tag, + uint32 reset_tag, + int32 count, + bool flush) { if (flush) { // Flushing but record is null, so nothing to do. if (record_state_ == kRecordNull) @@ -68,11 +76,15 @@ void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) { } else { // If there's no new data and the last record in the aggregation // file is with the same tag, there's nothing to do. - if (count <= 0 && record_state_ == kRecordValid && record_.tag() == tag) + if (count <= 0 && + record_state_ == kRecordValid && + record_.report_tag() == report_tag && + record_.reset_tag() == reset_tag) return; } - DLOG(INFO) << "tag: " << tag << " count: " << count << " flush: " << flush; + DLOG(INFO) << "report_tag: " << report_tag << " reset_tag: " << reset_tag + << " count: " << count << " flush: " << flush; DCHECK(!filename_.empty()); // NOTE: The assumption is that this TaggedCounter object is the @@ -84,10 +96,9 @@ void TaggedCounter::UpdateInternal(int32 tag, int32 count, bool flush) { PLOG(WARNING) << "Unable to open the persistent counter file"; return; } - ReadRecord(fd); - ReportRecord(tag, flush); - UpdateRecord(tag, count, flush); + ReportRecord(report_tag, reset_tag, flush); + UpdateRecord(report_tag, reset_tag, count, flush); WriteRecord(fd); HANDLE_EINTR(close(fd)); @@ -97,6 +108,9 @@ void TaggedCounter::ReadRecord(int fd) { if (record_state_ != kRecordInvalid) return; + // Three cases: 1. empty file (first time), 2. size of file == size of record + // (normal case), 3. size of file != size of record (new version). We treat + // cases 1 and 3 identically. if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) { if (record_.count() >= 0) { record_state_ = kRecordValid; @@ -111,42 +125,59 @@ void TaggedCounter::ReadRecord(int fd) { record_state_ = kRecordNull; } -void TaggedCounter::ReportRecord(int32 tag, bool flush) { +void TaggedCounter::ReportRecord(uint32 report_tag, + uint32 reset_tag, + bool flush) { // If no valid record, there's nothing to report. if (record_state_ != kRecordValid) { DCHECK_EQ(record_state_, kRecordNull); return; } - // If the current record has the same tag as the new tag, it's not - // ready to be reported yet. - if (!flush && record_.tag() == tag) + // If the current record has the same tags as the new ones, it's + // not ready to be reported yet. + if (!flush && + record_.report_tag() == report_tag && + record_.reset_tag() == reset_tag) return; if (reporter_) { - reporter_(reporter_handle_, record_.tag(), record_.count()); + reporter_(reporter_handle_, record_.count()); + } + // If the report tag has changed, update it to the current one. + if (record_.report_tag() != report_tag) { + record_.set_report_tag(report_tag); + record_state_ = kRecordValidDirty; + } + // If the reset tag has changed, the new state is NullDirty, no + // matter whether the report tag has changed or not. + if (record_.reset_tag() != reset_tag) { + record_state_ = kRecordNullDirty; } - record_state_ = kRecordNullDirty; } -void TaggedCounter::UpdateRecord(int32 tag, int32 count, bool flush) { - if (flush) { - DCHECK(record_state_ == kRecordNull || record_state_ == kRecordNullDirty); +void TaggedCounter::UpdateRecord(uint32 report_tag, + uint32 reset_tag, + int32 count, + bool flush) { + if (flush && + (record_state_ == kRecordNull || record_state_ == kRecordNullDirty)) return; - } switch (record_state_) { case kRecordNull: case kRecordNullDirty: // Current record is null, starting a new record. - record_.Init(tag, count); + record_.Init(report_tag, reset_tag, count); record_state_ = kRecordValidDirty; break; case kRecordValid: + case kRecordValidDirty: // If there's an existing record for the current tag, // accumulates the counts. - DCHECK_EQ(record_.tag(), tag); + DCHECK_EQ(record_.report_tag(), report_tag); + DCHECK_EQ(record_.reset_tag(), reset_tag); if (count > 0) { record_.Add(count); record_state_ = kRecordValidDirty; @@ -212,7 +243,7 @@ void TaggedCounterReporter::Init(const char* filename, CHECK(buckets_ > 0); } -void TaggedCounterReporter::Report(void* handle, int32 tag, int32 count) { +void TaggedCounterReporter::Report(void* handle, int32 count) { TaggedCounterReporter* this_reporter = reinterpret_cast(handle); DLOG(INFO) << "received metric: " << this_reporter->histogram_name_ @@ -236,17 +267,41 @@ FrequencyCounter::~FrequencyCounter() { void FrequencyCounter::Init(TaggedCounterInterface* tagged_counter, time_t cycle_duration) { tagged_counter_.reset(tagged_counter); - DCHECK(cycle_duration > 0); + DCHECK_GT(cycle_duration, 0); cycle_duration_ = cycle_duration; } void FrequencyCounter::UpdateInternal(int32 count, time_t now) { - DCHECK(tagged_counter_.get() != NULL); - tagged_counter_->Update(GetCycleNumber(now), count); + DCHECK(tagged_counter_.get()); + tagged_counter_->Update(0, GetCycleNumber(now), count); } int32 FrequencyCounter::GetCycleNumber(time_t now) { return now / cycle_duration_; } +VersionCounter::VersionCounter() : cycle_duration_(1) { +} + +VersionCounter::~VersionCounter() { +} + +void VersionCounter::Init(TaggedCounterInterface* tagged_counter, + time_t cycle_duration) { + tagged_counter_.reset(tagged_counter); + DCHECK_GT(cycle_duration, 0); + cycle_duration_ = cycle_duration; +} + +void VersionCounter::UpdateInternal(int32 count, + time_t now, + uint32 version_hash) { + DCHECK(tagged_counter_.get()); + tagged_counter_->Update(GetCycleNumber(now), version_hash, count); +} + +int32 VersionCounter::GetCycleNumber(time_t now) { + return now / cycle_duration_; +} + } // namespace chromeos_metrics diff --git a/metrics/counter.h b/metrics/counter.h index a360e1f7b..679aaafc9 100644 --- a/metrics/counter.h +++ b/metrics/counter.h @@ -20,15 +20,14 @@ namespace chromeos_metrics { const int kSecondsPerDay = 60 * 60 * 24; const int kSecondsPerWeek = kSecondsPerDay * 7; -// TaggedCounter maintains a persistent storage (i.e., a file) -// aggregation counter for a given tag (e.g., day, hour) that survives +// TaggedCounter maintains a persistent storage (i.e., a file) aggregation +// counter for given tags (e.g., day, hour, version number) that survives // system shutdowns, reboots and crashes, as well as daemon process -// restarts. The counter object is initialized by pointing to the -// persistent storage file and providing a callback used for reporting -// aggregated data. The counter can then be updated with additional -// event counts. The aggregated count is reported through the -// callback when the counter is explicitly flushed or when data for a -// new tag arrives. +// restarts. The counter object is initialized by pointing to the persistent +// storage file and providing a callback used for reporting aggregated data. +// The counter can then be updated with additional event counts. The +// aggregated count is reported through the callback when the counter is +// explicitly flushed or when data for a new tag arrives. // // The primary reason for using an interface is to allow easier unit // testing in clients through mocking thus avoiding file access and @@ -41,16 +40,16 @@ class TaggedCounterInterface { // aggregated data is discarded. // // |handle| is the |reporter_handle| pointer passed through Init. - // |tag| is the tag associated with the aggregated count. // |count| is aggregated count. - typedef void (*Reporter)(void* handle, int32 tag, int32 count); + typedef void (*Reporter)(void* handle, int32 count); virtual ~TaggedCounterInterface() {} - // Adds |count| of events for the given |tag|. If there's an - // existing aggregated count for a different tag, it's reported - // through the reporter callback and discarded. - virtual void Update(int32 tag, int32 count) = 0; + // Adds |count| of events for the given tags. If there's an existing + // aggregated count for different tags, it's reported through the reporter + // callback, and optionally discarded, depending on whether |report_tag| + // changed or |reset_tag| changed. + virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count) = 0; // Reports the current aggregated count (if any) through the // reporter callback and discards it. @@ -73,7 +72,7 @@ class TaggedCounter : public TaggedCounterInterface { Reporter reporter, void* reporter_handle); // Implementation of interface methods. - virtual void Update(int32 tag, int32 count); + virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count); virtual void Flush(); private: @@ -84,7 +83,7 @@ class TaggedCounter : public TaggedCounterInterface { FRIEND_TEST(TaggedCounterTest, InitFromFile); FRIEND_TEST(TaggedCounterTest, Update); - // The current tag/count record is cached by the counter object to + // The current record is cached by the counter object to // avoid potentially unnecessary I/O. The cached record can be in // one of the following states: enum RecordState { @@ -95,34 +94,42 @@ class TaggedCounter : public TaggedCounterInterface { kRecordValidDirty // Current record valid, persistent storage is invalid. }; - // Defines the tag/count record. Objects of this class are synced + // Defines the record. Objects of this class are synced // with the persistent storage through binary reads/writes. class Record { public: - // Creates a new Record with |tag_| and |count_| reset to 0. - Record() : tag_(0), count_(0) {} + // Creates a new Record with all fields reset to 0. + Record() : report_tag_(0), reset_tag_(0), count_(0) {} - // Initializes with |tag| and |count|. If |count| is negative, - // |count_| is set to 0. - void Init(int32 tag, int32 count); + // Initializes with |report_tag|, |reset_tag| and |count|. + // If |count| is negative, |count_| is set to 0. + void Init(uint32 report_tag, uint32 reset_tag, int32 count); // Adds |count| to the current |count_|. Negative |count| is // ignored. In case of positive overflow, |count_| is saturated to // kint32max. void Add(int32 count); - int32 tag() const { return tag_; } + uint32 report_tag() const { return report_tag_; } + void set_report_tag(uint32 report_tag) { report_tag_ = report_tag; } + uint32 reset_tag() const { return reset_tag_; } int32 count() const { return count_; } private: - int32 tag_; + // When |report_tag_| changes, the counter is reported as a UMA sample. + // When |reset_tag_| changes, the counter is both reported and reset. + uint32 report_tag_; + uint32 reset_tag_; int32 count_; }; // Implementation of the Update and Flush methods. Goes through the // necessary steps to read, report, update, and sync the aggregated // record. - void UpdateInternal(int32 tag, int32 count, bool flush); + void UpdateInternal(uint32 report_tag, + uint32 reset_tag, + int32 count, + bool flush); // If the current cached record is invalid, reads it from persistent // storage specified through file descriptor |fd| and updates the @@ -130,17 +137,19 @@ class TaggedCounter : public TaggedCounterInterface { // persistent storage contents. void ReadRecord(int fd); - // If there's an existing valid record and either |flush| is true, - // or the new |tag| is different than the old one, reports the - // aggregated data through the reporter callback and resets the - // cached record. - void ReportRecord(int32 tag, bool flush); + // If there's an existing valid record and either |flush| is true, or either + // new tag is different than the old one, reports the aggregated data through + // the reporter callback, and possibly resets the cached record. + void ReportRecord(uint32 report_tag, uint32 reset_tag, bool flush); - // Updates the cached record given the new |tag| and |count|. This + // Updates the cached record given the new tags and |count|. This // method expects either a null cached record, or a valid cached - // record with the same tag as |tag|. If |flush| is true, the method + // record with the same tags as given. If |flush| is true, the method // asserts that the cached record is null and returns. - void UpdateRecord(int32 tag, int32 count, bool flush); + void UpdateRecord(uint32 report_tag, + uint32 reset_tag, + int32 count, + bool flush); // If the cached record state is dirty, updates the persistent // storage specified through file descriptor |fd| and switches the @@ -187,8 +196,8 @@ class TaggedCounterReporter : public TaggedCounterInterface { int buckets); // Implementation of interface method. - virtual void Update(int32 tag, int32 count) { - tagged_counter_->Update(tag, count); + virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count) { + tagged_counter_->Update(report_tag, reset_tag, count); } // Implementation of interface method. virtual void Flush() { @@ -216,7 +225,7 @@ class TaggedCounterReporter : public TaggedCounterInterface { friend class TaggedCounterReporterTest; FRIEND_TEST(TaggedCounterReporterTest, Report); - static void Report(void* handle, int32 tag, int32 count); + static void Report(void* handle, int32 count); static MetricsLibraryInterface* metrics_lib_; scoped_ptr tagged_counter_; @@ -283,6 +292,54 @@ class FrequencyCounter { scoped_ptr tagged_counter_; }; +// VersionCounter is like a FrequencyCounter, but it exposes +// separate "report" and "reset" tags, for counters that should +// be reported more often than they are reset. +class VersionCounter { + public: + VersionCounter(); + virtual ~VersionCounter(); + + // Initialize a version counter, which is necessary before first use. + // |tagged_counter| is used to store the counts. Its memory is managed + // by this FrequencyCounter. |cycle_duration| is the number of seconds in a + // cycle. + virtual void Init(TaggedCounterInterface* tagged_counter, + time_t cycle_duration); + // Record that |count| events have occurred. The + // time is implicitly assumed to be the time of the call. + // The version hash is passed. + virtual void Update(int32 count, uint32 version_hash) { + UpdateInternal(count, time(NULL), version_hash); + } + + // Reports the counter if enough time has passed, and also resets it if the + // version number has changed. + virtual void FlushOnChange(uint32 version_hash) { + UpdateInternal(0, time(NULL), version_hash); + } + + // Accessor function. + const TaggedCounterInterface& tagged_counter() const { + return *tagged_counter_; + } + + time_t cycle_duration() const { + return cycle_duration_; + } + + private: + friend class VersionCounterTest; + FRIEND_TEST(VersionCounterTest, UpdateInternal); + + void UpdateInternal(int32 count, time_t now, uint32 version_hash); + // TODO(semenzato): it's generally better to use base::TimeTicks (for + // monotonically-increasing timestamps) or base::Time (for wall time) + int32 GetCycleNumber(time_t now); + time_t cycle_duration_; + scoped_ptr tagged_counter_; +}; + } // namespace chromeos_metrics #endif // METRICS_COUNTER_H_ diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h index 267aaca99..77272a18f 100644 --- a/metrics/counter_mock.h +++ b/metrics/counter_mock.h @@ -17,7 +17,7 @@ class TaggedCounterMock : public TaggedCounter { public: MOCK_METHOD3(Init, void(const char* filename, Reporter reporter, void* reporter_handle)); - MOCK_METHOD2(Update, void(int tag, int count)); + MOCK_METHOD3(Update, void(uint32 report_tag, uint32 reset_tag, int32 count)); MOCK_METHOD0(Flush, void()); }; @@ -28,7 +28,7 @@ class TaggedCounterReporterMock : public TaggedCounterReporter { int min, int max, int nbuckets)); - MOCK_METHOD2(Update, void(int32 tag, int32 count)); + MOCK_METHOD3(Update, void(uint32 report_tag, uint32 reset_tag, int32 count)); MOCK_METHOD0(Flush, void()); }; diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc index c0761e2e7..90c3cd8ce 100644 --- a/metrics/counter_test.cc +++ b/metrics/counter_test.cc @@ -28,7 +28,8 @@ static const char kDoesNotExistFile[] = "/does/not/exist"; class RecordTest : public testing::Test { protected: virtual void SetUp() { - EXPECT_EQ(0, record_.tag()); + EXPECT_EQ(0, record_.report_tag()); + EXPECT_EQ(0, record_.reset_tag()); EXPECT_EQ(0, record_.count()); } @@ -62,11 +63,28 @@ class TaggedCounterTest : public testing::Test { base::DeleteFile(FilePath(kTestRecordFile), false); } - // Asserts that the record file contains the specified contents. - testing::AssertionResult AssertRecord(const char* expr_tag, + testing::AssertionResult AssertRecord(const char* expr_reset_tag, const char* expr_count, - int32 expected_tag, + uint32 expected_reset_tag, int32 expected_count) { + return AssertRecordFull(12345, expected_reset_tag, expected_count, false); + } + + // Asserts that the record file contains the specified contents. + testing::AssertionResult AssertRecord3(const char* expr_report_tag, + const char* expr_reset_tag, + const char* expr_count, + uint32 expected_report_tag, + uint32 expected_reset_tag, + int32 expected_count) { + return AssertRecordFull(expected_report_tag, expected_reset_tag, + expected_count, true); + } + + testing::AssertionResult AssertRecordFull(uint32 expected_report_tag, + uint32 expected_reset_tag, + int32 expected_count, + bool check_report_tag) { int fd = HANDLE_EINTR(open(kTestRecordFile, O_RDONLY)); if (fd < 0) { testing::Message msg; @@ -84,10 +102,16 @@ class TaggedCounterTest : public testing::Test { return testing::AssertionFailure(msg); } - if (record.tag() != expected_tag || record.count() != expected_count) { + if ((check_report_tag && (record.report_tag() != expected_report_tag)) || + record.reset_tag() != expected_reset_tag || + record.count() != expected_count) { testing::Message msg; - msg << "actual record (" << record.tag() << ", " << record.count() - << ") expected (" << expected_tag << ", " << expected_count << ")"; + msg << "actual record (" << record.report_tag() << ", " + << record.reset_tag() << ", " << record.count() + << ") expected (" << expected_report_tag << ", " + << expected_reset_tag << ", " << expected_count << ")"; + if (!check_report_tag) + msg << "\n(ignore differences in the first field)"; HANDLE_EINTR(close(fd)); return testing::AssertionFailure(msg); } @@ -99,7 +123,7 @@ class TaggedCounterTest : public testing::Test { // Returns true if the persistent record file does not exist or is // empty, false otherwise. bool AssertNoOrEmptyRecordFile() { - FilePath record_file(counter_.filename_); + base::FilePath record_file(counter_.filename_); int64 record_file_size; return !base::PathExists(record_file) || (base::GetFileSize(record_file, &record_file_size) && @@ -108,18 +132,18 @@ class TaggedCounterTest : public testing::Test { // Adds a reporter call expectation that the specified tag/count // callback will be generated. - void ExpectReporterCall(int32 tag, int32 count) { - EXPECT_CALL(reporter_, Call(_, tag, count)) + void ExpectReporterCall(int32 count) { + EXPECT_CALL(reporter_, Call(_, count)) .Times(1) .RetiresOnSaturation(); } // The reporter callback forwards the call to the reporter mock so // that we can set call expectations. - static void Reporter(void* handle, int32 tag, int32 count) { + static void Reporter(void* handle, int32 count) { TaggedCounterTest* test = static_cast(handle); ASSERT_FALSE(NULL == test); - test->reporter_.Call(handle, tag, count); + test->reporter_.Call(handle, count); } // Collects log messages in the |log_| member string so that they @@ -148,8 +172,7 @@ class TaggedCounterTest : public testing::Test { std::string log_; // Reporter mock to set callback expectations on. - StrictMock > reporter_; + StrictMock > reporter_; // Pointer to the current test fixture. static TaggedCounterTest* test_; @@ -159,12 +182,14 @@ class TaggedCounterTest : public testing::Test { TaggedCounterTest* TaggedCounterTest::test_ = NULL; TEST_F(RecordTest, Init) { - record_.Init(/* tag */ 5, /* count */ -1); - EXPECT_EQ(5, record_.tag()); + record_.Init(/* report_tag */ 8, /* reset_tag */ 5, /* count */ -1); + EXPECT_EQ(8, record_.report_tag()); + EXPECT_EQ(5, record_.reset_tag()); EXPECT_EQ(0, record_.count()); - record_.Init(/* tag */ -2, /* count */ 10); - EXPECT_EQ(-2, record_.tag()); + record_.Init(/* report_tag */ -8, /* reset_tag */ -2, /* count */ 10); + EXPECT_EQ(-8, record_.report_tag()); + EXPECT_EQ(-2, record_.reset_tag()); EXPECT_EQ(10, record_.count()); } @@ -193,7 +218,7 @@ TEST_F(TaggedCounterTest, BadFileLocation) { // created. counter_.Init(kDoesNotExistFile, /* reporter */ NULL, /* reporter_handle */ NULL); - counter_.Update(/* tag */ 10, /* count */ 20); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 10, /* count */ 20); EXPECT_TRUE(LogContains("Unable to open the persistent counter file: " "No such file or directory")); EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); @@ -204,62 +229,72 @@ TEST_F(TaggedCounterTest, Flush) { counter_.Flush(); EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); - counter_.Update(/* tag */ 40, /* count */ 60); - ExpectReporterCall(/* tag */ 40, /* count */ 60); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 40, /* count */ 60); + ExpectReporterCall(/* count */ 60); counter_.Flush(); EXPECT_TRUE(AssertNoOrEmptyRecordFile()); EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); - counter_.Update(/* tag */ 41, /* count */ 70); - counter_.record_.Init(/* tag */ 0, /* count */ 0); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 41, /* count */ 70); + counter_.record_.Init(/* report_tag */ 0, /* reset_tag */ 0, /* count */ 0); counter_.record_state_ = TaggedCounter::kRecordInvalid; - ExpectReporterCall(/* tag */ 41, /* count */ 70); + ExpectReporterCall(/* count */ 70); counter_.Flush(); EXPECT_TRUE(AssertNoOrEmptyRecordFile()); EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); } TEST_F(TaggedCounterTest, InitFromFile) { - counter_.Update(/* tag */ 30, /* count */ 50); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 30, /* count */ 50); EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 50); EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); counter_.Init(kTestRecordFile, &Reporter, this); - counter_.Update(/* tag */ 30, /* count */ 40); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 30, /* count */ 40); EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 90); EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); counter_.Init(kTestRecordFile, &Reporter, this); - ExpectReporterCall(/* tag */ 30, /* count */ 90); - counter_.Update(/* tag */ 31, /* count */ 60); + ExpectReporterCall(/* count */ 90); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 31, /* count */ 60); EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 31, /* seconds */ 60); EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - ExpectReporterCall(/* tag */ 31, /* count */ 60); + ExpectReporterCall(/* count */ 60); counter_.Init(kTestRecordFile, &Reporter, this); - counter_.Update(/* tag */ 32, /* count */ 0); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 32, /* count */ 0); EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 32, /* seconds */ 0); EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); } TEST_F(TaggedCounterTest, Update) { - counter_.Update(/* tag */ 20, /* count */ 30); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 20, /* count */ 30); EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 30); EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - counter_.Update(/* tag */ 20, /* count */ 40); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 20, /* count */ 40); EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 70); EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - ExpectReporterCall(/* tag */ 20, /* count */ 70); - counter_.Update(/* tag */ 21, /* count */ 15); + ExpectReporterCall(/* count */ 70); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 21, /* count */ 15); EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 21, /* seconds */ 15); EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - ExpectReporterCall(/* tag */ 21, /* count */ 15); - counter_.Update(/* tag */ 22, /* count */ 0); + ExpectReporterCall(/* count */ 15); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 22, /* count */ 0); EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 22, /* seconds */ 0); EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); + + ExpectReporterCall(/* count */ 33); + counter_.Update(/* report_tag */ 0, /* reset_tag */ 22, /* count */ 33); + EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 22, /* seconds */ 33); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); + // Check that changing the report tag does not reset the counter. + counter_.Update(/* report_tag */ 1, /* reset_tag */ 22, /* count */ 0); + EXPECT_PRED_FORMAT3(AssertRecord3, /* version */ 1, + /* day */ 22, /* seconds */ 33); + EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); } static const char kTestFilename[] = "test_filename"; @@ -311,10 +346,10 @@ TEST_F(TaggedCounterReporterTest, Init) { TEST_F(TaggedCounterReporterTest, Update) { DoInit(); - EXPECT_CALL(*tagged_counter_, Update(1, 2)) + EXPECT_CALL(*tagged_counter_, Update(1, 0, 2)) .Times(1) .RetiresOnSaturation(); - reporter_.Update(1, 2); + reporter_.Update(1, 0, 2); } TEST_F(TaggedCounterReporterTest, Flush) { @@ -334,7 +369,7 @@ TEST_F(TaggedCounterReporterTest, Report) { kHistogramBuckets)) .Times(1) .RetiresOnSaturation(); - reporter_.Report(&reporter_, 127, 301); + reporter_.Report(&reporter_, 301); } class FrequencyCounterTest : public testing::Test { @@ -385,12 +420,39 @@ TEST_F(FrequencyCounterTest, GetCycleNumberForDay) { TEST_F(FrequencyCounterTest, UpdateInternal) { CheckInit(kSecondsPerWeek); - EXPECT_CALL(*tagged_counter_, Update(150, 2)) + EXPECT_CALL(*tagged_counter_, Update(0, 150, 2)) .Times(1) .RetiresOnSaturation(); frequency_counter_.UpdateInternal(2, kSecondsPerWeek * 150); } +class VersionCounterTest : public testing::Test { + protected: + virtual void SetUp() { + tagged_counter_ = NULL; + } + void Init(); + + VersionCounter version_counter_; + StrictMock* tagged_counter_; + + TaggedCounter::Reporter reporter_; +}; + +void VersionCounterTest::Init() { + tagged_counter_ = new StrictMock; + version_counter_.Init(tagged_counter_, 1); + EXPECT_EQ(tagged_counter_, version_counter_.tagged_counter_.get()); +} + +TEST_F(VersionCounterTest, UpdateInternal) { + Init(); + EXPECT_CALL(*tagged_counter_, Update(0, 150, 2)) + .Times(1) + .RetiresOnSaturation(); + version_counter_.UpdateInternal(2, 0, 150); +} + } // namespace chromeos_metrics int main(int argc, char** argv) { diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 44d8d4ff2..d6eb8a302 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -9,12 +9,16 @@ #include #include +#include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -85,6 +89,12 @@ const char MetricsDaemon::kMetricKernelCrashesDailyName[] = "Logging.KernelCrashesDaily"; const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] = "Logging.KernelCrashesWeekly"; +const char MetricsDaemon::kMetricKernelCrashesVersionName[] = + "Logging.KernelCrashesSinceUpdate"; +const int MetricsDaemon::kMetricCumulativeCrashCountMin = 1; +const int MetricsDaemon::kMetricCumulativeCrashCountMax = 500; +const int MetricsDaemon::kMetricCumulativeCrashCountBuckets = 100; + const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] = "Logging.UncleanShutdownsDaily"; const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] = @@ -93,9 +103,9 @@ const char MetricsDaemon::kMetricUserCrashesDailyName[] = "Logging.UserCrashesDaily"; const char MetricsDaemon::kMetricUserCrashesWeeklyName[] = "Logging.UserCrashesWeekly"; -const char MetricsDaemon::kMetricCrashFrequencyMin = 1; -const char MetricsDaemon::kMetricCrashFrequencyMax = 100; -const char MetricsDaemon::kMetricCrashFrequencyBuckets = 50; +const int MetricsDaemon::kMetricCrashFrequencyMin = 1; +const int MetricsDaemon::kMetricCrashFrequencyMax = 100; +const int MetricsDaemon::kMetricCrashFrequencyBuckets = 50; // disk stats metrics @@ -152,6 +162,10 @@ const char MetricsDaemon::kMetricScaledCpuFrequencyName[] = // persistent metrics path const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; +// file containing OS version string +const char MetricsDaemon::kLsbReleasePath[] = "/etc/lsb-release"; + + // static const char* MetricsDaemon::kPowerStates_[] = { #define STATE(name, capname) #name, @@ -214,12 +228,15 @@ void MetricsDaemon::DeleteFrequencyCounters() { } void MetricsDaemon::Run(bool run_as_daemon) { + base::AtExitManager at_exit_manager; + if (run_as_daemon && daemon(0, 0) != 0) return; if (CheckSystemCrash(kKernelCrashDetectedFile)) { ProcessKernelCrash(); } + kernel_crash_version_counter_->FlushOnChange(GetOsVersionHash()); if (CheckSystemCrash(kUncleanShutdownDetectedFile)) { ProcessUncleanShutdown(); @@ -266,6 +283,42 @@ void MetricsDaemon::ConfigureCrashFrequencyReporter( frequency_counters_[histogram_name] = new_counter.release(); } +void MetricsDaemon::ConfigureCrashVersionReporter( + const char* histogram_name) { + scoped_ptr reporter( + new chromeos_metrics::TaggedCounterReporter()); + FilePath file_path = GetHistogramPath(histogram_name); + reporter->Init(file_path.value().c_str(), + histogram_name, + kMetricCumulativeCrashCountMin, + kMetricCumulativeCrashCountMax, + kMetricCumulativeCrashCountBuckets); + scoped_ptr new_counter( + new chromeos_metrics::VersionCounter()); + new_counter->Init( + static_cast( + reporter.release()), + chromeos_metrics::kSecondsPerDay); + kernel_crash_version_counter_ = new_counter.release(); +} + +uint32 MetricsDaemon::GetOsVersionHash() { + static uint32 cached_version_hash = 0; + static bool version_hash_is_cached = false; + if (version_hash_is_cached) + return cached_version_hash; + version_hash_is_cached = true; + std::string version; + if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION", &version)) { + cached_version_hash = base::Hash(version); + } else if (testing_) { + cached_version_hash = 42; // return any plausible value for the hash + } else { + LOG(FATAL) << "could not find CHROMEOS_RELEASE_VERSION"; + } + return cached_version_hash; +} + void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, const string& diskstats_path, const string& vmstats_path, @@ -299,6 +352,8 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); + ConfigureCrashVersionReporter(kMetricKernelCrashesVersionName); + diskstats_path_ = diskstats_path; vmstats_path_ = vmstats_path; scaling_max_freq_path_ = scaling_max_freq_path; @@ -328,19 +383,19 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, vector matches; matches.push_back( - StringPrintf("type='signal',interface='%s',path='/',member='%s'", - kCrashReporterInterface, - kCrashReporterUserCrashSignal)); + base::StringPrintf("type='signal',interface='%s',path='/',member='%s'", + kCrashReporterInterface, + kCrashReporterUserCrashSignal)); matches.push_back( - StringPrintf("type='signal',interface='%s',path='%s',member='%s'", - power_manager::kPowerManagerInterface, - power_manager::kPowerManagerServicePath, - power_manager::kPowerStateChangedSignal)); + base::StringPrintf("type='signal',interface='%s',path='%s',member='%s'", + power_manager::kPowerManagerInterface, + power_manager::kPowerManagerServicePath, + power_manager::kPowerStateChangedSignal)); matches.push_back( - StringPrintf("type='signal',sender='%s',interface='%s',path='%s'", - login_manager::kSessionManagerServiceName, - login_manager::kSessionManagerInterface, - login_manager::kSessionManagerServicePath)); + base::StringPrintf("type='signal',sender='%s',interface='%s',path='%s'", + login_manager::kSessionManagerServiceName, + login_manager::kSessionManagerInterface, + login_manager::kSessionManagerServicePath)); // Registers D-Bus matches for the signals we would like to catch. for (vector::const_iterator it = matches.begin(); @@ -466,15 +521,19 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { } TimeDelta since_epoch = now - Time(); int day = since_epoch.InDays(); - daily_use_->Update(day, seconds); - user_crash_interval_->Update(0, seconds); - kernel_crash_interval_->Update(0, seconds); + daily_use_->Update(day, seconds, 0); + user_crash_interval_->Update(0, seconds, 0); + kernel_crash_interval_->Update(0, seconds, 0); // Flush finished cycles of all frequency counters. for (FrequencyCounters::iterator i = frequency_counters_.begin(); i != frequency_counters_.end(); ++i) { i->second->FlushFinishedCycles(); } + // Report count if we're on a new cycle. FlushOnChange can also reset the + // counter, but not when called from here, because any version change has + // already been processed during initialization. + kernel_crash_version_counter_->FlushOnChange(GetOsVersionHash()); // Schedules a use monitor on inactive->active transitions and // unschedules it on active->inactive transitions. @@ -513,6 +572,8 @@ void MetricsDaemon::ProcessKernelCrash() { frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1); frequency_counters_[kMetricAnyCrashesDailyName]->Update(1); frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1); + + kernel_crash_version_counter_->Update(1, GetOsVersionHash()); } void MetricsDaemon::ProcessUncleanShutdown() { @@ -535,8 +596,7 @@ bool MetricsDaemon::CheckSystemCrash(const string& crash_file) { // Deletes the crash-detected file so that the daemon doesn't report // another kernel crash in case it's restarted. - base::DeleteFile(crash_detected, - false); // recursive + base::DeleteFile(crash_detected, false); // not recursive return true; } @@ -951,7 +1011,8 @@ bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { int swap_free = 0; // Send all fields retrieved, except total memory. for (unsigned int i = 1; i < fields.size(); i++) { - string metrics_name = StringPrintf("Platform.Meminfo%s", fields[i].name); + string metrics_name = base::StringPrintf("Platform.Meminfo%s", + fields[i].name); int percent; switch (fields[i].op) { case kMeminfoOp_HistPercent: @@ -1079,15 +1140,15 @@ bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) { LOG(WARNING) << "borked meminfo parser"; return false; } - string metrics_name = StringPrintf("Platform.MemuseAnon%d", - memuse_interval_index_); + string metrics_name = base::StringPrintf("Platform.MemuseAnon%d", + memuse_interval_index_); SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total, 100, 101); return true; } // static -void MetricsDaemon::ReportDailyUse(void* handle, int tag, int count) { +void MetricsDaemon::ReportDailyUse(void* handle, int count) { if (count <= 0) return; diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 6ceb13617..6d8f81b1b 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -18,6 +18,7 @@ namespace chromeos_metrics { class FrequencyCounter; +class VersionCounter; class TaggedCounter; class TaggedCounterReporter; } @@ -133,20 +134,25 @@ class MetricsDaemon { // Metric parameters. static const char kMetricAnyCrashesDailyName[]; static const char kMetricAnyCrashesWeeklyName[]; - static const char kMetricCrashFrequencyBuckets; - static const char kMetricCrashFrequencyMax; - static const char kMetricCrashFrequencyMin; + static const int kMetricCrashFrequencyBuckets; + static const int kMetricCrashFrequencyMax; + static const int kMetricCrashFrequencyMin; static const int kMetricCrashIntervalBuckets; static const int kMetricCrashIntervalMax; static const int kMetricCrashIntervalMin; + static const int kMetricCumulativeCrashCountBuckets; + static const int kMetricCumulativeCrashCountMax; + static const int kMetricCumulativeCrashCountMin; static const int kMetricDailyUseTimeBuckets; static const int kMetricDailyUseTimeMax; static const int kMetricDailyUseTimeMin; static const char kMetricDailyUseTimeName[]; static const char kMetricKernelCrashesDailyName[]; static const char kMetricKernelCrashesWeeklyName[]; + static const char kMetricKernelCrashesVersionName[]; static const char kMetricKernelCrashIntervalName[]; static const char kMetricsPath[]; + static const char kLsbReleasePath[]; static const char kMetricUncleanShutdownIntervalName[]; static const char kMetricUncleanShutdownsDailyName[]; static const char kMetricUncleanShutdownsWeeklyName[]; @@ -194,6 +200,9 @@ class MetricsDaemon { // Configures the given frequency counter reporter. void ConfigureCrashFrequencyReporter(const char* histogram_name); + // Configures the given version counter reporter. + void ConfigureCrashVersionReporter(const char* histogram_name); + // Returns file path to persistent file for generating given histogram. base::FilePath GetHistogramPath(const char* histogram_name); @@ -271,7 +280,7 @@ class MetricsDaemon { void UnscheduleUseMonitor(); // Report daily use through UMA. - static void ReportDailyUse(void* handle, int tag, int count); + static void ReportDailyUse(void* handle, int count); // Sends a regular (exponential) histogram sample to Chrome for // transport to UMA. See MetricsLibrary::SendToUMA in @@ -352,6 +361,10 @@ class MetricsDaemon { // Reads an integer CPU frequency value from sysfs. bool ReadFreqToInt(const std::string& sysfs_file_name, int* value); + // Reads the current OS version from /etc/lsb-release and hashes it + // to a unsigned 32-bit int. + uint32 GetOsVersionHash(); + // Test mode. bool testing_; @@ -394,6 +407,10 @@ class MetricsDaemon { // Map of all frequency counters, to simplify flushing them. FrequencyCounters frequency_counters_; + // This contains a cumulative number of kernel crashes since the latest + // version update. + chromeos_metrics::VersionCounter* kernel_crash_version_counter_; + // Sleep period until the next daily usage aggregation performed by // the daily use monitor (see ScheduleUseMonitor). int usemon_interval_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 7f98a1956..255f26070 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -63,10 +64,12 @@ class MetricsDaemonTest : public testing::Test { EXPECT_EQ(NULL, daemon_.daily_use_.get()); EXPECT_EQ(NULL, daemon_.kernel_crash_interval_.get()); EXPECT_EQ(NULL, daemon_.user_crash_interval_.get()); - kFakeDiskStats[0] = StringPrintf(kFakeDiskStatsFormat, - kFakeReadSectors[0], kFakeWriteSectors[0]); - kFakeDiskStats[1] = StringPrintf(kFakeDiskStatsFormat, - kFakeReadSectors[1], kFakeWriteSectors[1]); + kFakeDiskStats[0] = base::StringPrintf(kFakeDiskStatsFormat, + kFakeReadSectors[0], + kFakeWriteSectors[0]); + kFakeDiskStats[1] = base::StringPrintf(kFakeDiskStatsFormat, + kFakeReadSectors[1], + kFakeWriteSectors[1]); CreateFakeDiskStatsFile(kFakeDiskStats[0].c_str()); CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, 10000000); CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 10000000); @@ -163,13 +166,13 @@ class MetricsDaemonTest : public testing::Test { // Adds active use aggregation counters update expectations that the // specified tag/count update will be generated. void ExpectActiveUseUpdate(int daily_tag, int count) { - EXPECT_CALL(*daily_use_, Update(daily_tag, count)) + EXPECT_CALL(*daily_use_, Update(daily_tag, count, 0)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*kernel_crash_interval_, Update(0, count)) + EXPECT_CALL(*kernel_crash_interval_, Update(0, count, 0)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*user_crash_interval_, Update(0, count)) + EXPECT_CALL(*user_crash_interval_, Update(0, count, 0)) .Times(1) .RetiresOnSaturation(); ExpectFrequencyFlushCalls(); @@ -178,13 +181,13 @@ class MetricsDaemonTest : public testing::Test { // Adds active use aggregation counters update expectations that // ignore the update arguments. void IgnoreActiveUseUpdate() { - EXPECT_CALL(*daily_use_, Update(_, _)) + EXPECT_CALL(*daily_use_, Update(_, _, _)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*kernel_crash_interval_, Update(_, _)) + EXPECT_CALL(*kernel_crash_interval_, Update(_, _, _)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*user_crash_interval_, Update(_, _)) + EXPECT_CALL(*user_crash_interval_, Update(_, _, _)) .Times(1) .RetiresOnSaturation(); ExpectFrequencyFlushCalls(); @@ -291,7 +294,7 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) { static const char kKernelCrashDetected[] = "test-kernel-crash-detected"; EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected)); - FilePath crash_detected(kKernelCrashDetected); + base::FilePath crash_detected(kKernelCrashDetected); file_util::WriteFile(crash_detected, "", 0); EXPECT_TRUE(base::PathExists(crash_detected)); EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected)); @@ -303,14 +306,14 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) { TEST_F(MetricsDaemonTest, ReportDailyUse) { ExpectDailyUseTimeMetric(/* sample */ 2); - MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 20, /* count */ 90); + MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 90); ExpectDailyUseTimeMetric(/* sample */ 1); - MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 23, /* count */ 89); + MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 89); // There should be no metrics generated for the calls below. - MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 50, /* count */ 0); - MetricsDaemon::ReportDailyUse(&daemon_, /* tag */ 60, /* count */ -5); + MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 0); + MetricsDaemon::ReportDailyUse(&daemon_, /* count */ -5); } TEST_F(MetricsDaemonTest, LookupPowerState) { @@ -698,5 +701,8 @@ TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) { int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); + // Some libchrome calls need this. + base::AtExitManager at_exit_manager; + return RUN_ALL_TESTS(); } diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index a748ed5c3..165864530 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -70,7 +70,7 @@ TEST_F(MetricsLibraryTest, IsDeviceMounted) { buffer, 1, &result)); - ASSERT_TRUE(file_util::WriteFile(FilePath(kTestMounts), + ASSERT_TRUE(file_util::WriteFile(base::FilePath(kTestMounts), kTestContents, strlen(kTestContents))); EXPECT_FALSE(lib_.IsDeviceMounted("guestfs", From 371a613294ea502b01681f15d93a0b4c2c0a8e94 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Fri, 21 Feb 2014 09:26:02 -0800 Subject: [PATCH 113/200] Add init scripts for the metrics package We are moving the init scripts out of chromeos-init and into metrics/init. BUG=chromium:346444 TEST=cbuildbot on x86-alex-paladin CQ-DEPEND=CL:187455, CL:187470 Change-Id: If226addfaa154794361d3cf9687ca336b95b3104 Reviewed-on: https://chromium-review.googlesource.com/187456 Reviewed-by: Gaurav Shah Reviewed-by: Mike Frysinger Commit-Queue: Bertrand Simonnet Tested-by: Bertrand Simonnet --- metrics/init/metrics_daemon.conf | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 metrics/init/metrics_daemon.conf diff --git a/metrics/init/metrics_daemon.conf b/metrics/init/metrics_daemon.conf new file mode 100644 index 000000000..68edff4ad --- /dev/null +++ b/metrics/init/metrics_daemon.conf @@ -0,0 +1,15 @@ +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +description "Metrics collection daemon" +author "chromium-os-dev@chromium.org" + +# The metrics daemon is responsible for receiving and forwarding to +# chrome UMA statistics not produced by chrome. +start on starting system-services +stop on stopping system-services +respawn + +expect fork +exec metrics_daemon From fd158294daa6061037ca564e21818a49ee56fc94 Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Sun, 9 Mar 2014 21:39:08 -0700 Subject: [PATCH 114/200] metrics: Make implicit MetricsLibrary d'tor explicit. Also add 'virtual' and 'OVERRIDE' to overridden methods. BUG=none TEST=did a build Change-Id: Iec71c59853b6ada38b20cea9d0605c3d39e9353f Reviewed-on: https://chromium-review.googlesource.com/189398 Reviewed-by: Daniel Erat Commit-Queue: Daniel Erat Tested-by: Daniel Erat --- metrics/metrics_library.cc | 2 ++ metrics/metrics_library.h | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index c53c16b12..1214dd060 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -88,6 +88,8 @@ MetricsLibrary::MetricsLibrary() : uma_events_file_(NULL), consent_file_(kConsentFile) {} +MetricsLibrary::~MetricsLibrary() {} + // We take buffer and buffer_size as parameters in order to simplify testing // of various alignments of the |device_name| with |buffer_size|. bool MetricsLibrary::IsDeviceMounted(const char* device_name, diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 4bba96dce..e9c6f4be4 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include // for FRIEND_TEST @@ -29,9 +31,10 @@ class MetricsLibraryInterface { class MetricsLibrary : public MetricsLibraryInterface { public: MetricsLibrary(); + virtual ~MetricsLibrary(); // Initializes the library. - void Init(); + virtual void Init() OVERRIDE; // Returns whether or not the machine is running in guest mode. bool IsGuestMode(); @@ -68,18 +71,20 @@ class MetricsLibrary : public MetricsLibraryInterface { // 0 is the implicit underflow bucket. // [|max|,infinity) is the implicit overflow bucket. // - // An enumaration histogram requires |max| + 1 number of + // An enumeration histogram requires |max| + 1 number of // buckets. Note that the memory allocated in Chrome for each // histogram is proportional to the number of buckets. Therefore, it // is strongly recommended to keep this number low (e.g., 50 is // normal, while 100 is high). - bool SendEnumToUMA(const std::string& name, int sample, int max); + virtual bool SendEnumToUMA(const std::string& name, + int sample, + int max) OVERRIDE; // Sends sparse histogram sample to Chrome for transport to UMA. Returns // true on success. // // |sample| is the 32-bit integer value to be recorded. - bool SendSparseToUMA(const std::string& name, int sample); + virtual bool SendSparseToUMA(const std::string& name, int sample) OVERRIDE; // Sends a user action to Chrome for transport to UMA and returns true on // success. This method results in the equivalent of an asynchronous @@ -91,7 +96,7 @@ class MetricsLibrary : public MetricsLibraryInterface { // chrome/browser/chromeos/external_metrics.cc. // // |action| is the user-generated event (e.g., "MuteKeyPressed"). - bool SendUserActionToUMA(const std::string& action); + virtual bool SendUserActionToUMA(const std::string& action) OVERRIDE; // Sends a signal to UMA that a crash of the given |crash_kind| // has occurred. Used by UMA to generate stability statistics. @@ -158,6 +163,8 @@ class MetricsLibrary : public MetricsLibraryInterface { const char* consent_file_; scoped_ptr policy_provider_; + + DISALLOW_COPY_AND_ASSIGN(MetricsLibrary); }; #endif // METRICS_LIBRARY_H_ From 2fd51cc42dddba7568baec97f6b3701e2a77253b Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Wed, 26 Feb 2014 11:53:16 -0800 Subject: [PATCH 115/200] metrics: refactor counters The metrics daemon had counter classes that represented persistent, tagged values, and UMA stats. To create some derived UMA stats we need to separate persistency and UMA stats, and the tagging doesn't help either. This rewrite is supposed to keep the same functionality. BUG=chromium:339588 TEST=ran and checked a few histograms BRANCH=none Change-Id: Ia1121ab2db391d71edffab9f52afe29ce17686ba Reviewed-on: https://chromium-review.googlesource.com/188082 Tested-by: Luigi Semenzato Reviewed-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/counter.cc | 307 -------------------- metrics/counter.h | 345 ---------------------- metrics/counter_mock.h | 45 --- metrics/counter_test.cc | 461 ------------------------------ metrics/metrics.gyp | 11 +- metrics/metrics_daemon.cc | 339 +++++++++------------- metrics/metrics_daemon.h | 116 +++----- metrics/metrics_daemon_test.cc | 283 +++++------------- metrics/persistent_integer.cc | 97 +++++++ metrics/persistent_integer.h | 65 +++++ metrics/persistent_integer_mock.h | 24 ++ 11 files changed, 443 insertions(+), 1650 deletions(-) delete mode 100644 metrics/counter.cc delete mode 100644 metrics/counter.h delete mode 100644 metrics/counter_mock.h delete mode 100644 metrics/counter_test.cc create mode 100644 metrics/persistent_integer.cc create mode 100644 metrics/persistent_integer.h create mode 100644 metrics/persistent_integer_mock.h diff --git a/metrics/counter.cc b/metrics/counter.cc deleted file mode 100644 index f71b4fbf6..000000000 --- a/metrics/counter.cc +++ /dev/null @@ -1,307 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "counter.h" - -#include - -#include -#include - -#include "metrics_library.h" - -namespace chromeos_metrics { - -// TaggedCounter::Record implementation. -void TaggedCounter::Record::Init(uint32 report_tag, - uint32 reset_tag, - int32 count) { - report_tag_ = report_tag; - reset_tag_ = reset_tag; - count_ = (count > 0) ? count : 0; -} - -void TaggedCounter::Record::Add(int32 count) { - if (count <= 0) - return; - - // Saturates on positive overflow. - int64 new_count = static_cast(count_) + static_cast(count); - if (new_count > kint32max) - count_ = kint32max; - else - count_ = static_cast(new_count); -} - -// TaggedCounter implementation. -TaggedCounter::TaggedCounter() - : reporter_(NULL), - reporter_handle_(NULL), - record_state_(kRecordInvalid) {} - -TaggedCounter::~TaggedCounter() {} - -void TaggedCounter::Init(const char* filename, - Reporter reporter, void* reporter_handle) { - DCHECK(filename); - filename_ = filename; - reporter_ = reporter; - reporter_handle_ = reporter_handle; - record_state_ = kRecordInvalid; -} - -void TaggedCounter::Update(uint32 report_tag, uint32 reset_tag, int32 count) { - UpdateInternal(report_tag, - reset_tag, - count, - false); // No flush. -} - -void TaggedCounter::Flush() { - UpdateInternal(0, // report_tag - 0, // reset_tag - 0, // count - true); // Do flush. -} - -void TaggedCounter::UpdateInternal(uint32 report_tag, - uint32 reset_tag, - int32 count, - bool flush) { - if (flush) { - // Flushing but record is null, so nothing to do. - if (record_state_ == kRecordNull) - return; - } else { - // If there's no new data and the last record in the aggregation - // file is with the same tag, there's nothing to do. - if (count <= 0 && - record_state_ == kRecordValid && - record_.report_tag() == report_tag && - record_.reset_tag() == reset_tag) - return; - } - - DLOG(INFO) << "report_tag: " << report_tag << " reset_tag: " << reset_tag - << " count: " << count << " flush: " << flush; - DCHECK(!filename_.empty()); - - // NOTE: The assumption is that this TaggedCounter object is the - // sole owner of the persistent storage file so no locking is - // necessary. - int fd = HANDLE_EINTR(open(filename_.c_str(), - O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)); - if (fd < 0) { - PLOG(WARNING) << "Unable to open the persistent counter file"; - return; - } - ReadRecord(fd); - ReportRecord(report_tag, reset_tag, flush); - UpdateRecord(report_tag, reset_tag, count, flush); - WriteRecord(fd); - - HANDLE_EINTR(close(fd)); -} - -void TaggedCounter::ReadRecord(int fd) { - if (record_state_ != kRecordInvalid) - return; - - // Three cases: 1. empty file (first time), 2. size of file == size of record - // (normal case), 3. size of file != size of record (new version). We treat - // cases 1 and 3 identically. - if (HANDLE_EINTR(read(fd, &record_, sizeof(record_))) == sizeof(record_)) { - if (record_.count() >= 0) { - record_state_ = kRecordValid; - return; - } - // This shouldn't happen normally unless somebody messed with the - // persistent storage file. - NOTREACHED(); - record_state_ = kRecordNullDirty; - return; - } - record_state_ = kRecordNull; -} - -void TaggedCounter::ReportRecord(uint32 report_tag, - uint32 reset_tag, - bool flush) { - // If no valid record, there's nothing to report. - if (record_state_ != kRecordValid) { - DCHECK_EQ(record_state_, kRecordNull); - return; - } - - // If the current record has the same tags as the new ones, it's - // not ready to be reported yet. - if (!flush && - record_.report_tag() == report_tag && - record_.reset_tag() == reset_tag) - return; - - if (reporter_) { - reporter_(reporter_handle_, record_.count()); - } - // If the report tag has changed, update it to the current one. - if (record_.report_tag() != report_tag) { - record_.set_report_tag(report_tag); - record_state_ = kRecordValidDirty; - } - // If the reset tag has changed, the new state is NullDirty, no - // matter whether the report tag has changed or not. - if (record_.reset_tag() != reset_tag) { - record_state_ = kRecordNullDirty; - } -} - -void TaggedCounter::UpdateRecord(uint32 report_tag, - uint32 reset_tag, - int32 count, - bool flush) { - if (flush && - (record_state_ == kRecordNull || record_state_ == kRecordNullDirty)) - return; - - switch (record_state_) { - case kRecordNull: - case kRecordNullDirty: - // Current record is null, starting a new record. - record_.Init(report_tag, reset_tag, count); - record_state_ = kRecordValidDirty; - break; - - case kRecordValid: - case kRecordValidDirty: - // If there's an existing record for the current tag, - // accumulates the counts. - DCHECK_EQ(record_.report_tag(), report_tag); - DCHECK_EQ(record_.reset_tag(), reset_tag); - if (count > 0) { - record_.Add(count); - record_state_ = kRecordValidDirty; - } - break; - - default: - NOTREACHED(); - } -} - -void TaggedCounter::WriteRecord(int fd) { - switch (record_state_) { - case kRecordNullDirty: - // Truncates the aggregation file to discard the record. - PLOG_IF(WARNING, HANDLE_EINTR(ftruncate(fd, 0)) != 0); - record_state_ = kRecordNull; - break; - - case kRecordValidDirty: - // Updates the accumulator record in the file if there's new data. - PLOG_IF(WARNING, HANDLE_EINTR(lseek(fd, 0, SEEK_SET)) != 0); - PLOG_IF(WARNING, - HANDLE_EINTR(write(fd, &record_, sizeof(record_))) != - sizeof(record_)); - record_state_ = kRecordValid; - break; - - case kRecordNull: - case kRecordValid: - // Nothing to do. - break; - - default: - NOTREACHED(); - } -} - -MetricsLibraryInterface* TaggedCounterReporter::metrics_lib_ = NULL; - -TaggedCounterReporter::TaggedCounterReporter() - : tagged_counter_(new TaggedCounter()), - min_(0), - max_(0), - buckets_(0) { -} - -TaggedCounterReporter::~TaggedCounterReporter() { -} - -void TaggedCounterReporter::Init(const char* filename, - const char* histogram_name, - int min, - int max, - int buckets) { - tagged_counter_->Init(filename, Report, this); - histogram_name_ = histogram_name; - min_ = min; - max_ = max; - buckets_ = buckets; - CHECK(min_ >= 0); - CHECK(max_ > min_); - CHECK(buckets_ > 0); -} - -void TaggedCounterReporter::Report(void* handle, int32 count) { - TaggedCounterReporter* this_reporter = - reinterpret_cast(handle); - DLOG(INFO) << "received metric: " << this_reporter->histogram_name_ - << " " << count << " " << this_reporter->min_ << " " - << this_reporter->max_ << " " << this_reporter->buckets_; - CHECK(metrics_lib_ != NULL); - CHECK(this_reporter->buckets_ > 0); - metrics_lib_->SendToUMA(this_reporter->histogram_name_, - count, - this_reporter->min_, - this_reporter->max_, - this_reporter->buckets_); -} - -FrequencyCounter::FrequencyCounter() : cycle_duration_(1) { -} - -FrequencyCounter::~FrequencyCounter() { -} - -void FrequencyCounter::Init(TaggedCounterInterface* tagged_counter, - time_t cycle_duration) { - tagged_counter_.reset(tagged_counter); - DCHECK_GT(cycle_duration, 0); - cycle_duration_ = cycle_duration; -} - -void FrequencyCounter::UpdateInternal(int32 count, time_t now) { - DCHECK(tagged_counter_.get()); - tagged_counter_->Update(0, GetCycleNumber(now), count); -} - -int32 FrequencyCounter::GetCycleNumber(time_t now) { - return now / cycle_duration_; -} - -VersionCounter::VersionCounter() : cycle_duration_(1) { -} - -VersionCounter::~VersionCounter() { -} - -void VersionCounter::Init(TaggedCounterInterface* tagged_counter, - time_t cycle_duration) { - tagged_counter_.reset(tagged_counter); - DCHECK_GT(cycle_duration, 0); - cycle_duration_ = cycle_duration; -} - -void VersionCounter::UpdateInternal(int32 count, - time_t now, - uint32 version_hash) { - DCHECK(tagged_counter_.get()); - tagged_counter_->Update(GetCycleNumber(now), version_hash, count); -} - -int32 VersionCounter::GetCycleNumber(time_t now) { - return now / cycle_duration_; -} - -} // namespace chromeos_metrics diff --git a/metrics/counter.h b/metrics/counter.h deleted file mode 100644 index 679aaafc9..000000000 --- a/metrics/counter.h +++ /dev/null @@ -1,345 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef METRICS_COUNTER_H_ -#define METRICS_COUNTER_H_ - -#include -#include - -#include -#include -#include // for FRIEND_TEST - -class MetricsLibraryInterface; - -namespace chromeos_metrics { - -// Constants useful for frequency statistics. -const int kSecondsPerDay = 60 * 60 * 24; -const int kSecondsPerWeek = kSecondsPerDay * 7; - -// TaggedCounter maintains a persistent storage (i.e., a file) aggregation -// counter for given tags (e.g., day, hour, version number) that survives -// system shutdowns, reboots and crashes, as well as daemon process -// restarts. The counter object is initialized by pointing to the persistent -// storage file and providing a callback used for reporting aggregated data. -// The counter can then be updated with additional event counts. The -// aggregated count is reported through the callback when the counter is -// explicitly flushed or when data for a new tag arrives. -// -// The primary reason for using an interface is to allow easier unit -// testing in clients through mocking thus avoiding file access and -// callbacks. Of course, it also enables alternative implementations -// of the counter with additional features. -class TaggedCounterInterface { - public: - // Callback type used for reporting aggregated or flushed data. - // Once this callback is invoked by the counter, the reported - // aggregated data is discarded. - // - // |handle| is the |reporter_handle| pointer passed through Init. - // |count| is aggregated count. - typedef void (*Reporter)(void* handle, int32 count); - - virtual ~TaggedCounterInterface() {} - - // Adds |count| of events for the given tags. If there's an existing - // aggregated count for different tags, it's reported through the reporter - // callback, and optionally discarded, depending on whether |report_tag| - // changed or |reset_tag| changed. - virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count) = 0; - - // Reports the current aggregated count (if any) through the - // reporter callback and discards it. - virtual void Flush() = 0; -}; - -class TaggedCounter : public TaggedCounterInterface { - public: - TaggedCounter(); - virtual ~TaggedCounter(); - - // Initializes the counter by providing the persistent storage - // location |filename| and a |reporter| callback for reporting - // aggregated counts. |reporter_handle| is sent to the |reporter| - // along with the aggregated counts. - // - // NOTE: The assumption is that this object is the sole owner of the - // persistent storage file so no locking is currently implemented. - virtual void Init(const char* filename, - Reporter reporter, void* reporter_handle); - - // Implementation of interface methods. - virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count); - virtual void Flush(); - - private: - friend class RecordTest; - friend class TaggedCounterTest; - FRIEND_TEST(TaggedCounterTest, BadFileLocation); - FRIEND_TEST(TaggedCounterTest, Flush); - FRIEND_TEST(TaggedCounterTest, InitFromFile); - FRIEND_TEST(TaggedCounterTest, Update); - - // The current record is cached by the counter object to - // avoid potentially unnecessary I/O. The cached record can be in - // one of the following states: - enum RecordState { - kRecordInvalid, // Invalid record, sync from persistent storage needed. - kRecordNull, // No current record, persistent storage synced. - kRecordNullDirty, // No current record, persistent storage is invalid. - kRecordValid, // Current record valid, persistent storage synced. - kRecordValidDirty // Current record valid, persistent storage is invalid. - }; - - // Defines the record. Objects of this class are synced - // with the persistent storage through binary reads/writes. - class Record { - public: - // Creates a new Record with all fields reset to 0. - Record() : report_tag_(0), reset_tag_(0), count_(0) {} - - // Initializes with |report_tag|, |reset_tag| and |count|. - // If |count| is negative, |count_| is set to 0. - void Init(uint32 report_tag, uint32 reset_tag, int32 count); - - // Adds |count| to the current |count_|. Negative |count| is - // ignored. In case of positive overflow, |count_| is saturated to - // kint32max. - void Add(int32 count); - - uint32 report_tag() const { return report_tag_; } - void set_report_tag(uint32 report_tag) { report_tag_ = report_tag; } - uint32 reset_tag() const { return reset_tag_; } - int32 count() const { return count_; } - - private: - // When |report_tag_| changes, the counter is reported as a UMA sample. - // When |reset_tag_| changes, the counter is both reported and reset. - uint32 report_tag_; - uint32 reset_tag_; - int32 count_; - }; - - // Implementation of the Update and Flush methods. Goes through the - // necessary steps to read, report, update, and sync the aggregated - // record. - void UpdateInternal(uint32 report_tag, - uint32 reset_tag, - int32 count, - bool flush); - - // If the current cached record is invalid, reads it from persistent - // storage specified through file descriptor |fd| and updates the - // cached record state to either null, or valid depending on the - // persistent storage contents. - void ReadRecord(int fd); - - // If there's an existing valid record and either |flush| is true, or either - // new tag is different than the old one, reports the aggregated data through - // the reporter callback, and possibly resets the cached record. - void ReportRecord(uint32 report_tag, uint32 reset_tag, bool flush); - - // Updates the cached record given the new tags and |count|. This - // method expects either a null cached record, or a valid cached - // record with the same tags as given. If |flush| is true, the method - // asserts that the cached record is null and returns. - void UpdateRecord(uint32 report_tag, - uint32 reset_tag, - int32 count, - bool flush); - - // If the cached record state is dirty, updates the persistent - // storage specified through file descriptor |fd| and switches the - // record state to non-dirty. - void WriteRecord(int fd); - - // Persistent storage file path. - std::string filename_; - - // Aggregated data reporter callback and handle to pass-through. - Reporter reporter_; - void* reporter_handle_; - - // Current cached aggregation record. - Record record_; - - // Current cached aggregation record state. - RecordState record_state_; -}; - -// TaggedCounterReporter provides a TaggedCounterInterface which both -// counts tagged events and reports them up through the metrics -// library to UMA. -class TaggedCounterReporter : public TaggedCounterInterface { - public: - TaggedCounterReporter(); - virtual ~TaggedCounterReporter(); - - // Set the metrics library used by all TaggedCounterReporter - // instances. We assume there is only one metrics library - // shared amongst all reporters. - static void SetMetricsLibraryInterface(MetricsLibraryInterface* metrics_lib) { - metrics_lib_ = metrics_lib; - } - - // Initializes the counter by providing the persistent storage - // location |filename|, a |histogram_name| (a linear histogram) to - // report to with |min|, |max|, and |buckets| attributes for the - // histogram. - virtual void Init(const char* filename, - const char* histogram_name, - int min, - int max, - int buckets); - - // Implementation of interface method. - virtual void Update(uint32 report_tag, uint32 reset_tag, int32 count) { - tagged_counter_->Update(report_tag, reset_tag, count); - } - // Implementation of interface method. - virtual void Flush() { - tagged_counter_->Flush(); - } - - // Accessor functions. - const std::string& histogram_name() const { - return histogram_name_; - } - - int min() const { - return min_; - } - - int max() const { - return max_; - } - - int buckets() const { - return buckets_; - } - - protected: - friend class TaggedCounterReporterTest; - FRIEND_TEST(TaggedCounterReporterTest, Report); - - static void Report(void* handle, int32 count); - - static MetricsLibraryInterface* metrics_lib_; - scoped_ptr tagged_counter_; - std::string histogram_name_; - int min_; - int max_; - int buckets_; -}; - -// FrequencyCounter uses TaggedCounter to maintain a persistent -// storage of the number of events that occur in a given cycle -// duration (in other words, a frequency count). For example, to -// count the number of blips per day, initialize |cycle_duration| to -// chromeos_metrics::kSecondsPerDay, and call Update with the number -// of blips that happen concurrently (usually 1). Reporting of the -// value is done through TaggedCounter's reporter function. -class FrequencyCounter { - public: - // Create a new frequency counter. - FrequencyCounter(); - virtual ~FrequencyCounter(); - - // Initialize a frequency counter, which is necessary before first - // use. |tagged_counter| is used to store the counts, its memory - // will be managed by this FrequencyCounter. |cycle_duration| is - // the number of seconds in a cycle. - virtual void Init(TaggedCounterInterface* tagged_counter, - time_t cycle_duration); - // Record that an event occurred. |count| is the number of concurrent - // events that have occurred. The time is implicitly assumed to be the - // time of the call. - virtual void Update(int32 count) { - UpdateInternal(count, time(NULL)); - } - - // Update the frequency counter based on the current time. If a - // cycle has finished, this will have the effect of flushing the - // cycle's count, without first requiring another update to the - // frequency counter. The more often this is called, the lower the - // latency to have a new sample submitted. - virtual void FlushFinishedCycles() { - Update(0); - } - - // Accessor function. - const TaggedCounterInterface& tagged_counter() const { - return *tagged_counter_; - } - - time_t cycle_duration() const { - return cycle_duration_; - } - - private: - friend class FrequencyCounterTest; - FRIEND_TEST(FrequencyCounterTest, UpdateInternal); - FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForWeek); - FRIEND_TEST(FrequencyCounterTest, GetCycleNumberForDay); - - void UpdateInternal(int32 count, time_t now); - int32 GetCycleNumber(time_t now); - - time_t cycle_duration_; - scoped_ptr tagged_counter_; -}; - -// VersionCounter is like a FrequencyCounter, but it exposes -// separate "report" and "reset" tags, for counters that should -// be reported more often than they are reset. -class VersionCounter { - public: - VersionCounter(); - virtual ~VersionCounter(); - - // Initialize a version counter, which is necessary before first use. - // |tagged_counter| is used to store the counts. Its memory is managed - // by this FrequencyCounter. |cycle_duration| is the number of seconds in a - // cycle. - virtual void Init(TaggedCounterInterface* tagged_counter, - time_t cycle_duration); - // Record that |count| events have occurred. The - // time is implicitly assumed to be the time of the call. - // The version hash is passed. - virtual void Update(int32 count, uint32 version_hash) { - UpdateInternal(count, time(NULL), version_hash); - } - - // Reports the counter if enough time has passed, and also resets it if the - // version number has changed. - virtual void FlushOnChange(uint32 version_hash) { - UpdateInternal(0, time(NULL), version_hash); - } - - // Accessor function. - const TaggedCounterInterface& tagged_counter() const { - return *tagged_counter_; - } - - time_t cycle_duration() const { - return cycle_duration_; - } - - private: - friend class VersionCounterTest; - FRIEND_TEST(VersionCounterTest, UpdateInternal); - - void UpdateInternal(int32 count, time_t now, uint32 version_hash); - // TODO(semenzato): it's generally better to use base::TimeTicks (for - // monotonically-increasing timestamps) or base::Time (for wall time) - int32 GetCycleNumber(time_t now); - time_t cycle_duration_; - scoped_ptr tagged_counter_; -}; - -} // namespace chromeos_metrics - -#endif // METRICS_COUNTER_H_ diff --git a/metrics/counter_mock.h b/metrics/counter_mock.h deleted file mode 100644 index 77272a18f..000000000 --- a/metrics/counter_mock.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef METRICS_COUNTER_MOCK_H_ -#define METRICS_COUNTER_MOCK_H_ - -#include - -#include - -#include "counter.h" - -namespace chromeos_metrics { - -class TaggedCounterMock : public TaggedCounter { - public: - MOCK_METHOD3(Init, void(const char* filename, - Reporter reporter, void* reporter_handle)); - MOCK_METHOD3(Update, void(uint32 report_tag, uint32 reset_tag, int32 count)); - MOCK_METHOD0(Flush, void()); -}; - -class TaggedCounterReporterMock : public TaggedCounterReporter { - public: - MOCK_METHOD5(Init, void(const char* filename, - const char* histogram_name, - int min, - int max, - int nbuckets)); - MOCK_METHOD3(Update, void(uint32 report_tag, uint32 reset_tag, int32 count)); - MOCK_METHOD0(Flush, void()); -}; - -class FrequencyCounterMock : public FrequencyCounter { - public: - MOCK_METHOD2(Init, void(TaggedCounterInterface* tagged_counter, - time_t cycle_duration)); - MOCK_METHOD1(Update, void(int32 count)); - MOCK_METHOD0(FlushFinishedCycles, void()); -}; - -} // namespace chromeos_metrics - -#endif // METRICS_COUNTER_MOCK_H_ diff --git a/metrics/counter_test.cc b/metrics/counter_test.cc deleted file mode 100644 index 90c3cd8ce..000000000 --- a/metrics/counter_test.cc +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include -#include -#include -#include -#include -#include - -#include "counter.h" -#include "counter_mock.h" // For TaggedCounterMock. -#include "metrics_library_mock.h" - -using base::FilePath; -using ::testing::_; -using ::testing::MockFunction; -using ::testing::StrictMock; - -namespace chromeos_metrics { - -static const char kTestRecordFile[] = "record-file"; -static const char kDoesNotExistFile[] = "/does/not/exist"; - -class RecordTest : public testing::Test { - protected: - virtual void SetUp() { - EXPECT_EQ(0, record_.report_tag()); - EXPECT_EQ(0, record_.reset_tag()); - EXPECT_EQ(0, record_.count()); - } - - // The record under test. - TaggedCounter::Record record_; -}; - -class TaggedCounterTest : public testing::Test { - protected: - virtual void SetUp() { - EXPECT_TRUE(counter_.filename_.empty()); - EXPECT_TRUE(NULL == counter_.reporter_); - EXPECT_EQ(NULL, counter_.reporter_handle_); - EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); - - counter_.Init(kTestRecordFile, &Reporter, this); - EXPECT_TRUE(AssertNoOrEmptyRecordFile()); - EXPECT_EQ(kTestRecordFile, counter_.filename_); - EXPECT_TRUE(Reporter == counter_.reporter_); - EXPECT_EQ(this, counter_.reporter_handle_); - EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); - - // The test fixture object will be used by the log message handler. - test_ = this; - logging::SetLogMessageHandler(HandleLogMessages); - } - - virtual void TearDown() { - logging::SetLogMessageHandler(NULL); - test_ = NULL; - base::DeleteFile(FilePath(kTestRecordFile), false); - } - - testing::AssertionResult AssertRecord(const char* expr_reset_tag, - const char* expr_count, - uint32 expected_reset_tag, - int32 expected_count) { - return AssertRecordFull(12345, expected_reset_tag, expected_count, false); - } - - // Asserts that the record file contains the specified contents. - testing::AssertionResult AssertRecord3(const char* expr_report_tag, - const char* expr_reset_tag, - const char* expr_count, - uint32 expected_report_tag, - uint32 expected_reset_tag, - int32 expected_count) { - return AssertRecordFull(expected_report_tag, expected_reset_tag, - expected_count, true); - } - - testing::AssertionResult AssertRecordFull(uint32 expected_report_tag, - uint32 expected_reset_tag, - int32 expected_count, - bool check_report_tag) { - int fd = HANDLE_EINTR(open(kTestRecordFile, O_RDONLY)); - if (fd < 0) { - testing::Message msg; - msg << "Unable to open " << kTestRecordFile; - return testing::AssertionFailure(msg); - } - - TaggedCounter::Record record; - if (!base::ReadFromFD(fd, reinterpret_cast(&record), - sizeof(record))) { - testing::Message msg; - msg << "Unable to read " << sizeof(record) << " bytes from " - << kTestRecordFile; - HANDLE_EINTR(close(fd)); - return testing::AssertionFailure(msg); - } - - if ((check_report_tag && (record.report_tag() != expected_report_tag)) || - record.reset_tag() != expected_reset_tag || - record.count() != expected_count) { - testing::Message msg; - msg << "actual record (" << record.report_tag() << ", " - << record.reset_tag() << ", " << record.count() - << ") expected (" << expected_report_tag << ", " - << expected_reset_tag << ", " << expected_count << ")"; - if (!check_report_tag) - msg << "\n(ignore differences in the first field)"; - HANDLE_EINTR(close(fd)); - return testing::AssertionFailure(msg); - } - - HANDLE_EINTR(close(fd)); - return testing::AssertionSuccess(); - } - - // Returns true if the persistent record file does not exist or is - // empty, false otherwise. - bool AssertNoOrEmptyRecordFile() { - base::FilePath record_file(counter_.filename_); - int64 record_file_size; - return !base::PathExists(record_file) || - (base::GetFileSize(record_file, &record_file_size) && - record_file_size == 0); - } - - // Adds a reporter call expectation that the specified tag/count - // callback will be generated. - void ExpectReporterCall(int32 count) { - EXPECT_CALL(reporter_, Call(_, count)) - .Times(1) - .RetiresOnSaturation(); - } - - // The reporter callback forwards the call to the reporter mock so - // that we can set call expectations. - static void Reporter(void* handle, int32 count) { - TaggedCounterTest* test = static_cast(handle); - ASSERT_FALSE(NULL == test); - test->reporter_.Call(handle, count); - } - - // Collects log messages in the |log_| member string so that they - // can be analyzed for errors and expected behavior. - static bool HandleLogMessages(int severity, - const char* file, - int line, - size_t message_start, - const std::string& str) { - test_->log_.append(str); - test_->log_.append("\n"); - - // Returning true would mute the log. - return false; - } - - // Returns true if the counter log contains |pattern|, false otherwise. - bool LogContains(const std::string& pattern) const { - return log_.find(pattern) != std::string::npos; - } - - // The TaggedCounter object under test. - TaggedCounter counter_; - - // The accumulated counter log. - std::string log_; - - // Reporter mock to set callback expectations on. - StrictMock > reporter_; - - // Pointer to the current test fixture. - static TaggedCounterTest* test_; -}; - -// static -TaggedCounterTest* TaggedCounterTest::test_ = NULL; - -TEST_F(RecordTest, Init) { - record_.Init(/* report_tag */ 8, /* reset_tag */ 5, /* count */ -1); - EXPECT_EQ(8, record_.report_tag()); - EXPECT_EQ(5, record_.reset_tag()); - EXPECT_EQ(0, record_.count()); - - record_.Init(/* report_tag */ -8, /* reset_tag */ -2, /* count */ 10); - EXPECT_EQ(-8, record_.report_tag()); - EXPECT_EQ(-2, record_.reset_tag()); - EXPECT_EQ(10, record_.count()); -} - -TEST_F(RecordTest, Add) { - record_.Add(/* count */ -1); - EXPECT_EQ(0, record_.count()); - - record_.Add(/* count */ 5); - EXPECT_EQ(5, record_.count()); - - record_.Add(/* count */ 10); - EXPECT_EQ(15, record_.count()); - - record_.Add(/* count */ -2); - EXPECT_EQ(15, record_.count()); - - record_.Add(/* count */ kint32max); - EXPECT_EQ(kint32max, record_.count()); - - record_.Add(/* count */ 1); - EXPECT_EQ(kint32max, record_.count()); -} - -TEST_F(TaggedCounterTest, BadFileLocation) { - // Checks that the counter doesn't die badly if the file can't be - // created. - counter_.Init(kDoesNotExistFile, - /* reporter */ NULL, /* reporter_handle */ NULL); - counter_.Update(/* report_tag */ 0, /* reset_tag */ 10, /* count */ 20); - EXPECT_TRUE(LogContains("Unable to open the persistent counter file: " - "No such file or directory")); - EXPECT_EQ(TaggedCounter::kRecordInvalid, counter_.record_state_); - base::DeleteFile(FilePath(kDoesNotExistFile), false); -} - -TEST_F(TaggedCounterTest, Flush) { - counter_.Flush(); - EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); - - counter_.Update(/* report_tag */ 0, /* reset_tag */ 40, /* count */ 60); - ExpectReporterCall(/* count */ 60); - counter_.Flush(); - EXPECT_TRUE(AssertNoOrEmptyRecordFile()); - EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); - - counter_.Update(/* report_tag */ 0, /* reset_tag */ 41, /* count */ 70); - counter_.record_.Init(/* report_tag */ 0, /* reset_tag */ 0, /* count */ 0); - counter_.record_state_ = TaggedCounter::kRecordInvalid; - ExpectReporterCall(/* count */ 70); - counter_.Flush(); - EXPECT_TRUE(AssertNoOrEmptyRecordFile()); - EXPECT_EQ(TaggedCounter::kRecordNull, counter_.record_state_); -} - -TEST_F(TaggedCounterTest, InitFromFile) { - counter_.Update(/* report_tag */ 0, /* reset_tag */ 30, /* count */ 50); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 50); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - - counter_.Init(kTestRecordFile, &Reporter, this); - counter_.Update(/* report_tag */ 0, /* reset_tag */ 30, /* count */ 40); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 30, /* seconds */ 90); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - - counter_.Init(kTestRecordFile, &Reporter, this); - ExpectReporterCall(/* count */ 90); - counter_.Update(/* report_tag */ 0, /* reset_tag */ 31, /* count */ 60); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 31, /* seconds */ 60); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - - ExpectReporterCall(/* count */ 60); - counter_.Init(kTestRecordFile, &Reporter, this); - counter_.Update(/* report_tag */ 0, /* reset_tag */ 32, /* count */ 0); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 32, /* seconds */ 0); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); -} - -TEST_F(TaggedCounterTest, Update) { - counter_.Update(/* report_tag */ 0, /* reset_tag */ 20, /* count */ 30); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 30); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - - counter_.Update(/* report_tag */ 0, /* reset_tag */ 20, /* count */ 40); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 20, /* seconds */ 70); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - - ExpectReporterCall(/* count */ 70); - counter_.Update(/* report_tag */ 0, /* reset_tag */ 21, /* count */ 15); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 21, /* seconds */ 15); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - - ExpectReporterCall(/* count */ 15); - counter_.Update(/* report_tag */ 0, /* reset_tag */ 22, /* count */ 0); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 22, /* seconds */ 0); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - - ExpectReporterCall(/* count */ 33); - counter_.Update(/* report_tag */ 0, /* reset_tag */ 22, /* count */ 33); - EXPECT_PRED_FORMAT2(AssertRecord, /* day */ 22, /* seconds */ 33); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); - // Check that changing the report tag does not reset the counter. - counter_.Update(/* report_tag */ 1, /* reset_tag */ 22, /* count */ 0); - EXPECT_PRED_FORMAT3(AssertRecord3, /* version */ 1, - /* day */ 22, /* seconds */ 33); - EXPECT_EQ(TaggedCounter::kRecordValid, counter_.record_state_); -} - -static const char kTestFilename[] = "test_filename"; -static const char kTestHistogram[] = "test_histogram"; -const int kHistogramMin = 15; -const int kHistogramMax = 1024; -const int kHistogramBuckets = 23; - -class TaggedCounterReporterTest : public testing::Test { - protected: - virtual void SetUp() { - tagged_counter_ = new StrictMock(); - reporter_.tagged_counter_.reset(tagged_counter_); - metrics_lib_.reset(new StrictMock); - reporter_.SetMetricsLibraryInterface(metrics_lib_.get()); - ASSERT_TRUE(metrics_lib_.get() == reporter_.metrics_lib_); - } - virtual void TearDown() { - reporter_.SetMetricsLibraryInterface(NULL); - } - - void DoInit(); - StrictMock* tagged_counter_; - TaggedCounterReporter reporter_; - scoped_ptr metrics_lib_; -}; - -void TaggedCounterReporterTest::DoInit() { - EXPECT_CALL(*tagged_counter_, - Init(kTestFilename, - TaggedCounterReporter::Report, - &reporter_)) - .Times(1) - .RetiresOnSaturation(); - reporter_.Init(kTestFilename, - kTestHistogram, - kHistogramMin, - kHistogramMax, - kHistogramBuckets); - EXPECT_EQ(kTestHistogram, reporter_.histogram_name_); - EXPECT_EQ(kHistogramBuckets, reporter_.buckets_); - EXPECT_EQ(kHistogramMax, reporter_.max_); - EXPECT_EQ(kHistogramMin, reporter_.min_); -} - -TEST_F(TaggedCounterReporterTest, Init) { - DoInit(); -} - -TEST_F(TaggedCounterReporterTest, Update) { - DoInit(); - EXPECT_CALL(*tagged_counter_, Update(1, 0, 2)) - .Times(1) - .RetiresOnSaturation(); - reporter_.Update(1, 0, 2); -} - -TEST_F(TaggedCounterReporterTest, Flush) { - DoInit(); - EXPECT_CALL(*tagged_counter_, Flush()) - .Times(1) - .RetiresOnSaturation(); - reporter_.Flush(); -} - -TEST_F(TaggedCounterReporterTest, Report) { - DoInit(); - EXPECT_CALL(*metrics_lib_, SendToUMA(kTestHistogram, - 301, - kHistogramMin, - kHistogramMax, - kHistogramBuckets)) - .Times(1) - .RetiresOnSaturation(); - reporter_.Report(&reporter_, 301); -} - -class FrequencyCounterTest : public testing::Test { - protected: - virtual void SetUp() { - tagged_counter_ = NULL; - } - - void CheckInit(int32 cycle_duration); - void CheckCycleNumber(int32 cycle_duration); - - FrequencyCounter frequency_counter_; - StrictMock* tagged_counter_; - - TaggedCounter::Reporter reporter_; -}; - -void FrequencyCounterTest::CheckInit(int32 cycle_duration) { - tagged_counter_ = new StrictMock; - frequency_counter_.Init(tagged_counter_, cycle_duration); - EXPECT_EQ(cycle_duration, frequency_counter_.cycle_duration_); - EXPECT_EQ(tagged_counter_, frequency_counter_.tagged_counter_.get()); -} - -TEST_F(FrequencyCounterTest, Init) { - CheckInit(100); -} - -void FrequencyCounterTest::CheckCycleNumber(int32 cycle_duration) { - CheckInit(cycle_duration); - EXPECT_EQ(150, frequency_counter_.GetCycleNumber( - cycle_duration * 150)); - EXPECT_EQ(150, frequency_counter_.GetCycleNumber( - cycle_duration * 150 + cycle_duration - 1)); - EXPECT_EQ(151, frequency_counter_.GetCycleNumber( - cycle_duration * 151 + 1)); - EXPECT_EQ(0, frequency_counter_.GetCycleNumber(0)); -} - - -TEST_F(FrequencyCounterTest, GetCycleNumberForWeek) { - CheckCycleNumber(kSecondsPerWeek); -} - -TEST_F(FrequencyCounterTest, GetCycleNumberForDay) { - CheckCycleNumber(kSecondsPerDay); -} - -TEST_F(FrequencyCounterTest, UpdateInternal) { - CheckInit(kSecondsPerWeek); - EXPECT_CALL(*tagged_counter_, Update(0, 150, 2)) - .Times(1) - .RetiresOnSaturation(); - frequency_counter_.UpdateInternal(2, kSecondsPerWeek * 150); -} - -class VersionCounterTest : public testing::Test { - protected: - virtual void SetUp() { - tagged_counter_ = NULL; - } - void Init(); - - VersionCounter version_counter_; - StrictMock* tagged_counter_; - - TaggedCounter::Reporter reporter_; -}; - -void VersionCounterTest::Init() { - tagged_counter_ = new StrictMock; - version_counter_.Init(tagged_counter_, 1); - EXPECT_EQ(tagged_counter_, version_counter_.tagged_counter_.get()); -} - -TEST_F(VersionCounterTest, UpdateInternal) { - Init(); - EXPECT_CALL(*tagged_counter_, Update(0, 150, 2)) - .Times(1) - .RetiresOnSaturation(); - version_counter_.UpdateInternal(2, 0, 150); -} - -} // namespace chromeos_metrics - -int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 0a5ac48ba..2906a64c6 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -34,7 +34,7 @@ ], }, 'sources': [ - 'counter.cc', + 'persistent_integer.cc', 'metrics_daemon.cc', 'metrics_daemon_main.cc', ] @@ -73,15 +73,6 @@ 'metrics_library_test.cc', ] }, - { - 'target_name': 'counter_test', - 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], - 'sources': [ - 'counter.cc', - 'counter_test.cc', - ] - }, { 'target_name': 'timer_test', 'type': 'executable', diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index d6eb8a302..883c132b8 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -22,13 +22,12 @@ #include #include -#include "counter.h" - using base::FilePath; using base::StringPrintf; using base::Time; using base::TimeDelta; using base::TimeTicks; +using chromeos_metrics::PersistentInteger; using std::map; using std::string; using std::vector; @@ -58,54 +57,7 @@ static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute; const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected"; static const char kUncleanShutdownDetectedFile[] = - "/var/run/unclean-shutdown-detected"; - -// static metrics parameters -const char MetricsDaemon::kMetricDailyUseTimeName[] = - "Logging.DailyUseTime"; -const int MetricsDaemon::kMetricDailyUseTimeMin = 1; -const int MetricsDaemon::kMetricDailyUseTimeMax = kMinutesPerDay; -const int MetricsDaemon::kMetricDailyUseTimeBuckets = 50; - -// crash interval metrics -const char MetricsDaemon::kMetricKernelCrashIntervalName[] = - "Logging.KernelCrashInterval"; -const char MetricsDaemon::kMetricUncleanShutdownIntervalName[] = - "Logging.UncleanShutdownInterval"; -const char MetricsDaemon::kMetricUserCrashIntervalName[] = - "Logging.UserCrashInterval"; - -const int MetricsDaemon::kMetricCrashIntervalMin = 1; -const int MetricsDaemon::kMetricCrashIntervalMax = - 4 * kSecondsPerWeek; -const int MetricsDaemon::kMetricCrashIntervalBuckets = 50; - -// crash frequency metrics -const char MetricsDaemon::kMetricAnyCrashesDailyName[] = - "Logging.AnyCrashesDaily"; -const char MetricsDaemon::kMetricAnyCrashesWeeklyName[] = - "Logging.AnyCrashesWeekly"; -const char MetricsDaemon::kMetricKernelCrashesDailyName[] = - "Logging.KernelCrashesDaily"; -const char MetricsDaemon::kMetricKernelCrashesWeeklyName[] = - "Logging.KernelCrashesWeekly"; -const char MetricsDaemon::kMetricKernelCrashesVersionName[] = - "Logging.KernelCrashesSinceUpdate"; -const int MetricsDaemon::kMetricCumulativeCrashCountMin = 1; -const int MetricsDaemon::kMetricCumulativeCrashCountMax = 500; -const int MetricsDaemon::kMetricCumulativeCrashCountBuckets = 100; - -const char MetricsDaemon::kMetricUncleanShutdownsDailyName[] = - "Logging.UncleanShutdownsDaily"; -const char MetricsDaemon::kMetricUncleanShutdownsWeeklyName[] = - "Logging.UncleanShutdownsWeekly"; -const char MetricsDaemon::kMetricUserCrashesDailyName[] = - "Logging.UserCrashesDaily"; -const char MetricsDaemon::kMetricUserCrashesWeeklyName[] = - "Logging.UserCrashesWeekly"; -const int MetricsDaemon::kMetricCrashFrequencyMin = 1; -const int MetricsDaemon::kMetricCrashFrequencyMax = 100; -const int MetricsDaemon::kMetricCrashFrequencyBuckets = 50; + "/var/run/unclean-shutdown-detected"; // disk stats metrics @@ -159,13 +111,6 @@ const char MetricsDaemon::kMetricSwapOutShortName[] = const char MetricsDaemon::kMetricScaledCpuFrequencyName[] = "Platform.CpuFrequencyThermalScaling"; -// persistent metrics path -const char MetricsDaemon::kMetricsPath[] = "/var/log/metrics"; - -// file containing OS version string -const char MetricsDaemon::kLsbReleasePath[] = "/etc/lsb-release"; - - // static const char* MetricsDaemon::kPowerStates_[] = { #define STATE(name, capname) #name, @@ -205,7 +150,6 @@ MetricsDaemon::MetricsDaemon() stats_initial_time_(0) {} MetricsDaemon::~MetricsDaemon() { - DeleteFrequencyCounters(); } double MetricsDaemon::GetActiveTime() { @@ -219,14 +163,6 @@ double MetricsDaemon::GetActiveTime() { } } -void MetricsDaemon::DeleteFrequencyCounters() { - for (FrequencyCounters::iterator i = frequency_counters_.begin(); - i != frequency_counters_.end(); ++i) { - delete i->second; - i->second = NULL; - } -} - void MetricsDaemon::Run(bool run_as_daemon) { base::AtExitManager at_exit_manager; @@ -236,72 +172,22 @@ void MetricsDaemon::Run(bool run_as_daemon) { if (CheckSystemCrash(kKernelCrashDetectedFile)) { ProcessKernelCrash(); } - kernel_crash_version_counter_->FlushOnChange(GetOsVersionHash()); if (CheckSystemCrash(kUncleanShutdownDetectedFile)) { ProcessUncleanShutdown(); } + // On OS version change, clear version stats (which are reported daily). + int32 version = GetOsVersionHash(); + if (version_cycle_->Get() != version) { + version_cycle_->Set(version); + SendKernelCrashesCumulativeCountSample(); + kernel_crashes_version_count_->Set(0); + } + Loop(); } -FilePath MetricsDaemon::GetHistogramPath(const char* histogram_name) { - return FilePath(kMetricsPath).Append(histogram_name); -} - -void MetricsDaemon::ConfigureCrashIntervalReporter( - const char* histogram_name, - scoped_ptr* reporter) { - reporter->reset(new chromeos_metrics::TaggedCounterReporter()); - FilePath file_path = GetHistogramPath(histogram_name); - (*reporter)->Init(file_path.value().c_str(), - histogram_name, - kMetricCrashIntervalMin, - kMetricCrashIntervalMax, - kMetricCrashIntervalBuckets); -} - -void MetricsDaemon::ConfigureCrashFrequencyReporter( - const char* histogram_name) { - scoped_ptr reporter( - new chromeos_metrics::TaggedCounterReporter()); - FilePath file_path = GetHistogramPath(histogram_name); - reporter->Init(file_path.value().c_str(), - histogram_name, - kMetricCrashFrequencyMin, - kMetricCrashFrequencyMax, - kMetricCrashFrequencyBuckets); - scoped_ptr new_counter( - new chromeos_metrics::FrequencyCounter()); - time_t cycle_duration = strstr(histogram_name, "Weekly") != NULL ? - chromeos_metrics::kSecondsPerWeek : - chromeos_metrics::kSecondsPerDay; - new_counter->Init( - static_cast( - reporter.release()), - cycle_duration); - frequency_counters_[histogram_name] = new_counter.release(); -} - -void MetricsDaemon::ConfigureCrashVersionReporter( - const char* histogram_name) { - scoped_ptr reporter( - new chromeos_metrics::TaggedCounterReporter()); - FilePath file_path = GetHistogramPath(histogram_name); - reporter->Init(file_path.value().c_str(), - histogram_name, - kMetricCumulativeCrashCountMin, - kMetricCumulativeCrashCountMax, - kMetricCumulativeCrashCountBuckets); - scoped_ptr new_counter( - new chromeos_metrics::VersionCounter()); - new_counter->Init( - static_cast( - reporter.release()), - chromeos_metrics::kSecondsPerDay); - kernel_crash_version_counter_ = new_counter.release(); -} - uint32 MetricsDaemon::GetOsVersionHash() { static uint32 cached_version_hash = 0; static bool version_hash_is_cached = false; @@ -328,31 +214,39 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; - chromeos_metrics::TaggedCounterReporter:: - SetMetricsLibraryInterface(metrics_lib); - static const char kDailyUseRecordFile[] = "/var/log/metrics/daily-usage"; - daily_use_.reset(new chromeos_metrics::TaggedCounter()); - daily_use_->Init(kDailyUseRecordFile, &ReportDailyUse, this); + daily_use_.reset( + new PersistentInteger("Logging.DailyUseTime")); - ConfigureCrashIntervalReporter(kMetricKernelCrashIntervalName, - &kernel_crash_interval_); - ConfigureCrashIntervalReporter(kMetricUncleanShutdownIntervalName, - &unclean_shutdown_interval_); - ConfigureCrashIntervalReporter(kMetricUserCrashIntervalName, - &user_crash_interval_); + kernel_crash_interval_.reset( + new PersistentInteger("Logging.KernelCrashInterval")); + unclean_shutdown_interval_.reset( + new PersistentInteger("Logging.UncleanShutdownInterval")); + user_crash_interval_.reset( + new PersistentInteger("Logging.UserCrashInterval")); - DeleteFrequencyCounters(); - ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName); - ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName); - ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName); - ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName); - ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsDailyName); - ConfigureCrashFrequencyReporter(kMetricUncleanShutdownsWeeklyName); - ConfigureCrashFrequencyReporter(kMetricUserCrashesDailyName); - ConfigureCrashFrequencyReporter(kMetricUserCrashesWeeklyName); + any_crashes_daily_count_.reset( + new PersistentInteger("Logging.AnyCrashesDaily")); + any_crashes_weekly_count_.reset( + new PersistentInteger("Logging.AnyCrashesWeekly")); + user_crashes_daily_count_.reset( + new PersistentInteger("Logging.UserCrashesDaily")); + user_crashes_weekly_count_.reset( + new PersistentInteger("Logging.UserCrashesWeekly")); + kernel_crashes_daily_count_.reset( + new PersistentInteger("Logging.KernelCrashesDaily")); + kernel_crashes_weekly_count_.reset( + new PersistentInteger("Logging.KernelCrashesWeekly")); + kernel_crashes_version_count_.reset( + new PersistentInteger("Logging.KernelCrashesSinceUpdate")); + unclean_shutdowns_daily_count_.reset( + new PersistentInteger("Logging.UncleanShutdownsDaily")); + unclean_shutdowns_weekly_count_.reset( + new PersistentInteger("Logging.UncleanShutdownsWeekly")); - ConfigureCrashVersionReporter(kMetricKernelCrashesVersionName); + daily_cycle_.reset(new PersistentInteger("daily.cycle")); + weekly_cycle_.reset(new PersistentInteger("weekly.cycle")); + version_cycle_.reset(new PersistentInteger("version.cycle")); diskstats_path_ = diskstats_path; vmstats_path_ = vmstats_path; @@ -504,6 +398,37 @@ MetricsDaemon::LookupSessionState(const char* state_name) { return kUnknownSessionState; } +void MetricsDaemon::ReportStats(Time now) { + TimeDelta since_epoch = now - Time::UnixEpoch(); + int day = since_epoch.InDays(); + int week = day / 7; + + if (daily_cycle_->Get() == day) { + // We did today already. + return; + } + daily_cycle_->Set(day); + + // Daily stats. + SendCrashFrequencySample(any_crashes_daily_count_); + SendCrashFrequencySample(user_crashes_daily_count_); + SendCrashFrequencySample(kernel_crashes_daily_count_); + SendCrashFrequencySample(unclean_shutdowns_daily_count_); + SendKernelCrashesCumulativeCountSample(); + + if (weekly_cycle_->Get() == week) { + // We did this week already. + return; + } + weekly_cycle_->Set(week); + + // Weekly stats. + SendCrashFrequencySample(any_crashes_weekly_count_); + SendCrashFrequencySample(user_crashes_weekly_count_); + SendCrashFrequencySample(kernel_crashes_weekly_count_); + SendCrashFrequencySample(unclean_shutdowns_weekly_count_); +} + void MetricsDaemon::SetUserActiveState(bool active, Time now) { DLOG(INFO) << "user: " << (active ? "active" : "inactive"); @@ -519,21 +444,12 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { seconds = static_cast(since_active.InSeconds()); } } - TimeDelta since_epoch = now - Time(); - int day = since_epoch.InDays(); - daily_use_->Update(day, seconds, 0); - user_crash_interval_->Update(0, seconds, 0); - kernel_crash_interval_->Update(0, seconds, 0); + daily_use_->Add(seconds); + user_crash_interval_->Add(seconds); + kernel_crash_interval_->Add(seconds); - // Flush finished cycles of all frequency counters. - for (FrequencyCounters::iterator i = frequency_counters_.begin(); - i != frequency_counters_.end(); ++i) { - i->second->FlushFinishedCycles(); - } - // Report count if we're on a new cycle. FlushOnChange can also reset the - // counter, but not when called from here, because any version change has - // already been processed during initialization. - kernel_crash_version_counter_->FlushOnChange(GetOsVersionHash()); + // Report daily and weekly stats as needed. + ReportStats(now); // Schedules a use monitor on inactive->active transitions and // unschedules it on active->inactive transitions. @@ -553,12 +469,12 @@ void MetricsDaemon::ProcessUserCrash() { SetUserActiveState(user_active_, Time::Now()); // Reports the active use time since the last crash and resets it. - user_crash_interval_->Flush(); + SendCrashIntervalSample(user_crash_interval_); - frequency_counters_[kMetricUserCrashesDailyName]->Update(1); - frequency_counters_[kMetricUserCrashesWeeklyName]->Update(1); - frequency_counters_[kMetricAnyCrashesDailyName]->Update(1); - frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1); + any_crashes_daily_count_->Add(1); + any_crashes_weekly_count_->Add(1); + user_crashes_daily_count_->Add(1); + user_crashes_weekly_count_->Add(1); } void MetricsDaemon::ProcessKernelCrash() { @@ -566,14 +482,14 @@ void MetricsDaemon::ProcessKernelCrash() { SetUserActiveState(user_active_, Time::Now()); // Reports the active use time since the last crash and resets it. - kernel_crash_interval_->Flush(); + SendCrashIntervalSample(kernel_crash_interval_); - frequency_counters_[kMetricKernelCrashesDailyName]->Update(1); - frequency_counters_[kMetricKernelCrashesWeeklyName]->Update(1); - frequency_counters_[kMetricAnyCrashesDailyName]->Update(1); - frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1); + any_crashes_daily_count_->Add(1); + any_crashes_weekly_count_->Add(1); + kernel_crashes_daily_count_->Add(1); + kernel_crashes_weekly_count_->Add(1); - kernel_crash_version_counter_->Update(1, GetOsVersionHash()); + kernel_crashes_version_count_->Add(1); } void MetricsDaemon::ProcessUncleanShutdown() { @@ -581,12 +497,12 @@ void MetricsDaemon::ProcessUncleanShutdown() { SetUserActiveState(user_active_, Time::Now()); // Reports the active use time since the last crash and resets it. - unclean_shutdown_interval_->Flush(); + SendCrashIntervalSample(unclean_shutdown_interval_); - frequency_counters_[kMetricUncleanShutdownsDailyName]->Update(1); - frequency_counters_[kMetricUncleanShutdownsWeeklyName]->Update(1); - frequency_counters_[kMetricAnyCrashesDailyName]->Update(1); - frequency_counters_[kMetricAnyCrashesWeeklyName]->Update(1); + unclean_shutdowns_daily_count_->Add(1); + unclean_shutdowns_weekly_count_->Add(1); + any_crashes_daily_count_->Add(1); + any_crashes_weekly_count_->Add(1); } bool MetricsDaemon::CheckSystemCrash(const string& crash_file) { @@ -830,7 +746,7 @@ void MetricsDaemon::SendCpuThrottleMetrics() { // scaled_freq is not the actual turbo frequency. We indicate this situation // with a 101% value. int percent = scaled_freq > max_freq ? 101 : scaled_freq / (max_freq / 100); - SendLinearMetric(kMetricScaledCpuFrequencyName, percent, 101, 102); + SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102); } // static @@ -868,29 +784,29 @@ void MetricsDaemon::StatsCallback() { switch (stats_state_) { case kStatsShort: if (diskstats_success) { - SendMetric(kMetricReadSectorsShortName, + SendSample(kMetricReadSectorsShortName, read_sectors_per_second, 1, kMetricSectorsIOMax, kMetricSectorsBuckets); - SendMetric(kMetricWriteSectorsShortName, + SendSample(kMetricWriteSectorsShortName, write_sectors_per_second, 1, kMetricSectorsIOMax, kMetricSectorsBuckets); } if (vmstats_success) { - SendMetric(kMetricPageFaultsShortName, + SendSample(kMetricPageFaultsShortName, page_faults_per_second, 1, kMetricPageFaultsMax, kMetricPageFaultsBuckets); - SendMetric(kMetricSwapInShortName, + SendSample(kMetricSwapInShortName, swap_in_per_second, 1, kMetricPageFaultsMax, kMetricPageFaultsBuckets); - SendMetric(kMetricSwapOutShortName, + SendSample(kMetricSwapOutShortName, swap_out_per_second, 1, kMetricPageFaultsMax, @@ -903,12 +819,12 @@ void MetricsDaemon::StatsCallback() { break; case kStatsLong: if (diskstats_success) { - SendMetric(kMetricReadSectorsLongName, + SendSample(kMetricReadSectorsLongName, read_sectors_per_second, 1, kMetricSectorsIOMax, kMetricSectorsBuckets); - SendMetric(kMetricWriteSectorsLongName, + SendSample(kMetricWriteSectorsLongName, write_sectors_per_second, 1, kMetricSectorsIOMax, @@ -918,17 +834,17 @@ void MetricsDaemon::StatsCallback() { write_sectors_ = write_sectors_now; } if (vmstats_success) { - SendMetric(kMetricPageFaultsLongName, + SendSample(kMetricPageFaultsLongName, page_faults_per_second, 1, kMetricPageFaultsMax, kMetricPageFaultsBuckets); - SendMetric(kMetricSwapInLongName, + SendSample(kMetricSwapInLongName, swap_in_per_second, 1, kMetricPageFaultsMax, kMetricPageFaultsBuckets); - SendMetric(kMetricSwapOutLongName, + SendSample(kMetricSwapOutLongName, swap_out_per_second, 1, kMetricPageFaultsMax, @@ -1018,11 +934,11 @@ bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { case kMeminfoOp_HistPercent: // report value as percent of total memory percent = fields[i].value * 100 / total_memory; - SendLinearMetric(metrics_name, percent, 100, 101); + SendLinearSample(metrics_name, percent, 100, 101); break; case kMeminfoOp_HistLog: // report value in kbytes, log scale, 4Gb max - SendMetric(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100); + SendSample(metrics_name, fields[i].value, 1, 4 * 1000 * 1000, 100); break; case kMeminfoOp_SwapTotal: swap_total = fields[i].value; @@ -1034,8 +950,8 @@ bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { if (swap_total > 0) { int swap_used = swap_total - swap_free; int swap_used_percent = swap_used * 100 / swap_total; - SendMetric("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100); - SendLinearMetric("Platform.MeminfoSwapUsedPercent", swap_used_percent, + SendSample("Platform.MeminfoSwapUsed", swap_used, 1, 8 * 1000 * 1000, 100); + SendLinearSample("Platform.MeminfoSwapUsedPercent", swap_used_percent, 100, 101); } return true; @@ -1142,7 +1058,7 @@ bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) { } string metrics_name = base::StringPrintf("Platform.MemuseAnon%d", memuse_interval_index_); - SendLinearMetric(metrics_name, (active_anon + inactive_anon) * 100 / total, + SendLinearSample(metrics_name, (active_anon + inactive_anon) * 100 / total, 100, 101); return true; } @@ -1154,20 +1070,49 @@ void MetricsDaemon::ReportDailyUse(void* handle, int count) { MetricsDaemon* daemon = static_cast(handle); int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; - daemon->SendMetric(kMetricDailyUseTimeName, minutes, - kMetricDailyUseTimeMin, - kMetricDailyUseTimeMax, - kMetricDailyUseTimeBuckets); + daemon->SendSample("Logging.DailyUseTime", + minutes, + 1, + kMinutesPerDay, + 50); } -void MetricsDaemon::SendMetric(const string& name, int sample, +void MetricsDaemon::SendSample(const string& name, int sample, int min, int max, int nbuckets) { DLOG(INFO) << "received metric: " << name << " " << sample << " " << min << " " << max << " " << nbuckets; metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); } -void MetricsDaemon::SendLinearMetric(const string& name, int sample, +void MetricsDaemon::SendKernelCrashesCumulativeCountSample() { + // Report the number of crashes for this OS version, but don't clear the + // counter. It is cleared elsewhere on version change. + SendSample(kernel_crashes_version_count_->Name(), + kernel_crashes_version_count_->Get(), + 1, // value of first bucket + 500, // value of last bucket + 100); // number of buckets +} + +void MetricsDaemon::SendCrashIntervalSample( + const scoped_ptr& interval) { + SendSample(interval->Name(), + interval->GetAndClear(), + 1, // value of first bucket + 4 * kSecondsPerWeek, // value of last bucket + 50); // number of buckets +} + +void MetricsDaemon::SendCrashFrequencySample( + const scoped_ptr& frequency) { + SendSample(frequency->Name(), + frequency->GetAndClear(), + 1, // value of first bucket + 100, // value of last bucket + 50); // number of buckets +} + +void MetricsDaemon::SendLinearSample(const string& name, int sample, int max, int nbuckets) { DLOG(INFO) << "received linear metric: " << name << " " << sample << " " << max << " " << nbuckets; diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 6d8f81b1b..e4bf853ac 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -15,13 +15,9 @@ #include // for FRIEND_TEST #include "metrics_library.h" +#include "persistent_integer.h" -namespace chromeos_metrics { -class FrequencyCounter; -class VersionCounter; -class TaggedCounter; -class TaggedCounterReporter; -} +using chromeos_metrics::PersistentInteger; class MetricsDaemon { @@ -66,7 +62,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval); FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval); FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); - FRIEND_TEST(MetricsDaemonTest, SendMetric); + FRIEND_TEST(MetricsDaemonTest, SendSample); FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics); FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); @@ -128,37 +124,7 @@ class MetricsDaemon { uint64_t swap_out_; // pages swapped out }; - typedef std::map - FrequencyCounters; - // Metric parameters. - static const char kMetricAnyCrashesDailyName[]; - static const char kMetricAnyCrashesWeeklyName[]; - static const int kMetricCrashFrequencyBuckets; - static const int kMetricCrashFrequencyMax; - static const int kMetricCrashFrequencyMin; - static const int kMetricCrashIntervalBuckets; - static const int kMetricCrashIntervalMax; - static const int kMetricCrashIntervalMin; - static const int kMetricCumulativeCrashCountBuckets; - static const int kMetricCumulativeCrashCountMax; - static const int kMetricCumulativeCrashCountMin; - static const int kMetricDailyUseTimeBuckets; - static const int kMetricDailyUseTimeMax; - static const int kMetricDailyUseTimeMin; - static const char kMetricDailyUseTimeName[]; - static const char kMetricKernelCrashesDailyName[]; - static const char kMetricKernelCrashesWeeklyName[]; - static const char kMetricKernelCrashesVersionName[]; - static const char kMetricKernelCrashIntervalName[]; - static const char kMetricsPath[]; - static const char kLsbReleasePath[]; - static const char kMetricUncleanShutdownIntervalName[]; - static const char kMetricUncleanShutdownsDailyName[]; - static const char kMetricUncleanShutdownsWeeklyName[]; - static const char kMetricUserCrashesDailyName[]; - static const char kMetricUserCrashesWeeklyName[]; - static const char kMetricUserCrashIntervalName[]; static const char kMetricReadSectorsLongName[]; static const char kMetricReadSectorsShortName[]; static const char kMetricWriteSectorsLongName[]; @@ -189,23 +155,6 @@ class MetricsDaemon { // Returns the active time since boot (uptime minus sleep time) in seconds. double GetActiveTime(); - // Clears and deletes the data contained in frequency_counters_. - void DeleteFrequencyCounters(); - - // Configures the given crash interval reporter. - void ConfigureCrashIntervalReporter( - const char* histogram_name, - scoped_ptr* reporter); - - // Configures the given frequency counter reporter. - void ConfigureCrashFrequencyReporter(const char* histogram_name); - - // Configures the given version counter reporter. - void ConfigureCrashVersionReporter(const char* histogram_name); - - // Returns file path to persistent file for generating given histogram. - base::FilePath GetHistogramPath(const char* histogram_name); - // Creates the event loop and enters it. void Loop(); @@ -285,15 +234,26 @@ class MetricsDaemon { // Sends a regular (exponential) histogram sample to Chrome for // transport to UMA. See MetricsLibrary::SendToUMA in // metrics_library.h for a description of the arguments. - void SendMetric(const std::string& name, int sample, + void SendSample(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, + void SendLinearSample(const std::string& name, int sample, int max, int nbuckets); + // Sends a histogram sample with the total number of kernel crashes since the + // last version update. + void SendKernelCrashesCumulativeCountSample(); + + // Sends a sample representing a time interval between two crashes of the + // same type. + void SendCrashIntervalSample(const scoped_ptr& interval); + + // Sends a sample representing a frequency of crashes of some type. + void SendCrashFrequencySample(const scoped_ptr& frequency); + // Initializes vm and disk stats reporting. void StatsReporterInit(); @@ -361,6 +321,9 @@ class MetricsDaemon { // Reads an integer CPU frequency value from sysfs. bool ReadFreqToInt(const std::string& sysfs_file_name, int* value); + // Report UMA stats when cycles (daily or weekly) have changed. + void ReportStats(base::Time now); + // Reads the current OS version from /etc/lsb-release and hashes it // to a unsigned 32-bit int. uint32 GetOsVersionHash(); @@ -391,26 +354,6 @@ class MetricsDaemon { // the timestamp. base::Time user_active_last_; - // Daily active use time in seconds. - scoped_ptr daily_use_; - - // Active use time between user-space process crashes. - scoped_ptr user_crash_interval_; - - // Active use time between kernel crashes. - scoped_ptr kernel_crash_interval_; - - // Active use time between unclean shutdowns crashes. - scoped_ptr - unclean_shutdown_interval_; - - // Map of all frequency counters, to simplify flushing them. - FrequencyCounters frequency_counters_; - - // This contains a cumulative number of kernel crashes since the latest - // version update. - chromeos_metrics::VersionCounter* kernel_crash_version_counter_; - // Sleep period until the next daily usage aggregation performed by // the daily use monitor (see ScheduleUseMonitor). int usemon_interval_; @@ -432,6 +375,27 @@ class MetricsDaemon { StatsState stats_state_; double stats_initial_time_; + // Persistent counters for crash statistics. + scoped_ptr daily_cycle_; + scoped_ptr weekly_cycle_; + scoped_ptr version_cycle_; + + scoped_ptr daily_use_; + + scoped_ptr user_crash_interval_; + scoped_ptr kernel_crash_interval_; + scoped_ptr unclean_shutdown_interval_; + + scoped_ptr any_crashes_daily_count_; + scoped_ptr any_crashes_weekly_count_; + scoped_ptr user_crashes_daily_count_; + scoped_ptr user_crashes_weekly_count_; + scoped_ptr kernel_crashes_daily_count_; + scoped_ptr kernel_crashes_weekly_count_; + scoped_ptr kernel_crashes_version_count_; + scoped_ptr unclean_shutdowns_daily_count_; + scoped_ptr unclean_shutdowns_weekly_count_; + std::string diskstats_path_; std::string vmstats_path_; std::string scaling_max_freq_path_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 255f26070..aea0f4bd6 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -13,25 +13,21 @@ #include #include -#include "counter_mock.h" #include "metrics_daemon.h" #include "metrics_library_mock.h" +#include "persistent_integer_mock.h" using base::FilePath; using base::StringPrintf; using base::Time; using base::TimeTicks; -using chromeos_metrics::FrequencyCounter; -using chromeos_metrics::FrequencyCounterMock; -using chromeos_metrics::TaggedCounterMock; -using chromeos_metrics::TaggedCounterReporter; -using chromeos_metrics::TaggedCounterReporterMock; using std::string; using std::vector; using ::testing::_; using ::testing::Return; using ::testing::StrictMock; using ::testing::AtLeast; +using chromeos_metrics::PersistentIntegerMock; static const int kSecondsPerDay = 24 * 60 * 60; @@ -61,9 +57,6 @@ class TestTicks : public TimeTicks { class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { - EXPECT_EQ(NULL, daemon_.daily_use_.get()); - EXPECT_EQ(NULL, daemon_.kernel_crash_interval_.get()); - EXPECT_EQ(NULL, daemon_.user_crash_interval_.get()); kFakeDiskStats[0] = base::StringPrintf(kFakeDiskStatsFormat, kFakeReadSectors[0], kFakeWriteSectors[0]); @@ -74,64 +67,10 @@ class MetricsDaemonTest : public testing::Test { CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, 10000000); CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 10000000); + chromeos_metrics::PersistentInteger::SetTestingMode(true); daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath, kFakeVmStatsPath, kFakeScalingMaxFreqPath, kFakeCpuinfoMaxFreqPath); - // Check configuration of a few histograms. - FrequencyCounter* frequency_counter = - daemon_.frequency_counters_[MetricsDaemon::kMetricAnyCrashesDailyName]; - const TaggedCounterReporter* reporter = GetReporter(frequency_counter); - EXPECT_EQ(MetricsDaemon::kMetricAnyCrashesDailyName, - reporter->histogram_name()); - EXPECT_EQ(chromeos_metrics::kSecondsPerDay, - frequency_counter->cycle_duration()); - EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyMin, reporter->min()); - EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyMax, reporter->max()); - EXPECT_EQ(MetricsDaemon::kMetricCrashFrequencyBuckets, reporter->buckets()); - - frequency_counter = - daemon_.frequency_counters_[MetricsDaemon::kMetricAnyCrashesWeeklyName]; - reporter = GetReporter(frequency_counter); - EXPECT_EQ(MetricsDaemon::kMetricAnyCrashesWeeklyName, - reporter->histogram_name()); - EXPECT_EQ(chromeos_metrics::kSecondsPerWeek, - frequency_counter->cycle_duration()); - - EXPECT_EQ(MetricsDaemon::kMetricKernelCrashIntervalName, - daemon_.kernel_crash_interval_->histogram_name()); - EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalMin, - daemon_.kernel_crash_interval_->min()); - EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalMax, - daemon_.kernel_crash_interval_->max()); - EXPECT_EQ(MetricsDaemon::kMetricCrashIntervalBuckets, - daemon_.kernel_crash_interval_->buckets()); - - EXPECT_EQ(MetricsDaemon::kMetricUncleanShutdownIntervalName, - daemon_.unclean_shutdown_interval_->histogram_name()); - - // Tests constructor initialization. Switches to mock counters. - EXPECT_TRUE(NULL != daemon_.daily_use_.get()); - EXPECT_TRUE(NULL != daemon_.kernel_crash_interval_.get()); - EXPECT_TRUE(NULL != daemon_.user_crash_interval_.get()); - - // Allocates mock counter and transfers ownership. - daily_use_ = new StrictMock(); - daemon_.daily_use_.reset(daily_use_); - kernel_crash_interval_ = new StrictMock(); - daemon_.kernel_crash_interval_.reset(kernel_crash_interval_); - user_crash_interval_ = new StrictMock(); - daemon_.user_crash_interval_.reset(user_crash_interval_); - unclean_shutdown_interval_ = new StrictMock(); - daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_); - - // Reset all frequency counter reporters to mocks for further testing. - MetricsDaemon::FrequencyCounters::iterator i; - for (i = daemon_.frequency_counters_.begin(); - i != daemon_.frequency_counters_.end(); ++i) { - delete i->second; - i->second = new StrictMock(); - } - EXPECT_FALSE(daemon_.user_active_); EXPECT_TRUE(daemon_.user_active_last_.is_null()); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); @@ -139,6 +78,24 @@ class MetricsDaemonTest : public testing::Test { base::DeleteFile(FilePath(kTestDir), true); base::CreateDirectory(FilePath(kTestDir)); + + // Replace original persistent values with mock ones. + daily_use_mock_ = + new StrictMock("1.mock"); + daemon_.daily_use_.reset(daily_use_mock_); + + kernel_crash_interval_mock_ = + new StrictMock("2.mock"); + daemon_.kernel_crash_interval_.reset(kernel_crash_interval_mock_); + + user_crash_interval_mock_ = + new StrictMock("3.mock"); + daemon_.user_crash_interval_.reset(user_crash_interval_mock_); + + unclean_shutdown_interval_mock_ = + new StrictMock("4.mock"); + daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_mock_); + } virtual void TearDown() { @@ -147,57 +104,37 @@ class MetricsDaemonTest : public testing::Test { EXPECT_EQ(0, unlink(kFakeCpuinfoMaxFreqPath)); } - const TaggedCounterReporter* - GetReporter(FrequencyCounter* frequency_counter) const { - return static_cast( - &frequency_counter->tagged_counter()); - } - - void ExpectFrequencyFlushCalls() { - MetricsDaemon::FrequencyCounters::iterator i; - for (i = daemon_.frequency_counters_.begin(); - i != daemon_.frequency_counters_.end(); ++i) { - FrequencyCounterMock* mock = - static_cast(i->second); - EXPECT_CALL(*mock, FlushFinishedCycles()); - } - } - // Adds active use aggregation counters update expectations that the - // specified tag/count update will be generated. - void ExpectActiveUseUpdate(int daily_tag, int count) { - EXPECT_CALL(*daily_use_, Update(daily_tag, count, 0)) + // specified count will be added. + void ExpectActiveUseUpdate(int count) { + EXPECT_CALL(*daily_use_mock_, Add(count)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*kernel_crash_interval_, Update(0, count, 0)) + EXPECT_CALL(*kernel_crash_interval_mock_, Add(count)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*user_crash_interval_, Update(0, count, 0)) + EXPECT_CALL(*user_crash_interval_mock_, Add(count)) .Times(1) .RetiresOnSaturation(); - ExpectFrequencyFlushCalls(); } - // Adds active use aggregation counters update expectations that - // ignore the update arguments. + // As above, but ignore values of counter updates. void IgnoreActiveUseUpdate() { - EXPECT_CALL(*daily_use_, Update(_, _, _)) + EXPECT_CALL(*daily_use_mock_, Add(_)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*kernel_crash_interval_, Update(_, _, _)) + EXPECT_CALL(*kernel_crash_interval_mock_, Add(_)) .Times(1) .RetiresOnSaturation(); - EXPECT_CALL(*user_crash_interval_, Update(_, _, _)) + EXPECT_CALL(*user_crash_interval_mock_, Add(_)) .Times(1) .RetiresOnSaturation(); - ExpectFrequencyFlushCalls(); } // Adds a metrics library mock expectation that the specified metric // will be generated. - void ExpectMetric(const string& name, int sample, - int min, int max, int buckets) { - EXPECT_CALL(metrics_lib_, SendToUMA(name, sample, min, max, buckets)) + void ExpectSample(int sample) { + EXPECT_CALL(metrics_lib_, SendToUMA(_, sample, _, _, _)) .Times(1) .WillOnce(Return(true)) .RetiresOnSaturation(); @@ -205,11 +142,8 @@ class MetricsDaemonTest : public testing::Test { // Adds a metrics library mock expectation that the specified daily // use time metric will be generated. - void ExpectDailyUseTimeMetric(int sample) { - ExpectMetric(MetricsDaemon::kMetricDailyUseTimeName, sample, - MetricsDaemon::kMetricDailyUseTimeMin, - MetricsDaemon::kMetricDailyUseTimeMax, - MetricsDaemon::kMetricDailyUseTimeBuckets); + void ExpectDailyUseTimeSample(int sample) { + ExpectSample(sample); } // Converts from seconds to a Time object. @@ -247,12 +181,6 @@ class MetricsDaemonTest : public testing::Test { dbus_message_unref(msg); } - // Gets the frequency counter for the given name. - FrequencyCounterMock& GetFrequencyMock(const char* histogram_name) { - return *static_cast( - daemon_.frequency_counters_[histogram_name]); - } - // Creates or overwrites an input file containing fake disk stats. void CreateFakeDiskStatsFile(const char* fake_stats) { if (unlink(kFakeDiskStatsPath) < 0) { @@ -277,17 +205,13 @@ class MetricsDaemonTest : public testing::Test { // The MetricsDaemon under test. MetricsDaemon daemon_; - // Metrics library mock. It's a strict mock so that all unexpected - // metric generation calls are marked as failures. + // Mocks. They are strict mock so that all unexpected + // calls are marked as failures. StrictMock metrics_lib_; - - // Counter mocks. They are strict mocks so that all unexpected - // update calls are marked as failures. They are pointers so that - // they can replace the scoped_ptr's allocated by the daemon. - StrictMock* daily_use_; - StrictMock* kernel_crash_interval_; - StrictMock* user_crash_interval_; - StrictMock* unclean_shutdown_interval_; + StrictMock* daily_use_mock_; + StrictMock* kernel_crash_interval_mock_; + StrictMock* user_crash_interval_mock_; + StrictMock* unclean_shutdown_interval_mock_; }; TEST_F(MetricsDaemonTest, CheckSystemCrash) { @@ -305,10 +229,10 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) { } TEST_F(MetricsDaemonTest, ReportDailyUse) { - ExpectDailyUseTimeMetric(/* sample */ 2); + ExpectDailyUseTimeSample(/* sample */ 2); MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 90); - ExpectDailyUseTimeMetric(/* sample */ 1); + ExpectDailyUseTimeSample(/* sample */ 1); MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 89); // There should be no metrics generated for the calls below. @@ -335,6 +259,9 @@ TEST_F(MetricsDaemonTest, LookupSessionState) { } TEST_F(MetricsDaemonTest, MessageFilter) { + // Ignore calls to SendToUMA. + EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); + DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); DBusHandlerResult res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); @@ -342,25 +269,6 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); IgnoreActiveUseUpdate(); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName), - Update(1)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName), - Update(1)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesDailyName), - Update(1)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesWeeklyName), - Update(1)) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(*user_crash_interval_, Flush()) - .Times(1) - .RetiresOnSaturation(); vector signal_args; msg = NewDBusSignalString("/", "org.chromium.CrashReporter", @@ -421,13 +329,16 @@ TEST_F(MetricsDaemonTest, MessageFilter) { } TEST_F(MetricsDaemonTest, PowerStateChanged) { - ExpectActiveUseUpdate(7, 0); + // Ignore calls to SendToUMA. + EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); + + ExpectActiveUseUpdate(0); daemon_.SetUserActiveState(/* active */ true, TestTime(7 * kSecondsPerDay + 15)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 15), daemon_.user_active_last_); - ExpectActiveUseUpdate(7, 30); + ExpectActiveUseUpdate(30); daemon_.PowerStateChanged("mem", TestTime(7 * kSecondsPerDay + 45)); EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); @@ -438,82 +349,36 @@ TEST_F(MetricsDaemonTest, PowerStateChanged) { EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_); - ExpectActiveUseUpdate(7, 0); + ExpectActiveUseUpdate(0); daemon_.PowerStateChanged("otherstate", TestTime(7 * kSecondsPerDay + 185)); EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(7 * kSecondsPerDay + 185), daemon_.user_active_last_); } -TEST_F(MetricsDaemonTest, ProcessKernelCrash) { - IgnoreActiveUseUpdate(); - EXPECT_CALL(*kernel_crash_interval_, Flush()) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName), - Update(1)); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName), - Update(1)); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricKernelCrashesDailyName), - Update(1)); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricKernelCrashesWeeklyName), - Update(1)); - daemon_.ProcessKernelCrash(); -} - -TEST_F(MetricsDaemonTest, ProcessUncleanShutdown) { - IgnoreActiveUseUpdate(); - EXPECT_CALL(*unclean_shutdown_interval_, Flush()) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName), - Update(1)); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName), - Update(1)); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUncleanShutdownsDailyName), - Update(1)); - EXPECT_CALL( - GetFrequencyMock(MetricsDaemon::kMetricUncleanShutdownsWeeklyName), - Update(1)); - daemon_.ProcessUncleanShutdown(); -} - -TEST_F(MetricsDaemonTest, ProcessUserCrash) { - IgnoreActiveUseUpdate(); - EXPECT_CALL(*user_crash_interval_, Flush()) - .Times(1) - .RetiresOnSaturation(); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesDailyName), - Update(1)); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricAnyCrashesWeeklyName), - Update(1)); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesDailyName), - Update(1)); - EXPECT_CALL(GetFrequencyMock(MetricsDaemon::kMetricUserCrashesWeeklyName), - Update(1)); - daemon_.ProcessUserCrash(); -} - -TEST_F(MetricsDaemonTest, SendMetric) { - ExpectMetric("Dummy.Metric", 3, 1, 100, 50); - daemon_.SendMetric("Dummy.Metric", /* sample */ 3, +TEST_F(MetricsDaemonTest, SendSample) { + ExpectSample(3); + daemon_.SendSample("Dummy.Metric", /* sample */ 3, /* min */ 1, /* max */ 100, /* buckets */ 50); } TEST_F(MetricsDaemonTest, SessionStateChanged) { - ExpectActiveUseUpdate(15, 0); + // Ignore calls to SendToUMA. + EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); + + ExpectActiveUseUpdate(0); daemon_.SessionStateChanged("started", TestTime(15 * kSecondsPerDay + 20)); EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(15 * kSecondsPerDay + 20), daemon_.user_active_last_); - ExpectActiveUseUpdate(15, 130); + ExpectActiveUseUpdate(130); daemon_.SessionStateChanged("stopped", TestTime(15 * kSecondsPerDay + 150)); EXPECT_EQ(MetricsDaemon::kSessionStateStopped, daemon_.session_state_); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(15 * kSecondsPerDay + 150), daemon_.user_active_last_); - ExpectActiveUseUpdate(15, 0); + ExpectActiveUseUpdate(0); daemon_.SessionStateChanged("otherstate", TestTime(15 * kSecondsPerDay + 300)); EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); @@ -522,31 +387,34 @@ TEST_F(MetricsDaemonTest, SessionStateChanged) { } TEST_F(MetricsDaemonTest, SetUserActiveState) { - ExpectActiveUseUpdate(5, 0); + // Ignore calls to SendToUMA. + EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); + + ExpectActiveUseUpdate(0); daemon_.SetUserActiveState(/* active */ false, TestTime(5 * kSecondsPerDay + 10)); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(5 * kSecondsPerDay + 10), daemon_.user_active_last_); - ExpectActiveUseUpdate(6, 0); + ExpectActiveUseUpdate(0); daemon_.SetUserActiveState(/* active */ true, TestTime(6 * kSecondsPerDay + 20)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(6 * kSecondsPerDay + 20), daemon_.user_active_last_); - ExpectActiveUseUpdate(6, 100); + ExpectActiveUseUpdate(100); daemon_.SetUserActiveState(/* active */ true, TestTime(6 * kSecondsPerDay + 120)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(6 * kSecondsPerDay + 120), daemon_.user_active_last_); - ExpectActiveUseUpdate(6, 110); + ExpectActiveUseUpdate(110); daemon_.SetUserActiveState(/* active */ false, TestTime(6 * kSecondsPerDay + 230)); EXPECT_FALSE(daemon_.user_active_); EXPECT_EQ(TestTime(6 * kSecondsPerDay + 230), daemon_.user_active_last_); - ExpectActiveUseUpdate(6, 0); + ExpectActiveUseUpdate(0); daemon_.SetUserActiveState(/* active */ false, TestTime(6 * kSecondsPerDay + 260)); EXPECT_FALSE(daemon_.user_active_); @@ -554,31 +422,28 @@ TEST_F(MetricsDaemonTest, SetUserActiveState) { } TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) { - ExpectActiveUseUpdate(10, 0); + // Ignore calls to SendToUMA. + EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); + + ExpectActiveUseUpdate(0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 500)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 500), daemon_.user_active_last_); - ExpectActiveUseUpdate(10, 0); + ExpectActiveUseUpdate(0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 300)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 300), daemon_.user_active_last_); - ExpectActiveUseUpdate(10, 0); + ExpectActiveUseUpdate(0); daemon_.SetUserActiveState(/* active */ true, TestTime(10 * kSecondsPerDay + 1000)); EXPECT_TRUE(daemon_.user_active_); EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_); } -TEST_F(MetricsDaemonTest, GetHistogramPath) { - EXPECT_EQ("/var/log/metrics/Logging.AnyCrashesDaily", - daemon_.GetHistogramPath( - MetricsDaemon::kMetricAnyCrashesDailyName).value()); -} - TEST_F(MetricsDaemonTest, ReportDiskStats) { long int read_sectors_now, write_sectors_now; diff --git a/metrics/persistent_integer.cc b/metrics/persistent_integer.cc new file mode 100644 index 000000000..c6ccf0ec7 --- /dev/null +++ b/metrics/persistent_integer.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "persistent_integer.h" + +#include + +#include +#include + +#include "metrics_library.h" + +namespace chromeos_metrics { + +// The directory for the persistent storage. +const char* const kBackingFilesDirectory = "/var/log/metrics/"; + +// Static class member instantiation. +bool PersistentInteger::testing_ = false; + +PersistentInteger::PersistentInteger(const std::string& name) : + value_(0), + version_(kVersion), + name_(name), + synced_(false) { + if (testing_) { + backing_file_name_ = name_; + } else { + backing_file_name_ = kBackingFilesDirectory + name_; + } +} + +PersistentInteger::~PersistentInteger() {} + +void PersistentInteger::Set(int64 value) { + value_ = value; + Write(value); +} + +int64 PersistentInteger::Get() { + // If not synced, then read. If the read fails, it's a good idea to write. + if (!synced_ && !Read()) + Write(value_); + return value_; +} + +int64 PersistentInteger::GetAndClear() { + int64 v = Get(); + Set(0); + return v; +} + +void PersistentInteger::Add(int64 x) { + Set(Get() + x); +} + +void PersistentInteger::Write(int64 value) { + int fd = HANDLE_EINTR(open(backing_file_name_.c_str(), + O_WRONLY | O_CREAT | O_TRUNC, + S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)); + PCHECK(fd >= 0) << "cannot open " << backing_file_name_ << " for writing"; + PCHECK((HANDLE_EINTR(write(fd, &version_, sizeof(version_))) == + sizeof(version_)) && + (HANDLE_EINTR(write(fd, &value_, sizeof(value_))) == + sizeof(value_))) + << "cannot write to " << backing_file_name_; + close(fd); + synced_ = true; +} + +bool PersistentInteger::Read() { + int fd = HANDLE_EINTR(open(backing_file_name_.c_str(), O_RDONLY)); + if (fd < 0) { + PLOG(WARNING) << "cannot open " << backing_file_name_ << " for reading"; + return false; + } + int32 version; + int64 value; + bool read_succeeded = false; + if (HANDLE_EINTR(read(fd, &version, sizeof(version))) == sizeof(version) && + version == version_ && + HANDLE_EINTR(read(fd, &value, sizeof(value))) == sizeof(value)) { + value_ = value; + read_succeeded = true; + synced_ = true; + } + close(fd); + return read_succeeded; +} + +void PersistentInteger::SetTestingMode(bool testing) { + testing_ = testing; +} + + +} // namespace chromeos_metrics diff --git a/metrics/persistent_integer.h b/metrics/persistent_integer.h new file mode 100644 index 000000000..7d920b5c7 --- /dev/null +++ b/metrics/persistent_integer.h @@ -0,0 +1,65 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_PERSISTENT_INTEGER_H_ +#define METRICS_PERSISTENT_INTEGER_H_ + +#include +#include + +namespace chromeos_metrics { + +// PersistentIntegers is a named 64-bit integer value backed by a file. +// The in-memory value acts as a write-through cache of the file value. +// If the backing file doesn't exist or has bad content, the value is 0. + +class PersistentInteger { + public: + PersistentInteger(const std::string& name); + + // Virtual only because of mock. + virtual ~PersistentInteger(); + + // Sets the value. This writes through to the backing file. + void Set(int64 v); + + // Gets the value. May sync from backing file first. + int64 Get(); + + // Returns the name of the object. + std::string Name() { return name_; } + + // Convenience function for Get() followed by Set(0). + int64 GetAndClear(); + + // Convenience function for v = Get, Set(v + x). + // Virtual only because of mock. + virtual void Add(int64 x); + + // After calling with |testing| = true, changes some behavior for the purpose + // of testing. For instance: instances created while testing use the current + // directory for the backing files. + static void SetTestingMode(bool testing); + + private: + static const int kVersion = 1001; + + // Writes |value| to the backing file, creating it if necessary. + void Write(int64 value); + + // Reads the value from the backing file, stores it in |value_|, and returns + // true if the backing file is valid. Returns false otherwise. + bool Read(); + + int64 value_; + int32 version_; + std::string name_; + std::string backing_file_name_; + bool synced_; + static bool testing_; +}; + +} + +#endif // METRICS_PERSISTENT_INTEGER_H_ diff --git a/metrics/persistent_integer_mock.h b/metrics/persistent_integer_mock.h new file mode 100644 index 000000000..46570f966 --- /dev/null +++ b/metrics/persistent_integer_mock.h @@ -0,0 +1,24 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_PERSISTENT_INTEGER_MOCK_H_ +#define METRICS_PERSISTENT_INTEGER_MOCK_H_ + +#include + +#include + +#include "persistent_integer.h" + +namespace chromeos_metrics { + +class PersistentIntegerMock : public PersistentInteger { + public: + PersistentIntegerMock(const std::string& name) : PersistentInteger(name) {} + MOCK_METHOD1(Add, void(int64 count)); +}; + +} // namespace chromeos_metrics + +#endif // METRICS_PERSISTENT_INTEGER_MOCK_H_ From 49fb1adc5ab55c5ed3a636f0bb7ef383188d2376 Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Thu, 13 Mar 2014 14:24:31 -0700 Subject: [PATCH 116/200] metrics: use abs() for integer only. This cast the type of the paramenter of abs() to integer before calling the abs() function. BUG=chromium:352331 TEST=FEATURES="test" emerge-amd64-generic platform2 passes. Change-Id: Id8b994b743345456f14194b45f3a288b7e3b74f3 Reviewed-on: https://chromium-review.googlesource.com/189849 Reviewed-by: Caroline Tice Reviewed-by: Mike Frysinger Tested-by: Yunlian Jiang Commit-Queue: Yunlian Jiang --- metrics/metrics_library_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 165864530..0a0768b21 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -151,7 +151,7 @@ void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) { ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) .WillOnce(SetMetricsPolicy(to_value)); - ASSERT_LT(abs(time(NULL) - lib_.cached_enabled_time_), 5); + ASSERT_LT(abs(static_cast(time(NULL) - lib_.cached_enabled_time_)), 5); // Sleep one second (or cheat to be faster). --lib_.cached_enabled_time_; ASSERT_EQ(to_value, lib_.AreMetricsEnabled()); From ba0c65d09832bd6d17936a9cfd9005d767615b72 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 17 Mar 2014 12:28:38 -0700 Subject: [PATCH 117/200] metrics: add per-version daily stats reporting Adds a few kernel crash stats which are reported daily but are accumulated from beginning to end of a Chrome OS version. BUG=chromium:339588 TEST=ran and checked histograms on device BRANCH=none Change-Id: I630c673156c28dc90ffe0c9c2df58caaada082dc Reviewed-on: https://chromium-review.googlesource.com/190404 Reviewed-by: Luigi Semenzato Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_daemon.cc | 112 ++++++++++++++++++++++++++++++++++---- metrics/metrics_daemon.h | 27 +++++++-- 2 files changed, 124 insertions(+), 15 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 883c132b8..b9d8cf0c4 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -106,6 +106,9 @@ const char MetricsDaemon::kMetricSwapOutLongName[] = const char MetricsDaemon::kMetricSwapOutShortName[] = "Platform.SwapOutShort"; +const char MetricsDaemon::kMetricsProcStatFileName[] = "/proc/stat"; +const int MetricsDaemon::kMetricsProcStatFirstLineItemsCount = 11; + // Thermal CPU throttling. const char MetricsDaemon::kMetricScaledCpuFrequencyName[] = @@ -147,7 +150,9 @@ MetricsDaemon::MetricsDaemon() write_sectors_(0), vmstats_(), stats_state_(kStatsShort), - stats_initial_time_(0) {} + stats_initial_time_(0), + ticks_per_second_(0), + latest_cpu_use_ticks_(0) {} MetricsDaemon::~MetricsDaemon() { } @@ -181,8 +186,8 @@ void MetricsDaemon::Run(bool run_as_daemon) { int32 version = GetOsVersionHash(); if (version_cycle_->Get() != version) { version_cycle_->Set(version); - SendKernelCrashesCumulativeCountSample(); kernel_crashes_version_count_->Set(0); + version_cumulative_cpu_use_->Set(0); } Loop(); @@ -215,8 +220,14 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; + // Get ticks per second (HZ) on this system. + // Sysconf cannot fail, so no sanity checks are needed. + ticks_per_second_ = sysconf(_SC_CLK_TCK); + daily_use_.reset( new PersistentInteger("Logging.DailyUseTime")); + version_cumulative_cpu_use_.reset( + new PersistentInteger("Logging.CumulativeCpuTime")); kernel_crash_interval_.reset( new PersistentInteger("Logging.KernelCrashInterval")); @@ -398,7 +409,7 @@ MetricsDaemon::LookupSessionState(const char* state_name) { return kUnknownSessionState; } -void MetricsDaemon::ReportStats(Time now) { +void MetricsDaemon::ReportStats(int64 active_use_seconds, Time now) { TimeDelta since_epoch = now - Time::UnixEpoch(); int day = since_epoch.InDays(); int week = day / 7; @@ -414,7 +425,7 @@ void MetricsDaemon::ReportStats(Time now) { SendCrashFrequencySample(user_crashes_daily_count_); SendCrashFrequencySample(kernel_crashes_daily_count_); SendCrashFrequencySample(unclean_shutdowns_daily_count_); - SendKernelCrashesCumulativeCountSample(); + SendKernelCrashesCumulativeCountStats(active_use_seconds); if (weekly_cycle_->Get() == week) { // We did this week already. @@ -429,6 +440,54 @@ void MetricsDaemon::ReportStats(Time now) { SendCrashFrequencySample(unclean_shutdowns_weekly_count_); } +// One might argue that parts of this should go into +// chromium/src/base/sys_info_chromeos.c instead, but put it here for now. + +TimeDelta MetricsDaemon::GetIncrementalCpuUse() { + + FilePath proc_stat_path = FilePath(kMetricsProcStatFileName); + std::string proc_stat_string; + if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) { + LOG(WARNING) << "cannot open " << kMetricsProcStatFileName; + return TimeDelta(); + } + + std::vector proc_stat_lines; + base::SplitString(proc_stat_string, '\n', &proc_stat_lines); + if (proc_stat_lines.empty()) { + LOG(WARNING) << "cannot parse " << kMetricsProcStatFileName + << ": " << proc_stat_string; + return TimeDelta(); + } + std::vector proc_stat_totals; + base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals); + + uint64 user_ticks, user_nice_ticks, system_ticks; + if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount || + proc_stat_totals[0] != "cpu" || + !base::StringToUint64(proc_stat_totals[1], &user_ticks) || + !base::StringToUint64(proc_stat_totals[2], &user_nice_ticks) || + !base::StringToUint64(proc_stat_totals[3], &system_ticks)) { + LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0]; + return TimeDelta(base::TimeDelta::FromSeconds(0)); + } + + uint64 total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks; + + // Sanity check. + if (total_cpu_use_ticks < latest_cpu_use_ticks_) { + LOG(WARNING) << "CPU time decreasing from " << latest_cpu_use_ticks_ + << " to " << total_cpu_use_ticks; + return TimeDelta(); + } + + uint64 diff = total_cpu_use_ticks - latest_cpu_use_ticks_; + latest_cpu_use_ticks_ = total_cpu_use_ticks; + // Use microseconds to avoid significant truncations. + return base::TimeDelta::FromMicroseconds( + diff * 1000 * 1000 / ticks_per_second_); +} + void MetricsDaemon::SetUserActiveState(bool active, Time now) { DLOG(INFO) << "user: " << (active ? "active" : "inactive"); @@ -448,8 +507,11 @@ void MetricsDaemon::SetUserActiveState(bool active, Time now) { user_crash_interval_->Add(seconds); kernel_crash_interval_->Add(seconds); + // Updates the CPU time accumulator. + version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds()); + // Report daily and weekly stats as needed. - ReportStats(now); + ReportStats(daily_use_->Get(), now); // Schedules a use monitor on inactive->active transitions and // unschedules it on active->inactive transitions. @@ -1084,14 +1146,44 @@ void MetricsDaemon::SendSample(const string& name, int sample, metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); } -void MetricsDaemon::SendKernelCrashesCumulativeCountSample() { +void MetricsDaemon::SendKernelCrashesCumulativeCountStats( + int64 active_use_seconds) { // Report the number of crashes for this OS version, but don't clear the // counter. It is cleared elsewhere on version change. + int64 crashes_count = kernel_crashes_version_count_->Get(); SendSample(kernel_crashes_version_count_->Name(), - kernel_crashes_version_count_->Get(), - 1, // value of first bucket - 500, // value of last bucket - 100); // number of buckets + crashes_count, + 1, // value of first bucket + 500, // value of last bucket + 100); // number of buckets + + + int64 cpu_use_ms = version_cumulative_cpu_use_->Get(); + SendSample(version_cumulative_cpu_use_->Name(), + cpu_use_ms / 1000, // stat is in seconds + 1, // device may be used very little... + 8 * 1000 * 1000, // ... or a lot (a little over 90 days) + 100); + + // On the first run after an autoupdate, cpu_use_ms and active_use_seconds + // can be zero. Avoid division by zero. + if (cpu_use_ms > 0) { + // Send the crash frequency since update in number of crashes per CPU year. + SendSample("Logging.KernelCrashesPerCpuYear", + crashes_count * kSecondsPerDay * 365 * 1000 / cpu_use_ms, + 1, + 1000 * 1000, // about one crash every 30s of CPU time + 100); + } + + if (active_use_seconds > 0) { + // Same as above, but per year of active time. + SendSample("Logging.KernelCrashesPerActiveYear", + crashes_count * kSecondsPerDay * 365 / active_use_seconds, + 1, + 1000 * 1000, // about one crash every 30s of active time + 100); + } } void MetricsDaemon::SendCrashIntervalSample( diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index e4bf853ac..2805cb758 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -145,6 +145,8 @@ class MetricsDaemon { static const int kMetricPageFaultsBuckets; static const char kMetricsDiskStatsPath[]; static const char kMetricsVmStatsPath[]; + static const char kMetricsProcStatFileName[]; + static const int kMetricsProcStatFirstLineItemsCount; // Array of power states. static const char* kPowerStates_[kNumberPowerStates]; @@ -243,9 +245,13 @@ class MetricsDaemon { void SendLinearSample(const std::string& name, int sample, int max, int nbuckets); - // Sends a histogram sample with the total number of kernel crashes since the - // last version update. - void SendKernelCrashesCumulativeCountSample(); + // Sends various cumulative kernel crash-related stats, for instance the + // total number of kernel crashes since the last version update. + void SendKernelCrashesCumulativeCountStats(int64 active_time_seconds); + + // Returns the total (system-wide) CPU usage between the time of the most + // recent call to this function and now. + base::TimeDelta GetIncrementalCpuUse(); // Sends a sample representing a time interval between two crashes of the // same type. @@ -322,7 +328,7 @@ class MetricsDaemon { bool ReadFreqToInt(const std::string& sysfs_file_name, int* value); // Report UMA stats when cycles (daily or weekly) have changed. - void ReportStats(base::Time now); + void ReportStats(int64 active_time_seconds, base::Time now); // Reads the current OS version from /etc/lsb-release and hashes it // to a unsigned 32-bit int. @@ -375,13 +381,24 @@ class MetricsDaemon { StatsState stats_state_; double stats_initial_time_; - // Persistent counters for crash statistics. + // The system "HZ", or frequency of ticks. Some system data uses ticks as a + // unit, and this is used to convert to standard time units. + uint32 ticks_per_second_; + // Used internally by GetIncrementalCpuUse() to return the CPU utilization + // between calls. + uint64 latest_cpu_use_ticks_; + + // Persistent values and accumulators for crash statistics. scoped_ptr daily_cycle_; scoped_ptr weekly_cycle_; scoped_ptr version_cycle_; scoped_ptr daily_use_; + // The CPU time accumulator. This contains the CPU time, in milliseconds, + // used by the system since the most recent OS version update. + scoped_ptr version_cumulative_cpu_use_; + scoped_ptr user_crash_interval_; scoped_ptr kernel_crash_interval_; scoped_ptr unclean_shutdown_interval_; From 6c320063efc4b8d2b4ee198b9fd7821a3fde43f3 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 14 Mar 2014 14:17:52 -0700 Subject: [PATCH 118/200] metrics: add PersistentInteger unit test. BUG=chromium:339588 TEST=P2_TEST_FILTER="metrics::*" FEATURES=test emerge-panther -v platform2 BRANCH=none Change-Id: I78bdd8eed2da69ae6284d0f7770918d1be9f0e9f Reviewed-on: https://chromium-review.googlesource.com/190121 Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/metrics.gyp | 9 ++++ metrics/persistent_integer_test.cc | 67 ++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 metrics/persistent_integer_test.cc diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 2906a64c6..834e52fb8 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -62,6 +62,15 @@ }], ['USE_test == 1', { 'targets': [ + { + 'target_name': 'persistent_integer_test', + 'type': 'executable', + 'includes': ['../common-mk/common_test.gypi'], + 'sources': [ + 'persistent_integer.cc', + 'persistent_integer_test.cc', + ] + }, { 'target_name': 'metrics_library_test', 'type': 'executable', diff --git a/metrics/persistent_integer_test.cc b/metrics/persistent_integer_test.cc new file mode 100644 index 000000000..ec3a6e414 --- /dev/null +++ b/metrics/persistent_integer_test.cc @@ -0,0 +1,67 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include +#include +#include + +#include "metrics/persistent_integer.h" + +const char kBackingFileName[] = "1.pibakf"; +const char kBackingFilePattern[] = "*.pibakf"; + +using chromeos_metrics::PersistentInteger; + +class PersistentIntegerTest : public testing::Test { + + virtual void SetUp() OVERRIDE { + // Set testing mode. + chromeos_metrics::PersistentInteger::SetTestingMode(true); + } + + virtual void TearDown() OVERRIDE { + // Remove backing files. The convention is that they all end in ".pibakf". + base::FileEnumerator f_enum(base::FilePath("."), + false, + base::FileEnumerator::FILES, + FILE_PATH_LITERAL(kBackingFilePattern)); + for (base::FilePath name = f_enum.Next(); + !name.empty(); + name = f_enum.Next()) { + base::DeleteFile(name, false); + } + } +}; + +TEST_F(PersistentIntegerTest, BasicChecks) { + scoped_ptr pi(new PersistentInteger(kBackingFileName)); + + // Test initialization. + EXPECT_EQ(0, pi->Get()); + EXPECT_EQ(kBackingFileName, pi->Name()); // boring + + // Test set and add. + pi->Set(2); + pi->Add(3); + EXPECT_EQ(5, pi->Get()); + + // Test persistence. + pi.reset(new PersistentInteger(kBackingFileName)); + EXPECT_EQ(5, pi->Get()); + + // Test GetAndClear. + EXPECT_EQ(5, pi->GetAndClear()); + EXPECT_EQ(pi->Get(), 0); + + // Another persistence test. + pi.reset(new PersistentInteger(kBackingFileName)); + EXPECT_EQ(0, pi->Get()); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 4f05c0e9ee3db4e5287696582518c5320b9a1d1d Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 27 Mar 2014 18:18:16 -0700 Subject: [PATCH 119/200] Add OWNERS for metrics. It looks like all the other people who made significant contributions to this project have left the team. BUG=none TEST=none Change-Id: Ie572a43e85cee565c71c4101d1474e77e51449e5 Reviewed-on: https://chromium-review.googlesource.com/192008 Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/OWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 metrics/OWNERS diff --git a/metrics/OWNERS b/metrics/OWNERS new file mode 100644 index 000000000..a680f7ca9 --- /dev/null +++ b/metrics/OWNERS @@ -0,0 +1,2 @@ +semenzato@chromium.org +derat@chromium.org From d8abf55c1b0807622d546b6d73f1b781c9dc8151 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 27 Mar 2014 14:19:06 -0700 Subject: [PATCH 120/200] metrics client: add basic sanity check for numeric arguments "atoi" and "atod" don't do any conversions or error checks, they just return 0 on error. They should be illegal. BUG=chromium:356932 TEST=checked various valid and invalid inputs Change-Id: I53a92ccab5f44e18e8ac27154c2b2826d355a6c0 Reviewed-on: https://chromium-review.googlesource.com/191971 Reviewed-by: Darren Krahn Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/metrics_client.cc | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index cdf09a2ca..03fb924cf 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -41,6 +41,26 @@ void ShowUsage() { exit(1); } +static int ParseInt(const char *arg) { + char *endptr; + int value = strtol(arg, &endptr, 0); + if (*endptr != '\0') { + fprintf(stderr, "metrics client: bad integer \"%s\"\n", arg); + ShowUsage(); + } + return value; +} + +static double ParseDouble(const char *arg) { + char *endptr; + double value = strtod(arg, &endptr); + if (*endptr != '\0') { + fprintf(stderr, "metrics client: bad double \"%s\"\n", arg); + ShowUsage(); + } + return value; +} + static int SendStats(char* argv[], int name_index, enum Mode mode, @@ -50,9 +70,9 @@ static int SendStats(char* argv[], const char* name = argv[name_index]; int sample; if (secs_to_msecs) { - sample = static_cast(atof(argv[name_index + 1]) * 1000.0); + sample = static_cast(ParseDouble(argv[name_index + 1]) * 1000.0); } else { - sample = atoi(argv[name_index + 1]); + sample = ParseInt(argv[name_index + 1]); } // Send metrics @@ -66,12 +86,12 @@ static int SendStats(char* argv[], if (mode == kModeSendSparseSample) { metrics_lib.SendSparseToUMA(name, sample); } else if (mode == kModeSendEnumSample) { - int max = atoi(argv[name_index + 2]); + int max = ParseInt(argv[name_index + 2]); metrics_lib.SendEnumToUMA(name, sample, max); } else { - int min = atoi(argv[name_index + 2]); - int max = atoi(argv[name_index + 3]); - int nbuckets = atoi(argv[name_index + 4]); + int min = ParseInt(argv[name_index + 2]); + int max = ParseInt(argv[name_index + 3]); + int nbuckets = ParseInt(argv[name_index + 4]); metrics_lib.SendToUMA(name, sample, min, max, nbuckets); } } From a5f4fe6b98ae5d214ecbe6a946d77240b9446409 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 15 Apr 2014 09:31:47 -0700 Subject: [PATCH 121/200] metrics_daemon: fix harmless confusion in writing persistent integers As the subject says, I don't think this can change any of the behavior, but the argument to Write() was ignored and merely confusing. BUG=chromium:339588 TEST=ran unit tests Change-Id: Ie6d59e6879a734523978c21d66170721e0f499c7 Reviewed-on: https://chromium-review.googlesource.com/194919 Tested-by: Luigi Semenzato Reviewed-by: Daniel Erat Reviewed-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/persistent_integer.cc | 6 +++--- metrics/persistent_integer.h | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/metrics/persistent_integer.cc b/metrics/persistent_integer.cc index c6ccf0ec7..658c9d391 100644 --- a/metrics/persistent_integer.cc +++ b/metrics/persistent_integer.cc @@ -35,13 +35,13 @@ PersistentInteger::~PersistentInteger() {} void PersistentInteger::Set(int64 value) { value_ = value; - Write(value); + Write(); } int64 PersistentInteger::Get() { // If not synced, then read. If the read fails, it's a good idea to write. if (!synced_ && !Read()) - Write(value_); + Write(); return value_; } @@ -55,7 +55,7 @@ void PersistentInteger::Add(int64 x) { Set(Get() + x); } -void PersistentInteger::Write(int64 value) { +void PersistentInteger::Write() { int fd = HANDLE_EINTR(open(backing_file_name_.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH)); diff --git a/metrics/persistent_integer.h b/metrics/persistent_integer.h index 7d920b5c7..5b159c7f9 100644 --- a/metrics/persistent_integer.h +++ b/metrics/persistent_integer.h @@ -45,11 +45,12 @@ class PersistentInteger { private: static const int kVersion = 1001; - // Writes |value| to the backing file, creating it if necessary. - void Write(int64 value); + // Writes |value_| to the backing file, creating it if necessary. + void Write(); // Reads the value from the backing file, stores it in |value_|, and returns - // true if the backing file is valid. Returns false otherwise. + // true if the backing file is valid. Returns false otherwise, and creates + // a valid backing file as a side effect. bool Read(); int64 value_; From 5ef2e3911957dada3c9e5e8d42d90d0dd56d1a29 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 15 Apr 2014 15:15:02 -0700 Subject: [PATCH 122/200] metrics_daemon: report cumulative daily use. This had been lost in the previous changes. Also, the meaning of DailyUse changes: it is now cumulative daily use. But we should revise these stats so I think it's OK for now. BUG=chromium:339588 TEST=unit tests Change-Id: I4894fef0ffb237633abcf949c01dd7ea5f5e5d6e Reviewed-on: https://chromium-review.googlesource.com/195005 Reviewed-by: Daniel Erat Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/metrics_daemon.cc | 19 +++++++++---------- metrics/metrics_daemon.h | 2 +- metrics/metrics_daemon_test.cc | 8 ++++---- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index b9d8cf0c4..1f20699ac 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -421,6 +421,7 @@ void MetricsDaemon::ReportStats(int64 active_use_seconds, Time now) { daily_cycle_->Set(day); // Daily stats. + ReportDailyUse(active_use_seconds); SendCrashFrequencySample(any_crashes_daily_count_); SendCrashFrequencySample(user_crashes_daily_count_); SendCrashFrequencySample(kernel_crashes_daily_count_); @@ -1125,18 +1126,16 @@ bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) { return true; } -// static -void MetricsDaemon::ReportDailyUse(void* handle, int count) { - if (count <= 0) +void MetricsDaemon::ReportDailyUse(int use_seconds) { + if (use_seconds <= 0) return; - MetricsDaemon* daemon = static_cast(handle); - int minutes = (count + kSecondsPerMinute / 2) / kSecondsPerMinute; - daemon->SendSample("Logging.DailyUseTime", - minutes, - 1, - kMinutesPerDay, - 50); + int minutes = (use_seconds + kSecondsPerMinute / 2) / kSecondsPerMinute; + SendSample("Logging.DailyUseTime", + minutes, + 1, + kMinutesPerDay * 30 * 2, // cumulative---two months worth + 50); } void MetricsDaemon::SendSample(const string& name, int sample, diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 2805cb758..de9fd740a 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -231,7 +231,7 @@ class MetricsDaemon { void UnscheduleUseMonitor(); // Report daily use through UMA. - static void ReportDailyUse(void* handle, int count); + void ReportDailyUse(int use_seconds); // Sends a regular (exponential) histogram sample to Chrome for // transport to UMA. See MetricsLibrary::SendToUMA in diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index aea0f4bd6..e83ad4d75 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -230,14 +230,14 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) { TEST_F(MetricsDaemonTest, ReportDailyUse) { ExpectDailyUseTimeSample(/* sample */ 2); - MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 90); + daemon_.ReportDailyUse(/* count */ 90); ExpectDailyUseTimeSample(/* sample */ 1); - MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 89); + daemon_.ReportDailyUse(/* count */ 89); // There should be no metrics generated for the calls below. - MetricsDaemon::ReportDailyUse(&daemon_, /* count */ 0); - MetricsDaemon::ReportDailyUse(&daemon_, /* count */ -5); + daemon_.ReportDailyUse(/* count */ 0); + daemon_.ReportDailyUse(/* count */ -5); } TEST_F(MetricsDaemonTest, LookupPowerState) { From e5c7eb1d1399f3c6c7fb566aab0ff3de47471ce8 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Wed, 16 Apr 2014 17:05:05 -0700 Subject: [PATCH 123/200] metrics_daemon: store persistent counters in /var/lib, not /var/log. BUG=chromium:356781 TEST=manual Change-Id: I7d36876347bdbbe329e24adf3345c2ade97be5fe Reviewed-on: https://chromium-review.googlesource.com/195310 Reviewed-by: Luigi Semenzato Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/init/metrics_daemon.conf | 5 +++++ metrics/persistent_integer.cc | 8 ++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/metrics/init/metrics_daemon.conf b/metrics/init/metrics_daemon.conf index 68edff4ad..9b1e2a52a 100644 --- a/metrics/init/metrics_daemon.conf +++ b/metrics/init/metrics_daemon.conf @@ -11,5 +11,10 @@ start on starting system-services stop on stopping system-services respawn +pre-start script + # Make the directory, but don't die on error. Let someone else complain. + mkdir -p /var/lib/metrics || : +end script + expect fork exec metrics_daemon diff --git a/metrics/persistent_integer.cc b/metrics/persistent_integer.cc index 658c9d391..d4ef8b216 100644 --- a/metrics/persistent_integer.cc +++ b/metrics/persistent_integer.cc @@ -11,10 +11,14 @@ #include "metrics_library.h" -namespace chromeos_metrics { +namespace { // The directory for the persistent storage. -const char* const kBackingFilesDirectory = "/var/log/metrics/"; +const char kBackingFilesDirectory[] = "/var/lib/metrics/"; + +} + +namespace chromeos_metrics { // Static class member instantiation. bool PersistentInteger::testing_ = false; From c83975ab1e1569ec023f166971fc76b110618d01 Mon Sep 17 00:00:00 2001 From: Daniel Erat Date: Fri, 4 Apr 2014 08:53:44 -0700 Subject: [PATCH 124/200] metrics: Clean up user-active code. Stop listening for the soon-to-deleted PowerStateChanged D-Bus signal from powerd; the code that was listening for it had some issues (there was no guarantee that it'd run before the system suspended, and resumes weren't handled at all). Also remove the session-state- and screen-lock-handling code; it provided an inaccurate view of user activity (what if the user is listening to music with the screen locked or if they don't have screen-locking enabled?). Track uptime instead via a timer that fires every five minutes. BUG=chromium:359619 TEST=manual: ran metrics_daemon --nodaemon and watched the logs Change-Id: I4ad74773daefa01afdea080d20001ff1944c2eee Reviewed-on: https://chromium-review.googlesource.com/195491 Reviewed-by: Daniel Erat Commit-Queue: Daniel Erat Tested-by: Daniel Erat --- metrics/metrics_daemon.cc | 317 ++++++++------------------------- metrics/metrics_daemon.h | 99 ++-------- metrics/metrics_daemon_test.cc | 212 ++-------------------- metrics/power_states.h | 17 -- metrics/session_states.h | 17 -- 5 files changed, 93 insertions(+), 569 deletions(-) delete mode 100644 metrics/power_states.h delete mode 100644 metrics/session_states.h diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 1f20699ac..47d74fa98 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -32,33 +32,30 @@ using std::map; using std::string; using std::vector; +namespace { #define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error") -static const char kCrashReporterInterface[] = "org.chromium.CrashReporter"; -static const char kCrashReporterUserCrashSignal[] = "UserCrash"; +const char kCrashReporterInterface[] = "org.chromium.CrashReporter"; +const char kCrashReporterUserCrashSignal[] = "UserCrash"; -static const int kSecondsPerMinute = 60; -static const int kMinutesPerHour = 60; -static const int kHoursPerDay = 24; -static const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour; -static const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay; -static const int kDaysPerWeek = 7; -static const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek; +const int kSecondsPerMinute = 60; +const int kMinutesPerHour = 60; +const int kHoursPerDay = 24; +const int kMinutesPerDay = kHoursPerDay * kMinutesPerHour; +const int kSecondsPerDay = kSecondsPerMinute * kMinutesPerDay; +const int kDaysPerWeek = 7; +const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek; -// The daily use monitor is scheduled to a 1-minute interval after -// initial user activity and then it's exponentially backed off to -// 10-minute intervals. Although not required, the back off is -// implemented because the histogram buckets are spaced exponentially -// anyway and to avoid too frequent metrics daemon process wake-ups -// and file I/O. -static const int kUseMonitorIntervalInit = 1 * kSecondsPerMinute; -static const int kUseMonitorIntervalMax = 10 * kSecondsPerMinute; +// Interval between calls to UpdateStats(). +const guint kUpdateStatsIntervalMs = 300000; const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected"; -static const char kUncleanShutdownDetectedFile[] = +const char kUncleanShutdownDetectedFile[] = "/var/run/unclean-shutdown-detected"; +} // namespace + // disk stats metrics // The {Read,Write}Sectors numbers are in sectors/second. @@ -114,18 +111,6 @@ const int MetricsDaemon::kMetricsProcStatFirstLineItemsCount = 11; const char MetricsDaemon::kMetricScaledCpuFrequencyName[] = "Platform.CpuFrequencyThermalScaling"; -// static -const char* MetricsDaemon::kPowerStates_[] = { -#define STATE(name, capname) #name, -#include "power_states.h" -}; - -// static -const char* MetricsDaemon::kSessionStates_[] = { -#define STATE(name, capname) #name, -#include "session_states.h" -}; - // Memory use stats collection intervals. We collect some memory use interval // at these intervals after boot, and we stop collecting after the last one, // with the assumption that in most cases the memory use won't change much @@ -139,11 +124,7 @@ static const int kMemuseIntervals[] = { }; MetricsDaemon::MetricsDaemon() - : power_state_(kUnknownPowerState), - session_state_(kUnknownSessionState), - user_active_(false), - usemon_interval_(0), - usemon_source_(NULL), + : update_stats_timeout_id_(-1), memuse_final_time_(0), memuse_interval_index_(0), read_sectors_(0), @@ -155,6 +136,8 @@ MetricsDaemon::MetricsDaemon() latest_cpu_use_ticks_(0) {} MetricsDaemon::~MetricsDaemon() { + if (update_stats_timeout_id_ > -1) + g_source_remove(update_stats_timeout_id_); } double MetricsDaemon::GetActiveTime() { @@ -214,8 +197,7 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, const string& diskstats_path, const string& vmstats_path, const string& scaling_max_freq_path, - const string& cpuinfo_max_freq_path - ) { + const string& cpuinfo_max_freq_path) { testing_ = testing; DCHECK(metrics_lib != NULL); metrics_lib_ = metrics_lib; @@ -291,16 +273,6 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, base::StringPrintf("type='signal',interface='%s',path='/',member='%s'", kCrashReporterInterface, kCrashReporterUserCrashSignal)); - matches.push_back( - base::StringPrintf("type='signal',interface='%s',path='%s',member='%s'", - power_manager::kPowerManagerInterface, - power_manager::kPowerManagerServicePath, - power_manager::kPowerStateChangedSignal)); - matches.push_back( - base::StringPrintf("type='signal',sender='%s',interface='%s',path='%s'", - login_manager::kSessionManagerServiceName, - login_manager::kSessionManagerInterface, - login_manager::kSessionManagerServicePath)); // Registers D-Bus matches for the signals we would like to catch. for (vector::const_iterator it = matches.begin(); @@ -316,6 +288,9 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, // the registered D-Bus matches is successful. The daemon is not // activated for D-Bus messages that don't match. CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL)); + + update_stats_timeout_id_ = + g_timeout_add(kUpdateStatsIntervalMs, &HandleUpdateStatsTimeout, this); } void MetricsDaemon::Loop() { @@ -327,9 +302,6 @@ void MetricsDaemon::Loop() { DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, DBusMessage* message, void* user_data) { - Time now = Time::Now(); - DLOG(INFO) << "message intercepted @ " << now.ToInternalValue(); - int message_type = dbus_message_get_type(message); if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) { DLOG(WARNING) << "unexpected message type " << message_type; @@ -337,115 +309,29 @@ DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, } // Signal messages always have interfaces. - const char* interface = dbus_message_get_interface(message); - CHECK(interface != NULL); + const std::string interface(dbus_message_get_interface(message)); + const std::string member(dbus_message_get_member(message)); + DLOG(INFO) << "Got " << interface << "." << member << " D-Bus signal"; MetricsDaemon* daemon = static_cast(user_data); DBusMessageIter iter; dbus_message_iter_init(message, &iter); - if (strcmp(interface, kCrashReporterInterface) == 0) { - CHECK(strcmp(dbus_message_get_member(message), - kCrashReporterUserCrashSignal) == 0); + if (interface == kCrashReporterInterface) { + CHECK_EQ(member, kCrashReporterUserCrashSignal); daemon->ProcessUserCrash(); - } else if (strcmp(interface, power_manager::kPowerManagerInterface) == 0) { - CHECK(strcmp(dbus_message_get_member(message), - power_manager::kPowerStateChangedSignal) == 0); - char* state_name; - dbus_message_iter_get_basic(&iter, &state_name); - daemon->PowerStateChanged(state_name, now); - } else if (strcmp(interface, login_manager::kSessionManagerInterface) == 0) { - const char* member = dbus_message_get_member(message); - if (strcmp(member, login_manager::kScreenIsLockedSignal) == 0) { - daemon->SetUserActiveState(false, now); - } else if (strcmp(member, login_manager::kScreenIsUnlockedSignal) == 0) { - daemon->SetUserActiveState(true, now); - } else if (strcmp(member, login_manager::kSessionStateChangedSignal) == 0) { - char* state_name; - dbus_message_iter_get_basic(&iter, &state_name); - daemon->SessionStateChanged(state_name, now); - } } else { - DLOG(WARNING) << "unexpected interface: " << interface; + // Ignore messages from the bus itself. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } return DBUS_HANDLER_RESULT_HANDLED; } -void MetricsDaemon::PowerStateChanged(const char* state_name, Time now) { - DLOG(INFO) << "power state: " << state_name; - power_state_ = LookupPowerState(state_name); - - if (power_state_ != kPowerStateOn) - SetUserActiveState(false, now); -} - -MetricsDaemon::PowerState -MetricsDaemon::LookupPowerState(const char* state_name) { - for (int i = 0; i < kNumberPowerStates; i++) { - if (strcmp(state_name, kPowerStates_[i]) == 0) { - return static_cast(i); - } - } - DLOG(WARNING) << "unknown power state: " << state_name; - return kUnknownPowerState; -} - -void MetricsDaemon::SessionStateChanged(const char* state_name, Time now) { - DLOG(INFO) << "user session state: " << state_name; - session_state_ = LookupSessionState(state_name); - SetUserActiveState(session_state_ == kSessionStateStarted, now); -} - -MetricsDaemon::SessionState -MetricsDaemon::LookupSessionState(const char* state_name) { - for (int i = 0; i < kNumberSessionStates; i++) { - if (strcmp(state_name, kSessionStates_[i]) == 0) { - return static_cast(i); - } - } - DLOG(WARNING) << "unknown user session state: " << state_name; - return kUnknownSessionState; -} - -void MetricsDaemon::ReportStats(int64 active_use_seconds, Time now) { - TimeDelta since_epoch = now - Time::UnixEpoch(); - int day = since_epoch.InDays(); - int week = day / 7; - - if (daily_cycle_->Get() == day) { - // We did today already. - return; - } - daily_cycle_->Set(day); - - // Daily stats. - ReportDailyUse(active_use_seconds); - SendCrashFrequencySample(any_crashes_daily_count_); - SendCrashFrequencySample(user_crashes_daily_count_); - SendCrashFrequencySample(kernel_crashes_daily_count_); - SendCrashFrequencySample(unclean_shutdowns_daily_count_); - SendKernelCrashesCumulativeCountStats(active_use_seconds); - - if (weekly_cycle_->Get() == week) { - // We did this week already. - return; - } - weekly_cycle_->Set(week); - - // Weekly stats. - SendCrashFrequencySample(any_crashes_weekly_count_); - SendCrashFrequencySample(user_crashes_weekly_count_); - SendCrashFrequencySample(kernel_crashes_weekly_count_); - SendCrashFrequencySample(unclean_shutdowns_weekly_count_); -} - // One might argue that parts of this should go into // chromium/src/base/sys_info_chromeos.c instead, but put it here for now. TimeDelta MetricsDaemon::GetIncrementalCpuUse() { - FilePath proc_stat_path = FilePath(kMetricsProcStatFileName); std::string proc_stat_string; if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) { @@ -489,47 +375,9 @@ TimeDelta MetricsDaemon::GetIncrementalCpuUse() { diff * 1000 * 1000 / ticks_per_second_); } -void MetricsDaemon::SetUserActiveState(bool active, Time now) { - DLOG(INFO) << "user: " << (active ? "active" : "inactive"); - - // Calculates the seconds of active use since the last update and - // the day since Epoch, and logs the usage data. Guards against the - // time jumping back and forth due to the user changing it by - // discarding the new use time. - int seconds = 0; - if (user_active_ && now > user_active_last_) { - TimeDelta since_active = now - user_active_last_; - if (since_active < TimeDelta::FromSeconds( - kUseMonitorIntervalMax + kSecondsPerMinute)) { - seconds = static_cast(since_active.InSeconds()); - } - } - daily_use_->Add(seconds); - user_crash_interval_->Add(seconds); - kernel_crash_interval_->Add(seconds); - - // Updates the CPU time accumulator. - version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds()); - - // Report daily and weekly stats as needed. - ReportStats(daily_use_->Get(), now); - - // Schedules a use monitor on inactive->active transitions and - // unschedules it on active->inactive transitions. - if (!user_active_ && active) - ScheduleUseMonitor(kUseMonitorIntervalInit, /* backoff */ false); - else if (user_active_ && !active) - UnscheduleUseMonitor(); - - // Remembers the current active state and the time of the last - // activity update. - user_active_ = active; - user_active_last_ = now; -} - void MetricsDaemon::ProcessUserCrash() { - // Counts the active use time up to now. - SetUserActiveState(user_active_, Time::Now()); + // Counts the active time up to now. + UpdateStats(TimeTicks::Now(), Time::Now()); // Reports the active use time since the last crash and resets it. SendCrashIntervalSample(user_crash_interval_); @@ -541,8 +389,8 @@ void MetricsDaemon::ProcessUserCrash() { } void MetricsDaemon::ProcessKernelCrash() { - // Counts the active use time up to now. - SetUserActiveState(user_active_, Time::Now()); + // Counts the active time up to now. + UpdateStats(TimeTicks::Now(), Time::Now()); // Reports the active use time since the last crash and resets it. SendCrashIntervalSample(kernel_crash_interval_); @@ -556,8 +404,8 @@ void MetricsDaemon::ProcessKernelCrash() { } void MetricsDaemon::ProcessUncleanShutdown() { - // Counts the active use time up to now. - SetUserActiveState(user_active_, Time::Now()); + // Counts the active time up to now. + UpdateStats(TimeTicks::Now(), Time::Now()); // Reports the active use time since the last crash and resets it. SendCrashIntervalSample(unclean_shutdown_interval_); @@ -579,66 +427,6 @@ bool MetricsDaemon::CheckSystemCrash(const string& crash_file) { return true; } -// static -gboolean MetricsDaemon::UseMonitorStatic(gpointer data) { - return static_cast(data)->UseMonitor() ? TRUE : FALSE; -} - -bool MetricsDaemon::UseMonitor() { - SetUserActiveState(user_active_, Time::Now()); - - // If a new monitor source/instance is scheduled, returns false to - // tell GLib to destroy this monitor source/instance. Returns true - // otherwise to keep calling back this monitor. - return !ScheduleUseMonitor(usemon_interval_ * 2, /* backoff */ true); -} - -bool MetricsDaemon::ScheduleUseMonitor(int interval, bool backoff) -{ - if (testing_) - return false; - - // Caps the interval -- the bigger the interval, the more active use - // time will be potentially dropped on system shutdown. - if (interval > kUseMonitorIntervalMax) - interval = kUseMonitorIntervalMax; - - if (backoff) { - // Back-off mode is used by the use monitor to reschedule itself - // with exponential back-off in time. This mode doesn't create a - // new timeout source if the new interval is the same as the old - // one. Also, if a new timeout source is created, the old one is - // not destroyed explicitly here -- it will be destroyed by GLib - // when the monitor returns FALSE (see UseMonitor and - // UseMonitorStatic). - if (interval == usemon_interval_) - return false; - } else { - UnscheduleUseMonitor(); - } - - // Schedules a new use monitor for |interval| seconds from now. - DLOG(INFO) << "scheduling use monitor in " << interval << " seconds"; - usemon_source_ = g_timeout_source_new_seconds(interval); - g_source_set_callback(usemon_source_, UseMonitorStatic, this, - NULL); // No destroy notification. - g_source_attach(usemon_source_, - NULL); // Default context. - usemon_interval_ = interval; - return true; -} - -void MetricsDaemon::UnscheduleUseMonitor() { - // If there's a use monitor scheduled already, destroys it. - if (usemon_source_ == NULL) - return; - - DLOG(INFO) << "destroying use monitor"; - g_source_destroy(usemon_source_); - usemon_source_ = NULL; - usemon_interval_ = 0; -} - void MetricsDaemon::StatsReporterInit() { DiskStatsReadStats(&read_sectors_, &write_sectors_); VmStatsReadStats(&vmstats_); @@ -1212,3 +1000,40 @@ void MetricsDaemon::SendLinearSample(const string& name, int sample, LOG_IF(FATAL, nbuckets != max + 1) << "unsupported histogram scale"; metrics_lib_->SendEnumToUMA(name, sample, max); } + +void MetricsDaemon::UpdateStats(TimeTicks now_ticks, + Time now_wall_time) { + const int elapsed_seconds = (now_ticks - last_update_stats_time_).InSeconds(); + daily_use_->Add(elapsed_seconds); + user_crash_interval_->Add(elapsed_seconds); + kernel_crash_interval_->Add(elapsed_seconds); + version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds()); + last_update_stats_time_ = now_ticks; + + const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch(); + const int day = since_epoch.InDays(); + const int week = day / 7; + + if (daily_cycle_->Get() != day) { + daily_cycle_->Set(day); + SendCrashFrequencySample(any_crashes_daily_count_); + SendCrashFrequencySample(user_crashes_daily_count_); + SendCrashFrequencySample(kernel_crashes_daily_count_); + SendCrashFrequencySample(unclean_shutdowns_daily_count_); + SendKernelCrashesCumulativeCountStats(daily_use_->Get()); + } + + if (weekly_cycle_->Get() != week) { + weekly_cycle_->Set(week); + SendCrashFrequencySample(any_crashes_weekly_count_); + SendCrashFrequencySample(user_crashes_weekly_count_); + SendCrashFrequencySample(kernel_crashes_weekly_count_); + SendCrashFrequencySample(unclean_shutdowns_weekly_count_); + } +} + +// static +gboolean MetricsDaemon::HandleUpdateStatsTimeout(gpointer data) { + static_cast(data)->UpdateStats(TimeTicks::Now(), Time::Now()); + return TRUE; +} diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index de9fd740a..929706a2d 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -43,12 +43,8 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ComputeEpochNoLast); FRIEND_TEST(MetricsDaemonTest, GetHistogramPath); FRIEND_TEST(MetricsDaemonTest, IsNewEpoch); - FRIEND_TEST(MetricsDaemonTest, LookupPowerState); - FRIEND_TEST(MetricsDaemonTest, LookupScreenSaverState); - FRIEND_TEST(MetricsDaemonTest, LookupSessionState); FRIEND_TEST(MetricsDaemonTest, MessageFilter); FRIEND_TEST(MetricsDaemonTest, ParseVmStats); - FRIEND_TEST(MetricsDaemonTest, PowerStateChanged); FRIEND_TEST(MetricsDaemonTest, ProcessKernelCrash); FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo); FRIEND_TEST(MetricsDaemonTest, ProcessMeminfo2); @@ -61,28 +57,8 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval); FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval); FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval); - FRIEND_TEST(MetricsDaemonTest, ScreenSaverStateChanged); FRIEND_TEST(MetricsDaemonTest, SendSample); FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics); - FRIEND_TEST(MetricsDaemonTest, SessionStateChanged); - FRIEND_TEST(MetricsDaemonTest, SetUserActiveState); - FRIEND_TEST(MetricsDaemonTest, SetUserActiveStateTimeJump); - - // The power states (see power_states.h). - enum PowerState { - kUnknownPowerState = -1, // Initial/unknown power state. -#define STATE(name, capname) kPowerState ## capname, -#include "power_states.h" - kNumberPowerStates - }; - - // The user session states (see session_states.h). - enum SessionState { - kUnknownSessionState = -1, // Initial/unknown user session state. -#define STATE(name, capname) kSessionState ## capname, -#include "session_states.h" - kNumberSessionStates - }; // State for disk stats collector callback. enum StatsState { @@ -148,12 +124,6 @@ class MetricsDaemon { static const char kMetricsProcStatFileName[]; static const int kMetricsProcStatFirstLineItemsCount; - // Array of power states. - static const char* kPowerStates_[kNumberPowerStates]; - - // Array of user session states. - static const char* kSessionStates_[kNumberSessionStates]; - // Returns the active time since boot (uptime minus sleep time) in seconds. double GetActiveTime(); @@ -165,27 +135,6 @@ class MetricsDaemon { DBusMessage* message, void* user_data); - // Processes power state change. - void PowerStateChanged(const char* state_name, base::Time now); - - // Given the state name, returns the state id. - PowerState LookupPowerState(const char* state_name); - - // Processes user session state change. - void SessionStateChanged(const char* state_name, base::Time now); - - // Given the state name, returns the state id. - SessionState LookupSessionState(const char* state_name); - - // Updates the user-active state to |active| and logs the usage data - // since the last update. If the user has just become active, - // reschedule the daily use monitor for more frequent updates -- - // this is followed by an exponential back-off (see UseMonitor). - // While in active use, this method should be called at intervals no - // longer than kUseMonitorIntervalMax otherwise new use time will be - // discarded. - void SetUserActiveState(bool active, base::Time now); - // Updates the daily usage file, if necessary, by adding |seconds| // of active use to the |day| since Epoch. If there's usage data for // day in the past in the usage file, that data is sent to UMA and @@ -209,27 +158,6 @@ class MetricsDaemon { // exists, so it must not be called more than once. bool CheckSystemCrash(const std::string& crash_file); - // Callbacks for the daily use monitor. The daily use monitor uses - // LogDailyUseRecord to aggregate current usage data and send it to - // UMA, if necessary. It also reschedules itself using an - // exponentially bigger interval (up to a certain maximum) -- so - // usage is monitored less frequently with longer active use. - static gboolean UseMonitorStatic(gpointer data); - bool UseMonitor(); - - // Schedules or reschedules a daily use monitor for |interval| - // seconds from now. |backoff| mode is used by the use monitor to - // reschedule itself. If there's a monitor scheduled already and - // |backoff| is false, unschedules it first. Doesn't schedule a - // monitor for more than kUseMonitorIntervalMax seconds in the - // future (see metrics_daemon.cc). Returns true if a new use monitor - // was scheduled, false otherwise (note that if |backoff| is false a - // new use monitor will always be scheduled). - bool ScheduleUseMonitor(int interval, bool backoff); - - // Unschedules a scheduled use monitor, if any. - void UnscheduleUseMonitor(); - // Report daily use through UMA. void ReportDailyUse(int use_seconds); @@ -327,13 +255,17 @@ class MetricsDaemon { // Reads an integer CPU frequency value from sysfs. bool ReadFreqToInt(const std::string& sysfs_file_name, int* value); - // Report UMA stats when cycles (daily or weekly) have changed. - void ReportStats(int64 active_time_seconds, base::Time now); - // Reads the current OS version from /etc/lsb-release and hashes it // to a unsigned 32-bit int. uint32 GetOsVersionHash(); + // Updates stats, additionally sending them to UMA if enough time has elapsed + // since the last report. + void UpdateStats(base::TimeTicks now_ticks, base::Time now_wall_time); + + // Invoked periodically by |update_stats_timeout_id_| to call UpdateStats(). + static gboolean HandleUpdateStatsTimeout(gpointer data); + // Test mode. bool testing_; @@ -345,20 +277,11 @@ class MetricsDaemon { // TimeTicks ensures a monotonically increasing TimeDelta. base::TimeTicks network_state_last_; - // Current power state. - PowerState power_state_; + // The last time that UpdateStats() was called. + base::TimeTicks last_update_stats_time_; - // Current user session state. - SessionState session_state_; - - // Is the user currently active: power is on, user session has - // started, screen is not locked. - bool user_active_; - - // Timestamps last user active update. Active use time is aggregated - // each day before sending to UMA so using time since the epoch as - // the timestamp. - base::Time user_active_last_; + // ID of a GLib timeout that repeatedly runs UpdateStats(). + gint update_stats_timeout_id_; // Sleep period until the next daily usage aggregation performed by // the daily use monitor (see ScheduleUseMonitor). diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index e83ad4d75..de4aab674 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -20,13 +20,14 @@ using base::FilePath; using base::StringPrintf; using base::Time; +using base::TimeDelta; using base::TimeTicks; using std::string; using std::vector; using ::testing::_; +using ::testing::AtLeast; using ::testing::Return; using ::testing::StrictMock; -using ::testing::AtLeast; using chromeos_metrics::PersistentIntegerMock; static const int kSecondsPerDay = 24 * 60 * 60; @@ -45,15 +46,6 @@ static const char kFakeVmStatsPath[] = "fake-vm-stats"; static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq"; static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq"; -// This class allows a TimeTicks object to be initialized with seconds -// (rather than microseconds) through the protected TimeTicks(int64) -// constructor. -class TestTicks : public TimeTicks { - public: - TestTicks(int64 seconds) - : TimeTicks(seconds * Time::kMicrosecondsPerSecond) {} -}; - class MetricsDaemonTest : public testing::Test { protected: virtual void SetUp() { @@ -71,11 +63,6 @@ class MetricsDaemonTest : public testing::Test { daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath, kFakeVmStatsPath, kFakeScalingMaxFreqPath, kFakeCpuinfoMaxFreqPath); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_TRUE(daemon_.user_active_last_.is_null()); - EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); - EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); - base::DeleteFile(FilePath(kTestDir), true); base::CreateDirectory(FilePath(kTestDir)); @@ -133,24 +120,13 @@ class MetricsDaemonTest : public testing::Test { // Adds a metrics library mock expectation that the specified metric // will be generated. - void ExpectSample(int sample) { - EXPECT_CALL(metrics_lib_, SendToUMA(_, sample, _, _, _)) + void ExpectSample(const std::string& name, int sample) { + EXPECT_CALL(metrics_lib_, SendToUMA(name, sample, _, _, _)) .Times(1) .WillOnce(Return(true)) .RetiresOnSaturation(); } - // Adds a metrics library mock expectation that the specified daily - // use time metric will be generated. - void ExpectDailyUseTimeSample(int sample) { - ExpectSample(sample); - } - - // Converts from seconds to a Time object. - Time TestTime(int64 seconds) { - return Time::FromInternalValue(seconds * Time::kMicrosecondsPerSecond); - } - // Creates a new DBus signal message with zero or more string arguments. // The message can be deallocated through DeleteDBusMessage. // @@ -229,33 +205,15 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) { } TEST_F(MetricsDaemonTest, ReportDailyUse) { - ExpectDailyUseTimeSample(/* sample */ 2); - daemon_.ReportDailyUse(/* count */ 90); + ExpectSample("Logging.DailyUseTime", 2); + daemon_.ReportDailyUse(90); - ExpectDailyUseTimeSample(/* sample */ 1); - daemon_.ReportDailyUse(/* count */ 89); + ExpectSample("Logging.DailyUseTime", 1); + daemon_.ReportDailyUse(89); // There should be no metrics generated for the calls below. - daemon_.ReportDailyUse(/* count */ 0); - daemon_.ReportDailyUse(/* count */ -5); -} - -TEST_F(MetricsDaemonTest, LookupPowerState) { - EXPECT_EQ(MetricsDaemon::kPowerStateOn, - daemon_.LookupPowerState("on")); - EXPECT_EQ(MetricsDaemon::kPowerStateMem, - daemon_.LookupPowerState("mem")); - EXPECT_EQ(MetricsDaemon::kUnknownPowerState, - daemon_.LookupPowerState("somestate")); -} - -TEST_F(MetricsDaemonTest, LookupSessionState) { - EXPECT_EQ(MetricsDaemon::kSessionStateStarted, - daemon_.LookupSessionState("started")); - EXPECT_EQ(MetricsDaemon::kSessionStateStopped, - daemon_.LookupSessionState("stopped")); - EXPECT_EQ(MetricsDaemon::kUnknownSessionState, - daemon_.LookupSessionState("somestate")); + daemon_.ReportDailyUse(0); + daemon_.ReportDailyUse(-5); } TEST_F(MetricsDaemonTest, MessageFilter) { @@ -278,44 +236,6 @@ TEST_F(MetricsDaemonTest, MessageFilter) { EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); - signal_args.clear(); - signal_args.push_back("on"); - msg = NewDBusSignalString(power_manager::kPowerManagerServicePath, - power_manager::kPowerManagerInterface, - power_manager::kPowerStateChangedSignal, - signal_args); - EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); - res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); - EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); - EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); - DeleteDBusMessage(msg); - - signal_args.clear(); - IgnoreActiveUseUpdate(); - msg = NewDBusSignalString(login_manager::kSessionManagerServicePath, - login_manager::kSessionManagerInterface, - login_manager::kScreenIsUnlockedSignal, - signal_args); - EXPECT_FALSE(daemon_.user_active_); - res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); - DeleteDBusMessage(msg); - - IgnoreActiveUseUpdate(); - signal_args.clear(); - signal_args.push_back("started"); - signal_args.push_back("bob"); // arbitrary username - msg = NewDBusSignalString(login_manager::kSessionManagerServicePath, - login_manager::kSessionManagerInterface, - login_manager::kSessionStateChangedSignal, - signal_args); - EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); - res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); - EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); - EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); - DeleteDBusMessage(msg); - signal_args.clear(); signal_args.push_back("randomstate"); signal_args.push_back("bob"); // arbitrary username @@ -328,122 +248,12 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DeleteDBusMessage(msg); } -TEST_F(MetricsDaemonTest, PowerStateChanged) { - // Ignore calls to SendToUMA. - EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); - - ExpectActiveUseUpdate(0); - daemon_.SetUserActiveState(/* active */ true, - TestTime(7 * kSecondsPerDay + 15)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(7 * kSecondsPerDay + 15), daemon_.user_active_last_); - - ExpectActiveUseUpdate(30); - daemon_.PowerStateChanged("mem", TestTime(7 * kSecondsPerDay + 45)); - EXPECT_EQ(MetricsDaemon::kPowerStateMem, daemon_.power_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_); - - daemon_.PowerStateChanged("on", TestTime(7 * kSecondsPerDay + 85)); - EXPECT_EQ(MetricsDaemon::kPowerStateOn, daemon_.power_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(7 * kSecondsPerDay + 45), daemon_.user_active_last_); - - ExpectActiveUseUpdate(0); - daemon_.PowerStateChanged("otherstate", TestTime(7 * kSecondsPerDay + 185)); - EXPECT_EQ(MetricsDaemon::kUnknownPowerState, daemon_.power_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(7 * kSecondsPerDay + 185), daemon_.user_active_last_); -} - TEST_F(MetricsDaemonTest, SendSample) { - ExpectSample(3); + ExpectSample("Dummy.Metric", 3); daemon_.SendSample("Dummy.Metric", /* sample */ 3, /* min */ 1, /* max */ 100, /* buckets */ 50); } -TEST_F(MetricsDaemonTest, SessionStateChanged) { - // Ignore calls to SendToUMA. - EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); - - ExpectActiveUseUpdate(0); - daemon_.SessionStateChanged("started", TestTime(15 * kSecondsPerDay + 20)); - EXPECT_EQ(MetricsDaemon::kSessionStateStarted, daemon_.session_state_); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(15 * kSecondsPerDay + 20), daemon_.user_active_last_); - - ExpectActiveUseUpdate(130); - daemon_.SessionStateChanged("stopped", TestTime(15 * kSecondsPerDay + 150)); - EXPECT_EQ(MetricsDaemon::kSessionStateStopped, daemon_.session_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(15 * kSecondsPerDay + 150), daemon_.user_active_last_); - - ExpectActiveUseUpdate(0); - daemon_.SessionStateChanged("otherstate", - TestTime(15 * kSecondsPerDay + 300)); - EXPECT_EQ(MetricsDaemon::kUnknownSessionState, daemon_.session_state_); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(15 * kSecondsPerDay + 300), daemon_.user_active_last_); -} - -TEST_F(MetricsDaemonTest, SetUserActiveState) { - // Ignore calls to SendToUMA. - EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); - - ExpectActiveUseUpdate(0); - daemon_.SetUserActiveState(/* active */ false, - TestTime(5 * kSecondsPerDay + 10)); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(5 * kSecondsPerDay + 10), daemon_.user_active_last_); - - ExpectActiveUseUpdate(0); - daemon_.SetUserActiveState(/* active */ true, - TestTime(6 * kSecondsPerDay + 20)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(6 * kSecondsPerDay + 20), daemon_.user_active_last_); - - ExpectActiveUseUpdate(100); - daemon_.SetUserActiveState(/* active */ true, - TestTime(6 * kSecondsPerDay + 120)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(6 * kSecondsPerDay + 120), daemon_.user_active_last_); - - ExpectActiveUseUpdate(110); - daemon_.SetUserActiveState(/* active */ false, - TestTime(6 * kSecondsPerDay + 230)); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(6 * kSecondsPerDay + 230), daemon_.user_active_last_); - - ExpectActiveUseUpdate(0); - daemon_.SetUserActiveState(/* active */ false, - TestTime(6 * kSecondsPerDay + 260)); - EXPECT_FALSE(daemon_.user_active_); - EXPECT_EQ(TestTime(6 * kSecondsPerDay + 260), daemon_.user_active_last_); -} - -TEST_F(MetricsDaemonTest, SetUserActiveStateTimeJump) { - // Ignore calls to SendToUMA. - EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); - - ExpectActiveUseUpdate(0); - daemon_.SetUserActiveState(/* active */ true, - TestTime(10 * kSecondsPerDay + 500)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(10 * kSecondsPerDay + 500), daemon_.user_active_last_); - - ExpectActiveUseUpdate(0); - daemon_.SetUserActiveState(/* active */ true, - TestTime(10 * kSecondsPerDay + 300)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(10 * kSecondsPerDay + 300), daemon_.user_active_last_); - - ExpectActiveUseUpdate(0); - daemon_.SetUserActiveState(/* active */ true, - TestTime(10 * kSecondsPerDay + 1000)); - EXPECT_TRUE(daemon_.user_active_); - EXPECT_EQ(TestTime(10 * kSecondsPerDay + 1000), daemon_.user_active_last_); -} - TEST_F(MetricsDaemonTest, ReportDiskStats) { long int read_sectors_now, write_sectors_now; diff --git a/metrics/power_states.h b/metrics/power_states.h deleted file mode 100644 index 74fbfecdd..000000000 --- a/metrics/power_states.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// A table of power states, to be included when building tabular things. -// -// See network_states.h for details. - - -#ifndef STATE -#define STATE(name, capname) -#endif - -STATE(on, On) -STATE(mem, Mem) - -#undef STATE diff --git a/metrics/session_states.h b/metrics/session_states.h deleted file mode 100644 index 293dbd995..000000000 --- a/metrics/session_states.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// A table of user session states, to be included when building tabular things. -// -// See network_states.h for details. - - -#ifndef STATE -#define STATE(name, capname) -#endif - -STATE(started, Started) -STATE(stopped, Stopped) - -#undef STATE From cc43be17ccd87bae1ae1db35cc04dfd80b0f6003 Mon Sep 17 00:00:00 2001 From: Wu-Cheng Li Date: Fri, 18 Apr 2014 20:13:09 +0800 Subject: [PATCH 125/200] Remove unused variable in metrics_daemon_test.cc. x86-generic ASAN builder unittest failed at metrics_daemon_test.cc:33:18: error: unused variable 'kSecondsPerDay'. Hopefully this can fix the failure. BUG=chromium:364818 TEST=NONE Change-Id: Icb63ed50a285e5f9a522d9dc2a7ab98f4affbf74 Reviewed-on: https://chromium-review.googlesource.com/195557 Reviewed-by: Kuang-che Wu Commit-Queue: Wu-Cheng Li Tested-by: Wu-Cheng Li Reviewed-by: Daniel Erat --- metrics/metrics_daemon_test.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index de4aab674..4217d51dd 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -30,8 +30,6 @@ using ::testing::Return; using ::testing::StrictMock; using chromeos_metrics::PersistentIntegerMock; -static const int kSecondsPerDay = 24 * 60 * 60; - static const char kTestDir[] = "test"; static const char kFakeDiskStatsPath[] = "fake-disk-stats"; static const char kFakeDiskStatsFormat[] = From 0d97a47418a41c4abe233a3c06a8392b883c33f5 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Fri, 18 Apr 2014 10:44:17 -0700 Subject: [PATCH 126/200] libmetrics: remove version 180609 BUG=chromium:361748 CQ-DEPEND=CL:195565 CQ-DEPEND=CL:195570 CQ-DEPEND=CL:195548 TEST=Trybot runs on paladin, release and chromiumos-sdk builders. Change-Id: I09fdbdad6752ced19bc0dc6cfecdc67f1867c2cf Reviewed-on: https://chromium-review.googlesource.com/195580 Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/libmetrics-180609.gyp | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 metrics/libmetrics-180609.gyp diff --git a/metrics/libmetrics-180609.gyp b/metrics/libmetrics-180609.gyp deleted file mode 100644 index 4226712c7..000000000 --- a/metrics/libmetrics-180609.gyp +++ /dev/null @@ -1,8 +0,0 @@ -{ - 'variables': { - 'libbase_ver': 180609, - }, - 'includes': [ - '../metrics/libmetrics.gypi', - ], -} From d884a2e47858c8970a9d698804362ce90c8185cf Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Thu, 24 Apr 2014 20:19:25 -0700 Subject: [PATCH 127/200] libmetrics: add pkg-config file BUG=chromium:366984 TEST=`FEATURES=test emerge-$BOARD platform2` and verify libmetrics-242728.pc is created. Change-Id: I3cd79e7421a5f666a4bf2551c87086f779078771 Reviewed-on: https://chromium-review.googlesource.com/196974 Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/libmetrics.pc.in | 7 +++++++ metrics/platform2_preinstall.sh | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 metrics/libmetrics.pc.in create mode 100755 metrics/platform2_preinstall.sh diff --git a/metrics/libmetrics.pc.in b/metrics/libmetrics.pc.in new file mode 100644 index 000000000..233f3181a --- /dev/null +++ b/metrics/libmetrics.pc.in @@ -0,0 +1,7 @@ +bslot=@BSLOT@ + +Name: libmetrics +Description: Chrome OS metrics library +Version: ${bslot} +Requires.private: libchrome-${bslot} +Libs: -lmetrics-${bslot} diff --git a/metrics/platform2_preinstall.sh b/metrics/platform2_preinstall.sh new file mode 100755 index 000000000..ccf353ff4 --- /dev/null +++ b/metrics/platform2_preinstall.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +set -e + +OUT=$1 +shift +for v; do + sed -e "s/@BSLOT@/${v}/g" libmetrics.pc.in > "${OUT}/lib/libmetrics-${v}.pc" +done From 254d22e5fd79f99d9af20338187bf60538f59720 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 21 Apr 2014 14:33:32 -0700 Subject: [PATCH 128/200] metrics_daemon: log to syslog Also log to stderr when not running as daemon. BUG=chromium:364700 TEST=manually verified in both daemon and non-daemon modes Change-Id: If62f70c1d327a5ecaf3075b6689b83a1494c09d5 Reviewed-on: https://chromium-review.googlesource.com/199380 Tested-by: Luigi Semenzato Reviewed-by: Daniel Erat Commit-Queue: Luigi Semenzato --- metrics/metrics_daemon.cc | 4 ---- metrics/metrics_daemon_main.cc | 8 ++++++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 47d74fa98..d1eb25e60 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -928,8 +928,6 @@ void MetricsDaemon::ReportDailyUse(int use_seconds) { void MetricsDaemon::SendSample(const string& name, int sample, int min, int max, int nbuckets) { - DLOG(INFO) << "received metric: " << name << " " << sample << " " - << min << " " << max << " " << nbuckets; metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); } @@ -993,8 +991,6 @@ void MetricsDaemon::SendCrashFrequencySample( void MetricsDaemon::SendLinearSample(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"; diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 5fb7507e7..ffe74f2a7 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -3,8 +3,10 @@ // found in the LICENSE file. +#include #include #include +#include #include #include @@ -43,7 +45,13 @@ const std::string MetricsMainDiskStatsPath() { } int main(int argc, char** argv) { + CommandLine::Init(argc, argv); google::ParseCommandLineFlags(&argc, &argv, true); + + // Also log to stderr when not running as daemon. + chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader | + (FLAGS_daemon ? 0 : chromeos::kLogToStderr)); + MetricsLibrary metrics_lib; metrics_lib.Init(); MetricsDaemon daemon; From e5883fadd92dc8592d63685100ff85e14eef228c Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 18 Apr 2014 17:00:35 -0700 Subject: [PATCH 129/200] metrics_daemon: restore correct meaning of Logging.DailyUse. Maintain separate persistent values for DailyActiveUse and CumulativeActiveUse. The latter is used to compute crash rates relative to active use. BUG=chromium:364746 TEST=manual Change-Id: I4d0485016eb49b30a77169ba1cedc1ffbff8810e Reviewed-on: https://chromium-review.googlesource.com/195698 Reviewed-by: Daniel Erat Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/metrics_daemon.cc | 30 +++++++++++++++++++++++++----- metrics/metrics_daemon.h | 11 +++++++++-- metrics/metrics_daemon_test.cc | 10 +++++----- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index d1eb25e60..97c099ccd 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -170,6 +170,7 @@ void MetricsDaemon::Run(bool run_as_daemon) { if (version_cycle_->Get() != version) { version_cycle_->Set(version); kernel_crashes_version_count_->Set(0); + version_cumulative_active_use_->Set(0); version_cumulative_cpu_use_->Set(0); } @@ -206,8 +207,10 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, // Sysconf cannot fail, so no sanity checks are needed. ticks_per_second_ = sysconf(_SC_CLK_TCK); - daily_use_.reset( + daily_active_use_.reset( new PersistentInteger("Logging.DailyUseTime")); + version_cumulative_active_use_.reset( + new PersistentInteger("Logging.CumulativeDailyUseTime")); version_cumulative_cpu_use_.reset( new PersistentInteger("Logging.CumulativeCpuTime")); @@ -931,8 +934,7 @@ void MetricsDaemon::SendSample(const string& name, int sample, metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); } -void MetricsDaemon::SendKernelCrashesCumulativeCountStats( - int64 active_use_seconds) { +void MetricsDaemon::SendKernelCrashesCumulativeCountStats() { // Report the number of crashes for this OS version, but don't clear the // counter. It is cleared elsewhere on version change. int64 crashes_count = kernel_crashes_version_count_->Get(); @@ -961,7 +963,13 @@ void MetricsDaemon::SendKernelCrashesCumulativeCountStats( 100); } + int64 active_use_seconds = version_cumulative_active_use_->Get(); if (active_use_seconds > 0) { + SendSample(version_cumulative_active_use_->Name(), + active_use_seconds / 1000, // stat is in seconds + 1, // device may be used very little... + 8 * 1000 * 1000, // ... or a lot (about 90 days) + 100); // Same as above, but per year of active time. SendSample("Logging.KernelCrashesPerActiveYear", crashes_count * kSecondsPerDay * 365 / active_use_seconds, @@ -971,6 +979,15 @@ void MetricsDaemon::SendKernelCrashesCumulativeCountStats( } } +void MetricsDaemon::SendDailyUseSample( + const scoped_ptr& use) { + SendSample(use->Name(), + use->GetAndClear(), + 1, // value of first bucket + kSecondsPerDay, // value of last bucket + 50); // number of buckets +} + void MetricsDaemon::SendCrashIntervalSample( const scoped_ptr& interval) { SendSample(interval->Name(), @@ -1000,7 +1017,8 @@ void MetricsDaemon::SendLinearSample(const string& name, int sample, void MetricsDaemon::UpdateStats(TimeTicks now_ticks, Time now_wall_time) { const int elapsed_seconds = (now_ticks - last_update_stats_time_).InSeconds(); - daily_use_->Add(elapsed_seconds); + daily_active_use_->Add(elapsed_seconds); + version_cumulative_active_use_->Add(elapsed_seconds); user_crash_interval_->Add(elapsed_seconds); kernel_crash_interval_->Add(elapsed_seconds); version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds()); @@ -1012,11 +1030,13 @@ void MetricsDaemon::UpdateStats(TimeTicks now_ticks, if (daily_cycle_->Get() != day) { daily_cycle_->Set(day); + SendDailyUseSample(daily_active_use_); + SendDailyUseSample(version_cumulative_active_use_); SendCrashFrequencySample(any_crashes_daily_count_); SendCrashFrequencySample(user_crashes_daily_count_); SendCrashFrequencySample(kernel_crashes_daily_count_); SendCrashFrequencySample(unclean_shutdowns_daily_count_); - SendKernelCrashesCumulativeCountStats(daily_use_->Get()); + SendKernelCrashesCumulativeCountStats(); } if (weekly_cycle_->Get() != week) { diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 929706a2d..81e53865d 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -175,12 +175,16 @@ class MetricsDaemon { // Sends various cumulative kernel crash-related stats, for instance the // total number of kernel crashes since the last version update. - void SendKernelCrashesCumulativeCountStats(int64 active_time_seconds); + void SendKernelCrashesCumulativeCountStats(); // Returns the total (system-wide) CPU usage between the time of the most // recent call to this function and now. base::TimeDelta GetIncrementalCpuUse(); + // Sends a sample representing the number of seconds of active use + // for a 24-hour period. + void SendDailyUseSample(const scoped_ptr& use); + // Sends a sample representing a time interval between two crashes of the // same type. void SendCrashIntervalSample(const scoped_ptr& interval); @@ -316,7 +320,10 @@ class MetricsDaemon { scoped_ptr weekly_cycle_; scoped_ptr version_cycle_; - scoped_ptr daily_use_; + // Active use accumulated in a day. + scoped_ptr daily_active_use_; + // Active use accumulated since the latest version update. + scoped_ptr version_cumulative_active_use_; // The CPU time accumulator. This contains the CPU time, in milliseconds, // used by the system since the most recent OS version update. diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 4217d51dd..264d5cd03 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -65,9 +65,9 @@ class MetricsDaemonTest : public testing::Test { base::CreateDirectory(FilePath(kTestDir)); // Replace original persistent values with mock ones. - daily_use_mock_ = + daily_active_use_mock_ = new StrictMock("1.mock"); - daemon_.daily_use_.reset(daily_use_mock_); + daemon_.daily_active_use_.reset(daily_active_use_mock_); kernel_crash_interval_mock_ = new StrictMock("2.mock"); @@ -92,7 +92,7 @@ class MetricsDaemonTest : public testing::Test { // Adds active use aggregation counters update expectations that the // specified count will be added. void ExpectActiveUseUpdate(int count) { - EXPECT_CALL(*daily_use_mock_, Add(count)) + EXPECT_CALL(*daily_active_use_mock_, Add(count)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*kernel_crash_interval_mock_, Add(count)) @@ -105,7 +105,7 @@ class MetricsDaemonTest : public testing::Test { // As above, but ignore values of counter updates. void IgnoreActiveUseUpdate() { - EXPECT_CALL(*daily_use_mock_, Add(_)) + EXPECT_CALL(*daily_active_use_mock_, Add(_)) .Times(1) .RetiresOnSaturation(); EXPECT_CALL(*kernel_crash_interval_mock_, Add(_)) @@ -182,7 +182,7 @@ class MetricsDaemonTest : public testing::Test { // Mocks. They are strict mock so that all unexpected // calls are marked as failures. StrictMock metrics_lib_; - StrictMock* daily_use_mock_; + StrictMock* daily_active_use_mock_; StrictMock* kernel_crash_interval_mock_; StrictMock* user_crash_interval_mock_; StrictMock* unclean_shutdown_interval_mock_; From 3e8a851625e336d11228c5f039421dfaba4c02cb Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 14 May 2014 16:14:37 -0400 Subject: [PATCH 130/200] use IGNORE_EINTR w/close HANDLE_EINTR is both not safe and not useful on Linux systems. Switch to IGNORE_EINTR like Chromium has done everywhere. See http://crbug.com/269623 for details. BUG=chromium:373154 TEST=`cbuildbot {arm,amd64,x86}-generic-full` passes Change-Id: I3d99bb2376cb7961ac31ba0fa82bafc1b4c14bd9 Reviewed-on: https://chromium-review.googlesource.com/199821 Reviewed-by: Daniel Erat Reviewed-by: Luigi Semenzato Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- metrics/metrics_daemon.cc | 2 +- metrics/metrics_library.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 97c099ccd..e3f6bf7e7 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -481,7 +481,7 @@ bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors, << diskstats_path_ << ", expected 2"; } } - HANDLE_EINTR(close(file)); + IGNORE_EINTR(close(file)); return success; } diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 1214dd060..32bacf6a7 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -209,7 +209,7 @@ bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { // underneath us. Keep the file locked as briefly as possible. if (HANDLE_EINTR(flock(chrome_fd, LOCK_EX)) < 0) { PrintError("flock", uma_events_file_, errno); - HANDLE_EINTR(close(chrome_fd)); + IGNORE_EINTR(close(chrome_fd)); return false; } @@ -220,7 +220,7 @@ bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { } // Close the file and release the lock. - HANDLE_EINTR(close(chrome_fd)); + IGNORE_EINTR(close(chrome_fd)); return success; } From 41c54505234bf8baee7d5b1bab54758bd7893877 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Tue, 13 May 2014 15:16:24 -0700 Subject: [PATCH 131/200] metrics library: convert to proper C++/libbase This mostly converts fprintfs to proper logs, char* to std::string wherever appropriate. BUG=chromium:355796 TEST=unit tests Change-Id: Ieb1cb110be5e281b7e0c764a0dfce895f33d4a3c Reviewed-on: https://chromium-review.googlesource.com/199610 Reviewed-by: Luigi Semenzato Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_library.cc | 156 +++++++++++--------------------- metrics/metrics_library.h | 24 ++--- metrics/metrics_library_test.cc | 38 ++++---- 3 files changed, 76 insertions(+), 142 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 32bacf6a7..97cb278fd 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -4,6 +4,8 @@ #include "metrics_library.h" +#include +#include #include #include #include @@ -51,23 +53,6 @@ static const char *kCrosEventNames[] = { time_t MetricsLibrary::cached_enabled_time_ = 0; bool MetricsLibrary::cached_enabled_ = false; -using std::string; - -// TODO(sosa@chromium.org) - use Chromium logger instead of stderr -static void PrintError(const char* message, const char* file, - int code) { - static const char kProgramName[] = "libmetrics"; - if (code == 0) { - fprintf(stderr, "%s: %s\n", kProgramName, message); - } else if (file == NULL) { - fprintf(stderr, "%s: ", kProgramName); - perror(message); - } else { - fprintf(stderr, "%s: %s: ", kProgramName, file); - perror(message); - } -} - // Copied from libbase to avoid pulling in all of libbase just for libmetrics. static int WriteFileDescriptor(const int fd, const char* data, int size) { // Allow for partial writes. @@ -84,10 +69,7 @@ static int WriteFileDescriptor(const int fd, const char* data, int size) { return bytes_written_total; } -MetricsLibrary::MetricsLibrary() - : uma_events_file_(NULL), - consent_file_(kConsentFile) {} - +MetricsLibrary::MetricsLibrary() : consent_file_(kConsentFile) {} MetricsLibrary::~MetricsLibrary() {} // We take buffer and buffer_size as parameters in order to simplify testing @@ -178,7 +160,7 @@ bool MetricsLibrary::AreMetricsEnabled() { // still respect the consent file if it is present for migration purposes. // TODO(pastarmovj) if (!has_policy) { - enabled = stat(consent_file_, &stat_buffer) >= 0; + enabled = stat(consent_file_.c_str(), &stat_buffer) >= 0; } if (enabled && !IsGuestMode()) @@ -189,14 +171,20 @@ bool MetricsLibrary::AreMetricsEnabled() { return cached_enabled_; } -bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { - - int chrome_fd = HANDLE_EINTR(open(uma_events_file_, +bool MetricsLibrary::SendMessageToChrome(const std::string& message) { + int size = static_cast(message.size()); + if (size > kBufferSize) { + LOG(ERROR) << "chrome message too big (" << size << " bytes)"; + return false; + } + // Use libc here instead of chromium base classes because we need a UNIX fd + // for flock. + int chrome_fd = HANDLE_EINTR(open(uma_events_file_.c_str(), O_WRONLY | O_APPEND | O_CREAT, READ_WRITE_ALL_FILE_FLAGS)); // If we failed to open it, return. if (chrome_fd < 0) { - PrintError("open", uma_events_file_, errno); + PLOG(ERROR) << uma_events_file_ << ": open"; return false; } @@ -206,16 +194,16 @@ bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS); // Grab an exclusive lock to protect Chrome from truncating - // underneath us. Keep the file locked as briefly as possible. + // underneath us. if (HANDLE_EINTR(flock(chrome_fd, LOCK_EX)) < 0) { - PrintError("flock", uma_events_file_, errno); + PLOG(ERROR) << uma_events_file_ << ": flock"; IGNORE_EINTR(close(chrome_fd)); return false; } bool success = true; - if (WriteFileDescriptor(chrome_fd, message, length) != length) { - PrintError("write", uma_events_file_, errno); + if (WriteFileDescriptor(chrome_fd, message.c_str(), size) != size) { + PLOG(ERROR) << uma_events_file_ << ": write"; success = false; } @@ -224,44 +212,28 @@ bool MetricsLibrary::SendMessageToChrome(int32_t length, const char* message) { return success; } -int32_t MetricsLibrary::FormatChromeMessage(int32_t buffer_size, char* buffer, - const char* format, ...) { - int32_t message_length; - size_t len_size = sizeof(message_length); - - // Format the non-LENGTH contents in the buffer by leaving space for - // LENGTH at the start of the buffer. - va_list args; - va_start(args, format); - message_length = vsnprintf(&buffer[len_size], buffer_size - len_size, - format, args); - va_end(args); - - if (message_length < 0) { - PrintError("chrome message format error", NULL, 0); - return -1; - } - - // +1 to account for the trailing \0. - message_length += len_size + 1; - if (message_length > buffer_size) { - PrintError("chrome message too long", NULL, 0); - return -1; - } - - // Prepend LENGTH to the message. - memcpy(buffer, &message_length, len_size); - return message_length; +const std::string MetricsLibrary::FormatChromeMessage( + const std::string& name, + const std::string& value) { + uint32 message_length = + sizeof(message_length) + name.size() + 1 + value.size() + 1; + std::string result; + result.reserve(message_length); + // Marshal the total message length in the native byte order. + result.assign(reinterpret_cast(&message_length), + sizeof(message_length)); + result += name + '\0' + value + '\0'; + return result; } void MetricsLibrary::Init() { uma_events_file_ = kUMAEventsPath; } -bool MetricsLibrary::SendToAutotest(const string& name, int value) { +bool MetricsLibrary::SendToAutotest(const std::string& name, int value) { FILE* autotest_file = fopen(kAutotestPath, "a+"); if (autotest_file == NULL) { - PrintError("fopen", kAutotestPath, errno); + PLOG(ERROR) << kAutotestPath << ": fopen"; return false; } @@ -270,74 +242,48 @@ bool MetricsLibrary::SendToAutotest(const string& name, int value) { return true; } -bool MetricsLibrary::SendToUMA(const string& name, int sample, - int min, int max, int nbuckets) { +bool MetricsLibrary::SendToUMA(const std::string& name, + int sample, + int min, + int max, + int nbuckets) { // Format the message. - char message[kBufferSize]; - int32_t message_length = - FormatChromeMessage(kBufferSize, message, - "histogram%c%s %d %d %d %d", '\0', - name.c_str(), sample, min, max, nbuckets); - if (message_length < 0) - return false; - + std::string value = base::StringPrintf("%s %d %d %d %d", + name.c_str(), sample, min, max, nbuckets); + std::string message = FormatChromeMessage("histogram", value); // Send the message. - return SendMessageToChrome(message_length, message); + return SendMessageToChrome(message); } bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, int max) { // Format the message. - char message[kBufferSize]; - int32_t message_length = - FormatChromeMessage(kBufferSize, message, - "linearhistogram%c%s %d %d", '\0', - name.c_str(), sample, max); - if (message_length < 0) - return false; - + std::string value = base::StringPrintf("%s %d %d", name.c_str(), sample, max); + std::string message = FormatChromeMessage("linearhistogram", value); // Send the message. - return SendMessageToChrome(message_length, message); + return SendMessageToChrome(message); } bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) { // Format the message. - char message[kBufferSize]; - int32_t message_length = - FormatChromeMessage(kBufferSize, message, "sparsehistogram%c%s %d", - '\0', name.c_str(), sample); - if (message_length < 0) - return false; - + std::string value = base::StringPrintf("%s %d", name.c_str(), sample); + std::string message = FormatChromeMessage("sparsehistogram", value); // Send the message. - return SendMessageToChrome(message_length, message); + return SendMessageToChrome(message); } bool MetricsLibrary::SendUserActionToUMA(const std::string& action) { // Format the message. - char message[kBufferSize]; - int32_t message_length = - FormatChromeMessage(kBufferSize, message, - "useraction%c%s", '\0', action.c_str()); - if (message_length < 0) - return false; - + std::string message = FormatChromeMessage("useraction", action); // Send the message. - return SendMessageToChrome(message_length, message); + return SendMessageToChrome(message); } bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { // Format the message. - char message[kBufferSize]; - int32_t message_length = - FormatChromeMessage(kBufferSize, message, - "crash%c%s", '\0', crash_kind); - - if (message_length < 0) - return false; - + std::string message = FormatChromeMessage("crash", crash_kind); // Send the message. - return SendMessageToChrome(message_length, message); + return SendMessageToChrome(message); } void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index e9c6f4be4..74f5de226 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -133,22 +133,16 @@ class MetricsLibrary : public MetricsLibraryInterface { // Sends message of size |length| to Chrome for transport to UMA and // returns true on success. - bool SendMessageToChrome(int32_t length, const char* message); + bool SendMessageToChrome(const std::string& message); - // Formats a name/value message for Chrome in |buffer| and returns the - // length of the message or a negative value on error. + // Serializes a name/value pair into a message buffer. // - // Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 | + // The serialized format is: | LENGTH | NAME | \0 | VALUE | \0 | // - // The arbitrary |format| argument covers the non-LENGTH portion of the - // message. The caller is responsible to store the \0 character - // between NAME and VALUE (e.g. "%s%c%d", name, '\0', value). - // - // Ideally we'd use "3, 4" here instead of "4, 5", but it seems clang/gcc - // have an implicit first arg ("this"). http://crbug.com/329356 - __attribute__((__format__(__printf__, 4, 5))) - int32_t FormatChromeMessage(int32_t buffer_size, char* buffer, - const char* format, ...); + // where LENGTH is a 32-bit integer in native endianness, and NAME and VALUE + // are null-terminated strings (the zero bytes are explicitly shown above). + const std::string FormatChromeMessage(const std::string& name, + const std::string& value); // This function is used by tests only to mock the device policies. void SetPolicyProvider(policy::PolicyProvider* provider); @@ -159,8 +153,8 @@ class MetricsLibrary : public MetricsLibraryInterface { // Cached state of whether or not metrics were enabled. static bool cached_enabled_; - const char* uma_events_file_; - const char* consent_file_; + std::string uma_events_file_; + std::string consent_file_; scoped_ptr policy_provider_; diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 0a0768b21..f6cd7e7c3 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -29,10 +29,10 @@ ACTION_P(SetMetricsPolicy, enabled) { class MetricsLibraryTest : public testing::Test { protected: virtual void SetUp() { - EXPECT_EQ(NULL, lib_.uma_events_file_); + EXPECT_TRUE(lib_.uma_events_file_.empty()); lib_.Init(); - EXPECT_TRUE(NULL != lib_.uma_events_file_); - lib_.uma_events_file_ = kTestUMAEventsFile.value().c_str(); + EXPECT_FALSE(lib_.uma_events_file_.empty()); + lib_.uma_events_file_ = kTestUMAEventsFile.value(); device_policy_ = new policy::MockDevicePolicy(); EXPECT_CALL(*device_policy_, LoadPolicy()) .Times(AnyNumber()) @@ -165,18 +165,12 @@ TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) { } TEST_F(MetricsLibraryTest, FormatChromeMessage) { - char buf[7]; - const int kLen = 6; - EXPECT_EQ(kLen, lib_.FormatChromeMessage(7, buf, "%d", 1)); - - char exp[kLen]; - sprintf(exp, "%c%c%c%c1", kLen, 0, 0, 0); - EXPECT_EQ(0, memcmp(exp, buf, kLen)); -} - -TEST_F(MetricsLibraryTest, FormatChromeMessageTooLong) { - char buf[7]; - EXPECT_EQ(-1, lib_.FormatChromeMessage(7, buf, "test")); + char kLen = 10; + char exp[] = { kLen, 0, 0, 0, 'f', 'o', 'o', 0, '1', 0 }; + // The message is composed by a 4-byte length field, followed + // by two null-terminated strings (name/value). + EXPECT_EQ(sizeof(exp), kLen); + EXPECT_EQ(std::string(exp, kLen), lib_.FormatChromeMessage("foo", "1")); } TEST_F(MetricsLibraryTest, SendEnumToUMA) { @@ -192,8 +186,8 @@ TEST_F(MetricsLibraryTest, SendEnumToUMA) { } TEST_F(MetricsLibraryTest, SendMessageToChrome) { - EXPECT_TRUE(lib_.SendMessageToChrome(4, "test")); - EXPECT_TRUE(lib_.SendMessageToChrome(7, "content")); + EXPECT_TRUE(lib_.SendMessageToChrome(std::string("test"))); + EXPECT_TRUE(lib_.SendMessageToChrome(std::string("content"))); std::string uma_events; EXPECT_TRUE(base::ReadFileToString(kTestUMAEventsFile, &uma_events)); EXPECT_EQ("testcontent", uma_events); @@ -204,8 +198,8 @@ TEST_F(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation) { // created. static const char kDoesNotExistFile[] = "/does/not/exist"; lib_.uma_events_file_ = kDoesNotExistFile; - static const char kDummyMessage[] = "Dummy Message"; - EXPECT_FALSE(lib_.SendMessageToChrome(strlen(kDummyMessage), kDummyMessage)); + const std::string kDummyMessage = "Dummy Message"; + EXPECT_FALSE(lib_.SendMessageToChrome(kDummyMessage)); base::DeleteFile(FilePath(kDoesNotExistFile), false); } @@ -258,10 +252,10 @@ class CMetricsLibraryTest : public testing::Test { virtual void SetUp() { lib_ = CMetricsLibraryNew(); MetricsLibrary& ml = *reinterpret_cast(lib_); - EXPECT_EQ(NULL, ml.uma_events_file_); + EXPECT_TRUE(ml.uma_events_file_.empty()); CMetricsLibraryInit(lib_); - EXPECT_TRUE(NULL != ml.uma_events_file_); - ml.uma_events_file_ = kTestUMAEventsFile.value().c_str(); + EXPECT_FALSE(ml.uma_events_file_.empty()); + ml.uma_events_file_ = kTestUMAEventsFile.value(); device_policy_ = new policy::MockDevicePolicy(); EXPECT_CALL(*device_policy_, LoadPolicy()) .Times(AnyNumber()) From 960e0e12c021b5986675c15182e3a94e644f5755 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 15 May 2014 10:48:19 -0700 Subject: [PATCH 132/200] metrics library: move event file location from /var/log to /var/run The event file, which is used for communicating metric events from chrome os to chrome, has been living in the very non-kosher location /var/log/metrics/uma-events. This change duplicates it in /var/run/metrics/uma-events. A later chrome change will pick up the metrics from there. I also opened crbug.com/373833 to remove the duplication once Chrome has switched over. BUG=chromium:361331 TEST=tested with unit tests and manually Change-Id: I98b741798aa0481b7987f93f087239ad214b759f Reviewed-on: https://chromium-review.googlesource.com/200091 Tested-by: Luigi Semenzato Reviewed-by: Daniel Erat Commit-Queue: Luigi Semenzato --- metrics/init/metrics_daemon.conf | 7 +++++-- metrics/metrics_library.cc | 24 +++++++++++++++++++----- metrics/metrics_library.h | 9 +++++++-- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/metrics/init/metrics_daemon.conf b/metrics/init/metrics_daemon.conf index 9b1e2a52a..885ab0fc0 100644 --- a/metrics/init/metrics_daemon.conf +++ b/metrics/init/metrics_daemon.conf @@ -12,8 +12,11 @@ stop on stopping system-services respawn pre-start script - # Make the directory, but don't die on error. Let someone else complain. - mkdir -p /var/lib/metrics || : + # Make directories, but don't die on error. Let someone else complain. + LIBDIR=/var/lib/metrics + RUNDIR=/var/run/metrics + mkdir -p $LIBDIR || logger "metrics_daemon: cannot create $LIBDIR" + mkdir -p $RUNDIR || logger "metrics_daemon: cannot create $RUNDIR" end script expect fork diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 97cb278fd..c71234efd 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -25,6 +25,7 @@ static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; static const char kUMAEventsPath[] = "/var/log/metrics/uma-events"; +static const char kNewUMAEventsPath[] = "/var/run/metrics/uma-events"; static const char kConsentFile[] = "/home/chronos/Consent To Send Stats"; static const int32_t kBufferSize = 1024; static const char kCrosEventHistogramName[] = "Platform.CrOSEvent"; @@ -171,7 +172,8 @@ bool MetricsLibrary::AreMetricsEnabled() { return cached_enabled_; } -bool MetricsLibrary::SendMessageToChrome(const std::string& message) { +bool MetricsLibrary::StoreMessageInFile(const std::string& message, + const std::string& events_file) { int size = static_cast(message.size()); if (size > kBufferSize) { LOG(ERROR) << "chrome message too big (" << size << " bytes)"; @@ -179,12 +181,12 @@ bool MetricsLibrary::SendMessageToChrome(const std::string& message) { } // Use libc here instead of chromium base classes because we need a UNIX fd // for flock. - int chrome_fd = HANDLE_EINTR(open(uma_events_file_.c_str(), + int chrome_fd = HANDLE_EINTR(open(events_file.c_str(), O_WRONLY | O_APPEND | O_CREAT, READ_WRITE_ALL_FILE_FLAGS)); // If we failed to open it, return. if (chrome_fd < 0) { - PLOG(ERROR) << uma_events_file_ << ": open"; + PLOG(ERROR) << events_file << ": open"; return false; } @@ -196,14 +198,14 @@ bool MetricsLibrary::SendMessageToChrome(const std::string& message) { // Grab an exclusive lock to protect Chrome from truncating // underneath us. if (HANDLE_EINTR(flock(chrome_fd, LOCK_EX)) < 0) { - PLOG(ERROR) << uma_events_file_ << ": flock"; + PLOG(ERROR) << events_file << ": flock"; IGNORE_EINTR(close(chrome_fd)); return false; } bool success = true; if (WriteFileDescriptor(chrome_fd, message.c_str(), size) != size) { - PLOG(ERROR) << uma_events_file_ << ": write"; + PLOG(ERROR) << events_file << ": write"; success = false; } @@ -212,6 +214,17 @@ bool MetricsLibrary::SendMessageToChrome(const std::string& message) { return success; } +bool MetricsLibrary::SendMessageToChrome(const std::string& message) { + // TEMPORARY: store to both new and old file, to facilitate change if the + // Chrome side is out of sync with this. See crbug.com/373833. + + // If one store fails, we'll be cool... hey man it's OK, you know, whatever. + // If both stores fail, then definitely something is wrong. + bool success = StoreMessageInFile(message, uma_events_file_); + success |= StoreMessageInFile(message, new_uma_events_file_); + return success; +} + const std::string MetricsLibrary::FormatChromeMessage( const std::string& name, const std::string& value) { @@ -228,6 +241,7 @@ const std::string MetricsLibrary::FormatChromeMessage( void MetricsLibrary::Init() { uma_events_file_ = kUMAEventsPath; + new_uma_events_file_ = kNewUMAEventsPath; } bool MetricsLibrary::SendToAutotest(const std::string& name, int value) { diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 74f5de226..a3f43c5a7 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -131,8 +131,12 @@ class MetricsLibrary : public MetricsLibraryInterface { char* buffer, int buffer_size, bool* result); - // Sends message of size |length| to Chrome for transport to UMA and - // returns true on success. + // TEMPORARY (see crbug.com/373833). Stores a message to Chrome in the + // events file. + bool StoreMessageInFile(const std::string& message, + const std::string& events_file); + + // Sends message to Chrome for transport to UMA and returns true on success. bool SendMessageToChrome(const std::string& message); // Serializes a name/value pair into a message buffer. @@ -154,6 +158,7 @@ class MetricsLibrary : public MetricsLibraryInterface { static bool cached_enabled_; std::string uma_events_file_; + std::string new_uma_events_file_; // TEMPORARY see crbug.com/373833. std::string consent_file_; scoped_ptr policy_provider_; From 31cda9806e7daa73abaa6e4bd09321ef52357367 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 19 May 2014 08:30:42 -0700 Subject: [PATCH 133/200] metrics library: complete switchover from /var/log to /var/run This removes the double metrics even file use to facilitate the transition. The corresponding change has been in Chromium for 2 days now. BUG=chromium:373833 TEST=manually verified Change-Id: I36ff355e6519b3894c0a4579758ae87bdac6b148 Reviewed-on: https://chromium-review.googlesource.com/200426 Reviewed-by: Bertrand Simonnet Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_library.cc | 26 ++++++-------------------- metrics/metrics_library.h | 6 ------ 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index c71234efd..b15fd4999 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -24,8 +24,7 @@ #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; -static const char kUMAEventsPath[] = "/var/log/metrics/uma-events"; -static const char kNewUMAEventsPath[] = "/var/run/metrics/uma-events"; +static const char kUMAEventsPath[] = "/var/run/metrics/uma-events"; static const char kConsentFile[] = "/home/chronos/Consent To Send Stats"; static const int32_t kBufferSize = 1024; static const char kCrosEventHistogramName[] = "Platform.CrOSEvent"; @@ -172,8 +171,7 @@ bool MetricsLibrary::AreMetricsEnabled() { return cached_enabled_; } -bool MetricsLibrary::StoreMessageInFile(const std::string& message, - const std::string& events_file) { +bool MetricsLibrary::SendMessageToChrome(const std::string& message) { int size = static_cast(message.size()); if (size > kBufferSize) { LOG(ERROR) << "chrome message too big (" << size << " bytes)"; @@ -181,12 +179,12 @@ bool MetricsLibrary::StoreMessageInFile(const std::string& message, } // Use libc here instead of chromium base classes because we need a UNIX fd // for flock. - int chrome_fd = HANDLE_EINTR(open(events_file.c_str(), + int chrome_fd = HANDLE_EINTR(open(uma_events_file_.c_str(), O_WRONLY | O_APPEND | O_CREAT, READ_WRITE_ALL_FILE_FLAGS)); // If we failed to open it, return. if (chrome_fd < 0) { - PLOG(ERROR) << events_file << ": open"; + PLOG(ERROR) << uma_events_file_ << ": open"; return false; } @@ -198,14 +196,14 @@ bool MetricsLibrary::StoreMessageInFile(const std::string& message, // Grab an exclusive lock to protect Chrome from truncating // underneath us. if (HANDLE_EINTR(flock(chrome_fd, LOCK_EX)) < 0) { - PLOG(ERROR) << events_file << ": flock"; + PLOG(ERROR) << uma_events_file_ << ": flock"; IGNORE_EINTR(close(chrome_fd)); return false; } bool success = true; if (WriteFileDescriptor(chrome_fd, message.c_str(), size) != size) { - PLOG(ERROR) << events_file << ": write"; + PLOG(ERROR) << uma_events_file_ << ": write"; success = false; } @@ -214,17 +212,6 @@ bool MetricsLibrary::StoreMessageInFile(const std::string& message, return success; } -bool MetricsLibrary::SendMessageToChrome(const std::string& message) { - // TEMPORARY: store to both new and old file, to facilitate change if the - // Chrome side is out of sync with this. See crbug.com/373833. - - // If one store fails, we'll be cool... hey man it's OK, you know, whatever. - // If both stores fail, then definitely something is wrong. - bool success = StoreMessageInFile(message, uma_events_file_); - success |= StoreMessageInFile(message, new_uma_events_file_); - return success; -} - const std::string MetricsLibrary::FormatChromeMessage( const std::string& name, const std::string& value) { @@ -241,7 +228,6 @@ const std::string MetricsLibrary::FormatChromeMessage( void MetricsLibrary::Init() { uma_events_file_ = kUMAEventsPath; - new_uma_events_file_ = kNewUMAEventsPath; } bool MetricsLibrary::SendToAutotest(const std::string& name, int value) { diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index a3f43c5a7..e34ff4a72 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -131,11 +131,6 @@ class MetricsLibrary : public MetricsLibraryInterface { char* buffer, int buffer_size, bool* result); - // TEMPORARY (see crbug.com/373833). Stores a message to Chrome in the - // events file. - bool StoreMessageInFile(const std::string& message, - const std::string& events_file); - // Sends message to Chrome for transport to UMA and returns true on success. bool SendMessageToChrome(const std::string& message); @@ -158,7 +153,6 @@ class MetricsLibrary : public MetricsLibraryInterface { static bool cached_enabled_; std::string uma_events_file_; - std::string new_uma_events_file_; // TEMPORARY see crbug.com/373833. std::string consent_file_; scoped_ptr policy_provider_; From 65dd4c61b823dfcac3d18a167abe61a9488fab1f Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Mon, 19 May 2014 20:28:16 -0700 Subject: [PATCH 134/200] libmetrics: Add support for building against libchrome-271506 BUG=chromium:375032 CQ-DEPEND=CL:200532 CQ-DEPEND=CL:200522 TEST=`FEATURES=test emerge-$BOARD platform2` TEST=Trybot runs on paladin, release, and chromiumos-sdk builders. Change-Id: Ie3e9cc522099a8225f54a4442d0215d580425846 Reviewed-on: https://chromium-review.googlesource.com/200518 Reviewed-by: Daniel Erat Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/libmetrics-271506.gyp | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 metrics/libmetrics-271506.gyp diff --git a/metrics/libmetrics-271506.gyp b/metrics/libmetrics-271506.gyp new file mode 100644 index 000000000..6df23813c --- /dev/null +++ b/metrics/libmetrics-271506.gyp @@ -0,0 +1,8 @@ +{ + 'variables': { + 'libbase_ver': 271506, + }, + 'includes': [ + '../metrics/libmetrics.gypi', + ], +} From 8c3cd4162030fe2a55d2768b32b708bbdb10710d Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Mon, 19 May 2014 20:30:51 -0700 Subject: [PATCH 135/200] metrics: Update to build against libchrome-271506 BUG=chromium:375032 TEST=`FEATURES=test emerge-$BOARD platform2` TEST=Trybot runs on paladin, release, and chromiumos-sdk builders. Change-Id: Icb524d3fd4001ebc36edc84306ea70db70f7599b Reviewed-on: https://chromium-review.googlesource.com/200519 Reviewed-by: Luigi Semenzato Reviewed-by: Daniel Erat Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/metrics.gyp | 2 +- metrics/metrics_daemon_test.cc | 6 +++--- metrics/metrics_library_test.cc | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 834e52fb8..3d7c2d57a 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -1,6 +1,6 @@ { 'variables': { - 'libbase_ver': 242728, + 'libbase_ver': 271506, }, 'target_defaults': { 'dependencies': [ diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 264d5cd03..33d0d1122 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -172,8 +172,8 @@ class MetricsDaemonTest : public testing::Test { std::string frequency_string = StringPrintf("%d\n", frequency); int frequency_string_length = frequency_string.length(); EXPECT_EQ(frequency_string.length(), - file_util::WriteFile(path, frequency_string.c_str(), - frequency_string_length)); + base::WriteFile(path, frequency_string.c_str(), + frequency_string_length)); } // The MetricsDaemon under test. @@ -193,7 +193,7 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) { EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected)); base::FilePath crash_detected(kKernelCrashDetected); - file_util::WriteFile(crash_detected, "", 0); + base::WriteFile(crash_detected, "", 0); EXPECT_TRUE(base::PathExists(crash_detected)); EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected)); EXPECT_FALSE(base::PathExists(crash_detected)); diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index f6cd7e7c3..377495ffb 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -70,9 +70,9 @@ TEST_F(MetricsLibraryTest, IsDeviceMounted) { buffer, 1, &result)); - ASSERT_TRUE(file_util::WriteFile(base::FilePath(kTestMounts), - kTestContents, - strlen(kTestContents))); + ASSERT_TRUE(base::WriteFile(base::FilePath(kTestMounts), + kTestContents, + strlen(kTestContents))); EXPECT_FALSE(lib_.IsDeviceMounted("guestfs", kTestMounts, buffer, From 52178b487164061c1e325f2d61e0315b974e2c2a Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 21 May 2014 19:39:02 -0700 Subject: [PATCH 136/200] libmetrics: remove version 242728 BUG=chromium:375032 TEST=Trybot runs on paladin, release and chromiumos-sdk builders. Change-Id: I661a5646c37f43e92c6666790b11a4cc8451ee05 Reviewed-on: https://chromium-review.googlesource.com/201004 Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/libmetrics-242728.gyp | 8 -------- metrics/timer.h | 4 ---- 2 files changed, 12 deletions(-) delete mode 100644 metrics/libmetrics-242728.gyp diff --git a/metrics/libmetrics-242728.gyp b/metrics/libmetrics-242728.gyp deleted file mode 100644 index a8b194840..000000000 --- a/metrics/libmetrics-242728.gyp +++ /dev/null @@ -1,8 +0,0 @@ -{ - 'variables': { - 'libbase_ver': 242728, - }, - 'includes': [ - '../metrics/libmetrics.gypi', - ], -} diff --git a/metrics/timer.h b/metrics/timer.h index ce8887303..654c2cb31 100644 --- a/metrics/timer.h +++ b/metrics/timer.h @@ -10,11 +10,7 @@ #include #include -#if BASE_VER >= 242728 #include -#else -#include -#endif #include // for FRIEND_TEST class MetricsLibraryInterface; From a5db220722d0bc9c5a0c58f9edba0bdb9657c374 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 23 May 2014 15:06:24 -0700 Subject: [PATCH 137/200] metrics daemon: create /var/run/metrics/uma-events earlier The uma-events file needs to be created earlier, for all users of the metrics library. This also avoids permission issues with writing to /var/run/metrics. BUG=chromium:376891 TEST=manual CQ-DEPEND=CL:201416 Change-Id: I3a48a3779edb24f3bd08a91fbee4e8fe5a25ec49 Reviewed-on: https://chromium-review.googlesource.com/201347 Tested-by: Luigi Semenzato Reviewed-by: Jorge Lucangeli Obes Commit-Queue: Luigi Semenzato --- metrics/init/metrics_daemon.conf | 7 ++----- metrics/init/metrics_library.conf | 19 +++++++++++++++++++ metrics/metrics_library.cc | 12 ++---------- metrics/metrics_library_test.cc | 2 ++ 4 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 metrics/init/metrics_library.conf diff --git a/metrics/init/metrics_daemon.conf b/metrics/init/metrics_daemon.conf index 885ab0fc0..52fe90d46 100644 --- a/metrics/init/metrics_daemon.conf +++ b/metrics/init/metrics_daemon.conf @@ -12,11 +12,8 @@ stop on stopping system-services respawn pre-start script - # Make directories, but don't die on error. Let someone else complain. - LIBDIR=/var/lib/metrics - RUNDIR=/var/run/metrics - mkdir -p $LIBDIR || logger "metrics_daemon: cannot create $LIBDIR" - mkdir -p $RUNDIR || logger "metrics_daemon: cannot create $RUNDIR" + # Make directory, but don't die on error. Let someone else complain. + mkdir -p /var/lib/metrics end script expect fork diff --git a/metrics/init/metrics_library.conf b/metrics/init/metrics_library.conf new file mode 100644 index 000000000..6aa49edd0 --- /dev/null +++ b/metrics/init/metrics_library.conf @@ -0,0 +1,19 @@ +# Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +description "Metrics Library upstart file" +author "chromium-os-dev@chromium.org" + +# The metrics library is used by several programs (daemons and others) +# to send UMA stats. +start on starting boot-services + +pre-start script + # Create the file used as communication endpoint for metrics. + RUNDIR=/var/run/metrics + EVENTS_FILE=${RUNDIR}/uma-events + mkdir -p ${RUNDIR} + touch ${EVENTS_FILE} + chmod 666 ${EVENTS_FILE} +end script diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index b15fd4999..4fbc1d8dd 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -19,8 +19,6 @@ #include "policy/device_policy.h" -#define READ_WRITE_ALL_FILE_FLAGS \ - (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; @@ -178,21 +176,15 @@ bool MetricsLibrary::SendMessageToChrome(const std::string& message) { return false; } // Use libc here instead of chromium base classes because we need a UNIX fd - // for flock. + // for flock. |uma_events_file_| must exist already. int chrome_fd = HANDLE_EINTR(open(uma_events_file_.c_str(), - O_WRONLY | O_APPEND | O_CREAT, - READ_WRITE_ALL_FILE_FLAGS)); + O_WRONLY | O_APPEND)); // If we failed to open it, return. if (chrome_fd < 0) { PLOG(ERROR) << uma_events_file_ << ": open"; return false; } - // Need to chmod because open flags are anded with umask. Ignore the - // exit code -- a chronos process may fail chmoding because the file - // has been created by a root process but that should be OK. - fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS); - // Grab an exclusive lock to protect Chrome from truncating // underneath us. if (HANDLE_EINTR(flock(chrome_fd, LOCK_EX)) < 0) { diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 377495ffb..82418ede5 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -33,6 +33,7 @@ class MetricsLibraryTest : public testing::Test { lib_.Init(); EXPECT_FALSE(lib_.uma_events_file_.empty()); lib_.uma_events_file_ = kTestUMAEventsFile.value(); + EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0)); device_policy_ = new policy::MockDevicePolicy(); EXPECT_CALL(*device_policy_, LoadPolicy()) .Times(AnyNumber()) @@ -256,6 +257,7 @@ class CMetricsLibraryTest : public testing::Test { CMetricsLibraryInit(lib_); EXPECT_FALSE(ml.uma_events_file_.empty()); ml.uma_events_file_ = kTestUMAEventsFile.value(); + EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0)); device_policy_ = new policy::MockDevicePolicy(); EXPECT_CALL(*device_policy_, LoadPolicy()) .Times(AnyNumber()) From 38f6bfa90b26f7637f3f36f5681c595fee574f87 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 28 May 2014 16:22:43 -0700 Subject: [PATCH 138/200] metrics: Update gyp file to use src/platform2/common-mk We are moving the packages built by platform2's ebuild into src/platform2. We need packages to use src/platform2/common-mk instead of src/platform/common-mk. BUG=chromium:378554 TEST=emerge-daisy platform2 TEST=trybot run on daisy, link, mario and duck CQ-DEPEND=CL:201938 Change-Id: Ibe02d33d7d17a830f0d72cdb2597b8e97b1864da Reviewed-on: https://chromium-review.googlesource.com/201982 Tested-by: Bertrand Simonnet Reviewed-by: Prathmesh Prabhu Commit-Queue: Bertrand Simonnet --- metrics/metrics.gyp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 3d7c2d57a..4a0b11a1a 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -65,7 +65,7 @@ { 'target_name': 'persistent_integer_test', 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'sources': [ 'persistent_integer.cc', 'persistent_integer_test.cc', @@ -77,7 +77,7 @@ 'dependencies': [ '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', ], - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'sources': [ 'metrics_library_test.cc', ] @@ -85,7 +85,7 @@ { 'target_name': 'timer_test', 'type': 'executable', - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'sources': [ 'timer.cc', 'timer_test.cc', @@ -101,7 +101,7 @@ 'dependencies': [ 'libmetrics_daemon', ], - 'includes': ['../common-mk/common_test.gypi'], + 'includes': ['../../platform2/common-mk/common_test.gypi'], 'sources': [ 'metrics_daemon_test.cc', ] From a756f7e66f1d82d64f39662fb9a5b9577d37d1bc Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Mon, 2 Jun 2014 14:12:02 -0700 Subject: [PATCH 139/200] metrics: Update gyp files to use libchromeos from src/platform2 libchromeos was moved from src/platform to src/platform2. This CL updates the gyp files to use the new location. BUG=chromium:370258 TEST=FEATURES=test emerge-amd64-generic platform2 TEST=trybot run on daisy, link, mario and duck CQ-DEPEND=CL:202334 Change-Id: I939dd5672120d47e8dc388f75b3dfeeb234c47b7 Reviewed-on: https://chromium-review.googlesource.com/202398 Reviewed-by: Prathmesh Prabhu Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- metrics/libmetrics.gypi | 2 +- metrics/metrics.gyp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/libmetrics.gypi b/metrics/libmetrics.gypi index 21af05d4c..bc000a481 100644 --- a/metrics/libmetrics.gypi +++ b/metrics/libmetrics.gypi @@ -1,7 +1,7 @@ { 'target_defaults': { 'dependencies': [ - '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', ], 'variables': { 'deps': [ diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 4a0b11a1a..c5bfedb6a 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -4,7 +4,7 @@ }, 'target_defaults': { 'dependencies': [ - '../libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', + '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', ], 'variables': { 'deps': [ From 963601989221ef8aaff56fde22d3353794abe12b Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Wed, 4 Jun 2014 10:53:35 -0700 Subject: [PATCH 140/200] metrics_daemon: add zram stats collection Memory compression stats are being collected by Chrome, but it is more natural to do it here since they are system-wide rather than Chrome-specific. In addition, this provides better granularity for the compression ratio (percents, from 100% to 600%) since we're especially interested in the distribution of values between 1 and 2, and currently these all fall in the same bucket. Finally, we collect more interesting stats on zero pages. BUG=chromium:315113 TEST=unit testing, checked about:histograms Change-Id: I09c974989661d42f45d44afd428e8114e4ee1dbd Reviewed-on: https://chromium-review.googlesource.com/202587 Reviewed-by: Luigi Semenzato Commit-Queue: Luigi Semenzato Tested-by: Luigi Semenzato --- metrics/metrics_daemon.cc | 63 +++++++++++++++++++++++++- metrics/metrics_daemon.h | 15 +++++++ metrics/metrics_daemon_test.cc | 81 +++++++++++++++++++++++----------- 3 files changed, 133 insertions(+), 26 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index e3f6bf7e7..9b66877a1 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -111,6 +111,12 @@ const int MetricsDaemon::kMetricsProcStatFirstLineItemsCount = 11; const char MetricsDaemon::kMetricScaledCpuFrequencyName[] = "Platform.CpuFrequencyThermalScaling"; +// Zram sysfs entries. + +const char MetricsDaemon::kComprDataSizeName[] = "compr_data_size"; +const char MetricsDaemon::kOrigDataSizeName[] = "orig_data_size"; +const char MetricsDaemon::kZeroPagesName[] = "zero_pages"; + // Memory use stats collection intervals. We collect some memory use interval // at these intervals after boot, and we stop collecting after the last one, // with the assumption that in most cases the memory use won't change much @@ -737,7 +743,62 @@ bool MetricsDaemon::MeminfoCallback() { LOG(WARNING) << "cannot read " << meminfo_path.value().c_str(); return false; } - return ProcessMeminfo(meminfo_raw); + // Make both calls even if the first one fails. + bool success = ProcessMeminfo(meminfo_raw); + return ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) && + success; +} + +// static +bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path, + uint64* value) { + std::string content; + if (!base::ReadFileToString(path, &content)) { + PLOG(WARNING) << "cannot read " << path.MaybeAsASCII(); + return false; + } + if (!base::StringToUint64(content, value)) { + LOG(WARNING) << "invalid integer: " << content; + return false; + } + return true; +} + +bool MetricsDaemon::ReportZram(const base::FilePath& zram_dir) { + // Data sizes are in bytes. |zero_pages| is in number of pages. + uint64 compr_data_size, orig_data_size, zero_pages; + const size_t page_size = 4096; + + if (!ReadFileToUint64(zram_dir.Append(kComprDataSizeName), + &compr_data_size) || + !ReadFileToUint64(zram_dir.Append(kOrigDataSizeName), &orig_data_size) || + !ReadFileToUint64(zram_dir.Append(kZeroPagesName), &zero_pages)) { + return false; + } + + // |orig_data_size| does not include zero-filled pages. + orig_data_size += zero_pages * page_size; + + const int compr_data_size_mb = compr_data_size >> 20; + const int savings_mb = (orig_data_size - compr_data_size) >> 20; + const int zero_ratio_percent = zero_pages * page_size * 100 / orig_data_size; + + // Report compressed size in megabytes. 100 MB or less has little impact. + SendSample("Platform.ZramCompressedSize", compr_data_size_mb, 100, 4000, 50); + SendSample("Platform.ZramSavings", savings_mb, 100, 4000, 50); + // The compression ratio is multiplied by 100 for better resolution. The + // ratios of interest are between 1 and 6 (100% and 600% as reported). We + // don't want samples when very little memory is being compressed. + if (compr_data_size_mb >= 1) { + SendSample("Platform.ZramCompressionRatioPercent", + orig_data_size * 100 / compr_data_size, 100, 600, 50); + } + // The values of interest for zero_pages are between 1MB and 1GB. The units + // are number of pages. + SendSample("Platform.ZramZeroPages", zero_pages, 256, 256 * 1024, 50); + SendSample("Platform.ZramZeroRatioPercent", zero_ratio_percent, 1, 50, 50); + + return true; } bool MetricsDaemon::ProcessMeminfo(const string& meminfo_raw) { diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 81e53865d..df85a2fc6 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -36,6 +36,12 @@ class MetricsDaemon { // forking. void Run(bool run_as_daemon); + protected: + // Used also by the unit tests. + static const char kComprDataSizeName[]; + static const char kOrigDataSizeName[]; + static const char kZeroPagesName[]; + private: friend class MetricsDaemonTest; FRIEND_TEST(MetricsDaemonTest, CheckSystemCrash); @@ -59,6 +65,7 @@ class MetricsDaemon { FRIEND_TEST(MetricsDaemonTest, ReportUserCrashInterval); FRIEND_TEST(MetricsDaemonTest, SendSample); FRIEND_TEST(MetricsDaemonTest, SendCpuThrottleMetrics); + FRIEND_TEST(MetricsDaemonTest, SendZramMetrics); // State for disk stats collector callback. enum StatsState { @@ -270,6 +277,14 @@ class MetricsDaemon { // Invoked periodically by |update_stats_timeout_id_| to call UpdateStats(). static gboolean HandleUpdateStatsTimeout(gpointer data); + // Reports zram statistics. + bool ReportZram(const base::FilePath& zram_dir); + + // Reads a string from a file and converts it to uint64. + static bool ReadFileToUint64(const base::FilePath& path, uint64* value); + + // VARIABLES + // Test mode. bool testing_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 33d0d1122..3d5f5d77a 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -30,8 +31,7 @@ using ::testing::Return; using ::testing::StrictMock; using chromeos_metrics::PersistentIntegerMock; -static const char kTestDir[] = "test"; -static const char kFakeDiskStatsPath[] = "fake-disk-stats"; +static const char kFakeDiskStatsName[] = "fake-disk-stats"; static const char kFakeDiskStatsFormat[] = " 1793 1788 %d 105580 " " 196 175 %d 30290 " @@ -40,7 +40,7 @@ static string kFakeDiskStats[2]; static const int kFakeReadSectors[] = {80000, 100000}; static const int kFakeWriteSectors[] = {3000, 4000}; -static const char kFakeVmStatsPath[] = "fake-vm-stats"; +static const char kFakeVmStatsName[] = "fake-vm-stats"; static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq"; static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq"; @@ -54,16 +54,13 @@ class MetricsDaemonTest : public testing::Test { kFakeReadSectors[1], kFakeWriteSectors[1]); CreateFakeDiskStatsFile(kFakeDiskStats[0].c_str()); - CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, 10000000); - CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 10000000); + CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 10000000); + CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 10000000); chromeos_metrics::PersistentInteger::SetTestingMode(true); - daemon_.Init(true, &metrics_lib_, kFakeDiskStatsPath, kFakeVmStatsPath, + daemon_.Init(true, &metrics_lib_, kFakeDiskStatsName, kFakeVmStatsName, kFakeScalingMaxFreqPath, kFakeCpuinfoMaxFreqPath); - base::DeleteFile(FilePath(kTestDir), true); - base::CreateDirectory(FilePath(kTestDir)); - // Replace original persistent values with mock ones. daily_active_use_mock_ = new StrictMock("1.mock"); @@ -84,7 +81,7 @@ class MetricsDaemonTest : public testing::Test { } virtual void TearDown() { - EXPECT_EQ(0, unlink(kFakeDiskStatsPath)); + EXPECT_EQ(0, unlink(kFakeDiskStatsName)); EXPECT_EQ(0, unlink(kFakeScalingMaxFreqPath)); EXPECT_EQ(0, unlink(kFakeCpuinfoMaxFreqPath)); } @@ -157,23 +154,22 @@ class MetricsDaemonTest : public testing::Test { // Creates or overwrites an input file containing fake disk stats. void CreateFakeDiskStatsFile(const char* fake_stats) { - if (unlink(kFakeDiskStatsPath) < 0) { + if (unlink(kFakeDiskStatsName) < 0) { EXPECT_EQ(errno, ENOENT); } - FILE* f = fopen(kFakeDiskStatsPath, "w"); + FILE* f = fopen(kFakeDiskStatsName, "w"); EXPECT_EQ(1, fwrite(fake_stats, strlen(fake_stats), 1, f)); EXPECT_EQ(0, fclose(f)); } - // Creates or overwrites an input file containing a fake CPU frequency. - void CreateFakeCpuFrequencyFile(const char* filename, int frequency) { - FilePath path(filename); + // Creates or overwrites the file in |path| so that it contains the printable + // representation of |value|. + void CreateUint64ValueFile(const base::FilePath& path, uint64 value) { base::DeleteFile(path, false); - std::string frequency_string = StringPrintf("%d\n", frequency); - int frequency_string_length = frequency_string.length(); - EXPECT_EQ(frequency_string.length(), - base::WriteFile(path, frequency_string.c_str(), - frequency_string_length)); + std::string value_string = base::Uint64ToString(value); + ASSERT_EQ(value_string.length(), + base::WriteFile(path, value_string.c_str(), + value_string.length())); } // The MetricsDaemon under test. @@ -351,8 +347,9 @@ TEST_F(MetricsDaemonTest, ReadFreqToInt) { const int fake_max_freq = 2000000; int scaled_freq = 0; int max_freq = 0; - CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, fake_scaled_freq); - CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, fake_max_freq); + CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), + fake_scaled_freq); + CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), fake_max_freq); EXPECT_TRUE(daemon_.testing_); EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeScalingMaxFreqPath, &scaled_freq)); EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeCpuinfoMaxFreqPath, &max_freq)); @@ -361,17 +358,51 @@ TEST_F(MetricsDaemonTest, ReadFreqToInt) { } TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) { - CreateFakeCpuFrequencyFile(kFakeCpuinfoMaxFreqPath, 2001000); + CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 2001000); // Test the 101% and 100% cases. - CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 2001000); + CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2001000); EXPECT_TRUE(daemon_.testing_); EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101)); daemon_.SendCpuThrottleMetrics(); - CreateFakeCpuFrequencyFile(kFakeScalingMaxFreqPath, 2000000); + CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2000000); EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 100, 101)); daemon_.SendCpuThrottleMetrics(); } +TEST_F(MetricsDaemonTest, SendZramMetrics) { + EXPECT_TRUE(daemon_.testing_); + + // |compr_data_size| is the size in bytes of compressed data. + const uint64 compr_data_size = 50 * 1000 * 1000; + // The constant '3' is a realistic but random choice. + // |orig_data_size| does not include zero pages. + const uint64 orig_data_size = compr_data_size * 3; + const uint64 page_size = 4096; + const uint64 zero_pages = 10 * 1000 * 1000 / page_size; + + CreateUint64ValueFile(base::FilePath(MetricsDaemon::kComprDataSizeName), + compr_data_size); + CreateUint64ValueFile(base::FilePath(MetricsDaemon::kOrigDataSizeName), + orig_data_size); + CreateUint64ValueFile(base::FilePath(MetricsDaemon::kZeroPagesName), + zero_pages); + + const uint64 real_orig_size = orig_data_size + zero_pages * page_size; + const uint64 zero_ratio_percent = + zero_pages * page_size * 100 / real_orig_size; + // Ratio samples are in percents. + const uint64 actual_ratio_sample = real_orig_size * 100 / compr_data_size; + + EXPECT_CALL(metrics_lib_, SendToUMA(_, compr_data_size >> 20, _, _, _)); + EXPECT_CALL(metrics_lib_, + SendToUMA(_, (real_orig_size - compr_data_size) >> 20, _, _, _)); + EXPECT_CALL(metrics_lib_, SendToUMA(_, actual_ratio_sample, _, _, _)); + EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_pages, _, _, _)); + EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_ratio_percent, _, _, _)); + + EXPECT_TRUE(daemon_.ReportZram(base::FilePath("."))); +} + int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); // Some libchrome calls need this. From 4764f52d9caf455c617ea574ca24603ca615df03 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 4 Jun 2014 13:08:52 -0700 Subject: [PATCH 141/200] metrics: Migrate libchromeos to its own ebuild All platform2 packages should have their own ebuild that will be compiled independently. Packages should DEPEND on libchromeos and should not have a gyp dependency on libchromeos's gyp file anymore. BUG=chromium:381372 TEST=emerge-daisy libchromeos && emerge-daisy platform2 work TEST=trybot run on daisy, link, mario and duck. TEST=trybot run on lumpy-incremental-paladin. CQ-DEPEND=CL:202748 Change-Id: I0fe0732d47463e880b11d3d547e99dba0ac83ace Reviewed-on: https://chromium-review.googlesource.com/202771 Tested-by: Bertrand Simonnet Reviewed-by: Mike Frysinger Commit-Queue: Bertrand Simonnet --- metrics/libmetrics.gypi | 7 ++++--- metrics/metrics.gyp | 12 ++++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/metrics/libmetrics.gypi b/metrics/libmetrics.gypi index bc000a481..1753830ba 100644 --- a/metrics/libmetrics.gypi +++ b/metrics/libmetrics.gypi @@ -1,11 +1,9 @@ { 'target_defaults': { - 'dependencies': [ - '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', - ], 'variables': { 'deps': [ 'libchrome-<(libbase_ver)', + 'libchromeos-<(libbase_ver)', ] }, 'cflags_cc': [ @@ -19,6 +17,9 @@ 'cflags': [ '-fvisibility=default', ], + 'libraries+': [ + '-lpolicy-<(libbase_ver)', + ], 'sources': [ 'c_metrics_library.cc', 'metrics_library.cc', diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index c5bfedb6a..b8dccf644 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -3,9 +3,6 @@ 'libbase_ver': 271506, }, 'target_defaults': { - 'dependencies': [ - '../../platform2/libchromeos/libchromeos-<(libbase_ver).gyp:libchromeos-<(libbase_ver)', - ], 'variables': { 'deps': [ 'dbus-1', @@ -14,6 +11,7 @@ 'gobject-2.0', 'gthread-2.0', 'libchrome-<(libbase_ver)', + 'libchromeos-<(libbase_ver)', ] }, 'cflags_cc': [ @@ -26,6 +24,7 @@ 'type': 'static_library', 'dependencies': [ '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', + '<(platform_root)/system_api/system_api.gyp:system_api-headers', ], 'link_settings': { 'libraries': [ @@ -80,7 +79,12 @@ 'includes': ['../../platform2/common-mk/common_test.gypi'], 'sources': [ 'metrics_library_test.cc', - ] + ], + 'link_settings': { + 'libraries': [ + '-lpolicy-<(libbase_ver)', + ] + } }, { 'target_name': 'timer_test', From 0d4b551b5454bb1528acdc75430279e0d9d9923b Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Tue, 17 Jun 2014 18:15:59 -0700 Subject: [PATCH 142/200] metrics: Create a standalone ebuild for system_api system_api is required by a lot of platform2 packages. We need it to be in a standalone ebuild in order for other packages to depend on it. This package installs the common headers and compiles the protobufs into static libraries. BUG=chromium:386223 TEST=FEATURES=test emerge-amd64-generic platform2. TEST=trybot run on daisy, link, duck, x86-mario. TEST=trybot run on lumpy-incremental-release. CQ-DEPEND=CL:204594 Change-Id: If0b601576593855cbc21a6358977d6e313cf696e Reviewed-on: https://chromium-review.googlesource.com/204582 Reviewed-by: Mike Frysinger Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- metrics/metrics.gyp | 1 - 1 file changed, 1 deletion(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index b8dccf644..3df31ffa1 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -24,7 +24,6 @@ 'type': 'static_library', 'dependencies': [ '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', - '<(platform_root)/system_api/system_api.gyp:system_api-headers', ], 'link_settings': { 'libraries': [ From 6a26a0d4c69528ddd827357ecf2dc4bd95ee42d8 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Mon, 23 Jun 2014 20:02:04 -0400 Subject: [PATCH 143/200] clean up licenses/urls after merge We have a common LICENSE file for this repo, so use that. Update the URLs to use the new git paths. BUG=None TEST=build_packages passes for many boards CQ-DEPEND=CL:205370 Change-Id: Ia60071ad2bec9a90b913a4057a617c6916533df6 Reviewed-on: https://chromium-review.googlesource.com/204953 Reviewed-by: Bertrand Simonnet Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- metrics/LICENSE | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 metrics/LICENSE diff --git a/metrics/LICENSE b/metrics/LICENSE deleted file mode 100644 index d25149653..000000000 --- a/metrics/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 4a6c942d1d3bae7ba8dfe67e1487b66bc8c311c7 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Mon, 30 Jun 2014 18:12:28 -0700 Subject: [PATCH 144/200] metrics_daemon: fix parsing of zram sysfs The only real change is in ReadFileToUint64. The other changes appease the updated pre-commit checks. BUG=chromium:390334 TEST=ran manually, checked syslog, checked chrome://histograms Change-Id: I5ca10aab03697aa8fd64c5dd51ca64911e8d2a76 Reviewed-on: https://chromium-review.googlesource.com/206285 Tested-by: Luigi Semenzato Reviewed-by: Daniel Erat Commit-Queue: Luigi Semenzato --- metrics/metrics_daemon.cc | 19 +++++++++++++------ metrics/metrics_daemon.h | 19 ++++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 9b66877a1..e8f0c6c19 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -2,9 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "metrics_daemon.h" +// For PRIu64 in inttypes.h, used by scanf. TODO(semenzato): replace +// with libchromeos methods. +#define __STDC_FORMAT_MACROS + +#include "metrics/metrics_daemon.h" #include +#include #include #include #include @@ -153,7 +158,7 @@ double MetricsDaemon::GetActiveTime() { PLOG(WARNING) << "clock_gettime(CLOCK_MONOTONIC) failed"; return 0; } else { - return ts.tv_sec + ((double) ts.tv_nsec) / (1000 * 1000 * 1000); + return ts.tv_sec + static_cast(ts.tv_nsec) / (1000 * 1000 * 1000); } } @@ -456,8 +461,8 @@ void MetricsDaemon::ScheduleStatsCallback(int wait) { g_timeout_add_seconds(wait, StatsCallbackStatic, this); } -bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors, - long int* write_sectors) { +bool MetricsDaemon::DiskStatsReadStats(uint64* read_sectors, + uint64* write_sectors) { int nchars; int nitems; bool success = false; @@ -478,7 +483,7 @@ bool MetricsDaemon::DiskStatsReadStats(long int* read_sectors, LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in " << diskstats_path_; line[nchars] = '\0'; - nitems = sscanf(line, "%*d %*d %ld %*d %*d %*d %ld", + nitems = sscanf(line, "%*d %*d %" PRIu64 "d %*d %*d %*d %" PRIu64 "d", read_sectors, write_sectors); if (nitems == 2) { success = true; @@ -618,7 +623,7 @@ gboolean MetricsDaemon::StatsCallbackStatic(void* handle) { // Collects disk and vm stats alternating over a short and a long interval. void MetricsDaemon::StatsCallback() { - long int read_sectors_now, write_sectors_now; + uint64 read_sectors_now, write_sectors_now; struct VmstatRecord vmstats_now; double time_now = GetActiveTime(); double delta_time = time_now - stats_initial_time_; @@ -757,6 +762,8 @@ bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path, PLOG(WARNING) << "cannot read " << path.MaybeAsASCII(); return false; } + // Remove final newline. + base::TrimWhitespaceASCII(content, base::TRIM_TRAILING, &content); if (!base::StringToUint64(content, value)) { LOG(WARNING) << "invalid integer: " << content; return false; diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index df85a2fc6..9c234a132 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -2,25 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef METRICS_DAEMON_H_ -#define METRICS_DAEMON_H_ +#ifndef METRICS_METRICS_DAEMON_H_ +#define METRICS_METRICS_DAEMON_H_ #include #include #include +#include +#include #include #include #include #include // for FRIEND_TEST -#include "metrics_library.h" -#include "persistent_integer.h" +#include "metrics/metrics_library.h" +#include "metrics/persistent_integer.h" using chromeos_metrics::PersistentInteger; class MetricsDaemon { - public: MetricsDaemon(); ~MetricsDaemon(); @@ -206,7 +207,7 @@ class MetricsDaemon { void ScheduleStatsCallback(int wait); // Reads cumulative disk statistics from sysfs. Returns true for success. - bool DiskStatsReadStats(long int* read_sectors, long int* write_sectors); + bool DiskStatsReadStats(uint64* read_sectors, uint64* write_sectors); // Reads cumulative vm statistics from procfs. Returns true for success. bool VmStatsReadStats(struct VmstatRecord* stats); @@ -316,8 +317,8 @@ class MetricsDaemon { unsigned int memuse_interval_index_; // Contain the most recent disk and vm cumulative stats. - long int read_sectors_; - long int write_sectors_; + uint64 read_sectors_; + uint64 write_sectors_; struct VmstatRecord vmstats_; StatsState stats_state_; @@ -364,4 +365,4 @@ class MetricsDaemon { std::string cpuinfo_max_freq_path_; }; -#endif // METRICS_DAEMON_H_ +#endif // METRICS_METRICS_DAEMON_H_ From 46b49da5ad156911cf17aa79f373d90595063ba0 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 25 Jun 2014 14:38:07 -0700 Subject: [PATCH 145/200] Add an uploader to metrics_daemon Metrics_daemon will now upload the metrics to Chrome's backend when Chrome is not available. This uses //components/metrics from chrome to use its protobuf definition and the metrics common code. This functionality is not yet enabled. It will be once the end-to-end test is enabled. BUG=chromium:358283, chromium:364579 TEST=FEATURES=test emerge-amd64-generic metrics CQ-DEPEND=CL:205790 CQ-DEPEND=CL:206055 Change-Id: I87aaf7a2ac041581fa3ffd4ec61f73e933c00a52 Reviewed-on: https://chromium-review.googlesource.com/205810 Reviewed-by: Bertrand Simonnet Tested-by: Bertrand Simonnet Reviewed-by: Luigi Semenzato Commit-Queue: Bertrand Simonnet --- metrics/metrics.gyp | 88 ++++++- metrics/metrics_daemon.cc | 17 +- metrics/metrics_daemon.h | 10 +- metrics/metrics_daemon_main.cc | 31 ++- metrics/metrics_daemon_test.cc | 9 +- metrics/uploader/curl_sender.cc | 70 +++++ metrics/uploader/curl_sender.h | 34 +++ metrics/uploader/metrics_log.cc | 41 +++ metrics/uploader/metrics_log.h | 40 +++ .../mock/mock_system_profile_setter.h | 20 ++ metrics/uploader/mock/sender_mock.cc | 24 ++ metrics/uploader/mock/sender_mock.h | 48 ++++ metrics/uploader/sender.h | 17 ++ metrics/uploader/system_profile_cache.cc | 140 ++++++++++ metrics/uploader/system_profile_cache.h | 76 ++++++ metrics/uploader/system_profile_setter.h | 20 ++ metrics/uploader/upload_service.cc | 205 +++++++++++++++ metrics/uploader/upload_service.h | 137 ++++++++++ metrics/uploader/upload_service_test.cc | 243 ++++++++++++++++++ 19 files changed, 1256 insertions(+), 14 deletions(-) create mode 100644 metrics/uploader/curl_sender.cc create mode 100644 metrics/uploader/curl_sender.h create mode 100644 metrics/uploader/metrics_log.cc create mode 100644 metrics/uploader/metrics_log.h create mode 100644 metrics/uploader/mock/mock_system_profile_setter.h create mode 100644 metrics/uploader/mock/sender_mock.cc create mode 100644 metrics/uploader/mock/sender_mock.h create mode 100644 metrics/uploader/sender.h create mode 100644 metrics/uploader/system_profile_cache.cc create mode 100644 metrics/uploader/system_profile_cache.h create mode 100644 metrics/uploader/system_profile_setter.h create mode 100644 metrics/uploader/upload_service.cc create mode 100644 metrics/uploader/upload_service.h create mode 100644 metrics/uploader/upload_service_test.cc diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 3df31ffa1..044ff6673 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -12,6 +12,7 @@ 'gthread-2.0', 'libchrome-<(libbase_ver)', 'libchromeos-<(libbase_ver)', + 'libcurl', ] }, 'cflags_cc': [ @@ -24,6 +25,8 @@ 'type': 'static_library', 'dependencies': [ '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', + 'libupload_service', + 'metrics_proto', ], 'link_settings': { 'libraries': [ @@ -35,7 +38,8 @@ 'persistent_integer.cc', 'metrics_daemon.cc', 'metrics_daemon_main.cc', - ] + ], + 'include_dirs': ['.'], }, { 'target_name': 'metrics_client', @@ -47,6 +51,67 @@ 'metrics_client.cc', ] }, + { + 'target_name': 'libupload_service', + 'type': 'static_library', + 'dependencies': [ + 'metrics_proto' + ], + 'link_settings': { + 'libraries': [ + '-lgflags', + '-lvboot_host', + ], + }, + 'variables': { + 'exported_deps': [ + 'protobuf-lite', + ], + 'deps': [ + '<@(exported_deps)', + ], + }, + 'all_dependent_settings': { + 'variables': { + 'deps+': [ + '<@(exported_deps)', + ], + }, + }, + 'sources': [ + 'uploader/upload_service.cc', + 'uploader/metrics_log.cc', + 'uploader/system_profile_cache.cc', + 'uploader/curl_sender.cc', + 'components/metrics/chromeos/metric_sample.cc', + 'components/metrics/chromeos/serialization_utils.cc', + 'components/metrics/metrics_log_base.cc', + 'components/metrics/metrics_log_manager.cc', + 'components/metrics/metrics_hashes.cc', + ], + 'include_dirs': ['.'] + }, + { + 'target_name': 'metrics_proto', + 'type': 'static_library', + 'variables': { + 'proto_in_dir': 'components/metrics/proto/', + 'proto_out_dir': 'include/components/metrics/proto', + }, + 'sources': [ + '<(proto_in_dir)/chrome_user_metrics_extension.proto', + '<(proto_in_dir)/histogram_event.proto', + '<(proto_in_dir)/omnibox_event.proto', + '<(proto_in_dir)/perf_data.proto', + '<(proto_in_dir)/profiler_event.proto', + '<(proto_in_dir)/sampled_profile.proto', + '<(proto_in_dir)/system_profile.proto', + '<(proto_in_dir)/user_action_event.proto', + ], + 'includes': [ + '../../platform2/common-mk/protoc.gypi' + ], + }, ], 'conditions': [ ['USE_passive_metrics == 1', { @@ -94,6 +159,22 @@ 'timer_test.cc', ] }, + { + 'target_name': 'upload_service_test', + 'type': 'executable', + 'sources': [ + 'persistent_integer.cc', + 'uploader/mock/sender_mock.cc', + 'uploader/upload_service_test.cc', + ], + 'dependencies': [ + 'libupload_service', + ], + 'includes':[ + '../../platform2/common-mk/common_test.gypi', + ], + 'include_dirs': ['.'] + }, ], }], ['USE_passive_metrics == 1 and USE_test == 1', { @@ -107,9 +188,10 @@ 'includes': ['../../platform2/common-mk/common_test.gypi'], 'sources': [ 'metrics_daemon_test.cc', - ] + ], + 'include_dirs': ['.'], }, ], }], - ], + ] } diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index e8f0c6c19..b9212fb0e 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -14,7 +14,6 @@ #include #include -#include #include #include #include @@ -26,6 +25,7 @@ #include #include #include +#include "uploader/upload_service.h" using base::FilePath; using base::StringPrintf; @@ -163,8 +163,6 @@ double MetricsDaemon::GetActiveTime() { } void MetricsDaemon::Run(bool run_as_daemon) { - base::AtExitManager at_exit_manager; - if (run_as_daemon && daemon(0, 0) != 0) return; @@ -188,6 +186,10 @@ void MetricsDaemon::Run(bool run_as_daemon) { Loop(); } +void MetricsDaemon::RunUploaderTest() { + upload_service_->UploadEvent(); +} + uint32 MetricsDaemon::GetOsVersionHash() { static uint32 cached_version_hash = 0; static bool version_hash_is_cached = false; @@ -205,7 +207,9 @@ uint32 MetricsDaemon::GetOsVersionHash() { return cached_version_hash; } -void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, +void MetricsDaemon::Init(bool testing, + bool uploader_active, + MetricsLibraryInterface* metrics_lib, const string& diskstats_path, const string& vmstats_path, const string& scaling_max_freq_path, @@ -305,6 +309,11 @@ void MetricsDaemon::Init(bool testing, MetricsLibraryInterface* metrics_lib, update_stats_timeout_id_ = g_timeout_add(kUpdateStatsIntervalMs, &HandleUpdateStatsTimeout, this); + + if (uploader_active) { + upload_service_.reset(new UploadService()); + upload_service_->Init(); + } } void MetricsDaemon::Loop() { diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 9c234a132..2bd12b026 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -18,6 +18,7 @@ #include "metrics/metrics_library.h" #include "metrics/persistent_integer.h" +#include "uploader/upload_service.h" using chromeos_metrics::PersistentInteger; @@ -27,7 +28,9 @@ class MetricsDaemon { ~MetricsDaemon(); // Initializes. - void Init(bool testing, MetricsLibraryInterface* metrics_lib, + void Init(bool testing, + bool uploader_active, + MetricsLibraryInterface* metrics_lib, const std::string& diskstats_path, const std::string& vmstats_path, const std::string& cpuinfo_max_freq_path, @@ -37,6 +40,9 @@ class MetricsDaemon { // forking. void Run(bool run_as_daemon); + // Triggers an upload event and exit. (Used to test UploadService) + void RunUploaderTest(); + protected: // Used also by the unit tests. static const char kComprDataSizeName[]; @@ -363,6 +369,8 @@ class MetricsDaemon { std::string vmstats_path_; std::string scaling_max_freq_path_; std::string cpuinfo_max_freq_path_; + + scoped_ptr upload_service_; }; #endif // METRICS_METRICS_DAEMON_H_ diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index ffe74f2a7..8ecb885a1 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - +#include #include #include #include @@ -17,8 +17,19 @@ const char kScalingMaxFreqPath[] = const char kCpuinfoMaxFreqPath[] = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; +// TODO(bsimonnet): Change this to use base::CommandLine (crbug.com/375017) DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); +// The uploader is disabled by default on ChromeOS as Chrome is responsible for +// sending the metrics. +DEFINE_bool(uploader, false, "activate the uploader"); + +// Upload the metrics once and exit. (used for testing) +DEFINE_bool( + uploader_test, + false, + "run the uploader once and exit"); + // Returns the path to the disk stats in the sysfs. Returns the null string if // it cannot find the disk stats file. static @@ -51,11 +62,23 @@ int main(int argc, char** argv) { // Also log to stderr when not running as daemon. chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader | (FLAGS_daemon ? 0 : chromeos::kLogToStderr)); - MetricsLibrary metrics_lib; metrics_lib.Init(); MetricsDaemon daemon; - daemon.Init(false, &metrics_lib, MetricsMainDiskStatsPath(), - "/proc/vmstat", kScalingMaxFreqPath, kCpuinfoMaxFreqPath); + daemon.Init(false, + FLAGS_uploader | FLAGS_uploader_test, + &metrics_lib, + MetricsMainDiskStatsPath(), + "/proc/vmstat", + kScalingMaxFreqPath, + kCpuinfoMaxFreqPath); + + + base::AtExitManager at_exit_manager; + if (FLAGS_uploader_test) { + daemon.RunUploaderTest(); + return 0; + } + daemon.Run(FLAGS_daemon); } diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 3d5f5d77a..3973ecbc7 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -58,8 +58,13 @@ class MetricsDaemonTest : public testing::Test { CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 10000000); chromeos_metrics::PersistentInteger::SetTestingMode(true); - daemon_.Init(true, &metrics_lib_, kFakeDiskStatsName, kFakeVmStatsName, - kFakeScalingMaxFreqPath, kFakeCpuinfoMaxFreqPath); + daemon_.Init(true, + false, + &metrics_lib_, + kFakeDiskStatsName, + kFakeVmStatsName, + kFakeScalingMaxFreqPath, + kFakeCpuinfoMaxFreqPath); // Replace original persistent values with mock ones. daily_active_use_mock_ = diff --git a/metrics/uploader/curl_sender.cc b/metrics/uploader/curl_sender.cc new file mode 100644 index 000000000..023b5f0f1 --- /dev/null +++ b/metrics/uploader/curl_sender.cc @@ -0,0 +1,70 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "uploader/curl_sender.h" + +#include +#include + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" + +CurlSender::CurlSender(const std::string server_url) + : server_url_(server_url) {} + +bool CurlSender::Send(const std::string& content, + const std::string& content_hash) { + CURL* postrequest = curl_easy_init(); + + if (!postrequest) { + DLOG(ERROR) << "Error creating the post request"; + return false; + } + + curl_easy_setopt(postrequest, CURLOPT_URL, server_url_.c_str()); + curl_easy_setopt(postrequest, CURLOPT_POST, 1); + + const std::string hash = + base::HexEncode(content_hash.data(), content_hash.size()); + + curl_slist* headers = + curl_slist_append(NULL, ("X-Chrome-UMA-Log-SHA1: " + hash).c_str()); + if (!headers) { + DLOG(ERROR) << "failed setting the headers"; + curl_easy_cleanup(postrequest); + return false; + } + + std::string output; + + curl_easy_setopt(postrequest, CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(postrequest, CURLOPT_POSTFIELDSIZE, content.size()); + curl_easy_setopt(postrequest, CURLOPT_POSTFIELDS, content.c_str()); + + // Set the callback function used to read the response and the destination. + curl_easy_setopt(postrequest, CURLOPT_WRITEFUNCTION, ReadData); + curl_easy_setopt(postrequest, CURLOPT_WRITEDATA, &output); + + CURLcode result = curl_easy_perform(postrequest); + + if (result == CURLE_OK && output == "OK") { + curl_easy_cleanup(postrequest); + return true; + } + + curl_easy_cleanup(postrequest); + + return false; +} + +// static +size_t CurlSender::ReadData(void* buffer, size_t size, size_t nmember, + std::string* out) { + CHECK(out); + + // This function might be called several time so we want to append the data at + // the end of the string. + *out += std::string(static_cast(buffer), size * nmember); + return size * nmember; +} diff --git a/metrics/uploader/curl_sender.h b/metrics/uploader/curl_sender.h new file mode 100644 index 000000000..011360139 --- /dev/null +++ b/metrics/uploader/curl_sender.h @@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_CURL_SENDER_H_ +#define METRICS_UPLOADER_CURL_SENDER_H_ + +#include + +#include "base/compiler_specific.h" +#include "uploader/sender.h" + +// Sender implemented using libcurl +class CurlSender : public Sender { + public: + explicit CurlSender(std::string server_url); + + // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous + // POST request to server_url. + virtual bool Send(const std::string& content, + const std::string& hash) OVERRIDE; + + // Static callback required by curl to retrieve the response data. + // + // Copies |size| * |nmember| bytes of data from |buffer| to |out|. + // Returns the number of bytes copied. + static size_t ReadData(void* buffer, size_t size, size_t nmember, + std::string* out); + + private: + const std::string server_url_; +}; + +#endif // METRICS_UPLOADER_CURL_SENDER_H_ diff --git a/metrics/uploader/metrics_log.cc b/metrics/uploader/metrics_log.cc new file mode 100644 index 000000000..c3aa69e18 --- /dev/null +++ b/metrics/uploader/metrics_log.cc @@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "uploader/metrics_log.h" + +#include + +#include "components/metrics/proto/system_profile.pb.h" +#include "uploader/system_profile_setter.h" + +// We use default values for the MetricsLogBase constructor as the setter will +// override them. +MetricsLog::MetricsLog() + : MetricsLogBase("", 0, metrics::MetricsLogBase::ONGOING_LOG, "") { +} + +void MetricsLog::IncrementUserCrashCount() { + metrics::SystemProfileProto::Stability* stability( + uma_proto()->mutable_system_profile()->mutable_stability()); + int current = stability->other_user_crash_count(); + stability->set_other_user_crash_count(current + 1); +} + +void MetricsLog::IncrementKernelCrashCount() { + metrics::SystemProfileProto::Stability* stability( + uma_proto()->mutable_system_profile()->mutable_stability()); + int current = stability->kernel_crash_count(); + stability->set_kernel_crash_count(current + 1); +} + +void MetricsLog::IncrementUncleanShutdownCount() { + metrics::SystemProfileProto::Stability* stability( + uma_proto()->mutable_system_profile()->mutable_stability()); + int current = stability->unclean_system_shutdown_count(); + stability->set_unclean_system_shutdown_count(current + 1); +} + +void MetricsLog::PopulateSystemProfile(SystemProfileSetter* profile_setter) { + profile_setter->Populate(uma_proto()); +} diff --git a/metrics/uploader/metrics_log.h b/metrics/uploader/metrics_log.h new file mode 100644 index 000000000..bd1958cbf --- /dev/null +++ b/metrics/uploader/metrics_log.h @@ -0,0 +1,40 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_METRICS_LOG_H_ +#define METRICS_UPLOADER_METRICS_LOG_H_ + +#include + +#include "components/metrics/metrics_log_base.h" + +// This file defines a set of user experience metrics data recorded by +// the MetricsService. This is the unit of data that is sent to the server. +class SystemProfileSetter; + +// This class provides base functionality for logging metrics data. +class MetricsLog : public metrics::MetricsLogBase { + public: + // The constructor doesn't set any metadata. The metadata is only set by a + // SystemProfileSetter. + MetricsLog(); + + void IncrementUserCrashCount(); + void IncrementKernelCrashCount(); + void IncrementUncleanShutdownCount(); + + // Populate the system profile with system information using setter. + void PopulateSystemProfile(SystemProfileSetter* setter); + + private: + FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues); + FRIEND_TEST(UploadServiceTest, LogKernelCrash); + FRIEND_TEST(UploadServiceTest, LogUncleanShutdown); + FRIEND_TEST(UploadServiceTest, LogUserCrash); + FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored); + + DISALLOW_COPY_AND_ASSIGN(MetricsLog); +}; + +#endif // METRICS_UPLOADER_METRICS_LOG_H_ diff --git a/metrics/uploader/mock/mock_system_profile_setter.h b/metrics/uploader/mock/mock_system_profile_setter.h new file mode 100644 index 000000000..0614eb913 --- /dev/null +++ b/metrics/uploader/mock/mock_system_profile_setter.h @@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_ +#define METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_ + +#include "uploader/system_profile_setter.h" + +namespace metrics { +class ChromeUserMetricsExtension; +} + +// Mock profile setter used for testing. +class MockSystemProfileSetter : public SystemProfileSetter { + public: + void Populate(metrics::ChromeUserMetricsExtension* profile_proto) OVERRIDE {} +}; + +#endif // METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_ diff --git a/metrics/uploader/mock/sender_mock.cc b/metrics/uploader/mock/sender_mock.cc new file mode 100644 index 000000000..064ec6d01 --- /dev/null +++ b/metrics/uploader/mock/sender_mock.cc @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "uploader/mock/sender_mock.h" + +SenderMock::SenderMock() { + Reset(); +} + +bool SenderMock::Send(const std::string& content, const std::string& hash) { + send_call_count_ += 1; + last_message_ = content; + is_good_proto_ = last_message_proto_.ParseFromString(content); + return should_succeed_; +} + +void SenderMock::Reset() { + send_call_count_ = 0; + last_message_ = ""; + should_succeed_ = true; + last_message_proto_.Clear(); + is_good_proto_ = false; +} diff --git a/metrics/uploader/mock/sender_mock.h b/metrics/uploader/mock/sender_mock.h new file mode 100644 index 000000000..f6f19b9a2 --- /dev/null +++ b/metrics/uploader/mock/sender_mock.h @@ -0,0 +1,48 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_MOCK_SENDER_MOCK_H_ +#define METRICS_UPLOADER_MOCK_SENDER_MOCK_H_ + +#include + +#include "base/compiler_specific.h" +#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" +#include "uploader/sender.h" + +class SenderMock : public Sender { + public: + SenderMock(); + + bool Send(const std::string& content, const std::string& hash) OVERRIDE; + void Reset(); + + bool is_good_proto() { return is_good_proto_; } + int send_call_count() { return send_call_count_; } + const std::string last_message() { return last_message_; } + metrics::ChromeUserMetricsExtension last_message_proto() { + return last_message_proto_; + } + void set_should_succeed(bool succeed) { should_succeed_ = succeed; } + + private: + // Is set to true if the proto was parsed successfully. + bool is_good_proto_; + + // If set to true, the Send method will return true to simulate a successful + // send. + bool should_succeed_; + + // Count of how many times Send was called since the last reset. + int send_call_count_; + + // Last message received by Send. + std::string last_message_; + + // If is_good_proto is true, last_message_proto is the deserialized + // representation of last_message. + metrics::ChromeUserMetricsExtension last_message_proto_; +}; + +#endif // METRICS_UPLOADER_MOCK_SENDER_MOCK_H_ diff --git a/metrics/uploader/sender.h b/metrics/uploader/sender.h new file mode 100644 index 000000000..70d29cb7e --- /dev/null +++ b/metrics/uploader/sender.h @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_SENDER_H_ +#define METRICS_UPLOADER_SENDER_H_ + +#include + +// Abstract class for a Sender that uploads a metrics message. +class Sender { + public: + // Sends a message |content| with its sha1 hash |hash| + virtual bool Send(const std::string& content, const std::string& hash) = 0; +}; + +#endif // METRICS_UPLOADER_SENDER_H_ diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc new file mode 100644 index 000000000..0090d38da --- /dev/null +++ b/metrics/uploader/system_profile_cache.cc @@ -0,0 +1,140 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "uploader/system_profile_cache.h" + +#include +#include +#include + +#include "base/file_util.h" +#include "base/guid.h" +#include "base/logging.h" +#include "base/sys_info.h" +#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" +#include "components/metrics/metrics_log_base.h" +#include "metrics/persistent_integer.h" +#include "vboot/crossystem.h" + +const char* SystemProfileCache::kPersistentGUIDFile = + "/var/lib/metrics/Sysinfo.GUID"; +const char* SystemProfileCache::kPersistentSessionIdFilename = + "Sysinfo.SessionId"; + +SystemProfileCache::SystemProfileCache() + : initialized_(false), + is_testing_(false), + session_id_(new chromeos_metrics::PersistentInteger( + kPersistentSessionIdFilename)) { +} + +bool SystemProfileCache::Initialize() { + CHECK(!initialized_) + << "this should be called only once in the metrics_daemon lifetime."; + + if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_NAME", + &profile_.os_name) || + !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION", + &profile_.os_version) || + !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_DESCRIPTION", + &profile_.app_version) || + !GetHardwareId(&profile_.hardware_class)) { + DLOG(ERROR) << "failing to initialize profile cache"; + return false; + } + + std::string channel_string; + base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &channel_string); + profile_.channel = ProtoChannelFromString(channel_string); + + profile_.client_id = + is_testing_ ? "client_id_test" : GetPersistentGUID(kPersistentGUIDFile); + + // Increment the session_id everytime we initialize this. If metrics_daemon + // does not crash, this should correspond to the number of reboots of the + // system. + // TODO(bsimonnet): Change this to map to the number of time system-services + // is started. + session_id_->Add(1); + profile_.session_id = static_cast(session_id_->Get()); + + initialized_ = true; + return initialized_; +} + +bool SystemProfileCache::InitializeOrCheck() { + return initialized_ || Initialize(); +} + +void SystemProfileCache::Populate( + metrics::ChromeUserMetricsExtension* metrics_proto) { + CHECK(metrics_proto); + CHECK(InitializeOrCheck()) + << "failed to initialize system information."; + + // The client id is hashed before being sent. + metrics_proto->set_client_id( + metrics::MetricsLogBase::Hash(profile_.client_id)); + metrics_proto->set_session_id(profile_.session_id); + + metrics::SystemProfileProto* profile_proto = + metrics_proto->mutable_system_profile(); + profile_proto->mutable_hardware()->set_hardware_class( + profile_.hardware_class); + profile_proto->set_app_version(profile_.app_version); + profile_proto->set_channel(profile_.channel); + + metrics::SystemProfileProto_OS* os = profile_proto->mutable_os(); + os->set_name(profile_.os_name); + os->set_version(profile_.os_version); +} + +std::string SystemProfileCache::GetPersistentGUID(const std::string& filename) { + std::string guid; + base::FilePath filepath(filename); + if (!base::ReadFileToString(filepath, &guid)) { + guid = base::GenerateGUID(); + // If we can't read or write the file, the guid will not be preserved during + // the next reboot. Crash. + CHECK(base::WriteFile(filepath, guid.c_str(), guid.size())); + } + return guid; +} + +bool SystemProfileCache::GetHardwareId(std::string* hwid) { + CHECK(hwid); + + if (is_testing_) { + // if we are in test mode, we do not call crossystem directly. + DLOG(INFO) << "skipping hardware id"; + *hwid = ""; + return true; + } + + char buffer[128]; + if (buffer != VbGetSystemPropertyString("hwid", buffer, sizeof(buffer))) { + LOG(ERROR) << "error getting hwid"; + return false; + } + + *hwid = std::string(buffer); + return true; +} + +metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString( + const std::string& channel) { + + if (channel == "stable-channel") { + return metrics::SystemProfileProto::CHANNEL_STABLE; + } else if (channel == "dev-channel") { + return metrics::SystemProfileProto::CHANNEL_DEV; + } else if (channel == "beta-channel") { + return metrics::SystemProfileProto::CHANNEL_BETA; + } else if (channel == "canary-channel") { + return metrics::SystemProfileProto::CHANNEL_CANARY; + } + + DLOG(INFO) << "unknown channel: " << channel; + return metrics::SystemProfileProto::CHANNEL_UNKNOWN; +} diff --git a/metrics/uploader/system_profile_cache.h b/metrics/uploader/system_profile_cache.h new file mode 100644 index 000000000..74a37662b --- /dev/null +++ b/metrics/uploader/system_profile_cache.h @@ -0,0 +1,76 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_ +#define METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_ + +#include + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "components/metrics/proto/system_profile.pb.h" +#include "metrics/persistent_integer.h" +#include "metrics/uploader/system_profile_setter.h" + +namespace metrics { +class ChromeUserMetricsExtension; +} + +struct SystemProfile { + std::string os_name; + std::string os_version; + metrics::SystemProfileProto::Channel channel; + std::string app_version; + std::string hardware_class; + std::string client_id; + int32 session_id; +}; + +// Retrieves general system informations needed by the protobuf for context and +// remembers them to avoid expensive calls. +// +// The cache is populated lazily. The only method needed is Populate. +class SystemProfileCache : public SystemProfileSetter { + public: + SystemProfileCache(); + + // Populates the ProfileSystem protobuf with system information. + void Populate(metrics::ChromeUserMetricsExtension* profile_proto) OVERRIDE; + + // Converts a string representation of the channel (|channel|-channel) to a + // SystemProfileProto_Channel + static metrics::SystemProfileProto_Channel ProtoChannelFromString( + const std::string& channel); + + // Gets the persistent GUID and create it if it has not been created yet. + static std::string GetPersistentGUID(const std::string& filename); + + private: + friend class UploadServiceTest; + FRIEND_TEST(UploadServiceTest, ExtractChannelFromDescription); + FRIEND_TEST(UploadServiceTest, ReadKeyValueFromFile); + FRIEND_TEST(UploadServiceTest, SessionIdIncrementedAtInitialization); + FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent); + + static const char* kPersistentGUIDFile; + static const char* kPersistentSessionIdFilename; + + // Fetches all informations and populates |profile_| + bool Initialize(); + + // Initializes |profile_| only if it has not been yet initialized. + bool InitializeOrCheck(); + + // Gets the hardware ID using crossystem + bool GetHardwareId(std::string* hwid); + + bool initialized_; + bool is_testing_; + scoped_ptr session_id_; + SystemProfile profile_; +}; + +#endif // METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_ diff --git a/metrics/uploader/system_profile_setter.h b/metrics/uploader/system_profile_setter.h new file mode 100644 index 000000000..1e2baab49 --- /dev/null +++ b/metrics/uploader/system_profile_setter.h @@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_ +#define METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_ + +namespace metrics { +class ChromeUserMetricsExtension; +} + +// Abstract class used to delegate populating SystemProfileProto with system +// information to simplify testing. +class SystemProfileSetter { + public: + // Populates the protobuf with system informations. + virtual void Populate(metrics::ChromeUserMetricsExtension* profile_proto) = 0; +}; + +#endif // METRICS_UPLOADER_SYSTEM_PROFILE_SETTER_H_ diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc new file mode 100644 index 000000000..5e05dcf18 --- /dev/null +++ b/metrics/uploader/upload_service.cc @@ -0,0 +1,205 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "uploader/upload_service.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/memory/scoped_vector.h" +#include "base/metrics/histogram.h" +#include "base/metrics/histogram_base.h" +#include "base/metrics/histogram_snapshot_manager.h" +#include "base/metrics/sparse_histogram.h" +#include "base/metrics/statistics_recorder.h" +#include "base/sha1.h" +#include "components/metrics/chromeos/serialization_utils.h" +#include "components/metrics/chromeos/metric_sample.h" +#include "gflags/gflags.h" +#include "uploader/curl_sender.h" +#include "uploader/metrics_log.h" +#include "uploader/system_profile_cache.h" + +DEFINE_int32( + upload_interval_secs, + 1800, + "Interval at which metrics_daemon sends the metrics. (needs -uploader)"); + +DEFINE_string(server, + "https://clients4.google.com/uma/v2", + "Server to upload the metrics to. (needs -uploader)"); + +DEFINE_string(metrics_file, + "/var/run/metrics/uma-events", + "File to use as a proxy for uploading the metrics"); + +const int UploadService::kMaxFailedUpload = 10; + +UploadService::UploadService() + : system_profile_setter_(new SystemProfileCache()), + histogram_snapshot_manager_(this), + sender_(new CurlSender(FLAGS_server)) { +} + +void UploadService::Init() { + base::StatisticsRecorder::Initialize(); + g_timeout_add_seconds(FLAGS_upload_interval_secs, &UploadEventStatic, this); +} + +void UploadService::StartNewLog() { + CHECK(!staged_log_) << "the staged log should be discarded before starting " + "a new metrics log"; + MetricsLog* log = new MetricsLog(); + log->PopulateSystemProfile(system_profile_setter_); + current_log_.reset(log); +} + +// static +int UploadService::UploadEventStatic(void* uploader) { + CHECK(uploader); + // This is called by glib with a pointer to an UploadEvent object. + static_cast(uploader)->UploadEvent(); + return 1; +} + +void UploadService::UploadEvent() { + if (staged_log_) { + // Previous upload failed, retry sending the logs. + SendStagedLog(); + return; + } + + // Previous upload successful, reading metrics sample from the file. + ReadMetrics(); + GatherHistograms(); + + // No samples found. Exit to avoid sending an empty log. + if (!current_log_) + return; + + StageCurrentLog(); + SendStagedLog(); +} + +void UploadService::SendStagedLog() { + CHECK(staged_log_) << "staged_log_ must exist to be sent"; + + std::string log_text; + staged_log_->GetEncodedLog(&log_text); + if (!sender_->Send(log_text, base::SHA1HashString(log_text))) { + ++failed_upload_count_; + if (failed_upload_count_ <= kMaxFailedUpload) { + LOG(WARNING) << "log upload failed " << failed_upload_count_ + << " times. It will be retried later."; + return; + } + LOG(WARNING) << "log failed more than " << kMaxFailedUpload << " times."; + } + // Discard staged log. + staged_log_.reset(); +} + +void UploadService::Reset() { + staged_log_.reset(); + current_log_.reset(); + failed_upload_count_ = 0; +} + +void UploadService::ReadMetrics() { + CHECK(!staged_log_) + << "cannot read metrics until the old logs have been discarded"; + + ScopedVector vector; + metrics::SerializationUtils::ReadAndTruncateMetricsFromFile( + FLAGS_metrics_file, &vector); + + int i = 0; + for (ScopedVector::iterator it = vector.begin(); + it != vector.end(); it++) { + metrics::MetricSample* sample = *it; + AddSample(*sample); + i++; + } + DLOG(INFO) << i << " samples read"; +} + +void UploadService::AddSample(const metrics::MetricSample& sample) { + base::HistogramBase* counter; + switch (sample.type()) { + case metrics::MetricSample::CRASH: + AddCrash(sample.name()); + break; + case metrics::MetricSample::HISTOGRAM: + counter = base::Histogram::FactoryGet( + sample.name(), sample.min(), sample.max(), sample.bucket_count(), + base::Histogram::kUmaTargetedHistogramFlag); + counter->Add(sample.sample()); + break; + case metrics::MetricSample::SPARSE_HISTOGRAM: + counter = base::SparseHistogram::FactoryGet( + sample.name(), base::HistogramBase::kUmaTargetedHistogramFlag); + counter->Add(sample.sample()); + break; + case metrics::MetricSample::LINEAR_HISTOGRAM: + counter = base::LinearHistogram::FactoryGet( + sample.name(), + 1, + sample.max(), + sample.max() + 1, + base::Histogram::kUmaTargetedHistogramFlag); + counter->Add(sample.sample()); + break; + case metrics::MetricSample::USER_ACTION: + GetOrCreateCurrentLog()->RecordUserAction(sample.name()); + break; + default: + break; + } +} + +void UploadService::AddCrash(const std::string& crash_name) { + if (crash_name == "user") { + GetOrCreateCurrentLog()->IncrementUserCrashCount(); + } else if (crash_name == "kernel") { + GetOrCreateCurrentLog()->IncrementKernelCrashCount(); + } else if (crash_name == "uncleanshutdown") { + GetOrCreateCurrentLog()->IncrementUncleanShutdownCount(); + } else { + DLOG(ERROR) << "crash name unknown" << crash_name; + } +} + +void UploadService::GatherHistograms() { + base::StatisticsRecorder::Histograms histograms; + base::StatisticsRecorder::GetHistograms(&histograms); + + histogram_snapshot_manager_.PrepareDeltas( + base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag); +} + +void UploadService::RecordDelta(const base::HistogramBase& histogram, + const base::HistogramSamples& snapshot) { + GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(), + snapshot); +} + +void UploadService::StageCurrentLog() { + CHECK(!staged_log_) + << "staged logs must be discarded before another log can be staged"; + + if (!current_log_) return; + + staged_log_.swap(current_log_); + staged_log_->CloseLog(); + failed_upload_count_ = 0; +} + +MetricsLog* UploadService::GetOrCreateCurrentLog() { + if (!current_log_) { + StartNewLog(); + } + return current_log_.get(); +} diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h new file mode 100644 index 000000000..7c1748869 --- /dev/null +++ b/metrics/uploader/upload_service.h @@ -0,0 +1,137 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_UPLOAD_SERVICE_H_ +#define METRICS_UPLOADER_UPLOAD_SERVICE_H_ + +#include + +#include "base/metrics/histogram_base.h" +#include "base/metrics/histogram_flattener.h" +#include "base/metrics/histogram_snapshot_manager.h" +#include "uploader/metrics_log.h" +#include "uploader/sender.h" + +namespace metrics { +class ChromeUserMetricsExtension; +class CrashSample; +class HistogramSample; +class LinearHistogramSample; +class MetricSample; +class SparseHistogramSample; +class UserActionSample; +} + +class SystemProfileSetter; + +// Service responsible for uploading the metrics periodically to the server. +// This service works as a simple 2-state state-machine. +// +// The two states are the presence or not of a staged log. +// A staged log is a compressed protobuffer containing both the aggregated +// metrics and event and information about the client. (product, hardware id, +// etc...). +// +// At regular intervals, the upload event will be triggered and the following +// will happen: +// * if a staged log is present: +// The previous upload may have failed for various reason. We then retry to +// upload the same log. +// - if the upload is successful, we discard the log (therefore +// transitioning back to no staged log) +// - if the upload fails, we keep the log to try again later. +// We do not try to read the metrics that are stored on +// the disk as we want to avoid storing the metrics in memory. +// +// * if no staged logs are present: +// Read all metrics from the disk, aggregate them and try to send them. +// - if the upload succeeds, we discard the staged log (transitioning back +// to the no staged log state) +// - if the upload fails, we keep the staged log in memory to retry +// uploading later. +// +class UploadService : public base::HistogramFlattener { + public: + UploadService(); + + void Init(); + + // Starts a new log. The log needs to be regenerated after each successful + // launch as it is destroyed when staging the log. + void StartNewLog(); + + // Glib takes a function pointer and passes the object as a void*. + // Uploader is expected to be an UploaderService. + static int UploadEventStatic(void* uploader); + + // Triggers an upload event. + void UploadEvent(); + + // Sends the staged log. + void SendStagedLog(); + + // Implements inconsistency detection to match HistogramFlattener's + // interface. + void InconsistencyDetected( + base::HistogramBase::Inconsistency problem) OVERRIDE {} + void UniqueInconsistencyDetected( + base::HistogramBase::Inconsistency problem) OVERRIDE {} + void InconsistencyDetectedInLoggedCount(int amount) OVERRIDE {} + + private: + friend class UploadServiceTest; + + FRIEND_TEST(UploadServiceTest, CanSendMultipleTimes); + FRIEND_TEST(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload); + FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent); + FRIEND_TEST(UploadServiceTest, FailedSendAreRetried); + FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues); + FRIEND_TEST(UploadServiceTest, LogEmptyAfterUpload); + FRIEND_TEST(UploadServiceTest, LogEmptyByDefault); + FRIEND_TEST(UploadServiceTest, LogKernelCrash); + FRIEND_TEST(UploadServiceTest, LogUncleanShutdown); + FRIEND_TEST(UploadServiceTest, LogUserCrash); + FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored); + FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent); + + // If a staged log fails to upload more than kMaxFailedUpload times, it + // will be discarded. + static const int kMaxFailedUpload; + + // Resets the internal state. + void Reset(); + + // Reads all the metrics from the disk. + void ReadMetrics(); + + // Adds a generic sample to the current log. + void AddSample(const metrics::MetricSample& sample); + + // Adds a crash to the current log. + void AddCrash(const std::string& crash_name); + + // Aggregates all histogram available in memory and store them in the current + // log. + void GatherHistograms(); + + // Callback for HistogramSnapshotManager to store the histograms. + void RecordDelta(const base::HistogramBase& histogram, + const base::HistogramSamples& snapshot) OVERRIDE; + + // Compiles all the samples received into a single protobuf and adds all + // system information. + void StageCurrentLog(); + + // Returns the current log. If there is no current log, creates it first. + MetricsLog* GetOrCreateCurrentLog(); + + SystemProfileSetter* system_profile_setter_; + base::HistogramSnapshotManager histogram_snapshot_manager_; + Sender* sender_; + int failed_upload_count_; + scoped_ptr current_log_; + scoped_ptr staged_log_; +}; + +#endif // METRICS_UPLOADER_UPLOAD_SERVICE_H_ diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc new file mode 100644 index 000000000..efc382ef4 --- /dev/null +++ b/metrics/uploader/upload_service_test.cc @@ -0,0 +1,243 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/at_exit.h" +#include "base/logging.h" +#include "base/file_util.h" +#include "base/files/scoped_temp_dir.h" +#include "base/sys_info.h" +#include "components/metrics/chromeos/metric_sample.h" +#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" +#include "components/metrics/proto/system_profile.pb.h" +#include "components/metrics/proto/histogram_event.pb.h" +#include "uploader/metrics_log.h" +#include "uploader/upload_service.h" +#include "uploader/mock/sender_mock.h" +#include "uploader/mock/mock_system_profile_setter.h" +#include "uploader/system_profile_cache.h" + +class UploadServiceTest : public testing::Test { + protected: + UploadServiceTest() + : upload_service_(), exit_manager_(new base::AtExitManager()) { + upload_service_.sender_ = &sender_; + upload_service_.system_profile_setter_ = new MockSystemProfileSetter(); + upload_service_.Init(); + } + + virtual void SetUp() { + CHECK(dir_.CreateUniqueTempDir()); + upload_service_.GatherHistograms(); + upload_service_.Reset(); + sender_.Reset(); + cache_.is_testing_ = true; + + chromeos_metrics::PersistentInteger::SetTestingMode(true); + cache_.session_id_.reset(new chromeos_metrics::PersistentInteger( + dir_.path().Append("session_id").value())); + } + + scoped_ptr Crash(const std::string& name) { + return metrics::MetricSample::CrashSample(name); + } + + base::ScopedTempDir dir_; + SenderMock sender_; + SystemProfileCache cache_; + UploadService upload_service_; + + scoped_ptr exit_manager_; +}; + +// Tests that the right crash increments a values. +TEST_F(UploadServiceTest, LogUserCrash) { + upload_service_.AddSample(*Crash("user").get()); + + MetricsLog* log = upload_service_.current_log_.get(); + metrics::ChromeUserMetricsExtension* proto = log->uma_proto(); + + EXPECT_EQ(1, proto->system_profile().stability().other_user_crash_count()); +} + +TEST_F(UploadServiceTest, LogUncleanShutdown) { + upload_service_.AddSample(*Crash("uncleanshutdown")); + + EXPECT_EQ(1, upload_service_.current_log_ + ->uma_proto() + ->system_profile() + .stability() + .unclean_system_shutdown_count()); +} + +TEST_F(UploadServiceTest, LogKernelCrash) { + upload_service_.AddSample(*Crash("kernel")); + + EXPECT_EQ(1, upload_service_.current_log_ + ->uma_proto() + ->system_profile() + .stability() + .kernel_crash_count()); +} + +TEST_F(UploadServiceTest, UnknownCrashIgnored) { + upload_service_.AddSample(*Crash("foo")); + + // The log should be empty. + EXPECT_FALSE(upload_service_.current_log_); +} + +TEST_F(UploadServiceTest, FailedSendAreRetried) { + sender_.set_should_succeed(false); + + upload_service_.AddSample(*Crash("user")); + upload_service_.UploadEvent(); + EXPECT_EQ(1, sender_.send_call_count()); + std::string sent_string = sender_.last_message(); + + upload_service_.UploadEvent(); + EXPECT_EQ(2, sender_.send_call_count()); + EXPECT_EQ(sent_string, sender_.last_message()); +} + +TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) { + sender_.set_should_succeed(false); + upload_service_.AddSample(*Crash("user")); + + for (int i = 0; i < UploadService::kMaxFailedUpload; i++) { + upload_service_.UploadEvent(); + } + + EXPECT_TRUE(upload_service_.staged_log_); + upload_service_.UploadEvent(); + EXPECT_FALSE(upload_service_.staged_log_); +} + +TEST_F(UploadServiceTest, EmptyLogsAreNotSent) { + upload_service_.UploadEvent(); + EXPECT_FALSE(upload_service_.current_log_); + EXPECT_EQ(0, sender_.send_call_count()); +} + +TEST_F(UploadServiceTest, LogEmptyByDefault) { + UploadService upload_service; + + // current_log_ should be initialized later as it needs AtExitManager to exit + // in order to gather system information from SysInfo. + EXPECT_FALSE(upload_service.current_log_); +} + +TEST_F(UploadServiceTest, CanSendMultipleTimes) { + upload_service_.AddSample(*Crash("user")); + upload_service_.UploadEvent(); + + std::string first_message = sender_.last_message(); + + upload_service_.AddSample(*Crash("kernel")); + upload_service_.UploadEvent(); + + EXPECT_NE(first_message, sender_.last_message()); +} + +TEST_F(UploadServiceTest, LogEmptyAfterUpload) { + upload_service_.AddSample(*Crash("user")); + + EXPECT_TRUE(upload_service_.current_log_); + + upload_service_.UploadEvent(); + EXPECT_FALSE(upload_service_.current_log_); +} + +TEST_F(UploadServiceTest, LogContainsAggregatedValues) { + scoped_ptr histogram = + metrics::MetricSample::HistogramSample("foo", 10, 0, 42, 10); + upload_service_.AddSample(*histogram.get()); + + + scoped_ptr histogram2 = + metrics::MetricSample::HistogramSample("foo", 11, 0, 42, 10); + upload_service_.AddSample(*histogram2.get()); + + upload_service_.GatherHistograms(); + metrics::ChromeUserMetricsExtension* proto = + upload_service_.current_log_->uma_proto(); + EXPECT_EQ(1, proto->histogram_event().size()); +} + +TEST_F(UploadServiceTest, ExtractChannelFromString) { + EXPECT_EQ( + SystemProfileCache::ProtoChannelFromString( + "developer-build"), + metrics::SystemProfileProto::CHANNEL_UNKNOWN); + + EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV, + SystemProfileCache::ProtoChannelFromString("dev-channel")); + + EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN, + SystemProfileCache::ProtoChannelFromString("dev-channel test")); +} + +TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) { + std::string name("os name"); + std::string content( + "CHROMEOS_RELEASE_NAME=" + name + + "\nCHROMEOS_RELEASE_VERSION=version\n" + "CHROMEOS_RELEASE_DESCRIPTION=description beta-channel test\n" + "CHROMEOS_RELEASE_TRACK=beta-channel"); + + base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time()); + scoped_ptr histogram = + metrics::MetricSample::SparseHistogramSample("myhistogram", 1); + + upload_service_.system_profile_setter_ = &cache_; + // Reset to create the new log with the profile setter. + upload_service_.Reset(); + upload_service_.AddSample(*histogram.get()); + upload_service_.UploadEvent(); + + EXPECT_EQ(1, sender_.send_call_count()); + EXPECT_TRUE(sender_.is_good_proto()); + EXPECT_EQ(1, sender_.last_message_proto().histogram_event().size()); + + EXPECT_EQ(name, sender_.last_message_proto().system_profile().os().name()); + EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_BETA, + sender_.last_message_proto().system_profile().channel()); + EXPECT_NE(0, sender_.last_message_proto().client_id()); + EXPECT_NE(0, sender_.last_message_proto().system_profile().build_timestamp()); + EXPECT_NE(0, sender_.last_message_proto().session_id()); +} + +TEST_F(UploadServiceTest, PersistentGUID) { + std::string tmp_file = dir_.path().Append("tmpfile").value(); + + std::string first_guid = SystemProfileCache::GetPersistentGUID(tmp_file); + std::string second_guid = SystemProfileCache::GetPersistentGUID(tmp_file); + + // The GUID are cached. + EXPECT_EQ(first_guid, second_guid); + + base::DeleteFile(base::FilePath(tmp_file), false); + + first_guid = SystemProfileCache::GetPersistentGUID(tmp_file); + base::DeleteFile(base::FilePath(tmp_file), false); + second_guid = SystemProfileCache::GetPersistentGUID(tmp_file); + + // Random GUIDs are generated (not all the same). + EXPECT_NE(first_guid, second_guid); +} + +TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) { + cache_.Initialize(); + int session_id = cache_.profile_.session_id; + cache_.initialized_ = false; + cache_.Initialize(); + EXPECT_EQ(cache_.profile_.session_id, session_id + 1); +} + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} From 346c43baf2d0c58d4d96df8c5c5366eb9923d45d Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 9 Jul 2014 09:31:42 -0700 Subject: [PATCH 146/200] metrics: fix unittest This fixes a compilation bug that was not caught because of crbug.com/392504. BUG=chromium:392504 TEST=FEATURES=test emerge-amd64-generic metrics Change-Id: Id71a8945d1ec80c7e5d7f2d8de5d287321b47de7 Reviewed-on: https://chromium-review.googlesource.com/207131 Reviewed-by: Luigi Semenzato Commit-Queue: Bertrand Simonnet Tested-by: Bertrand Simonnet --- metrics/metrics_daemon_test.cc | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 3973ecbc7..4f2741b05 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#define __STDC_FORMAT_MACROS + +#include #include #include @@ -33,12 +36,12 @@ using chromeos_metrics::PersistentIntegerMock; static const char kFakeDiskStatsName[] = "fake-disk-stats"; static const char kFakeDiskStatsFormat[] = - " 1793 1788 %d 105580 " - " 196 175 %d 30290 " + " 1793 1788 %" PRIu64 "d 105580 " + " 196 175 %" PRIu64 "d 30290 " " 0 44060 135850\n"; static string kFakeDiskStats[2]; -static const int kFakeReadSectors[] = {80000, 100000}; -static const int kFakeWriteSectors[] = {3000, 4000}; +static const uint64 kFakeReadSectors[] = {80000, 100000}; +static const uint64 kFakeWriteSectors[] = {3000, 4000}; static const char kFakeVmStatsName[] = "fake-vm-stats"; static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq"; @@ -254,7 +257,7 @@ TEST_F(MetricsDaemonTest, SendSample) { } TEST_F(MetricsDaemonTest, ReportDiskStats) { - long int read_sectors_now, write_sectors_now; + uint64 read_sectors_now, write_sectors_now; CreateFakeDiskStatsFile(kFakeDiskStats[1].c_str()); daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now); From d83ca801ad65ae389d20e5dd3e30aed6f285ebf4 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 9 Jul 2014 16:34:29 -0700 Subject: [PATCH 147/200] metrics: Use the new serialization mechanism in libmetrics Convert libmetrics to the new serialization mechanism living in //components/metrics in the chrome repo. BUG=chromium:374368 TEST=FEATURES=test emerge-amd64-generic metrics Change-Id: Ia8cf128d04fedd9672fb47096dc6fd87d6a9043d Reviewed-on: https://chromium-review.googlesource.com/207237 Reviewed-by: Luigi Semenzato Commit-Queue: Bertrand Simonnet Tested-by: Bertrand Simonnet --- metrics/libmetrics.gypi | 3 + metrics/metrics.gyp | 5 +- metrics/metrics_library.cc | 110 ++++---------------------- metrics/metrics_library.h | 18 +---- metrics/metrics_library_test.cc | 132 +------------------------------- 5 files changed, 27 insertions(+), 241 deletions(-) diff --git a/metrics/libmetrics.gypi b/metrics/libmetrics.gypi index 1753830ba..65de6f57b 100644 --- a/metrics/libmetrics.gypi +++ b/metrics/libmetrics.gypi @@ -24,7 +24,10 @@ 'c_metrics_library.cc', 'metrics_library.cc', 'timer.cc', + 'components/metrics/chromeos/metric_sample.cc', + 'components/metrics/chromeos/serialization_utils.cc', ], + 'include_dirs': ['.'], }, ], } diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 044ff6673..7a4338b05 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -55,7 +55,8 @@ 'target_name': 'libupload_service', 'type': 'static_library', 'dependencies': [ - 'metrics_proto' + 'metrics_proto', + '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', ], 'link_settings': { 'libraries': [ @@ -83,8 +84,6 @@ 'uploader/metrics_log.cc', 'uploader/system_profile_cache.cc', 'uploader/curl_sender.cc', - 'components/metrics/chromeos/metric_sample.cc', - 'components/metrics/chromeos/serialization_utils.cc', 'components/metrics/metrics_log_base.cc', 'components/metrics/metrics_log_manager.cc', 'components/metrics/metrics_hashes.cc', diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 4fbc1d8dd..c4188644d 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "metrics_library.h" +#include "metrics/metrics_library.h" #include #include @@ -10,12 +10,11 @@ #include #include -#include #include #include -// HANDLE_EINTR macro, no libbase required. -#include +#include "components/metrics/chromeos/metric_sample.h" +#include "components/metrics/chromeos/serialization_utils.h" #include "policy/device_policy.h" @@ -51,22 +50,6 @@ static const char *kCrosEventNames[] = { time_t MetricsLibrary::cached_enabled_time_ = 0; bool MetricsLibrary::cached_enabled_ = false; -// Copied from libbase to avoid pulling in all of libbase just for libmetrics. -static int WriteFileDescriptor(const int fd, const char* data, int size) { - // Allow for partial writes. - ssize_t bytes_written_total = 0; - for (ssize_t bytes_written_partial = 0; bytes_written_total < size; - bytes_written_total += bytes_written_partial) { - bytes_written_partial = - HANDLE_EINTR(write(fd, data + bytes_written_total, - size - bytes_written_total)); - if (bytes_written_partial < 0) - return -1; - } - - return bytes_written_total; -} - MetricsLibrary::MetricsLibrary() : consent_file_(kConsentFile) {} MetricsLibrary::~MetricsLibrary() {} @@ -169,55 +152,6 @@ bool MetricsLibrary::AreMetricsEnabled() { return cached_enabled_; } -bool MetricsLibrary::SendMessageToChrome(const std::string& message) { - int size = static_cast(message.size()); - if (size > kBufferSize) { - LOG(ERROR) << "chrome message too big (" << size << " bytes)"; - return false; - } - // Use libc here instead of chromium base classes because we need a UNIX fd - // for flock. |uma_events_file_| must exist already. - int chrome_fd = HANDLE_EINTR(open(uma_events_file_.c_str(), - O_WRONLY | O_APPEND)); - // If we failed to open it, return. - if (chrome_fd < 0) { - PLOG(ERROR) << uma_events_file_ << ": open"; - return false; - } - - // Grab an exclusive lock to protect Chrome from truncating - // underneath us. - if (HANDLE_EINTR(flock(chrome_fd, LOCK_EX)) < 0) { - PLOG(ERROR) << uma_events_file_ << ": flock"; - IGNORE_EINTR(close(chrome_fd)); - return false; - } - - bool success = true; - if (WriteFileDescriptor(chrome_fd, message.c_str(), size) != size) { - PLOG(ERROR) << uma_events_file_ << ": write"; - success = false; - } - - // Close the file and release the lock. - IGNORE_EINTR(close(chrome_fd)); - return success; -} - -const std::string MetricsLibrary::FormatChromeMessage( - const std::string& name, - const std::string& value) { - uint32 message_length = - sizeof(message_length) + name.size() + 1 + value.size() + 1; - std::string result; - result.reserve(message_length); - // Marshal the total message length in the native byte order. - result.assign(reinterpret_cast(&message_length), - sizeof(message_length)); - result += name + '\0' + value + '\0'; - return result; -} - void MetricsLibrary::Init() { uma_events_file_ = kUMAEventsPath; } @@ -239,43 +173,33 @@ bool MetricsLibrary::SendToUMA(const std::string& name, int min, int max, int nbuckets) { - // Format the message. - std::string value = base::StringPrintf("%s %d %d %d %d", - name.c_str(), sample, min, max, nbuckets); - std::string message = FormatChromeMessage("histogram", value); - // Send the message. - return SendMessageToChrome(message); + return metrics::SerializationUtils::WriteMetricToFile( + *metrics::MetricSample::HistogramSample(name, sample, min, max, nbuckets) + .get(), + kUMAEventsPath); } bool MetricsLibrary::SendEnumToUMA(const std::string& name, int sample, int max) { - // Format the message. - std::string value = base::StringPrintf("%s %d %d", name.c_str(), sample, max); - std::string message = FormatChromeMessage("linearhistogram", value); - // Send the message. - return SendMessageToChrome(message); + return metrics::SerializationUtils::WriteMetricToFile( + *metrics::MetricSample::LinearHistogramSample(name, sample, max).get(), + kUMAEventsPath); } bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) { - // Format the message. - std::string value = base::StringPrintf("%s %d", name.c_str(), sample); - std::string message = FormatChromeMessage("sparsehistogram", value); - // Send the message. - return SendMessageToChrome(message); + return metrics::SerializationUtils::WriteMetricToFile( + *metrics::MetricSample::SparseHistogramSample(name, sample).get(), + kUMAEventsPath); } bool MetricsLibrary::SendUserActionToUMA(const std::string& action) { - // Format the message. - std::string message = FormatChromeMessage("useraction", action); - // Send the message. - return SendMessageToChrome(message); + return metrics::SerializationUtils::WriteMetricToFile( + *metrics::MetricSample::UserActionSample(action).get(), kUMAEventsPath); } bool MetricsLibrary::SendCrashToUMA(const char *crash_kind) { - // Format the message. - std::string message = FormatChromeMessage("crash", crash_kind); - // Send the message. - return SendMessageToChrome(message); + return metrics::SerializationUtils::WriteMetricToFile( + *metrics::MetricSample::CrashSample(crash_kind).get(), kUMAEventsPath); } void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index e34ff4a72..9e4be89b2 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef METRICS_LIBRARY_H_ -#define METRICS_LIBRARY_H_ +#ifndef METRICS_METRICS_LIBRARY_H_ +#define METRICS_METRICS_LIBRARY_H_ #include #include @@ -131,18 +131,6 @@ class MetricsLibrary : public MetricsLibraryInterface { char* buffer, int buffer_size, bool* result); - // Sends message to Chrome for transport to UMA and returns true on success. - bool SendMessageToChrome(const std::string& message); - - // Serializes a name/value pair into a message buffer. - // - // The serialized format is: | LENGTH | NAME | \0 | VALUE | \0 | - // - // where LENGTH is a 32-bit integer in native endianness, and NAME and VALUE - // are null-terminated strings (the zero bytes are explicitly shown above). - const std::string FormatChromeMessage(const std::string& name, - const std::string& value); - // This function is used by tests only to mock the device policies. void SetPolicyProvider(policy::PolicyProvider* provider); @@ -160,4 +148,4 @@ class MetricsLibrary : public MetricsLibraryInterface { DISALLOW_COPY_AND_ASSIGN(MetricsLibrary); }; -#endif // METRICS_LIBRARY_H_ +#endif // METRICS_METRICS_LIBRARY_H_ diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 82418ede5..ccd41294c 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -10,8 +10,8 @@ #include #include -#include "c_metrics_library.h" -#include "metrics_library.h" +#include "metrics/c_metrics_library.h" +#include "metrics/metrics_library.h" using base::FilePath; using ::testing::_; @@ -165,89 +165,6 @@ TEST_F(MetricsLibraryTest, AreMetricsEnabledCaching) { VerifyEnabledCacheEviction(true); } -TEST_F(MetricsLibraryTest, FormatChromeMessage) { - char kLen = 10; - char exp[] = { kLen, 0, 0, 0, 'f', 'o', 'o', 0, '1', 0 }; - // The message is composed by a 4-byte length field, followed - // by two null-terminated strings (name/value). - EXPECT_EQ(sizeof(exp), kLen); - EXPECT_EQ(std::string(exp, kLen), lib_.FormatChromeMessage("foo", "1")); -} - -TEST_F(MetricsLibraryTest, SendEnumToUMA) { - char buf[100]; - const int kLen = 40; - EXPECT_TRUE(lib_.SendEnumToUMA("Test.EnumMetric", 1, 3)); - EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); - - char exp[kLen]; - sprintf(exp, "%c%c%c%clinearhistogram%cTest.EnumMetric 1 3", - kLen, 0, 0, 0, 0); - EXPECT_EQ(0, memcmp(exp, buf, kLen)); -} - -TEST_F(MetricsLibraryTest, SendMessageToChrome) { - EXPECT_TRUE(lib_.SendMessageToChrome(std::string("test"))); - EXPECT_TRUE(lib_.SendMessageToChrome(std::string("content"))); - std::string uma_events; - EXPECT_TRUE(base::ReadFileToString(kTestUMAEventsFile, &uma_events)); - EXPECT_EQ("testcontent", uma_events); -} - -TEST_F(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation) { - // Checks that the library doesn't die badly if the file can't be - // created. - static const char kDoesNotExistFile[] = "/does/not/exist"; - lib_.uma_events_file_ = kDoesNotExistFile; - const std::string kDummyMessage = "Dummy Message"; - EXPECT_FALSE(lib_.SendMessageToChrome(kDummyMessage)); - base::DeleteFile(FilePath(kDoesNotExistFile), false); -} - -TEST_F(MetricsLibraryTest, SendToUMA) { - char buf[100]; - const int kLen = 37; - EXPECT_TRUE(lib_.SendToUMA("Test.Metric", 2, 1, 100, 50)); - EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); - - char exp[kLen]; - sprintf(exp, "%c%c%c%chistogram%cTest.Metric 2 1 100 50", kLen, 0, 0, 0, 0); - EXPECT_EQ(0, memcmp(exp, buf, kLen)); -} - -TEST_F(MetricsLibraryTest, SendUserActionToUMA) { - char buf[100]; - const int kLen = 30; - EXPECT_TRUE(lib_.SendUserActionToUMA("SomeKeyPressed")); - EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); - - char exp[kLen]; - sprintf(exp, "%c%c%c%cuseraction%cSomeKeyPressed", kLen, 0, 0, 0, 0); - EXPECT_EQ(0, memcmp(exp, buf, kLen)); -} - -TEST_F(MetricsLibraryTest, SendSparseToUMA) { - char buf[100]; - const int kLen = 4 + sizeof("sparsehistogram") + sizeof("Test.Sparse 1234"); - EXPECT_TRUE(lib_.SendSparseToUMA("Test.Sparse", 1234)); - EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); - - char exp[kLen]; - sprintf(exp, "%c%c%c%csparsehistogram%cTest.Sparse 1234", kLen, 0, 0, 0, 0); - EXPECT_EQ(0, memcmp(exp, buf, kLen)); -} - -TEST_F(MetricsLibraryTest, SendCrashToUMA) { - EXPECT_TRUE(lib_.SendCrashToUMA("kernel")); - char exp[100]; - int len = sprintf(exp, "%c%c%c%ccrash%ckernel", - 0, 0, 0, 0, 0) + 1; - exp[0] = len; - char buf[100]; - EXPECT_EQ(len, base::ReadFile(kTestUMAEventsFile, buf, 100)); - EXPECT_EQ(0, memcmp(exp, buf, len)); -} - class CMetricsLibraryTest : public testing::Test { protected: virtual void SetUp() { @@ -290,51 +207,6 @@ TEST_F(CMetricsLibraryTest, AreMetricsEnabledTrue) { EXPECT_TRUE(CMetricsLibraryAreMetricsEnabled(lib_)); } -TEST_F(CMetricsLibraryTest, SendEnumToUMA) { - char buf[100]; - const int kLen = 40; - EXPECT_TRUE(CMetricsLibrarySendEnumToUMA(lib_, "Test.EnumMetric", 1, 3)); - EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); - - char exp[kLen]; - sprintf(exp, "%c%c%c%clinearhistogram%cTest.EnumMetric 1 3", - kLen, 0, 0, 0, 0); - EXPECT_EQ(0, memcmp(exp, buf, kLen)); -} - -TEST_F(CMetricsLibraryTest, SendToUMA) { - char buf[100]; - const int kLen = 37; - EXPECT_TRUE(CMetricsLibrarySendToUMA(lib_, "Test.Metric", 2, 1, 100, 50)); - EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); - - char exp[kLen]; - sprintf(exp, "%c%c%c%chistogram%cTest.Metric 2 1 100 50", kLen, 0, 0, 0, 0); - EXPECT_EQ(0, memcmp(exp, buf, kLen)); -} - -TEST_F(CMetricsLibraryTest, SendUserActionToUMA) { - char buf[100]; - const int kLen = 30; - EXPECT_TRUE(CMetricsLibrarySendUserActionToUMA(lib_, "SomeKeyPressed")); - EXPECT_EQ(kLen, base::ReadFile(kTestUMAEventsFile, buf, 100)); - - char exp[kLen]; - sprintf(exp, "%c%c%c%cuseraction%cSomeKeyPressed", kLen, 0, 0, 0, 0); - EXPECT_EQ(0, memcmp(exp, buf, kLen)); -} - -TEST_F(CMetricsLibraryTest, SendCrashToUMA) { - char buf[100]; - char exp[100]; - int len = sprintf(exp, "%c%c%c%ccrash%cuser", 0, 0, 0, 0, 0) + 1; - exp[0] = len; - EXPECT_TRUE(CMetricsLibrarySendCrashToUMA(lib_, "user")); - EXPECT_EQ(len, base::ReadFile(kTestUMAEventsFile, buf, 100)); - - EXPECT_EQ(0, memcmp(exp, buf, len)); -} - int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 5984b2116c6af00c6ba5f5ebe5ab8c8d59de9aca Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Thu, 10 Jul 2014 17:32:40 -0700 Subject: [PATCH 148/200] metrics: remove unused constant A constant was left during the refactoring (https://chromium-review.googlesource.com/#/c/207237/) and it is failing the generic ASAN buildbot. BUG=chromium:374368 TEST=FEATURES=test emerge-amd64-generic metrics Change-Id: Ief9f2a064dbeded87e2e5fc67d3659fedac88867 Reviewed-on: https://chromium-review.googlesource.com/207460 Tested-by: Bertrand Simonnet Reviewed-by: Luigi Semenzato Commit-Queue: Bertrand Simonnet --- metrics/metrics_library.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index c4188644d..ac97c3e2a 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -23,7 +23,6 @@ static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; static const char kUMAEventsPath[] = "/var/run/metrics/uma-events"; static const char kConsentFile[] = "/home/chronos/Consent To Send Stats"; -static const int32_t kBufferSize = 1024; static const char kCrosEventHistogramName[] = "Platform.CrOSEvent"; static const int kCrosEventHistogramMax = 100; From e6cfd64e6a819dc7e5e2d32facb3e1e357fbecb2 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 9 Jul 2014 16:35:23 -0700 Subject: [PATCH 149/200] metrics: fix lint warnings This CL solve the cros lint warnings. This is mostly due to: * header guards * include path * few google style guide warnings BUG=chromium:389229 TEST=FEATURES=test emerge-amd64-generic metrics Change-Id: Ibbfcd2c88926bcc0c1ce9275b4ad0fb0748cd4de Reviewed-on: https://chromium-review.googlesource.com/207248 Reviewed-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet Tested-by: Bertrand Simonnet Reviewed-by: Luigi Semenzato --- metrics/c_metrics_library.cc | 6 ++- metrics/c_metrics_library.h | 6 +-- metrics/metrics_client.cc | 7 ++- metrics/metrics_daemon_main.cc | 2 +- metrics/metrics_daemon_test.cc | 84 ++++++++++++------------------ metrics/metrics_library_mock.h | 8 +-- metrics/persistent_integer.cc | 4 +- metrics/persistent_integer.h | 4 +- metrics/persistent_integer_mock.h | 5 +- metrics/persistent_integer_test.cc | 1 - metrics/timer.cc | 4 +- metrics/timer_mock.h | 4 +- metrics/timer_test.cc | 6 +-- 13 files changed, 61 insertions(+), 80 deletions(-) diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc index 5c7553c2c..3e2e261b9 100644 --- a/metrics/c_metrics_library.cc +++ b/metrics/c_metrics_library.cc @@ -6,8 +6,10 @@ // C wrapper to libmetrics // -#include "c_metrics_library.h" -#include "metrics_library.h" +#include + +#include "metrics/c_metrics_library.h" +#include "metrics/metrics_library.h" extern "C" CMetricsLibrary CMetricsLibraryNew(void) { MetricsLibrary* lib = new MetricsLibrary; diff --git a/metrics/c_metrics_library.h b/metrics/c_metrics_library.h index 28ae91600..7f78e43a7 100644 --- a/metrics/c_metrics_library.h +++ b/metrics/c_metrics_library.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef C_METRICS_LIBRARY_H_ -#define C_METRICS_LIBRARY_H_ +#ifndef METRICS_C_METRICS_LIBRARY_H_ +#define METRICS_C_METRICS_LIBRARY_H_ #if defined(__cplusplus) extern "C" { @@ -46,4 +46,4 @@ int CMetricsLibraryAreMetricsEnabled(CMetricsLibrary handle); #if defined(__cplusplus) } #endif -#endif // C_METRICS_LIBRARY_H_ +#endif // METRICS_C_METRICS_LIBRARY_H_ diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index 03fb924cf..6819e1d95 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -5,7 +5,7 @@ #include #include -#include "metrics_library.h" +#include "metrics/metrics_library.h" enum Mode { kModeSendSample, @@ -36,8 +36,7 @@ void ShowUsage() { " -s: send a sparse histogram sample\n" " -t: convert sample from double seconds to int milliseconds\n" " -u: send a user action to Chrome\n" - " -v: send a Platform.CrOSEvent enum histogram sample\n" - ); + " -v: send a Platform.CrOSEvent enum histogram sample\n"); exit(1); } @@ -193,7 +192,7 @@ int main(int argc, char** argv) { ShowUsage(); } - switch(mode) { + switch (mode) { case kModeSendSample: case kModeSendEnumSample: case kModeSendSparseSample: diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 8ecb885a1..93e24142b 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -10,7 +10,7 @@ #include #include -#include "metrics_daemon.h" +#include "metrics/metrics_daemon.h" const char kScalingMaxFreqPath[] = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 4f2741b05..d6abc4286 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -17,9 +17,9 @@ #include #include -#include "metrics_daemon.h" -#include "metrics_library_mock.h" -#include "persistent_integer_mock.h" +#include "metrics/metrics_daemon.h" +#include "metrics/metrics_library_mock.h" +#include "metrics/persistent_integer_mock.h" using base::FilePath; using base::StringPrintf; @@ -39,7 +39,6 @@ static const char kFakeDiskStatsFormat[] = " 1793 1788 %" PRIu64 "d 105580 " " 196 175 %" PRIu64 "d 30290 " " 0 44060 135850\n"; -static string kFakeDiskStats[2]; static const uint64 kFakeReadSectors[] = {80000, 100000}; static const uint64 kFakeWriteSectors[] = {3000, 4000}; @@ -49,14 +48,17 @@ static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq"; class MetricsDaemonTest : public testing::Test { protected: + std::string kFakeDiskStats0; + std::string kFakeDiskStats1; + virtual void SetUp() { - kFakeDiskStats[0] = base::StringPrintf(kFakeDiskStatsFormat, + kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat, kFakeReadSectors[0], kFakeWriteSectors[0]); - kFakeDiskStats[1] = base::StringPrintf(kFakeDiskStatsFormat, + kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat, kFakeReadSectors[1], kFakeWriteSectors[1]); - CreateFakeDiskStatsFile(kFakeDiskStats[0].c_str()); + CreateFakeDiskStatsFile(kFakeDiskStats0.c_str()); CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 10000000); CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 10000000); @@ -85,7 +87,6 @@ class MetricsDaemonTest : public testing::Test { unclean_shutdown_interval_mock_ = new StrictMock("4.mock"); daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_mock_); - } virtual void TearDown() { @@ -259,7 +260,7 @@ TEST_F(MetricsDaemonTest, SendSample) { TEST_F(MetricsDaemonTest, ReportDiskStats) { uint64 read_sectors_now, write_sectors_now; - CreateFakeDiskStatsFile(kFakeDiskStats[1].c_str()); + CreateFakeDiskStatsFile(kFakeDiskStats1.c_str()); daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now); EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]); EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]); @@ -277,46 +278,28 @@ TEST_F(MetricsDaemonTest, ReportDiskStats) { } TEST_F(MetricsDaemonTest, ProcessMeminfo) { - string meminfo = "\ -MemTotal: 2000000 kB\n\ -MemFree: 500000 kB\n\ -Buffers: 1000000 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\ -"; + string meminfo = + "MemTotal: 2000000 kB\nMemFree: 500000 kB\n" + "Buffers: 1000000 kB\nCached: 213652 kB\n" + "SwapCached: 0 kB\nActive: 133400 kB\n" + "Inactive: 183396 kB\nActive(anon): 92984 kB\n" + "Inactive(anon): 58860 kB\nActive(file): 40416 kB\n" + "Inactive(file): 124536 kB\nUnevictable: 0 kB\n" + "Mlocked: 0 kB\nSwapTotal: 0 kB\n" + "SwapFree: 0 kB\nDirty: 40 kB\n" + "Writeback: 0 kB\nAnonPages: 92652 kB\n" + "Mapped: 59716 kB\nShmem: 59196 kB\n" + "Slab: 16656 kB\nSReclaimable: 6132 kB\n" + "SUnreclaim: 10524 kB\nKernelStack: 1648 kB\n" + "PageTables: 2780 kB\nNFS_Unstable: 0 kB\n" + "Bounce: 0 kB\nWritebackTmp: 0 kB\n" + "CommitLimit: 970656 kB\nCommitted_AS: 1260528 kB\n" + "VmallocTotal: 122880 kB\nVmallocUsed: 12144 kB\n" + "VmallocChunk: 103824 kB\nDirectMap4k: 9636 kB\n" + "DirectMap2M: 1955840 kB\n"; + // All enum calls must report percents. - EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, 100)) - .Times(AtLeast(1)); + EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, 100)).Times(AtLeast(1)); // Check that MemFree is correctly computed at 25%. EXPECT_CALL(metrics_lib_, SendEnumToUMA("Platform.MeminfoMemFree", 25, 100)) .Times(AtLeast(1)); @@ -332,10 +315,7 @@ DirectMap2M: 1955840 kB\n\ } TEST_F(MetricsDaemonTest, ProcessMeminfo2) { - string meminfo = "\ -MemTotal: 2000000 kB\n\ -MemFree: 1000000 kB\n\ -"; + string meminfo = "MemTotal: 2000000 kB\nMemFree: 1000000 kB\n"; // Not enough fields. EXPECT_FALSE(daemon_.ProcessMeminfo(meminfo)); } diff --git a/metrics/metrics_library_mock.h b/metrics/metrics_library_mock.h index 537f4e47b..0f1047f2e 100644 --- a/metrics/metrics_library_mock.h +++ b/metrics/metrics_library_mock.h @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef METRICS_LIBRARY_MOCK_H_ -#define METRICS_LIBRARY_MOCK_H_ +#ifndef METRICS_METRICS_LIBRARY_MOCK_H_ +#define METRICS_METRICS_LIBRARY_MOCK_H_ #include -#include "metrics_library.h" +#include "metrics/metrics_library.h" #include @@ -22,4 +22,4 @@ class MetricsLibraryMock : public MetricsLibraryInterface { MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action)); }; -#endif // METRICS_LIBRARY_MOCK_H_ +#endif // METRICS_METRICS_LIBRARY_MOCK_H_ diff --git a/metrics/persistent_integer.cc b/metrics/persistent_integer.cc index d4ef8b216..3020f7b46 100644 --- a/metrics/persistent_integer.cc +++ b/metrics/persistent_integer.cc @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "persistent_integer.h" +#include "metrics/persistent_integer.h" #include #include #include -#include "metrics_library.h" +#include "metrics/metrics_library.h" namespace { diff --git a/metrics/persistent_integer.h b/metrics/persistent_integer.h index 5b159c7f9..4a5670cd5 100644 --- a/metrics/persistent_integer.h +++ b/metrics/persistent_integer.h @@ -16,7 +16,7 @@ namespace chromeos_metrics { class PersistentInteger { public: - PersistentInteger(const std::string& name); + explicit PersistentInteger(const std::string& name); // Virtual only because of mock. virtual ~PersistentInteger(); @@ -61,6 +61,6 @@ class PersistentInteger { static bool testing_; }; -} +} // namespace chromeos_metrics #endif // METRICS_PERSISTENT_INTEGER_H_ diff --git a/metrics/persistent_integer_mock.h b/metrics/persistent_integer_mock.h index 46570f966..bb42023b8 100644 --- a/metrics/persistent_integer_mock.h +++ b/metrics/persistent_integer_mock.h @@ -9,13 +9,14 @@ #include -#include "persistent_integer.h" +#include "metrics/persistent_integer.h" namespace chromeos_metrics { class PersistentIntegerMock : public PersistentInteger { public: - PersistentIntegerMock(const std::string& name) : PersistentInteger(name) {} + explicit PersistentIntegerMock(const std::string& name) + : PersistentInteger(name) {} MOCK_METHOD1(Add, void(int64 count)); }; diff --git a/metrics/persistent_integer_test.cc b/metrics/persistent_integer_test.cc index ec3a6e414..7d9e21c4a 100644 --- a/metrics/persistent_integer_test.cc +++ b/metrics/persistent_integer_test.cc @@ -16,7 +16,6 @@ const char kBackingFilePattern[] = "*.pibakf"; using chromeos_metrics::PersistentInteger; class PersistentIntegerTest : public testing::Test { - virtual void SetUp() OVERRIDE { // Set testing mode. chromeos_metrics::PersistentInteger::SetTestingMode(true); diff --git a/metrics/timer.cc b/metrics/timer.cc index 90948b5ca..dd7842500 100644 --- a/metrics/timer.cc +++ b/metrics/timer.cc @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "timer.h" +#include "metrics/timer.h" #include #include -#include "metrics_library.h" +#include "metrics/metrics_library.h" namespace chromeos_metrics { diff --git a/metrics/timer_mock.h b/metrics/timer_mock.h index 6034ee3c9..6eef76152 100644 --- a/metrics/timer_mock.h +++ b/metrics/timer_mock.h @@ -11,7 +11,7 @@ #include #include -#include "timer.h" +#include "metrics/timer.h" namespace chromeos_metrics { @@ -26,7 +26,7 @@ class TimerMock : public Timer { class TimerReporterMock : public TimerReporter { public: - TimerReporterMock() : TimerReporter("",0,0,0) {} + TimerReporterMock() : TimerReporter("", 0, 0, 0) {} MOCK_METHOD0(Start, bool()); MOCK_METHOD0(Stop, bool()); MOCK_METHOD0(Reset, bool()); diff --git a/metrics/timer_test.cc b/metrics/timer_test.cc index d64beab3a..b678a27a0 100644 --- a/metrics/timer_test.cc +++ b/metrics/timer_test.cc @@ -6,9 +6,9 @@ #include #include -#include "metrics_library_mock.h" -#include "timer.h" -#include "timer_mock.h" +#include "metrics/metrics_library_mock.h" +#include "metrics/timer.h" +#include "metrics/timer_mock.h" using ::testing::_; using ::testing::Return; From 4e59634509d6d3dd4c9423e0c48c62c09b8cbe15 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Wed, 30 Jul 2014 11:48:30 -0700 Subject: [PATCH 150/200] metrics: fix remaining linter issues Fixed various issues reported by cpplint.py when run on src/platform2/metrics. BUG=None TEST=cpplint.py `find metrics/* | grep "\.cc\|\.h"` FEATURES=test emerge-link metrics Change-Id: I589be238c97c38f985f24ffe26d2cdf259c96760 Reviewed-on: https://chromium-review.googlesource.com/210505 Tested-by: Alex Vakulenko Reviewed-by: Alex Deymo Commit-Queue: Alex Vakulenko --- metrics/uploader/system_profile_cache.cc | 2 +- metrics/uploader/upload_service.cc | 2 +- metrics/uploader/upload_service_test.cc | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index 0090d38da..4b79c7380 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -12,8 +12,8 @@ #include "base/guid.h" #include "base/logging.h" #include "base/sys_info.h" -#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "components/metrics/metrics_log_base.h" +#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "metrics/persistent_integer.h" #include "vboot/crossystem.h" diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 5e05dcf18..62f5d920e 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -16,8 +16,8 @@ #include "base/metrics/sparse_histogram.h" #include "base/metrics/statistics_recorder.h" #include "base/sha1.h" -#include "components/metrics/chromeos/serialization_utils.h" #include "components/metrics/chromeos/metric_sample.h" +#include "components/metrics/chromeos/serialization_utils.h" #include "gflags/gflags.h" #include "uploader/curl_sender.h" #include "uploader/metrics_log.h" diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index efc382ef4..9e5bd8d17 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -5,19 +5,19 @@ #include #include "base/at_exit.h" -#include "base/logging.h" #include "base/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/logging.h" #include "base/sys_info.h" #include "components/metrics/chromeos/metric_sample.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" -#include "components/metrics/proto/system_profile.pb.h" #include "components/metrics/proto/histogram_event.pb.h" +#include "components/metrics/proto/system_profile.pb.h" #include "uploader/metrics_log.h" -#include "uploader/upload_service.h" -#include "uploader/mock/sender_mock.h" #include "uploader/mock/mock_system_profile_setter.h" +#include "uploader/mock/sender_mock.h" #include "uploader/system_profile_cache.h" +#include "uploader/upload_service.h" class UploadServiceTest : public testing::Test { protected: From f05ab40ab949c1052d1d443f841416f1480924e4 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Thu, 7 Aug 2014 00:54:59 -0700 Subject: [PATCH 151/200] metrics: Use integer types from stdint.h This CL replaces the deprecated int* and uint* types from 'base/basictypes.h' with the int*_t and uint*_t types from 'stdint.h'. BUG=chromium:401356 TEST=`FEATURES=test emerge-$BOARD metrics` Change-Id: Ie5a69edba2c8a9d5185bbc548ed70a5b121c3e3b Reviewed-on: https://chromium-review.googlesource.com/211381 Reviewed-by: Mike Frysinger Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/metrics_daemon.cc | 28 ++++++++++++------------ metrics/metrics_daemon.h | 18 ++++++++------- metrics/metrics_daemon_test.cc | 22 +++++++++---------- metrics/persistent_integer.cc | 14 ++++++------ metrics/persistent_integer.h | 15 +++++++------ metrics/persistent_integer_mock.h | 2 +- metrics/timer_test.cc | 20 +++++++++-------- metrics/uploader/system_profile_cache.cc | 2 +- metrics/uploader/system_profile_cache.h | 5 +++-- 9 files changed, 66 insertions(+), 60 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index b9212fb0e..9e0bf0437 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -175,7 +175,7 @@ void MetricsDaemon::Run(bool run_as_daemon) { } // On OS version change, clear version stats (which are reported daily). - int32 version = GetOsVersionHash(); + int32_t version = GetOsVersionHash(); if (version_cycle_->Get() != version) { version_cycle_->Set(version); kernel_crashes_version_count_->Set(0); @@ -190,8 +190,8 @@ void MetricsDaemon::RunUploaderTest() { upload_service_->UploadEvent(); } -uint32 MetricsDaemon::GetOsVersionHash() { - static uint32 cached_version_hash = 0; +uint32_t MetricsDaemon::GetOsVersionHash() { + static uint32_t cached_version_hash = 0; static bool version_hash_is_cached = false; if (version_hash_is_cached) return cached_version_hash; @@ -372,7 +372,7 @@ TimeDelta MetricsDaemon::GetIncrementalCpuUse() { std::vector proc_stat_totals; base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals); - uint64 user_ticks, user_nice_ticks, system_ticks; + uint64_t user_ticks, user_nice_ticks, system_ticks; if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount || proc_stat_totals[0] != "cpu" || !base::StringToUint64(proc_stat_totals[1], &user_ticks) || @@ -382,7 +382,7 @@ TimeDelta MetricsDaemon::GetIncrementalCpuUse() { return TimeDelta(base::TimeDelta::FromSeconds(0)); } - uint64 total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks; + uint64_t total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks; // Sanity check. if (total_cpu_use_ticks < latest_cpu_use_ticks_) { @@ -391,7 +391,7 @@ TimeDelta MetricsDaemon::GetIncrementalCpuUse() { return TimeDelta(); } - uint64 diff = total_cpu_use_ticks - latest_cpu_use_ticks_; + uint64_t diff = total_cpu_use_ticks - latest_cpu_use_ticks_; latest_cpu_use_ticks_ = total_cpu_use_ticks; // Use microseconds to avoid significant truncations. return base::TimeDelta::FromMicroseconds( @@ -470,8 +470,8 @@ void MetricsDaemon::ScheduleStatsCallback(int wait) { g_timeout_add_seconds(wait, StatsCallbackStatic, this); } -bool MetricsDaemon::DiskStatsReadStats(uint64* read_sectors, - uint64* write_sectors) { +bool MetricsDaemon::DiskStatsReadStats(uint64_t* read_sectors, + uint64_t* write_sectors) { int nchars; int nitems; bool success = false; @@ -632,7 +632,7 @@ gboolean MetricsDaemon::StatsCallbackStatic(void* handle) { // Collects disk and vm stats alternating over a short and a long interval. void MetricsDaemon::StatsCallback() { - uint64 read_sectors_now, write_sectors_now; + uint64_t read_sectors_now, write_sectors_now; struct VmstatRecord vmstats_now; double time_now = GetActiveTime(); double delta_time = time_now - stats_initial_time_; @@ -765,7 +765,7 @@ bool MetricsDaemon::MeminfoCallback() { // static bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path, - uint64* value) { + uint64_t* value) { std::string content; if (!base::ReadFileToString(path, &content)) { PLOG(WARNING) << "cannot read " << path.MaybeAsASCII(); @@ -782,7 +782,7 @@ bool MetricsDaemon::ReadFileToUint64(const base::FilePath& path, bool MetricsDaemon::ReportZram(const base::FilePath& zram_dir) { // Data sizes are in bytes. |zero_pages| is in number of pages. - uint64 compr_data_size, orig_data_size, zero_pages; + uint64_t compr_data_size, orig_data_size, zero_pages; const size_t page_size = 4096; if (!ReadFileToUint64(zram_dir.Append(kComprDataSizeName), @@ -1014,7 +1014,7 @@ void MetricsDaemon::SendSample(const string& name, int sample, void MetricsDaemon::SendKernelCrashesCumulativeCountStats() { // Report the number of crashes for this OS version, but don't clear the // counter. It is cleared elsewhere on version change. - int64 crashes_count = kernel_crashes_version_count_->Get(); + int64_t crashes_count = kernel_crashes_version_count_->Get(); SendSample(kernel_crashes_version_count_->Name(), crashes_count, 1, // value of first bucket @@ -1022,7 +1022,7 @@ void MetricsDaemon::SendKernelCrashesCumulativeCountStats() { 100); // number of buckets - int64 cpu_use_ms = version_cumulative_cpu_use_->Get(); + int64_t cpu_use_ms = version_cumulative_cpu_use_->Get(); SendSample(version_cumulative_cpu_use_->Name(), cpu_use_ms / 1000, // stat is in seconds 1, // device may be used very little... @@ -1040,7 +1040,7 @@ void MetricsDaemon::SendKernelCrashesCumulativeCountStats() { 100); } - int64 active_use_seconds = version_cumulative_active_use_->Get(); + int64_t active_use_seconds = version_cumulative_active_use_->Get(); if (active_use_seconds > 0) { SendSample(version_cumulative_active_use_->Name(), active_use_seconds / 1000, // stat is in seconds diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 2bd12b026..205ea6995 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -5,6 +5,8 @@ #ifndef METRICS_METRICS_DAEMON_H_ #define METRICS_METRICS_DAEMON_H_ +#include + #include #include #include @@ -213,7 +215,7 @@ class MetricsDaemon { void ScheduleStatsCallback(int wait); // Reads cumulative disk statistics from sysfs. Returns true for success. - bool DiskStatsReadStats(uint64* read_sectors, uint64* write_sectors); + bool DiskStatsReadStats(uint64_t* read_sectors, uint64_t* write_sectors); // Reads cumulative vm statistics from procfs. Returns true for success. bool VmStatsReadStats(struct VmstatRecord* stats); @@ -275,7 +277,7 @@ class MetricsDaemon { // Reads the current OS version from /etc/lsb-release and hashes it // to a unsigned 32-bit int. - uint32 GetOsVersionHash(); + uint32_t GetOsVersionHash(); // Updates stats, additionally sending them to UMA if enough time has elapsed // since the last report. @@ -287,8 +289,8 @@ class MetricsDaemon { // Reports zram statistics. bool ReportZram(const base::FilePath& zram_dir); - // Reads a string from a file and converts it to uint64. - static bool ReadFileToUint64(const base::FilePath& path, uint64* value); + // Reads a string from a file and converts it to uint64_t. + static bool ReadFileToUint64(const base::FilePath& path, uint64_t* value); // VARIABLES @@ -323,8 +325,8 @@ class MetricsDaemon { unsigned int memuse_interval_index_; // Contain the most recent disk and vm cumulative stats. - uint64 read_sectors_; - uint64 write_sectors_; + uint64_t read_sectors_; + uint64_t write_sectors_; struct VmstatRecord vmstats_; StatsState stats_state_; @@ -332,10 +334,10 @@ class MetricsDaemon { // The system "HZ", or frequency of ticks. Some system data uses ticks as a // unit, and this is used to convert to standard time units. - uint32 ticks_per_second_; + uint32_t ticks_per_second_; // Used internally by GetIncrementalCpuUse() to return the CPU utilization // between calls. - uint64 latest_cpu_use_ticks_; + uint64_t latest_cpu_use_ticks_; // Persistent values and accumulators for crash statistics. scoped_ptr daily_cycle_; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index d6abc4286..1e9cb3fae 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -39,8 +39,8 @@ static const char kFakeDiskStatsFormat[] = " 1793 1788 %" PRIu64 "d 105580 " " 196 175 %" PRIu64 "d 30290 " " 0 44060 135850\n"; -static const uint64 kFakeReadSectors[] = {80000, 100000}; -static const uint64 kFakeWriteSectors[] = {3000, 4000}; +static const uint64_t kFakeReadSectors[] = {80000, 100000}; +static const uint64_t kFakeWriteSectors[] = {3000, 4000}; static const char kFakeVmStatsName[] = "fake-vm-stats"; static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq"; @@ -173,7 +173,7 @@ class MetricsDaemonTest : public testing::Test { // Creates or overwrites the file in |path| so that it contains the printable // representation of |value|. - void CreateUint64ValueFile(const base::FilePath& path, uint64 value) { + void CreateUint64ValueFile(const base::FilePath& path, uint64_t value) { base::DeleteFile(path, false); std::string value_string = base::Uint64ToString(value); ASSERT_EQ(value_string.length(), @@ -258,7 +258,7 @@ TEST_F(MetricsDaemonTest, SendSample) { } TEST_F(MetricsDaemonTest, ReportDiskStats) { - uint64 read_sectors_now, write_sectors_now; + uint64_t read_sectors_now, write_sectors_now; CreateFakeDiskStatsFile(kFakeDiskStats1.c_str()); daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now); @@ -361,12 +361,12 @@ TEST_F(MetricsDaemonTest, SendZramMetrics) { EXPECT_TRUE(daemon_.testing_); // |compr_data_size| is the size in bytes of compressed data. - const uint64 compr_data_size = 50 * 1000 * 1000; + const uint64_t compr_data_size = 50 * 1000 * 1000; // The constant '3' is a realistic but random choice. // |orig_data_size| does not include zero pages. - const uint64 orig_data_size = compr_data_size * 3; - const uint64 page_size = 4096; - const uint64 zero_pages = 10 * 1000 * 1000 / page_size; + const uint64_t orig_data_size = compr_data_size * 3; + const uint64_t page_size = 4096; + const uint64_t zero_pages = 10 * 1000 * 1000 / page_size; CreateUint64ValueFile(base::FilePath(MetricsDaemon::kComprDataSizeName), compr_data_size); @@ -375,11 +375,11 @@ TEST_F(MetricsDaemonTest, SendZramMetrics) { CreateUint64ValueFile(base::FilePath(MetricsDaemon::kZeroPagesName), zero_pages); - const uint64 real_orig_size = orig_data_size + zero_pages * page_size; - const uint64 zero_ratio_percent = + const uint64_t real_orig_size = orig_data_size + zero_pages * page_size; + const uint64_t zero_ratio_percent = zero_pages * page_size * 100 / real_orig_size; // Ratio samples are in percents. - const uint64 actual_ratio_sample = real_orig_size * 100 / compr_data_size; + const uint64_t actual_ratio_sample = real_orig_size * 100 / compr_data_size; EXPECT_CALL(metrics_lib_, SendToUMA(_, compr_data_size >> 20, _, _, _)); EXPECT_CALL(metrics_lib_, diff --git a/metrics/persistent_integer.cc b/metrics/persistent_integer.cc index 3020f7b46..dd38f1e6f 100644 --- a/metrics/persistent_integer.cc +++ b/metrics/persistent_integer.cc @@ -37,25 +37,25 @@ PersistentInteger::PersistentInteger(const std::string& name) : PersistentInteger::~PersistentInteger() {} -void PersistentInteger::Set(int64 value) { +void PersistentInteger::Set(int64_t value) { value_ = value; Write(); } -int64 PersistentInteger::Get() { +int64_t PersistentInteger::Get() { // If not synced, then read. If the read fails, it's a good idea to write. if (!synced_ && !Read()) Write(); return value_; } -int64 PersistentInteger::GetAndClear() { - int64 v = Get(); +int64_t PersistentInteger::GetAndClear() { + int64_t v = Get(); Set(0); return v; } -void PersistentInteger::Add(int64 x) { +void PersistentInteger::Add(int64_t x) { Set(Get() + x); } @@ -79,8 +79,8 @@ bool PersistentInteger::Read() { PLOG(WARNING) << "cannot open " << backing_file_name_ << " for reading"; return false; } - int32 version; - int64 value; + int32_t version; + int64_t value; bool read_succeeded = false; if (HANDLE_EINTR(read(fd, &version, sizeof(version))) == sizeof(version) && version == version_ && diff --git a/metrics/persistent_integer.h b/metrics/persistent_integer.h index 4a5670cd5..b1cfcf4ef 100644 --- a/metrics/persistent_integer.h +++ b/metrics/persistent_integer.h @@ -5,7 +5,8 @@ #ifndef METRICS_PERSISTENT_INTEGER_H_ #define METRICS_PERSISTENT_INTEGER_H_ -#include +#include + #include namespace chromeos_metrics { @@ -22,20 +23,20 @@ class PersistentInteger { virtual ~PersistentInteger(); // Sets the value. This writes through to the backing file. - void Set(int64 v); + void Set(int64_t v); // Gets the value. May sync from backing file first. - int64 Get(); + int64_t Get(); // Returns the name of the object. std::string Name() { return name_; } // Convenience function for Get() followed by Set(0). - int64 GetAndClear(); + int64_t GetAndClear(); // Convenience function for v = Get, Set(v + x). // Virtual only because of mock. - virtual void Add(int64 x); + virtual void Add(int64_t x); // After calling with |testing| = true, changes some behavior for the purpose // of testing. For instance: instances created while testing use the current @@ -53,8 +54,8 @@ class PersistentInteger { // a valid backing file as a side effect. bool Read(); - int64 value_; - int32 version_; + int64_t value_; + int32_t version_; std::string name_; std::string backing_file_name_; bool synced_; diff --git a/metrics/persistent_integer_mock.h b/metrics/persistent_integer_mock.h index bb42023b8..2061e55c0 100644 --- a/metrics/persistent_integer_mock.h +++ b/metrics/persistent_integer_mock.h @@ -17,7 +17,7 @@ class PersistentIntegerMock : public PersistentInteger { public: explicit PersistentIntegerMock(const std::string& name) : PersistentInteger(name) {} - MOCK_METHOD1(Add, void(int64 count)); + MOCK_METHOD1(Add, void(int64_t count)); }; } // namespace chromeos_metrics diff --git a/metrics/timer_test.cc b/metrics/timer_test.cc index b678a27a0..a712605be 100644 --- a/metrics/timer_test.cc +++ b/metrics/timer_test.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include + #include #include #include @@ -16,17 +18,17 @@ using ::testing::Return; namespace chromeos_metrics { namespace { -const int64 kStime1MSec = 1400; -const int64 kEtime1MSec = 3000; -const int64 kDelta1MSec = 1600; +const int64_t kStime1MSec = 1400; +const int64_t kEtime1MSec = 3000; +const int64_t kDelta1MSec = 1600; -const int64 kStime2MSec = 4200; -const int64 kEtime2MSec = 5000; -const int64 kDelta2MSec = 800; +const int64_t kStime2MSec = 4200; +const int64_t kEtime2MSec = 5000; +const int64_t kDelta2MSec = 800; -const int64 kStime3MSec = 6600; -const int64 kEtime3MSec = 6800; -const int64 kDelta3MSec = 200; +const int64_t kStime3MSec = 6600; +const int64_t kEtime3MSec = 6800; +const int64_t kDelta3MSec = 200; } // namespace class TimerTest : public testing::Test { diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index 4b79c7380..fd7996140 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -57,7 +57,7 @@ bool SystemProfileCache::Initialize() { // TODO(bsimonnet): Change this to map to the number of time system-services // is started. session_id_->Add(1); - profile_.session_id = static_cast(session_id_->Get()); + profile_.session_id = static_cast(session_id_->Get()); initialized_ = true; return initialized_; diff --git a/metrics/uploader/system_profile_cache.h b/metrics/uploader/system_profile_cache.h index 74a37662b..28391798f 100644 --- a/metrics/uploader/system_profile_cache.h +++ b/metrics/uploader/system_profile_cache.h @@ -5,9 +5,10 @@ #ifndef METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_ #define METRICS_UPLOADER_SYSTEM_PROFILE_CACHE_H_ +#include + #include -#include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" @@ -26,7 +27,7 @@ struct SystemProfile { std::string app_version; std::string hardware_class; std::string client_id; - int32 session_id; + int32_t session_id; }; // Retrieves general system informations needed by the protobuf for context and From e8a8e30b25782048a766f9d8ac9133406a013f47 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Thu, 14 Aug 2014 12:56:21 -0700 Subject: [PATCH 152/200] metrics: Replace 'OVERRIDE' with 'override' and fix linter Replace OVERRIDE macro with native C++ 'override' keyword. Also fixed the style issue of using both 'override' and 'virtual' on member function overrides. Only one is required and cpplint complains if both are specified now. And since gobi-chromo-plugin is the only project that uses libmetrics and is not C++11 yet, enabled C++11 features in its makefile to allow for the 'override' keywords to compile in the included metrics/metrics_library.h header. BUG=None TEST=USE="cellular buffet" ./build_packages Change-Id: I28dace6dc4bcb07386632eaed890ce41564e8144 Reviewed-on: https://chromium-review.googlesource.com/212494 Reviewed-by: Ben Chan Commit-Queue: Alex Vakulenko Tested-by: Alex Vakulenko --- metrics/metrics_library.h | 10 ++++------ metrics/persistent_integer_test.cc | 4 ++-- metrics/uploader/curl_sender.h | 3 +-- metrics/uploader/mock/mock_system_profile_setter.h | 2 +- metrics/uploader/mock/sender_mock.h | 2 +- metrics/uploader/system_profile_cache.h | 2 +- metrics/uploader/upload_service.h | 8 ++++---- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 9e4be89b2..97b2b72e2 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -34,7 +34,7 @@ class MetricsLibrary : public MetricsLibraryInterface { virtual ~MetricsLibrary(); // Initializes the library. - virtual void Init() OVERRIDE; + void Init() override; // Returns whether or not the machine is running in guest mode. bool IsGuestMode(); @@ -76,15 +76,13 @@ class MetricsLibrary : public MetricsLibraryInterface { // histogram is proportional to the number of buckets. Therefore, it // is strongly recommended to keep this number low (e.g., 50 is // normal, while 100 is high). - virtual bool SendEnumToUMA(const std::string& name, - int sample, - int max) OVERRIDE; + bool SendEnumToUMA(const std::string& name, int sample, int max) override; // Sends sparse histogram sample to Chrome for transport to UMA. Returns // true on success. // // |sample| is the 32-bit integer value to be recorded. - virtual bool SendSparseToUMA(const std::string& name, int sample) OVERRIDE; + bool SendSparseToUMA(const std::string& name, int sample) override; // Sends a user action to Chrome for transport to UMA and returns true on // success. This method results in the equivalent of an asynchronous @@ -96,7 +94,7 @@ class MetricsLibrary : public MetricsLibraryInterface { // chrome/browser/chromeos/external_metrics.cc. // // |action| is the user-generated event (e.g., "MuteKeyPressed"). - virtual bool SendUserActionToUMA(const std::string& action) OVERRIDE; + bool SendUserActionToUMA(const std::string& action) override; // Sends a signal to UMA that a crash of the given |crash_kind| // has occurred. Used by UMA to generate stability statistics. diff --git a/metrics/persistent_integer_test.cc b/metrics/persistent_integer_test.cc index 7d9e21c4a..097ec7da6 100644 --- a/metrics/persistent_integer_test.cc +++ b/metrics/persistent_integer_test.cc @@ -16,12 +16,12 @@ const char kBackingFilePattern[] = "*.pibakf"; using chromeos_metrics::PersistentInteger; class PersistentIntegerTest : public testing::Test { - virtual void SetUp() OVERRIDE { + void SetUp() override { // Set testing mode. chromeos_metrics::PersistentInteger::SetTestingMode(true); } - virtual void TearDown() OVERRIDE { + void TearDown() override { // Remove backing files. The convention is that they all end in ".pibakf". base::FileEnumerator f_enum(base::FilePath("."), false, diff --git a/metrics/uploader/curl_sender.h b/metrics/uploader/curl_sender.h index 011360139..fc5b0f481 100644 --- a/metrics/uploader/curl_sender.h +++ b/metrics/uploader/curl_sender.h @@ -17,8 +17,7 @@ class CurlSender : public Sender { // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous // POST request to server_url. - virtual bool Send(const std::string& content, - const std::string& hash) OVERRIDE; + bool Send(const std::string& content, const std::string& hash) override; // Static callback required by curl to retrieve the response data. // diff --git a/metrics/uploader/mock/mock_system_profile_setter.h b/metrics/uploader/mock/mock_system_profile_setter.h index 0614eb913..c6e8f0d1e 100644 --- a/metrics/uploader/mock/mock_system_profile_setter.h +++ b/metrics/uploader/mock/mock_system_profile_setter.h @@ -14,7 +14,7 @@ class ChromeUserMetricsExtension; // Mock profile setter used for testing. class MockSystemProfileSetter : public SystemProfileSetter { public: - void Populate(metrics::ChromeUserMetricsExtension* profile_proto) OVERRIDE {} + void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {} }; #endif // METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_ diff --git a/metrics/uploader/mock/sender_mock.h b/metrics/uploader/mock/sender_mock.h index f6f19b9a2..4845f9dc5 100644 --- a/metrics/uploader/mock/sender_mock.h +++ b/metrics/uploader/mock/sender_mock.h @@ -15,7 +15,7 @@ class SenderMock : public Sender { public: SenderMock(); - bool Send(const std::string& content, const std::string& hash) OVERRIDE; + bool Send(const std::string& content, const std::string& hash) override; void Reset(); bool is_good_proto() { return is_good_proto_; } diff --git a/metrics/uploader/system_profile_cache.h b/metrics/uploader/system_profile_cache.h index 28391798f..03e92fca5 100644 --- a/metrics/uploader/system_profile_cache.h +++ b/metrics/uploader/system_profile_cache.h @@ -39,7 +39,7 @@ class SystemProfileCache : public SystemProfileSetter { SystemProfileCache(); // Populates the ProfileSystem protobuf with system information. - void Populate(metrics::ChromeUserMetricsExtension* profile_proto) OVERRIDE; + void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override; // Converts a string representation of the channel (|channel|-channel) to a // SystemProfileProto_Channel diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index 7c1748869..ed5ab8526 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -74,10 +74,10 @@ class UploadService : public base::HistogramFlattener { // Implements inconsistency detection to match HistogramFlattener's // interface. void InconsistencyDetected( - base::HistogramBase::Inconsistency problem) OVERRIDE {} + base::HistogramBase::Inconsistency problem) override {} void UniqueInconsistencyDetected( - base::HistogramBase::Inconsistency problem) OVERRIDE {} - void InconsistencyDetectedInLoggedCount(int amount) OVERRIDE {} + base::HistogramBase::Inconsistency problem) override {} + void InconsistencyDetectedInLoggedCount(int amount) override {} private: friend class UploadServiceTest; @@ -117,7 +117,7 @@ class UploadService : public base::HistogramFlattener { // Callback for HistogramSnapshotManager to store the histograms. void RecordDelta(const base::HistogramBase& histogram, - const base::HistogramSamples& snapshot) OVERRIDE; + const base::HistogramSamples& snapshot) override; // Compiles all the samples received into a single protobuf and adds all // system information. From 8c56c5ed5f9ee9f9a8d80c29717c8fa82a9be932 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Sat, 16 Aug 2014 15:15:14 -0700 Subject: [PATCH 153/200] platform2: Remove legacy inherit-review-settings-ok files. BUG=None TEST=Trybot run on paladin, release, and chromiumos-sdk builders. Change-Id: I11a13e64cd1fd9e3394d1f298425d29204bb06aa Reviewed-on: https://chromium-review.googlesource.com/212840 Tested-by: Ben Chan Reviewed-by: Mike Frysinger Commit-Queue: Ben Chan --- metrics/inherit-review-settings-ok | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 metrics/inherit-review-settings-ok diff --git a/metrics/inherit-review-settings-ok b/metrics/inherit-review-settings-ok deleted file mode 100644 index e69de29bb..000000000 From 0b8cc1c952d078405e4d7bd7a67bcbb567f33bad Mon Sep 17 00:00:00 2001 From: Thiemo Nagel Date: Thu, 21 Aug 2014 15:00:50 +0200 Subject: [PATCH 154/200] metrics: Improve metrics_client usage docs. BUG=chromium:393334 TEST=unit tests passed Change-Id: Iaf6b75cba2fddce5ea5f770d227ed5df70c9bed8 Reviewed-on: https://chromium-review.googlesource.com/213471 Reviewed-by: Daniel Erat Commit-Queue: Thiemo Nagel Tested-by: Thiemo Nagel --- metrics/metrics_client.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc index 6819e1d95..bbe9dcda2 100644 --- a/metrics/metrics_client.cc +++ b/metrics/metrics_client.cc @@ -30,7 +30,8 @@ void ShowUsage() { " |min| > 0, |min| <= sample < |max|\n" " -a: send metric (name/sample) to Autotest only\n" " -b: send metric to both Chrome and Autotest\n" - " -c: return exit status 0 if user consents to stats, 1 otherwise\n" + " -c: return exit status 0 if user consents to stats, 1 otherwise,\n" + " in guest mode always return 1\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" From b1640eee939813efd80e3ead1cd0e10d5a3d28d5 Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Wed, 27 Aug 2014 16:22:19 -0700 Subject: [PATCH 155/200] metrics: fix memory leaks in unittest. This fixes the memory leaks exposed in unittest. BUG=chromium:408309 TEST=The memory leak related to |sender_| is gone. Change-Id: I92970d0560f6ccd1ccd7f5022ece8cc5eba4a674 Reviewed-on: https://chromium-review.googlesource.com/214578 Reviewed-by: Yunlian Jiang Tested-by: Yunlian Jiang Commit-Queue: Yunlian Jiang --- metrics/uploader/curl_sender.h | 2 +- metrics/uploader/sender.h | 1 + metrics/uploader/upload_service.h | 2 +- metrics/uploader/upload_service_test.cc | 42 +++++++++++++------------ 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/metrics/uploader/curl_sender.h b/metrics/uploader/curl_sender.h index fc5b0f481..b7e958524 100644 --- a/metrics/uploader/curl_sender.h +++ b/metrics/uploader/curl_sender.h @@ -14,7 +14,7 @@ class CurlSender : public Sender { public: explicit CurlSender(std::string server_url); - + virtual ~CurlSender() {} // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous // POST request to server_url. bool Send(const std::string& content, const std::string& hash) override; diff --git a/metrics/uploader/sender.h b/metrics/uploader/sender.h index 70d29cb7e..521183407 100644 --- a/metrics/uploader/sender.h +++ b/metrics/uploader/sender.h @@ -10,6 +10,7 @@ // Abstract class for a Sender that uploads a metrics message. class Sender { public: + virtual ~Sender() {} // Sends a message |content| with its sha1 hash |hash| virtual bool Send(const std::string& content, const std::string& hash) = 0; }; diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index ed5ab8526..80b1bf371 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -128,7 +128,7 @@ class UploadService : public base::HistogramFlattener { SystemProfileSetter* system_profile_setter_; base::HistogramSnapshotManager histogram_snapshot_manager_; - Sender* sender_; + scoped_ptr sender_; int failed_upload_count_; scoped_ptr current_log_; scoped_ptr staged_log_; diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index 9e5bd8d17..7a82624fa 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -23,7 +23,8 @@ class UploadServiceTest : public testing::Test { protected: UploadServiceTest() : upload_service_(), exit_manager_(new base::AtExitManager()) { - upload_service_.sender_ = &sender_; + sender_ = new SenderMock; + upload_service_.sender_.reset(sender_); upload_service_.system_profile_setter_ = new MockSystemProfileSetter(); upload_service_.Init(); } @@ -32,7 +33,7 @@ class UploadServiceTest : public testing::Test { CHECK(dir_.CreateUniqueTempDir()); upload_service_.GatherHistograms(); upload_service_.Reset(); - sender_.Reset(); + sender_->Reset(); cache_.is_testing_ = true; chromeos_metrics::PersistentInteger::SetTestingMode(true); @@ -45,7 +46,7 @@ class UploadServiceTest : public testing::Test { } base::ScopedTempDir dir_; - SenderMock sender_; + SenderMock *sender_; SystemProfileCache cache_; UploadService upload_service_; @@ -90,20 +91,20 @@ TEST_F(UploadServiceTest, UnknownCrashIgnored) { } TEST_F(UploadServiceTest, FailedSendAreRetried) { - sender_.set_should_succeed(false); + sender_->set_should_succeed(false); upload_service_.AddSample(*Crash("user")); upload_service_.UploadEvent(); - EXPECT_EQ(1, sender_.send_call_count()); - std::string sent_string = sender_.last_message(); + EXPECT_EQ(1, sender_->send_call_count()); + std::string sent_string = sender_->last_message(); upload_service_.UploadEvent(); - EXPECT_EQ(2, sender_.send_call_count()); - EXPECT_EQ(sent_string, sender_.last_message()); + EXPECT_EQ(2, sender_->send_call_count()); + EXPECT_EQ(sent_string, sender_->last_message()); } TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) { - sender_.set_should_succeed(false); + sender_->set_should_succeed(false); upload_service_.AddSample(*Crash("user")); for (int i = 0; i < UploadService::kMaxFailedUpload; i++) { @@ -118,7 +119,7 @@ TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) { TEST_F(UploadServiceTest, EmptyLogsAreNotSent) { upload_service_.UploadEvent(); EXPECT_FALSE(upload_service_.current_log_); - EXPECT_EQ(0, sender_.send_call_count()); + EXPECT_EQ(0, sender_->send_call_count()); } TEST_F(UploadServiceTest, LogEmptyByDefault) { @@ -133,12 +134,12 @@ TEST_F(UploadServiceTest, CanSendMultipleTimes) { upload_service_.AddSample(*Crash("user")); upload_service_.UploadEvent(); - std::string first_message = sender_.last_message(); + std::string first_message = sender_->last_message(); upload_service_.AddSample(*Crash("kernel")); upload_service_.UploadEvent(); - EXPECT_NE(first_message, sender_.last_message()); + EXPECT_NE(first_message, sender_->last_message()); } TEST_F(UploadServiceTest, LogEmptyAfterUpload) { @@ -197,16 +198,17 @@ TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) { upload_service_.AddSample(*histogram.get()); upload_service_.UploadEvent(); - EXPECT_EQ(1, sender_.send_call_count()); - EXPECT_TRUE(sender_.is_good_proto()); - EXPECT_EQ(1, sender_.last_message_proto().histogram_event().size()); + EXPECT_EQ(1, sender_->send_call_count()); + EXPECT_TRUE(sender_->is_good_proto()); + EXPECT_EQ(1, sender_->last_message_proto().histogram_event().size()); - EXPECT_EQ(name, sender_.last_message_proto().system_profile().os().name()); + EXPECT_EQ(name, sender_->last_message_proto().system_profile().os().name()); EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_BETA, - sender_.last_message_proto().system_profile().channel()); - EXPECT_NE(0, sender_.last_message_proto().client_id()); - EXPECT_NE(0, sender_.last_message_proto().system_profile().build_timestamp()); - EXPECT_NE(0, sender_.last_message_proto().session_id()); + sender_->last_message_proto().system_profile().channel()); + EXPECT_NE(0, sender_->last_message_proto().client_id()); + EXPECT_NE(0, + sender_->last_message_proto().system_profile().build_timestamp()); + EXPECT_NE(0, sender_->last_message_proto().session_id()); } TEST_F(UploadServiceTest, PersistentGUID) { From 14595032aa4781d430caff2eed02b9ce74132749 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Thu, 28 Aug 2014 14:59:56 -0700 Subject: [PATCH 156/200] platform2: Replace NULL with nullptr in minor components Replace NULL with nullptr in C++11-enabled components of platform2 that don't have too many occurrences of NULL. Other major "offenders" will be handled separately. BUG=None TEST=FEATURES=test emerge-link app-shell-launcher buffet easy-unlock platform2 metrics wimax_manager Change-Id: I61b25a057e3d6865908bc74f9f3d4cb55e08af26 Reviewed-on: https://chromium-review.googlesource.com/214837 Tested-by: Alex Vakulenko Reviewed-by: Ben Chan Commit-Queue: Alex Vakulenko --- metrics/metrics_daemon.cc | 8 ++++---- metrics/metrics_daemon_test.cc | 6 +++--- metrics/metrics_library.cc | 6 +++--- metrics/metrics_library_test.cc | 3 ++- metrics/timer.cc | 2 +- metrics/timer_test.cc | 2 +- metrics/uploader/curl_sender.cc | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 9e0bf0437..0552adb2a 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -215,7 +215,7 @@ void MetricsDaemon::Init(bool testing, const string& scaling_max_freq_path, const string& cpuinfo_max_freq_path) { testing_ = testing; - DCHECK(metrics_lib != NULL); + DCHECK(metrics_lib != nullptr); metrics_lib_ = metrics_lib; // Get ticks per second (HZ) on this system. @@ -284,7 +284,7 @@ void MetricsDaemon::Init(bool testing, LOG_IF(FATAL, dbus_error_is_set(&error)) << "No D-Bus connection: " << SAFE_MESSAGE(error); - dbus_connection_setup_with_g_main(connection, NULL); + dbus_connection_setup_with_g_main(connection, nullptr); vector matches; matches.push_back( @@ -305,7 +305,7 @@ void MetricsDaemon::Init(bool testing, // Adds the D-Bus filter routine to be called back whenever one of // the registered D-Bus matches is successful. The daemon is not // activated for D-Bus messages that don't match. - CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL)); + CHECK(dbus_connection_add_filter(connection, MessageFilter, this, nullptr)); update_stats_timeout_id_ = g_timeout_add(kUpdateStatsIntervalMs, &HandleUpdateStatsTimeout, this); @@ -317,7 +317,7 @@ void MetricsDaemon::Init(bool testing, } void MetricsDaemon::Loop() { - GMainLoop* loop = g_main_loop_new(NULL, false); + GMainLoop* loop = g_main_loop_new(nullptr, false); g_main_loop_run(loop); } diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 1e9cb3fae..9528e774f 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -225,7 +225,7 @@ TEST_F(MetricsDaemonTest, MessageFilter) { DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); DBusHandlerResult res = - MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_); EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res); DeleteDBusMessage(msg); @@ -235,7 +235,7 @@ TEST_F(MetricsDaemonTest, MessageFilter) { "org.chromium.CrashReporter", "UserCrash", signal_args); - res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_); EXPECT_EQ(DBUS_HANDLER_RESULT_HANDLED, res); DeleteDBusMessage(msg); @@ -246,7 +246,7 @@ TEST_F(MetricsDaemonTest, MessageFilter) { "org.chromium.UnknownService.Manager", "StateChanged", signal_args); - res = MetricsDaemon::MessageFilter(/* connection */ NULL, msg, &daemon_); + res = MetricsDaemon::MessageFilter(/* connection */ nullptr, msg, &daemon_); EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res); DeleteDBusMessage(msg); } diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index ac97c3e2a..c352bcf51 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -59,7 +59,7 @@ bool MetricsLibrary::IsDeviceMounted(const char* device_name, char* buffer, int buffer_size, bool* result) { - if (buffer == NULL || buffer_size < 1) + if (buffer == nullptr || buffer_size < 1) return false; int mounts_fd = open(mounts_file, O_RDONLY); if (mounts_fd < 0) @@ -121,7 +121,7 @@ bool MetricsLibrary::IsGuestMode() { bool MetricsLibrary::AreMetricsEnabled() { static struct stat stat_buffer; - time_t this_check_time = time(NULL); + time_t this_check_time = time(nullptr); if (this_check_time != cached_enabled_time_) { cached_enabled_time_ = this_check_time; @@ -157,7 +157,7 @@ void MetricsLibrary::Init() { bool MetricsLibrary::SendToAutotest(const std::string& name, int value) { FILE* autotest_file = fopen(kAutotestPath, "a+"); - if (autotest_file == NULL) { + if (autotest_file == nullptr) { PLOG(ERROR) << kAutotestPath << ": fopen"; return false; } diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index ccd41294c..7bf5c6391 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -152,7 +152,8 @@ void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) { ASSERT_EQ(!to_value, lib_.AreMetricsEnabled()); EXPECT_CALL(*device_policy_, GetMetricsEnabled(_)) .WillOnce(SetMetricsPolicy(to_value)); - ASSERT_LT(abs(static_cast(time(NULL) - lib_.cached_enabled_time_)), 5); + ASSERT_LT(abs(static_cast(time(nullptr) - lib_.cached_enabled_time_)), + 5); // Sleep one second (or cheat to be faster). --lib_.cached_enabled_time_; ASSERT_EQ(to_value, lib_.AreMetricsEnabled()); diff --git a/metrics/timer.cc b/metrics/timer.cc index dd7842500..99f68fefd 100644 --- a/metrics/timer.cc +++ b/metrics/timer.cc @@ -86,7 +86,7 @@ bool Timer::GetElapsedTime(base::TimeDelta* elapsed_time) const { } // static -MetricsLibraryInterface* TimerReporter::metrics_lib_ = NULL; +MetricsLibraryInterface* TimerReporter::metrics_lib_ = nullptr; TimerReporter::TimerReporter(const std::string& histogram_name, int min, int max, int num_buckets) diff --git a/metrics/timer_test.cc b/metrics/timer_test.cc index a712605be..ec6c6bdba 100644 --- a/metrics/timer_test.cc +++ b/metrics/timer_test.cc @@ -419,7 +419,7 @@ class TimerReporterTest : public testing::Test { } virtual void TearDown() { - timer_reporter_.set_metrics_lib(NULL); + timer_reporter_.set_metrics_lib(nullptr); } TimerReporter timer_reporter_; diff --git a/metrics/uploader/curl_sender.cc b/metrics/uploader/curl_sender.cc index 023b5f0f1..5049ef4b9 100644 --- a/metrics/uploader/curl_sender.cc +++ b/metrics/uploader/curl_sender.cc @@ -29,7 +29,7 @@ bool CurlSender::Send(const std::string& content, base::HexEncode(content_hash.data(), content_hash.size()); curl_slist* headers = - curl_slist_append(NULL, ("X-Chrome-UMA-Log-SHA1: " + hash).c_str()); + curl_slist_append(nullptr, ("X-Chrome-UMA-Log-SHA1: " + hash).c_str()); if (!headers) { DLOG(ERROR) << "failed setting the headers"; curl_easy_cleanup(postrequest); From ef3aebe6a77b2e0a89f41e5dab6fe04188c4b298 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Mon, 25 Aug 2014 18:27:48 -0700 Subject: [PATCH 157/200] metrics: Activate the uploader for embedded boards Now that the code is under tests, add an extra flag to metrics_daemon.conf to enable the uploader when chrome is not available. BUG=chromium:389222 TEST=build and deploy metrics on gizmo. metrics_daemon is running and the metrics are reported. CQ-DEPEND=CL:214286 Change-Id: I5daa72a95c3bf1c15387a40c6964be44bc6da082 Reviewed-on: https://chromium-review.googlesource.com/214285 Reviewed-by: Mike Frysinger Commit-Queue: Bertrand Simonnet Tested-by: Bertrand Simonnet --- metrics/init/metrics_daemon.conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/metrics/init/metrics_daemon.conf b/metrics/init/metrics_daemon.conf index 52fe90d46..3fc2dd7b4 100644 --- a/metrics/init/metrics_daemon.conf +++ b/metrics/init/metrics_daemon.conf @@ -16,5 +16,8 @@ pre-start script mkdir -p /var/lib/metrics end script +# metrics will update the next line to add -uploader for embedded builds. +env DAEMON_FLAGS="" + expect fork -exec metrics_daemon +exec metrics_daemon ${DAEMON_FLAGS} From e2c2d8947b699af976bc05284371bcb1944f6d06 Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Tue, 2 Sep 2014 10:40:19 -0700 Subject: [PATCH 158/200] metrics: fix memory leaks in unittest. This fixes the memory leaks exposed in unittest. BUG=chromium:408309 TEST=USE="clang asan" FEATURES="test" emerge-amd64-generic metrics passes. Change-Id: I6f612499d7e67fa9171b95de4dbfac4e7f6698a0 Reviewed-on: https://chromium-review.googlesource.com/215887 Reviewed-by: Yunlian Jiang Tested-by: Yunlian Jiang Commit-Queue: Yunlian Jiang --- metrics/uploader/system_profile_setter.h | 1 + metrics/uploader/upload_service.cc | 2 +- metrics/uploader/upload_service.h | 3 ++- metrics/uploader/upload_service_test.cc | 10 +++++++--- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/metrics/uploader/system_profile_setter.h b/metrics/uploader/system_profile_setter.h index 1e2baab49..c535664a0 100644 --- a/metrics/uploader/system_profile_setter.h +++ b/metrics/uploader/system_profile_setter.h @@ -13,6 +13,7 @@ class ChromeUserMetricsExtension; // information to simplify testing. class SystemProfileSetter { public: + virtual ~SystemProfileSetter() {} // Populates the protobuf with system informations. virtual void Populate(metrics::ChromeUserMetricsExtension* profile_proto) = 0; }; diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 62f5d920e..8b8e351bb 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -53,7 +53,7 @@ void UploadService::StartNewLog() { CHECK(!staged_log_) << "the staged log should be discarded before starting " "a new metrics log"; MetricsLog* log = new MetricsLog(); - log->PopulateSystemProfile(system_profile_setter_); + log->PopulateSystemProfile(system_profile_setter_.get()); current_log_.reset(log); } diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index 80b1bf371..d677424e1 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -12,6 +12,7 @@ #include "base/metrics/histogram_snapshot_manager.h" #include "uploader/metrics_log.h" #include "uploader/sender.h" +#include "uploader/system_profile_cache.h" namespace metrics { class ChromeUserMetricsExtension; @@ -126,7 +127,7 @@ class UploadService : public base::HistogramFlattener { // Returns the current log. If there is no current log, creates it first. MetricsLog* GetOrCreateCurrentLog(); - SystemProfileSetter* system_profile_setter_; + scoped_ptr system_profile_setter_; base::HistogramSnapshotManager histogram_snapshot_manager_; scoped_ptr sender_; int failed_upload_count_; diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index 7a82624fa..5076e614c 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -25,7 +25,7 @@ class UploadServiceTest : public testing::Test { : upload_service_(), exit_manager_(new base::AtExitManager()) { sender_ = new SenderMock; upload_service_.sender_.reset(sender_); - upload_service_.system_profile_setter_ = new MockSystemProfileSetter(); + upload_service_.system_profile_setter_.reset(new MockSystemProfileSetter()); upload_service_.Init(); } @@ -46,7 +46,7 @@ class UploadServiceTest : public testing::Test { } base::ScopedTempDir dir_; - SenderMock *sender_; + SenderMock* sender_; SystemProfileCache cache_; UploadService upload_service_; @@ -191,8 +191,12 @@ TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) { base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time()); scoped_ptr histogram = metrics::MetricSample::SparseHistogramSample("myhistogram", 1); + SystemProfileCache* local_cache_ = new SystemProfileCache; + local_cache_->is_testing_ = true; + local_cache_->session_id_.reset(new chromeos_metrics::PersistentInteger( + dir_.path().Append("session_id").value())); - upload_service_.system_profile_setter_ = &cache_; + upload_service_.system_profile_setter_.reset(local_cache_); // Reset to create the new log with the profile setter. upload_service_.Reset(); upload_service_.AddSample(*histogram.get()); From 652d6971b19c2db2d3dcd1f8db36abadfc22079e Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Wed, 3 Sep 2014 17:23:46 -0700 Subject: [PATCH 159/200] metrics: Include base/macros.h instead of base/basictypes.h metrics no longer uses the integer types from base/basictypes.h. It should simply include base/macros.h for the DISALLOW_COPY_AND_ASSIGN macro instead. BUG=None TEST=`FEATURES=test emerge-$BOARD metrics platform2` Change-Id: Id325bcb6c9d3318b5b78e42a04da9c53d0a8c3ce Reviewed-on: https://chromium-review.googlesource.com/216349 Tested-by: Alex Vakulenko Tested-by: Ben Chan Reviewed-by: Alex Vakulenko Commit-Queue: Ben Chan --- metrics/metrics_library.h | 2 +- metrics/timer.h | 1 + metrics/timer_mock.h | 2 -- metrics/uploader/metrics_log.h | 2 ++ 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 97b2b72e2..38ab47fdb 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -9,8 +9,8 @@ #include #include -#include #include +#include #include #include // for FRIEND_TEST diff --git a/metrics/timer.h b/metrics/timer.h index 654c2cb31..52cc57892 100644 --- a/metrics/timer.h +++ b/metrics/timer.h @@ -9,6 +9,7 @@ #include +#include #include #include #include // for FRIEND_TEST diff --git a/metrics/timer_mock.h b/metrics/timer_mock.h index 6eef76152..2f2d0f4b6 100644 --- a/metrics/timer_mock.h +++ b/metrics/timer_mock.h @@ -5,10 +5,8 @@ #ifndef METRICS_TIMER_MOCK_H_ #define METRICS_TIMER_MOCK_H_ - #include -#include #include #include "metrics/timer.h" diff --git a/metrics/uploader/metrics_log.h b/metrics/uploader/metrics_log.h index bd1958cbf..37a82bdd4 100644 --- a/metrics/uploader/metrics_log.h +++ b/metrics/uploader/metrics_log.h @@ -7,6 +7,8 @@ #include +#include + #include "components/metrics/metrics_log_base.h" // This file defines a set of user experience metrics data recorded by From ec991df012c662698e673d2f607d025f0b72afab Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Thu, 4 Sep 2014 16:16:28 -0700 Subject: [PATCH 160/200] metrics: Switch uploader to use http_utils from libchromeos Removed explicit dependency on libcurl from metrics and using chromeos::http::PostText() function instead. BUG=chromium:411076 TEST=FEATURES=test emerge-link metrics Change-Id: Ida883fa18d266e9bb87c3a4271e57d44c9308c79 Reviewed-on: https://chromium-review.googlesource.com/216526 Reviewed-by: Bertrand Simonnet Tested-by: Alex Vakulenko Commit-Queue: Alex Vakulenko --- metrics/metrics.gyp | 3 +- metrics/uploader/curl_sender.cc | 70 ------------------------------ metrics/uploader/curl_sender.h | 33 -------------- metrics/uploader/sender_http.cc | 36 +++++++++++++++ metrics/uploader/sender_http.h | 29 +++++++++++++ metrics/uploader/upload_service.cc | 34 +++++++-------- metrics/uploader/upload_service.h | 6 +-- 7 files changed, 86 insertions(+), 125 deletions(-) delete mode 100644 metrics/uploader/curl_sender.cc delete mode 100644 metrics/uploader/curl_sender.h create mode 100644 metrics/uploader/sender_http.cc create mode 100644 metrics/uploader/sender_http.h diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 7a4338b05..d8f697ce8 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -12,7 +12,6 @@ 'gthread-2.0', 'libchrome-<(libbase_ver)', 'libchromeos-<(libbase_ver)', - 'libcurl', ] }, 'cflags_cc': [ @@ -83,7 +82,7 @@ 'uploader/upload_service.cc', 'uploader/metrics_log.cc', 'uploader/system_profile_cache.cc', - 'uploader/curl_sender.cc', + 'uploader/sender_http.cc', 'components/metrics/metrics_log_base.cc', 'components/metrics/metrics_log_manager.cc', 'components/metrics/metrics_hashes.cc', diff --git a/metrics/uploader/curl_sender.cc b/metrics/uploader/curl_sender.cc deleted file mode 100644 index 5049ef4b9..000000000 --- a/metrics/uploader/curl_sender.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "uploader/curl_sender.h" - -#include -#include - -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" - -CurlSender::CurlSender(const std::string server_url) - : server_url_(server_url) {} - -bool CurlSender::Send(const std::string& content, - const std::string& content_hash) { - CURL* postrequest = curl_easy_init(); - - if (!postrequest) { - DLOG(ERROR) << "Error creating the post request"; - return false; - } - - curl_easy_setopt(postrequest, CURLOPT_URL, server_url_.c_str()); - curl_easy_setopt(postrequest, CURLOPT_POST, 1); - - const std::string hash = - base::HexEncode(content_hash.data(), content_hash.size()); - - curl_slist* headers = - curl_slist_append(nullptr, ("X-Chrome-UMA-Log-SHA1: " + hash).c_str()); - if (!headers) { - DLOG(ERROR) << "failed setting the headers"; - curl_easy_cleanup(postrequest); - return false; - } - - std::string output; - - curl_easy_setopt(postrequest, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(postrequest, CURLOPT_POSTFIELDSIZE, content.size()); - curl_easy_setopt(postrequest, CURLOPT_POSTFIELDS, content.c_str()); - - // Set the callback function used to read the response and the destination. - curl_easy_setopt(postrequest, CURLOPT_WRITEFUNCTION, ReadData); - curl_easy_setopt(postrequest, CURLOPT_WRITEDATA, &output); - - CURLcode result = curl_easy_perform(postrequest); - - if (result == CURLE_OK && output == "OK") { - curl_easy_cleanup(postrequest); - return true; - } - - curl_easy_cleanup(postrequest); - - return false; -} - -// static -size_t CurlSender::ReadData(void* buffer, size_t size, size_t nmember, - std::string* out) { - CHECK(out); - - // This function might be called several time so we want to append the data at - // the end of the string. - *out += std::string(static_cast(buffer), size * nmember); - return size * nmember; -} diff --git a/metrics/uploader/curl_sender.h b/metrics/uploader/curl_sender.h deleted file mode 100644 index b7e958524..000000000 --- a/metrics/uploader/curl_sender.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef METRICS_UPLOADER_CURL_SENDER_H_ -#define METRICS_UPLOADER_CURL_SENDER_H_ - -#include - -#include "base/compiler_specific.h" -#include "uploader/sender.h" - -// Sender implemented using libcurl -class CurlSender : public Sender { - public: - explicit CurlSender(std::string server_url); - virtual ~CurlSender() {} - // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous - // POST request to server_url. - bool Send(const std::string& content, const std::string& hash) override; - - // Static callback required by curl to retrieve the response data. - // - // Copies |size| * |nmember| bytes of data from |buffer| to |out|. - // Returns the number of bytes copied. - static size_t ReadData(void* buffer, size_t size, size_t nmember, - std::string* out); - - private: - const std::string server_url_; -}; - -#endif // METRICS_UPLOADER_CURL_SENDER_H_ diff --git a/metrics/uploader/sender_http.cc b/metrics/uploader/sender_http.cc new file mode 100644 index 000000000..585287f22 --- /dev/null +++ b/metrics/uploader/sender_http.cc @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics/uploader/sender_http.h" + +#include + +#include +#include +#include +#include + +HttpSender::HttpSender(const std::string server_url) + : server_url_(server_url) {} + +bool HttpSender::Send(const std::string& content, + const std::string& content_hash) { + const std::string hash = + base::HexEncode(content_hash.data(), content_hash.size()); + + chromeos::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}}; + chromeos::ErrorPtr error; + auto response = chromeos::http::PostText( + server_url_, + content.c_str(), + chromeos::mime::application::kWwwFormUrlEncoded, + headers, + chromeos::http::Transport::CreateDefault(), + &error); + if (!response || response->GetDataAsString() != "OK") { + DLOG(ERROR) << "Failed to send data: " << error->GetMessage(); + return false; + } + return true; +} diff --git a/metrics/uploader/sender_http.h b/metrics/uploader/sender_http.h new file mode 100644 index 000000000..4880b2880 --- /dev/null +++ b/metrics/uploader/sender_http.h @@ -0,0 +1,29 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_SENDER_HTTP_H_ +#define METRICS_UPLOADER_SENDER_HTTP_H_ + +#include + +#include + +#include "metrics/uploader/sender.h" + +// Sender implemented using http_utils from libchromeos +class HttpSender : public Sender { + public: + explicit HttpSender(std::string server_url); + ~HttpSender() override = default; + // Sends |content| whose SHA1 hash is |hash| to server_url with a synchronous + // POST request to server_url. + bool Send(const std::string& content, const std::string& hash) override; + + private: + const std::string server_url_; + + DISALLOW_COPY_AND_ASSIGN(HttpSender); +}; + +#endif // METRICS_UPLOADER_SENDER_HTTP_H_ diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 8b8e351bb..8094fecdf 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -2,26 +2,26 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "uploader/upload_service.h" +#include "metrics/uploader/upload_service.h" -#include #include #include -#include "base/logging.h" -#include "base/memory/scoped_vector.h" -#include "base/metrics/histogram.h" -#include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_snapshot_manager.h" -#include "base/metrics/sparse_histogram.h" -#include "base/metrics/statistics_recorder.h" -#include "base/sha1.h" -#include "components/metrics/chromeos/metric_sample.h" -#include "components/metrics/chromeos/serialization_utils.h" -#include "gflags/gflags.h" -#include "uploader/curl_sender.h" -#include "uploader/metrics_log.h" -#include "uploader/system_profile_cache.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "metrics/uploader/metrics_log.h" +#include "metrics/uploader/sender_http.h" +#include "metrics/uploader/system_profile_cache.h" DEFINE_int32( upload_interval_secs, @@ -41,7 +41,7 @@ const int UploadService::kMaxFailedUpload = 10; UploadService::UploadService() : system_profile_setter_(new SystemProfileCache()), histogram_snapshot_manager_(this), - sender_(new CurlSender(FLAGS_server)) { + sender_(new HttpSender(FLAGS_server)) { } void UploadService::Init() { diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index d677424e1..0d17e0e39 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -10,9 +10,9 @@ #include "base/metrics/histogram_base.h" #include "base/metrics/histogram_flattener.h" #include "base/metrics/histogram_snapshot_manager.h" -#include "uploader/metrics_log.h" -#include "uploader/sender.h" -#include "uploader/system_profile_cache.h" +#include "metrics/uploader/metrics_log.h" +#include "metrics/uploader/sender.h" +#include "metrics/uploader/system_profile_cache.h" namespace metrics { class ChromeUserMetricsExtension; From 4bcffd3d709c04385043808b9ca515c8a28b5fe1 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Fri, 5 Sep 2014 05:30:40 -0700 Subject: [PATCH 161/200] metrics: add bsimonnet to OWNERS BUG=None TEST=None Change-Id: I9e89ac959c352560a29e46fa3ea56a91a65c8941 Reviewed-on: https://chromium-review.googlesource.com/216583 Reviewed-by: Daniel Erat Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- metrics/OWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/OWNERS b/metrics/OWNERS index a680f7ca9..7f5e50dab 100644 --- a/metrics/OWNERS +++ b/metrics/OWNERS @@ -1,2 +1,3 @@ semenzato@chromium.org derat@chromium.org +bsimonnet@chromium.org From 43bee50572d68ee803f8e53d5e736658fcea6222 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Fri, 5 Sep 2014 05:27:52 -0700 Subject: [PATCH 162/200] metrics: Document uploader functionality Metrics uploader can be activated with the metrics_uploader use flag, update README to reflect that. Also fix the section about activating metrics. BUG=None TEST=None Change-Id: I2c6d0abe6536eb419c5b49f149b6d3f097670325 Reviewed-on: https://chromium-review.googlesource.com/216631 Reviewed-by: Mike Frysinger Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- metrics/README | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/metrics/README b/metrics/README index 2ecec5f42..6150dc7be 100644 --- a/metrics/README +++ b/metrics/README @@ -3,8 +3,11 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. The Chrome OS "metrics" package contains utilities for client-side user metric -collection. The collected data is sent to Chrome for transport to the UMA -server. +collection. +When Chrome is installed, Chrome will take care of aggregating and uploading the +metrics to the UMA server. +When Chrome is not installed (embedded build) and the metrics_uploader USE flag +is set, metrics_daemon will aggregate and upload the metrics itself. ================================================================================ @@ -36,8 +39,8 @@ UMA. In order to use the library in a module, you need to do the following: For more information on the C API see c_metrics_library.h. - Samples are sent to Chrome only if the "/home/chronos/Consent To Send Stats" - file exists (see the AreMetricsEnabled API method). Normally, this file is - created when the user opts into metrics collection. + file exists or the metrics are declared enabled in the policy file (see the + AreMetricsEnabled API method). - On the target platform, shortly after the sample is sent, it should be visible in Chrome through "about:histograms". From 37595f893f8ffbaa689d8d0a4c4dffb0593e7d5b Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Fri, 5 Sep 2014 08:20:59 -0700 Subject: [PATCH 163/200] Update to build against libchrome-293518. libchrome is updated from revision 271506 to 293518. This CL updates platform2 code to build against libchrome-293518 with the following changes: - LOG_ERROR_REPORT is removed (https://codereview.chromium.org/331143007) - StringToLowerASCII is moved to base namespace (https://codereview.chromium.org/448853002) BUG=chromium:411001 CQ-DEPEND=CL:216584 CQ-DEPEND=CL:216585 CQ-DEPEND=CL:216586 CQ-DEPEND=CL:216511 CQ-DEPEND=CL:217084 CQ-DEPEND=CL:217085 TEST=Trybot run on paladin, release, and chromiumos-sdk builders. Change-Id: I9fbdad30b3a7c79c1ec4e208664b8befea31a3ec Reviewed-on: https://chromium-review.googlesource.com/216589 Reviewed-by: Alex Vakulenko Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/{libmetrics-271506.gyp => libmetrics-293518.gyp} | 2 +- metrics/metrics.gyp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) rename metrics/{libmetrics-271506.gyp => libmetrics-293518.gyp} (75%) diff --git a/metrics/libmetrics-271506.gyp b/metrics/libmetrics-293518.gyp similarity index 75% rename from metrics/libmetrics-271506.gyp rename to metrics/libmetrics-293518.gyp index 6df23813c..1b0f78cf4 100644 --- a/metrics/libmetrics-271506.gyp +++ b/metrics/libmetrics-293518.gyp @@ -1,6 +1,6 @@ { 'variables': { - 'libbase_ver': 271506, + 'libbase_ver': 293518, }, 'includes': [ '../metrics/libmetrics.gypi', diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index d8f697ce8..b5c356186 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -1,7 +1,4 @@ { - 'variables': { - 'libbase_ver': 271506, - }, 'target_defaults': { 'variables': { 'deps': [ From 51bf92a3cbe1fe27153baa0becae49075d9ea3b0 Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Fri, 5 Sep 2014 08:21:06 -0700 Subject: [PATCH 164/200] Update code to include base/files/file_util.h file_util.h was moved from base to base/files (https://codereview.chromium.org/468253002). This CL updates platform2 code to include base/files/file_util.h instead of base/file_util.h. BUG=chromium:411001 TEST=Trybot run on paladin, release, and chromiumos-sdk builders. Change-Id: I488925b54615e131e508a460dc1a27f88168f936 Reviewed-on: https://chromium-review.googlesource.com/216851 Reviewed-by: Alex Vakulenko Tested-by: Ben Chan Commit-Queue: Ben Chan --- metrics/metrics_daemon.cc | 2 +- metrics/metrics_daemon_test.cc | 2 +- metrics/metrics_library_test.cc | 2 +- metrics/persistent_integer_test.cc | 2 +- metrics/uploader/system_profile_cache.cc | 2 +- metrics/uploader/upload_service_test.cc | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 0552adb2a..07c32209d 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -14,8 +14,8 @@ #include #include -#include #include +#include #include #include #include diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 9528e774f..0116a1934 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc index 7bf5c6391..7ede303c8 100644 --- a/metrics/metrics_library_test.cc +++ b/metrics/metrics_library_test.cc @@ -4,7 +4,7 @@ #include -#include +#include #include #include #include diff --git a/metrics/persistent_integer_test.cc b/metrics/persistent_integer_test.cc index 097ec7da6..a56aedec9 100644 --- a/metrics/persistent_integer_test.cc +++ b/metrics/persistent_integer_test.cc @@ -5,8 +5,8 @@ #include #include -#include #include +#include #include "metrics/persistent_integer.h" diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index fd7996140..12e8bece3 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -8,7 +8,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/guid.h" #include "base/logging.h" #include "base/sys_info.h" diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index 5076e614c..d60413d45 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -5,7 +5,7 @@ #include #include "base/at_exit.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/sys_info.h" From eb815be5a9c3df5fcede522f02623e525d65bbc0 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Thu, 11 Sep 2014 07:38:17 -0700 Subject: [PATCH 165/200] metrics: Fix metrics_uploader on VMs metrics_uploader must be in testing mode so that it does not try to grab the real hardware id (not available on VMs). BUG=chromium:413256 TEST=FEATURES=test emerge-amd64-generic metrics. TEST=platform_MetricsUploader succeeds on a gizmo VM. Change-Id: I9e508c8661dfdb7933161b0d41ef4cf9bd7db2c6 Reviewed-on: https://chromium-review.googlesource.com/217760 Reviewed-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet Tested-by: Bertrand Simonnet --- metrics/metrics_daemon.cc | 4 +++- metrics/metrics_daemon_main.cc | 2 +- metrics/uploader/system_profile_cache.cc | 8 ++++---- metrics/uploader/system_profile_cache.h | 4 ++-- metrics/uploader/upload_service.cc | 4 ++-- metrics/uploader/upload_service.h | 2 +- metrics/uploader/upload_service_test.cc | 10 +++++----- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 07c32209d..c0c77faea 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -187,6 +187,8 @@ void MetricsDaemon::Run(bool run_as_daemon) { } void MetricsDaemon::RunUploaderTest() { + upload_service_.reset(new UploadService(testing_)); + upload_service_->Init(); upload_service_->UploadEvent(); } @@ -311,7 +313,7 @@ void MetricsDaemon::Init(bool testing, g_timeout_add(kUpdateStatsIntervalMs, &HandleUpdateStatsTimeout, this); if (uploader_active) { - upload_service_.reset(new UploadService()); + upload_service_.reset(new UploadService(testing_)); upload_service_->Init(); } } diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 93e24142b..e529afd4d 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -65,7 +65,7 @@ int main(int argc, char** argv) { MetricsLibrary metrics_lib; metrics_lib.Init(); MetricsDaemon daemon; - daemon.Init(false, + daemon.Init(FLAGS_uploader_test, FLAGS_uploader | FLAGS_uploader_test, &metrics_lib, MetricsMainDiskStatsPath(), diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index 12e8bece3..604c060ca 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -22,9 +22,9 @@ const char* SystemProfileCache::kPersistentGUIDFile = const char* SystemProfileCache::kPersistentSessionIdFilename = "Sysinfo.SessionId"; -SystemProfileCache::SystemProfileCache() +SystemProfileCache::SystemProfileCache(bool testing) : initialized_(false), - is_testing_(false), + testing_(testing), session_id_(new chromeos_metrics::PersistentInteger( kPersistentSessionIdFilename)) { } @@ -49,7 +49,7 @@ bool SystemProfileCache::Initialize() { profile_.channel = ProtoChannelFromString(channel_string); profile_.client_id = - is_testing_ ? "client_id_test" : GetPersistentGUID(kPersistentGUIDFile); + testing_ ? "client_id_test" : GetPersistentGUID(kPersistentGUIDFile); // Increment the session_id everytime we initialize this. If metrics_daemon // does not crash, this should correspond to the number of reboots of the @@ -105,7 +105,7 @@ std::string SystemProfileCache::GetPersistentGUID(const std::string& filename) { bool SystemProfileCache::GetHardwareId(std::string* hwid) { CHECK(hwid); - if (is_testing_) { + if (testing_) { // if we are in test mode, we do not call crossystem directly. DLOG(INFO) << "skipping hardware id"; *hwid = ""; diff --git a/metrics/uploader/system_profile_cache.h b/metrics/uploader/system_profile_cache.h index 03e92fca5..f85c1b190 100644 --- a/metrics/uploader/system_profile_cache.h +++ b/metrics/uploader/system_profile_cache.h @@ -36,7 +36,7 @@ struct SystemProfile { // The cache is populated lazily. The only method needed is Populate. class SystemProfileCache : public SystemProfileSetter { public: - SystemProfileCache(); + explicit SystemProfileCache(bool testing); // Populates the ProfileSystem protobuf with system information. void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override; @@ -69,7 +69,7 @@ class SystemProfileCache : public SystemProfileSetter { bool GetHardwareId(std::string* hwid); bool initialized_; - bool is_testing_; + bool testing_; scoped_ptr session_id_; SystemProfile profile_; }; diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 8094fecdf..8e08f28c7 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -38,8 +38,8 @@ DEFINE_string(metrics_file, const int UploadService::kMaxFailedUpload = 10; -UploadService::UploadService() - : system_profile_setter_(new SystemProfileCache()), +UploadService::UploadService(bool testing) + : system_profile_setter_(new SystemProfileCache(testing)), histogram_snapshot_manager_(this), sender_(new HttpSender(FLAGS_server)) { } diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index 0d17e0e39..114c5e4ea 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -54,7 +54,7 @@ class SystemProfileSetter; // class UploadService : public base::HistogramFlattener { public: - UploadService(); + explicit UploadService(bool testing); void Init(); diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index d60413d45..94453b24a 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -22,7 +22,9 @@ class UploadServiceTest : public testing::Test { protected: UploadServiceTest() - : upload_service_(), exit_manager_(new base::AtExitManager()) { + : cache_(true), + upload_service_(true), + exit_manager_(new base::AtExitManager()) { sender_ = new SenderMock; upload_service_.sender_.reset(sender_); upload_service_.system_profile_setter_.reset(new MockSystemProfileSetter()); @@ -34,7 +36,6 @@ class UploadServiceTest : public testing::Test { upload_service_.GatherHistograms(); upload_service_.Reset(); sender_->Reset(); - cache_.is_testing_ = true; chromeos_metrics::PersistentInteger::SetTestingMode(true); cache_.session_id_.reset(new chromeos_metrics::PersistentInteger( @@ -123,7 +124,7 @@ TEST_F(UploadServiceTest, EmptyLogsAreNotSent) { } TEST_F(UploadServiceTest, LogEmptyByDefault) { - UploadService upload_service; + UploadService upload_service(true); // current_log_ should be initialized later as it needs AtExitManager to exit // in order to gather system information from SysInfo. @@ -191,8 +192,7 @@ TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) { base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time()); scoped_ptr histogram = metrics::MetricSample::SparseHistogramSample("myhistogram", 1); - SystemProfileCache* local_cache_ = new SystemProfileCache; - local_cache_->is_testing_ = true; + SystemProfileCache* local_cache_ = new SystemProfileCache(true); local_cache_->session_id_.reset(new chromeos_metrics::PersistentInteger( dir_.path().Append("session_id").value())); From 0be0f736d7d28b20e116422d563224a4f68d5fb5 Mon Sep 17 00:00:00 2001 From: Nathan Bullock Date: Thu, 11 Sep 2014 10:29:17 -0400 Subject: [PATCH 166/200] metrics: Add more log messages to uploader TEST=compiled BUG=none Change-Id: Iee49b104d0b6214e035f70fa108ae7464bd7988f Reviewed-on: https://chromium-review.googlesource.com/217338 Reviewed-by: Bertrand Simonnet Tested-by: Nathan Bullock Commit-Queue: Nathan Bullock --- metrics/metrics_daemon.cc | 1 + metrics/uploader/upload_service.cc | 2 ++ 2 files changed, 3 insertions(+) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index c0c77faea..b8d013d76 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -313,6 +313,7 @@ void MetricsDaemon::Init(bool testing, g_timeout_add(kUpdateStatsIntervalMs, &HandleUpdateStatsTimeout, this); if (uploader_active) { + LOG(INFO) << "uploader enabled"; upload_service_.reset(new UploadService(testing_)); upload_service_->Init(); } diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 8e08f28c7..3266a8e31 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -97,6 +97,8 @@ void UploadService::SendStagedLog() { return; } LOG(WARNING) << "log failed more than " << kMaxFailedUpload << " times."; + } else { + LOG(INFO) << "uploaded " << log_text.length() << " bytes"; } // Discard staged log. staged_log_.reset(); From dc1ef3c356922a77e96873f94342966890038d3f Mon Sep 17 00:00:00 2001 From: Nathan Bullock Date: Wed, 17 Sep 2014 09:24:10 -0400 Subject: [PATCH 167/200] metrics: read /sys/class/block/*/stat correctly TEST=unit tests, manual tested on panther_embedded BUG=chromium:415118 Change-Id: I849a9b7849a27d29d9d24bb1575f113e43716bd5 Reviewed-on: https://chromium-review.googlesource.com/218504 Reviewed-by: Bertrand Simonnet Commit-Queue: Nathan Bullock Tested-by: Nathan Bullock --- metrics/metrics_daemon.cc | 2 +- metrics/metrics_daemon_test.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index b8d013d76..5f9d85110 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -495,7 +495,7 @@ bool MetricsDaemon::DiskStatsReadStats(uint64_t* read_sectors, LOG_IF(WARNING, nchars == sizeof(line)) << "line too long in " << diskstats_path_; line[nchars] = '\0'; - nitems = sscanf(line, "%*d %*d %" PRIu64 "d %*d %*d %*d %" PRIu64 "d", + nitems = sscanf(line, "%*d %*d %" PRIu64 " %*d %*d %*d %" PRIu64, read_sectors, write_sectors); if (nitems == 2) { success = true; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 0116a1934..9b60ca4ca 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -36,8 +36,8 @@ using chromeos_metrics::PersistentIntegerMock; static const char kFakeDiskStatsName[] = "fake-disk-stats"; static const char kFakeDiskStatsFormat[] = - " 1793 1788 %" PRIu64 "d 105580 " - " 196 175 %" PRIu64 "d 30290 " + " 1793 1788 %" PRIu64 " 105580 " + " 196 175 %" PRIu64 " 30290 " " 0 44060 135850\n"; static const uint64_t kFakeReadSectors[] = {80000, 100000}; static const uint64_t kFakeWriteSectors[] = {3000, 4000}; From dfea2f8992e9f909634b9af6f1481a6b2f08cdc2 Mon Sep 17 00:00:00 2001 From: Alex Deymo Date: Wed, 24 Sep 2014 11:09:43 -0700 Subject: [PATCH 168/200] Remove '../platform2/' from platform2 relative paths in gyp files. The paths in platform2 gyp files are relative to the gyp file so they don't require to have '../platform2/' in them. BUG=None TEST=cbuildbot amd64-generic-full arm-generic-full Change-Id: I4da32969fc4956df14e9d3685f790565cd33d20f Reviewed-on: https://chromium-review.googlesource.com/219788 Reviewed-by: Alex Vakulenko Commit-Queue: Alex Deymo Tested-by: Alex Deymo --- metrics/metrics.gyp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index b5c356186..c6c7ffed7 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -104,7 +104,7 @@ '<(proto_in_dir)/user_action_event.proto', ], 'includes': [ - '../../platform2/common-mk/protoc.gypi' + '../common-mk/protoc.gypi' ], }, ], @@ -123,7 +123,7 @@ { 'target_name': 'persistent_integer_test', 'type': 'executable', - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'sources': [ 'persistent_integer.cc', 'persistent_integer_test.cc', @@ -135,7 +135,7 @@ 'dependencies': [ '../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)', ], - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'sources': [ 'metrics_library_test.cc', ], @@ -148,7 +148,7 @@ { 'target_name': 'timer_test', 'type': 'executable', - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'sources': [ 'timer.cc', 'timer_test.cc', @@ -166,7 +166,7 @@ 'libupload_service', ], 'includes':[ - '../../platform2/common-mk/common_test.gypi', + '../common-mk/common_test.gypi', ], 'include_dirs': ['.'] }, @@ -180,7 +180,7 @@ 'dependencies': [ 'libmetrics_daemon', ], - 'includes': ['../../platform2/common-mk/common_test.gypi'], + 'includes': ['../common-mk/common_test.gypi'], 'sources': [ 'metrics_daemon_test.cc', ], From 52d9edb59008c40d22e4c7a6f74229ce23e2effe Mon Sep 17 00:00:00 2001 From: Nathan Bullock Date: Wed, 1 Oct 2014 09:31:31 -0400 Subject: [PATCH 169/200] metrics: 'Send' method crashes on NULL ErrorPtr TEST=manually tested on panther_embedded BUG=none Change-Id: Iced380180918839d34a46e1cb2743a4cffdd5b4c Reviewed-on: https://chromium-review.googlesource.com/220780 Reviewed-by: Bertrand Simonnet Commit-Queue: Nathan Bullock Tested-by: Nathan Bullock --- metrics/uploader/sender_http.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/metrics/uploader/sender_http.cc b/metrics/uploader/sender_http.cc index 585287f22..7b5228a9c 100644 --- a/metrics/uploader/sender_http.cc +++ b/metrics/uploader/sender_http.cc @@ -29,7 +29,9 @@ bool HttpSender::Send(const std::string& content, chromeos::http::Transport::CreateDefault(), &error); if (!response || response->GetDataAsString() != "OK") { - DLOG(ERROR) << "Failed to send data: " << error->GetMessage(); + if (error) { + DLOG(ERROR) << "Failed to send data: " << error->GetMessage(); + } return false; } return true; From e812249cab30068a4397c4f4c40078519713e24b Mon Sep 17 00:00:00 2001 From: Nathan Bullock Date: Thu, 2 Oct 2014 08:36:42 -0400 Subject: [PATCH 170/200] metrics: protobufs need to be uploaded as binary TEST=manually tested on panther_embedded BUG=none Change-Id: Id07952d2b015c4199344556686218a78b8d79193 Reviewed-on: https://chromium-review.googlesource.com/221060 Reviewed-by: Bertrand Simonnet Commit-Queue: Nathan Bullock Tested-by: Nathan Bullock --- metrics/uploader/sender_http.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metrics/uploader/sender_http.cc b/metrics/uploader/sender_http.cc index 7b5228a9c..ff417b3c4 100644 --- a/metrics/uploader/sender_http.cc +++ b/metrics/uploader/sender_http.cc @@ -21,9 +21,10 @@ bool HttpSender::Send(const std::string& content, chromeos::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}}; chromeos::ErrorPtr error; - auto response = chromeos::http::PostText( + auto response = chromeos::http::PostBinary( server_url_, content.c_str(), + content.size(), chromeos::mime::application::kWwwFormUrlEncoded, headers, chromeos::http::Transport::CreateDefault(), From 67906c6bbbc4f9eb8e624f9dd61e866ccd77d724 Mon Sep 17 00:00:00 2001 From: Steve Fung Date: Mon, 6 Oct 2014 15:15:30 -0700 Subject: [PATCH 171/200] metrics: remove gflags dependency We are switching to using chromeos/flag_helper.h instead to standardize the code everywhere. BUG=chromium:375017 TEST=`FEATURES=test emerge-panther metrics` TEST=`cbuildbot --remote --hwtest -p 'chromiumos/platform2' falco-paladin` passes Change-Id: Icd08f65fd639e82ac6fe1581c763d60a189db827 Reviewed-on: https://chromium-review.googlesource.com/221757 Reviewed-by: Bertrand Simonnet Commit-Queue: Steve Fung Tested-by: Steve Fung --- metrics/metrics.gyp | 2 -- metrics/metrics_daemon.cc | 17 ++++++--- metrics/metrics_daemon.h | 9 ++++- metrics/metrics_daemon_main.cc | 46 ++++++++++++++++--------- metrics/metrics_daemon_test.cc | 7 +++- metrics/uploader/upload_service.cc | 26 ++++---------- metrics/uploader/upload_service.h | 7 ++-- metrics/uploader/upload_service_test.cc | 9 +++-- 8 files changed, 73 insertions(+), 50 deletions(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index c6c7ffed7..cd0768244 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -27,7 +27,6 @@ 'link_settings': { 'libraries': [ '-lrootdev', - '-lgflags', ], }, 'sources': [ @@ -56,7 +55,6 @@ ], 'link_settings': { 'libraries': [ - '-lgflags', '-lvboot_host', ], }, diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 5f9d85110..bdd181c64 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -187,8 +187,8 @@ void MetricsDaemon::Run(bool run_as_daemon) { } void MetricsDaemon::RunUploaderTest() { - upload_service_.reset(new UploadService(testing_)); - upload_service_->Init(); + upload_service_.reset(new UploadService(testing_, server_)); + upload_service_->Init(upload_interval_secs_, metrics_file_); upload_service_->UploadEvent(); } @@ -215,11 +215,18 @@ void MetricsDaemon::Init(bool testing, const string& diskstats_path, const string& vmstats_path, const string& scaling_max_freq_path, - const string& cpuinfo_max_freq_path) { + const string& cpuinfo_max_freq_path, + int upload_interval_secs, + const string& server, + const string& metrics_file) { testing_ = testing; DCHECK(metrics_lib != nullptr); metrics_lib_ = metrics_lib; + upload_interval_secs_ = upload_interval_secs; + server_ = server; + metrics_file_ = metrics_file; + // Get ticks per second (HZ) on this system. // Sysconf cannot fail, so no sanity checks are needed. ticks_per_second_ = sysconf(_SC_CLK_TCK); @@ -314,8 +321,8 @@ void MetricsDaemon::Init(bool testing, if (uploader_active) { LOG(INFO) << "uploader enabled"; - upload_service_.reset(new UploadService(testing_)); - upload_service_->Init(); + upload_service_.reset(new UploadService(testing_, server_)); + upload_service_->Init(upload_interval_secs_, metrics_file_); } } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 205ea6995..07072f9b4 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -36,7 +36,10 @@ class MetricsDaemon { const std::string& diskstats_path, const std::string& vmstats_path, const std::string& cpuinfo_max_freq_path, - const std::string& scaling_max_freq_path); + const std::string& scaling_max_freq_path, + int upload_interval_secs, + const std::string& server, + const std::string& metrics_file); // Does all the work. If |run_as_daemon| is true, daemonizes by // forking. @@ -372,6 +375,10 @@ class MetricsDaemon { std::string scaling_max_freq_path_; std::string cpuinfo_max_freq_path_; + int upload_interval_secs_; + std::string server_; + std::string metrics_file_; + scoped_ptr upload_service_; }; diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index e529afd4d..e92ce31e2 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -6,8 +6,8 @@ #include #include #include +#include #include -#include #include #include "metrics/metrics_daemon.h" @@ -17,19 +17,6 @@ const char kScalingMaxFreqPath[] = const char kCpuinfoMaxFreqPath[] = "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq"; -// TODO(bsimonnet): Change this to use base::CommandLine (crbug.com/375017) -DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); - -// The uploader is disabled by default on ChromeOS as Chrome is responsible for -// sending the metrics. -DEFINE_bool(uploader, false, "activate the uploader"); - -// Upload the metrics once and exit. (used for testing) -DEFINE_bool( - uploader_test, - false, - "run the uploader once and exit"); - // Returns the path to the disk stats in the sysfs. Returns the null string if // it cannot find the disk stats file. static @@ -56,8 +43,30 @@ const std::string MetricsMainDiskStatsPath() { } int main(int argc, char** argv) { - CommandLine::Init(argc, argv); - google::ParseCommandLineFlags(&argc, &argv, true); + DEFINE_bool(daemon, true, "run as daemon (use -nodaemon for debugging)"); + + // The uploader is disabled by default on ChromeOS as Chrome is responsible + // for sending the metrics. + DEFINE_bool(uploader, false, "activate the uploader"); + + // Upload the metrics once and exit. (used for testing) + DEFINE_bool(uploader_test, + false, + "run the uploader once and exit"); + + // Upload Service flags. + DEFINE_int32(upload_interval_secs, + 1800, + "Interval at which metrics_daemon sends the metrics. (needs " + "-uploader)"); + DEFINE_string(server, + "https://clients4.google.com/uma/v2", + "Server to upload the metrics to. (needs -uploader)"); + DEFINE_string(metrics_file, + "/var/run/metrics/uma-events", + "File to use as a proxy for uploading the metrics"); + + chromeos::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon"); // Also log to stderr when not running as daemon. chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader | @@ -71,7 +80,10 @@ int main(int argc, char** argv) { MetricsMainDiskStatsPath(), "/proc/vmstat", kScalingMaxFreqPath, - kCpuinfoMaxFreqPath); + kCpuinfoMaxFreqPath, + FLAGS_upload_interval_secs, + FLAGS_server, + FLAGS_metrics_file); base::AtExitManager at_exit_manager; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 9b60ca4ca..78559df67 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -45,6 +45,8 @@ static const uint64_t kFakeWriteSectors[] = {3000, 4000}; static const char kFakeVmStatsName[] = "fake-vm-stats"; static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq"; static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq"; +static const char kMetricsServer[] = "https://clients4.google.com/uma/v2"; +static const char kMetricsFilePath[] = "/var/run/metrics/uma-events"; class MetricsDaemonTest : public testing::Test { protected: @@ -69,7 +71,10 @@ class MetricsDaemonTest : public testing::Test { kFakeDiskStatsName, kFakeVmStatsName, kFakeScalingMaxFreqPath, - kFakeCpuinfoMaxFreqPath); + kFakeCpuinfoMaxFreqPath, + 1800, + kMetricsServer, + kMetricsFilePath); // Replace original persistent values with mock ones. daily_active_use_mock_ = diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 3266a8e31..0ebfc7816 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -17,36 +17,24 @@ #include #include #include -#include #include "metrics/uploader/metrics_log.h" #include "metrics/uploader/sender_http.h" #include "metrics/uploader/system_profile_cache.h" -DEFINE_int32( - upload_interval_secs, - 1800, - "Interval at which metrics_daemon sends the metrics. (needs -uploader)"); - -DEFINE_string(server, - "https://clients4.google.com/uma/v2", - "Server to upload the metrics to. (needs -uploader)"); - -DEFINE_string(metrics_file, - "/var/run/metrics/uma-events", - "File to use as a proxy for uploading the metrics"); - const int UploadService::kMaxFailedUpload = 10; -UploadService::UploadService(bool testing) +UploadService::UploadService(bool testing, const std::string& server) : system_profile_setter_(new SystemProfileCache(testing)), histogram_snapshot_manager_(this), - sender_(new HttpSender(FLAGS_server)) { + sender_(new HttpSender(server)) { } -void UploadService::Init() { +void UploadService::Init(int upload_interval_secs, + const std::string& metrics_file) { base::StatisticsRecorder::Initialize(); - g_timeout_add_seconds(FLAGS_upload_interval_secs, &UploadEventStatic, this); + metrics_file_ = metrics_file; + g_timeout_add_seconds(upload_interval_secs, &UploadEventStatic, this); } void UploadService::StartNewLog() { @@ -116,7 +104,7 @@ void UploadService::ReadMetrics() { ScopedVector vector; metrics::SerializationUtils::ReadAndTruncateMetricsFromFile( - FLAGS_metrics_file, &vector); + metrics_file_, &vector); int i = 0; for (ScopedVector::iterator it = vector.begin(); diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index 114c5e4ea..c0a143089 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -54,9 +54,10 @@ class SystemProfileSetter; // class UploadService : public base::HistogramFlattener { public: - explicit UploadService(bool testing); + explicit UploadService(bool testing, const std::string& server); - void Init(); + void Init(int upload_interval_secs, + const std::string& metrics_file); // Starts a new log. The log needs to be regenerated after each successful // launch as it is destroyed when staging the log. @@ -133,6 +134,8 @@ class UploadService : public base::HistogramFlattener { int failed_upload_count_; scoped_ptr current_log_; scoped_ptr staged_log_; + + std::string metrics_file_; }; #endif // METRICS_UPLOADER_UPLOAD_SERVICE_H_ diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index 94453b24a..9badf6401 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -19,16 +19,19 @@ #include "uploader/system_profile_cache.h" #include "uploader/upload_service.h" +static const char kMetricsServer[] = "https://clients4.google.com/uma/v2"; +static const char kMetricsFilePath[] = "/var/run/metrics/uma-events"; + class UploadServiceTest : public testing::Test { protected: UploadServiceTest() : cache_(true), - upload_service_(true), + upload_service_(true, kMetricsServer), exit_manager_(new base::AtExitManager()) { sender_ = new SenderMock; upload_service_.sender_.reset(sender_); upload_service_.system_profile_setter_.reset(new MockSystemProfileSetter()); - upload_service_.Init(); + upload_service_.Init(1800, kMetricsFilePath); } virtual void SetUp() { @@ -124,7 +127,7 @@ TEST_F(UploadServiceTest, EmptyLogsAreNotSent) { } TEST_F(UploadServiceTest, LogEmptyByDefault) { - UploadService upload_service(true); + UploadService upload_service(true, kMetricsServer); // current_log_ should be initialized later as it needs AtExitManager to exit // in order to gather system information from SysInfo. From 71a62efc54d08bc9668d00857fc59c4fe772c64c Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Tue, 7 Oct 2014 11:26:25 -0700 Subject: [PATCH 172/200] metrics: add support for other product id metrics_uploader should use the GOOGLE_METRICS_PRODUCT_ID field from os-release whenever possible instead of the default Chrome product id. BUG=chromium:415744 TEST=FEATURES=test emerge-gizmo metrics succeeds. TEST=test_that platform_MetricsUploader succeeds. CQ-DEPEND=CL:221963 Change-Id: I69b1a6ca766048ad80d93008a2fe3b18879bf1da Reviewed-on: https://chromium-review.googlesource.com/221953 Tested-by: Bertrand Simonnet Reviewed-by: Alex Vakulenko Commit-Queue: Bertrand Simonnet --- metrics/metrics_daemon.cc | 10 +++-- metrics/metrics_daemon.h | 6 ++- metrics/metrics_daemon_main.cc | 5 ++- metrics/metrics_daemon_test.cc | 3 +- metrics/uploader/system_profile_cache.cc | 53 +++++++++++++++++++++--- metrics/uploader/system_profile_cache.h | 13 ++++-- metrics/uploader/upload_service.cc | 5 ++- metrics/uploader/upload_service.h | 3 +- metrics/uploader/upload_service_test.cc | 9 ++-- 9 files changed, 84 insertions(+), 23 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index bdd181c64..b0247c692 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -187,7 +187,9 @@ void MetricsDaemon::Run(bool run_as_daemon) { } void MetricsDaemon::RunUploaderTest() { - upload_service_.reset(new UploadService(testing_, server_)); + upload_service_.reset(new UploadService(new SystemProfileCache(true, + config_root_), + server_)); upload_service_->Init(upload_interval_secs_, metrics_file_); upload_service_->UploadEvent(); } @@ -218,8 +220,10 @@ void MetricsDaemon::Init(bool testing, const string& cpuinfo_max_freq_path, int upload_interval_secs, const string& server, - const string& metrics_file) { + const string& metrics_file, + const string& config_root) { testing_ = testing; + config_root_ = config_root; DCHECK(metrics_lib != nullptr); metrics_lib_ = metrics_lib; @@ -321,7 +325,7 @@ void MetricsDaemon::Init(bool testing, if (uploader_active) { LOG(INFO) << "uploader enabled"; - upload_service_.reset(new UploadService(testing_, server_)); + upload_service_.reset(new UploadService(new SystemProfileCache(), server_)); upload_service_->Init(upload_interval_secs_, metrics_file_); } } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 07072f9b4..7c7f8d2f0 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -39,7 +39,8 @@ class MetricsDaemon { const std::string& scaling_max_freq_path, int upload_interval_secs, const std::string& server, - const std::string& metrics_file); + const std::string& metrics_file, + const std::string& config_root); // Does all the work. If |run_as_daemon| is true, daemonizes by // forking. @@ -300,6 +301,9 @@ class MetricsDaemon { // Test mode. bool testing_; + // Root of the configuration files to use. + std::string config_root_; + // The metrics library handle. MetricsLibraryInterface* metrics_lib_; diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index e92ce31e2..f4a660b35 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -65,6 +65,8 @@ int main(int argc, char** argv) { DEFINE_string(metrics_file, "/var/run/metrics/uma-events", "File to use as a proxy for uploading the metrics"); + DEFINE_string(config_root, + "/", "Root of the configuration files (testing only)"); chromeos::FlagHelper::Init(argc, argv, "Chromium OS Metrics Daemon"); @@ -83,7 +85,8 @@ int main(int argc, char** argv) { kCpuinfoMaxFreqPath, FLAGS_upload_interval_secs, FLAGS_server, - FLAGS_metrics_file); + FLAGS_metrics_file, + FLAGS_config_root); base::AtExitManager at_exit_manager; diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 78559df67..947be8773 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -74,7 +74,8 @@ class MetricsDaemonTest : public testing::Test { kFakeCpuinfoMaxFreqPath, 1800, kMetricsServer, - kMetricsFilePath); + kMetricsFilePath, + "/"); // Replace original persistent values with mock ones. daily_active_use_mock_ = diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index 604c060ca..b08fbd915 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -11,20 +11,35 @@ #include "base/files/file_util.h" #include "base/guid.h" #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "base/sys_info.h" #include "components/metrics/metrics_log_base.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "metrics/persistent_integer.h" #include "vboot/crossystem.h" -const char* SystemProfileCache::kPersistentGUIDFile = - "/var/lib/metrics/Sysinfo.GUID"; -const char* SystemProfileCache::kPersistentSessionIdFilename = - "Sysinfo.SessionId"; +namespace { -SystemProfileCache::SystemProfileCache(bool testing) +const char kPersistentGUIDFile[] = "/var/lib/metrics/Sysinfo.GUID"; +const char kPersistentSessionIdFilename[] = "Sysinfo.SessionId"; +const char kProductIdFieldName[] = "GOOGLE_METRICS_PRODUCT_ID"; + +} // namespace + + +SystemProfileCache::SystemProfileCache() + : initialized_(false), + testing_(false), + config_root_("/"), + session_id_(new chromeos_metrics::PersistentInteger( + kPersistentSessionIdFilename)) { +} + +SystemProfileCache::SystemProfileCache(bool testing, + const std::string& config_root) : initialized_(false), testing_(testing), + config_root_(config_root), session_id_(new chromeos_metrics::PersistentInteger( kPersistentSessionIdFilename)) { } @@ -48,6 +63,12 @@ bool SystemProfileCache::Initialize() { base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &channel_string); profile_.channel = ProtoChannelFromString(channel_string); + // If the product id is not defined, use the default one from the protobuf. + profile_.product_id = metrics::ChromeUserMetricsExtension::CHROME; + if (GetProductId(&profile_.product_id)) { + DLOG(INFO) << "Set the product id to " << profile_.product_id; + } + profile_.client_id = testing_ ? "client_id_test" : GetPersistentGUID(kPersistentGUIDFile); @@ -78,6 +99,9 @@ void SystemProfileCache::Populate( metrics::MetricsLogBase::Hash(profile_.client_id)); metrics_proto->set_session_id(profile_.session_id); + // Sets the product id. + metrics_proto->set_product(profile_.product_id); + metrics::SystemProfileProto* profile_proto = metrics_proto->mutable_system_profile(); profile_proto->mutable_hardware()->set_hardware_class( @@ -122,6 +146,25 @@ bool SystemProfileCache::GetHardwareId(std::string* hwid) { return true; } +bool SystemProfileCache::GetProductId(int* product_id) const { + chromeos::OsReleaseReader reader; + if (testing_) { + base::FilePath root(config_root_); + CHECK(reader.LoadTestingOnly(root)) << "Failed to load os-release fields " + "from" << root.value(); + } else { + CHECK(reader.Load()) << "Failed to load os-release fields."; + } + + std::string id; + if (reader.GetString(kProductIdFieldName, &id)) { + CHECK(base::StringToInt(id, product_id)) << "Failed to convert product_id " + << id << " to int."; + return true; + } + return false; +} + metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString( const std::string& channel) { diff --git a/metrics/uploader/system_profile_cache.h b/metrics/uploader/system_profile_cache.h index f85c1b190..95f554f8e 100644 --- a/metrics/uploader/system_profile_cache.h +++ b/metrics/uploader/system_profile_cache.h @@ -12,6 +12,7 @@ #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" +#include "chromeos/osrelease_reader.h" #include "components/metrics/proto/system_profile.pb.h" #include "metrics/persistent_integer.h" #include "metrics/uploader/system_profile_setter.h" @@ -28,6 +29,7 @@ struct SystemProfile { std::string hardware_class; std::string client_id; int32_t session_id; + int32_t product_id; }; // Retrieves general system informations needed by the protobuf for context and @@ -36,7 +38,9 @@ struct SystemProfile { // The cache is populated lazily. The only method needed is Populate. class SystemProfileCache : public SystemProfileSetter { public: - explicit SystemProfileCache(bool testing); + SystemProfileCache(); + + SystemProfileCache(bool testing, const std::string& config_root); // Populates the ProfileSystem protobuf with system information. void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override; @@ -56,9 +60,6 @@ class SystemProfileCache : public SystemProfileSetter { FRIEND_TEST(UploadServiceTest, SessionIdIncrementedAtInitialization); FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent); - static const char* kPersistentGUIDFile; - static const char* kPersistentSessionIdFilename; - // Fetches all informations and populates |profile_| bool Initialize(); @@ -68,8 +69,12 @@ class SystemProfileCache : public SystemProfileSetter { // Gets the hardware ID using crossystem bool GetHardwareId(std::string* hwid); + // Gets the product ID from the GOOGLE_METRICS_PRODUCT_ID field. + bool GetProductId(int* product_id) const; + bool initialized_; bool testing_; + std::string config_root_; scoped_ptr session_id_; SystemProfile profile_; }; diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 0ebfc7816..55d76a406 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -24,8 +24,9 @@ const int UploadService::kMaxFailedUpload = 10; -UploadService::UploadService(bool testing, const std::string& server) - : system_profile_setter_(new SystemProfileCache(testing)), +UploadService::UploadService(SystemProfileSetter* setter, + const std::string& server) + : system_profile_setter_(setter), histogram_snapshot_manager_(this), sender_(new HttpSender(server)) { } diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index c0a143089..a72349e2f 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -54,7 +54,8 @@ class SystemProfileSetter; // class UploadService : public base::HistogramFlattener { public: - explicit UploadService(bool testing, const std::string& server); + explicit UploadService(SystemProfileSetter* setter, + const std::string& server); void Init(int upload_interval_secs, const std::string& metrics_file); diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index 9badf6401..2cdc4f5e9 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -25,12 +25,11 @@ static const char kMetricsFilePath[] = "/var/run/metrics/uma-events"; class UploadServiceTest : public testing::Test { protected: UploadServiceTest() - : cache_(true), - upload_service_(true, kMetricsServer), + : cache_(true, "/"), + upload_service_(new MockSystemProfileSetter(), kMetricsServer), exit_manager_(new base::AtExitManager()) { sender_ = new SenderMock; upload_service_.sender_.reset(sender_); - upload_service_.system_profile_setter_.reset(new MockSystemProfileSetter()); upload_service_.Init(1800, kMetricsFilePath); } @@ -127,7 +126,7 @@ TEST_F(UploadServiceTest, EmptyLogsAreNotSent) { } TEST_F(UploadServiceTest, LogEmptyByDefault) { - UploadService upload_service(true, kMetricsServer); + UploadService upload_service(new MockSystemProfileSetter(), kMetricsServer); // current_log_ should be initialized later as it needs AtExitManager to exit // in order to gather system information from SysInfo. @@ -195,7 +194,7 @@ TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) { base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time()); scoped_ptr histogram = metrics::MetricSample::SparseHistogramSample("myhistogram", 1); - SystemProfileCache* local_cache_ = new SystemProfileCache(true); + SystemProfileCache* local_cache_ = new SystemProfileCache(true, "/"); local_cache_->session_id_.reset(new chromeos_metrics::PersistentInteger( dir_.path().Append("session_id").value())); From cac74e122f4937af632679328ca539631325fd80 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Thu, 9 Oct 2014 10:14:13 -0700 Subject: [PATCH 173/200] metrics: use TimeDelta for upload_interval Use TimeDelta instead of int interpreted as seconds. BUG=None TEST=Unittests. Change-Id: I18db8d558303291ab86b26c68c89203e0364b623 Reviewed-on: https://chromium-review.googlesource.com/222611 Reviewed-by: Bertrand Simonnet Tested-by: Bertrand Simonnet Commit-Queue: Bertrand Simonnet --- metrics/metrics_daemon.cc | 8 ++++---- metrics/metrics_daemon.h | 4 ++-- metrics/metrics_daemon_main.cc | 2 +- metrics/metrics_daemon_test.cc | 2 +- metrics/uploader/upload_service.cc | 4 ++-- metrics/uploader/upload_service.h | 2 +- metrics/uploader/upload_service_test.cc | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index b0247c692..7226594cb 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -190,7 +190,7 @@ void MetricsDaemon::RunUploaderTest() { upload_service_.reset(new UploadService(new SystemProfileCache(true, config_root_), server_)); - upload_service_->Init(upload_interval_secs_, metrics_file_); + upload_service_->Init(upload_interval_, metrics_file_); upload_service_->UploadEvent(); } @@ -218,7 +218,7 @@ void MetricsDaemon::Init(bool testing, const string& vmstats_path, const string& scaling_max_freq_path, const string& cpuinfo_max_freq_path, - int upload_interval_secs, + const base::TimeDelta& upload_interval, const string& server, const string& metrics_file, const string& config_root) { @@ -227,7 +227,7 @@ void MetricsDaemon::Init(bool testing, DCHECK(metrics_lib != nullptr); metrics_lib_ = metrics_lib; - upload_interval_secs_ = upload_interval_secs; + upload_interval_ = upload_interval; server_ = server; metrics_file_ = metrics_file; @@ -326,7 +326,7 @@ void MetricsDaemon::Init(bool testing, if (uploader_active) { LOG(INFO) << "uploader enabled"; upload_service_.reset(new UploadService(new SystemProfileCache(), server_)); - upload_service_->Init(upload_interval_secs_, metrics_file_); + upload_service_->Init(upload_interval_, metrics_file_); } } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 7c7f8d2f0..f4f14307e 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -37,7 +37,7 @@ class MetricsDaemon { const std::string& vmstats_path, const std::string& cpuinfo_max_freq_path, const std::string& scaling_max_freq_path, - int upload_interval_secs, + const base::TimeDelta& upload_interval, const std::string& server, const std::string& metrics_file, const std::string& config_root); @@ -379,7 +379,7 @@ class MetricsDaemon { std::string scaling_max_freq_path_; std::string cpuinfo_max_freq_path_; - int upload_interval_secs_; + base::TimeDelta upload_interval_; std::string server_; std::string metrics_file_; diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index f4a660b35..9322771ee 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -83,7 +83,7 @@ int main(int argc, char** argv) { "/proc/vmstat", kScalingMaxFreqPath, kCpuinfoMaxFreqPath, - FLAGS_upload_interval_secs, + base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs), FLAGS_server, FLAGS_metrics_file, FLAGS_config_root); diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 947be8773..ed6856a95 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -72,7 +72,7 @@ class MetricsDaemonTest : public testing::Test { kFakeVmStatsName, kFakeScalingMaxFreqPath, kFakeCpuinfoMaxFreqPath, - 1800, + base::TimeDelta::FromMinutes(30), kMetricsServer, kMetricsFilePath, "/"); diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 55d76a406..7e856f925 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -31,11 +31,11 @@ UploadService::UploadService(SystemProfileSetter* setter, sender_(new HttpSender(server)) { } -void UploadService::Init(int upload_interval_secs, +void UploadService::Init(const base::TimeDelta& upload_interval, const std::string& metrics_file) { base::StatisticsRecorder::Initialize(); metrics_file_ = metrics_file; - g_timeout_add_seconds(upload_interval_secs, &UploadEventStatic, this); + g_timeout_add_seconds(upload_interval.InSeconds(), &UploadEventStatic, this); } void UploadService::StartNewLog() { diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index a72349e2f..3611794e3 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -57,7 +57,7 @@ class UploadService : public base::HistogramFlattener { explicit UploadService(SystemProfileSetter* setter, const std::string& server); - void Init(int upload_interval_secs, + void Init(const base::TimeDelta& upload_interval, const std::string& metrics_file); // Starts a new log. The log needs to be regenerated after each successful diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index 2cdc4f5e9..c48747e81 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -30,7 +30,7 @@ class UploadServiceTest : public testing::Test { exit_manager_(new base::AtExitManager()) { sender_ = new SenderMock; upload_service_.sender_.reset(sender_); - upload_service_.Init(1800, kMetricsFilePath); + upload_service_.Init(base::TimeDelta::FromMinutes(30), kMetricsFilePath); } virtual void SetUp() { From d78f3df6bc49e48ca3efb4a292adac7d9bf60709 Mon Sep 17 00:00:00 2001 From: Nathan Bullock Date: Fri, 17 Oct 2014 10:03:52 -0400 Subject: [PATCH 174/200] libchromeos: void OsReleaseReader.Load() Previously OsReleaseReader returned a bool, which meant that code that called load wanted to CHECK the return values and possibly log warning messages, etc. This was unecessary since OsReleaseReader.Load never returns false. TEST=unittests BUG=none Change-Id: I7064c6a788897b5d5c687d6c9c5f4e03d4ca21a7 Reviewed-on: https://chromium-review.googlesource.com/223990 Reviewed-by: Bertrand Simonnet Commit-Queue: Nathan Bullock Tested-by: Nathan Bullock --- metrics/uploader/system_profile_cache.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index b08fbd915..cbb80512c 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -150,10 +150,9 @@ bool SystemProfileCache::GetProductId(int* product_id) const { chromeos::OsReleaseReader reader; if (testing_) { base::FilePath root(config_root_); - CHECK(reader.LoadTestingOnly(root)) << "Failed to load os-release fields " - "from" << root.value(); + reader.LoadTestingOnly(root); } else { - CHECK(reader.Load()) << "Failed to load os-release fields."; + reader.Load(); } std::string id; From 7845a57134091b9854f468bcda169a0d12073d8d Mon Sep 17 00:00:00 2001 From: Alex Deymo Date: Mon, 10 Nov 2014 19:55:35 -0800 Subject: [PATCH 175/200] Include the implemented header first in all projects. The Google C++ style guide dictates that foo.cc and foo_unittest.cc should include foo.h in the first place, so missing headers in foo.h are detected with a compile error of the module implementing them and not when another module uses them. This CL sweeps across all the .cc file in platform2 enforcing this. BUG=None TEST=cbuildbot amd64-generic Change-Id: I41835835caba13f54c3c844ecf552eb0e47efa9d Reviewed-on: https://chromium-review.googlesource.com/228894 Tested-by: Alex Deymo Reviewed-by: Alex Deymo Commit-Queue: Alex Vakulenko --- metrics/c_metrics_library.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metrics/c_metrics_library.cc b/metrics/c_metrics_library.cc index 3e2e261b9..90a2d59c9 100644 --- a/metrics/c_metrics_library.cc +++ b/metrics/c_metrics_library.cc @@ -6,9 +6,10 @@ // C wrapper to libmetrics // +#include "metrics/c_metrics_library.h" + #include -#include "metrics/c_metrics_library.h" #include "metrics/metrics_library.h" extern "C" CMetricsLibrary CMetricsLibraryNew(void) { From 788d3b63656d26938c9ea005e1bc82f230aa9b38 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Thu, 11 Dec 2014 09:48:46 -0800 Subject: [PATCH 176/200] metrics: fork metrics sources from Chromium source base In preparation to libchrome uprev, fork off non-protobuf code from Chromium's metrics component into platform2/metrics. The current version of Chromium's metrics component has been significantly refactored and pieces of functionality that Chromium OS was dependant on (e.g. MetricsLogBase class) has been removed completely. So, taking the r293518 version we have been using for a while and putting it as part of platform2/metrics now. BUG=None TEST=FEATURES=test emerge-link metrics Change-Id: Ib46ac1dff2e2b9cc881e4787b3c6b6250f0bf9c4 Reviewed-on: https://chromium-review.googlesource.com/234635 Tested-by: Alex Vakulenko Reviewed-by: Bertrand Simonnet Commit-Queue: Alex Vakulenko Trybot-Ready: Alex Vakulenko --- metrics/libmetrics.gypi | 4 +- metrics/metrics.gyp | 8 +- metrics/metrics_library.cc | 8 +- metrics/serialization/metric_sample.cc | 197 ++++++++++++++++ metrics/serialization/metric_sample.h | 119 ++++++++++ metrics/serialization/serialization_utils.cc | 216 ++++++++++++++++++ metrics/serialization/serialization_utils.h | 48 ++++ .../serialization_utils_unittest.cc | 169 ++++++++++++++ metrics/uploader/metrics_hashes.cc | 39 ++++ metrics/uploader/metrics_hashes.h | 18 ++ metrics/uploader/metrics_hashes_unittest.cc | 32 +++ metrics/uploader/metrics_log.h | 2 +- metrics/uploader/metrics_log_base.cc | 142 ++++++++++++ metrics/uploader/metrics_log_base.h | 110 +++++++++ metrics/uploader/metrics_log_base_unittest.cc | 126 ++++++++++ metrics/uploader/system_profile_cache.cc | 2 +- metrics/uploader/upload_service.cc | 4 +- metrics/uploader/upload_service_test.cc | 12 +- 18 files changed, 1236 insertions(+), 20 deletions(-) create mode 100644 metrics/serialization/metric_sample.cc create mode 100644 metrics/serialization/metric_sample.h create mode 100644 metrics/serialization/serialization_utils.cc create mode 100644 metrics/serialization/serialization_utils.h create mode 100644 metrics/serialization/serialization_utils_unittest.cc create mode 100644 metrics/uploader/metrics_hashes.cc create mode 100644 metrics/uploader/metrics_hashes.h create mode 100644 metrics/uploader/metrics_hashes_unittest.cc create mode 100644 metrics/uploader/metrics_log_base.cc create mode 100644 metrics/uploader/metrics_log_base.h create mode 100644 metrics/uploader/metrics_log_base_unittest.cc diff --git a/metrics/libmetrics.gypi b/metrics/libmetrics.gypi index 65de6f57b..5b90a550c 100644 --- a/metrics/libmetrics.gypi +++ b/metrics/libmetrics.gypi @@ -23,9 +23,9 @@ 'sources': [ 'c_metrics_library.cc', 'metrics_library.cc', + 'serialization/metric_sample.cc', + 'serialization/serialization_utils.cc', 'timer.cc', - 'components/metrics/chromeos/metric_sample.cc', - 'components/metrics/chromeos/serialization_utils.cc', ], 'include_dirs': ['.'], }, diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index cd0768244..9614826e0 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -75,12 +75,11 @@ }, 'sources': [ 'uploader/upload_service.cc', + 'uploader/metrics_hashes.cc', 'uploader/metrics_log.cc', + 'uploader/metrics_log_base.cc', 'uploader/system_profile_cache.cc', 'uploader/sender_http.cc', - 'components/metrics/metrics_log_base.cc', - 'components/metrics/metrics_log_manager.cc', - 'components/metrics/metrics_hashes.cc', ], 'include_dirs': ['.'] }, @@ -136,6 +135,7 @@ 'includes': ['../common-mk/common_test.gypi'], 'sources': [ 'metrics_library_test.cc', + 'serialization/serialization_utils_unittest.cc', ], 'link_settings': { 'libraries': [ @@ -157,6 +157,8 @@ 'type': 'executable', 'sources': [ 'persistent_integer.cc', + 'uploader/metrics_hashes_unittest.cc', + 'uploader/metrics_log_base_unittest.cc', 'uploader/mock/sender_mock.cc', 'uploader/upload_service_test.cc', ], diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index c352bcf51..5088caee3 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -13,13 +13,11 @@ #include #include -#include "components/metrics/chromeos/metric_sample.h" -#include "components/metrics/chromeos/serialization_utils.h" +#include "metrics/serialization/metric_sample.h" +#include "metrics/serialization/serialization_utils.h" #include "policy/device_policy.h" -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) - static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; static const char kUMAEventsPath[] = "/var/run/metrics/uma-events"; static const char kConsentFile[] = "/home/chronos/Consent To Send Stats"; @@ -206,7 +204,7 @@ void MetricsLibrary::SetPolicyProvider(policy::PolicyProvider* provider) { } bool MetricsLibrary::SendCrosEventToUMA(const std::string& event) { - for (size_t i = 0; i < ARRAY_SIZE(kCrosEventNames); i++) { + for (size_t i = 0; i < arraysize(kCrosEventNames); i++) { if (strcmp(event.c_str(), kCrosEventNames[i]) == 0) { return SendEnumToUMA(kCrosEventHistogramName, i, kCrosEventHistogramMax); } diff --git a/metrics/serialization/metric_sample.cc b/metrics/serialization/metric_sample.cc new file mode 100644 index 000000000..5447497ce --- /dev/null +++ b/metrics/serialization/metric_sample.cc @@ -0,0 +1,197 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics/serialization/metric_sample.h" + +#include +#include + +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/stringprintf.h" + +namespace metrics { + +MetricSample::MetricSample(MetricSample::SampleType sample_type, + const std::string& metric_name, + int sample, + int min, + int max, + int bucket_count) + : type_(sample_type), + name_(metric_name), + sample_(sample), + min_(min), + max_(max), + bucket_count_(bucket_count) { +} + +MetricSample::~MetricSample() { +} + +bool MetricSample::IsValid() const { + return name().find(' ') == std::string::npos && + name().find('\0') == std::string::npos && !name().empty(); +} + +std::string MetricSample::ToString() const { + if (type_ == CRASH) { + return base::StringPrintf("crash%c%s%c", + '\0', + name().c_str(), + '\0'); + } else if (type_ == SPARSE_HISTOGRAM) { + return base::StringPrintf("sparsehistogram%c%s %d%c", + '\0', + name().c_str(), + sample_, + '\0'); + } else if (type_ == LINEAR_HISTOGRAM) { + return base::StringPrintf("linearhistogram%c%s %d %d%c", + '\0', + name().c_str(), + sample_, + max_, + '\0'); + } else if (type_ == HISTOGRAM) { + return base::StringPrintf("histogram%c%s %d %d %d %d%c", + '\0', + name().c_str(), + sample_, + min_, + max_, + bucket_count_, + '\0'); + } else { + // The type can only be USER_ACTION. + CHECK_EQ(type_, USER_ACTION); + return base::StringPrintf("useraction%c%s%c", + '\0', + name().c_str(), + '\0'); + } +} + +int MetricSample::sample() const { + CHECK_NE(type_, USER_ACTION); + CHECK_NE(type_, CRASH); + return sample_; +} + +int MetricSample::min() const { + CHECK_EQ(type_, HISTOGRAM); + return min_; +} + +int MetricSample::max() const { + CHECK_NE(type_, CRASH); + CHECK_NE(type_, USER_ACTION); + CHECK_NE(type_, SPARSE_HISTOGRAM); + return max_; +} + +int MetricSample::bucket_count() const { + CHECK_EQ(type_, HISTOGRAM); + return bucket_count_; +} + +// static +scoped_ptr MetricSample::CrashSample( + const std::string& crash_name) { + return scoped_ptr( + new MetricSample(CRASH, crash_name, 0, 0, 0, 0)); +} + +// static +scoped_ptr MetricSample::HistogramSample( + const std::string& histogram_name, + int sample, + int min, + int max, + int bucket_count) { + return scoped_ptr(new MetricSample( + HISTOGRAM, histogram_name, sample, min, max, bucket_count)); +} + +// static +scoped_ptr MetricSample::ParseHistogram( + const std::string& serialized_histogram) { + std::vector parts; + base::SplitString(serialized_histogram, ' ', &parts); + + if (parts.size() != 5) + return scoped_ptr(); + int sample, min, max, bucket_count; + if (parts[0].empty() || !base::StringToInt(parts[1], &sample) || + !base::StringToInt(parts[2], &min) || + !base::StringToInt(parts[3], &max) || + !base::StringToInt(parts[4], &bucket_count)) { + return scoped_ptr(); + } + + return HistogramSample(parts[0], sample, min, max, bucket_count); +} + +// static +scoped_ptr MetricSample::SparseHistogramSample( + const std::string& histogram_name, + int sample) { + return scoped_ptr( + new MetricSample(SPARSE_HISTOGRAM, histogram_name, sample, 0, 0, 0)); +} + +// static +scoped_ptr MetricSample::ParseSparseHistogram( + const std::string& serialized_histogram) { + std::vector parts; + base::SplitString(serialized_histogram, ' ', &parts); + if (parts.size() != 2) + return scoped_ptr(); + int sample; + if (parts[0].empty() || !base::StringToInt(parts[1], &sample)) + return scoped_ptr(); + + return SparseHistogramSample(parts[0], sample); +} + +// static +scoped_ptr MetricSample::LinearHistogramSample( + const std::string& histogram_name, + int sample, + int max) { + return scoped_ptr( + new MetricSample(LINEAR_HISTOGRAM, histogram_name, sample, 0, max, 0)); +} + +// static +scoped_ptr MetricSample::ParseLinearHistogram( + const std::string& serialized_histogram) { + std::vector parts; + int sample, max; + base::SplitString(serialized_histogram, ' ', &parts); + if (parts.size() != 3) + return scoped_ptr(); + if (parts[0].empty() || !base::StringToInt(parts[1], &sample) || + !base::StringToInt(parts[2], &max)) { + return scoped_ptr(); + } + + return LinearHistogramSample(parts[0], sample, max); +} + +// static +scoped_ptr MetricSample::UserActionSample( + const std::string& action_name) { + return scoped_ptr( + new MetricSample(USER_ACTION, action_name, 0, 0, 0, 0)); +} + +bool MetricSample::IsEqual(const MetricSample& metric) { + return type_ == metric.type_ && name_ == metric.name_ && + sample_ == metric.sample_ && min_ == metric.min_ && + max_ == metric.max_ && bucket_count_ == metric.bucket_count_; +} + +} // namespace metrics diff --git a/metrics/serialization/metric_sample.h b/metrics/serialization/metric_sample.h new file mode 100644 index 000000000..877114d0a --- /dev/null +++ b/metrics/serialization/metric_sample.h @@ -0,0 +1,119 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_SERIALIZATION_METRIC_SAMPLE_H_ +#define METRICS_SERIALIZATION_METRIC_SAMPLE_H_ + +#include + +#include "base/gtest_prod_util.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" + +namespace metrics { + +// This class is used by libmetrics (ChromeOS) to serialize +// and deserialize measurements to send them to a metrics sending service. +// It is meant to be a simple container with serialization functions. +class MetricSample { + public: + // Types of metric sample used. + enum SampleType { + CRASH, + HISTOGRAM, + LINEAR_HISTOGRAM, + SPARSE_HISTOGRAM, + USER_ACTION + }; + + ~MetricSample(); + + // Returns true if the sample is valid (can be serialized without ambiguity). + // + // This function should be used to filter bad samples before serializing them. + bool IsValid() const; + + // Getters for type and name. All types of metrics have these so we do not + // need to check the type. + SampleType type() const { return type_; } + const std::string& name() const { return name_; } + + // Getters for sample, min, max, bucket_count. + // Check the metric type to make sure the request make sense. (ex: a crash + // sample does not have a bucket_count so we crash if we call bucket_count() + // on it.) + int sample() const; + int min() const; + int max() const; + int bucket_count() const; + + // Returns a serialized version of the sample. + // + // The serialized message for each type is: + // crash: crash\0|name_|\0 + // user action: useraction\0|name_|\0 + // histogram: histogram\0|name_| |sample_| |min_| |max_| |bucket_count_|\0 + // sparsehistogram: sparsehistogram\0|name_| |sample_|\0 + // linearhistogram: linearhistogram\0|name_| |sample_| |max_|\0 + std::string ToString() const; + + // Builds a crash sample. + static scoped_ptr CrashSample(const std::string& crash_name); + + // Builds a histogram sample. + static scoped_ptr HistogramSample( + const std::string& histogram_name, + int sample, + int min, + int max, + int bucket_count); + // Deserializes a histogram sample. + static scoped_ptr ParseHistogram(const std::string& serialized); + + // Builds a sparse histogram sample. + static scoped_ptr SparseHistogramSample( + const std::string& histogram_name, + int sample); + // Deserializes a sparse histogram sample. + static scoped_ptr ParseSparseHistogram( + const std::string& serialized); + + // Builds a linear histogram sample. + static scoped_ptr LinearHistogramSample( + const std::string& histogram_name, + int sample, + int max); + // Deserializes a linear histogram sample. + static scoped_ptr ParseLinearHistogram( + const std::string& serialized); + + // Builds a user action sample. + static scoped_ptr UserActionSample( + const std::string& action_name); + + // Returns true if sample and this object represent the same sample (type, + // name, sample, min, max, bucket_count match). + bool IsEqual(const MetricSample& sample); + + private: + MetricSample(SampleType sample_type, + const std::string& metric_name, + const int sample, + const int min, + const int max, + const int bucket_count); + + const SampleType type_; + const std::string name_; + const int sample_; + const int min_; + const int max_; + const int bucket_count_; + + DISALLOW_COPY_AND_ASSIGN(MetricSample); +}; + +} // namespace metrics + +#endif // METRICS_SERIALIZATION_METRIC_SAMPLE_H_ diff --git a/metrics/serialization/serialization_utils.cc b/metrics/serialization/serialization_utils.cc new file mode 100644 index 000000000..fea493e6a --- /dev/null +++ b/metrics/serialization/serialization_utils.cc @@ -0,0 +1,216 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics/serialization/serialization_utils.h" + +#include + +#include +#include + +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "metrics/serialization/metric_sample.h" + +#define READ_WRITE_ALL_FILE_FLAGS \ + (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) + +namespace metrics { +namespace { + +// Reads the next message from |file_descriptor| into |message|. +// +// |message| will be set to the empty string if no message could be read (EOF) +// or the message was badly constructed. +// +// Returns false if no message can be read from this file anymore (EOF or +// unrecoverable error). +bool ReadMessage(int fd, std::string* message) { + CHECK(message); + + int result; + int32 message_size; + // The file containing the metrics do not leave the device so the writer and + // the reader will always have the same endianness. + result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size))); + if (result < 0) { + DPLOG(ERROR) << "reading metrics message header"; + return false; + } + if (result == 0) { + // This indicates a normal EOF. + return false; + } + if (result < static_cast(sizeof(message_size))) { + DLOG(ERROR) << "bad read size " << result << ", expecting " + << sizeof(message_size); + return false; + } + + // kMessageMaxLength applies to the entire message: the 4-byte + // length field and the content. + if (message_size > SerializationUtils::kMessageMaxLength) { + DLOG(ERROR) << "message too long : " << message_size; + if (HANDLE_EINTR(lseek(fd, message_size - 4, SEEK_CUR)) == -1) { + DLOG(ERROR) << "error while skipping message. abort"; + return false; + } + // Badly formatted message was skipped. Treat the badly formatted sample as + // an empty sample. + message->clear(); + return true; + } + + message_size -= sizeof(message_size); // The message size includes itself. + char buffer[SerializationUtils::kMessageMaxLength]; + if (!base::ReadFromFD(fd, buffer, message_size)) { + DPLOG(ERROR) << "reading metrics message body"; + return false; + } + *message = std::string(buffer, message_size); + return true; +} + +} // namespace + +scoped_ptr SerializationUtils::ParseSample( + const std::string& sample) { + if (sample.empty()) + return scoped_ptr(); + + std::vector parts; + base::SplitString(sample, '\0', &parts); + // We should have two null terminated strings so split should produce + // three chunks. + if (parts.size() != 3) { + DLOG(ERROR) << "splitting message on \\0 produced " << parts.size() + << " parts (expected 3)"; + return scoped_ptr(); + } + const std::string& name = parts[0]; + const std::string& value = parts[1]; + + if (LowerCaseEqualsASCII(name, "crash")) { + return MetricSample::CrashSample(value); + } else if (LowerCaseEqualsASCII(name, "histogram")) { + return MetricSample::ParseHistogram(value); + } else if (LowerCaseEqualsASCII(name, "linearhistogram")) { + return MetricSample::ParseLinearHistogram(value); + } else if (LowerCaseEqualsASCII(name, "sparsehistogram")) { + return MetricSample::ParseSparseHistogram(value); + } else if (LowerCaseEqualsASCII(name, "useraction")) { + return MetricSample::UserActionSample(value); + } else { + DLOG(ERROR) << "invalid event type: " << name << ", value: " << value; + } + return scoped_ptr(); +} + +void SerializationUtils::ReadAndTruncateMetricsFromFile( + const std::string& filename, + ScopedVector* 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. + 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); + if (result < 0) { + DPLOG(ERROR) << filename << ": cannot lock"; + return; + } + + // 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()); + } + + result = ftruncate(fd.get(), 0); + if (result < 0) + DPLOG(ERROR) << "truncate metrics log"; + + result = flock(fd.get(), LOCK_UN); + if (result < 0) + DPLOG(ERROR) << "unlock metrics log"; +} + +bool SerializationUtils::WriteMetricToFile(const MetricSample& sample, + const std::string& filename) { + if (!sample.IsValid()) + return false; + + base::ScopedFD file_descriptor(open(filename.c_str(), + O_WRONLY | O_APPEND | O_CREAT, + READ_WRITE_ALL_FILE_FLAGS)); + + if (file_descriptor.get() < 0) { + DLOG(ERROR) << "error openning the file"; + return false; + } + + fchmod(file_descriptor.get(), READ_WRITE_ALL_FILE_FLAGS); + // Grab a lock to avoid chrome truncating the file + // underneath us. Keep the file locked as briefly as possible. + // Freeing file_descriptor will close the file and and remove the lock. + if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) { + DLOG(ERROR) << "error locking" << filename << " : " << errno; + return false; + } + + std::string msg = sample.ToString(); + int32 size = msg.length() + sizeof(int32); + if (size > kMessageMaxLength) { + DLOG(ERROR) << "cannot write message: too long"; + return false; + } + + // The file containing the metrics samples will only be read by programs on + // the same device so we do not check endianness. + if (base::WriteFileDescriptor(file_descriptor.get(), + reinterpret_cast(&size), + sizeof(size)) != sizeof(size)) { + DPLOG(ERROR) << "error writing message length"; + return false; + } + + if (base::WriteFileDescriptor( + file_descriptor.get(), msg.c_str(), msg.size()) != + static_cast(msg.size())) { + DPLOG(ERROR) << "error writing message"; + return false; + } + + return true; +} + +} // namespace metrics diff --git a/metrics/serialization/serialization_utils.h b/metrics/serialization/serialization_utils.h new file mode 100644 index 000000000..5af61660f --- /dev/null +++ b/metrics/serialization/serialization_utils.h @@ -0,0 +1,48 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_ +#define METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_ + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" + +namespace metrics { + +class MetricSample; + +// Metrics helpers to serialize and deserialize metrics collected by +// ChromeOS. +namespace SerializationUtils { + +// Deserializes a sample passed as a string and return a sample. +// The return value will either be a scoped_ptr to a Metric sample (if the +// 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. +void ReadAndTruncateMetricsFromFile(const std::string& filename, + ScopedVector* metrics); + +// Serializes a sample and write it to filename. +// The format for the message is: +// message_size, serialized_message +// where +// * message_size is the total length of the message (message_size + +// serialized_message) on 4 bytes +// * serialized_message is the serialized version of sample (using ToString) +// +// NB: the file will never leave the device so message_size will be written +// with the architecture's endianness. +bool WriteMetricToFile(const MetricSample& sample, const std::string& filename); + +// Maximum length of a serialized message +static const int kMessageMaxLength = 1024; + +} // namespace SerializationUtils +} // namespace metrics + +#endif // METRICS_SERIALIZATION_SERIALIZATION_UTILS_H_ diff --git a/metrics/serialization/serialization_utils_unittest.cc b/metrics/serialization/serialization_utils_unittest.cc new file mode 100644 index 000000000..d47fbc847 --- /dev/null +++ b/metrics/serialization/serialization_utils_unittest.cc @@ -0,0 +1,169 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics/serialization/serialization_utils.h" + +#include +#include +#include +#include +#include + +#include "metrics/serialization/metric_sample.h" + +namespace metrics { +namespace { + +class SerializationUtilsTest : public testing::Test { + protected: + SerializationUtilsTest() { + bool success = temporary_dir.CreateUniqueTempDir(); + if (success) { + base::FilePath dir_path = temporary_dir.path(); + filename = dir_path.value() + "chromeossampletest"; + filepath = base::FilePath(filename); + } + } + + void SetUp() override { base::DeleteFile(filepath, false); } + + void TestSerialization(MetricSample* sample) { + std::string serialized(sample->ToString()); + ASSERT_EQ('\0', serialized[serialized.length() - 1]); + scoped_ptr deserialized = + SerializationUtils::ParseSample(serialized); + ASSERT_TRUE(deserialized); + EXPECT_TRUE(sample->IsEqual(*deserialized.get())); + } + + std::string filename; + base::ScopedTempDir temporary_dir; + base::FilePath filepath; +}; + +TEST_F(SerializationUtilsTest, CrashSerializeTest) { + TestSerialization(MetricSample::CrashSample("test").get()); +} + +TEST_F(SerializationUtilsTest, HistogramSerializeTest) { + TestSerialization( + MetricSample::HistogramSample("myhist", 13, 1, 100, 10).get()); +} + +TEST_F(SerializationUtilsTest, LinearSerializeTest) { + TestSerialization( + MetricSample::LinearHistogramSample("linearhist", 12, 30).get()); +} + +TEST_F(SerializationUtilsTest, SparseSerializeTest) { + TestSerialization(MetricSample::SparseHistogramSample("mysparse", 30).get()); +} + +TEST_F(SerializationUtilsTest, UserActionSerializeTest) { + TestSerialization(MetricSample::UserActionSample("myaction").get()); +} + +TEST_F(SerializationUtilsTest, IllegalNameAreFilteredTest) { + scoped_ptr sample1 = + MetricSample::SparseHistogramSample("no space", 10); + scoped_ptr sample2 = MetricSample::LinearHistogramSample( + base::StringPrintf("here%cbhe", '\0'), 1, 3); + + EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample1.get(), filename)); + EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*sample2.get(), filename)); + int64 size = 0; + + ASSERT_TRUE(!PathExists(filepath) || base::GetFileSize(filepath, &size)); + + EXPECT_EQ(0, size); +} + +TEST_F(SerializationUtilsTest, BadInputIsCaughtTest) { + std::string input( + base::StringPrintf("sparsehistogram%cname foo%c", '\0', '\0')); + EXPECT_EQ(NULL, MetricSample::ParseSparseHistogram(input).get()); +} + +TEST_F(SerializationUtilsTest, MessageSeparatedByZero) { + scoped_ptr crash = MetricSample::CrashSample("mycrash"); + + SerializationUtils::WriteMetricToFile(*crash.get(), filename); + int64 size = 0; + ASSERT_TRUE(base::GetFileSize(filepath, &size)); + // 4 bytes for the size + // 5 bytes for crash + // 7 bytes for mycrash + // 2 bytes for the \0 + // -> total of 18 + EXPECT_EQ(size, 18); +} + +TEST_F(SerializationUtilsTest, MessagesTooLongAreDiscardedTest) { + // Creates a message that is bigger than the maximum allowed size. + // As we are adding extra character (crash, \0s, etc), if the name is + // kMessageMaxLength long, it will be too long. + std::string name(SerializationUtils::kMessageMaxLength, 'c'); + + scoped_ptr crash = MetricSample::CrashSample(name); + EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*crash.get(), filename)); + int64 size = 0; + ASSERT_TRUE(base::GetFileSize(filepath, &size)); + EXPECT_EQ(0, size); +} + +TEST_F(SerializationUtilsTest, ReadLongMessageTest) { + base::File test_file(filepath, + base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND); + std::string message(SerializationUtils::kMessageMaxLength + 1, 'c'); + + int32 message_size = message.length() + sizeof(int32); + test_file.WriteAtCurrentPos(reinterpret_cast(&message_size), + sizeof(message_size)); + test_file.WriteAtCurrentPos(message.c_str(), message.length()); + test_file.Close(); + + scoped_ptr crash = MetricSample::CrashSample("test"); + SerializationUtils::WriteMetricToFile(*crash.get(), filename); + + ScopedVector samples; + SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &samples); + ASSERT_EQ(size_t(1), samples.size()); + ASSERT_TRUE(samples[0] != NULL); + EXPECT_TRUE(crash->IsEqual(*samples[0])); +} + +TEST_F(SerializationUtilsTest, WriteReadTest) { + scoped_ptr hist = + MetricSample::HistogramSample("myhist", 1, 2, 3, 4); + scoped_ptr crash = MetricSample::CrashSample("mycrash"); + scoped_ptr lhist = + MetricSample::LinearHistogramSample("linear", 1, 10); + scoped_ptr shist = + MetricSample::SparseHistogramSample("mysparse", 30); + scoped_ptr action = MetricSample::UserActionSample("myaction"); + + SerializationUtils::WriteMetricToFile(*hist.get(), filename); + SerializationUtils::WriteMetricToFile(*crash.get(), filename); + SerializationUtils::WriteMetricToFile(*lhist.get(), filename); + SerializationUtils::WriteMetricToFile(*shist.get(), filename); + SerializationUtils::WriteMetricToFile(*action.get(), filename); + ScopedVector vect; + SerializationUtils::ReadAndTruncateMetricsFromFile(filename, &vect); + ASSERT_EQ(vect.size(), size_t(5)); + for (int i = 0; i < 5; i++) { + ASSERT_TRUE(vect[0] != NULL); + } + EXPECT_TRUE(hist->IsEqual(*vect[0])); + EXPECT_TRUE(crash->IsEqual(*vect[1])); + EXPECT_TRUE(lhist->IsEqual(*vect[2])); + EXPECT_TRUE(shist->IsEqual(*vect[3])); + EXPECT_TRUE(action->IsEqual(*vect[4])); + + int64 size = 0; + ASSERT_TRUE(base::GetFileSize(filepath, &size)); + ASSERT_EQ(0, size); +} + +} // namespace +} // namespace metrics diff --git a/metrics/uploader/metrics_hashes.cc b/metrics/uploader/metrics_hashes.cc new file mode 100644 index 000000000..87405a377 --- /dev/null +++ b/metrics/uploader/metrics_hashes.cc @@ -0,0 +1,39 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics/uploader/metrics_hashes.h" + +#include "base/logging.h" +#include "base/md5.h" +#include "base/sys_byteorder.h" + +namespace metrics { + +namespace { + +// Converts the 8-byte prefix of an MD5 hash into a uint64 value. +inline uint64_t HashToUInt64(const std::string& hash) { + uint64_t value; + DCHECK_GE(hash.size(), sizeof(value)); + memcpy(&value, hash.data(), sizeof(value)); + return base::HostToNet64(value); +} + +} // namespace + +uint64_t HashMetricName(const std::string& name) { + // Create an MD5 hash of the given |name|, represented as a byte buffer + // encoded as an std::string. + base::MD5Context context; + base::MD5Init(&context); + base::MD5Update(&context, name); + + base::MD5Digest digest; + base::MD5Final(&digest, &context); + + std::string hash_str(reinterpret_cast(digest.a), arraysize(digest.a)); + return HashToUInt64(hash_str); +} + +} // namespace metrics diff --git a/metrics/uploader/metrics_hashes.h b/metrics/uploader/metrics_hashes.h new file mode 100644 index 000000000..8679077e1 --- /dev/null +++ b/metrics/uploader/metrics_hashes.h @@ -0,0 +1,18 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef METRICS_UPLOADER_METRICS_HASHES_H_ +#define METRICS_UPLOADER_METRICS_HASHES_H_ + +#include + +namespace metrics { + +// Computes a uint64 hash of a given string based on its MD5 hash. Suitable for +// metric names. +uint64_t HashMetricName(const std::string& name); + +} // namespace metrics + +#endif // METRICS_UPLOADER_METRICS_HASHES_H_ diff --git a/metrics/uploader/metrics_hashes_unittest.cc b/metrics/uploader/metrics_hashes_unittest.cc new file mode 100644 index 000000000..f7e390f64 --- /dev/null +++ b/metrics/uploader/metrics_hashes_unittest.cc @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics/uploader/metrics_hashes.h" + +#include +#include +#include +#include + +namespace metrics { + +// Make sure our ID hashes are the same as what we see on the server side. +TEST(MetricsUtilTest, HashMetricName) { + static const struct { + std::string input; + std::string output; + } cases[] = { + {"Back", "0x0557fa923dcee4d0"}, + {"Forward", "0x67d2f6740a8eaebf"}, + {"NewTab", "0x290eb683f96572f1"}, + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + uint64_t hash = HashMetricName(cases[i].input); + std::string hash_hex = base::StringPrintf("0x%016" PRIx64, hash); + EXPECT_EQ(cases[i].output, hash_hex); + } +} + +} // namespace metrics diff --git a/metrics/uploader/metrics_log.h b/metrics/uploader/metrics_log.h index 37a82bdd4..579632578 100644 --- a/metrics/uploader/metrics_log.h +++ b/metrics/uploader/metrics_log.h @@ -9,7 +9,7 @@ #include -#include "components/metrics/metrics_log_base.h" +#include "metrics/uploader/metrics_log_base.h" // This file defines a set of user experience metrics data recorded by // the MetricsService. This is the unit of data that is sent to the server. diff --git a/metrics/uploader/metrics_log_base.cc b/metrics/uploader/metrics_log_base.cc new file mode 100644 index 000000000..43cf82e29 --- /dev/null +++ b/metrics/uploader/metrics_log_base.cc @@ -0,0 +1,142 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics/uploader/metrics_log_base.h" + +#include "base/metrics/histogram_base.h" +#include "base/metrics/histogram_samples.h" +#include "components/metrics/proto/histogram_event.pb.h" +#include "components/metrics/proto/system_profile.pb.h" +#include "components/metrics/proto/user_action_event.pb.h" +#include "metrics/uploader/metrics_hashes.h" + +using base::Histogram; +using base::HistogramBase; +using base::HistogramSamples; +using base::SampleCountIterator; +using base::Time; +using base::TimeDelta; +using metrics::HistogramEventProto; +using metrics::SystemProfileProto; +using metrics::UserActionEventProto; + +namespace metrics { +namespace { + +// Any id less than 16 bytes is considered to be a testing id. +bool IsTestingID(const std::string& id) { + return id.size() < 16; +} + +} // namespace + +MetricsLogBase::MetricsLogBase(const std::string& client_id, + int session_id, + LogType log_type, + const std::string& version_string) + : num_events_(0), + locked_(false), + log_type_(log_type) { + DCHECK_NE(NO_LOG, log_type); + if (IsTestingID(client_id)) + uma_proto_.set_client_id(0); + else + uma_proto_.set_client_id(Hash(client_id)); + + uma_proto_.set_session_id(session_id); + uma_proto_.mutable_system_profile()->set_build_timestamp(GetBuildTime()); + uma_proto_.mutable_system_profile()->set_app_version(version_string); +} + +MetricsLogBase::~MetricsLogBase() {} + +// static +uint64_t MetricsLogBase::Hash(const std::string& value) { + uint64_t hash = metrics::HashMetricName(value); + + // The following log is VERY helpful when folks add some named histogram into + // the code, but forgot to update the descriptive list of histograms. When + // that happens, all we get to see (server side) is a hash of the histogram + // name. We can then use this logging to find out what histogram name was + // being hashed to a given MD5 value by just running the version of Chromium + // in question with --enable-logging. + DVLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << hash << "]"; + + return hash; +} + +// static +int64_t MetricsLogBase::GetBuildTime() { + static int64_t integral_build_time = 0; + if (!integral_build_time) { + Time time; + const char* kDateTime = __DATE__ " " __TIME__ " GMT"; + bool result = Time::FromString(kDateTime, &time); + DCHECK(result); + integral_build_time = static_cast(time.ToTimeT()); + } + return integral_build_time; +} + +// static +int64_t MetricsLogBase::GetCurrentTime() { + return (base::TimeTicks::Now() - base::TimeTicks()).InSeconds(); +} + +void MetricsLogBase::CloseLog() { + DCHECK(!locked_); + locked_ = true; +} + +void MetricsLogBase::GetEncodedLog(std::string* encoded_log) { + DCHECK(locked_); + uma_proto_.SerializeToString(encoded_log); +} + +void MetricsLogBase::RecordUserAction(const std::string& key) { + DCHECK(!locked_); + + UserActionEventProto* user_action = uma_proto_.add_user_action_event(); + user_action->set_name_hash(Hash(key)); + user_action->set_time(GetCurrentTime()); + + ++num_events_; +} + +void MetricsLogBase::RecordHistogramDelta(const std::string& histogram_name, + const HistogramSamples& snapshot) { + DCHECK(!locked_); + DCHECK_NE(0, snapshot.TotalCount()); + + // We will ignore the MAX_INT/infinite value in the last element of range[]. + + HistogramEventProto* histogram_proto = uma_proto_.add_histogram_event(); + histogram_proto->set_name_hash(Hash(histogram_name)); + histogram_proto->set_sum(snapshot.sum()); + + for (scoped_ptr it = snapshot.Iterator(); !it->Done(); + it->Next()) { + HistogramBase::Sample min; + HistogramBase::Sample max; + HistogramBase::Count count; + it->Get(&min, &max, &count); + HistogramEventProto::Bucket* bucket = histogram_proto->add_bucket(); + bucket->set_min(min); + bucket->set_max(max); + bucket->set_count(count); + } + + // Omit fields to save space (see rules in histogram_event.proto comments). + for (int i = 0; i < histogram_proto->bucket_size(); ++i) { + HistogramEventProto::Bucket* bucket = histogram_proto->mutable_bucket(i); + if (i + 1 < histogram_proto->bucket_size() && + bucket->max() == histogram_proto->bucket(i + 1).min()) { + bucket->clear_max(); + } else if (bucket->max() == bucket->min() + 1) { + bucket->clear_min(); + } + } +} + +} // namespace metrics diff --git a/metrics/uploader/metrics_log_base.h b/metrics/uploader/metrics_log_base.h new file mode 100644 index 000000000..5a54c30af --- /dev/null +++ b/metrics/uploader/metrics_log_base.h @@ -0,0 +1,110 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file defines a set of user experience metrics data recorded by +// the MetricsService. This is the unit of data that is sent to the server. + +#ifndef METRICS_UPLOADER_METRICS_LOG_BASE_H_ +#define METRICS_UPLOADER_METRICS_LOG_BASE_H_ + +#include + +#include "base/macros.h" +#include "base/metrics/histogram.h" +#include "base/time/time.h" +#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" + +namespace base { +class HistogramSamples; +} // namespace base + +namespace metrics { + +// This class provides base functionality for logging metrics data. +class MetricsLogBase { + public: + // TODO(asvitkine): Remove the NO_LOG value. + enum LogType { + INITIAL_STABILITY_LOG, // The initial log containing stability stats. + ONGOING_LOG, // Subsequent logs in a session. + NO_LOG, // Placeholder value for when there is no log. + }; + + // Creates a new metrics log of the specified type. + // client_id is the identifier for this profile on this installation + // session_id is an integer that's incremented on each application launch + MetricsLogBase(const std::string& client_id, + int session_id, + LogType log_type, + const std::string& version_string); + virtual ~MetricsLogBase(); + + // Computes the MD5 hash of the given string, and returns the first 8 bytes of + // the hash. + static uint64_t Hash(const std::string& value); + + // Get the GMT buildtime for the current binary, expressed in seconds since + // January 1, 1970 GMT. + // The value is used to identify when a new build is run, so that previous + // reliability stats, from other builds, can be abandoned. + static int64_t GetBuildTime(); + + // Convenience function to return the current time at a resolution in seconds. + // This wraps base::TimeTicks, and hence provides an abstract time that is + // always incrementing for use in measuring time durations. + static int64_t GetCurrentTime(); + + // Records a user-initiated action. + void RecordUserAction(const std::string& key); + + // Record any changes in a given histogram for transmission. + void RecordHistogramDelta(const std::string& histogram_name, + const base::HistogramSamples& snapshot); + + // Stop writing to this record and generate the encoded representation. + // None of the Record* methods can be called after this is called. + void CloseLog(); + + // Fills |encoded_log| with the serialized protobuf representation of the + // record. Must only be called after CloseLog() has been called. + void GetEncodedLog(std::string* encoded_log); + + int num_events() { return num_events_; } + + void set_hardware_class(const std::string& hardware_class) { + uma_proto_.mutable_system_profile()->mutable_hardware()->set_hardware_class( + hardware_class); + } + + LogType log_type() const { return log_type_; } + + protected: + bool locked() const { return locked_; } + + metrics::ChromeUserMetricsExtension* uma_proto() { return &uma_proto_; } + const metrics::ChromeUserMetricsExtension* uma_proto() const { + return &uma_proto_; + } + + // TODO(isherman): Remove this once the XML pipeline is outta here. + int num_events_; // the number of events recorded in this log + + private: + // locked_ is true when record has been packed up for sending, and should + // no longer be written to. It is only used for sanity checking and is + // not a real lock. + bool locked_; + + // The type of the log, i.e. initial or ongoing. + const LogType log_type_; + + // Stores the protocol buffer representation for this log. + metrics::ChromeUserMetricsExtension uma_proto_; + + DISALLOW_COPY_AND_ASSIGN(MetricsLogBase); +}; + +} // namespace metrics + +#endif // METRICS_UPLOADER_METRICS_LOG_BASE_H_ diff --git a/metrics/uploader/metrics_log_base_unittest.cc b/metrics/uploader/metrics_log_base_unittest.cc new file mode 100644 index 000000000..fe02fea06 --- /dev/null +++ b/metrics/uploader/metrics_log_base_unittest.cc @@ -0,0 +1,126 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "metrics/uploader/metrics_log_base.h" + +#include + +#include +#include +#include +#include + +#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" + +namespace metrics { + +namespace { + +class TestMetricsLogBase : public MetricsLogBase { + public: + TestMetricsLogBase() + : MetricsLogBase("client_id", 1, MetricsLogBase::ONGOING_LOG, "1.2.3.4") { + } + virtual ~TestMetricsLogBase() {} + + using MetricsLogBase::uma_proto; + + private: + DISALLOW_COPY_AND_ASSIGN(TestMetricsLogBase); +}; + +} // namespace + +TEST(MetricsLogBaseTest, LogType) { + MetricsLogBase log1("id", 0, MetricsLogBase::ONGOING_LOG, "1.2.3"); + EXPECT_EQ(MetricsLogBase::ONGOING_LOG, log1.log_type()); + + MetricsLogBase log2("id", 0, MetricsLogBase::INITIAL_STABILITY_LOG, "1.2.3"); + EXPECT_EQ(MetricsLogBase::INITIAL_STABILITY_LOG, log2.log_type()); +} + +TEST(MetricsLogBaseTest, EmptyRecord) { + MetricsLogBase log("totally bogus client ID", 137, + MetricsLogBase::ONGOING_LOG, "bogus version"); + log.set_hardware_class("sample-class"); + log.CloseLog(); + + std::string encoded; + log.GetEncodedLog(&encoded); + + // A couple of fields are hard to mock, so these will be copied over directly + // for the expected output. + metrics::ChromeUserMetricsExtension parsed; + ASSERT_TRUE(parsed.ParseFromString(encoded)); + + metrics::ChromeUserMetricsExtension expected; + expected.set_client_id(5217101509553811875); // Hashed bogus client ID + expected.set_session_id(137); + expected.mutable_system_profile()->set_build_timestamp( + parsed.system_profile().build_timestamp()); + expected.mutable_system_profile()->set_app_version("bogus version"); + expected.mutable_system_profile()->mutable_hardware()->set_hardware_class( + "sample-class"); + + EXPECT_EQ(expected.SerializeAsString(), encoded); +} + +TEST(MetricsLogBaseTest, HistogramBucketFields) { + // Create buckets: 1-5, 5-7, 7-8, 8-9, 9-10, 10-11, 11-12. + base::BucketRanges ranges(8); + ranges.set_range(0, 1); + ranges.set_range(1, 5); + ranges.set_range(2, 7); + ranges.set_range(3, 8); + ranges.set_range(4, 9); + ranges.set_range(5, 10); + ranges.set_range(6, 11); + ranges.set_range(7, 12); + + base::SampleVector samples(&ranges); + samples.Accumulate(3, 1); // Bucket 1-5. + samples.Accumulate(6, 1); // Bucket 5-7. + samples.Accumulate(8, 1); // Bucket 8-9. (7-8 skipped) + samples.Accumulate(10, 1); // Bucket 10-11. (9-10 skipped) + samples.Accumulate(11, 1); // Bucket 11-12. + + TestMetricsLogBase log; + log.RecordHistogramDelta("Test", samples); + + const metrics::ChromeUserMetricsExtension* uma_proto = log.uma_proto(); + const metrics::HistogramEventProto& histogram_proto = + uma_proto->histogram_event(uma_proto->histogram_event_size() - 1); + + // Buckets with samples: 1-5, 5-7, 8-9, 10-11, 11-12. + // Should become: 1-/, 5-7, /-9, 10-/, /-12. + ASSERT_EQ(5, histogram_proto.bucket_size()); + + // 1-5 becomes 1-/ (max is same as next min). + EXPECT_TRUE(histogram_proto.bucket(0).has_min()); + EXPECT_FALSE(histogram_proto.bucket(0).has_max()); + EXPECT_EQ(1, histogram_proto.bucket(0).min()); + + // 5-7 stays 5-7 (no optimization possible). + EXPECT_TRUE(histogram_proto.bucket(1).has_min()); + EXPECT_TRUE(histogram_proto.bucket(1).has_max()); + EXPECT_EQ(5, histogram_proto.bucket(1).min()); + EXPECT_EQ(7, histogram_proto.bucket(1).max()); + + // 8-9 becomes /-9 (min is same as max - 1). + EXPECT_FALSE(histogram_proto.bucket(2).has_min()); + EXPECT_TRUE(histogram_proto.bucket(2).has_max()); + EXPECT_EQ(9, histogram_proto.bucket(2).max()); + + // 10-11 becomes 10-/ (both optimizations apply, omit max is prioritized). + EXPECT_TRUE(histogram_proto.bucket(3).has_min()); + EXPECT_FALSE(histogram_proto.bucket(3).has_max()); + EXPECT_EQ(10, histogram_proto.bucket(3).min()); + + // 11-12 becomes /-12 (last record must keep max, min is same as max - 1). + EXPECT_FALSE(histogram_proto.bucket(4).has_min()); + EXPECT_TRUE(histogram_proto.bucket(4).has_max()); + EXPECT_EQ(12, histogram_proto.bucket(4).max()); +} + +} // namespace metrics diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index cbb80512c..be3d8ecaa 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -13,9 +13,9 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/sys_info.h" -#include "components/metrics/metrics_log_base.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "metrics/persistent_integer.h" +#include "metrics/uploader/metrics_log_base.h" #include "vboot/crossystem.h" namespace { diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index 7e856f925..cdec91e41 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -15,9 +15,9 @@ #include #include #include -#include -#include +#include "metrics/serialization/metric_sample.h" +#include "metrics/serialization/serialization_utils.h" #include "metrics/uploader/metrics_log.h" #include "metrics/uploader/sender_http.h" #include "metrics/uploader/system_profile_cache.h" diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index c48747e81..ff96f85ac 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -9,15 +9,15 @@ #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/sys_info.h" -#include "components/metrics/chromeos/metric_sample.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "components/metrics/proto/histogram_event.pb.h" #include "components/metrics/proto/system_profile.pb.h" -#include "uploader/metrics_log.h" -#include "uploader/mock/mock_system_profile_setter.h" -#include "uploader/mock/sender_mock.h" -#include "uploader/system_profile_cache.h" -#include "uploader/upload_service.h" +#include "metrics/serialization/metric_sample.h" +#include "metrics/uploader/metrics_log.h" +#include "metrics/uploader/mock/mock_system_profile_setter.h" +#include "metrics/uploader/mock/sender_mock.h" +#include "metrics/uploader/system_profile_cache.h" +#include "metrics/uploader/upload_service.h" static const char kMetricsServer[] = "https://clients4.google.com/uma/v2"; static const char kMetricsFilePath[] = "/var/run/metrics/uma-events"; From 73b40b468b32767191a8db55752f26ae89b4a607 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Wed, 10 Dec 2014 12:52:31 -0800 Subject: [PATCH 177/200] Update libchrome to r307740 and fix build errors Updated libchrome, libchrome_crypto, metrics, feedback to the latest revisions from Chrome (r307740). Fixed build breaks due to the changes in upstream code: - scope_ptr no longer needs explicit PassAs() calls. - scope_ptr no longer has implicit conversion to T*. Must use scope_ptr::get() instead. - base/file_util.h moved to base/files/file_util.h - ARRAYSIZE_UNSAFE() removed in favor of arraysize() - base::AppendToFile() and base::WriteFileDescriptor() now return bool instead of the number of bytes written. - dbus::Bus::AddFilterFunction() now returns void. - C++11 features are enabled in libchromeos, so all targets linking with it now have to support C++11 - OVERRIDE macro is removed in favor of native C++11 'override' keyword. BUG=chromium:416628, chromium:411508 TEST=Build the world on x86, x64, ARM. The following builders were tried: x86-generic-full amd64-generic-full arm-generic-full amd64-generic-asan daisy-full nyan-full pre-cq-group daisy-release-group sandybridge-release-group pineview-release-group CQ-DEPEND=CL:234450,CL:234980 Change-Id: I374bebe2211d533c4431c82efb8be1cdcb1f405d Reviewed-on: https://chromium-review.googlesource.com/234440 Reviewed-by: Bertrand Simonnet Tested-by: Alex Vakulenko Reviewed-by: Mike Frysinger Commit-Queue: Alex Vakulenko --- metrics/libmetrics-293518.gyp | 8 -------- metrics/libmetrics-307740.gyp | 8 ++++++++ metrics/metrics.gyp | 1 + metrics/serialization/serialization_utils.cc | 13 ++++++------- .../serialization/serialization_utils_unittest.cc | 2 +- metrics/uploader/system_profile_cache.cc | 2 +- 6 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 metrics/libmetrics-293518.gyp create mode 100644 metrics/libmetrics-307740.gyp diff --git a/metrics/libmetrics-293518.gyp b/metrics/libmetrics-293518.gyp deleted file mode 100644 index 1b0f78cf4..000000000 --- a/metrics/libmetrics-293518.gyp +++ /dev/null @@ -1,8 +0,0 @@ -{ - 'variables': { - 'libbase_ver': 293518, - }, - 'includes': [ - '../metrics/libmetrics.gypi', - ], -} diff --git a/metrics/libmetrics-307740.gyp b/metrics/libmetrics-307740.gyp new file mode 100644 index 000000000..42dd0dae8 --- /dev/null +++ b/metrics/libmetrics-307740.gyp @@ -0,0 +1,8 @@ +{ + 'variables': { + 'libbase_ver': 307740, + }, + 'includes': [ + 'libmetrics.gypi', + ], +} diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 9614826e0..60e2d1a95 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -94,6 +94,7 @@ '<(proto_in_dir)/chrome_user_metrics_extension.proto', '<(proto_in_dir)/histogram_event.proto', '<(proto_in_dir)/omnibox_event.proto', + '<(proto_in_dir)/omnibox_input_type.proto', '<(proto_in_dir)/perf_data.proto', '<(proto_in_dir)/profiler_event.proto', '<(proto_in_dir)/sampled_profile.proto', diff --git a/metrics/serialization/serialization_utils.cc b/metrics/serialization/serialization_utils.cc index fea493e6a..80e81d2c1 100644 --- a/metrics/serialization/serialization_utils.cc +++ b/metrics/serialization/serialization_utils.cc @@ -9,8 +9,8 @@ #include #include -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -174,7 +174,7 @@ bool SerializationUtils::WriteMetricToFile(const MetricSample& sample, READ_WRITE_ALL_FILE_FLAGS)); if (file_descriptor.get() < 0) { - DLOG(ERROR) << "error openning the file"; + DLOG(ERROR) << "error opening the file"; return false; } @@ -196,16 +196,15 @@ bool SerializationUtils::WriteMetricToFile(const MetricSample& sample, // The file containing the metrics samples will only be read by programs on // the same device so we do not check endianness. - if (base::WriteFileDescriptor(file_descriptor.get(), + if (!base::WriteFileDescriptor(file_descriptor.get(), reinterpret_cast(&size), - sizeof(size)) != sizeof(size)) { + sizeof(size))) { DPLOG(ERROR) << "error writing message length"; return false; } - if (base::WriteFileDescriptor( - file_descriptor.get(), msg.c_str(), msg.size()) != - static_cast(msg.size())) { + if (!base::WriteFileDescriptor( + file_descriptor.get(), msg.c_str(), msg.size())) { DPLOG(ERROR) << "error writing message"; return false; } diff --git a/metrics/serialization/serialization_utils_unittest.cc b/metrics/serialization/serialization_utils_unittest.cc index d47fbc847..34d76cf8b 100644 --- a/metrics/serialization/serialization_utils_unittest.cc +++ b/metrics/serialization/serialization_utils_unittest.cc @@ -4,7 +4,7 @@ #include "metrics/serialization/serialization_utils.h" -#include +#include #include #include #include diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index be3d8ecaa..54c269390 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "uploader/system_profile_cache.h" +#include "metrics/uploader/system_profile_cache.h" #include #include From e86591e58533e750481ee3177bd168331b5dd016 Mon Sep 17 00:00:00 2001 From: Steve Fung Date: Mon, 1 Dec 2014 13:38:21 -0800 Subject: [PATCH 178/200] metrics: Convert Metrics to DBusDaemon In order to remove glib, convert Metrics from the glib message loop to DBusDaemon, which is based on base::MessageLoop. Also use the DBusDaemon's dbus::Bus object directly to set up the CrashReporter signal handling, as the ObjectProxy used by chromeos-dbus-bindings requires all signals be sent from registered name owners. BUG=chromium:431838 TEST=Unittests and hwtests pass. TEST=`test_that -b panther platform_MetricsUploader` passes CQ-DEPEND=CL:236652 Change-Id: I6bc1f66999a43065b0d48325b031cd36bb782b76 Reviewed-on: https://chromium-review.googlesource.com/234359 Reviewed-by: Alex Vakulenko Commit-Queue: Steve Fung Tested-by: Steve Fung --- metrics/metrics_daemon.cc | 170 ++++++++++++++++++--------------- metrics/metrics_daemon.h | 51 ++++------ metrics/metrics_daemon_main.cc | 9 +- metrics/metrics_daemon_test.cc | 6 +- 4 files changed, 120 insertions(+), 116 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 7226594cb..acd96c079 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -24,7 +25,8 @@ #include #include #include -#include +#include +#include #include "uploader/upload_service.h" using base::FilePath; @@ -43,6 +45,8 @@ namespace { const char kCrashReporterInterface[] = "org.chromium.CrashReporter"; const char kCrashReporterUserCrashSignal[] = "UserCrash"; +const char kCrashReporterMatchRule[] = + "type='signal',interface='%s',path='/',member='%s'"; const int kSecondsPerMinute = 60; const int kMinutesPerHour = 60; @@ -53,7 +57,7 @@ const int kDaysPerWeek = 7; const int kSecondsPerWeek = kSecondsPerDay * kDaysPerWeek; // Interval between calls to UpdateStats(). -const guint kUpdateStatsIntervalMs = 300000; +const uint32_t kUpdateStatsIntervalMs = 300000; const char kKernelCrashDetectedFile[] = "/var/run/kernel-crash-detected"; const char kUncleanShutdownDetectedFile[] = @@ -135,8 +139,7 @@ static const int kMemuseIntervals[] = { }; MetricsDaemon::MetricsDaemon() - : update_stats_timeout_id_(-1), - memuse_final_time_(0), + : memuse_final_time_(0), memuse_interval_index_(0), read_sectors_(0), write_sectors_(0), @@ -147,8 +150,6 @@ MetricsDaemon::MetricsDaemon() latest_cpu_use_ticks_(0) {} MetricsDaemon::~MetricsDaemon() { - if (update_stats_timeout_id_ > -1) - g_source_remove(update_stats_timeout_id_); } double MetricsDaemon::GetActiveTime() { @@ -162,10 +163,7 @@ double MetricsDaemon::GetActiveTime() { } } -void MetricsDaemon::Run(bool run_as_daemon) { - if (run_as_daemon && daemon(0, 0) != 0) - return; - +int MetricsDaemon::Run() { if (CheckSystemCrash(kKernelCrashDetectedFile)) { ProcessKernelCrash(); } @@ -183,7 +181,7 @@ void MetricsDaemon::Run(bool run_as_daemon) { version_cumulative_cpu_use_->Set(0); } - Loop(); + return chromeos::DBusDaemon::Run(); } void MetricsDaemon::RunUploaderTest() { @@ -223,6 +221,7 @@ void MetricsDaemon::Init(bool testing, const string& metrics_file, const string& config_root) { testing_ = testing; + uploader_active_ = uploader_active; config_root_ = config_root; DCHECK(metrics_lib != nullptr); metrics_lib_ = metrics_lib; @@ -276,6 +275,17 @@ void MetricsDaemon::Init(bool testing, vmstats_path_ = vmstats_path; scaling_max_freq_path_ = scaling_max_freq_path; cpuinfo_max_freq_path_ = cpuinfo_max_freq_path; + + // If testing, initialize Stats Reporter without connecting DBus + if (testing_) + StatsReporterInit(); +} + +int MetricsDaemon::OnInit() { + int return_code = chromeos::DBusDaemon::OnInit(); + if (return_code != EX_OK) + return return_code; + StatsReporterInit(); // Start collecting meminfo stats. @@ -283,56 +293,67 @@ void MetricsDaemon::Init(bool testing, memuse_final_time_ = GetActiveTime() + kMemuseIntervals[0]; ScheduleMemuseCallback(kMemuseIntervals[0]); - // Don't setup D-Bus and GLib in test mode. - if (testing) - return; + if (testing_) + return EX_OK; - g_type_init(); - dbus_threads_init_default(); + bus_->AssertOnDBusThread(); + CHECK(bus_->SetUpAsyncOperations()); - DBusError error; - dbus_error_init(&error); + if (bus_->is_connected()) { + const std::string match_rule = + base::StringPrintf(kCrashReporterMatchRule, + kCrashReporterInterface, + kCrashReporterUserCrashSignal); - DBusConnection* connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); - LOG_IF(FATAL, dbus_error_is_set(&error)) << - "No D-Bus connection: " << SAFE_MESSAGE(error); + bus_->AddFilterFunction(&MetricsDaemon::MessageFilter, this); - dbus_connection_setup_with_g_main(connection, nullptr); + DBusError error; + dbus_error_init(&error); + bus_->AddMatch(match_rule, &error); - vector matches; - matches.push_back( - base::StringPrintf("type='signal',interface='%s',path='/',member='%s'", - kCrashReporterInterface, - kCrashReporterUserCrashSignal)); - - // Registers D-Bus matches for the signals we would like to catch. - for (vector::const_iterator it = matches.begin(); - it != matches.end(); ++it) { - const char* match = it->c_str(); - DLOG(INFO) << "adding dbus match: " << match; - dbus_bus_add_match(connection, match, &error); - LOG_IF(FATAL, dbus_error_is_set(&error)) << - "unable to add a match: " << SAFE_MESSAGE(error); + if (dbus_error_is_set(&error)) { + LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got " + << error.name << ": " << error.message; + return EX_SOFTWARE; + } + } else { + LOG(ERROR) << "DBus isn't connected."; + return EX_UNAVAILABLE; } - // Adds the D-Bus filter routine to be called back whenever one of - // the registered D-Bus matches is successful. The daemon is not - // activated for D-Bus messages that don't match. - CHECK(dbus_connection_add_filter(connection, MessageFilter, this, nullptr)); + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs)); - update_stats_timeout_id_ = - g_timeout_add(kUpdateStatsIntervalMs, &HandleUpdateStatsTimeout, this); - - if (uploader_active) { + if (uploader_active_) { LOG(INFO) << "uploader enabled"; upload_service_.reset(new UploadService(new SystemProfileCache(), server_)); upload_service_->Init(upload_interval_, metrics_file_); } + + return EX_OK; } -void MetricsDaemon::Loop() { - GMainLoop* loop = g_main_loop_new(nullptr, false); - g_main_loop_run(loop); +void MetricsDaemon::OnShutdown(int* return_code) { + if (!testing_ && bus_->is_connected()) { + const std::string match_rule = + base::StringPrintf(kCrashReporterMatchRule, + kCrashReporterInterface, + kCrashReporterUserCrashSignal); + + bus_->RemoveFilterFunction(&MetricsDaemon::MessageFilter, this); + + DBusError error; + dbus_error_init(&error); + bus_->RemoveMatch(match_rule, &error); + + if (dbus_error_is_set(&error)) { + LOG(ERROR) << "Failed to remove match rule \"" << match_rule << "\". Got " + << error.name << ": " << error.message; + } + } + chromeos::DBusDaemon::OnShutdown(return_code); } // static @@ -481,7 +502,9 @@ void MetricsDaemon::ScheduleStatsCallback(int wait) { if (testing_) { return; } - g_timeout_add_seconds(wait, StatsCallbackStatic, this); + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MetricsDaemon::StatsCallback, base::Unretained(this)), + base::TimeDelta::FromSeconds(wait)); } bool MetricsDaemon::DiskStatsReadStats(uint64_t* read_sectors, @@ -637,12 +660,6 @@ void MetricsDaemon::SendCpuThrottleMetrics() { SendLinearSample(kMetricScaledCpuFrequencyName, percent, 101, 102); } -// static -gboolean MetricsDaemon::StatsCallbackStatic(void* handle) { - (static_cast(handle))->StatsCallback(); - return false; // one-time callback -} - // Collects disk and vm stats alternating over a short and a long interval. void MetricsDaemon::StatsCallback() { @@ -756,25 +773,31 @@ void MetricsDaemon::ScheduleMeminfoCallback(int wait) { if (testing_) { return; } - g_timeout_add_seconds(wait, MeminfoCallbackStatic, this); + base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait); + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this), + base::TimeDelta::FromMilliseconds(kMetricMeminfoInterval)), + waitDelta); } -// static -gboolean MetricsDaemon::MeminfoCallbackStatic(void* handle) { - return (static_cast(handle))->MeminfoCallback(); -} - -bool MetricsDaemon::MeminfoCallback() { +void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) { string meminfo_raw; const FilePath meminfo_path("/proc/meminfo"); if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) { LOG(WARNING) << "cannot read " << meminfo_path.value().c_str(); - return false; + return; } // Make both calls even if the first one fails. bool success = ProcessMeminfo(meminfo_raw); - return ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) && + bool reschedule = + ReportZram(base::FilePath(FILE_PATH_LITERAL("/sys/block/zram0"))) && success; + if (reschedule) { + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this), + base::TimeDelta::FromMilliseconds(kMetricMeminfoInterval)), + wait); + } } // static @@ -941,14 +964,9 @@ void MetricsDaemon::ScheduleMemuseCallback(double interval) { if (testing_) { return; } - g_timeout_add_seconds(interval, MemuseCallbackStatic, this); -} - -// static -gboolean MetricsDaemon::MemuseCallbackStatic(void* handle) { - MetricsDaemon* daemon = static_cast(handle); - daemon->MemuseCallback(); - return false; + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MetricsDaemon::MemuseCallback, base::Unretained(this)), + base::TimeDelta::FromSeconds(interval)); } void MetricsDaemon::MemuseCallback() { @@ -1139,8 +1157,10 @@ void MetricsDaemon::UpdateStats(TimeTicks now_ticks, } } -// static -gboolean MetricsDaemon::HandleUpdateStatsTimeout(gpointer data) { - static_cast(data)->UpdateStats(TimeTicks::Now(), Time::Now()); - return TRUE; +void MetricsDaemon::HandleUpdateStatsTimeout() { + UpdateStats(TimeTicks::Now(), Time::Now()); + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs)); } diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index f4f14307e..397fd2109 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -7,8 +7,6 @@ #include -#include -#include #include #include #include @@ -16,6 +14,7 @@ #include #include #include +#include #include // for FRIEND_TEST #include "metrics/metrics_library.h" @@ -24,12 +23,12 @@ using chromeos_metrics::PersistentInteger; -class MetricsDaemon { +class MetricsDaemon : public chromeos::DBusDaemon { public: MetricsDaemon(); ~MetricsDaemon(); - // Initializes. + // Initializes metrics class variables. void Init(bool testing, bool uploader_active, MetricsLibraryInterface* metrics_lib, @@ -42,9 +41,14 @@ class MetricsDaemon { const std::string& metrics_file, const std::string& config_root); - // Does all the work. If |run_as_daemon| is true, daemonizes by - // forking. - void Run(bool run_as_daemon); + // Initializes DBus and MessageLoop variables before running the MessageLoop. + int OnInit() override; + + // Clean up data set up in OnInit before shutting down message loop. + void OnShutdown(int* return_code) override; + + // Does all the work. + int Run() override; // Triggers an upload event and exit. (Used to test UploadService) void RunUploaderTest(); @@ -147,9 +151,6 @@ class MetricsDaemon { // Returns the active time since boot (uptime minus sleep time) in seconds. double GetActiveTime(); - // Creates the event loop and enters it. - void Loop(); - // D-Bus filter callback. static DBusHandlerResult MessageFilter(DBusConnection* connection, DBusMessage* message, @@ -227,22 +228,14 @@ class MetricsDaemon { // Parse cumulative vm statistics from a C string. Returns true for success. bool VmStatsParseStats(const char* stats, struct VmstatRecord* record); - // Reports disk and vm statistics (static version for glib). Arguments are a - // glib artifact. - static gboolean StatsCallbackStatic(void* handle); - // Reports disk and vm statistics. void StatsCallback(); // 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. - bool MeminfoCallback(); + // Reports memory statistics. Reschedules callback on success. + void MeminfoCallback(base::TimeDelta wait); // Parses content of /proc/meminfo and sends fields of interest to UMA. // Returns false on errors. |meminfo_raw| contains the content of @@ -259,9 +252,6 @@ class MetricsDaemon { // Schedule a memory use callback in |interval| seconds. void ScheduleMemuseCallback(double interval); - // Static wrapper for MemuseCallback. Always returns false. - static gboolean MemuseCallbackStatic(void* handle); - // Calls MemuseCallbackWork, and possibly schedules next callback, if enough // active time has passed. Otherwise reschedules itself to simulate active // time callbacks (i.e. wall clock time minus sleep time). @@ -288,7 +278,7 @@ class MetricsDaemon { void UpdateStats(base::TimeTicks now_ticks, base::Time now_wall_time); // Invoked periodically by |update_stats_timeout_id_| to call UpdateStats(). - static gboolean HandleUpdateStatsTimeout(gpointer data); + void HandleUpdateStatsTimeout(); // Reports zram statistics. bool ReportZram(const base::FilePath& zram_dir); @@ -301,6 +291,9 @@ class MetricsDaemon { // Test mode. bool testing_; + // Whether the uploader is enabled or disabled. + bool uploader_active_; + // Root of the configuration files to use. std::string config_root_; @@ -315,16 +308,6 @@ class MetricsDaemon { // The last time that UpdateStats() was called. base::TimeTicks last_update_stats_time_; - // ID of a GLib timeout that repeatedly runs UpdateStats(). - gint update_stats_timeout_id_; - - // Sleep period until the next daily usage aggregation performed by - // the daily use monitor (see ScheduleUseMonitor). - int usemon_interval_; - - // Scheduled daily use monitor source (see ScheduleUseMonitor). - GSource* usemon_source_; - // End time of current memuse stat collection interval. double memuse_final_time_; diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index 9322771ee..cc0812d66 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -73,6 +73,11 @@ int main(int argc, char** argv) { // Also log to stderr when not running as daemon. chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogHeader | (FLAGS_daemon ? 0 : chromeos::kLogToStderr)); + + if (FLAGS_daemon && daemon(0, 0) != 0) { + return errno; + } + MetricsLibrary metrics_lib; metrics_lib.Init(); MetricsDaemon daemon; @@ -88,12 +93,10 @@ int main(int argc, char** argv) { FLAGS_metrics_file, FLAGS_config_root); - - base::AtExitManager at_exit_manager; if (FLAGS_uploader_test) { daemon.RunUploaderTest(); return 0; } - daemon.Run(FLAGS_daemon); + daemon.Run(); } diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index ed6856a95..abf8e4150 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -29,6 +29,7 @@ using base::TimeTicks; using std::string; using std::vector; using ::testing::_; +using ::testing::AnyNumber; using ::testing::AtLeast; using ::testing::Return; using ::testing::StrictMock; @@ -227,7 +228,7 @@ TEST_F(MetricsDaemonTest, ReportDailyUse) { TEST_F(MetricsDaemonTest, MessageFilter) { // Ignore calls to SendToUMA. - EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AtLeast(0)); + EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber()); DBusMessage* msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL); DBusHandlerResult res = @@ -265,7 +266,6 @@ TEST_F(MetricsDaemonTest, SendSample) { TEST_F(MetricsDaemonTest, ReportDiskStats) { uint64_t read_sectors_now, write_sectors_now; - CreateFakeDiskStatsFile(kFakeDiskStats1.c_str()); daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now); EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]); @@ -399,8 +399,6 @@ TEST_F(MetricsDaemonTest, SendZramMetrics) { int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); - // Some libchrome calls need this. - base::AtExitManager at_exit_manager; return RUN_ALL_TESTS(); } From 8ab89c5bfe31fe3237b0307e50bb301688166f7e Mon Sep 17 00:00:00 2001 From: Steve Fung Date: Mon, 5 Jan 2015 13:48:30 -0800 Subject: [PATCH 179/200] metrics: Fix metrics_daemon Meminfo callback interval With the switch from the glib run loop to base::MessageLoop, the interval for scheduling Meminfo callbacks was incorrectly set to milliseconds. Fix it back to seconds. BUG=chromium:445573 TEST=strace shows metrics_daemon sleep for 30 seconds between collecting statistics Change-Id: I4b4b597273d3cf04b9972011dd0a7386ea14233d Reviewed-on: https://chromium-review.googlesource.com/238466 Reviewed-by: Chih-Chung Chang Reviewed-by: Alex Vakulenko Commit-Queue: Steve Fung Tested-by: Steve Fung --- metrics/metrics_daemon.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index acd96c079..a746e06a0 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -776,7 +776,7 @@ void MetricsDaemon::ScheduleMeminfoCallback(int wait) { base::TimeDelta waitDelta = base::TimeDelta::FromSeconds(wait); base::MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this), - base::TimeDelta::FromMilliseconds(kMetricMeminfoInterval)), + waitDelta), waitDelta); } @@ -795,7 +795,7 @@ void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) { if (reschedule) { base::MessageLoop::current()->PostDelayedTask(FROM_HERE, base::Bind(&MetricsDaemon::MeminfoCallback, base::Unretained(this), - base::TimeDelta::FromMilliseconds(kMetricMeminfoInterval)), + wait), wait); } } From 3d41d4561d726c7ddd1cd952e57bcd01e86b6ad3 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Wed, 7 Jan 2015 12:05:12 -0800 Subject: [PATCH 180/200] libchromeos: Cleaned up HTTP utilities Renamed http utilities by appending ...AndBlock() to make it obvious that they are blocking (synchronous) calls. Async I/O is coming in subsequent CLs. Replaced a bunch of "const char*" with "const std::string&" in function prototypes. Also removed extra overloads for http_utils functions in preparation for adding asynchronous API which would bloat the overloads even more. The original overloads were to provide the ability to omit some less-commonly-used parameters, but that would eventually make the API harder to choose from since the number of overloads will get out of hand. Swept the code to remove unnecessary calls to std::string::c_str() and added omitted parameters in calls to http_utils functions. BUG=None TEST=FEATURES=test emerge-link libchromeos peerd privetd feedback metrics buffet CQ-DEPEND=CL:*191319 Change-Id: I9c1721cd2179ba3b389dd94ef181370eec98ed2e Reviewed-on: https://chromium-review.googlesource.com/239251 Tested-by: Alex Vakulenko Reviewed-by: Vitaly Buka Reviewed-by: Christopher Wiley Commit-Queue: Alex Vakulenko --- metrics/uploader/sender_http.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/metrics/uploader/sender_http.cc b/metrics/uploader/sender_http.cc index ff417b3c4..8e89b6eb5 100644 --- a/metrics/uploader/sender_http.cc +++ b/metrics/uploader/sender_http.cc @@ -21,10 +21,9 @@ bool HttpSender::Send(const std::string& content, chromeos::http::HeaderList headers = {{"X-Chrome-UMA-Log-SHA1", hash}}; chromeos::ErrorPtr error; - auto response = chromeos::http::PostBinary( + auto response = chromeos::http::PostTextAndBlock( server_url_, - content.c_str(), - content.size(), + content, chromeos::mime::application::kWwwFormUrlEncoded, headers, chromeos::http::Transport::CreateDefault(), From a8bcc18bea7fbaa154784df9161e1c6787cfe4a2 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Fri, 19 Dec 2014 09:35:15 -0800 Subject: [PATCH 181/200] metrics: generate app_version from standard lsb-release fields app_version used to be set to CHROMEOS_RELEASE_DESCRIPTION which has no structure and should never be used for parsing. Now that various version numbers have been added to lsb-release, use those fields to generate the version string. The version string format is "A.B.C.D (Official Build) $CHANNEL $BOARD" with: * A = chrome milestone * B = build number * C = branch number * D = patch number * CHANNEL is one of STABLE, DEV, BETA, CANARY * BOARD is the board name BUG=chromium:426889 TEST=trybot run on gizmo-paladin TEST=`test_that -b gizmo gizmo platform_MetricsUploader` TEST=`FEATURES=test emerge-gizmo metrics` Change-Id: I624df14bd859e0c0279dd3de621e651150d30add Reviewed-on: https://chromium-review.googlesource.com/236949 Tested-by: Bertrand Simonnet Reviewed-by: Mike Frysinger Commit-Queue: Bertrand Simonnet Trybot-Ready: Bertrand Simonnet --- metrics/uploader/system_profile_cache.cc | 61 +++++++++++++++++++++++- metrics/uploader/system_profile_cache.h | 9 ++++ metrics/uploader/upload_service_test.cc | 4 +- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index 54c269390..28e0f0923 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -12,6 +12,7 @@ #include "base/guid.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" #include "base/sys_info.h" #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "metrics/persistent_integer.h" @@ -26,6 +27,21 @@ const char kProductIdFieldName[] = "GOOGLE_METRICS_PRODUCT_ID"; } // namespace +std::string ChannelToString( + const metrics::SystemProfileProto_Channel& channel) { + switch (channel) { + case metrics::SystemProfileProto::CHANNEL_STABLE: + return "STABLE"; + case metrics::SystemProfileProto::CHANNEL_DEV: + return "DEV"; + case metrics::SystemProfileProto::CHANNEL_BETA: + return "BETA"; + case metrics::SystemProfileProto::CHANNEL_CANARY: + return "CANARY"; + default: + return "UNKNOWN"; + } +} SystemProfileCache::SystemProfileCache() : initialized_(false), @@ -48,12 +64,17 @@ bool SystemProfileCache::Initialize() { CHECK(!initialized_) << "this should be called only once in the metrics_daemon lifetime."; + std::string chromeos_version; + std::string board; + std::string build_type; if (!base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_NAME", &profile_.os_name) || !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_VERSION", &profile_.os_version) || - !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_DESCRIPTION", - &profile_.app_version) || + !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD", &board) || + !base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE", + &build_type) || + !GetChromeOSVersion(&chromeos_version) || !GetHardwareId(&profile_.hardware_class)) { DLOG(ERROR) << "failing to initialize profile cache"; return false; @@ -63,6 +84,9 @@ bool SystemProfileCache::Initialize() { base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_TRACK", &channel_string); profile_.channel = ProtoChannelFromString(channel_string); + profile_.app_version = chromeos_version + " (" + build_type + ")" + + ChannelToString(profile_.channel) + " " + board; + // If the product id is not defined, use the default one from the protobuf. profile_.product_id = metrics::ChromeUserMetricsExtension::CHROME; if (GetProductId(&profile_.product_id)) { @@ -126,6 +150,39 @@ std::string SystemProfileCache::GetPersistentGUID(const std::string& filename) { return guid; } +bool SystemProfileCache::GetChromeOSVersion(std::string* version) { + if (testing_) { + *version = "0.0.0.0"; + return true; + } + + std::string milestone, build, branch, patch; + unsigned tmp; + if (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_CHROME_MILESTONE", + &milestone) && + base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_NUMBER", + &build) && + base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BRANCH_NUMBER", + &branch) && + base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_PATCH_NUMBER", + &patch)) { + // Convert to uint to ensure those fields are positive numbers. + if (base::StringToUint(milestone, &tmp) && + base::StringToUint(build, &tmp) && + base::StringToUint(branch, &tmp) && + base::StringToUint(patch, &tmp)) { + std::vector parts = {milestone, build, branch, patch}; + *version = JoinString(parts, '.'); + return true; + } + DLOG(INFO) << "The milestone, build, branch or patch is not a positive " + << "number."; + return false; + } + DLOG(INFO) << "Field missing from /etc/lsb-release"; + return false; +} + bool SystemProfileCache::GetHardwareId(std::string* hwid) { CHECK(hwid); diff --git a/metrics/uploader/system_profile_cache.h b/metrics/uploader/system_profile_cache.h index 95f554f8e..bbfc90def 100644 --- a/metrics/uploader/system_profile_cache.h +++ b/metrics/uploader/system_profile_cache.h @@ -72,6 +72,15 @@ class SystemProfileCache : public SystemProfileSetter { // Gets the product ID from the GOOGLE_METRICS_PRODUCT_ID field. bool GetProductId(int* product_id) const; + // Generate the formatted chromeos version from the fields in + // /etc/lsb-release. The format is A.B.C.D where A, B, C and D are positive + // integer representing: + // * the chrome milestone + // * the build number + // * the branch number + // * the patch number + bool GetChromeOSVersion(std::string* version); + bool initialized_; bool testing_; std::string config_root_; diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index ff96f85ac..363c8c18a 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -189,7 +189,9 @@ TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) { "CHROMEOS_RELEASE_NAME=" + name + "\nCHROMEOS_RELEASE_VERSION=version\n" "CHROMEOS_RELEASE_DESCRIPTION=description beta-channel test\n" - "CHROMEOS_RELEASE_TRACK=beta-channel"); + "CHROMEOS_RELEASE_TRACK=beta-channel\n" + "CHROMEOS_RELEASE_BUILD_TYPE=developer build\n" + "CHROMEOS_RELEASE_BOARD=myboard"); base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time()); scoped_ptr histogram = From ae4bdc4c92507cdaa21d8af9cebd1831a4b2688a Mon Sep 17 00:00:00 2001 From: Steve Fung Date: Mon, 26 Jan 2015 17:13:24 -0800 Subject: [PATCH 182/200] metrics: Fix upload_service to work with base::MessageLoop With an earlier change, metrics_daemon was switched from the glib message loop to base::MessageLoop. UploadService still is trying to interact with the glib message loop, it needs to be switched to using base::MessageLoop. BUG=chromium:452228 TEST=`FEATURES=test emerge-panther metrics` Change-Id: I38eb52ca1995d75cfb7d0e69a434e649493e7c6f Reviewed-on: https://chromium-review.googlesource.com/243429 Reviewed-by: Nathan Bullock Reviewed-by: Bertrand Simonnet Tested-by: Stephen Fung Commit-Queue: Stephen Fung Trybot-Ready: Stephen Fung --- metrics/metrics.gyp | 4 --- metrics/uploader/system_profile_cache.cc | 1 - metrics/uploader/upload_service.cc | 36 ++++++++++++++++++------ metrics/uploader/upload_service.h | 12 ++++++-- metrics/uploader/upload_service_test.cc | 2 +- 5 files changed, 37 insertions(+), 18 deletions(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 60e2d1a95..31305097f 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -3,10 +3,6 @@ 'variables': { 'deps': [ 'dbus-1', - 'dbus-glib-1', - 'glib-2.0', - 'gobject-2.0', - 'gthread-2.0', 'libchrome-<(libbase_ver)', 'libchromeos-<(libbase_ver)', ] diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index 28e0f0923..d16b1dd58 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -4,7 +4,6 @@ #include "metrics/uploader/system_profile_cache.h" -#include #include #include diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index cdec91e41..dd49d1f67 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -4,11 +4,12 @@ #include "metrics/uploader/upload_service.h" -#include #include +#include #include #include +#include #include #include #include @@ -28,14 +29,29 @@ UploadService::UploadService(SystemProfileSetter* setter, const std::string& server) : system_profile_setter_(setter), histogram_snapshot_manager_(this), - sender_(new HttpSender(server)) { + sender_(new HttpSender(server)), + testing_(false) { +} + +UploadService::UploadService(SystemProfileSetter* setter, + const std::string& server, + bool testing) + : UploadService(setter, server) { + testing_ = testing; } void UploadService::Init(const base::TimeDelta& upload_interval, const std::string& metrics_file) { base::StatisticsRecorder::Initialize(); metrics_file_ = metrics_file; - g_timeout_add_seconds(upload_interval.InSeconds(), &UploadEventStatic, this); + + if (!testing_) { + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&UploadService::UploadEventCallback, + base::Unretained(this), + upload_interval), + upload_interval); + } } void UploadService::StartNewLog() { @@ -46,12 +62,14 @@ void UploadService::StartNewLog() { current_log_.reset(log); } -// static -int UploadService::UploadEventStatic(void* uploader) { - CHECK(uploader); - // This is called by glib with a pointer to an UploadEvent object. - static_cast(uploader)->UploadEvent(); - return 1; +void UploadService::UploadEventCallback(const base::TimeDelta& interval) { + UploadEvent(); + + base::MessageLoop::current()->PostDelayedTask(FROM_HERE, + base::Bind(&UploadService::UploadEventCallback, + base::Unretained(this), + interval), + interval); } void UploadService::UploadEvent() { diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index 3611794e3..0b087de12 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -64,9 +64,8 @@ class UploadService : public base::HistogramFlattener { // launch as it is destroyed when staging the log. void StartNewLog(); - // Glib takes a function pointer and passes the object as a void*. - // Uploader is expected to be an UploaderService. - static int UploadEventStatic(void* uploader); + // Event callback for handling MessageLoop events. + void UploadEventCallback(const base::TimeDelta& interval); // Triggers an upload event. void UploadEvent(); @@ -98,6 +97,11 @@ class UploadService : public base::HistogramFlattener { FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored); FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent); + // Private constructor for use in unit testing. + UploadService(SystemProfileSetter* setter, + const std::string& server, + bool testing); + // If a staged log fails to upload more than kMaxFailedUpload times, it // will be discarded. static const int kMaxFailedUpload; @@ -137,6 +141,8 @@ class UploadService : public base::HistogramFlattener { scoped_ptr staged_log_; std::string metrics_file_; + + bool testing_; }; #endif // METRICS_UPLOADER_UPLOAD_SERVICE_H_ diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index 363c8c18a..ca90e85ea 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -26,7 +26,7 @@ class UploadServiceTest : public testing::Test { protected: UploadServiceTest() : cache_(true, "/"), - upload_service_(new MockSystemProfileSetter(), kMetricsServer), + upload_service_(new MockSystemProfileSetter(), kMetricsServer, true), exit_manager_(new base::AtExitManager()) { sender_ = new SenderMock; upload_service_.sender_.reset(sender_); From 5a6ac9c97c1bf1d1f8e9cc6e84ace3d158294e1f Mon Sep 17 00:00:00 2001 From: Yunlian Jiang Date: Wed, 28 Jan 2015 13:12:38 -0800 Subject: [PATCH 183/200] metrics: fix -Winconsistent-missing-override warning BUG=chromium:453069 TEST=FEATURES="test" emerge-amd64-generic metrics with new clang. Change-Id: I1576200c94a01af46dd2f36e87806b8c93de7b86 Reviewed-on: https://chromium-review.googlesource.com/244052 Reviewed-by: Dan Erat Commit-Queue: Yunlian Jiang Tested-by: Yunlian Jiang --- metrics/metrics_library.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 38ab47fdb..24db112d5 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -59,7 +59,7 @@ class MetricsLibrary : public MetricsLibraryInterface { // recommended to keep this number low (e.g., 50 is normal, while // 100 is high). bool SendToUMA(const std::string& name, int sample, - int min, int max, int nbuckets); + int min, int max, int nbuckets) override; // Sends linear histogram data to Chrome for transport to UMA and // returns true on success. This method results in the equivalent of From 7e238f3bcb76fe5f311b89881a7bae594963f22e Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Mon, 9 Feb 2015 12:51:24 -0800 Subject: [PATCH 184/200] platform2: Switch over to using base64 functions from libchromeos Replaced existing implementations of Base64Encode/Base64Decode with the functions from libchromeos, which were added as part of an earlier change (see CL:247690). BUG=None TEST=`FEATURES=test emerge-link cryptohome debugd metrics privetd update_engine` Change-Id: I8cec677ce2c2fd3b97ca2228d35c2cf5cd133f4c Reviewed-on: https://chromium-review.googlesource.com/247792 Reviewed-by: Vitaly Buka Tested-by: Alex Vakulenko Commit-Queue: Alex Vakulenko --- metrics/uploader/metrics_log_base_unittest.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/metrics/uploader/metrics_log_base_unittest.cc b/metrics/uploader/metrics_log_base_unittest.cc index fe02fea06..01a027341 100644 --- a/metrics/uploader/metrics_log_base_unittest.cc +++ b/metrics/uploader/metrics_log_base_unittest.cc @@ -6,7 +6,6 @@ #include -#include #include #include #include From 067ec8ba78287d2992331423d74d978cfa8669ff Mon Sep 17 00:00:00 2001 From: Ben Chan Date: Tue, 17 Feb 2015 13:54:04 -0800 Subject: [PATCH 185/200] metrics: Disable uploader on non-official build. The metrics uploader should only be enabled on an official build. BUG=chromium:459105 TEST=`FEATURES=test emerge-$BOARD metrics` TEST=Verified that metrics uploader is disabled on a developer build. Change-Id: I5cadb3afeb56c0adac971228aa48ad56ed913bbf Reviewed-on: https://chromium-review.googlesource.com/250542 Reviewed-by: Bertrand Simonnet Commit-Queue: Ben Chan Tested-by: Ben Chan --- metrics/metrics_daemon.cc | 22 +++++++++++++++++++--- metrics/metrics_daemon.h | 3 +++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index a746e06a0..4a69d8b4c 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -48,6 +48,10 @@ const char kCrashReporterUserCrashSignal[] = "UserCrash"; const char kCrashReporterMatchRule[] = "type='signal',interface='%s',path='/',member='%s'"; +// Build type of an official build. +// See src/third_party/chromiumos-overlay/chromeos/scripts/cros_set_lsb_release. +const char kOfficialBuild[] = "Official Build"; + const int kSecondsPerMinute = 60; const int kMinutesPerHour = 60; const int kHoursPerDay = 24; @@ -209,6 +213,13 @@ uint32_t MetricsDaemon::GetOsVersionHash() { return cached_version_hash; } +bool MetricsDaemon::IsOnOfficialBuild() const { + std::string build_type; + return (base::SysInfo::GetLsbReleaseValue("CHROMEOS_RELEASE_BUILD_TYPE", + &build_type) && + build_type == kOfficialBuild); +} + void MetricsDaemon::Init(bool testing, bool uploader_active, MetricsLibraryInterface* metrics_lib, @@ -327,9 +338,14 @@ int MetricsDaemon::OnInit() { base::TimeDelta::FromMilliseconds(kUpdateStatsIntervalMs)); if (uploader_active_) { - LOG(INFO) << "uploader enabled"; - upload_service_.reset(new UploadService(new SystemProfileCache(), server_)); - upload_service_->Init(upload_interval_, metrics_file_); + if (IsOnOfficialBuild()) { + LOG(INFO) << "uploader enabled"; + upload_service_.reset( + new UploadService(new SystemProfileCache(), server_)); + upload_service_->Init(upload_interval_, metrics_file_); + } else { + LOG(INFO) << "uploader disabled on non-official build"; + } } return EX_OK; diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index 397fd2109..d38dcd92d 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -273,6 +273,9 @@ class MetricsDaemon : public chromeos::DBusDaemon { // to a unsigned 32-bit int. uint32_t GetOsVersionHash(); + // Returns true if the system is using an official build. + bool IsOnOfficialBuild() const; + // Updates stats, additionally sending them to UMA if enough time has elapsed // since the last report. void UpdateStats(base::TimeTicks now_ticks, base::Time now_wall_time); From e4fa61e05e17d759dae5a724d67c34dafa3d706a Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Wed, 18 Feb 2015 09:38:55 -0800 Subject: [PATCH 186/200] metrics: don't upload metrics when metrics are disabled The uploader should only send metrics samples when the metrics are enabled. The uploader daemon is still started when the metrics are disabled so that: * When we enable the metrics, we don't require a restart of metrics_daemon to start uploading metrics. * The metrics file is truncated periodically and avoid taking too much space on long running system with metrics disabled. BUG=chromium:459636 TEST=unittests TEST=`test_that -b gizmo gizmo platform_MetricsUploader` works TEST=manual: uploader does not upload metrics if metrics are disabled. CQ-DEPEND=CL:250980 Change-Id: I9f5da3457066a183c5791b5488e985b7ab13b6e1 Reviewed-on: https://chromium-review.googlesource.com/250822 Trybot-Ready: Bertrand Simonnet Tested-by: Bertrand Simonnet Reviewed-by: Nathan Bullock Commit-Queue: Bertrand Simonnet --- metrics/metrics_daemon.cc | 3 ++- metrics/metrics_library.h | 3 ++- metrics/metrics_library_mock.h | 4 ++++ metrics/uploader/upload_service.cc | 12 +++++++++++- metrics/uploader/upload_service.h | 5 +++++ metrics/uploader/upload_service_test.cc | 8 ++++++-- 6 files changed, 30 insertions(+), 5 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 4a69d8b4c..4ea899f51 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -191,6 +191,7 @@ int MetricsDaemon::Run() { void MetricsDaemon::RunUploaderTest() { upload_service_.reset(new UploadService(new SystemProfileCache(true, config_root_), + metrics_lib_, server_)); upload_service_->Init(upload_interval_, metrics_file_); upload_service_->UploadEvent(); @@ -341,7 +342,7 @@ int MetricsDaemon::OnInit() { if (IsOnOfficialBuild()) { LOG(INFO) << "uploader enabled"; upload_service_.reset( - new UploadService(new SystemProfileCache(), server_)); + new UploadService(new SystemProfileCache(), metrics_lib_, server_)); upload_service_->Init(upload_interval_, metrics_file_); } else { LOG(INFO) << "uploader disabled on non-official build"; diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h index 24db112d5..a90f3e61a 100644 --- a/metrics/metrics_library.h +++ b/metrics/metrics_library.h @@ -19,6 +19,7 @@ class MetricsLibraryInterface { public: virtual void Init() = 0; + virtual bool AreMetricsEnabled() = 0; virtual bool SendToUMA(const std::string& name, int sample, int min, int max, int nbuckets) = 0; virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0; @@ -40,7 +41,7 @@ class MetricsLibrary : public MetricsLibraryInterface { bool IsGuestMode(); // Returns whether or not metrics collection is enabled. - bool AreMetricsEnabled(); + bool AreMetricsEnabled() override; // Sends histogram data to Chrome for transport to UMA and returns // true on success. This method results in the equivalent of an diff --git a/metrics/metrics_library_mock.h b/metrics/metrics_library_mock.h index 0f1047f2e..99892bfa0 100644 --- a/metrics/metrics_library_mock.h +++ b/metrics/metrics_library_mock.h @@ -13,6 +13,8 @@ class MetricsLibraryMock : public MetricsLibraryInterface { public: + bool metrics_enabled_ = true; + MOCK_METHOD0(Init, void()); MOCK_METHOD5(SendToUMA, bool(const std::string& name, int sample, int min, int max, int nbuckets)); @@ -20,6 +22,8 @@ class MetricsLibraryMock : public MetricsLibraryInterface { int max)); MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample)); MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action)); + + bool AreMetricsEnabled() override {return metrics_enabled_;}; }; #endif // METRICS_METRICS_LIBRARY_MOCK_H_ diff --git a/metrics/uploader/upload_service.cc b/metrics/uploader/upload_service.cc index dd49d1f67..92c9e1061 100644 --- a/metrics/uploader/upload_service.cc +++ b/metrics/uploader/upload_service.cc @@ -26,17 +26,20 @@ const int UploadService::kMaxFailedUpload = 10; UploadService::UploadService(SystemProfileSetter* setter, + MetricsLibraryInterface* metrics_lib, const std::string& server) : system_profile_setter_(setter), + metrics_lib_(metrics_lib), histogram_snapshot_manager_(this), sender_(new HttpSender(server)), testing_(false) { } UploadService::UploadService(SystemProfileSetter* setter, + MetricsLibraryInterface* metrics_lib, const std::string& server, bool testing) - : UploadService(setter, server) { + : UploadService(setter, metrics_lib, server) { testing_ = testing; } @@ -94,6 +97,13 @@ void UploadService::UploadEvent() { void UploadService::SendStagedLog() { CHECK(staged_log_) << "staged_log_ must exist to be sent"; + // If metrics are not enabled, discard the log and exit. + if (!metrics_lib_->AreMetricsEnabled()) { + LOG(INFO) << "Metrics disabled. Don't upload metrics samples."; + staged_log_.reset(); + return; + } + std::string log_text; staged_log_->GetEncodedLog(&log_text); if (!sender_->Send(log_text, base::SHA1HashString(log_text))) { diff --git a/metrics/uploader/upload_service.h b/metrics/uploader/upload_service.h index 0b087de12..ebbb54f2a 100644 --- a/metrics/uploader/upload_service.h +++ b/metrics/uploader/upload_service.h @@ -10,6 +10,8 @@ #include "base/metrics/histogram_base.h" #include "base/metrics/histogram_flattener.h" #include "base/metrics/histogram_snapshot_manager.h" + +#include "metrics/metrics_library.h" #include "metrics/uploader/metrics_log.h" #include "metrics/uploader/sender.h" #include "metrics/uploader/system_profile_cache.h" @@ -55,6 +57,7 @@ class SystemProfileSetter; class UploadService : public base::HistogramFlattener { public: explicit UploadService(SystemProfileSetter* setter, + MetricsLibraryInterface* metrics_lib, const std::string& server); void Init(const base::TimeDelta& upload_interval, @@ -99,6 +102,7 @@ class UploadService : public base::HistogramFlattener { // Private constructor for use in unit testing. UploadService(SystemProfileSetter* setter, + MetricsLibraryInterface* metrics_lib, const std::string& server, bool testing); @@ -134,6 +138,7 @@ class UploadService : public base::HistogramFlattener { MetricsLog* GetOrCreateCurrentLog(); scoped_ptr system_profile_setter_; + MetricsLibraryInterface* metrics_lib_; base::HistogramSnapshotManager histogram_snapshot_manager_; scoped_ptr sender_; int failed_upload_count_; diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index ca90e85ea..d04cc72c4 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -12,6 +12,7 @@ #include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "components/metrics/proto/histogram_event.pb.h" #include "components/metrics/proto/system_profile.pb.h" +#include "metrics/metrics_library_mock.h" #include "metrics/serialization/metric_sample.h" #include "metrics/uploader/metrics_log.h" #include "metrics/uploader/mock/mock_system_profile_setter.h" @@ -26,7 +27,8 @@ class UploadServiceTest : public testing::Test { protected: UploadServiceTest() : cache_(true, "/"), - upload_service_(new MockSystemProfileSetter(), kMetricsServer, true), + upload_service_(new MockSystemProfileSetter(), &metrics_lib_, + kMetricsServer, true), exit_manager_(new base::AtExitManager()) { sender_ = new SenderMock; upload_service_.sender_.reset(sender_); @@ -52,6 +54,7 @@ class UploadServiceTest : public testing::Test { SenderMock* sender_; SystemProfileCache cache_; UploadService upload_service_; + MetricsLibraryMock metrics_lib_; scoped_ptr exit_manager_; }; @@ -126,7 +129,8 @@ TEST_F(UploadServiceTest, EmptyLogsAreNotSent) { } TEST_F(UploadServiceTest, LogEmptyByDefault) { - UploadService upload_service(new MockSystemProfileSetter(), kMetricsServer); + UploadService upload_service(new MockSystemProfileSetter(), &metrics_lib_, + kMetricsServer); // current_log_ should be initialized later as it needs AtExitManager to exit // in order to gather system information from SysInfo. From 5672f8b2e41a673e874b753d6492d5db841ae594 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 6 Mar 2015 14:43:58 -0800 Subject: [PATCH 187/200] metrics: move sample file from /var/run to /var/lib Some metrics are generated near shutdown and we expect to collect them at reboot, but /var/run is a tmpfs and /var/run/uma-events is lost. This changes the uma events file location from the perspective of the metrics daemon. I am making the same change to Chrome. I am adding a symlink to allow an older Chrome to read the samples from the old location. A newer Chrome will be screwed but I hope that's OK (i.e. this will work for official builds and for Chrome OS developers, but may cause malfunction for Chrome developers) BUG=chromium:447256 TEST=verified that /var/lib/metrics/uma-events grows and is truncated CQ-DEPEND=CL:258971 Change-Id: I677cda16486de239dd205247083d4ad7240d992b Reviewed-on: https://chromium-review.googlesource.com/257084 Reviewed-by: Luigi Semenzato Tested-by: Luigi Semenzato Commit-Queue: Luigi Semenzato --- metrics/init/metrics_daemon.conf | 5 ----- metrics/init/metrics_library.conf | 12 +++++++++--- metrics/metrics_daemon_main.cc | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/metrics/init/metrics_daemon.conf b/metrics/init/metrics_daemon.conf index 3fc2dd7b4..e6932cf99 100644 --- a/metrics/init/metrics_daemon.conf +++ b/metrics/init/metrics_daemon.conf @@ -11,11 +11,6 @@ start on starting system-services stop on stopping system-services respawn -pre-start script - # Make directory, but don't die on error. Let someone else complain. - mkdir -p /var/lib/metrics -end script - # metrics will update the next line to add -uploader for embedded builds. env DAEMON_FLAGS="" diff --git a/metrics/init/metrics_library.conf b/metrics/init/metrics_library.conf index 6aa49edd0..8202cc14e 100644 --- a/metrics/init/metrics_library.conf +++ b/metrics/init/metrics_library.conf @@ -11,9 +11,15 @@ start on starting boot-services pre-start script # Create the file used as communication endpoint for metrics. - RUNDIR=/var/run/metrics - EVENTS_FILE=${RUNDIR}/uma-events - mkdir -p ${RUNDIR} + METRICS_DIR=/var/lib/metrics + EVENTS_FILE=${METRICS_DIR}/uma-events + mkdir -p ${METRICS_DIR} touch ${EVENTS_FILE} + chown chronos.chronos ${EVENT_FILE} chmod 666 ${EVENTS_FILE} + # TRANSITION ONLY. + # TODO(semenzato) Remove after Chrome change, see issue 447256. + # Let Chrome read the metrics file from the old location. + mkdir -p /var/run/metrics + ln -s ${EVENTS_FILE} /var/run/metrics end script diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index cc0812d66..a6d040cee 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -63,7 +63,7 @@ int main(int argc, char** argv) { "https://clients4.google.com/uma/v2", "Server to upload the metrics to. (needs -uploader)"); DEFINE_string(metrics_file, - "/var/run/metrics/uma-events", + "/var/lib/metrics/uma-events", "File to use as a proxy for uploading the metrics"); DEFINE_string(config_root, "/", "Root of the configuration files (testing only)"); From 3b88766a3d6d1b9aff63b6ef972fd8034f53ff11 Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Mon, 16 Mar 2015 16:54:55 +0100 Subject: [PATCH 188/200] metrics: tell what file we can't open on error Use PDLOG and also log the filename. BUG=chromium:467586 TEST=rebuild image and checked the log Change-Id: I72f9838477adad71e79cc25f1f3a40bb9781b848 Reviewed-on: https://chromium-review.googlesource.com/260261 Tested-by: Stefan Sauer Reviewed-by: Bertrand Simonnet Commit-Queue: Stefan Sauer --- metrics/serialization/serialization_utils.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/serialization/serialization_utils.cc b/metrics/serialization/serialization_utils.cc index 80e81d2c1..abb73ab51 100644 --- a/metrics/serialization/serialization_utils.cc +++ b/metrics/serialization/serialization_utils.cc @@ -174,7 +174,7 @@ bool SerializationUtils::WriteMetricToFile(const MetricSample& sample, READ_WRITE_ALL_FILE_FLAGS)); if (file_descriptor.get() < 0) { - DLOG(ERROR) << "error opening the file"; + DPLOG(ERROR) << filename << ": cannot open"; return false; } @@ -183,7 +183,7 @@ bool SerializationUtils::WriteMetricToFile(const MetricSample& sample, // underneath us. Keep the file locked as briefly as possible. // Freeing file_descriptor will close the file and and remove the lock. if (HANDLE_EINTR(flock(file_descriptor.get(), LOCK_EX)) < 0) { - DLOG(ERROR) << "error locking" << filename << " : " << errno; + DPLOG(ERROR) << filename << ": cannot lock"; return false; } From 93408425eabe6489c262b7e1b00738d1aa5a6c05 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Fri, 20 Mar 2015 08:35:30 -0700 Subject: [PATCH 189/200] metrics: make metrics library also use /var/lib/metrics/uma-events I missed this when I changed the location used by the metrics daemon. BUG=chromium:447256 TEST=none BRANCH=none Change-Id: Ie964abb70911bcdc79993b62a066ea77f65acfa5 Reviewed-on: https://chromium-review.googlesource.com/261463 Tested-by: Luigi Semenzato Reviewed-by: Dan Erat Commit-Queue: Luigi Semenzato --- metrics/metrics_daemon_test.cc | 2 +- metrics/metrics_library.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index abf8e4150..a282bff2a 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -47,7 +47,7 @@ static const char kFakeVmStatsName[] = "fake-vm-stats"; static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq"; static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq"; static const char kMetricsServer[] = "https://clients4.google.com/uma/v2"; -static const char kMetricsFilePath[] = "/var/run/metrics/uma-events"; +static const char kMetricsFilePath[] = "/var/lib/metrics/uma-events"; class MetricsDaemonTest : public testing::Test { protected: diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index 5088caee3..b64152844 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -19,7 +19,7 @@ #include "policy/device_policy.h" static const char kAutotestPath[] = "/var/log/metrics/autotest-events"; -static const char kUMAEventsPath[] = "/var/run/metrics/uma-events"; +static const char kUMAEventsPath[] = "/var/lib/metrics/uma-events"; static const char kConsentFile[] = "/home/chronos/Consent To Send Stats"; static const char kCrosEventHistogramName[] = "Platform.CrOSEvent"; static const int kCrosEventHistogramMax = 100; From 732fe2108249820a921aabf35678c0f14cdd4019 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Fri, 27 Mar 2015 17:07:20 -0400 Subject: [PATCH 190/200] delete __STDC_{FORMAT,LIMIT}_MACROS With newer glibc versions (2.18+), these macros no longer exist (and the functionality they protected are always enabled). Delete them. BUG=chromium:401360 TEST=precq passes Change-Id: I21b0607a874b9f9e39dff050054a2928a6c55023 Reviewed-on: https://chromium-review.googlesource.com/262903 Trybot-Ready: Mike Frysinger Reviewed-by: Alex Vakulenko Commit-Queue: Mike Frysinger Tested-by: Mike Frysinger --- metrics/metrics_daemon.cc | 4 ---- metrics/metrics_daemon_test.cc | 2 -- 2 files changed, 6 deletions(-) diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 4ea899f51..9568592d5 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -2,10 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// For PRIu64 in inttypes.h, used by scanf. TODO(semenzato): replace -// with libchromeos methods. -#define __STDC_FORMAT_MACROS - #include "metrics/metrics_daemon.h" #include diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index a282bff2a..305501371 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#define __STDC_FORMAT_MACROS - #include #include From 538e2090cdac67acfa725a87ac94a75042bbdd61 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 19 Mar 2015 17:18:24 -0700 Subject: [PATCH 191/200] metrics: add TPM.EarlyResetDuringCommand CrOS event. This is reported if the TPM was reset during a command in the first 30 seconds of the previous boot session. BUG=chromium:431360 TEST=none BRANCH=none Change-Id: I66fb453094e5281301cb39d9b86ba5f9c82c10f6 Reviewed-on: https://chromium-review.googlesource.com/261357 Tested-by: Luigi Semenzato Reviewed-by: Darren Krahn Commit-Queue: Luigi Semenzato --- metrics/metrics_library.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc index b64152844..70c8eac2b 100644 --- a/metrics/metrics_library.cc +++ b/metrics/metrics_library.cc @@ -42,6 +42,7 @@ static const char *kCrosEventNames[] = { "SpringPowerSupply.Original.Low", // 9 "SpringPowerSupply.ChargerIdle", // 10 "TPM.NonZeroDictionaryAttackCounter", // 11 + "TPM.EarlyResetDuringCommand", // 12 }; time_t MetricsLibrary::cached_enabled_time_ = 0; From e97b741806e523d0aca2a3bc68e32a584e314b19 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Tue, 7 Apr 2015 08:03:18 -0700 Subject: [PATCH 192/200] platform2: Switch over to libchrome r323904 Using revision 323904 of libchrome by default. Fixed a few minor compiler error due to a change in the new libchrome's headers. BUG=chromium:472654 TEST=./build_packages CQ-DEPEND=CL:264350 Change-Id: I0d57f34403f60c3d60d9159b60b498bf7ff166f3 Reviewed-on: https://chromium-review.googlesource.com/264311 Tested-by: Alex Vakulenko Reviewed-by: Mike Frysinger Trybot-Ready: Alex Vakulenko Commit-Queue: Alex Vakulenko --- metrics/{libmetrics-307740.gyp => libmetrics-323904.gyp} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename metrics/{libmetrics-307740.gyp => libmetrics-323904.gyp} (72%) diff --git a/metrics/libmetrics-307740.gyp b/metrics/libmetrics-323904.gyp similarity index 72% rename from metrics/libmetrics-307740.gyp rename to metrics/libmetrics-323904.gyp index 42dd0dae8..fc5bd3584 100644 --- a/metrics/libmetrics-307740.gyp +++ b/metrics/libmetrics-323904.gyp @@ -1,6 +1,6 @@ { 'variables': { - 'libbase_ver': 307740, + 'libbase_ver': 323904, }, 'includes': [ 'libmetrics.gypi', From 6db436a03ba5b6b5bc8aced43b23121e7a63324d Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Tue, 7 Apr 2015 17:23:27 -0700 Subject: [PATCH 193/200] metrics: Fix the import path of protobufs. We are switching chromeos-base/metrics to use the metrics component pulled as part of the checkout. This components will be copied in $S and not in platform2/metrics. The import path need to be fixed. BUG=chromium:474811 TEST=trybots. TEST=unittests. CQ-DEPEND=CL:264544 Change-Id: I02a379eff0f97081dffd382bbee5028b3600f975 Reviewed-on: https://chromium-review.googlesource.com/264506 Tested-by: Bertrand Simonnet Reviewed-by: Mike Frysinger Commit-Queue: Bertrand Simonnet --- metrics/metrics.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 31305097f..7134e556e 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -83,7 +83,7 @@ 'target_name': 'metrics_proto', 'type': 'static_library', 'variables': { - 'proto_in_dir': 'components/metrics/proto/', + 'proto_in_dir': '../../components/metrics/proto/', 'proto_out_dir': 'include/components/metrics/proto', }, 'sources': [ From eba8a014cf779d5f57846b7d4a7edb794d0d97ca Mon Sep 17 00:00:00 2001 From: Bertrand Simonnet Date: Wed, 8 Apr 2015 20:49:01 +0000 Subject: [PATCH 194/200] Revert "metrics: Fix the import path of protobufs." This reverts commit 54297164b4258cd8098903a38fb3837c462e2db1. Dependent CL reverted. Change-Id: I35b4ede8b2f67d9dade60869cfbddea81ebeb556 Reviewed-on: https://chromium-review.googlesource.com/264784 Tested-by: Bertrand Simonnet Reviewed-by: Bertrand Simonnet --- metrics/metrics.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 7134e556e..31305097f 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -83,7 +83,7 @@ 'target_name': 'metrics_proto', 'type': 'static_library', 'variables': { - 'proto_in_dir': '../../components/metrics/proto/', + 'proto_in_dir': 'components/metrics/proto/', 'proto_out_dir': 'include/components/metrics/proto', }, 'sources': [ From ba08992d1e8fce01ebc37518efecfea92d8f44cf Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Thu, 9 Apr 2015 15:17:17 -0700 Subject: [PATCH 195/200] libchromeos: Switch http Response to use streams Make the response data of an HTTP request to use stream interface. BUG=None TEST=`FEATURES=test emerge-link libchromeos` Change-Id: I4d0e427e90f154a83ffd75973b4c211a989c1993 Reviewed-on: https://chromium-review.googlesource.com/265068 Trybot-Ready: Alex Vakulenko Tested-by: Alex Vakulenko Reviewed-by: Christopher Wiley Commit-Queue: Alex Vakulenko --- metrics/uploader/sender_http.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metrics/uploader/sender_http.cc b/metrics/uploader/sender_http.cc index 8e89b6eb5..8488b6666 100644 --- a/metrics/uploader/sender_http.cc +++ b/metrics/uploader/sender_http.cc @@ -28,7 +28,7 @@ bool HttpSender::Send(const std::string& content, headers, chromeos::http::Transport::CreateDefault(), &error); - if (!response || response->GetDataAsString() != "OK") { + if (!response || response->ExtractDataAsString() != "OK") { if (error) { DLOG(ERROR) << "Failed to send data: " << error->GetMessage(); } From ef31bec13e2e57ee7163b0a68b7a88cae0ee7956 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Tue, 9 Jun 2015 10:09:44 -0700 Subject: [PATCH 196/200] metrics: Add a check for abnormally small messages to prevent crashes In some situations the |message_size| read from |fd| comes up as 0. In this case we try to read a negative size for the message body and this leads for crashes. Add a check to make sure that message_size is at least 4 bytes long to account for the required 32-bit integer message size field. BUG=chrome-os-partner:40711 TEST=`FEATURES=test emerge-link metrics` Change-Id: Ie9adbc8e0e6a9f2c80450bf7ebcb3e05ad1f1f8e Reviewed-on: https://chromium-review.googlesource.com/276362 Trybot-Ready: Alex Vakulenko Tested-by: Alex Vakulenko Reviewed-by: Bertrand Simonnet Commit-Queue: Alex Vakulenko --- metrics/serialization/serialization_utils.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/metrics/serialization/serialization_utils.cc b/metrics/serialization/serialization_utils.cc index abb73ab51..80d586e4c 100644 --- a/metrics/serialization/serialization_utils.cc +++ b/metrics/serialization/serialization_utils.cc @@ -36,7 +36,8 @@ bool ReadMessage(int fd, std::string* message) { CHECK(message); int result; - int32 message_size; + int32_t message_size; + const int32_t message_hdr_size = sizeof(message_size); // The file containing the metrics do not leave the device so the writer and // the reader will always have the same endianness. result = HANDLE_EINTR(read(fd, &message_size, sizeof(message_size))); @@ -48,7 +49,7 @@ bool ReadMessage(int fd, std::string* message) { // This indicates a normal EOF. return false; } - if (result < static_cast(sizeof(message_size))) { + if (result < message_hdr_size) { DLOG(ERROR) << "bad read size " << result << ", expecting " << sizeof(message_size); return false; @@ -68,7 +69,12 @@ bool ReadMessage(int fd, std::string* message) { return true; } - message_size -= sizeof(message_size); // The message size includes itself. + if (message_size < message_hdr_size) { + DLOG(ERROR) << "message too short : " << message_size; + return false; + } + + message_size -= message_hdr_size; // The message size includes itself. char buffer[SerializationUtils::kMessageMaxLength]; if (!base::ReadFromFD(fd, buffer, message_size)) { DPLOG(ERROR) << "reading metrics message body"; From 323d70d143c9d7ed2e14ae96fe892f795ff5c09b Mon Sep 17 00:00:00 2001 From: Wei-Ning Huang Date: Tue, 26 May 2015 15:26:23 +0800 Subject: [PATCH 197/200] init: fix typo and prevent startup error metrics_library upstart job failes on boot because of a typo and soft link command linking on existing file. Fixing the typo and add '-f' flag when linking to supress error. BUG=chrome-os-partner:40606 TEST=`start metrics_library` Change-Id: I3c144f4a636a61bb460a9de74771f61ad1e6b0f9 Reviewed-on: https://chromium-review.googlesource.com/273172 Reviewed-by: Daniel Kurtz Reviewed-by: Luigi Semenzato Commit-Queue: Wei-Ning Huang Tested-by: Wei-Ning Huang --- metrics/init/metrics_library.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metrics/init/metrics_library.conf b/metrics/init/metrics_library.conf index 8202cc14e..03016d129 100644 --- a/metrics/init/metrics_library.conf +++ b/metrics/init/metrics_library.conf @@ -15,11 +15,11 @@ pre-start script EVENTS_FILE=${METRICS_DIR}/uma-events mkdir -p ${METRICS_DIR} touch ${EVENTS_FILE} - chown chronos.chronos ${EVENT_FILE} + chown chronos.chronos ${EVENTS_FILE} chmod 666 ${EVENTS_FILE} # TRANSITION ONLY. # TODO(semenzato) Remove after Chrome change, see issue 447256. # Let Chrome read the metrics file from the old location. mkdir -p /var/run/metrics - ln -s ${EVENTS_FILE} /var/run/metrics + ln -sf ${EVENTS_FILE} /var/run/metrics end script From 1eea1618cd663b32964c507ce1ed5959082dce72 Mon Sep 17 00:00:00 2001 From: Alex Vakulenko Date: Mon, 15 Jun 2015 12:53:22 -0700 Subject: [PATCH 198/200] platform2: Fix issues with new version of libchrome libchrome r334380 has the following breaking changes that need to be fixed: - base::JSONWriter::Write() and base::JSONWriter::WriteWithOptions() take "const base::Value&" instead of "const base::Value*" - base::JSONReader::Read() and base::JSONReader::ReadAndReturnError() return a scoped_ptr instead of base::Value* - base/safe_strerror_posix.h is moved to base/posix/safe_strerror.h - safe_strerror() is now in "base" namespace - StartsWithASCII(), EndsWith(), StringToUpperASCII(), LowerCaseEqualsASCII() are now in "base" namespace - ObserverList is now in "base" namespace - base::PrintTo(base::FilePath) used in gtest is now moved to libchrome-test library and as such, unit test runners need to link to this library now. - crypto::RSAPrivateKey::CreateSensitive() is now removed from //crypto, so some of tests in chromeos-login that used that function had to be changed to use crypto::GenerateRSAKeyPairNSS() directly. - UnixDomanSocket class is now in "base" namespace - Pickle class is now in "base" namespace BUG=chromium:496469 TEST=`./build_packages` CQ-DEPEND=CL:277662 Change-Id: I36e5fbf2e36a92068873ffbd44020c862a3ed9e3 Reviewed-on: https://chromium-review.googlesource.com/277671 Reviewed-by: Alex Vakulenko Commit-Queue: Alex Vakulenko Trybot-Ready: Alex Vakulenko Tested-by: Alex Vakulenko --- .../{libmetrics-323904.gyp => libmetrics-334380.gyp} | 2 +- metrics/metrics_daemon_main.cc | 2 +- metrics/serialization/serialization_utils.cc | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) rename metrics/{libmetrics-323904.gyp => libmetrics-334380.gyp} (72%) diff --git a/metrics/libmetrics-323904.gyp b/metrics/libmetrics-334380.gyp similarity index 72% rename from metrics/libmetrics-323904.gyp rename to metrics/libmetrics-334380.gyp index fc5bd3584..9771821fb 100644 --- a/metrics/libmetrics-323904.gyp +++ b/metrics/libmetrics-334380.gyp @@ -1,6 +1,6 @@ { 'variables': { - 'libbase_ver': 323904, + 'libbase_ver': 334380, }, 'includes': [ 'libmetrics.gypi', diff --git a/metrics/metrics_daemon_main.cc b/metrics/metrics_daemon_main.cc index a6d040cee..1f64ef341 100644 --- a/metrics/metrics_daemon_main.cc +++ b/metrics/metrics_daemon_main.cc @@ -33,7 +33,7 @@ const std::string MetricsMainDiskStatsPath() { } dev_path = dev_path_cstr; // Check that rootdev begins with "/dev/". - if (!StartsWithASCII(dev_path, dev_prefix, false)) { + if (!base::StartsWithASCII(dev_path, dev_prefix, false)) { LOG(WARNING) << "unexpected root device " << dev_path; return ""; } diff --git a/metrics/serialization/serialization_utils.cc b/metrics/serialization/serialization_utils.cc index 80d586e4c..9aa076a4b 100644 --- a/metrics/serialization/serialization_utils.cc +++ b/metrics/serialization/serialization_utils.cc @@ -103,15 +103,15 @@ scoped_ptr SerializationUtils::ParseSample( const std::string& name = parts[0]; const std::string& value = parts[1]; - if (LowerCaseEqualsASCII(name, "crash")) { + if (base::LowerCaseEqualsASCII(name, "crash")) { return MetricSample::CrashSample(value); - } else if (LowerCaseEqualsASCII(name, "histogram")) { + } else if (base::LowerCaseEqualsASCII(name, "histogram")) { return MetricSample::ParseHistogram(value); - } else if (LowerCaseEqualsASCII(name, "linearhistogram")) { + } else if (base::LowerCaseEqualsASCII(name, "linearhistogram")) { return MetricSample::ParseLinearHistogram(value); - } else if (LowerCaseEqualsASCII(name, "sparsehistogram")) { + } else if (base::LowerCaseEqualsASCII(name, "sparsehistogram")) { return MetricSample::ParseSparseHistogram(value); - } else if (LowerCaseEqualsASCII(name, "useraction")) { + } else if (base::LowerCaseEqualsASCII(name, "useraction")) { return MetricSample::UserActionSample(value); } else { DLOG(ERROR) << "invalid event type: " << name << ", value: " << value; From dc865898859a96c362c313285ce5aa6cc04229b8 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Thu, 9 Jul 2015 08:28:08 -0700 Subject: [PATCH 199/200] metrics: replace "Logging.*" with "Platform.*" "Logging.*" is not a good name. Also remove unused function ReportDailyUse(). BUG=chromium:508535 TEST=it compiles Change-Id: I070bada4857abd80989ecc746adcbf1dcf6239a3 Reviewed-on: https://chromium-review.googlesource.com/284610 Tested-by: Luigi Semenzato Reviewed-by: Sonny Rao Reviewed-by: Alexei Svitkine Commit-Queue: Luigi Semenzato --- metrics/README | 2 +- metrics/metrics_daemon.cc | 42 ++++++++++++---------------------- metrics/metrics_daemon.h | 4 ---- metrics/metrics_daemon_test.cc | 12 ---------- 4 files changed, 16 insertions(+), 44 deletions(-) diff --git a/metrics/README b/metrics/README index 6150dc7be..4b92af35f 100644 --- a/metrics/README +++ b/metrics/README @@ -52,7 +52,7 @@ Histogram Naming Convention Use TrackerArea.MetricName. For example: -Logging.CrashCounter +Platform.DailyUseTime Network.TimeToDrop diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc index 9568592d5..880e90ccf 100644 --- a/metrics/metrics_daemon.cc +++ b/metrics/metrics_daemon.cc @@ -243,37 +243,37 @@ void MetricsDaemon::Init(bool testing, ticks_per_second_ = sysconf(_SC_CLK_TCK); daily_active_use_.reset( - new PersistentInteger("Logging.DailyUseTime")); + new PersistentInteger("Platform.DailyUseTime")); version_cumulative_active_use_.reset( - new PersistentInteger("Logging.CumulativeDailyUseTime")); + new PersistentInteger("Platform.CumulativeDailyUseTime")); version_cumulative_cpu_use_.reset( - new PersistentInteger("Logging.CumulativeCpuTime")); + new PersistentInteger("Platform.CumulativeCpuTime")); kernel_crash_interval_.reset( - new PersistentInteger("Logging.KernelCrashInterval")); + new PersistentInteger("Platform.KernelCrashInterval")); unclean_shutdown_interval_.reset( - new PersistentInteger("Logging.UncleanShutdownInterval")); + new PersistentInteger("Platform.UncleanShutdownInterval")); user_crash_interval_.reset( - new PersistentInteger("Logging.UserCrashInterval")); + new PersistentInteger("Platform.UserCrashInterval")); any_crashes_daily_count_.reset( - new PersistentInteger("Logging.AnyCrashesDaily")); + new PersistentInteger("Platform.AnyCrashesDaily")); any_crashes_weekly_count_.reset( - new PersistentInteger("Logging.AnyCrashesWeekly")); + new PersistentInteger("Platform.AnyCrashesWeekly")); user_crashes_daily_count_.reset( - new PersistentInteger("Logging.UserCrashesDaily")); + new PersistentInteger("Platform.UserCrashesDaily")); user_crashes_weekly_count_.reset( - new PersistentInteger("Logging.UserCrashesWeekly")); + new PersistentInteger("Platform.UserCrashesWeekly")); kernel_crashes_daily_count_.reset( - new PersistentInteger("Logging.KernelCrashesDaily")); + new PersistentInteger("Platform.KernelCrashesDaily")); kernel_crashes_weekly_count_.reset( - new PersistentInteger("Logging.KernelCrashesWeekly")); + new PersistentInteger("Platform.KernelCrashesWeekly")); kernel_crashes_version_count_.reset( - new PersistentInteger("Logging.KernelCrashesSinceUpdate")); + new PersistentInteger("Platform.KernelCrashesSinceUpdate")); unclean_shutdowns_daily_count_.reset( - new PersistentInteger("Logging.UncleanShutdownsDaily")); + new PersistentInteger("Platform.UncleanShutdownsDaily")); unclean_shutdowns_weekly_count_.reset( - new PersistentInteger("Logging.UncleanShutdownsWeekly")); + new PersistentInteger("Platform.UncleanShutdownsWeekly")); daily_cycle_.reset(new PersistentInteger("daily.cycle")); weekly_cycle_.reset(new PersistentInteger("weekly.cycle")); @@ -1039,18 +1039,6 @@ bool MetricsDaemon::ProcessMemuse(const string& meminfo_raw) { return true; } -void MetricsDaemon::ReportDailyUse(int use_seconds) { - if (use_seconds <= 0) - return; - - int minutes = (use_seconds + kSecondsPerMinute / 2) / kSecondsPerMinute; - SendSample("Logging.DailyUseTime", - minutes, - 1, - kMinutesPerDay * 30 * 2, // cumulative---two months worth - 50); -} - void MetricsDaemon::SendSample(const string& name, int sample, int min, int max, int nbuckets) { metrics_lib_->SendToUMA(name, sample, min, max, nbuckets); diff --git a/metrics/metrics_daemon.h b/metrics/metrics_daemon.h index d38dcd92d..b1b2d11c1 100644 --- a/metrics/metrics_daemon.h +++ b/metrics/metrics_daemon.h @@ -75,7 +75,6 @@ class MetricsDaemon : public chromeos::DBusDaemon { FRIEND_TEST(MetricsDaemonTest, ProcessUserCrash); FRIEND_TEST(MetricsDaemonTest, ReportCrashesDailyFrequency); FRIEND_TEST(MetricsDaemonTest, ReadFreqToInt); - FRIEND_TEST(MetricsDaemonTest, ReportDailyUse); FRIEND_TEST(MetricsDaemonTest, ReportDiskStats); FRIEND_TEST(MetricsDaemonTest, ReportKernelCrashInterval); FRIEND_TEST(MetricsDaemonTest, ReportUncleanShutdownInterval); @@ -179,9 +178,6 @@ class MetricsDaemon : public chromeos::DBusDaemon { // exists, so it must not be called more than once. bool CheckSystemCrash(const std::string& crash_file); - // Report daily use through UMA. - void ReportDailyUse(int use_seconds); - // Sends a regular (exponential) histogram sample to Chrome for // transport to UMA. See MetricsLibrary::SendToUMA in // metrics_library.h for a description of the arguments. diff --git a/metrics/metrics_daemon_test.cc b/metrics/metrics_daemon_test.cc index 305501371..7dafbd689 100644 --- a/metrics/metrics_daemon_test.cc +++ b/metrics/metrics_daemon_test.cc @@ -212,18 +212,6 @@ TEST_F(MetricsDaemonTest, CheckSystemCrash) { base::DeleteFile(crash_detected, false); } -TEST_F(MetricsDaemonTest, ReportDailyUse) { - ExpectSample("Logging.DailyUseTime", 2); - daemon_.ReportDailyUse(90); - - ExpectSample("Logging.DailyUseTime", 1); - daemon_.ReportDailyUse(89); - - // There should be no metrics generated for the calls below. - daemon_.ReportDailyUse(0); - daemon_.ReportDailyUse(-5); -} - TEST_F(MetricsDaemonTest, MessageFilter) { // Ignore calls to SendToUMA. EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber()); From c95440e9b62b82591d4b03513958ab293fe187e6 Mon Sep 17 00:00:00 2001 From: Bertrand SIMONNET Date: Thu, 16 Jul 2015 17:16:51 -0700 Subject: [PATCH 200/200] metrics: Import protobufs from Chromium. Instead of pulling the protobufs from chromium, copy them into platform2/metrics/proto. This enables us to add Chromium OS specific changes without polluting Chromium's protobuf. BUG=chrome-os-partner:42861 TEST=`FEATURES=test emerge-gizmo metrics` succeeds. CQ-DEPEND=CL:286895 Change-Id: Ib919a21da6518199c29adb70c8bc27220e0dc031 Reviewed-on: https://chromium-review.googlesource.com/286943 Reviewed-by: Gaurav Shah Commit-Queue: Bertrand Simonnet Trybot-Ready: Bertrand Simonnet Tested-by: Bertrand Simonnet --- metrics/metrics.gyp | 9 +- metrics/uploader/metrics_log.cc | 2 +- metrics/uploader/metrics_log_base.cc | 6 +- metrics/uploader/metrics_log_base.h | 2 +- metrics/uploader/metrics_log_base_unittest.cc | 2 +- metrics/uploader/mock/sender_mock.h | 2 +- metrics/uploader/proto/README | 25 + .../proto/chrome_user_metrics_extension.proto | 60 ++ metrics/uploader/proto/histogram_event.proto | 47 ++ metrics/uploader/proto/system_profile.proto | 747 ++++++++++++++++++ .../uploader/proto/user_action_event.proto | 23 + metrics/uploader/system_profile_cache.cc | 2 +- metrics/uploader/system_profile_cache.h | 2 +- metrics/uploader/upload_service_test.cc | 6 +- 14 files changed, 916 insertions(+), 19 deletions(-) create mode 100644 metrics/uploader/proto/README create mode 100644 metrics/uploader/proto/chrome_user_metrics_extension.proto create mode 100644 metrics/uploader/proto/histogram_event.proto create mode 100644 metrics/uploader/proto/system_profile.proto create mode 100644 metrics/uploader/proto/user_action_event.proto diff --git a/metrics/metrics.gyp b/metrics/metrics.gyp index 31305097f..276ec7876 100644 --- a/metrics/metrics.gyp +++ b/metrics/metrics.gyp @@ -83,17 +83,12 @@ 'target_name': 'metrics_proto', 'type': 'static_library', 'variables': { - 'proto_in_dir': 'components/metrics/proto/', - 'proto_out_dir': 'include/components/metrics/proto', + 'proto_in_dir': 'uploader/proto', + 'proto_out_dir': 'include/metrics/uploader/proto', }, 'sources': [ '<(proto_in_dir)/chrome_user_metrics_extension.proto', '<(proto_in_dir)/histogram_event.proto', - '<(proto_in_dir)/omnibox_event.proto', - '<(proto_in_dir)/omnibox_input_type.proto', - '<(proto_in_dir)/perf_data.proto', - '<(proto_in_dir)/profiler_event.proto', - '<(proto_in_dir)/sampled_profile.proto', '<(proto_in_dir)/system_profile.proto', '<(proto_in_dir)/user_action_event.proto', ], diff --git a/metrics/uploader/metrics_log.cc b/metrics/uploader/metrics_log.cc index c3aa69e18..4d493b8aa 100644 --- a/metrics/uploader/metrics_log.cc +++ b/metrics/uploader/metrics_log.cc @@ -6,7 +6,7 @@ #include -#include "components/metrics/proto/system_profile.pb.h" +#include "metrics/uploader/proto/system_profile.pb.h" #include "uploader/system_profile_setter.h" // We use default values for the MetricsLogBase constructor as the setter will diff --git a/metrics/uploader/metrics_log_base.cc b/metrics/uploader/metrics_log_base.cc index 43cf82e29..7fe1ae1a6 100644 --- a/metrics/uploader/metrics_log_base.cc +++ b/metrics/uploader/metrics_log_base.cc @@ -6,10 +6,10 @@ #include "base/metrics/histogram_base.h" #include "base/metrics/histogram_samples.h" -#include "components/metrics/proto/histogram_event.pb.h" -#include "components/metrics/proto/system_profile.pb.h" -#include "components/metrics/proto/user_action_event.pb.h" #include "metrics/uploader/metrics_hashes.h" +#include "metrics/uploader/proto/histogram_event.pb.h" +#include "metrics/uploader/proto/system_profile.pb.h" +#include "metrics/uploader/proto/user_action_event.pb.h" using base::Histogram; using base::HistogramBase; diff --git a/metrics/uploader/metrics_log_base.h b/metrics/uploader/metrics_log_base.h index 5a54c30af..e871c0fac 100644 --- a/metrics/uploader/metrics_log_base.h +++ b/metrics/uploader/metrics_log_base.h @@ -13,7 +13,7 @@ #include "base/macros.h" #include "base/metrics/histogram.h" #include "base/time/time.h" -#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" +#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h" namespace base { class HistogramSamples; diff --git a/metrics/uploader/metrics_log_base_unittest.cc b/metrics/uploader/metrics_log_base_unittest.cc index 01a027341..5da428adc 100644 --- a/metrics/uploader/metrics_log_base_unittest.cc +++ b/metrics/uploader/metrics_log_base_unittest.cc @@ -10,7 +10,7 @@ #include #include -#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" +#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h" namespace metrics { diff --git a/metrics/uploader/mock/sender_mock.h b/metrics/uploader/mock/sender_mock.h index 4845f9dc5..159b64527 100644 --- a/metrics/uploader/mock/sender_mock.h +++ b/metrics/uploader/mock/sender_mock.h @@ -8,7 +8,7 @@ #include #include "base/compiler_specific.h" -#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" +#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h" #include "uploader/sender.h" class SenderMock : public Sender { diff --git a/metrics/uploader/proto/README b/metrics/uploader/proto/README new file mode 100644 index 000000000..9bd3249b0 --- /dev/null +++ b/metrics/uploader/proto/README @@ -0,0 +1,25 @@ +Copyright 2015 The Chromium OS Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. + + +This directory contains the protocol buffers used by the standalone metrics +uploader. Those protobuffers are copied from the chromium protobuffers from +https://chromium.googlesource.com/chromium/src/+/master/components/metrics/proto/ +at 3bfe5f2b4c03d2cac718d137ed14cd2c6354bfed. + +Any change to this protobuf must first be made to the backend's protobuf and be +compatible with the chromium protobuffers. + + +Q: Why fork the chromium protobuffers ? +A: The standalone metrics uploader needs chromium os fields that are not defined +by the chromium protobufs. Instead of pushing chromium os specific changes to +chromium, we can add them only to chromium os (and to the backend of course). + + +Q: What's the difference between those protobuffers and chromium's protobuffers? +A: When the protobuffers were copied, some chromium specific protobuffers were +not imported: +* omnibox related protobuffers. +* performance profiling protobuffers (not used in chromium os). diff --git a/metrics/uploader/proto/chrome_user_metrics_extension.proto b/metrics/uploader/proto/chrome_user_metrics_extension.proto new file mode 100644 index 000000000..f712fc9f7 --- /dev/null +++ b/metrics/uploader/proto/chrome_user_metrics_extension.proto @@ -0,0 +1,60 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Protocol buffer for Chrome UMA (User Metrics Analysis). +// +// Note: this protobuf must be compatible with the one in chromium. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; +option java_outer_classname = "ChromeUserMetricsExtensionProtos"; +option java_package = "org.chromium.components.metrics"; + +package metrics; + +import "histogram_event.proto"; +import "system_profile.proto"; +import "user_action_event.proto"; + +// Next tag: 13 +message ChromeUserMetricsExtension { + // The product (i.e. end user application) for a given UMA log. + enum Product { + // Google Chrome product family. + CHROME = 0; + } + // The product corresponding to this log. The field type is int32 instead of + // Product so that downstream users of the Chromium metrics component can + // introduce products without needing to make changes to the Chromium code + // (though they still need to add the new product to the server-side enum). + // Note: The default value is Chrome, so Chrome products will not transmit + // this field. + optional int32 product = 10 [default = 0]; + + // The id of the client install that generated these events. + // + // For Chrome clients, this id is unique to a top-level (one level above the + // "Default" directory) Chrome user data directory [1], and so is shared among + // all Chrome user profiles contained in this user data directory. + // An id of 0 is reserved for test data (monitoring and internal testing) and + // should normally be ignored in analysis of the data. + // [1] http://www.chromium.org/user-experience/user-data-directory + optional fixed64 client_id = 1; + + // The session id for this user. + // Values such as tab ids are only meaningful within a particular session. + // The client keeps track of the session id and sends it with each event. + // The session id is simply an integer that is incremented each time the user + // relaunches Chrome. + optional int32 session_id = 2; + + // Information about the user's browser and system configuration. + optional SystemProfileProto system_profile = 3; + + // This message will log one or more of the following event types: + repeated UserActionEventProto user_action_event = 4; + repeated HistogramEventProto histogram_event = 6; + +} diff --git a/metrics/uploader/proto/histogram_event.proto b/metrics/uploader/proto/histogram_event.proto new file mode 100644 index 000000000..4b68094ff --- /dev/null +++ b/metrics/uploader/proto/histogram_event.proto @@ -0,0 +1,47 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Histogram-collected metrics. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; +option java_outer_classname = "HistogramEventProtos"; +option java_package = "org.chromium.components.metrics"; + +package metrics; + +// Next tag: 4 +message HistogramEventProto { + // The name of the histogram, hashed. + optional fixed64 name_hash = 1; + + // The sum of all the sample values. + // Together with the total count of the sample values, this allows us to + // compute the average value. The count of all sample values is just the sum + // of the counts of all the buckets. + optional int64 sum = 2; + + // The per-bucket data. + message Bucket { + // Each bucket's range is bounded by min <= x < max. + // It is valid to omit one of these two fields in a bucket, but not both. + // If the min field is omitted, its value is assumed to be equal to max - 1. + // If the max field is omitted, its value is assumed to be equal to the next + // bucket's min value (possibly computed per above). The last bucket in a + // histogram should always include the max field. + optional int64 min = 1; + optional int64 max = 2; + + // The bucket's index in the list of buckets, sorted in ascending order. + // This field was intended to provide extra redundancy to detect corrupted + // records, but was never used. As of M31, it is no longer sent by Chrome + // clients to reduce the UMA upload size. + optional int32 bucket_index = 3 [deprecated = true]; + + // The number of entries in this bucket. + optional int64 count = 4; + } + repeated Bucket bucket = 3; +} diff --git a/metrics/uploader/proto/system_profile.proto b/metrics/uploader/proto/system_profile.proto new file mode 100644 index 000000000..d33ff6097 --- /dev/null +++ b/metrics/uploader/proto/system_profile.proto @@ -0,0 +1,747 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Stores information about the user's brower and system configuration. +// The system configuration fields are recorded once per client session. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; +option java_outer_classname = "SystemProfileProtos"; +option java_package = "org.chromium.components.metrics"; + +package metrics; + +// Next tag: 21 +message SystemProfileProto { + // The time when the client was compiled/linked, in seconds since the epoch. + optional int64 build_timestamp = 1; + + // A version number string for the application. + // Most commonly this is the browser version number found in a user agent + // string, and is typically a 4-tuple of numbers separated by periods. In + // cases where the user agent version might be ambiguous (example: Linux 64- + // bit build, rather than 32-bit build, or a Windows version used in some + // special context, such as ChromeFrame running in IE), then this may include + // some additional postfix to provide clarification not available in the UA + // string. + // + // An example of a browser version 4-tuple is "5.0.322.0". Currently used + // postfixes are: + // + // "-64": a 64-bit build + // "-F": Chrome is running under control of ChromeFrame + // "-devel": this is not an official build of Chrome + // + // A full version number string could look similar to: + // "5.0.322.0-F-devel". + // + // This value, when available, is more trustworthy than the UA string + // associated with the request; and including the postfix, may be more + // specific. + optional string app_version = 2; + + // The brand code or distribution tag assigned to a partner, if available. + // Brand codes are only available on Windows. Not every Windows install + // though will have a brand code. + optional string brand_code = 12; + + // The possible channels for an installation, from least to most stable. + enum Channel { + CHANNEL_UNKNOWN = 0; // Unknown channel -- perhaps an unofficial build? + CHANNEL_CANARY = 1; + CHANNEL_DEV = 2; + CHANNEL_BETA = 3; + CHANNEL_STABLE = 4; + } + optional Channel channel = 10; + + // True if Chrome build is ASan-instrumented. + optional bool is_asan_build = 20 [default = false]; + + // The date the user enabled UMA, in seconds since the epoch. + // If the user has toggled the UMA enabled state multiple times, this will + // be the most recent date on which UMA was enabled. + // For privacy, this is rounded to the nearest hour. + optional int64 uma_enabled_date = 3; + + // The time when the client was installed, in seconds since the epoch. + // For privacy, this is rounded to the nearest hour. + optional int64 install_date = 16; + + // The user's selected application locale, i.e. the user interface language. + // The locale includes a language code and, possibly, also a country code, + // e.g. "en-US". + optional string application_locale = 4; + + message BrilloDeviceData { + optional string build_target_id = 1; + } + optional BrilloDeviceData brillo = 21; + + // Information on the user's operating system. + message OS { + // The user's operating system. This should be one of: + // - Android + // - Windows NT + // - Linux (includes ChromeOS) + // - iPhone OS + // - Mac OS X + optional string name = 1; + + // The version of the OS. The meaning of this field is OS-dependent. + optional string version = 2; + + // The fingerprint of the build. This field is used only on Android. + optional string fingerprint = 3; + + // Whether the version of iOS appears to be "jailbroken". This field is + // used only on iOS. Chrome for iOS detects whether device contains a + // DynamicLibraries/ directory. It's a necessary but insufficient indicator + // of whether the operating system has been jailbroken. + optional bool is_jailbroken = 4; + } + optional OS os = 5; + + // Next tag for Hardware: 18 + // Information on the user's hardware. + message Hardware { + // The CPU architecture (x86, PowerPC, x86_64, ...) + optional string cpu_architecture = 1; + + // The amount of RAM present on the system, in megabytes. + optional int64 system_ram_mb = 2; + + // The base memory address that chrome.dll was loaded at. + // (Logged only on Windows.) + optional int64 dll_base = 3; + + // The Chrome OS device hardware class ID is a unique string associated with + // each Chrome OS device product revision generally assigned at hardware + // qualification time. The hardware class effectively identifies the + // configured system components such as CPU, WiFi adapter, etc. + // + // An example of such a hardware class is "IEC MARIO PONY 6101". An + // internal database associates this hardware class with the qualified + // device specifications including OEM information, schematics, hardware + // qualification reports, test device tags, etc. + optional string hardware_class = 4; + + // The number of physical screens. + optional int32 screen_count = 5; + + // The screen dimensions of the primary screen, in pixels. + optional int32 primary_screen_width = 6; + optional int32 primary_screen_height = 7; + + // The device scale factor of the primary screen. + optional float primary_screen_scale_factor = 12; + + // Max DPI for any attached screen. (Windows only) + optional float max_dpi_x = 9; + optional float max_dpi_y = 10; + + // Information on the CPU obtained by CPUID. + message CPU { + // A 12 character string naming the vendor, e.g. "GeniuneIntel". + optional string vendor_name = 1; + + // The signature reported by CPUID (from EAX). + optional uint32 signature = 2; + + // Number of logical processors/cores on the current machine. + optional uint32 num_cores = 3; + } + optional CPU cpu = 13; + + // Information on the GPU + message Graphics { + // The GPU manufacturer's vendor id. + optional uint32 vendor_id = 1; + + // The GPU manufacturer's device id for the chip set. + optional uint32 device_id = 2; + + // The driver version on the GPU. + optional string driver_version = 3; + + // The driver date on the GPU. + optional string driver_date = 4; + + // The GL_VENDOR string. An example of a gl_vendor string is + // "Imagination Technologies". "" if we are not using OpenGL. + optional string gl_vendor = 6; + + // The GL_RENDERER string. An example of a gl_renderer string is + // "PowerVR SGX 540". "" if we are not using OpenGL. + optional string gl_renderer = 7; + } + optional Graphics gpu = 8; + + // Information about Bluetooth devices paired with the system. + message Bluetooth { + // Whether Bluetooth is present on this system. + optional bool is_present = 1; + + // Whether Bluetooth is enabled on this system. + optional bool is_enabled = 2; + + // Describes a paired device. + message PairedDevice { + // Assigned class of the device. This is a bitfield according to the + // Bluetooth specification available at the following URL: + // https://www.bluetooth.org/en-us/specification/assigned-numbers-overview/baseband + optional uint32 bluetooth_class = 1; + + // Decoded device type. + enum Type { + DEVICE_UNKNOWN = 0; + DEVICE_COMPUTER = 1; + DEVICE_PHONE = 2; + DEVICE_MODEM = 3; + DEVICE_AUDIO = 4; + DEVICE_CAR_AUDIO = 5; + DEVICE_VIDEO = 6; + DEVICE_PERIPHERAL = 7; + DEVICE_JOYSTICK = 8; + DEVICE_GAMEPAD = 9; + DEVICE_KEYBOARD = 10; + DEVICE_MOUSE = 11; + DEVICE_TABLET = 12; + DEVICE_KEYBOARD_MOUSE_COMBO = 13; + } + optional Type type = 2; + + // Vendor prefix of the Bluetooth address, these are OUI registered by + // the IEEE and are encoded with the first byte in bits 16-23, the + // second byte in bits 8-15 and the third byte in bits 0-7. + // + // ie. Google's OUI (00:1A:11) is encoded as 0x00001A11 + optional uint32 vendor_prefix = 4; + + // The Vendor ID of a device, returned in vendor_id below, can be + // either allocated by the Bluetooth SIG or USB IF, providing two + // completely overlapping namespaces for identifiers. + // + // This field should be read along with vendor_id to correctly + // identify the vendor. For example Google is identified by either + // vendor_id_source = VENDOR_ID_BLUETOOTH, vendor_id = 0x00E0 or + // vendor_id_source = VENDOR_ID_USB, vendor_id = 0x18D1. + // + // If the device does not support the Device ID specification the + // unknown value will be set. + enum VendorIDSource { + VENDOR_ID_UNKNOWN = 0; + VENDOR_ID_BLUETOOTH = 1; + VENDOR_ID_USB = 2; + } + optional VendorIDSource vendor_id_source = 8; + + // Vendor ID of the device, where available. + optional uint32 vendor_id = 5; + + // Product ID of the device, where available. + optional uint32 product_id = 6; + + // Device ID of the device, generally the release or version number in + // BCD format, where available. + optional uint32 device_id = 7; + } + repeated PairedDevice paired_device = 3; + } + optional Bluetooth bluetooth = 11; + + // Whether the internal display produces touch events. Omitted if unknown. + // Logged on ChromeOS only. + optional bool internal_display_supports_touch = 14; + + // Vendor ids and product ids of external touchscreens. + message TouchScreen { + // Touch screen vendor id. + optional uint32 vendor_id = 1; + // Touch screen product id. + optional uint32 product_id = 2; + } + // Lists vendor and product ids of external touchscreens. + // Logged on ChromeOS only. + repeated TouchScreen external_touchscreen = 15; + + // Drive messages are currently logged on Windows 7+, iOS, and Android. + message Drive { + // Whether this drive incurs a time penalty when randomly accessed. This + // should be true for spinning disks but false for SSDs or other + // flash-based drives. + optional bool has_seek_penalty = 1; + } + // The drive that the application executable was loaded from. + optional Drive app_drive = 16; + // The drive that the current user data directory was loaded from. + optional Drive user_data_drive = 17; + } + optional Hardware hardware = 6; + + // Information about the network connection. + message Network { + // Set to true if connection_type changed during the lifetime of the log. + optional bool connection_type_is_ambiguous = 1; + + // See net::NetworkChangeNotifier::ConnectionType. + enum ConnectionType { + CONNECTION_UNKNOWN = 0; + CONNECTION_ETHERNET = 1; + CONNECTION_WIFI = 2; + CONNECTION_2G = 3; + CONNECTION_3G = 4; + CONNECTION_4G = 5; + CONNECTION_BLUETOOTH = 6; + } + // The connection type according to NetworkChangeNotifier. + optional ConnectionType connection_type = 2; + + // Set to true if wifi_phy_layer_protocol changed during the lifetime of the log. + optional bool wifi_phy_layer_protocol_is_ambiguous = 3; + + // See net::WifiPHYLayerProtocol. + enum WifiPHYLayerProtocol { + WIFI_PHY_LAYER_PROTOCOL_NONE = 0; + WIFI_PHY_LAYER_PROTOCOL_ANCIENT = 1; + WIFI_PHY_LAYER_PROTOCOL_A = 2; + WIFI_PHY_LAYER_PROTOCOL_B = 3; + WIFI_PHY_LAYER_PROTOCOL_G = 4; + WIFI_PHY_LAYER_PROTOCOL_N = 5; + WIFI_PHY_LAYER_PROTOCOL_UNKNOWN = 6; + } + // The physical layer mode of the associated wifi access point, if any. + optional WifiPHYLayerProtocol wifi_phy_layer_protocol = 4; + + // Describe wifi access point information. + message WifiAccessPoint { + // Vendor prefix of the access point's BSSID, these are OUIs + // (Organizationally Unique Identifiers) registered by + // the IEEE and are encoded with the first byte in bits 16-23, the + // second byte in bits 8-15 and the third byte in bits 0-7. + optional uint32 vendor_prefix = 1; + + // Access point seurity mode definitions. + enum SecurityMode { + SECURITY_UNKNOWN = 0; + SECURITY_WPA = 1; + SECURITY_WEP = 2; + SECURITY_RSN = 3; + SECURITY_802_1X = 4; + SECURITY_PSK = 5; + SECURITY_NONE = 6; + } + // The security mode of the access point. + optional SecurityMode security_mode = 2; + + // Vendor specific information. + message VendorInformation { + // The model number, for example "0". + optional string model_number = 1; + + // The model name (sometimes the same as the model_number), + // for example "WZR-HP-AG300H". + optional string model_name = 2; + + // The device name (sometimes the same as the model_number), + // for example "Dummynet" + optional string device_name = 3; + + // The list of vendor-specific OUIs (Organziationally Unqiue + // Identifiers). These are provided by the vendor through WPS + // (Wireless Provisioning Service) information elements, which + // identifies the content of the element. + repeated uint32 element_identifier = 4; + } + // The wireless access point vendor information. + optional VendorInformation vendor_info = 3; + } + // Information of the wireless AP that device is connected to. + optional WifiAccessPoint access_point_info = 5; + } + optional Network network = 13; + + // Information on the Google Update install that is managing this client. + message GoogleUpdate { + // Whether the Google Update install is system-level or user-level. + optional bool is_system_install = 1; + + // The date at which Google Update last started performing an automatic + // update check, in seconds since the Unix epoch. + optional int64 last_automatic_start_timestamp = 2; + + // The date at which Google Update last successfully sent an update check + // and recieved an intact response from the server, in seconds since the + // Unix epoch. (The updates don't need to be successfully installed.) + optional int64 last_update_check_timestamp = 3; + + // Describes a product being managed by Google Update. (This can also + // describe Google Update itself.) + message ProductInfo { + // The current version of the product that is installed. + optional string version = 1; + + // The date at which Google Update successfully updated this product, + // stored in seconds since the Unix epoch. This is updated when an update + // is successfully applied, or if the server reports that no update + // is available. + optional int64 last_update_success_timestamp = 2; + + // The result reported by the product updater on its last run. + enum InstallResult { + INSTALL_RESULT_SUCCESS = 0; + INSTALL_RESULT_FAILED_CUSTOM_ERROR = 1; + INSTALL_RESULT_FAILED_MSI_ERROR = 2; + INSTALL_RESULT_FAILED_SYSTEM_ERROR = 3; + INSTALL_RESULT_EXIT_CODE = 4; + } + optional InstallResult last_result = 3; + + // The error code reported by the product updater on its last run. This + // will typically be a error code specific to the product installer. + optional int32 last_error = 4; + + // The extra error code reported by the product updater on its last run. + // This will typically be a Win32 error code. + optional int32 last_extra_error = 5; + } + optional ProductInfo google_update_status = 4; + optional ProductInfo client_status = 5; + } + optional GoogleUpdate google_update = 11; + + // Information on all installed plugins. + message Plugin { + // The plugin's self-reported name and filename (without path). + optional string name = 1; + optional string filename = 2; + + // The plugin's version. + optional string version = 3; + + // True if the plugin is disabled. + // If a client has multiple local Chrome user accounts, this is logged based + // on the first user account launched during the current session. + optional bool is_disabled = 4; + + // True if the plugin is PPAPI. + optional bool is_pepper = 5; + } + repeated Plugin plugin = 7; + + // Figures that can be used to generate application stability metrics. + // All values are counts of events since the last time that these + // values were reported. + // Next tag: 24 + message Stability { + // Total amount of time that the program was running, in seconds, + // since the last time a log was recorded, as measured using a client-side + // clock implemented via TimeTicks, which guarantees that it is monotonic + // and does not jump if the user changes his/her clock. The TimeTicks + // implementation also makes the clock not count time the computer is + // suspended. + optional int64 incremental_uptime_sec = 1; + + // Total amount of time that the program was running, in seconds, + // since startup, as measured using a client-side clock implemented + // via TimeTicks, which guarantees that it is monotonic and does not + // jump if the user changes his/her clock. The TimeTicks implementation + // also makes the clock not count time the computer is suspended. + // This field was added for M-35. + optional int64 uptime_sec = 23; + + // Page loads along with renderer crashes and hangs, since page load count + // roughly corresponds to usage. + optional int32 page_load_count = 2; + optional int32 renderer_crash_count = 3; + optional int32 renderer_hang_count = 4; + + // Number of renderer crashes that were for extensions. These crashes are + // not counted in renderer_crash_count. + optional int32 extension_renderer_crash_count = 5; + + // Number of non-renderer child process crashes. + optional int32 child_process_crash_count = 6; + + // Number of times the browser has crashed while logged in as the "other + // user" (guest) account. + // Logged on ChromeOS only. + optional int32 other_user_crash_count = 7; + + // Number of times the kernel has crashed. + // Logged on ChromeOS only. + optional int32 kernel_crash_count = 8; + + // Number of times the system has shut down uncleanly. + // Logged on ChromeOS only. + optional int32 unclean_system_shutdown_count = 9; + + // + // All the remaining fields in the Stability are recorded at most once per + // client session. + // + + // The number of times the program was launched. + // This will typically be equal to 1. However, it is possible that Chrome + // was unable to upload stability metrics for previous launches (e.g. due to + // crashing early during startup), and hence this value might be greater + // than 1. + optional int32 launch_count = 15; + // The number of times that it didn't exit cleanly (which we assume to be + // mostly crashes). + optional int32 crash_count = 16; + + // The number of times the program began, but did not complete, the shutdown + // process. (For example, this may occur when Windows is shutting down, and + // it only gives the process a few seconds to clean up.) + optional int32 incomplete_shutdown_count = 17; + + // The number of times the program was able register with breakpad crash + // services. + optional int32 breakpad_registration_success_count = 18; + + // The number of times the program failed to register with breakpad crash + // services. If crash registration fails then when the program crashes no + // crash report will be generated. + optional int32 breakpad_registration_failure_count = 19; + + // The number of times the program has run under a debugger. This should + // be an exceptional condition. Running under a debugger prevents crash + // dumps from being generated. + optional int32 debugger_present_count = 20; + + // The number of times the program has run without a debugger attached. + // This should be most common scenario and should be very close to + // |launch_count|. + optional int32 debugger_not_present_count = 21; + + // Stability information for all installed plugins. + message PluginStability { + // The relevant plugin's information (name, etc.) + optional Plugin plugin = 1; + + // The number of times this plugin's process was launched. + optional int32 launch_count = 2; + + // The number of times this plugin was instantiated on a web page. + // This will be >= |launch_count|. + // (A page load with multiple sections drawn by this plugin will + // increase this count multiple times.) + optional int32 instance_count = 3; + + // The number of times this plugin process crashed. + // This value will be <= |launch_count|. + optional int32 crash_count = 4; + + // The number of times this plugin could not be loaded. + optional int32 loading_error_count = 5; + } + repeated PluginStability plugin_stability = 22; + } + optional Stability stability = 8; + + // Description of a field trial or experiment that the user is currently + // enrolled in. + // All metrics reported in this upload can potentially be influenced by the + // field trial. + message FieldTrial { + // The name of the field trial, as a 32-bit identifier. + // Currently, the identifier is a hash of the field trial's name. + optional fixed32 name_id = 1; + + // The user's group within the field trial, as a 32-bit identifier. + // Currently, the identifier is a hash of the group's name. + optional fixed32 group_id = 2; + } + repeated FieldTrial field_trial = 9; + + // Information about the A/V output device(s) (typically just a TV). + // However, a configuration may have one or more intermediate A/V devices + // between the source device and the TV (e.g. an A/V receiver, video + // processor, etc.). + message ExternalAudioVideoDevice { + // The manufacturer name (possibly encoded as a 3-letter code, e.g. "YMH" + // for Yamaha). + optional string manufacturer_name = 1; + + // The model name (e.g. "RX-V1900"). Some devices may report generic names + // like "receiver" or use the full manufacturer name (e.g "PHILIPS"). + optional string model_name = 2; + + // The product code (e.g. "0218"). + optional string product_code = 3; + + // The device types. A single device can have multiple types (e.g. a set-top + // box could be both a tuner and a player). The same type may even be + // repeated (e.g a device that reports two tuners). + enum AVDeviceType { + AV_DEVICE_TYPE_UNKNOWN = 0; + AV_DEVICE_TYPE_TV = 1; + AV_DEVICE_TYPE_RECORDER = 2; + AV_DEVICE_TYPE_TUNER = 3; + AV_DEVICE_TYPE_PLAYER = 4; + AV_DEVICE_TYPE_AUDIO_SYSTEM = 5; + } + repeated AVDeviceType av_device_type = 4; + + // The year of manufacture. + optional int32 manufacture_year = 5; + + // The week of manufacture. + // Note: per the Wikipedia EDID article, numbering for this field may not + // be consistent between manufacturers. + optional int32 manufacture_week = 6; + + // Max horizontal resolution in pixels. + optional int32 horizontal_resolution = 7; + + // Max vertical resolution in pixels. + optional int32 vertical_resolution = 8; + + // Audio capabilities of the device. + // Ref: http://en.wikipedia.org/wiki/Extended_display_identification_data + message AudioDescription { + // Audio format + enum AudioFormat { + AUDIO_FORMAT_UNKNOWN = 0; + AUDIO_FORMAT_LPCM = 1; + AUDIO_FORMAT_AC_3 = 2; + AUDIO_FORMAT_MPEG1 = 3; + AUDIO_FORMAT_MP3 = 4; + AUDIO_FORMAT_MPEG2 = 5; + AUDIO_FORMAT_AAC = 6; + AUDIO_FORMAT_DTS = 7; + AUDIO_FORMAT_ATRAC = 8; + AUDIO_FORMAT_ONE_BIT = 9; + AUDIO_FORMAT_DD_PLUS = 10; + AUDIO_FORMAT_DTS_HD = 11; + AUDIO_FORMAT_MLP_DOLBY_TRUEHD = 12; + AUDIO_FORMAT_DST_AUDIO = 13; + AUDIO_FORMAT_MICROSOFT_WMA_PRO = 14; + } + optional AudioFormat audio_format = 1; + + // Number of channels (e.g. 1, 2, 8, etc.). + optional int32 num_channels = 2; + + // Supported sample frequencies in Hz (e.g. 32000, 44100, etc.). + // Multiple frequencies may be specified. + repeated int32 sample_frequency_hz = 3; + + // Maximum bit rate in bits/s. + optional int32 max_bit_rate_per_second = 4; + + // Bit depth (e.g. 16, 20, 24, etc.). + optional int32 bit_depth = 5; + } + repeated AudioDescription audio_description = 9; + + // The position in AV setup. + // A value of 0 means this device is the TV. + // A value of 1 means this device is directly connected to one of + // the TV's inputs. + // Values > 1 indicate there are 1 or more devices between this device + // and the TV. + optional int32 position_in_setup = 10; + + // Whether this device is in the path to the TV. + optional bool is_in_path_to_tv = 11; + + // The CEC version the device supports. + // CEC stands for Consumer Electronics Control, a part of the HDMI + // specification. Not all HDMI devices support CEC. + // Only devices that support CEC will report a value here. + optional int32 cec_version = 12; + + // This message reports CEC commands seen by a device. + // After each log is sent, this information is cleared and gathered again. + // By collecting CEC status information by opcode we can determine + // which CEC features can be supported. + message CECCommand { + // The CEC command opcode. CEC supports up to 256 opcodes. + // We add only one CECCommand message per unique opcode. Only opcodes + // seen by the device will be reported. The remainder of the message + // accumulates status for this opcode (and device). + optional int32 opcode = 1; + + // The total number of commands received from the external device. + optional int32 num_received_direct = 2; + + // The number of commands received from the external device as part of a + // broadcast message. + optional int32 num_received_broadcast = 3; + + // The total number of commands sent to the external device. + optional int32 num_sent_direct = 4; + + // The number of commands sent to the external device as part of a + // broadcast message. + optional int32 num_sent_broadcast = 5; + + // The number of aborted commands for unknown reasons. + optional int32 num_aborted_unknown_reason = 6; + + // The number of aborted commands because of an unrecognized opcode. + optional int32 num_aborted_unrecognized = 7; + } + repeated CECCommand cec_command = 13; + } + repeated ExternalAudioVideoDevice external_audio_video_device = 14; + + // Information about the current wireless access point. Collected directly + // from the wireless access point via standard apis if the device is + // connected to the Internet wirelessly. Introduced for Chrome on TV devices + // but also can be collected by ChromeOS, Android or other clients. + message ExternalAccessPoint { + // The manufacturer name, for example "ASUSTeK Computer Inc.". + optional string manufacturer = 1; + + // The model name, for example "Wi-Fi Protected Setup Router". + optional string model_name = 2; + + // The model number, for example "RT-N16". + optional string model_number = 3; + + // The device name (sometime same as model_number), for example "RT-N16". + optional string device_name = 4; + } + optional ExternalAccessPoint external_access_point = 15; + + // Number of users currently signed into a multiprofile session. + // A zero value indicates that the user count changed while the log is open. + // Logged only on ChromeOS. + optional uint32 multi_profile_user_count = 17; + + // Information about extensions that are installed, masked to provide better + // privacy. Only extensions from a single profile are reported; this will + // generally be the profile used when the browser is started. The profile + // reported on will remain consistent at least until the browser is + // relaunched (or the profile is deleted by the user). + // + // Each client first picks a value for client_key derived from its UMA + // client_id: + // client_key = client_id % 4096 + // Then, each installed extension is mapped into a hash bucket according to + // bucket = CityHash64(StringPrintf("%d:%s", + // client_key, extension_id)) % 1024 + // The client reports the set of hash buckets occupied by all installed + // extensions. If multiple extensions map to the same bucket, that bucket is + // still only reported once. + repeated int32 occupied_extension_bucket = 18; + + // The state of loaded extensions for this system. The system can have either + // no applicable extensions, extensions only from the webstore and verified by + // the webstore, extensions only from the webstore but not verified, or + // extensions not from the store. If there is a single off-store extension, + // then HAS_OFFSTORE is reported. This should be kept in sync with the + // corresponding enum in chrome/browser/metrics/extensions_metrics_provider.cc + enum ExtensionsState { + NO_EXTENSIONS = 0; + NO_OFFSTORE_VERIFIED = 1; + NO_OFFSTORE_UNVERIFIED = 2; + HAS_OFFSTORE = 3; + } + optional ExtensionsState offstore_extensions_state = 19; +} diff --git a/metrics/uploader/proto/user_action_event.proto b/metrics/uploader/proto/user_action_event.proto new file mode 100644 index 000000000..30a93180d --- /dev/null +++ b/metrics/uploader/proto/user_action_event.proto @@ -0,0 +1,23 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Stores information about an event that occurs in response to a user action, +// e.g. an interaction with a browser UI element. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; +option java_outer_classname = "UserActionEventProtos"; +option java_package = "org.chromium.components.metrics"; + +package metrics; + +// Next tag: 3 +message UserActionEventProto { + // The name of the action, hashed. + optional fixed64 name_hash = 1; + + // The timestamp for the event, in seconds since the epoch. + optional int64 time = 2; +} diff --git a/metrics/uploader/system_profile_cache.cc b/metrics/uploader/system_profile_cache.cc index d16b1dd58..ea4a38c2c 100644 --- a/metrics/uploader/system_profile_cache.cc +++ b/metrics/uploader/system_profile_cache.cc @@ -13,9 +13,9 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/sys_info.h" -#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" #include "metrics/persistent_integer.h" #include "metrics/uploader/metrics_log_base.h" +#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h" #include "vboot/crossystem.h" namespace { diff --git a/metrics/uploader/system_profile_cache.h b/metrics/uploader/system_profile_cache.h index bbfc90def..e7a73370a 100644 --- a/metrics/uploader/system_profile_cache.h +++ b/metrics/uploader/system_profile_cache.h @@ -13,8 +13,8 @@ #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "chromeos/osrelease_reader.h" -#include "components/metrics/proto/system_profile.pb.h" #include "metrics/persistent_integer.h" +#include "metrics/uploader/proto/system_profile.pb.h" #include "metrics/uploader/system_profile_setter.h" namespace metrics { diff --git a/metrics/uploader/upload_service_test.cc b/metrics/uploader/upload_service_test.cc index d04cc72c4..ee17e1533 100644 --- a/metrics/uploader/upload_service_test.cc +++ b/metrics/uploader/upload_service_test.cc @@ -9,14 +9,14 @@ #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/sys_info.h" -#include "components/metrics/proto/chrome_user_metrics_extension.pb.h" -#include "components/metrics/proto/histogram_event.pb.h" -#include "components/metrics/proto/system_profile.pb.h" #include "metrics/metrics_library_mock.h" #include "metrics/serialization/metric_sample.h" #include "metrics/uploader/metrics_log.h" #include "metrics/uploader/mock/mock_system_profile_setter.h" #include "metrics/uploader/mock/sender_mock.h" +#include "metrics/uploader/proto/chrome_user_metrics_extension.pb.h" +#include "metrics/uploader/proto/histogram_event.pb.h" +#include "metrics/uploader/proto/system_profile.pb.h" #include "metrics/uploader/system_profile_cache.h" #include "metrics/uploader/upload_service.h"