blob: a5cf7319912b125bdb189ca370b6210a9c610aa2 [file] [log] [blame]
Thieu Lead1ec2c2012-01-05 23:39:48 +00001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Thieu Le48e6d6d2011-12-06 00:40:27 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/metrics.h"
6
7#include <base/lazy_instance.h>
8#include <base/logging.h>
9#include <base/string_util.h>
10#include <base/stringprintf.h>
Thieu Lead1ec2c2012-01-05 23:39:48 +000011#include <chromeos/dbus/service_constants.h>
Thieu Le48e6d6d2011-12-06 00:40:27 +000012
13#include "shill/wifi_service.h"
14
15using std::string;
16using std::tr1::shared_ptr;
17
18namespace shill {
19
20static base::LazyInstance<Metrics> g_metrics(base::LINKER_INITIALIZED);
21
22// static
Thieu Le67370f62012-02-14 23:01:42 +000023const char Metrics::kMetricDisconnect[] = "Network.Shill.%s.Disconnect";
24const int Metrics::kMetricDisconnectMax = 1;
25const int Metrics::kMetricDisconnectMin = 0;
26const int Metrics::kMetricDisconnectNumBuckets = 2;
27
Thieu Le48e6d6d2011-12-06 00:40:27 +000028const char Metrics::kMetricNetworkChannel[] = "Network.Shill.%s.Channel";
29const int Metrics::kMetricNetworkChannelMax = Metrics::kWiFiChannelMax;
Thieu Lead1ec2c2012-01-05 23:39:48 +000030const char Metrics::kMetricNetworkPhyMode[] = "Network.Shill.%s.PhyMode";
31const int Metrics::kMetricNetworkPhyModeMax = Metrics::kWiFiNetworkPhyModeMax;
32const char Metrics::kMetricNetworkSecurity[] = "Network.Shill.%s.Security";
33const int Metrics::kMetricNetworkSecurityMax = Metrics::kWiFiSecurityMax;
Thieu Le48e6d6d2011-12-06 00:40:27 +000034const char Metrics::kMetricNetworkServiceErrors[] =
35 "Network.Shill.ServiceErrors";
36const int Metrics::kMetricNetworkServiceErrorsMax = Service::kFailureMax;
Thieu Lea20cbc22012-01-09 22:01:43 +000037
38const char Metrics::kMetricTimeOnlineSeconds[] = "Network.Shill.%s.TimeOnline";
39const int Metrics::kMetricTimeOnlineSecondsMax = 8 * 60 * 60; // 8 hours
40const int Metrics::kMetricTimeOnlineSecondsMin = 1;
41
42const char Metrics::kMetricTimeToDropSeconds[] = "Network.Shill.TimeToDrop";;
43const int Metrics::kMetricTimeToDropSecondsMax = 8 * 60 * 60; // 8 hours
44const int Metrics::kMetricTimeToDropSecondsMin = 1;
45
Thieu Le48e6d6d2011-12-06 00:40:27 +000046const char Metrics::kMetricTimeToConfigMilliseconds[] =
47 "Network.Shill.%s.TimeToConfig";
48const char Metrics::kMetricTimeToJoinMilliseconds[] =
49 "Network.Shill.%s.TimeToJoin";
50const char Metrics::kMetricTimeToOnlineMilliseconds[] =
51 "Network.Shill.%s.TimeToOnline";
52const char Metrics::kMetricTimeToPortalMilliseconds[] =
53 "Network.Shill.%s.TimeToPortal";
Thieu Lea20cbc22012-01-09 22:01:43 +000054const int Metrics::kTimerHistogramMillisecondsMax = 45 * 1000;
55const int Metrics::kTimerHistogramMillisecondsMin = 1;
Thieu Le48e6d6d2011-12-06 00:40:27 +000056const int Metrics::kTimerHistogramNumBuckets = 50;
57
58// static
59const uint16 Metrics::kWiFiBandwidth5MHz = 5;
60const uint16 Metrics::kWiFiBandwidth20MHz = 20;
61const uint16 Metrics::kWiFiFrequency2412 = 2412;
62const uint16 Metrics::kWiFiFrequency2472 = 2472;
63const uint16 Metrics::kWiFiFrequency2484 = 2484;
64const uint16 Metrics::kWiFiFrequency5170 = 5170;
65const uint16 Metrics::kWiFiFrequency5180 = 5180;
66const uint16 Metrics::kWiFiFrequency5230 = 5230;
67const uint16 Metrics::kWiFiFrequency5240 = 5240;
68const uint16 Metrics::kWiFiFrequency5320 = 5320;
69const uint16 Metrics::kWiFiFrequency5500 = 5500;
70const uint16 Metrics::kWiFiFrequency5700 = 5700;
71const uint16 Metrics::kWiFiFrequency5745 = 5745;
72const uint16 Metrics::kWiFiFrequency5825 = 5825;
73
Thieu Lea20cbc22012-01-09 22:01:43 +000074Metrics::Metrics()
75 : library_(&metrics_library_),
76 last_default_technology_(Technology::kUnknown),
77 was_online_(false),
78 time_online_timer_(new chromeos_metrics::Timer),
79 time_to_drop_timer_(new chromeos_metrics::Timer) {
Thieu Le48e6d6d2011-12-06 00:40:27 +000080 metrics_library_.Init();
81 chromeos_metrics::TimerReporter::set_metrics_lib(library_);
82}
83
84Metrics::~Metrics() {}
85
86// static
Thieu Le48e6d6d2011-12-06 00:40:27 +000087Metrics::WiFiChannel Metrics::WiFiFrequencyToChannel(uint16 frequency) {
88 WiFiChannel channel = kWiFiChannelUndef;
89 if (kWiFiFrequency2412 <= frequency && frequency <= kWiFiFrequency2472) {
90 if (((frequency - kWiFiFrequency2412) % kWiFiBandwidth5MHz) == 0)
91 channel = static_cast<WiFiChannel>(
92 kWiFiChannel2412 +
93 (frequency - kWiFiFrequency2412) / kWiFiBandwidth5MHz);
94 } else if (frequency == kWiFiFrequency2484) {
95 channel = kWiFiChannel2484;
96 } else if (kWiFiFrequency5170 <= frequency &&
97 frequency <= kWiFiFrequency5230) {
98 if ((frequency % kWiFiBandwidth20MHz) == 0)
99 channel = static_cast<WiFiChannel>(
100 kWiFiChannel5180 +
101 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
102 if ((frequency % kWiFiBandwidth20MHz) == 10)
103 channel = static_cast<WiFiChannel>(
104 kWiFiChannel5170 +
105 (frequency - kWiFiFrequency5170) / kWiFiBandwidth20MHz);
106 } else if (kWiFiFrequency5240 <= frequency &&
107 frequency <= kWiFiFrequency5320) {
108 if (((frequency - kWiFiFrequency5180) % kWiFiBandwidth20MHz) == 0)
109 channel = static_cast<WiFiChannel>(
110 kWiFiChannel5180 +
111 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
112 } else if (kWiFiFrequency5500 <= frequency &&
113 frequency <= kWiFiFrequency5700) {
114 if (((frequency - kWiFiFrequency5500) % kWiFiBandwidth20MHz) == 0)
115 channel = static_cast<WiFiChannel>(
116 kWiFiChannel5500 +
117 (frequency - kWiFiFrequency5500) / kWiFiBandwidth20MHz);
118 } else if (kWiFiFrequency5745 <= frequency &&
119 frequency <= kWiFiFrequency5825) {
120 if (((frequency - kWiFiFrequency5745) % kWiFiBandwidth20MHz) == 0)
121 channel = static_cast<WiFiChannel>(
122 kWiFiChannel5745 +
123 (frequency - kWiFiFrequency5745) / kWiFiBandwidth20MHz);
124 }
125 CHECK(kWiFiChannelUndef <= channel && channel < kWiFiChannelMax);
126
127 if (channel == kWiFiChannelUndef)
128 LOG(WARNING) << "no mapping for frequency " << frequency;
129 else
130 VLOG(3) << "map " << frequency << " to " << channel;
131
132 return channel;
133}
134
Thieu Lead1ec2c2012-01-05 23:39:48 +0000135// static
136Metrics::WiFiSecurity Metrics::WiFiSecurityStringToEnum(
137 const std::string &security) {
138 if (security == flimflam::kSecurityNone) {
139 return kWiFiSecurityNone;
140 } else if (security == flimflam::kSecurityWep) {
141 return kWiFiSecurityWep;
142 } else if (security == flimflam::kSecurityWpa) {
143 return kWiFiSecurityWpa;
144 } else if (security == flimflam::kSecurityRsn) {
145 return kWiFiSecurityRsn;
146 } else if (security == flimflam::kSecurity8021x) {
147 return kWiFiSecurity8021x;
148 } else if (security == flimflam::kSecurityPsk) {
149 return kWiFiSecurityPsk;
150 } else {
151 return kWiFiSecurityUnknown;
152 }
153}
154
Thieu Le48e6d6d2011-12-06 00:40:27 +0000155void Metrics::RegisterService(const Service *service) {
156 shared_ptr<ServiceMetrics> service_metrics(new ServiceMetrics);
157 services_metrics_[service] = service_metrics;
158 service_metrics->service = service;
159 InitializeCommonServiceMetrics(service);
160 service->InitializeCustomMetrics();
161}
162
163void Metrics::DeregisterService(const Service *service) {
164 services_metrics_.erase(service);
165}
166
167void Metrics::AddServiceStateTransitionTimer(
168 const Service *service,
169 const string &histogram_name,
170 Service::ConnectState start_state,
171 Service::ConnectState stop_state) {
172 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
173 if (it == services_metrics_.end()) {
174 VLOG(1) << "service not found";
175 DCHECK(false);
176 return;
177 }
178 ServiceMetrics *service_metrics = it->second.get();
179 CHECK(start_state < stop_state);
180 chromeos_metrics::TimerReporter *timer =
181 new chromeos_metrics::TimerReporter(histogram_name,
Thieu Lea20cbc22012-01-09 22:01:43 +0000182 kTimerHistogramMillisecondsMin,
183 kTimerHistogramMillisecondsMax,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000184 kTimerHistogramNumBuckets);
185 service_metrics->timers.push_back(timer); // passes ownership.
186 service_metrics->start_on_state[start_state].push_back(timer);
187 service_metrics->stop_on_state[stop_state].push_back(timer);
188}
189
Thieu Lea20cbc22012-01-09 22:01:43 +0000190void Metrics::NotifyDefaultServiceChanged(const Service *service) {
191 base::TimeDelta elapsed_seconds;
192
193 Technology::Identifier technology = (service) ? service->technology() :
194 Technology::kUnknown;
195 if (technology != last_default_technology_) {
196 if (last_default_technology_ != Technology::kUnknown) {
197 string histogram = GetFullMetricName(kMetricTimeOnlineSeconds,
198 last_default_technology_);
199 time_online_timer_->GetElapsedTime(&elapsed_seconds);
200 SendToUMA(histogram,
201 elapsed_seconds.InSeconds(),
202 kMetricTimeOnlineSecondsMin,
203 kMetricTimeOnlineSecondsMax,
204 kTimerHistogramNumBuckets);
205 }
206 last_default_technology_ = technology;
207 time_online_timer_->Start();
208 }
209
210 // TODO(thieule): Ignore changes when suspending.
211 // crosbug.com/24440
212
213 // Ignore changes that are not online/offline transitions; e.g.
214 // switching between wired and wireless. TimeToDrop measures
215 // time online regardless of how we are connected.
216 if ((service == NULL && !was_online_) || (service != NULL && was_online_))
217 return;
218
219 if (service == NULL) {
220 time_to_drop_timer_->GetElapsedTime(&elapsed_seconds);
221 SendToUMA(kMetricTimeToDropSeconds,
222 elapsed_seconds.InSeconds(),
223 kMetricTimeToDropSecondsMin,
224 kMetricTimeToDropSecondsMax,
225 kTimerHistogramNumBuckets);
226 } else {
227 time_to_drop_timer_->Start();
228 }
229
230 was_online_ = (service != NULL);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000231}
232
233void Metrics::NotifyServiceStateChanged(const Service *service,
234 Service::ConnectState new_state) {
235 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
236 if (it == services_metrics_.end()) {
237 VLOG(1) << "service not found";
238 DCHECK(false);
239 return;
240 }
241 ServiceMetrics *service_metrics = it->second.get();
242 UpdateServiceStateTransitionMetrics(service_metrics, new_state);
243
244 if (new_state == Service::kStateFailure)
245 SendServiceFailure(service);
246
Paul Stewart20088d82012-02-16 06:58:55 -0800247 if (new_state != Service::kStateConnected)
Thieu Le48e6d6d2011-12-06 00:40:27 +0000248 return;
249
250 service->SendPostReadyStateMetrics();
251}
252
253string Metrics::GetFullMetricName(const char *metric_name,
254 Technology::Identifier technology_id) {
255 string technology = Technology::NameFromIdentifier(technology_id);
256 technology[0] = base::ToUpperASCII(technology[0]);
257 return base::StringPrintf(metric_name, technology.c_str());
258}
259
Thieu Le67370f62012-02-14 23:01:42 +0000260void Metrics::NotifyServiceDisconnect(const Service *service) {
261 Technology::Identifier technology = service->technology();
262 string histogram = GetFullMetricName(kMetricDisconnect, technology);
263 SendToUMA(histogram,
264 service->explicitly_disconnected(),
265 kMetricDisconnectMin,
266 kMetricDisconnectMax,
267 kMetricDisconnectNumBuckets);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000268}
269
270void Metrics::NotifyPower() {
271 // TODO(thieule): Handle suspend and resume.
272 // crosbug.com/24440
273}
274
275bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
276 return library_->SendEnumToUMA(name, sample, max);
277}
278
Thieu Lea20cbc22012-01-09 22:01:43 +0000279bool Metrics::SendToUMA(const string &name, int sample, int min, int max,
280 int num_buckets) {
281 return library_->SendToUMA(name, sample, min, max, num_buckets);
282}
283
Thieu Le48e6d6d2011-12-06 00:40:27 +0000284void Metrics::InitializeCommonServiceMetrics(const Service *service) {
285 Technology::Identifier technology = service->technology();
286 string histogram = GetFullMetricName(kMetricTimeToConfigMilliseconds,
287 technology);
288 AddServiceStateTransitionTimer(
289 service,
290 histogram,
291 Service::kStateConfiguring,
Paul Stewart20088d82012-02-16 06:58:55 -0800292 Service::kStateConnected);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000293 histogram = GetFullMetricName(kMetricTimeToPortalMilliseconds, technology);
294 AddServiceStateTransitionTimer(
295 service,
296 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800297 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000298 Service::kStatePortal);
299 histogram = GetFullMetricName(kMetricTimeToOnlineMilliseconds, technology);
300 AddServiceStateTransitionTimer(
301 service,
302 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800303 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000304 Service::kStateOnline);
305}
306
307void Metrics::UpdateServiceStateTransitionMetrics(
308 ServiceMetrics *service_metrics,
309 Service::ConnectState new_state) {
310 TimerReportersList::iterator it;
311 TimerReportersList &start_timers = service_metrics->start_on_state[new_state];
312 for (it = start_timers.begin(); it != start_timers.end(); ++it)
313 (*it)->Start();
314
315 TimerReportersList &stop_timers = service_metrics->stop_on_state[new_state];
316 for (it = stop_timers.begin(); it != stop_timers.end(); ++it) {
317 (*it)->Stop();
318 (*it)->ReportMilliseconds();
319 }
320}
321
322void Metrics::SendServiceFailure(const Service *service) {
323 library_->SendEnumToUMA(kMetricNetworkServiceErrors,
324 service->failure(),
325 kMetricNetworkServiceErrorsMax);
326}
327
328void Metrics::set_library(MetricsLibraryInterface *library) {
329 chromeos_metrics::TimerReporter::set_metrics_lib(library);
330 library_ = library;
331}
332
333} // namespace shill