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