blob: bf37f722b761c37576cd069d5dc81327ee5f490c [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
Ben Chanfad4a0b2012-04-18 15:49:59 -070013#include "shill/scope_logger.h"
Thieu Le48e6d6d2011-12-06 00:40:27 +000014#include "shill/wifi_service.h"
15
16using std::string;
17using std::tr1::shared_ptr;
18
19namespace shill {
20
Thieu Le48e6d6d2011-12-06 00:40:27 +000021// static
Thieu Le67370f62012-02-14 23:01:42 +000022const char Metrics::kMetricDisconnect[] = "Network.Shill.%s.Disconnect";
23const int Metrics::kMetricDisconnectMax = 1;
24const int Metrics::kMetricDisconnectMin = 0;
25const int Metrics::kMetricDisconnectNumBuckets = 2;
26
Thieu Le48e6d6d2011-12-06 00:40:27 +000027const char Metrics::kMetricNetworkChannel[] = "Network.Shill.%s.Channel";
28const int Metrics::kMetricNetworkChannelMax = Metrics::kWiFiChannelMax;
Thieu Lead1ec2c2012-01-05 23:39:48 +000029const char Metrics::kMetricNetworkPhyMode[] = "Network.Shill.%s.PhyMode";
30const int Metrics::kMetricNetworkPhyModeMax = Metrics::kWiFiNetworkPhyModeMax;
31const char Metrics::kMetricNetworkSecurity[] = "Network.Shill.%s.Security";
32const int Metrics::kMetricNetworkSecurityMax = Metrics::kWiFiSecurityMax;
Thieu Le48e6d6d2011-12-06 00:40:27 +000033const char Metrics::kMetricNetworkServiceErrors[] =
34 "Network.Shill.ServiceErrors";
35const int Metrics::kMetricNetworkServiceErrorsMax = Service::kFailureMax;
Thieu Lea20cbc22012-01-09 22:01:43 +000036
37const char Metrics::kMetricTimeOnlineSeconds[] = "Network.Shill.%s.TimeOnline";
38const int Metrics::kMetricTimeOnlineSecondsMax = 8 * 60 * 60; // 8 hours
39const int Metrics::kMetricTimeOnlineSecondsMin = 1;
40
41const char Metrics::kMetricTimeToDropSeconds[] = "Network.Shill.TimeToDrop";;
42const int Metrics::kMetricTimeToDropSecondsMax = 8 * 60 * 60; // 8 hours
43const int Metrics::kMetricTimeToDropSecondsMin = 1;
44
Thieu Leb84ba342012-03-02 15:15:19 -080045const char Metrics::kMetricTimeResumeToReadyMilliseconds[] =
46 "Network.Shill.%s.TimeResumeToReady";
Thieu Le48e6d6d2011-12-06 00:40:27 +000047const char Metrics::kMetricTimeToConfigMilliseconds[] =
48 "Network.Shill.%s.TimeToConfig";
49const char Metrics::kMetricTimeToJoinMilliseconds[] =
50 "Network.Shill.%s.TimeToJoin";
51const char Metrics::kMetricTimeToOnlineMilliseconds[] =
52 "Network.Shill.%s.TimeToOnline";
53const char Metrics::kMetricTimeToPortalMilliseconds[] =
54 "Network.Shill.%s.TimeToPortal";
Thieu Lea20cbc22012-01-09 22:01:43 +000055const int Metrics::kTimerHistogramMillisecondsMax = 45 * 1000;
56const int Metrics::kTimerHistogramMillisecondsMin = 1;
Thieu Le48e6d6d2011-12-06 00:40:27 +000057const int Metrics::kTimerHistogramNumBuckets = 50;
58
Thieu Le85e050b2012-03-13 15:04:38 -070059const char Metrics::kMetricPortalAttempts[] =
60 "Network.Shill.%s.PortalAttempts";
61const int Metrics::kMetricPortalAttemptsMax =
62 PortalDetector::kMaxRequestAttempts;
63const int Metrics::kMetricPortalAttemptsMin = 1;
64const int Metrics::kMetricPortalAttemptsNumBuckets =
65 Metrics::kMetricPortalAttemptsMax;
66
67const char Metrics::kMetricPortalAttemptsToOnline[] =
68 "Network.Shill.%s.PortalAttemptsToOnline";
69const int Metrics::kMetricPortalAttemptsToOnlineMax = 100;
70const int Metrics::kMetricPortalAttemptsToOnlineMin = 1;
71const int Metrics::kMetricPortalAttemptsToOnlineNumBuckets = 10;
72
73const char Metrics::kMetricPortalResult[] = "Network.Shill.%s.PortalResult";
74
Thieu Le48e6d6d2011-12-06 00:40:27 +000075// static
76const uint16 Metrics::kWiFiBandwidth5MHz = 5;
77const uint16 Metrics::kWiFiBandwidth20MHz = 20;
78const uint16 Metrics::kWiFiFrequency2412 = 2412;
79const uint16 Metrics::kWiFiFrequency2472 = 2472;
80const uint16 Metrics::kWiFiFrequency2484 = 2484;
81const uint16 Metrics::kWiFiFrequency5170 = 5170;
82const uint16 Metrics::kWiFiFrequency5180 = 5180;
83const uint16 Metrics::kWiFiFrequency5230 = 5230;
84const uint16 Metrics::kWiFiFrequency5240 = 5240;
85const uint16 Metrics::kWiFiFrequency5320 = 5320;
86const uint16 Metrics::kWiFiFrequency5500 = 5500;
87const uint16 Metrics::kWiFiFrequency5700 = 5700;
88const uint16 Metrics::kWiFiFrequency5745 = 5745;
89const uint16 Metrics::kWiFiFrequency5825 = 5825;
90
Thieu Leb84ba342012-03-02 15:15:19 -080091// static
92const char Metrics::kMetricPowerManagerKey[] = "metrics";
93
Thieu Lea20cbc22012-01-09 22:01:43 +000094Metrics::Metrics()
95 : library_(&metrics_library_),
96 last_default_technology_(Technology::kUnknown),
97 was_online_(false),
98 time_online_timer_(new chromeos_metrics::Timer),
Thieu Leb84ba342012-03-02 15:15:19 -080099 time_to_drop_timer_(new chromeos_metrics::Timer),
100 time_resume_to_ready_timer_(new chromeos_metrics::Timer) {
Thieu Le48e6d6d2011-12-06 00:40:27 +0000101 metrics_library_.Init();
102 chromeos_metrics::TimerReporter::set_metrics_lib(library_);
103}
104
105Metrics::~Metrics() {}
106
107// static
Thieu Le48e6d6d2011-12-06 00:40:27 +0000108Metrics::WiFiChannel Metrics::WiFiFrequencyToChannel(uint16 frequency) {
109 WiFiChannel channel = kWiFiChannelUndef;
110 if (kWiFiFrequency2412 <= frequency && frequency <= kWiFiFrequency2472) {
111 if (((frequency - kWiFiFrequency2412) % kWiFiBandwidth5MHz) == 0)
112 channel = static_cast<WiFiChannel>(
113 kWiFiChannel2412 +
114 (frequency - kWiFiFrequency2412) / kWiFiBandwidth5MHz);
115 } else if (frequency == kWiFiFrequency2484) {
116 channel = kWiFiChannel2484;
117 } else if (kWiFiFrequency5170 <= frequency &&
118 frequency <= kWiFiFrequency5230) {
119 if ((frequency % kWiFiBandwidth20MHz) == 0)
120 channel = static_cast<WiFiChannel>(
121 kWiFiChannel5180 +
122 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
123 if ((frequency % kWiFiBandwidth20MHz) == 10)
124 channel = static_cast<WiFiChannel>(
125 kWiFiChannel5170 +
126 (frequency - kWiFiFrequency5170) / kWiFiBandwidth20MHz);
127 } else if (kWiFiFrequency5240 <= frequency &&
128 frequency <= kWiFiFrequency5320) {
129 if (((frequency - kWiFiFrequency5180) % kWiFiBandwidth20MHz) == 0)
130 channel = static_cast<WiFiChannel>(
131 kWiFiChannel5180 +
132 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
133 } else if (kWiFiFrequency5500 <= frequency &&
134 frequency <= kWiFiFrequency5700) {
135 if (((frequency - kWiFiFrequency5500) % kWiFiBandwidth20MHz) == 0)
136 channel = static_cast<WiFiChannel>(
137 kWiFiChannel5500 +
138 (frequency - kWiFiFrequency5500) / kWiFiBandwidth20MHz);
139 } else if (kWiFiFrequency5745 <= frequency &&
140 frequency <= kWiFiFrequency5825) {
141 if (((frequency - kWiFiFrequency5745) % kWiFiBandwidth20MHz) == 0)
142 channel = static_cast<WiFiChannel>(
143 kWiFiChannel5745 +
144 (frequency - kWiFiFrequency5745) / kWiFiBandwidth20MHz);
145 }
146 CHECK(kWiFiChannelUndef <= channel && channel < kWiFiChannelMax);
147
148 if (channel == kWiFiChannelUndef)
149 LOG(WARNING) << "no mapping for frequency " << frequency;
150 else
Ben Chanfad4a0b2012-04-18 15:49:59 -0700151 SLOG(Metrics, 3) << "map " << frequency << " to " << channel;
Thieu Le48e6d6d2011-12-06 00:40:27 +0000152
153 return channel;
154}
155
Thieu Lead1ec2c2012-01-05 23:39:48 +0000156// static
157Metrics::WiFiSecurity Metrics::WiFiSecurityStringToEnum(
158 const std::string &security) {
159 if (security == flimflam::kSecurityNone) {
160 return kWiFiSecurityNone;
161 } else if (security == flimflam::kSecurityWep) {
162 return kWiFiSecurityWep;
163 } else if (security == flimflam::kSecurityWpa) {
164 return kWiFiSecurityWpa;
165 } else if (security == flimflam::kSecurityRsn) {
166 return kWiFiSecurityRsn;
167 } else if (security == flimflam::kSecurity8021x) {
168 return kWiFiSecurity8021x;
169 } else if (security == flimflam::kSecurityPsk) {
170 return kWiFiSecurityPsk;
171 } else {
172 return kWiFiSecurityUnknown;
173 }
174}
175
Thieu Le85e050b2012-03-13 15:04:38 -0700176// static
177Metrics::PortalResult Metrics::PortalDetectionResultToEnum(
178 const PortalDetector::Result &result) {
179 DCHECK(result.final);
180 PortalResult retval = kPortalResultUnknown;
181 // The only time we should end a successful portal detection is when we're
182 // in the Content phase. If we end with kStatusSuccess in any other phase,
183 // then this indicates that something bad has happened.
184 switch (result.phase) {
185 case PortalDetector::kPhaseDNS:
186 if (result.status == PortalDetector::kStatusFailure)
187 retval = kPortalResultDNSFailure;
188 else if (result.status == PortalDetector::kStatusTimeout)
189 retval = kPortalResultDNSTimeout;
190 else
191 LOG(DFATAL) << __func__ << ": Final result status " << result.status
192 << " is not allowed in the DNS phase";
193 break;
194
195 case PortalDetector::kPhaseConnection:
196 if (result.status == PortalDetector::kStatusFailure)
197 retval = kPortalResultConnectionFailure;
198 else if (result.status == PortalDetector::kStatusTimeout)
199 retval = kPortalResultConnectionTimeout;
200 else
201 LOG(DFATAL) << __func__ << ": Final result status " << result.status
202 << " is not allowed in the Connection phase";
203 break;
204
205 case PortalDetector::kPhaseHTTP:
206 if (result.status == PortalDetector::kStatusFailure)
207 retval = kPortalResultHTTPFailure;
208 else if (result.status == PortalDetector::kStatusTimeout)
209 retval = kPortalResultHTTPTimeout;
210 else
211 LOG(DFATAL) << __func__ << ": Final result status " << result.status
212 << " is not allowed in the HTTP phase";
213 break;
214
215 case PortalDetector::kPhaseContent:
216 if (result.status == PortalDetector::kStatusSuccess)
217 retval = kPortalResultSuccess;
218 else if (result.status == PortalDetector::kStatusFailure)
219 retval = kPortalResultContentFailure;
220 else if (result.status == PortalDetector::kStatusTimeout)
221 retval = kPortalResultContentTimeout;
222 else
223 LOG(DFATAL) << __func__ << ": Final result status " << result.status
224 << " is not allowed in the Content phase";
225 break;
226
227 case PortalDetector::kPhaseUnknown:
228 retval = kPortalResultUnknown;
229 break;
230
231 default:
232 LOG(DFATAL) << __func__ << ": Invalid phase " << result.phase;
233 break;
234 }
235
236 return retval;
237}
238
Thieu Le48e6d6d2011-12-06 00:40:27 +0000239void Metrics::RegisterService(const Service *service) {
240 shared_ptr<ServiceMetrics> service_metrics(new ServiceMetrics);
241 services_metrics_[service] = service_metrics;
242 service_metrics->service = service;
243 InitializeCommonServiceMetrics(service);
244 service->InitializeCustomMetrics();
245}
246
247void Metrics::DeregisterService(const Service *service) {
248 services_metrics_.erase(service);
249}
250
251void Metrics::AddServiceStateTransitionTimer(
252 const Service *service,
253 const string &histogram_name,
254 Service::ConnectState start_state,
255 Service::ConnectState stop_state) {
256 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
257 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700258 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000259 DCHECK(false);
260 return;
261 }
262 ServiceMetrics *service_metrics = it->second.get();
263 CHECK(start_state < stop_state);
264 chromeos_metrics::TimerReporter *timer =
265 new chromeos_metrics::TimerReporter(histogram_name,
Thieu Lea20cbc22012-01-09 22:01:43 +0000266 kTimerHistogramMillisecondsMin,
267 kTimerHistogramMillisecondsMax,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000268 kTimerHistogramNumBuckets);
269 service_metrics->timers.push_back(timer); // passes ownership.
270 service_metrics->start_on_state[start_state].push_back(timer);
271 service_metrics->stop_on_state[stop_state].push_back(timer);
272}
273
Thieu Lea20cbc22012-01-09 22:01:43 +0000274void Metrics::NotifyDefaultServiceChanged(const Service *service) {
275 base::TimeDelta elapsed_seconds;
276
277 Technology::Identifier technology = (service) ? service->technology() :
278 Technology::kUnknown;
279 if (technology != last_default_technology_) {
280 if (last_default_technology_ != Technology::kUnknown) {
281 string histogram = GetFullMetricName(kMetricTimeOnlineSeconds,
282 last_default_technology_);
283 time_online_timer_->GetElapsedTime(&elapsed_seconds);
284 SendToUMA(histogram,
285 elapsed_seconds.InSeconds(),
286 kMetricTimeOnlineSecondsMin,
287 kMetricTimeOnlineSecondsMax,
288 kTimerHistogramNumBuckets);
289 }
290 last_default_technology_ = technology;
291 time_online_timer_->Start();
292 }
293
Thieu Lea20cbc22012-01-09 22:01:43 +0000294 // Ignore changes that are not online/offline transitions; e.g.
295 // switching between wired and wireless. TimeToDrop measures
296 // time online regardless of how we are connected.
297 if ((service == NULL && !was_online_) || (service != NULL && was_online_))
298 return;
299
300 if (service == NULL) {
301 time_to_drop_timer_->GetElapsedTime(&elapsed_seconds);
302 SendToUMA(kMetricTimeToDropSeconds,
303 elapsed_seconds.InSeconds(),
304 kMetricTimeToDropSecondsMin,
305 kMetricTimeToDropSecondsMax,
306 kTimerHistogramNumBuckets);
307 } else {
308 time_to_drop_timer_->Start();
309 }
310
311 was_online_ = (service != NULL);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000312}
313
314void Metrics::NotifyServiceStateChanged(const Service *service,
315 Service::ConnectState new_state) {
316 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
317 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700318 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000319 DCHECK(false);
320 return;
321 }
322 ServiceMetrics *service_metrics = it->second.get();
323 UpdateServiceStateTransitionMetrics(service_metrics, new_state);
324
325 if (new_state == Service::kStateFailure)
326 SendServiceFailure(service);
327
Paul Stewart20088d82012-02-16 06:58:55 -0800328 if (new_state != Service::kStateConnected)
Thieu Le48e6d6d2011-12-06 00:40:27 +0000329 return;
330
Thieu Leb84ba342012-03-02 15:15:19 -0800331 base::TimeDelta time_resume_to_ready;
332 time_resume_to_ready_timer_->GetElapsedTime(&time_resume_to_ready);
333 time_resume_to_ready_timer_->Reset();
334 service->SendPostReadyStateMetrics(time_resume_to_ready.InMilliseconds());
Thieu Le48e6d6d2011-12-06 00:40:27 +0000335}
336
337string Metrics::GetFullMetricName(const char *metric_name,
338 Technology::Identifier technology_id) {
339 string technology = Technology::NameFromIdentifier(technology_id);
340 technology[0] = base::ToUpperASCII(technology[0]);
341 return base::StringPrintf(metric_name, technology.c_str());
342}
343
Thieu Le67370f62012-02-14 23:01:42 +0000344void Metrics::NotifyServiceDisconnect(const Service *service) {
345 Technology::Identifier technology = service->technology();
346 string histogram = GetFullMetricName(kMetricDisconnect, technology);
347 SendToUMA(histogram,
348 service->explicitly_disconnected(),
349 kMetricDisconnectMin,
350 kMetricDisconnectMax,
351 kMetricDisconnectNumBuckets);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000352}
353
Thieu Leb84ba342012-03-02 15:15:19 -0800354void Metrics::NotifyPowerStateChange(PowerManager::SuspendState new_state) {
355 if (new_state == PowerManagerProxyDelegate::kOn) {
356 time_resume_to_ready_timer_->Start();
357 } else {
358 time_resume_to_ready_timer_->Reset();
359 }
Thieu Le48e6d6d2011-12-06 00:40:27 +0000360}
361
362bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
363 return library_->SendEnumToUMA(name, sample, max);
364}
365
Thieu Lea20cbc22012-01-09 22:01:43 +0000366bool Metrics::SendToUMA(const string &name, int sample, int min, int max,
367 int num_buckets) {
368 return library_->SendToUMA(name, sample, min, max, num_buckets);
369}
370
Thieu Le48e6d6d2011-12-06 00:40:27 +0000371void Metrics::InitializeCommonServiceMetrics(const Service *service) {
372 Technology::Identifier technology = service->technology();
373 string histogram = GetFullMetricName(kMetricTimeToConfigMilliseconds,
374 technology);
375 AddServiceStateTransitionTimer(
376 service,
377 histogram,
378 Service::kStateConfiguring,
Paul Stewart20088d82012-02-16 06:58:55 -0800379 Service::kStateConnected);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000380 histogram = GetFullMetricName(kMetricTimeToPortalMilliseconds, technology);
381 AddServiceStateTransitionTimer(
382 service,
383 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800384 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000385 Service::kStatePortal);
386 histogram = GetFullMetricName(kMetricTimeToOnlineMilliseconds, technology);
387 AddServiceStateTransitionTimer(
388 service,
389 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800390 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000391 Service::kStateOnline);
392}
393
394void Metrics::UpdateServiceStateTransitionMetrics(
395 ServiceMetrics *service_metrics,
396 Service::ConnectState new_state) {
397 TimerReportersList::iterator it;
398 TimerReportersList &start_timers = service_metrics->start_on_state[new_state];
399 for (it = start_timers.begin(); it != start_timers.end(); ++it)
400 (*it)->Start();
401
402 TimerReportersList &stop_timers = service_metrics->stop_on_state[new_state];
403 for (it = stop_timers.begin(); it != stop_timers.end(); ++it) {
404 (*it)->Stop();
405 (*it)->ReportMilliseconds();
406 }
407}
408
409void Metrics::SendServiceFailure(const Service *service) {
410 library_->SendEnumToUMA(kMetricNetworkServiceErrors,
411 service->failure(),
412 kMetricNetworkServiceErrorsMax);
413}
414
415void Metrics::set_library(MetricsLibraryInterface *library) {
416 chromeos_metrics::TimerReporter::set_metrics_lib(library);
417 library_ = library;
418}
419
420} // namespace shill