Index: src/platform/metrics/metrics_daemon.cc |
diff --git a/src/platform/metrics/metrics_daemon.cc b/src/platform/metrics/metrics_daemon.cc |
index 940085167c9dc4cb279cf40b4bdd7be9a7081e41..32581c1a1b71e5488e3e43f3b59d1d98eb7c0c31 100644 |
--- a/src/platform/metrics/metrics_daemon.cc |
+++ b/src/platform/metrics/metrics_daemon.cc |
@@ -5,22 +5,43 @@ |
#include "metrics_daemon.h" |
#include "metrics_library.h" |
-#include <glib-object.h> |
- |
-extern "C" { |
-#include "marshal_void__string_boxed.h" |
-} |
+#include <dbus/dbus-glib-lowlevel.h> |
#include <base/logging.h> |
-#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" |
+ |
+// 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'", |
+}; |
-MetricsDaemon::NetworkState |
+// 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; |
- |
- ::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); |
+ network_state_ = kUnknownNetworkState; |
+ network_state_changed_ = 0; |
+ power_state_ = kUnknownPowerState; |
+ |
+ g_thread_init(NULL); |
+ g_type_init(); |
+ dbus_g_thread_init(); |
+ |
+ DBusError error; |
+ dbus_error_init(&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(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<MetricsDaemon*>(data))->NetSignalHandler(proxy, property, value); |
-} |
+// static |
+DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection, |
+ DBusMessage* message, |
+ void* user_data) { |
+ LOG(INFO) << "message filter"; |
-void MetricsDaemon::NetSignalHandler(::DBusGProxy* proxy, |
- const char* property, |
- const ::GValue* value) { |
- if (strcmp("ConnectionState", property) != 0) { |
- return; |
+ 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; |
} |
- const char* newstate = static_cast<const char*>(g_value_get_string(value)); |
- LogNetworkStateChange(newstate); |
-} |
+ // Signal messages always have interfaces. |
+ const char* interface = dbus_message_get_interface(message); |
+ CHECK(interface != NULL); |
-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; |
+ MetricsDaemon* daemon = static_cast<MetricsDaemon*>(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; |
} |
- struct timeval now; |
- if (gettimeofday(&now, NULL) != 0) { |
- PLOG(WARNING) << "gettimeofday"; |
+ |
+ return DBUS_HANDLER_RESULT_HANDLED; |
+} |
+ |
+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<int>(now - network_state_changed_); |
+ PublishMetric("Network.TimeToDrop", online_time, |
+ 1, 8 /* hours */ * 60 * 60, 50); |
} |
- 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; |
+ |
+ network_state_ = state; |
+ network_state_changed_ = now; |
+} |
+ |
+MetricsDaemon::NetworkState |
+MetricsDaemon::LookupNetworkState(const char* state_name) { |
+ for (int i = 0; i < kNumberNetworkStates; i++) { |
+ if (strcmp(state_name, network_states_[i]) == 0) { |
+ return static_cast<NetworkState>(i); |
} |
- 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; |
+ LOG(WARNING) << "unknown network connection state: " << state_name; |
+ return kUnknownNetworkState; |
} |
-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<NetworkStateId>(i); |
+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<PowerState>(i); |
} |
} |
- return static_cast<NetworkStateId>(-1); |
+ 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); |
} |
} |