| 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 |