blob: 57de815543e81dd3e65bd5ab2be9262f07057ab2 [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
Thieu Le48e6d6d2011-12-06 00:40:27 +00007#include <base/logging.h>
8#include <base/string_util.h>
9#include <base/stringprintf.h>
Thieu Lead1ec2c2012-01-05 23:39:48 +000010#include <chromeos/dbus/service_constants.h>
Thieu Le48e6d6d2011-12-06 00:40:27 +000011
Ben Chanfad4a0b2012-04-18 15:49:59 -070012#include "shill/scope_logger.h"
Thieu Le48e6d6d2011-12-06 00:40:27 +000013#include "shill/wifi_service.h"
14
15using std::string;
16using std::tr1::shared_ptr;
17
18namespace shill {
19
Thieu Le48e6d6d2011-12-06 00:40:27 +000020// static
Thieu Le67370f62012-02-14 23:01:42 +000021const char Metrics::kMetricDisconnect[] = "Network.Shill.%s.Disconnect";
22const int Metrics::kMetricDisconnectMax = 1;
23const int Metrics::kMetricDisconnectMin = 0;
24const int Metrics::kMetricDisconnectNumBuckets = 2;
25
Thieu Le48e6d6d2011-12-06 00:40:27 +000026const char Metrics::kMetricNetworkChannel[] = "Network.Shill.%s.Channel";
27const int Metrics::kMetricNetworkChannelMax = Metrics::kWiFiChannelMax;
Thieu Lead1ec2c2012-01-05 23:39:48 +000028const char Metrics::kMetricNetworkPhyMode[] = "Network.Shill.%s.PhyMode";
29const int Metrics::kMetricNetworkPhyModeMax = Metrics::kWiFiNetworkPhyModeMax;
30const char Metrics::kMetricNetworkSecurity[] = "Network.Shill.%s.Security";
31const int Metrics::kMetricNetworkSecurityMax = Metrics::kWiFiSecurityMax;
Thieu Le48e6d6d2011-12-06 00:40:27 +000032const char Metrics::kMetricNetworkServiceErrors[] =
33 "Network.Shill.ServiceErrors";
34const int Metrics::kMetricNetworkServiceErrorsMax = Service::kFailureMax;
Thieu Lea20cbc22012-01-09 22:01:43 +000035
36const char Metrics::kMetricTimeOnlineSeconds[] = "Network.Shill.%s.TimeOnline";
37const int Metrics::kMetricTimeOnlineSecondsMax = 8 * 60 * 60; // 8 hours
38const int Metrics::kMetricTimeOnlineSecondsMin = 1;
39
40const char Metrics::kMetricTimeToDropSeconds[] = "Network.Shill.TimeToDrop";;
41const int Metrics::kMetricTimeToDropSecondsMax = 8 * 60 * 60; // 8 hours
42const int Metrics::kMetricTimeToDropSecondsMin = 1;
43
Thieu Leb84ba342012-03-02 15:15:19 -080044const char Metrics::kMetricTimeResumeToReadyMilliseconds[] =
45 "Network.Shill.%s.TimeResumeToReady";
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
Thieu Le85e050b2012-03-13 15:04:38 -070058const char Metrics::kMetricPortalAttempts[] =
59 "Network.Shill.%s.PortalAttempts";
60const int Metrics::kMetricPortalAttemptsMax =
61 PortalDetector::kMaxRequestAttempts;
62const int Metrics::kMetricPortalAttemptsMin = 1;
63const int Metrics::kMetricPortalAttemptsNumBuckets =
64 Metrics::kMetricPortalAttemptsMax;
65
66const char Metrics::kMetricPortalAttemptsToOnline[] =
67 "Network.Shill.%s.PortalAttemptsToOnline";
68const int Metrics::kMetricPortalAttemptsToOnlineMax = 100;
69const int Metrics::kMetricPortalAttemptsToOnlineMin = 1;
70const int Metrics::kMetricPortalAttemptsToOnlineNumBuckets = 10;
71
72const char Metrics::kMetricPortalResult[] = "Network.Shill.%s.PortalResult";
73
Thieu Le48e6d6d2011-12-06 00:40:27 +000074// static
75const uint16 Metrics::kWiFiBandwidth5MHz = 5;
76const uint16 Metrics::kWiFiBandwidth20MHz = 20;
77const uint16 Metrics::kWiFiFrequency2412 = 2412;
78const uint16 Metrics::kWiFiFrequency2472 = 2472;
79const uint16 Metrics::kWiFiFrequency2484 = 2484;
80const uint16 Metrics::kWiFiFrequency5170 = 5170;
81const uint16 Metrics::kWiFiFrequency5180 = 5180;
82const uint16 Metrics::kWiFiFrequency5230 = 5230;
83const uint16 Metrics::kWiFiFrequency5240 = 5240;
84const uint16 Metrics::kWiFiFrequency5320 = 5320;
85const uint16 Metrics::kWiFiFrequency5500 = 5500;
86const uint16 Metrics::kWiFiFrequency5700 = 5700;
87const uint16 Metrics::kWiFiFrequency5745 = 5745;
88const uint16 Metrics::kWiFiFrequency5825 = 5825;
89
Thieu Leb84ba342012-03-02 15:15:19 -080090// static
91const char Metrics::kMetricPowerManagerKey[] = "metrics";
92
Thieu Lea20cbc22012-01-09 22:01:43 +000093Metrics::Metrics()
94 : library_(&metrics_library_),
95 last_default_technology_(Technology::kUnknown),
96 was_online_(false),
97 time_online_timer_(new chromeos_metrics::Timer),
Thieu Leb84ba342012-03-02 15:15:19 -080098 time_to_drop_timer_(new chromeos_metrics::Timer),
99 time_resume_to_ready_timer_(new chromeos_metrics::Timer) {
Thieu Le48e6d6d2011-12-06 00:40:27 +0000100 metrics_library_.Init();
101 chromeos_metrics::TimerReporter::set_metrics_lib(library_);
102}
103
104Metrics::~Metrics() {}
105
106// static
Thieu Le48e6d6d2011-12-06 00:40:27 +0000107Metrics::WiFiChannel Metrics::WiFiFrequencyToChannel(uint16 frequency) {
108 WiFiChannel channel = kWiFiChannelUndef;
109 if (kWiFiFrequency2412 <= frequency && frequency <= kWiFiFrequency2472) {
110 if (((frequency - kWiFiFrequency2412) % kWiFiBandwidth5MHz) == 0)
111 channel = static_cast<WiFiChannel>(
112 kWiFiChannel2412 +
113 (frequency - kWiFiFrequency2412) / kWiFiBandwidth5MHz);
114 } else if (frequency == kWiFiFrequency2484) {
115 channel = kWiFiChannel2484;
116 } else if (kWiFiFrequency5170 <= frequency &&
117 frequency <= kWiFiFrequency5230) {
118 if ((frequency % kWiFiBandwidth20MHz) == 0)
119 channel = static_cast<WiFiChannel>(
120 kWiFiChannel5180 +
121 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
122 if ((frequency % kWiFiBandwidth20MHz) == 10)
123 channel = static_cast<WiFiChannel>(
124 kWiFiChannel5170 +
125 (frequency - kWiFiFrequency5170) / kWiFiBandwidth20MHz);
126 } else if (kWiFiFrequency5240 <= frequency &&
127 frequency <= kWiFiFrequency5320) {
128 if (((frequency - kWiFiFrequency5180) % kWiFiBandwidth20MHz) == 0)
129 channel = static_cast<WiFiChannel>(
130 kWiFiChannel5180 +
131 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
132 } else if (kWiFiFrequency5500 <= frequency &&
133 frequency <= kWiFiFrequency5700) {
134 if (((frequency - kWiFiFrequency5500) % kWiFiBandwidth20MHz) == 0)
135 channel = static_cast<WiFiChannel>(
136 kWiFiChannel5500 +
137 (frequency - kWiFiFrequency5500) / kWiFiBandwidth20MHz);
138 } else if (kWiFiFrequency5745 <= frequency &&
139 frequency <= kWiFiFrequency5825) {
140 if (((frequency - kWiFiFrequency5745) % kWiFiBandwidth20MHz) == 0)
141 channel = static_cast<WiFiChannel>(
142 kWiFiChannel5745 +
143 (frequency - kWiFiFrequency5745) / kWiFiBandwidth20MHz);
144 }
145 CHECK(kWiFiChannelUndef <= channel && channel < kWiFiChannelMax);
146
147 if (channel == kWiFiChannelUndef)
148 LOG(WARNING) << "no mapping for frequency " << frequency;
149 else
Ben Chanfad4a0b2012-04-18 15:49:59 -0700150 SLOG(Metrics, 3) << "map " << frequency << " to " << channel;
Thieu Le48e6d6d2011-12-06 00:40:27 +0000151
152 return channel;
153}
154
Thieu Lead1ec2c2012-01-05 23:39:48 +0000155// static
156Metrics::WiFiSecurity Metrics::WiFiSecurityStringToEnum(
157 const std::string &security) {
158 if (security == flimflam::kSecurityNone) {
159 return kWiFiSecurityNone;
160 } else if (security == flimflam::kSecurityWep) {
161 return kWiFiSecurityWep;
162 } else if (security == flimflam::kSecurityWpa) {
163 return kWiFiSecurityWpa;
164 } else if (security == flimflam::kSecurityRsn) {
165 return kWiFiSecurityRsn;
166 } else if (security == flimflam::kSecurity8021x) {
167 return kWiFiSecurity8021x;
168 } else if (security == flimflam::kSecurityPsk) {
169 return kWiFiSecurityPsk;
170 } else {
171 return kWiFiSecurityUnknown;
172 }
173}
174
Thieu Le85e050b2012-03-13 15:04:38 -0700175// static
176Metrics::PortalResult Metrics::PortalDetectionResultToEnum(
177 const PortalDetector::Result &result) {
178 DCHECK(result.final);
179 PortalResult retval = kPortalResultUnknown;
180 // The only time we should end a successful portal detection is when we're
181 // in the Content phase. If we end with kStatusSuccess in any other phase,
182 // then this indicates that something bad has happened.
183 switch (result.phase) {
184 case PortalDetector::kPhaseDNS:
185 if (result.status == PortalDetector::kStatusFailure)
186 retval = kPortalResultDNSFailure;
187 else if (result.status == PortalDetector::kStatusTimeout)
188 retval = kPortalResultDNSTimeout;
189 else
190 LOG(DFATAL) << __func__ << ": Final result status " << result.status
191 << " is not allowed in the DNS phase";
192 break;
193
194 case PortalDetector::kPhaseConnection:
195 if (result.status == PortalDetector::kStatusFailure)
196 retval = kPortalResultConnectionFailure;
197 else if (result.status == PortalDetector::kStatusTimeout)
198 retval = kPortalResultConnectionTimeout;
199 else
200 LOG(DFATAL) << __func__ << ": Final result status " << result.status
201 << " is not allowed in the Connection phase";
202 break;
203
204 case PortalDetector::kPhaseHTTP:
205 if (result.status == PortalDetector::kStatusFailure)
206 retval = kPortalResultHTTPFailure;
207 else if (result.status == PortalDetector::kStatusTimeout)
208 retval = kPortalResultHTTPTimeout;
209 else
210 LOG(DFATAL) << __func__ << ": Final result status " << result.status
211 << " is not allowed in the HTTP phase";
212 break;
213
214 case PortalDetector::kPhaseContent:
215 if (result.status == PortalDetector::kStatusSuccess)
216 retval = kPortalResultSuccess;
217 else if (result.status == PortalDetector::kStatusFailure)
218 retval = kPortalResultContentFailure;
219 else if (result.status == PortalDetector::kStatusTimeout)
220 retval = kPortalResultContentTimeout;
221 else
222 LOG(DFATAL) << __func__ << ": Final result status " << result.status
223 << " is not allowed in the Content phase";
224 break;
225
226 case PortalDetector::kPhaseUnknown:
227 retval = kPortalResultUnknown;
228 break;
229
230 default:
231 LOG(DFATAL) << __func__ << ": Invalid phase " << result.phase;
232 break;
233 }
234
235 return retval;
236}
237
Thieu Le48e6d6d2011-12-06 00:40:27 +0000238void Metrics::RegisterService(const Service *service) {
239 shared_ptr<ServiceMetrics> service_metrics(new ServiceMetrics);
240 services_metrics_[service] = service_metrics;
241 service_metrics->service = service;
242 InitializeCommonServiceMetrics(service);
243 service->InitializeCustomMetrics();
244}
245
246void Metrics::DeregisterService(const Service *service) {
247 services_metrics_.erase(service);
248}
249
250void Metrics::AddServiceStateTransitionTimer(
251 const Service *service,
252 const string &histogram_name,
253 Service::ConnectState start_state,
254 Service::ConnectState stop_state) {
255 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
256 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700257 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000258 DCHECK(false);
259 return;
260 }
261 ServiceMetrics *service_metrics = it->second.get();
262 CHECK(start_state < stop_state);
263 chromeos_metrics::TimerReporter *timer =
264 new chromeos_metrics::TimerReporter(histogram_name,
Thieu Lea20cbc22012-01-09 22:01:43 +0000265 kTimerHistogramMillisecondsMin,
266 kTimerHistogramMillisecondsMax,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000267 kTimerHistogramNumBuckets);
268 service_metrics->timers.push_back(timer); // passes ownership.
269 service_metrics->start_on_state[start_state].push_back(timer);
270 service_metrics->stop_on_state[stop_state].push_back(timer);
271}
272
Thieu Lea20cbc22012-01-09 22:01:43 +0000273void Metrics::NotifyDefaultServiceChanged(const Service *service) {
274 base::TimeDelta elapsed_seconds;
275
276 Technology::Identifier technology = (service) ? service->technology() :
277 Technology::kUnknown;
278 if (technology != last_default_technology_) {
279 if (last_default_technology_ != Technology::kUnknown) {
280 string histogram = GetFullMetricName(kMetricTimeOnlineSeconds,
281 last_default_technology_);
282 time_online_timer_->GetElapsedTime(&elapsed_seconds);
283 SendToUMA(histogram,
284 elapsed_seconds.InSeconds(),
285 kMetricTimeOnlineSecondsMin,
286 kMetricTimeOnlineSecondsMax,
287 kTimerHistogramNumBuckets);
288 }
289 last_default_technology_ = technology;
290 time_online_timer_->Start();
291 }
292
Thieu Lea20cbc22012-01-09 22:01:43 +0000293 // Ignore changes that are not online/offline transitions; e.g.
294 // switching between wired and wireless. TimeToDrop measures
295 // time online regardless of how we are connected.
296 if ((service == NULL && !was_online_) || (service != NULL && was_online_))
297 return;
298
299 if (service == NULL) {
300 time_to_drop_timer_->GetElapsedTime(&elapsed_seconds);
301 SendToUMA(kMetricTimeToDropSeconds,
302 elapsed_seconds.InSeconds(),
303 kMetricTimeToDropSecondsMin,
304 kMetricTimeToDropSecondsMax,
305 kTimerHistogramNumBuckets);
306 } else {
307 time_to_drop_timer_->Start();
308 }
309
310 was_online_ = (service != NULL);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000311}
312
313void Metrics::NotifyServiceStateChanged(const Service *service,
314 Service::ConnectState new_state) {
315 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
316 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700317 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000318 DCHECK(false);
319 return;
320 }
321 ServiceMetrics *service_metrics = it->second.get();
322 UpdateServiceStateTransitionMetrics(service_metrics, new_state);
323
324 if (new_state == Service::kStateFailure)
325 SendServiceFailure(service);
326
Paul Stewart20088d82012-02-16 06:58:55 -0800327 if (new_state != Service::kStateConnected)
Thieu Le48e6d6d2011-12-06 00:40:27 +0000328 return;
329
Thieu Leb84ba342012-03-02 15:15:19 -0800330 base::TimeDelta time_resume_to_ready;
331 time_resume_to_ready_timer_->GetElapsedTime(&time_resume_to_ready);
332 time_resume_to_ready_timer_->Reset();
333 service->SendPostReadyStateMetrics(time_resume_to_ready.InMilliseconds());
Thieu Le48e6d6d2011-12-06 00:40:27 +0000334}
335
336string Metrics::GetFullMetricName(const char *metric_name,
337 Technology::Identifier technology_id) {
338 string technology = Technology::NameFromIdentifier(technology_id);
339 technology[0] = base::ToUpperASCII(technology[0]);
340 return base::StringPrintf(metric_name, technology.c_str());
341}
342
Thieu Le67370f62012-02-14 23:01:42 +0000343void Metrics::NotifyServiceDisconnect(const Service *service) {
344 Technology::Identifier technology = service->technology();
345 string histogram = GetFullMetricName(kMetricDisconnect, technology);
346 SendToUMA(histogram,
347 service->explicitly_disconnected(),
348 kMetricDisconnectMin,
349 kMetricDisconnectMax,
350 kMetricDisconnectNumBuckets);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000351}
352
Thieu Leb84ba342012-03-02 15:15:19 -0800353void Metrics::NotifyPowerStateChange(PowerManager::SuspendState new_state) {
354 if (new_state == PowerManagerProxyDelegate::kOn) {
355 time_resume_to_ready_timer_->Start();
356 } else {
357 time_resume_to_ready_timer_->Reset();
358 }
Thieu Le48e6d6d2011-12-06 00:40:27 +0000359}
360
361bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
362 return library_->SendEnumToUMA(name, sample, max);
363}
364
Thieu Lea20cbc22012-01-09 22:01:43 +0000365bool Metrics::SendToUMA(const string &name, int sample, int min, int max,
366 int num_buckets) {
367 return library_->SendToUMA(name, sample, min, max, num_buckets);
368}
369
Thieu Le48e6d6d2011-12-06 00:40:27 +0000370void Metrics::InitializeCommonServiceMetrics(const Service *service) {
371 Technology::Identifier technology = service->technology();
372 string histogram = GetFullMetricName(kMetricTimeToConfigMilliseconds,
373 technology);
374 AddServiceStateTransitionTimer(
375 service,
376 histogram,
377 Service::kStateConfiguring,
Paul Stewart20088d82012-02-16 06:58:55 -0800378 Service::kStateConnected);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000379 histogram = GetFullMetricName(kMetricTimeToPortalMilliseconds, technology);
380 AddServiceStateTransitionTimer(
381 service,
382 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800383 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000384 Service::kStatePortal);
385 histogram = GetFullMetricName(kMetricTimeToOnlineMilliseconds, technology);
386 AddServiceStateTransitionTimer(
387 service,
388 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800389 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000390 Service::kStateOnline);
391}
392
393void Metrics::UpdateServiceStateTransitionMetrics(
394 ServiceMetrics *service_metrics,
395 Service::ConnectState new_state) {
396 TimerReportersList::iterator it;
397 TimerReportersList &start_timers = service_metrics->start_on_state[new_state];
398 for (it = start_timers.begin(); it != start_timers.end(); ++it)
399 (*it)->Start();
400
401 TimerReportersList &stop_timers = service_metrics->stop_on_state[new_state];
402 for (it = stop_timers.begin(); it != stop_timers.end(); ++it) {
403 (*it)->Stop();
404 (*it)->ReportMilliseconds();
405 }
406}
407
408void Metrics::SendServiceFailure(const Service *service) {
409 library_->SendEnumToUMA(kMetricNetworkServiceErrors,
410 service->failure(),
411 kMetricNetworkServiceErrorsMax);
412}
413
414void Metrics::set_library(MetricsLibraryInterface *library) {
415 chromeos_metrics::TimerReporter::set_metrics_lib(library);
416 library_ = library;
417}
418
419} // namespace shill