blob: 940085167c9dc4cb279cf40b4bdd7be9a7081e41 [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
8#include <glib-object.h>
9
10extern "C" {
11#include "marshal_void__string_boxed.h"
12}
13
14#include <base/logging.h>
15
16#define SAFE_MESSAGE(e) ((e && e->message) ? e->message : "unknown error")
17
18MetricsDaemon::NetworkState
19MetricsDaemon::network_states_[MetricsDaemon::kNumberNetworkStates] = {
Darin Petkovc2526a12010-04-21 14:24:04 -070020#define STATE(name, capname) { #name, "Network.Connman" # capname },
Darin Petkov65b01462010-04-14 13:32:20 -070021#include "network_states.h"
22};
23
24void MetricsDaemon::Run(bool run_as_daemon, bool testing) {
25 Init(testing);
26 if (!run_as_daemon || daemon(0, 0) == 0) {
27 Loop();
28 }
29}
30
31void MetricsDaemon::Init(bool testing) {
32 testing_ = testing;
33 network_state_id_ = kUnknownNetworkStateId;
34
35 ::g_thread_init(NULL);
36 ::g_type_init();
37 ::dbus_g_thread_init();
38
39 ::GError* error = NULL;
40 ::DBusGConnection* dbc = ::dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
41 // Note that LOG(FATAL) terminates the process; otherwise we'd have to worry
42 // about leaking |error|.
43 LOG_IF(FATAL, dbc == NULL) <<
44 "cannot connect to dbus: " << SAFE_MESSAGE(error);
45
46 ::DBusGProxy* net_proxy = ::dbus_g_proxy_new_for_name(
47 dbc, "org.moblin.connman", "/", "org.moblin.connman.Metrics");
48 LOG_IF(FATAL, net_proxy == NULL) << "no dbus proxy for network";
49
50#if 0
51 // Unclear how soon one can call dbus_g_type_get_map(). Doing it before the
52 // call to dbus_g_bus_get() results in a (non-fatal) assertion failure.
53 // GetProperties returns a hash table.
54 hashtable_gtype = ::dbus_g_type_get_map("GHashTable", G_TYPE_STRING,
55 G_TYPE_VALUE);
56#endif
57
58 dbus_g_object_register_marshaller(marshal_VOID__STRING_BOXED,
59 G_TYPE_NONE,
60 G_TYPE_STRING,
61 G_TYPE_VALUE,
62 G_TYPE_INVALID);
63 ::dbus_g_proxy_add_signal(net_proxy, "ConnectionStateChanged",
64 G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID);
65 ::dbus_g_proxy_connect_signal(net_proxy, "ConnectionStateChanged",
66 G_CALLBACK(&StaticNetSignalHandler),
67 this, NULL);
68}
69
70void MetricsDaemon::Loop() {
71 ::GMainLoop* loop = ::g_main_loop_new(NULL, false);
72 ::g_main_loop_run(loop);
73}
74
75void MetricsDaemon::StaticNetSignalHandler(::DBusGProxy* proxy,
76 const char* property,
77 const ::GValue* value,
78 void *data) {
79 (static_cast<MetricsDaemon*>(data))->NetSignalHandler(proxy, property, value);
80}
81
82void MetricsDaemon::NetSignalHandler(::DBusGProxy* proxy,
83 const char* property,
84 const ::GValue* value) {
85 if (strcmp("ConnectionState", property) != 0) {
86 return;
87 }
88
89 const char* newstate = static_cast<const char*>(g_value_get_string(value));
90 LogNetworkStateChange(newstate);
91}
92
93void MetricsDaemon::LogNetworkStateChange(const char* newstate) {
94 NetworkStateId new_id = GetNetworkStateId(newstate);
95 if (new_id == kUnknownNetworkStateId) {
96 LOG(WARNING) << "unknown network connection state " << newstate;
97 return;
98 }
99 NetworkStateId old_id = network_state_id_;
100 if (new_id == old_id) { // valid new state and no change
101 return;
102 }
103 struct timeval now;
104 if (gettimeofday(&now, NULL) != 0) {
105 PLOG(WARNING) << "gettimeofday";
106 }
107 if (old_id != kUnknownNetworkStateId) {
108 struct timeval diff;
109 timersub(&now, &network_state_start_, &diff);
110 int diff_ms = diff.tv_usec / 1000 + diff.tv_sec * 1000;
111 // Saturates rather than overflowing. We expect this to be statistically
112 // insignificant, since INT_MAX milliseconds is 24.8 days.
113 if (diff.tv_sec >= INT_MAX / 1000) {
114 diff_ms = INT_MAX;
115 }
Darin Petkovc2526a12010-04-21 14:24:04 -0700116 PublishMetric(network_states_[old_id].stat_name,
117 diff_ms,
118 1,
119 8 * 60 * 60 * 1000, // 8 hours in milliseconds
120 100);
Darin Petkov65b01462010-04-14 13:32:20 -0700121 }
122 network_state_id_ = new_id;
123 network_state_start_ = now;
124}
125
126MetricsDaemon::NetworkStateId
127MetricsDaemon::GetNetworkStateId(const char* state_name) {
128 for (int i = 0; i < kNumberNetworkStates; i++) {
129 if (strcmp(state_name, network_states_[i].name) == 0) {
130 return static_cast<NetworkStateId>(i);
131 }
132 }
133 return static_cast<NetworkStateId>(-1);
134}
135
Darin Petkovc2526a12010-04-21 14:24:04 -0700136void MetricsDaemon::PublishMetric(const char* name, int sample,
137 int min, int max, int nbuckets) {
138 if (testing_) {
139 LOG(INFO) << "received metric: " << name << " " << sample <<
140 " " << min << " " << max << " " << nbuckets;
141 } else {
142 MetricsLibrary::SendToChrome(name, sample, min, max, nbuckets);
143 }
Darin Petkov65b01462010-04-14 13:32:20 -0700144}