blob: 32581c1a1b71e5488e3e43f3b59d1d98eb7c0c31 [file] [log] [blame]
Darin Petkov65b01462010-04-14 13:32:20 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "metrics_daemon.h"
6#include "metrics_library.h"
7
Darin Petkov703ec972010-04-27 11:02:18 -07008#include <dbus/dbus-glib-lowlevel.h>
Darin Petkov65b01462010-04-14 13:32:20 -07009
10#include <base/logging.h>
11
Darin Petkov703ec972010-04-27 11:02:18 -070012#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
13#define DBUS_IFACE_CONNMAN_MANAGER "org.moblin.connman.Manager"
14#define DBUS_IFACE_POWER_MANAGER "org.chromium.Power.Manager"
Darin Petkov65b01462010-04-14 13:32:20 -070015
Darin Petkov703ec972010-04-27 11:02:18 -070016// static
17const char*
18MetricsDaemon::dbus_matches_[] = {
19 "type='signal',"
20 "sender='org.moblin.connman',"
21 "interface='" DBUS_IFACE_CONNMAN_MANAGER "',"
22 "path='/',"
23 "member='StateChanged'",
24
25 "type='signal',"
26 "interface='" DBUS_IFACE_POWER_MANAGER "',"
27 "path='/',"
28 "member='PowerStateChanged'",
29};
30
31// static
32const char *
Darin Petkov65b01462010-04-14 13:32:20 -070033MetricsDaemon::network_states_[MetricsDaemon::kNumberNetworkStates] = {
Darin Petkov703ec972010-04-27 11:02:18 -070034#define STATE(name, capname) #name,
Darin Petkov65b01462010-04-14 13:32:20 -070035#include "network_states.h"
36};
37
Darin Petkov703ec972010-04-27 11:02:18 -070038// static
39const char *
40MetricsDaemon::power_states_[MetricsDaemon::kNumberPowerStates] = {
41#define STATE(name, capname) #name,
42#include "power_states.h"
43};
44
Darin Petkov65b01462010-04-14 13:32:20 -070045void MetricsDaemon::Run(bool run_as_daemon, bool testing) {
46 Init(testing);
47 if (!run_as_daemon || daemon(0, 0) == 0) {
48 Loop();
49 }
50}
51
52void MetricsDaemon::Init(bool testing) {
53 testing_ = testing;
Darin Petkov703ec972010-04-27 11:02:18 -070054 network_state_ = kUnknownNetworkState;
55 network_state_changed_ = 0;
56 power_state_ = kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -070057
Darin Petkov703ec972010-04-27 11:02:18 -070058 g_thread_init(NULL);
59 g_type_init();
60 dbus_g_thread_init();
Darin Petkov65b01462010-04-14 13:32:20 -070061
Darin Petkov703ec972010-04-27 11:02:18 -070062 DBusError error;
63 dbus_error_init(&error);
Darin Petkov65b01462010-04-14 13:32:20 -070064
Darin Petkov703ec972010-04-27 11:02:18 -070065 DBusConnection *connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
66 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
67 "No D-Bus connection: " << SAFE_MESSAGE(error);
Darin Petkov65b01462010-04-14 13:32:20 -070068
Darin Petkov703ec972010-04-27 11:02:18 -070069 dbus_connection_setup_with_g_main(connection, NULL);
Darin Petkov65b01462010-04-14 13:32:20 -070070
Darin Petkov703ec972010-04-27 11:02:18 -070071 // Registers D-Bus matches for the signals we would like to catch.
72 for (unsigned int m = 0; m < sizeof(dbus_matches_) / sizeof(char *); m++) {
73 const char* match = dbus_matches_[m];
74 LOG(INFO) << "adding dbus match: " << match;
75 dbus_bus_add_match(connection, match, &error);
76 LOG_IF(FATAL, dbus_error_is_set(&error)) <<
77 "unable to add a match: " << SAFE_MESSAGE(error);
78 }
79
80 // Adds the D-Bus filter routine to be called back whenever one of
81 // the registered D-Bus matches is successful. The daemon is not
82 // activated for D-Bus messages that don't match.
83 CHECK(dbus_connection_add_filter(connection, MessageFilter, this, NULL));
Darin Petkov65b01462010-04-14 13:32:20 -070084}
85
86void MetricsDaemon::Loop() {
Darin Petkov703ec972010-04-27 11:02:18 -070087 GMainLoop* loop = g_main_loop_new(NULL, false);
88 g_main_loop_run(loop);
Darin Petkov65b01462010-04-14 13:32:20 -070089}
90
Darin Petkov703ec972010-04-27 11:02:18 -070091// static
92DBusHandlerResult MetricsDaemon::MessageFilter(DBusConnection* connection,
93 DBusMessage* message,
94 void* user_data) {
95 LOG(INFO) << "message filter";
96
97 int message_type = dbus_message_get_type(message);
98 if (message_type != DBUS_MESSAGE_TYPE_SIGNAL) {
99 LOG(WARNING) << "unexpected message type " << message_type;
100 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
101 }
102
103 // Signal messages always have interfaces.
104 const char* interface = dbus_message_get_interface(message);
105 CHECK(interface != NULL);
106
107 MetricsDaemon* daemon = static_cast<MetricsDaemon*>(user_data);
108
109 DBusMessageIter iter;
110 dbus_message_iter_init(message, &iter);
111 if (strcmp(interface, DBUS_IFACE_CONNMAN_MANAGER) == 0) {
112 CHECK(strcmp(dbus_message_get_member(message), "StateChanged") == 0);
113
114 char *state_name;
115 dbus_message_iter_get_basic(&iter, &state_name);
116 daemon->NetStateChanged(state_name);
117 } else if (strcmp(interface, DBUS_IFACE_POWER_MANAGER) == 0) {
118 CHECK(strcmp(dbus_message_get_member(message), "PowerStateChanged") == 0);
119
120 char *state_name;
121 dbus_message_iter_get_basic(&iter, &state_name);
122 daemon->PowerStateChanged(state_name);
123 } else {
124 LOG(WARNING) << "unexpected interface: " << interface;
125 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
126 }
127
128 return DBUS_HANDLER_RESULT_HANDLED;
Darin Petkov65b01462010-04-14 13:32:20 -0700129}
130
Darin Petkov703ec972010-04-27 11:02:18 -0700131void MetricsDaemon::NetStateChanged(const char* state_name) {
132 LOG(INFO) << "network state: " << state_name;
133
134 time_t now = time(NULL);
135 NetworkState state = LookupNetworkState(state_name);
136
137 // Logs the time in seconds between the network going online to
138 // going offline in order to measure the mean time to network
139 // dropping. Going offline as part of suspend-to-RAM is not logged
140 // as network drop -- the assumption is that the message for
141 // suspend-to-RAM comes before the network offline message which
142 // seems to and should be the case.
143 if (state == kNetworkStateOffline &&
144 network_state_ == kNetworkStateOnline &&
145 power_state_ != kPowerStateMem) {
146 int online_time = static_cast<int>(now - network_state_changed_);
147 PublishMetric("Network.TimeToDrop", online_time,
148 1, 8 /* hours */ * 60 * 60, 50);
Darin Petkov65b01462010-04-14 13:32:20 -0700149 }
150
Darin Petkov703ec972010-04-27 11:02:18 -0700151 network_state_ = state;
152 network_state_changed_ = now;
Darin Petkov65b01462010-04-14 13:32:20 -0700153}
154
Darin Petkov703ec972010-04-27 11:02:18 -0700155MetricsDaemon::NetworkState
156MetricsDaemon::LookupNetworkState(const char* state_name) {
Darin Petkov65b01462010-04-14 13:32:20 -0700157 for (int i = 0; i < kNumberNetworkStates; i++) {
Darin Petkov703ec972010-04-27 11:02:18 -0700158 if (strcmp(state_name, network_states_[i]) == 0) {
159 return static_cast<NetworkState>(i);
Darin Petkov65b01462010-04-14 13:32:20 -0700160 }
161 }
Darin Petkov703ec972010-04-27 11:02:18 -0700162 LOG(WARNING) << "unknown network connection state: " << state_name;
163 return kUnknownNetworkState;
164}
165
166void MetricsDaemon::PowerStateChanged(const char* state_name) {
167 LOG(INFO) << "power state: " << state_name;
168 power_state_ = LookupPowerState(state_name);
169}
170
171MetricsDaemon::PowerState
172MetricsDaemon::LookupPowerState(const char* state_name) {
173 for (int i = 0; i < kNumberPowerStates; i++) {
174 if (strcmp(state_name, power_states_[i]) == 0) {
175 return static_cast<PowerState>(i);
176 }
177 }
178 LOG(WARNING) << "unknown power state: " << state_name;
179 return kUnknownPowerState;
Darin Petkov65b01462010-04-14 13:32:20 -0700180}
181
Darin Petkovc2526a12010-04-21 14:24:04 -0700182void MetricsDaemon::PublishMetric(const char* name, int sample,
183 int min, int max, int nbuckets) {
Darin Petkov703ec972010-04-27 11:02:18 -0700184 LOG(INFO) << "received metric: " << name << " " << sample <<
185 " " << min << " " << max << " " << nbuckets;
186 if (!testing_) {
Darin Petkovc2526a12010-04-21 14:24:04 -0700187 MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets);
188 }
Darin Petkov65b01462010-04-14 13:32:20 -0700189}