OLD | NEW |
| (Empty) |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/net/network_stats.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/logging.h" | |
9 #include "base/message_loop/message_loop.h" | |
10 #include "base/metrics/field_trial.h" | |
11 #include "base/metrics/histogram.h" | |
12 #include "base/profiler/scoped_tracker.h" | |
13 #include "base/rand_util.h" | |
14 #include "base/strings/stringprintf.h" | |
15 #include "base/time/time.h" | |
16 #include "chrome/common/chrome_version_info.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 #include "net/base/load_flags.h" | |
19 #include "net/base/net_errors.h" | |
20 #include "net/base/network_change_notifier.h" | |
21 #include "net/base/test_completion_callback.h" | |
22 #include "net/dns/single_request_host_resolver.h" | |
23 #include "net/proxy/proxy_service.h" | |
24 #include "net/socket/client_socket_factory.h" | |
25 #include "net/udp/datagram_client_socket.h" | |
26 #include "url/gurl.h" | |
27 | |
28 using content::BrowserThread; | |
29 | |
30 namespace chrome_browser_net { | |
31 | |
32 // static | |
33 uint32 NetworkStats::maximum_tests_ = 8; | |
34 // static | |
35 uint32 NetworkStats::maximum_sequential_packets_ = 21; | |
36 // static | |
37 uint32 NetworkStats::maximum_NAT_packets_ = 2; | |
38 // static | |
39 uint32 NetworkStats::maximum_NAT_idle_seconds_ = 300; | |
40 // static | |
41 bool NetworkStats::start_test_after_connect_ = true; | |
42 | |
43 // Specify the possible choices of probe packet sizes. | |
44 const uint32 kProbePacketBytes[] = {100, 500, 1200}; | |
45 const uint32 kPacketSizeChoices = arraysize(kProbePacketBytes); | |
46 | |
47 // List of ports used for probing test. | |
48 const uint16 kPorts[] = {443, 80}; | |
49 | |
50 // Number of first few packets that are recorded in a packet-correlation | |
51 // histogram, which shows exactly what sequence of packets were received. | |
52 // We use this to deduce specific packet loss correlation. | |
53 const uint32 kCorrelatedLossPacketCount = 6; | |
54 | |
55 // This specifies the maximum message (payload) size of one packet. | |
56 const uint32 kMaxMessageSize = 1600; | |
57 | |
58 // This specifies the maximum udp receiver buffer size. | |
59 const uint32 kMaxUdpReceiveBufferSize = 63000; | |
60 | |
61 // This specifies the maximum udp receiver buffer size. | |
62 const uint32 kMaxUdpSendBufferSize = 4096; | |
63 | |
64 // This should match TestType except for the last one. | |
65 const char* kTestName[] = {"TokenRequest", "StartPacket", "NonPacedPacket", | |
66 "PacedPacket", "NATBind", "PacketSizeTest"}; | |
67 | |
68 // Perform Pacing/Non-pacing test only if at least 2 packets are received | |
69 // in the StartPacketTest. | |
70 const uint32 kMinimumReceivedPacketsForPacingTest = 2; | |
71 // Perform NAT binding test only if at least 10 packets are received. | |
72 const uint32 kMinimumReceivedPacketsForNATTest = 10; | |
73 | |
74 // Maximum inter-packet pacing interval in microseconds. | |
75 const uint32 kMaximumPacingMicros = 1000000; | |
76 // Timeout value for getting the token. | |
77 const uint32 kGetTokenTimeoutSeconds = 10; | |
78 // Timeout value for StartPacket and NonPacedPacket if the client does not get | |
79 // reply. For PacedPacket test, the timeout value is this number plus the total | |
80 // pacing interval. | |
81 const uint32 kReadDataTimeoutSeconds = 30; | |
82 // This is the timeout for NAT without Idle periods. | |
83 // For NAT test with idle periods, the timeout is the Idle period + this value. | |
84 const uint32 kReadNATTimeoutSeconds = 10; | |
85 // This is the timeout for PACKET_SIZE_TEST. | |
86 const uint32 kReadPacketSizeTimeoutSeconds = 10; | |
87 // This is the maxmium number of packets we would send for PACKET_SIZE_TEST. | |
88 uint32 kMaximumPacketSizeTestPackets = 1; | |
89 | |
90 // These helper functions are similar to UMA_HISTOGRAM_XXX except that they do | |
91 // not create a static histogram_pointer. | |
92 void DynamicHistogramEnumeration(const std::string& name, | |
93 uint32 sample, | |
94 uint32 boundary_value) { | |
95 base::HistogramBase* histogram_pointer = base::LinearHistogram::FactoryGet( | |
96 name, | |
97 1, | |
98 boundary_value, | |
99 boundary_value + 1, | |
100 base::HistogramBase::kUmaTargetedHistogramFlag); | |
101 histogram_pointer->Add(sample); | |
102 } | |
103 | |
104 void DynamicHistogramTimes(const std::string& name, | |
105 const base::TimeDelta& sample) { | |
106 base::HistogramBase* histogram_pointer = base::Histogram::FactoryTimeGet( | |
107 name, | |
108 base::TimeDelta::FromMilliseconds(1), | |
109 base::TimeDelta::FromSeconds(30), | |
110 50, | |
111 base::HistogramBase::kUmaTargetedHistogramFlag); | |
112 histogram_pointer->AddTime(sample); | |
113 } | |
114 | |
115 void DynamicHistogramCounts(const std::string& name, | |
116 uint32 sample, | |
117 uint32 min, | |
118 uint32 max, | |
119 uint32 bucket_count) { | |
120 base::HistogramBase* histogram_pointer = base::Histogram::FactoryGet( | |
121 name, min, max, bucket_count, | |
122 base::HistogramBase::kUmaTargetedHistogramFlag); | |
123 histogram_pointer->Add(sample); | |
124 } | |
125 | |
126 NetworkStats::NetworkStats(net::ClientSocketFactory* socket_factory) | |
127 : socket_factory_(socket_factory), | |
128 histogram_port_(0), | |
129 has_proxy_server_(false), | |
130 probe_packet_bytes_(0), | |
131 bytes_for_packet_size_test_(0), | |
132 current_test_index_(0), | |
133 read_state_(READ_STATE_IDLE), | |
134 write_state_(WRITE_STATE_IDLE), | |
135 weak_factory_(this) { | |
136 ResetData(); | |
137 } | |
138 | |
139 NetworkStats::~NetworkStats() {} | |
140 | |
141 bool NetworkStats::Start(net::HostResolver* host_resolver, | |
142 const net::HostPortPair& server_host_port_pair, | |
143 uint16 histogram_port, | |
144 bool has_proxy_server, | |
145 uint32 probe_bytes, | |
146 uint32 bytes_for_packet_size_test, | |
147 const net::CompletionCallback& finished_callback) { | |
148 DCHECK(host_resolver); | |
149 histogram_port_ = histogram_port; | |
150 has_proxy_server_ = has_proxy_server; | |
151 probe_packet_bytes_ = probe_bytes; | |
152 bytes_for_packet_size_test_ = bytes_for_packet_size_test; | |
153 finished_callback_ = finished_callback; | |
154 test_sequence_.clear(); | |
155 test_sequence_.push_back(TOKEN_REQUEST); | |
156 | |
157 ResetData(); | |
158 | |
159 scoped_ptr<net::SingleRequestHostResolver> resolver( | |
160 new net::SingleRequestHostResolver(host_resolver)); | |
161 net::HostResolver::RequestInfo request(server_host_port_pair); | |
162 int rv = | |
163 resolver->Resolve(request, | |
164 net::DEFAULT_PRIORITY, | |
165 &addresses_, | |
166 base::Bind(base::IgnoreResult(&NetworkStats::DoConnect), | |
167 base::Unretained(this)), | |
168 net::BoundNetLog()); | |
169 if (rv == net::ERR_IO_PENDING) { | |
170 resolver_.swap(resolver); | |
171 return true; | |
172 } | |
173 return DoConnect(rv); | |
174 } | |
175 | |
176 void NetworkStats::StartOneTest() { | |
177 if (test_sequence_[current_test_index_] == TOKEN_REQUEST) { | |
178 DCHECK_EQ(WRITE_STATE_IDLE, write_state_); | |
179 write_buffer_ = NULL; | |
180 SendHelloRequest(); | |
181 } else { | |
182 SendProbeRequest(); | |
183 } | |
184 } | |
185 | |
186 void NetworkStats::ResetData() { | |
187 DCHECK_EQ(WRITE_STATE_IDLE, write_state_); | |
188 write_buffer_ = NULL; | |
189 packets_received_mask_.reset(); | |
190 first_arrival_time_ = base::TimeTicks(); | |
191 last_arrival_time_ = base::TimeTicks(); | |
192 | |
193 packet_rtt_.clear(); | |
194 packet_rtt_.resize(maximum_sequential_packets_); | |
195 probe_request_time_ = base::TimeTicks(); | |
196 // Note: inter_arrival_time_ should not be reset here because it is used in | |
197 // subsequent tests. | |
198 } | |
199 | |
200 bool NetworkStats::DoConnect(int result) { | |
201 // TODO(vadimt): Remove ScopedTracker below once crbug.com/436634 is fixed. | |
202 tracked_objects::ScopedTracker tracking_profile( | |
203 FROM_HERE_WITH_EXPLICIT_FUNCTION("436634 NetworkStats::DoConnect")); | |
204 | |
205 if (result != net::OK) { | |
206 TestPhaseComplete(RESOLVE_FAILED, result); | |
207 return false; | |
208 } | |
209 | |
210 scoped_ptr<net::DatagramClientSocket> udp_socket = | |
211 socket_factory_->CreateDatagramClientSocket( | |
212 net::DatagramSocket::DEFAULT_BIND, | |
213 net::RandIntCallback(), | |
214 NULL, | |
215 net::NetLog::Source()); | |
216 DCHECK(udp_socket); | |
217 DCHECK(!socket_); | |
218 socket_ = udp_socket.Pass(); | |
219 | |
220 const net::IPEndPoint& endpoint = addresses_.front(); | |
221 int rv = socket_->Connect(endpoint); | |
222 if (rv < 0) { | |
223 TestPhaseComplete(CONNECT_FAILED, rv); | |
224 return false; | |
225 } | |
226 | |
227 socket_->SetSendBufferSize(kMaxUdpSendBufferSize); | |
228 socket_->SetReceiveBufferSize(kMaxUdpReceiveBufferSize); | |
229 return ConnectComplete(rv); | |
230 } | |
231 | |
232 bool NetworkStats::ConnectComplete(int result) { | |
233 if (result < 0) { | |
234 TestPhaseComplete(CONNECT_FAILED, result); | |
235 return false; | |
236 } | |
237 | |
238 if (start_test_after_connect_) { | |
239 // Reads data for all HelloReply and all subsequent probe tests. | |
240 if (ReadData() != net::ERR_IO_PENDING) { | |
241 TestPhaseComplete(READ_FAILED, result); | |
242 return false; | |
243 } | |
244 SendHelloRequest(); | |
245 } else { | |
246 // For unittesting. Only run the callback, do not destroy it. | |
247 if (!finished_callback_.is_null()) | |
248 finished_callback_.Run(result); | |
249 } | |
250 return true; | |
251 } | |
252 | |
253 void NetworkStats::SendHelloRequest() { | |
254 StartReadDataTimer(kGetTokenTimeoutSeconds, current_test_index_); | |
255 ProbePacket probe_packet; | |
256 probe_message_.SetPacketHeader(ProbePacket_Type_HELLO_REQUEST, &probe_packet); | |
257 probe_packet.set_group_id(current_test_index_); | |
258 std::string output = probe_message_.MakeEncodedPacket(probe_packet); | |
259 | |
260 int result = SendData(output); | |
261 if (result < 0 && result != net::ERR_IO_PENDING) | |
262 TestPhaseComplete(WRITE_FAILED, result); | |
263 } | |
264 | |
265 void NetworkStats::SendProbeRequest() { | |
266 ResetData(); | |
267 // Use default timeout except for the NAT bind test. | |
268 uint32 timeout_seconds = kReadDataTimeoutSeconds; | |
269 uint32 number_packets = maximum_sequential_packets_; | |
270 uint32 probe_bytes = probe_packet_bytes_; | |
271 pacing_interval_ = base::TimeDelta(); | |
272 switch (test_sequence_[current_test_index_]) { | |
273 case START_PACKET_TEST: | |
274 case NON_PACED_PACKET_TEST: | |
275 break; | |
276 case PACED_PACKET_TEST: { | |
277 pacing_interval_ = | |
278 std::min(inter_arrival_time_, | |
279 base::TimeDelta::FromMicroseconds(kMaximumPacingMicros)); | |
280 timeout_seconds += pacing_interval_.InMicroseconds() * | |
281 (maximum_sequential_packets_ - 1) / 1000000; | |
282 break; | |
283 } | |
284 case NAT_BIND_TEST: { | |
285 // Make sure no integer overflow. | |
286 DCHECK_LE(maximum_NAT_idle_seconds_, 4000U); | |
287 int nat_test_idle_seconds = base::RandInt(1, maximum_NAT_idle_seconds_); | |
288 pacing_interval_ = base::TimeDelta::FromSeconds(nat_test_idle_seconds); | |
289 timeout_seconds = nat_test_idle_seconds + kReadNATTimeoutSeconds; | |
290 number_packets = maximum_NAT_packets_; | |
291 break; | |
292 } | |
293 case PACKET_SIZE_TEST: { | |
294 number_packets = kMaximumPacketSizeTestPackets; | |
295 probe_bytes = bytes_for_packet_size_test_; | |
296 timeout_seconds = kReadPacketSizeTimeoutSeconds; | |
297 break; | |
298 } | |
299 default: | |
300 NOTREACHED(); | |
301 return; | |
302 } | |
303 DVLOG(1) << "NetworkStat: Probe pacing " << pacing_interval_.InMicroseconds() | |
304 << " microseconds. Time out " << timeout_seconds << " seconds"; | |
305 ProbePacket probe_packet; | |
306 probe_message_.GenerateProbeRequest(token_, | |
307 current_test_index_, | |
308 probe_bytes, | |
309 pacing_interval_.InMicroseconds(), | |
310 number_packets, | |
311 &probe_packet); | |
312 std::string output = probe_message_.MakeEncodedPacket(probe_packet); | |
313 | |
314 StartReadDataTimer(timeout_seconds, current_test_index_); | |
315 probe_request_time_ = base::TimeTicks::Now(); | |
316 int result = SendData(output); | |
317 if (result < 0 && result != net::ERR_IO_PENDING) | |
318 TestPhaseComplete(WRITE_FAILED, result); | |
319 } | |
320 | |
321 int NetworkStats::ReadData() { | |
322 if (!socket_.get()) | |
323 return 0; | |
324 | |
325 if (read_state_ == READ_STATE_READ_PENDING) | |
326 return net::ERR_IO_PENDING; | |
327 | |
328 int rv = 0; | |
329 while (true) { | |
330 DCHECK(!read_buffer_.get()); | |
331 read_buffer_ = new net::IOBuffer(kMaxMessageSize); | |
332 | |
333 rv = socket_->Read( | |
334 read_buffer_.get(), | |
335 kMaxMessageSize, | |
336 base::Bind(&NetworkStats::OnReadComplete, weak_factory_.GetWeakPtr())); | |
337 if (rv <= 0) | |
338 break; | |
339 if (ReadComplete(rv)) | |
340 return rv; | |
341 } | |
342 if (rv == net::ERR_IO_PENDING) | |
343 read_state_ = READ_STATE_READ_PENDING; | |
344 return rv; | |
345 } | |
346 | |
347 void NetworkStats::OnReadComplete(int result) { | |
348 DCHECK_NE(net::ERR_IO_PENDING, result); | |
349 DCHECK_EQ(READ_STATE_READ_PENDING, read_state_); | |
350 | |
351 read_state_ = READ_STATE_IDLE; | |
352 if (!ReadComplete(result)) { | |
353 // Called ReadData() via PostDelayedTask() to avoid recursion. Added a delay | |
354 // of 1ms so that the time-out will fire before we have time to really hog | |
355 // the CPU too extensively (waiting for the time-out) in case of an infinite | |
356 // loop. | |
357 base::MessageLoop::current()->PostDelayedTask( | |
358 FROM_HERE, | |
359 base::Bind(base::IgnoreResult(&NetworkStats::ReadData), | |
360 weak_factory_.GetWeakPtr()), | |
361 base::TimeDelta::FromMilliseconds(1)); | |
362 } | |
363 } | |
364 | |
365 bool NetworkStats::ReadComplete(int result) { | |
366 DCHECK(socket_.get()); | |
367 DCHECK_NE(net::ERR_IO_PENDING, result); | |
368 if (result < 0) { | |
369 // Something is wrong, finish the test. | |
370 read_buffer_ = NULL; | |
371 TestPhaseComplete(READ_FAILED, result); | |
372 return true; | |
373 } | |
374 | |
375 std::string encoded_message(read_buffer_->data(), | |
376 read_buffer_->data() + result); | |
377 read_buffer_ = NULL; | |
378 ProbePacket probe_packet; | |
379 if (!probe_message_.ParseInput(encoded_message, &probe_packet)) | |
380 return false; | |
381 // Discard if the packet is for a different test. | |
382 if (probe_packet.group_id() != current_test_index_) | |
383 return false; | |
384 | |
385 // Whether all packets in the current test have been received. | |
386 bool current_test_complete = false; | |
387 switch (probe_packet.header().type()) { | |
388 case ProbePacket_Type_HELLO_REPLY: | |
389 token_ = probe_packet.token(); | |
390 if (current_test_index_ == 0) | |
391 test_sequence_.push_back(START_PACKET_TEST); | |
392 current_test_complete = true; | |
393 break; | |
394 case ProbePacket_Type_PROBE_REPLY: | |
395 current_test_complete = UpdateReception(probe_packet); | |
396 break; | |
397 default: | |
398 DVLOG(1) << "Received unexpected packet type: " | |
399 << probe_packet.header().type(); | |
400 } | |
401 | |
402 if (!current_test_complete) { | |
403 // All packets have not been received for the current test. | |
404 return false; | |
405 } | |
406 // All packets are received for the current test. | |
407 // Read completes if all tests are done (if TestPhaseComplete didn't start | |
408 // another test). | |
409 return TestPhaseComplete(SUCCESS, net::OK); | |
410 } | |
411 | |
412 bool NetworkStats::UpdateReception(const ProbePacket& probe_packet) { | |
413 uint32 packet_index = probe_packet.packet_index(); | |
414 if (packet_index >= packet_rtt_.size()) | |
415 return false; | |
416 packets_received_mask_.set(packet_index); | |
417 TestType test_type = test_sequence_[current_test_index_]; | |
418 uint32 received_packets = packets_received_mask_.count(); | |
419 | |
420 base::TimeTicks current_time = base::TimeTicks::Now(); | |
421 last_arrival_time_ = current_time; | |
422 if (first_arrival_time_.is_null()) | |
423 first_arrival_time_ = current_time; | |
424 | |
425 // Need to do this after updating the last_arrival_time_ since NAT_BIND_TEST | |
426 // and PACKET_SIZE_TEST record the SendToLastRecvDelay. | |
427 if (test_type == NAT_BIND_TEST) { | |
428 return received_packets >= maximum_NAT_packets_; | |
429 } | |
430 if (test_type == PACKET_SIZE_TEST) { | |
431 return received_packets >= kMaximumPacketSizeTestPackets; | |
432 } | |
433 | |
434 base::TimeDelta rtt = | |
435 current_time - probe_request_time_ - | |
436 base::TimeDelta::FromMicroseconds(std::max( | |
437 static_cast<int64>(0), probe_packet.server_processing_micros())); | |
438 base::TimeDelta min_rtt = base::TimeDelta::FromMicroseconds(1L); | |
439 packet_rtt_[packet_index] = (rtt >= min_rtt) ? rtt : min_rtt; | |
440 | |
441 if (received_packets < maximum_sequential_packets_) | |
442 return false; | |
443 // All packets in the current test are received. | |
444 inter_arrival_time_ = (last_arrival_time_ - first_arrival_time_) / | |
445 std::max(1U, (received_packets - 1)); | |
446 if (test_type == START_PACKET_TEST) { | |
447 test_sequence_.push_back(PACKET_SIZE_TEST); | |
448 test_sequence_.push_back(TOKEN_REQUEST); | |
449 // No need to add TOKEN_REQUEST here when all packets are received. | |
450 test_sequence_.push_back(base::RandInt(0, 1) ? PACED_PACKET_TEST | |
451 : NON_PACED_PACKET_TEST); | |
452 test_sequence_.push_back(TOKEN_REQUEST); | |
453 test_sequence_.push_back(NAT_BIND_TEST); | |
454 test_sequence_.push_back(TOKEN_REQUEST); | |
455 } | |
456 return true; | |
457 } | |
458 | |
459 int NetworkStats::SendData(const std::string& output) { | |
460 if (write_buffer_.get() || !socket_.get() || | |
461 write_state_ == WRITE_STATE_WRITE_PENDING) { | |
462 return net::ERR_UNEXPECTED; | |
463 } | |
464 scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer(output)); | |
465 write_buffer_ = new net::DrainableIOBuffer(buffer.get(), buffer->size()); | |
466 | |
467 int bytes_written = socket_->Write( | |
468 write_buffer_.get(), | |
469 write_buffer_->BytesRemaining(), | |
470 base::Bind(&NetworkStats::OnWriteComplete, weak_factory_.GetWeakPtr())); | |
471 if (bytes_written < 0) { | |
472 if (bytes_written == net::ERR_IO_PENDING) | |
473 write_state_ = WRITE_STATE_WRITE_PENDING; | |
474 return bytes_written; | |
475 } | |
476 UpdateSendBuffer(bytes_written); | |
477 return net::OK; | |
478 } | |
479 | |
480 void NetworkStats::OnWriteComplete(int result) { | |
481 DCHECK_NE(net::ERR_IO_PENDING, result); | |
482 DCHECK_EQ(WRITE_STATE_WRITE_PENDING, write_state_); | |
483 write_state_ = WRITE_STATE_IDLE; | |
484 if (result < 0 || !socket_.get() || write_buffer_.get() == NULL) { | |
485 TestPhaseComplete(WRITE_FAILED, result); | |
486 return; | |
487 } | |
488 UpdateSendBuffer(result); | |
489 } | |
490 | |
491 void NetworkStats::UpdateSendBuffer(int bytes_sent) { | |
492 write_buffer_->DidConsume(bytes_sent); | |
493 DCHECK_EQ(write_buffer_->BytesRemaining(), 0); | |
494 DCHECK_EQ(WRITE_STATE_IDLE, write_state_); | |
495 write_buffer_ = NULL; | |
496 } | |
497 | |
498 void NetworkStats::StartReadDataTimer(uint32 seconds, uint32 test_index) { | |
499 base::MessageLoop::current()->PostDelayedTask( | |
500 FROM_HERE, | |
501 base::Bind(&NetworkStats::OnReadDataTimeout, | |
502 weak_factory_.GetWeakPtr(), | |
503 test_index), | |
504 base::TimeDelta::FromSeconds(seconds)); | |
505 } | |
506 | |
507 void NetworkStats::OnReadDataTimeout(uint32 test_index) { | |
508 // If the current_test_index_ has changed since we set the timeout, | |
509 // the current test has been completed, so do nothing. | |
510 if (test_index != current_test_index_) | |
511 return; | |
512 // If test_type is TOKEN_REQUEST, it will do nothing but call | |
513 // TestPhaseComplete(). | |
514 TestType test_type = test_sequence_[current_test_index_]; | |
515 | |
516 uint32 received_packets = packets_received_mask_.count(); | |
517 if (received_packets >= 2) { | |
518 inter_arrival_time_ = | |
519 (last_arrival_time_ - first_arrival_time_) / (received_packets - 1); | |
520 } | |
521 // Add other tests if this is START_PACKET_TEST. | |
522 if (test_type == START_PACKET_TEST) { | |
523 if (received_packets >= kMinimumReceivedPacketsForPacingTest) { | |
524 test_sequence_.push_back(TOKEN_REQUEST); | |
525 test_sequence_.push_back(PACKET_SIZE_TEST); | |
526 test_sequence_.push_back(TOKEN_REQUEST); | |
527 test_sequence_.push_back(base::RandInt(0, 1) ? PACED_PACKET_TEST | |
528 : NON_PACED_PACKET_TEST); | |
529 } | |
530 if (received_packets >= kMinimumReceivedPacketsForNATTest) { | |
531 test_sequence_.push_back(TOKEN_REQUEST); | |
532 test_sequence_.push_back(NAT_BIND_TEST); | |
533 test_sequence_.push_back(TOKEN_REQUEST); | |
534 } | |
535 } | |
536 TestPhaseComplete(READ_TIMED_OUT, net::ERR_FAILED); | |
537 } | |
538 | |
539 bool NetworkStats::TestPhaseComplete(Status status, int result) { | |
540 // If there is no valid token, do nothing and delete self. | |
541 // This includes all connection error, name resolve error, etc. | |
542 if (write_state_ == WRITE_STATE_WRITE_PENDING) { | |
543 UMA_HISTOGRAM_BOOLEAN("NetConnectivity5.TestFailed.WritePending", true); | |
544 } else if (status == SUCCESS || status == READ_TIMED_OUT) { | |
545 TestType current_test = test_sequence_[current_test_index_]; | |
546 DCHECK_LT(current_test, TEST_TYPE_MAX); | |
547 if (current_test != TOKEN_REQUEST) { | |
548 RecordHistograms(current_test); | |
549 } else if (current_test_index_ > 0) { | |
550 if (test_sequence_[current_test_index_ - 1] == NAT_BIND_TEST) { | |
551 // We record the NATTestReceivedHistograms after the succeeding | |
552 // TokenRequest. | |
553 RecordNATTestReceivedHistograms(status); | |
554 } else if (test_sequence_[current_test_index_ - 1] == PACKET_SIZE_TEST) { | |
555 // We record the PacketSizeTestReceivedHistograms after the succeeding | |
556 // TokenRequest. | |
557 RecordPacketSizeTestReceivedHistograms(status); | |
558 } | |
559 } | |
560 | |
561 // Move to the next test. | |
562 current_test = GetNextTest(); | |
563 if (current_test_index_ <= maximum_tests_ && current_test < TEST_TYPE_MAX) { | |
564 DVLOG(1) << "NetworkStat: Start Probe test: " << current_test; | |
565 base::MessageLoop::current()->PostTask( | |
566 FROM_HERE, | |
567 base::Bind(&NetworkStats::StartOneTest, weak_factory_.GetWeakPtr())); | |
568 return false; | |
569 } | |
570 } | |
571 | |
572 // All tests are done. | |
573 DoFinishCallback(result); | |
574 | |
575 // Close the socket so that there are no more IO operations. | |
576 if (socket_.get()) | |
577 socket_->Close(); | |
578 | |
579 DVLOG(1) << "NetworkStat: schedule delete self at test index " | |
580 << current_test_index_; | |
581 delete this; | |
582 return true; | |
583 } | |
584 | |
585 NetworkStats::TestType NetworkStats::GetNextTest() { | |
586 ++current_test_index_; | |
587 if (current_test_index_ >= test_sequence_.size()) | |
588 return TEST_TYPE_MAX; | |
589 return test_sequence_[current_test_index_]; | |
590 } | |
591 | |
592 void NetworkStats::DoFinishCallback(int result) { | |
593 if (!finished_callback_.is_null()) { | |
594 net::CompletionCallback callback = finished_callback_; | |
595 finished_callback_.Reset(); | |
596 callback.Run(result); | |
597 } | |
598 } | |
599 | |
600 void NetworkStats::RecordHistograms(TestType test_type) { | |
601 switch (test_type) { | |
602 case START_PACKET_TEST: | |
603 case NON_PACED_PACKET_TEST: | |
604 case PACED_PACKET_TEST: { | |
605 RecordInterArrivalHistograms(test_type); | |
606 RecordPacketLossSeriesHistograms(test_type); | |
607 RecordPacketsReceivedHistograms(test_type); | |
608 // Only record RTT for these packet indices. | |
609 uint32 rtt_indices[] = {0, 1, 2, 9, 19}; | |
610 for (uint32 i = 0; i < arraysize(rtt_indices); ++i) { | |
611 if (rtt_indices[i] < packet_rtt_.size()) | |
612 RecordRTTHistograms(test_type, rtt_indices[i]); | |
613 } | |
614 RecordSendToLastRecvDelayHistograms(test_type); | |
615 return; | |
616 } | |
617 case NAT_BIND_TEST: | |
618 RecordSendToLastRecvDelayHistograms(test_type); | |
619 return; | |
620 case PACKET_SIZE_TEST: | |
621 // No need to record RTT for PacketSizeTest. | |
622 return; | |
623 default: | |
624 DVLOG(1) << "Unexpected test type " << test_type | |
625 << " in RecordHistograms."; | |
626 } | |
627 } | |
628 | |
629 void NetworkStats::RecordInterArrivalHistograms(TestType test_type) { | |
630 DCHECK_NE(test_type, PACKET_SIZE_TEST); | |
631 std::string histogram_name = | |
632 base::StringPrintf("NetConnectivity5.%s.Sent%d.PacketDelay.%d.%dB", | |
633 kTestName[test_type], | |
634 maximum_sequential_packets_, | |
635 histogram_port_, | |
636 probe_packet_bytes_); | |
637 // Record the time normalized to 20 packet inter-arrivals. | |
638 DynamicHistogramTimes(histogram_name, inter_arrival_time_ * 20); | |
639 } | |
640 | |
641 void NetworkStats::RecordPacketsReceivedHistograms(TestType test_type) { | |
642 DCHECK_NE(test_type, PACKET_SIZE_TEST); | |
643 const char* test_name = kTestName[test_type]; | |
644 std::string histogram_prefix = base::StringPrintf( | |
645 "NetConnectivity5.%s.Sent%d.", test_name, maximum_sequential_packets_); | |
646 std::string histogram_suffix = | |
647 base::StringPrintf(".%d.%dB", histogram_port_, probe_packet_bytes_); | |
648 std::string name = histogram_prefix + "GotAPacket" + histogram_suffix; | |
649 base::HistogramBase* histogram_pointer = base::BooleanHistogram::FactoryGet( | |
650 name, base::HistogramBase::kUmaTargetedHistogramFlag); | |
651 histogram_pointer->Add(packets_received_mask_.any()); | |
652 | |
653 DynamicHistogramEnumeration( | |
654 histogram_prefix + "PacketsRecv" + histogram_suffix, | |
655 packets_received_mask_.count(), | |
656 maximum_sequential_packets_ + 1); | |
657 | |
658 if (!packets_received_mask_.any()) | |
659 return; | |
660 | |
661 base::HistogramBase* received_nth_packet_histogram = | |
662 base::Histogram::FactoryGet( | |
663 histogram_prefix + "RecvNthPacket" + histogram_suffix, | |
664 1, | |
665 maximum_sequential_packets_ + 1, | |
666 maximum_sequential_packets_ + 2, | |
667 base::HistogramBase::kUmaTargetedHistogramFlag); | |
668 | |
669 int count = 0; | |
670 for (size_t j = 0; j < maximum_sequential_packets_; ++j) { | |
671 int packet_number = j + 1; | |
672 if (packets_received_mask_.test(j)) { | |
673 received_nth_packet_histogram->Add(packet_number); | |
674 ++count; | |
675 } | |
676 std::string histogram_name = | |
677 base::StringPrintf("%sNumRecvFromFirst%02dPackets%s", | |
678 histogram_prefix.c_str(), | |
679 packet_number, | |
680 histogram_suffix.c_str()); | |
681 DynamicHistogramEnumeration(histogram_name, count, packet_number + 1); | |
682 } | |
683 } | |
684 | |
685 void NetworkStats::RecordNATTestReceivedHistograms(Status status) { | |
686 const char* test_name = kTestName[NAT_BIND_TEST]; | |
687 bool test_result = status == SUCCESS; | |
688 std::string middle_name = test_result ? "Connectivity.Success" | |
689 : "Connectivity.Failure"; | |
690 // Record whether the HelloRequest got reply successfully. | |
691 std::string histogram_name = | |
692 base::StringPrintf("NetConnectivity5.%s.Sent%d.%s.%d.%dB", | |
693 test_name, | |
694 maximum_NAT_packets_, | |
695 middle_name.c_str(), | |
696 histogram_port_, | |
697 probe_packet_bytes_); | |
698 uint32 bucket_count = std::min(maximum_NAT_idle_seconds_ + 2, 50U); | |
699 DynamicHistogramCounts(histogram_name, | |
700 pacing_interval_.InSeconds(), | |
701 1, | |
702 maximum_NAT_idle_seconds_ + 1, | |
703 bucket_count); | |
704 | |
705 // Record the NAT bind result only if the HelloRequest successfully got the | |
706 // token and the first NAT test packet is received. | |
707 if (!test_result || !packets_received_mask_.test(0)) | |
708 return; | |
709 | |
710 middle_name = packets_received_mask_.test(1) ? "Bind.Success" | |
711 : "Bind.Failure"; | |
712 histogram_name = base::StringPrintf("NetConnectivity5.%s.Sent%d.%s.%d.%dB", | |
713 test_name, | |
714 maximum_NAT_packets_, | |
715 middle_name.c_str(), | |
716 histogram_port_, | |
717 probe_packet_bytes_); | |
718 DynamicHistogramCounts(histogram_name, | |
719 pacing_interval_.InSeconds(), | |
720 1, | |
721 maximum_NAT_idle_seconds_ + 1, | |
722 bucket_count); | |
723 } | |
724 | |
725 void NetworkStats::RecordPacketSizeTestReceivedHistograms(Status status) { | |
726 const char* test_name = kTestName[PACKET_SIZE_TEST]; | |
727 bool test_result = (status == SUCCESS && packets_received_mask_.test(0)); | |
728 std::string middle_name = test_result ? "Connectivity.Success" | |
729 : "Connectivity.Failure"; | |
730 // Record whether the HelloRequest got reply successfully. | |
731 std::string histogram_name = | |
732 base::StringPrintf("NetConnectivity5.%s.%s.%d", | |
733 test_name, | |
734 middle_name.c_str(), | |
735 histogram_port_); | |
736 base::HistogramBase* histogram_pointer = base::LinearHistogram::FactoryGet( | |
737 histogram_name, kProbePacketBytes[kPacketSizeChoices - 1], | |
738 ProbeMessage::kMaxProbePacketBytes, 60, | |
739 base::HistogramBase::kUmaTargetedHistogramFlag); | |
740 histogram_pointer->Add(bytes_for_packet_size_test_); | |
741 } | |
742 | |
743 void NetworkStats::RecordPacketLossSeriesHistograms(TestType test_type) { | |
744 DCHECK_NE(test_type, PACKET_SIZE_TEST); | |
745 const char* test_name = kTestName[test_type]; | |
746 // Build "NetConnectivity5.<TestName>.First6.SeriesRecv.<port>.<probe_size>" | |
747 // histogram name. Total 3(tests) x 12 histograms. | |
748 std::string series_acked_histogram_name = | |
749 base::StringPrintf("NetConnectivity5.%s.First6.SeriesRecv.%d.%dB", | |
750 test_name, | |
751 histogram_port_, | |
752 probe_packet_bytes_); | |
753 uint32 histogram_boundary = 1 << kCorrelatedLossPacketCount; | |
754 uint32 correlated_packet_mask = | |
755 (histogram_boundary - 1) & packets_received_mask_.to_ulong(); | |
756 DynamicHistogramEnumeration( | |
757 series_acked_histogram_name, correlated_packet_mask, histogram_boundary); | |
758 | |
759 // If we are running without a proxy, we'll generate an extra histogram with | |
760 // the ".NoProxy" suffix. | |
761 if (!has_proxy_server_) { | |
762 series_acked_histogram_name.append(".NoProxy"); | |
763 DynamicHistogramEnumeration(series_acked_histogram_name, | |
764 correlated_packet_mask, | |
765 histogram_boundary); | |
766 } | |
767 } | |
768 | |
769 void NetworkStats::RecordRTTHistograms(TestType test_type, uint32 index) { | |
770 DCHECK_NE(test_type, PACKET_SIZE_TEST); | |
771 DCHECK_LT(index, packet_rtt_.size()); | |
772 | |
773 if (!packets_received_mask_.test(index)) | |
774 return; // Probe packet never received. | |
775 | |
776 std::string rtt_histogram_name = base::StringPrintf( | |
777 "NetConnectivity5.%s.Sent%d.Success.RTT.Packet%02d.%d.%dB", | |
778 kTestName[test_type], | |
779 maximum_sequential_packets_, | |
780 index + 1, | |
781 histogram_port_, | |
782 probe_packet_bytes_); | |
783 DynamicHistogramTimes(rtt_histogram_name, packet_rtt_[index]); | |
784 } | |
785 | |
786 void NetworkStats::RecordSendToLastRecvDelayHistograms(TestType test_type) { | |
787 DCHECK_NE(test_type, PACKET_SIZE_TEST); | |
788 if (packets_received_mask_.count() < 2) | |
789 return; // Too few packets are received. | |
790 uint32 packets_sent = test_type == NAT_BIND_TEST | |
791 ? maximum_NAT_packets_ : maximum_sequential_packets_; | |
792 std::string histogram_name = base::StringPrintf( | |
793 "NetConnectivity5.%s.Sent%d.SendToLastRecvDelay.%d.%dB", | |
794 kTestName[test_type], | |
795 packets_sent, | |
796 histogram_port_, | |
797 probe_packet_bytes_); | |
798 base::TimeDelta send_to_last_recv_time = | |
799 std::max(last_arrival_time_ - probe_request_time_ - | |
800 pacing_interval_ * (packets_sent - 1), | |
801 base::TimeDelta::FromMilliseconds(0)); | |
802 DynamicHistogramTimes(histogram_name, send_to_last_recv_time); | |
803 } | |
804 | |
805 // ProxyDetector methods and members. | |
806 ProxyDetector::ProxyDetector(net::ProxyService* proxy_service, | |
807 const net::HostPortPair& server_address, | |
808 OnResolvedCallback callback) | |
809 : proxy_service_(proxy_service), | |
810 server_address_(server_address), | |
811 callback_(callback), | |
812 has_pending_proxy_resolution_(false) {} | |
813 | |
814 ProxyDetector::~ProxyDetector() { | |
815 CHECK(!has_pending_proxy_resolution_); | |
816 } | |
817 | |
818 void ProxyDetector::StartResolveProxy() { | |
819 std::string url = | |
820 base::StringPrintf("https://%s", server_address_.ToString().c_str()); | |
821 GURL gurl(url); | |
822 | |
823 has_pending_proxy_resolution_ = true; | |
824 DCHECK(proxy_service_); | |
825 int rv = proxy_service_->ResolveProxy( | |
826 gurl, | |
827 net::LOAD_NORMAL, | |
828 &proxy_info_, | |
829 base::Bind(&ProxyDetector::OnResolveProxyComplete, | |
830 base::Unretained(this)), | |
831 NULL, | |
832 NULL, | |
833 net::BoundNetLog()); | |
834 if (rv != net::ERR_IO_PENDING) | |
835 OnResolveProxyComplete(rv); | |
836 } | |
837 | |
838 void ProxyDetector::OnResolveProxyComplete(int result) { | |
839 has_pending_proxy_resolution_ = false; | |
840 bool has_proxy_server = | |
841 (result == net::OK && proxy_info_.proxy_server().is_valid() && | |
842 !proxy_info_.proxy_server().is_direct()); | |
843 | |
844 OnResolvedCallback callback = callback_; | |
845 BrowserThread::PostTask( | |
846 BrowserThread::IO, FROM_HERE, base::Bind(callback, has_proxy_server)); | |
847 | |
848 // TODO(rtenneti): Will we leak if ProxyResolve is cancelled (or proxy | |
849 // resolution never completes). | |
850 delete this; | |
851 } | |
852 | |
853 void CollectNetworkStats(const std::string& network_stats_server, | |
854 IOThread* io_thread) { | |
855 if (network_stats_server.empty()) | |
856 return; | |
857 | |
858 // If we are not on IO Thread, then post a task to call CollectNetworkStats on | |
859 // IO Thread. | |
860 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
861 BrowserThread::PostTask( | |
862 BrowserThread::IO, | |
863 FROM_HERE, | |
864 base::Bind(&CollectNetworkStats, network_stats_server, io_thread)); | |
865 return; | |
866 } | |
867 | |
868 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
869 | |
870 if (net::NetworkChangeNotifier::IsOffline()) { | |
871 return; | |
872 } | |
873 | |
874 CR_DEFINE_STATIC_LOCAL(scoped_refptr<base::FieldTrial>, trial, ()); | |
875 static bool collect_stats = false; | |
876 | |
877 if (!trial.get()) { | |
878 // Set up a field trial to collect network stats for UDP. | |
879 const base::FieldTrial::Probability kDivisor = 1000; | |
880 | |
881 // Enable the connectivity testing for 0.5% of the users in stable channel. | |
882 base::FieldTrial::Probability probability_per_group = kDivisor / 200; | |
883 | |
884 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); | |
885 if (channel == chrome::VersionInfo::CHANNEL_CANARY) { | |
886 // Enable the connectivity testing for 50% of the users in canary channel. | |
887 probability_per_group = kDivisor / 2; | |
888 } else if (channel == chrome::VersionInfo::CHANNEL_DEV) { | |
889 // Enable the connectivity testing for 10% of the users in dev channel. | |
890 probability_per_group = kDivisor / 10; | |
891 } else if (channel == chrome::VersionInfo::CHANNEL_BETA) { | |
892 // Enable the connectivity testing for 1% of the users in beta channel. | |
893 probability_per_group = kDivisor / 100; | |
894 } | |
895 | |
896 // After July 31, 2014 builds, it will always be in default group | |
897 // (disable_network_stats). | |
898 trial = base::FieldTrialList::FactoryGetFieldTrial( | |
899 "NetworkConnectivity", kDivisor, "disable_network_stats", | |
900 2014, 7, 31, base::FieldTrial::SESSION_RANDOMIZED, NULL); | |
901 | |
902 // Add option to collect_stats for NetworkConnectivity. | |
903 int collect_stats_group = | |
904 trial->AppendGroup("collect_stats", probability_per_group); | |
905 if (trial->group() == collect_stats_group) | |
906 collect_stats = true; | |
907 } | |
908 | |
909 if (!collect_stats) | |
910 return; | |
911 | |
912 // Run test kMaxNumberOfTests times. | |
913 const size_t kMaxNumberOfTests = INT_MAX; | |
914 static size_t number_of_tests_done = 0; | |
915 if (number_of_tests_done > kMaxNumberOfTests) | |
916 return; | |
917 ++number_of_tests_done; | |
918 | |
919 net::HostResolver* host_resolver = io_thread->globals()->host_resolver.get(); | |
920 DCHECK(host_resolver); | |
921 | |
922 uint32 port_index = base::RandInt(0, arraysize(kPorts) - 1); | |
923 uint16 histogram_port = kPorts[port_index]; | |
924 net::HostPortPair server_address(network_stats_server, histogram_port); | |
925 | |
926 net::ProxyService* proxy_service = | |
927 io_thread->globals()->system_proxy_service.get(); | |
928 DCHECK(proxy_service); | |
929 | |
930 ProxyDetector::OnResolvedCallback callback = base::Bind( | |
931 &StartNetworkStatsTest, host_resolver, server_address, histogram_port); | |
932 | |
933 ProxyDetector* proxy_client = | |
934 new ProxyDetector(proxy_service, server_address, callback); | |
935 proxy_client->StartResolveProxy(); | |
936 } | |
937 | |
938 void StartNetworkStatsTest(net::HostResolver* host_resolver, | |
939 const net::HostPortPair& server_address, | |
940 uint16 histogram_port, | |
941 bool has_proxy_server) { | |
942 int probe_choice = base::RandInt(0, kPacketSizeChoices - 1); | |
943 | |
944 DCHECK_LE(ProbeMessage::kMaxProbePacketBytes, kMaxMessageSize); | |
945 // Pick a packet size between 1200 and kMaxProbePacketBytes bytes. | |
946 uint32 bytes_for_packet_size_test = | |
947 base::RandInt(kProbePacketBytes[kPacketSizeChoices - 1], | |
948 ProbeMessage::kMaxProbePacketBytes); | |
949 | |
950 // |udp_stats_client| is owned and deleted in the class NetworkStats. | |
951 NetworkStats* udp_stats_client = | |
952 new NetworkStats(net::ClientSocketFactory::GetDefaultFactory()); | |
953 udp_stats_client->Start(host_resolver, | |
954 server_address, | |
955 histogram_port, | |
956 has_proxy_server, | |
957 kProbePacketBytes[probe_choice], | |
958 bytes_for_packet_size_test, | |
959 net::CompletionCallback()); | |
960 } | |
961 | |
962 } // namespace chrome_browser_net | |
OLD | NEW |