blob: 61b75cf701cb201dd8ace50cec79dadeb41b8ec4 [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 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),
Darin Petkov58f0b6d2012-06-12 12:52:30 +0200100 time_resume_to_ready_timer_(new chromeos_metrics::Timer),
101 collect_bootstats_(true) {
Thieu Le48e6d6d2011-12-06 00:40:27 +0000102 metrics_library_.Init();
103 chromeos_metrics::TimerReporter::set_metrics_lib(library_);
104}
105
106Metrics::~Metrics() {}
107
108// static
Thieu Le48e6d6d2011-12-06 00:40:27 +0000109Metrics::WiFiChannel Metrics::WiFiFrequencyToChannel(uint16 frequency) {
110 WiFiChannel channel = kWiFiChannelUndef;
111 if (kWiFiFrequency2412 <= frequency && frequency <= kWiFiFrequency2472) {
112 if (((frequency - kWiFiFrequency2412) % kWiFiBandwidth5MHz) == 0)
113 channel = static_cast<WiFiChannel>(
114 kWiFiChannel2412 +
115 (frequency - kWiFiFrequency2412) / kWiFiBandwidth5MHz);
116 } else if (frequency == kWiFiFrequency2484) {
117 channel = kWiFiChannel2484;
118 } else if (kWiFiFrequency5170 <= frequency &&
119 frequency <= kWiFiFrequency5230) {
120 if ((frequency % kWiFiBandwidth20MHz) == 0)
121 channel = static_cast<WiFiChannel>(
122 kWiFiChannel5180 +
123 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
124 if ((frequency % kWiFiBandwidth20MHz) == 10)
125 channel = static_cast<WiFiChannel>(
126 kWiFiChannel5170 +
127 (frequency - kWiFiFrequency5170) / kWiFiBandwidth20MHz);
128 } else if (kWiFiFrequency5240 <= frequency &&
129 frequency <= kWiFiFrequency5320) {
130 if (((frequency - kWiFiFrequency5180) % kWiFiBandwidth20MHz) == 0)
131 channel = static_cast<WiFiChannel>(
132 kWiFiChannel5180 +
133 (frequency - kWiFiFrequency5180) / kWiFiBandwidth20MHz);
134 } else if (kWiFiFrequency5500 <= frequency &&
135 frequency <= kWiFiFrequency5700) {
136 if (((frequency - kWiFiFrequency5500) % kWiFiBandwidth20MHz) == 0)
137 channel = static_cast<WiFiChannel>(
138 kWiFiChannel5500 +
139 (frequency - kWiFiFrequency5500) / kWiFiBandwidth20MHz);
140 } else if (kWiFiFrequency5745 <= frequency &&
141 frequency <= kWiFiFrequency5825) {
142 if (((frequency - kWiFiFrequency5745) % kWiFiBandwidth20MHz) == 0)
143 channel = static_cast<WiFiChannel>(
144 kWiFiChannel5745 +
145 (frequency - kWiFiFrequency5745) / kWiFiBandwidth20MHz);
146 }
147 CHECK(kWiFiChannelUndef <= channel && channel < kWiFiChannelMax);
148
149 if (channel == kWiFiChannelUndef)
150 LOG(WARNING) << "no mapping for frequency " << frequency;
151 else
Ben Chanfad4a0b2012-04-18 15:49:59 -0700152 SLOG(Metrics, 3) << "map " << frequency << " to " << channel;
Thieu Le48e6d6d2011-12-06 00:40:27 +0000153
154 return channel;
155}
156
Thieu Lead1ec2c2012-01-05 23:39:48 +0000157// static
158Metrics::WiFiSecurity Metrics::WiFiSecurityStringToEnum(
159 const std::string &security) {
160 if (security == flimflam::kSecurityNone) {
161 return kWiFiSecurityNone;
162 } else if (security == flimflam::kSecurityWep) {
163 return kWiFiSecurityWep;
164 } else if (security == flimflam::kSecurityWpa) {
165 return kWiFiSecurityWpa;
166 } else if (security == flimflam::kSecurityRsn) {
167 return kWiFiSecurityRsn;
168 } else if (security == flimflam::kSecurity8021x) {
169 return kWiFiSecurity8021x;
170 } else if (security == flimflam::kSecurityPsk) {
171 return kWiFiSecurityPsk;
172 } else {
173 return kWiFiSecurityUnknown;
174 }
175}
176
Thieu Le85e050b2012-03-13 15:04:38 -0700177// static
178Metrics::PortalResult Metrics::PortalDetectionResultToEnum(
179 const PortalDetector::Result &result) {
180 DCHECK(result.final);
181 PortalResult retval = kPortalResultUnknown;
182 // The only time we should end a successful portal detection is when we're
183 // in the Content phase. If we end with kStatusSuccess in any other phase,
184 // then this indicates that something bad has happened.
185 switch (result.phase) {
186 case PortalDetector::kPhaseDNS:
187 if (result.status == PortalDetector::kStatusFailure)
188 retval = kPortalResultDNSFailure;
189 else if (result.status == PortalDetector::kStatusTimeout)
190 retval = kPortalResultDNSTimeout;
191 else
192 LOG(DFATAL) << __func__ << ": Final result status " << result.status
193 << " is not allowed in the DNS phase";
194 break;
195
196 case PortalDetector::kPhaseConnection:
197 if (result.status == PortalDetector::kStatusFailure)
198 retval = kPortalResultConnectionFailure;
199 else if (result.status == PortalDetector::kStatusTimeout)
200 retval = kPortalResultConnectionTimeout;
201 else
202 LOG(DFATAL) << __func__ << ": Final result status " << result.status
203 << " is not allowed in the Connection phase";
204 break;
205
206 case PortalDetector::kPhaseHTTP:
207 if (result.status == PortalDetector::kStatusFailure)
208 retval = kPortalResultHTTPFailure;
209 else if (result.status == PortalDetector::kStatusTimeout)
210 retval = kPortalResultHTTPTimeout;
211 else
212 LOG(DFATAL) << __func__ << ": Final result status " << result.status
213 << " is not allowed in the HTTP phase";
214 break;
215
216 case PortalDetector::kPhaseContent:
217 if (result.status == PortalDetector::kStatusSuccess)
218 retval = kPortalResultSuccess;
219 else if (result.status == PortalDetector::kStatusFailure)
220 retval = kPortalResultContentFailure;
221 else if (result.status == PortalDetector::kStatusTimeout)
222 retval = kPortalResultContentTimeout;
223 else
224 LOG(DFATAL) << __func__ << ": Final result status " << result.status
225 << " is not allowed in the Content phase";
226 break;
227
228 case PortalDetector::kPhaseUnknown:
229 retval = kPortalResultUnknown;
230 break;
231
232 default:
233 LOG(DFATAL) << __func__ << ": Invalid phase " << result.phase;
234 break;
235 }
236
237 return retval;
238}
239
Thieu Le48e6d6d2011-12-06 00:40:27 +0000240void Metrics::RegisterService(const Service *service) {
241 shared_ptr<ServiceMetrics> service_metrics(new ServiceMetrics);
242 services_metrics_[service] = service_metrics;
243 service_metrics->service = service;
244 InitializeCommonServiceMetrics(service);
245 service->InitializeCustomMetrics();
246}
247
248void Metrics::DeregisterService(const Service *service) {
249 services_metrics_.erase(service);
250}
251
252void Metrics::AddServiceStateTransitionTimer(
253 const Service *service,
254 const string &histogram_name,
255 Service::ConnectState start_state,
256 Service::ConnectState stop_state) {
257 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
258 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700259 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000260 DCHECK(false);
261 return;
262 }
263 ServiceMetrics *service_metrics = it->second.get();
264 CHECK(start_state < stop_state);
265 chromeos_metrics::TimerReporter *timer =
266 new chromeos_metrics::TimerReporter(histogram_name,
Thieu Lea20cbc22012-01-09 22:01:43 +0000267 kTimerHistogramMillisecondsMin,
268 kTimerHistogramMillisecondsMax,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000269 kTimerHistogramNumBuckets);
270 service_metrics->timers.push_back(timer); // passes ownership.
271 service_metrics->start_on_state[start_state].push_back(timer);
272 service_metrics->stop_on_state[stop_state].push_back(timer);
273}
274
Thieu Lea20cbc22012-01-09 22:01:43 +0000275void Metrics::NotifyDefaultServiceChanged(const Service *service) {
276 base::TimeDelta elapsed_seconds;
277
278 Technology::Identifier technology = (service) ? service->technology() :
279 Technology::kUnknown;
280 if (technology != last_default_technology_) {
281 if (last_default_technology_ != Technology::kUnknown) {
282 string histogram = GetFullMetricName(kMetricTimeOnlineSeconds,
283 last_default_technology_);
284 time_online_timer_->GetElapsedTime(&elapsed_seconds);
285 SendToUMA(histogram,
286 elapsed_seconds.InSeconds(),
287 kMetricTimeOnlineSecondsMin,
288 kMetricTimeOnlineSecondsMax,
289 kTimerHistogramNumBuckets);
290 }
291 last_default_technology_ = technology;
292 time_online_timer_->Start();
293 }
294
Thieu Lea20cbc22012-01-09 22:01:43 +0000295 // Ignore changes that are not online/offline transitions; e.g.
296 // switching between wired and wireless. TimeToDrop measures
297 // time online regardless of how we are connected.
298 if ((service == NULL && !was_online_) || (service != NULL && was_online_))
299 return;
300
301 if (service == NULL) {
302 time_to_drop_timer_->GetElapsedTime(&elapsed_seconds);
303 SendToUMA(kMetricTimeToDropSeconds,
304 elapsed_seconds.InSeconds(),
305 kMetricTimeToDropSecondsMin,
306 kMetricTimeToDropSecondsMax,
307 kTimerHistogramNumBuckets);
308 } else {
309 time_to_drop_timer_->Start();
310 }
311
312 was_online_ = (service != NULL);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000313}
314
315void Metrics::NotifyServiceStateChanged(const Service *service,
316 Service::ConnectState new_state) {
317 ServiceMetricsLookupMap::iterator it = services_metrics_.find(service);
318 if (it == services_metrics_.end()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700319 SLOG(Metrics, 1) << "service not found";
Thieu Le48e6d6d2011-12-06 00:40:27 +0000320 DCHECK(false);
321 return;
322 }
323 ServiceMetrics *service_metrics = it->second.get();
324 UpdateServiceStateTransitionMetrics(service_metrics, new_state);
325
326 if (new_state == Service::kStateFailure)
327 SendServiceFailure(service);
328
Darin Petkov58f0b6d2012-06-12 12:52:30 +0200329 if (collect_bootstats_) {
330 bootstat_log(
331 StringPrintf("network-%s-%s",
332 Technology::NameFromIdentifier(
333 service->technology()).c_str(),
334 service->GetStateString().c_str()).c_str());
335 }
336
Paul Stewart20088d82012-02-16 06:58:55 -0800337 if (new_state != Service::kStateConnected)
Thieu Le48e6d6d2011-12-06 00:40:27 +0000338 return;
339
Thieu Leb84ba342012-03-02 15:15:19 -0800340 base::TimeDelta time_resume_to_ready;
341 time_resume_to_ready_timer_->GetElapsedTime(&time_resume_to_ready);
342 time_resume_to_ready_timer_->Reset();
343 service->SendPostReadyStateMetrics(time_resume_to_ready.InMilliseconds());
Thieu Le48e6d6d2011-12-06 00:40:27 +0000344}
345
346string Metrics::GetFullMetricName(const char *metric_name,
347 Technology::Identifier technology_id) {
348 string technology = Technology::NameFromIdentifier(technology_id);
349 technology[0] = base::ToUpperASCII(technology[0]);
350 return base::StringPrintf(metric_name, technology.c_str());
351}
352
Thieu Le67370f62012-02-14 23:01:42 +0000353void Metrics::NotifyServiceDisconnect(const Service *service) {
354 Technology::Identifier technology = service->technology();
355 string histogram = GetFullMetricName(kMetricDisconnect, technology);
356 SendToUMA(histogram,
357 service->explicitly_disconnected(),
358 kMetricDisconnectMin,
359 kMetricDisconnectMax,
360 kMetricDisconnectNumBuckets);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000361}
362
Thieu Leb84ba342012-03-02 15:15:19 -0800363void Metrics::NotifyPowerStateChange(PowerManager::SuspendState new_state) {
364 if (new_state == PowerManagerProxyDelegate::kOn) {
365 time_resume_to_ready_timer_->Start();
366 } else {
367 time_resume_to_ready_timer_->Reset();
368 }
Thieu Le48e6d6d2011-12-06 00:40:27 +0000369}
370
371bool Metrics::SendEnumToUMA(const string &name, int sample, int max) {
372 return library_->SendEnumToUMA(name, sample, max);
373}
374
Thieu Lea20cbc22012-01-09 22:01:43 +0000375bool Metrics::SendToUMA(const string &name, int sample, int min, int max,
376 int num_buckets) {
377 return library_->SendToUMA(name, sample, min, max, num_buckets);
378}
379
Thieu Le48e6d6d2011-12-06 00:40:27 +0000380void Metrics::InitializeCommonServiceMetrics(const Service *service) {
381 Technology::Identifier technology = service->technology();
382 string histogram = GetFullMetricName(kMetricTimeToConfigMilliseconds,
383 technology);
384 AddServiceStateTransitionTimer(
385 service,
386 histogram,
387 Service::kStateConfiguring,
Paul Stewart20088d82012-02-16 06:58:55 -0800388 Service::kStateConnected);
Thieu Le48e6d6d2011-12-06 00:40:27 +0000389 histogram = GetFullMetricName(kMetricTimeToPortalMilliseconds, technology);
390 AddServiceStateTransitionTimer(
391 service,
392 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800393 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000394 Service::kStatePortal);
395 histogram = GetFullMetricName(kMetricTimeToOnlineMilliseconds, technology);
396 AddServiceStateTransitionTimer(
397 service,
398 histogram,
Paul Stewart20088d82012-02-16 06:58:55 -0800399 Service::kStateConnected,
Thieu Le48e6d6d2011-12-06 00:40:27 +0000400 Service::kStateOnline);
401}
402
403void Metrics::UpdateServiceStateTransitionMetrics(
404 ServiceMetrics *service_metrics,
405 Service::ConnectState new_state) {
406 TimerReportersList::iterator it;
407 TimerReportersList &start_timers = service_metrics->start_on_state[new_state];
408 for (it = start_timers.begin(); it != start_timers.end(); ++it)
409 (*it)->Start();
410
411 TimerReportersList &stop_timers = service_metrics->stop_on_state[new_state];
412 for (it = stop_timers.begin(); it != stop_timers.end(); ++it) {
413 (*it)->Stop();
414 (*it)->ReportMilliseconds();
415 }
416}
417
418void Metrics::SendServiceFailure(const Service *service) {
419 library_->SendEnumToUMA(kMetricNetworkServiceErrors,
420 service->failure(),
421 kMetricNetworkServiceErrorsMax);
422}
423
424void Metrics::set_library(MetricsLibraryInterface *library) {
425 chromeos_metrics::TimerReporter::set_metrics_lib(library);
426 library_ = library;
427}
428
429} // namespace shill