blob: 85e37cd3a37c153763a2df391fc3f3ad7690f22b [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>
Darin Petkov58f0b6d2012-06-12 12:52:30 +020011#include <metrics/bootstat.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 Lec31e6f92012-08-03 13:08:58 -070022// Our disconnect enumeration values are 0 (System Disconnect) and
23// 1 (User Disconnect), see histograms.xml, but Chrome needs a minimum
24// enum value of 1 and the minimum number of buckets needs to be 3 (see
25// histogram.h). Instead of remapping System Disconnect to 1 and
26// User Disconnect to 2, we can just leave the enumerated values as-is
27// because Chrome implicitly creates a [0-1) bucket for us. Using Min=1,
28// Max=2 and NumBuckets=3 gives us the following three buckets:
29// [0-1), [1-2), [2-INT_MAX). We end up with an extra bucket [2-INT_MAX)
30// that we can safely ignore.
Thieu Le67370f62012-02-14 23:01:42 +000031const char Metrics::kMetricDisconnect[] = "Network.Shill.%s.Disconnect";
Thieu Lec31e6f92012-08-03 13:08:58 -070032const int Metrics::kMetricDisconnectMax = 2;
33const int Metrics::kMetricDisconnectMin = 1;
34const int Metrics::kMetricDisconnectNumBuckets = 3;
Thieu Le67370f62012-02-14 23:01:42 +000035
Thieu Le48e6d6d2011-12-06 00:40:27 +000036const char Metrics::kMetricNetworkChannel[] = "Network.Shill.%s.Channel";
37const int Metrics::kMetricNetworkChannelMax = Metrics::kWiFiChannelMax;
Thieu Lead1ec2c2012-01-05 23:39:48 +000038const char Metrics::kMetricNetworkPhyMode[] = "Network.Shill.%s.PhyMode";
39const int Metrics::kMetricNetworkPhyModeMax = Metrics::kWiFiNetworkPhyModeMax;
40const char Metrics::kMetricNetworkSecurity[] = "Network.Shill.%s.Security";
41const int Metrics::kMetricNetworkSecurityMax = Metrics::kWiFiSecurityMax;
Thieu Le48e6d6d2011-12-06 00:40:27 +000042const char Metrics::kMetricNetworkServiceErrors[] =
43 "Network.Shill.ServiceErrors";
44const int Metrics::kMetricNetworkServiceErrorsMax = Service::kFailureMax;
Thieu Lea20cbc22012-01-09 22:01:43 +000045
46const char Metrics::kMetricTimeOnlineSeconds[] = "Network.Shill.%s.TimeOnline";
47const int Metrics::kMetricTimeOnlineSecondsMax = 8 * 60 * 60; // 8 hours
48const int Metrics::kMetricTimeOnlineSecondsMin = 1;
49
50const char Metrics::kMetricTimeToDropSeconds[] = "Network.Shill.TimeToDrop";;
51const int Metrics::kMetricTimeToDropSecondsMax = 8 * 60 * 60; // 8 hours
52const int Metrics::kMetricTimeToDropSecondsMin = 1;
53
Thieu Leb84ba342012-03-02 15:15:19 -080054const char Metrics::kMetricTimeResumeToReadyMilliseconds[] =
55 "Network.Shill.%s.TimeResumeToReady";
Thieu Le48e6d6d2011-12-06 00:40:27 +000056const char Metrics::kMetricTimeToConfigMilliseconds[] =
57 "Network.Shill.%s.TimeToConfig";
58const char Metrics::kMetricTimeToJoinMilliseconds[] =
59 "Network.Shill.%s.TimeToJoin";
60const char Metrics::kMetricTimeToOnlineMilliseconds[] =
61 "Network.Shill.%s.TimeToOnline";
62const char Metrics::kMetricTimeToPortalMilliseconds[] =
63 "Network.Shill.%s.TimeToPortal";
Thieu Lea20cbc22012-01-09 22:01:43 +000064const int Metrics::kTimerHistogramMillisecondsMax = 45 * 1000;
65const int Metrics::kTimerHistogramMillisecondsMin = 1;
Thieu Le48e6d6d2011-12-06 00:40:27 +000066const int Metrics::kTimerHistogramNumBuckets = 50;
67
Thieu Le85e050b2012-03-13 15:04:38 -070068const char Metrics::kMetricPortalAttempts[] =
69 "Network.Shill.%s.PortalAttempts";
70const int Metrics::kMetricPortalAttemptsMax =
71 PortalDetector::kMaxRequestAttempts;
72const int Metrics::kMetricPortalAttemptsMin = 1;
73const int Metrics::kMetricPortalAttemptsNumBuckets =
74 Metrics::kMetricPortalAttemptsMax;
75
76const char Metrics::kMetricPortalAttemptsToOnline[] =
77 "Network.Shill.%s.PortalAttemptsToOnline";
78const int Metrics::kMetricPortalAttemptsToOnlineMax = 100;
79const int Metrics::kMetricPortalAttemptsToOnlineMin = 1;
80const int Metrics::kMetricPortalAttemptsToOnlineNumBuckets = 10;
81
82const char Metrics::kMetricPortalResult[] = "Network.Shill.%s.PortalResult";
83
Thieu Le48e6d6d2011-12-06 00:40:27 +000084// static
85const uint16 Metrics::kWiFiBandwidth5MHz = 5;
86const uint16 Metrics::kWiFiBandwidth20MHz = 20;
87const uint16 Metrics::kWiFiFrequency2412 = 2412;
88const uint16 Metrics::kWiFiFrequency2472 = 2472;
89const uint16 Metrics::kWiFiFrequency2484 = 2484;
90const uint16 Metrics::kWiFiFrequency5170 = 5170;
91const uint16 Metrics::kWiFiFrequency5180 = 5180;
92const uint16 Metrics::kWiFiFrequency5230 = 5230;
93const uint16 Metrics::kWiFiFrequency5240 = 5240;
94const uint16 Metrics::kWiFiFrequency5320 = 5320;
95const uint16 Metrics::kWiFiFrequency5500 = 5500;
96const uint16 Metrics::kWiFiFrequency5700 = 5700;
97const uint16 Metrics::kWiFiFrequency5745 = 5745;
98const uint16 Metrics::kWiFiFrequency5825 = 5825;
99
Thieu Leb84ba342012-03-02 15:15:19 -0800100// static
101const char Metrics::kMetricPowerManagerKey[] = "metrics";
102
Thieu Lea20cbc22012-01-09 22:01:43 +0000103Metrics::Metrics()
104 : library_(&metrics_library_),
105 last_default_technology_(Technology::kUnknown),
106 was_online_(false),
107 time_online_timer_(new chromeos_metrics::Timer),
Thieu Leb84ba342012-03-02 15:15:19 -0800108 time_to_drop_timer_(new chromeos_metrics::Timer),
Darin Petkov58f0b6d2012-06-12 12:52:30 +0200109 time_resume_to_ready_timer_(new chromeos_metrics::Timer),
110 collect_bootstats_(true) {
Thieu Le48e6d6d2011-12-06 00:40:27 +0000111 metrics_library_.Init();
112 chromeos_metrics::TimerReporter::set_metrics_lib(library_);
113}
114
115Metrics::~Metrics() {}
116
117// static
Thieu Le48e6d6d2011-12-06 00:40:27 +0000118Metrics::WiFiChannel Metrics::WiFiFrequencyToChannel(uint16 frequency) {
119 WiFiChannel channel = kWiFiChannelUndef;
120 if (kWiFiFrequency2412 <= frequency && frequency <= kWiFiFrequency2472) {
121 if (((frequency - kWiFiFrequency2412) % kWiFiBandwidth5MHz) == 0)
122 channel = static_cast<WiFiChannel>(
123 kWiFiChannel2412 +
124 (frequency - kWiFiFrequency2412) / kWiFiBandwidth5MHz);
125 } else if (frequency == kWiFiFrequency2484) {
126 channel = kWiFiChannel2484;
127 } else if (kWiFiFrequency5170 <= frequency &&
128 frequency <= kWiFiFrequency5230) {
129 if ((frequency % kWiFiBandwidth20MHz) == 0)
130 channel = static_cast<WiFiChannel>(
131 kWiFiChannel5180 +
132 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
133 if ((frequency % kWiFiBandwidth20MHz) == 10)
134 channel = static_cast<WiFiChannel>(
135 kWiFiChannel5170 +
136 (frequency - kWiFiFrequency5170) / kWiFiBandwidth20MHz);
137 } else if (kWiFiFrequency5240 <= frequency &&
138 frequency <= kWiFiFrequency5320) {
139 if (((frequency - kWiFiFrequency5180) % kWiFiBandwidth20MHz) == 0)
140 channel = static_cast<WiFiChannel>(
141 kWiFiChannel5180 +
142 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
143 } else if (kWiFiFrequency5500 <= frequency &&
144 frequency <= kWiFiFrequency5700) {
145 if (((frequency - kWiFiFrequency5500) % kWiFiBandwidth20MHz) == 0)
146 channel = static_cast<WiFiChannel>(
147 kWiFiChannel5500 +
148 (frequency - kWiFiFrequency5500) / kWiFiBandwidth20MHz);
149 } else if (kWiFiFrequency5745 <= frequency &&
150 frequency <= kWiFiFrequency5825) {
151 if (((frequency - kWiFiFrequency5745) % kWiFiBandwidth20MHz) == 0)
152 channel = static_cast<WiFiChannel>(
153 kWiFiChannel5745 +
154 (frequency - kWiFiFrequency5745) / kWiFiBandwidth20MHz);
155 }
156 CHECK(kWiFiChannelUndef <= channel && channel < kWiFiChannelMax);
157
158 if (channel == kWiFiChannelUndef)
159 LOG(WARNING) << "no mapping for frequency " << frequency;
160 else
Ben Chanfad4a0b2012-04-18 15:49:59 -0700161 SLOG(Metrics, 3) << "map " << frequency << " to " << channel;
Thieu Le48e6d6d2011-12-06 00:40:27 +0000162
163 return channel;
164}
165
Thieu Lead1ec2c2012-01-05 23:39:48 +0000166// static
167Metrics::WiFiSecurity Metrics::WiFiSecurityStringToEnum(
168 const std::string &security) {
169 if (security == flimflam::kSecurityNone) {
170 return kWiFiSecurityNone;
171 } else if (security == flimflam::kSecurityWep) {
172 return kWiFiSecurityWep;
173 } else if (security == flimflam::kSecurityWpa) {
174 return kWiFiSecurityWpa;
175 } else if (security == flimflam::kSecurityRsn) {
176 return kWiFiSecurityRsn;
177 } else if (security == flimflam::kSecurity8021x) {
178 return kWiFiSecurity8021x;
179 } else if (security == flimflam::kSecurityPsk) {
180 return kWiFiSecurityPsk;
181 } else {
182 return kWiFiSecurityUnknown;
183 }
184}
185
Thieu Le85e050b2012-03-13 15:04:38 -0700186// static
187Metrics::PortalResult Metrics::PortalDetectionResultToEnum(
188 const PortalDetector::Result &result) {
189 DCHECK(result.final);
190 PortalResult retval = kPortalResultUnknown;
191 // The only time we should end a successful portal detection is when we're
192 // in the Content phase. If we end with kStatusSuccess in any other phase,
193 // then this indicates that something bad has happened.
194 switch (result.phase) {
195 case PortalDetector::kPhaseDNS:
196 if (result.status == PortalDetector::kStatusFailure)
197 retval = kPortalResultDNSFailure;
198 else if (result.status == PortalDetector::kStatusTimeout)
199 retval = kPortalResultDNSTimeout;
200 else
201 LOG(DFATAL) << __func__ << ": Final result status " << result.status
202 << " is not allowed in the DNS phase";
203 break;
204
205 case PortalDetector::kPhaseConnection:
206 if (result.status == PortalDetector::kStatusFailure)
207 retval = kPortalResultConnectionFailure;
208 else if (result.status == PortalDetector::kStatusTimeout)
209 retval = kPortalResultConnectionTimeout;
210 else
211 LOG(DFATAL) << __func__ << ": Final result status " << result.status
212 << " is not allowed in the Connection phase";
213 break;
214
215 case PortalDetector::kPhaseHTTP:
216 if (result.status == PortalDetector::kStatusFailure)
217 retval = kPortalResultHTTPFailure;
218 else if (result.status == PortalDetector::kStatusTimeout)
219 retval = kPortalResultHTTPTimeout;
220 else
221 LOG(DFATAL) << __func__ << ": Final result status " << result.status
222 << " is not allowed in the HTTP phase";
223 break;
224
225 case PortalDetector::kPhaseContent:
226 if (result.status == PortalDetector::kStatusSuccess)
227 retval = kPortalResultSuccess;
228 else if (result.status == PortalDetector::kStatusFailure)
229 retval = kPortalResultContentFailure;
230 else if (result.status == PortalDetector::kStatusTimeout)
231 retval = kPortalResultContentTimeout;
232 else
233 LOG(DFATAL) << __func__ << ": Final result status " << result.status
234 << " is not allowed in the Content phase";
235 break;
236
237 case PortalDetector::kPhaseUnknown:
238 retval = kPortalResultUnknown;
239 break;
240
241 default:
242 LOG(DFATAL) << __func__ << ": Invalid phase " << result.phase;
243 break;
244 }
245
246 return retval;
247}
248
Thieu Le48e6d6d2011-12-06 00:40:27 +0000249void Metrics::RegisterService(const Service *service) {
250 shared_ptr<ServiceMetrics> service_metrics(new ServiceMetrics);
251 services_metrics_[service] = service_metrics;
252 service_metrics->service = service;
253 InitializeCommonServiceMetrics(service);
254 service->InitializeCustomMetrics();
255}
256
257void Metrics::DeregisterService(const Service *service) {
258 services_metrics_.erase(service);
259}
260
261void Metrics::AddServiceStateTransitionTimer(
262 const Service *service,
263 const string &histogram_name,
264 Service::ConnectState start_state,
265 Service::ConnectState stop_state) {
266 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
267 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700268 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000269 DCHECK(false);
270 return;
271 }
272 ServiceMetrics *service_metrics = it->second.get();
273 CHECK(start_state < stop_state);
274 chromeos_metrics::TimerReporter *timer =
275 new chromeos_metrics::TimerReporter(histogram_name,
Thieu Lea20cbc22012-01-09 22:01:43 +0000276 kTimerHistogramMillisecondsMin,
277 kTimerHistogramMillisecondsMax,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000278 kTimerHistogramNumBuckets);
279 service_metrics->timers.push_back(timer); // passes ownership.
280 service_metrics->start_on_state[start_state].push_back(timer);
281 service_metrics->stop_on_state[stop_state].push_back(timer);
282}
283
Thieu Lea20cbc22012-01-09 22:01:43 +0000284void Metrics::NotifyDefaultServiceChanged(const Service *service) {
285 base::TimeDelta elapsed_seconds;
286
287 Technology::Identifier technology = (service) ? service->technology() :
288 Technology::kUnknown;
289 if (technology != last_default_technology_) {
290 if (last_default_technology_ != Technology::kUnknown) {
291 string histogram = GetFullMetricName(kMetricTimeOnlineSeconds,
292 last_default_technology_);
293 time_online_timer_->GetElapsedTime(&elapsed_seconds);
294 SendToUMA(histogram,
295 elapsed_seconds.InSeconds(),
296 kMetricTimeOnlineSecondsMin,
297 kMetricTimeOnlineSecondsMax,
298 kTimerHistogramNumBuckets);
299 }
300 last_default_technology_ = technology;
301 time_online_timer_->Start();
302 }
303
Thieu Lea20cbc22012-01-09 22:01:43 +0000304 // Ignore changes that are not online/offline transitions; e.g.
305 // switching between wired and wireless. TimeToDrop measures
306 // time online regardless of how we are connected.
307 if ((service == NULL && !was_online_) || (service != NULL && was_online_))
308 return;
309
310 if (service == NULL) {
311 time_to_drop_timer_->GetElapsedTime(&elapsed_seconds);
312 SendToUMA(kMetricTimeToDropSeconds,
313 elapsed_seconds.InSeconds(),
314 kMetricTimeToDropSecondsMin,
315 kMetricTimeToDropSecondsMax,
316 kTimerHistogramNumBuckets);
317 } else {
318 time_to_drop_timer_->Start();
319 }
320
321 was_online_ = (service != NULL);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000322}
323
324void Metrics::NotifyServiceStateChanged(const Service *service,
325 Service::ConnectState new_state) {
326 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
327 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700328 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000329 DCHECK(false);
330 return;
331 }
332 ServiceMetrics *service_metrics = it->second.get();
333 UpdateServiceStateTransitionMetrics(service_metrics, new_state);
334
335 if (new_state == Service::kStateFailure)
336 SendServiceFailure(service);
337
Darin Petkov58f0b6d2012-06-12 12:52:30 +0200338 if (collect_bootstats_) {
339 bootstat_log(
340 StringPrintf("network-%s-%s",
341 Technology::NameFromIdentifier(
342 service->technology()).c_str(),
343 service->GetStateString().c_str()).c_str());
344 }
345
Paul Stewart20088d82012-02-16 06:58:55 -0800346 if (new_state != Service::kStateConnected)
Thieu Le48e6d6d2011-12-06 00:40:27 +0000347 return;
348
Thieu Leb84ba342012-03-02 15:15:19 -0800349 base::TimeDelta time_resume_to_ready;
350 time_resume_to_ready_timer_->GetElapsedTime(&time_resume_to_ready);
351 time_resume_to_ready_timer_->Reset();
352 service->SendPostReadyStateMetrics(time_resume_to_ready.InMilliseconds());
Thieu Le48e6d6d2011-12-06 00:40:27 +0000353}
354
355string Metrics::GetFullMetricName(const char *metric_name,
356 Technology::Identifier technology_id) {
357 string technology = Technology::NameFromIdentifier(technology_id);
358 technology[0] = base::ToUpperASCII(technology[0]);
359 return base::StringPrintf(metric_name, technology.c_str());
360}
361
Thieu Le67370f62012-02-14 23:01:42 +0000362void Metrics::NotifyServiceDisconnect(const Service *service) {
363 Technology::Identifier technology = service->technology();
364 string histogram = GetFullMetricName(kMetricDisconnect, technology);
365 SendToUMA(histogram,
366 service->explicitly_disconnected(),
367 kMetricDisconnectMin,
368 kMetricDisconnectMax,
369 kMetricDisconnectNumBuckets);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000370}
371
Thieu Leb84ba342012-03-02 15:15:19 -0800372void Metrics::NotifyPowerStateChange(PowerManager::SuspendState new_state) {
373 if (new_state == PowerManagerProxyDelegate::kOn) {
374 time_resume_to_ready_timer_->Start();
375 } else {
376 time_resume_to_ready_timer_->Reset();
377 }
Thieu Le48e6d6d2011-12-06 00:40:27 +0000378}
379
380bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
381 return library_->SendEnumToUMA(name, sample, max);
382}
383
Thieu Lea20cbc22012-01-09 22:01:43 +0000384bool Metrics::SendToUMA(const string &name, int sample, int min, int max,
385 int num_buckets) {
386 return library_->SendToUMA(name, sample, min, max, num_buckets);
387}
388
Thieu Le48e6d6d2011-12-06 00:40:27 +0000389void Metrics::InitializeCommonServiceMetrics(const Service *service) {
390 Technology::Identifier technology = service->technology();
391 string histogram = GetFullMetricName(kMetricTimeToConfigMilliseconds,
392 technology);
393 AddServiceStateTransitionTimer(
394 service,
395 histogram,
396 Service::kStateConfiguring,
Paul Stewart20088d82012-02-16 06:58:55 -0800397 Service::kStateConnected);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000398 histogram = GetFullMetricName(kMetricTimeToPortalMilliseconds, technology);
399 AddServiceStateTransitionTimer(
400 service,
401 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800402 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000403 Service::kStatePortal);
404 histogram = GetFullMetricName(kMetricTimeToOnlineMilliseconds, technology);
405 AddServiceStateTransitionTimer(
406 service,
407 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800408 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000409 Service::kStateOnline);
410}
411
412void Metrics::UpdateServiceStateTransitionMetrics(
413 ServiceMetrics *service_metrics,
414 Service::ConnectState new_state) {
415 TimerReportersList::iterator it;
416 TimerReportersList &start_timers = service_metrics->start_on_state[new_state];
417 for (it = start_timers.begin(); it != start_timers.end(); ++it)
418 (*it)->Start();
419
420 TimerReportersList &stop_timers = service_metrics->stop_on_state[new_state];
421 for (it = stop_timers.begin(); it != stop_timers.end(); ++it) {
422 (*it)->Stop();
423 (*it)->ReportMilliseconds();
424 }
425}
426
427void Metrics::SendServiceFailure(const Service *service) {
428 library_->SendEnumToUMA(kMetricNetworkServiceErrors,
429 service->failure(),
430 kMetricNetworkServiceErrorsMax);
431}
432
433void Metrics::set_library(MetricsLibraryInterface *library) {
434 chromeos_metrics::TimerReporter::set_metrics_lib(library);
435 library_ = library;
436}
437
438} // namespace shill