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