Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 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/callback_old.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "base/message_loop.h" | |
| 10 #include "base/metrics/field_trial.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "base/task.h" | |
| 13 #include "base/threading/platform_thread.h" | |
| 14 #include "base/time.h" | |
| 15 #include "base/tuple.h" | |
| 16 #include "content/browser/browser_thread.h" | |
| 17 #include "net/base/net_errors.h" | |
| 18 #include "net/base/net_util.h" | |
| 19 #include "net/base/network_change_notifier.h" | |
| 20 #include "net/base/sys_addrinfo.h" | |
| 21 #include "net/base/test_completion_callback.h" | |
| 22 #include "net/socket/tcp_client_socket.h" | |
| 23 #include "net/udp/udp_client_socket.h" | |
| 24 #include "net/udp/udp_server_socket.h" | |
| 25 | |
| 26 namespace chrome_browser_net { | |
| 27 | |
| 28 // This specifies the number of bytes to be sent to the TCP/UDP servers as part | |
| 29 // of small packet size test. | |
| 30 static const int kSmallTestBytesToSend = 100; | |
| 31 | |
| 32 // This specifies the number of bytes to be sent to the TCP/UDP servers as part | |
| 33 // of large packet size test. | |
| 34 static const int kLargeTestBytesToSend = 1200; | |
| 35 | |
| 36 // NetworkStats methods and members. | |
| 37 NetworkStats::NetworkStats() | |
| 38 : socket_(NULL), | |
| 39 bytes_to_read_(0), | |
| 40 bytes_to_send_(0), | |
| 41 ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 42 read_callback_(this, &NetworkStats::OnReadComplete)), | |
| 43 ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 44 write_callback_(this, &NetworkStats::OnWriteComplete)), | |
| 45 finished_callback_(NULL), | |
| 46 start_time_(base::TimeTicks::Now()) { | |
| 47 } | |
| 48 | |
| 49 NetworkStats::~NetworkStats() { | |
| 50 if (socket_) { | |
| 51 delete socket_; | |
| 52 socket_ = NULL; | |
| 53 } | |
| 54 read_buffer_ = NULL; | |
|
willchan no longer on Chromium
2011/06/08 14:07:02
Is this necessary? We already use a scoped_refptr.
ramant (doing other things)
2011/06/08 17:47:17
Done.
| |
| 55 } | |
| 56 | |
| 57 void NetworkStats::Initialize(int bytes_to_send, | |
| 58 net::CompletionCallback* finished_callback) { | |
| 59 DCHECK(bytes_to_send); // We should have data to send. | |
| 60 | |
| 61 load_size_ = bytes_to_send; | |
| 62 bytes_to_send_ = bytes_to_send; | |
| 63 bytes_to_read_ = bytes_to_send; | |
| 64 finished_callback_ = finished_callback; | |
| 65 } | |
| 66 | |
| 67 bool NetworkStats::DoStart(int result) { | |
| 68 if (result < 0) { | |
| 69 Finish(CONNECT_FAILED, result); | |
| 70 return false; | |
| 71 } | |
| 72 | |
| 73 DCHECK(bytes_to_send_); // We should have data to send. | |
| 74 | |
| 75 start_time_ = base::TimeTicks::Now(); | |
| 76 | |
| 77 int rv = SendData(); | |
| 78 if (rv < 0) { | |
| 79 if (rv != net::ERR_IO_PENDING) { | |
| 80 Finish(WRITE_FAILED, rv); | |
| 81 return false; | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 stream_.Reset(); | |
| 86 ReadData(); | |
| 87 | |
| 88 return true; | |
| 89 } | |
| 90 | |
| 91 void NetworkStats::DoFinishCallback(int result) { | |
| 92 if (finished_callback_ != NULL) { | |
| 93 net::CompletionCallback* callback = finished_callback_; | |
| 94 finished_callback_ = NULL; | |
| 95 callback->Run(result); | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 void NetworkStats::set_socket(net::Socket* socket) { | |
| 100 DCHECK(socket); | |
| 101 DCHECK(!socket_); | |
| 102 socket_ = socket; | |
| 103 } | |
| 104 | |
| 105 bool NetworkStats::ReadComplete(int result) { | |
|
willchan no longer on Chromium
2011/06/08 14:07:02
Add a DCHECK_NE(net::ERR_IO_PENDING, result);
ramant (doing other things)
2011/06/08 17:47:17
Done.
| |
| 106 DCHECK(socket_); | |
| 107 if (result < 0) { | |
| 108 Finish(READ_FAILED, result); | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 if (!stream_.VerifyBytes(read_buffer_->data(), result)) { | |
| 113 Finish(READ_VERIFY_FAILED, net::ERR_INVALID_RESPONSE); | |
| 114 return true; | |
| 115 } | |
| 116 | |
| 117 read_buffer_ = NULL; | |
| 118 bytes_to_read_ -= result; | |
| 119 | |
| 120 // No more data to read. | |
| 121 if (!bytes_to_read_) { | |
| 122 Finish(SUCCESS, net::OK); | |
| 123 return true; | |
| 124 } | |
| 125 ReadData(); | |
| 126 return false; | |
| 127 } | |
| 128 | |
| 129 void NetworkStats::OnReadComplete(int result) { | |
| 130 ReadComplete(result); | |
| 131 } | |
| 132 | |
| 133 void NetworkStats::OnWriteComplete(int result) { | |
|
willchan no longer on Chromium
2011/06/08 14:07:02
DCHECK_NE(net::ERR_IO_PENDING, result)
ramant (doing other things)
2011/06/08 17:47:17
Done.
| |
| 134 DCHECK(socket_); | |
| 135 if (result < 0) { | |
| 136 Finish(WRITE_FAILED, result); | |
| 137 return; | |
| 138 } | |
| 139 | |
| 140 write_buffer_->DidConsume(result); | |
| 141 bytes_to_send_ -= result; | |
| 142 if (!write_buffer_->BytesRemaining()) | |
| 143 write_buffer_ = NULL; | |
| 144 | |
| 145 if (bytes_to_send_) { | |
| 146 int rv = SendData(); | |
| 147 if (rv < 0) { | |
| 148 if (rv != net::ERR_IO_PENDING) { | |
| 149 Finish(WRITE_FAILED, rv); | |
| 150 return; | |
| 151 } | |
| 152 } | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 void NetworkStats::ReadData() { | |
| 157 DCHECK(!read_buffer_.get()); | |
| 158 int kMaxMessage = 2048; | |
| 159 | |
| 160 // We release the read_buffer_ in the destructor if there is an error. | |
| 161 read_buffer_ = new net::IOBuffer(kMaxMessage); | |
| 162 | |
| 163 int rv; | |
| 164 do { | |
| 165 DCHECK(socket_); | |
| 166 rv = socket_->Read(read_buffer_, kMaxMessage, &read_callback_); | |
| 167 if (rv == net::ERR_IO_PENDING) | |
| 168 return; | |
| 169 if (ReadComplete(rv)) // Complete the read manually. | |
| 170 return; | |
| 171 } while (rv > 0); | |
| 172 } | |
| 173 | |
| 174 int NetworkStats::SendData() { | |
| 175 DCHECK(bytes_to_send_); // We should have data to send. | |
| 176 do { | |
| 177 if (!write_buffer_.get()) { | |
| 178 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(bytes_to_send_)); | |
| 179 stream_.GetBytes(buffer->data(), bytes_to_send_); | |
| 180 write_buffer_ = new net::DrainableIOBuffer(buffer, bytes_to_send_); | |
| 181 } | |
| 182 | |
| 183 DCHECK(socket_); | |
| 184 int rv = socket_->Write(write_buffer_, | |
| 185 write_buffer_->BytesRemaining(), | |
| 186 &write_callback_); | |
| 187 if (rv < 0) | |
| 188 return rv; | |
| 189 write_buffer_->DidConsume(rv); | |
| 190 bytes_to_send_ -= rv; | |
| 191 if (!write_buffer_->BytesRemaining()) | |
| 192 write_buffer_ = NULL; | |
| 193 } while (bytes_to_send_); | |
| 194 return net::OK; | |
| 195 } | |
| 196 | |
| 197 // UDPStatsClient methods and members. | |
| 198 UDPStatsClient::UDPStatsClient() | |
| 199 : NetworkStats() { | |
| 200 } | |
| 201 | |
| 202 UDPStatsClient::~UDPStatsClient() { | |
| 203 } | |
| 204 | |
| 205 bool UDPStatsClient::Start(const std::string& ip_str, | |
| 206 int port, | |
| 207 int bytes_to_send, | |
| 208 net::CompletionCallback* finished_callback) { | |
| 209 DCHECK(port); | |
| 210 DCHECK(bytes_to_send); // We should have data to send. | |
| 211 | |
| 212 Initialize(bytes_to_send, finished_callback); | |
| 213 | |
| 214 net::IPAddressNumber ip_number; | |
| 215 if (!net::ParseIPLiteralToNumber(ip_str, &ip_number)) { | |
| 216 Finish(IP_STRING_PARSE_FAILED, net::ERR_INVALID_ARGUMENT); | |
| 217 return false; | |
| 218 } | |
| 219 net::IPEndPoint server_address = net::IPEndPoint(ip_number, port); | |
| 220 | |
| 221 net::UDPClientSocket* udp_socket = | |
| 222 new net::UDPClientSocket(NULL, net::NetLog::Source()); | |
| 223 DCHECK(udp_socket); | |
| 224 set_socket(udp_socket); | |
| 225 | |
| 226 int rv = udp_socket->Connect(server_address); | |
| 227 return DoStart(rv); | |
| 228 } | |
| 229 | |
| 230 void UDPStatsClient::Finish(Status status, int result) { | |
| 231 base::TimeDelta duration = base::TimeTicks::Now() - start_time(); | |
| 232 if (load_size() == kSmallTestBytesToSend) { | |
| 233 if (result == net::OK) | |
| 234 UMA_HISTOGRAM_TIMES("NetConnectivity.UDP.Success.100B.RTT", duration); | |
| 235 else | |
| 236 UMA_HISTOGRAM_TIMES("NetConnectivity.UDP.Fail.100B.RTT", duration); | |
| 237 | |
| 238 UMA_HISTOGRAM_ENUMERATION( | |
| 239 "NetConnectivity.UDP.Status.100B", status, STATUS_MAX); | |
| 240 } else { | |
| 241 if (result == net::OK) | |
| 242 UMA_HISTOGRAM_TIMES("NetConnectivity.UDP.Success.1K.RTT", duration); | |
| 243 else | |
| 244 UMA_HISTOGRAM_TIMES("NetConnectivity.UDP.Fail.1K.RTT", duration); | |
| 245 | |
| 246 UMA_HISTOGRAM_ENUMERATION( | |
| 247 "NetConnectivity.UDP.Status.1K", status, STATUS_MAX); | |
| 248 } | |
| 249 | |
| 250 DoFinishCallback(result); | |
| 251 | |
| 252 // Close the socket so that there are no more IO operations. | |
| 253 net::UDPClientSocket* udp_socket = | |
| 254 static_cast<net::UDPClientSocket*>(socket()); | |
| 255 if (udp_socket) | |
| 256 udp_socket->Close(); | |
| 257 | |
| 258 delete this; | |
| 259 } | |
| 260 | |
| 261 // TCPStatsClient methods and members. | |
| 262 TCPStatsClient::TCPStatsClient() | |
| 263 : NetworkStats(), | |
| 264 ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 265 resolve_callback_(this, &TCPStatsClient::OnResolveComplete)), | |
| 266 ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 267 connect_callback_(this, &TCPStatsClient::OnConnectComplete)) { | |
| 268 } | |
| 269 | |
| 270 TCPStatsClient::~TCPStatsClient() { | |
| 271 } | |
| 272 | |
| 273 bool TCPStatsClient::Start(net::HostResolver* host_resolver, | |
| 274 const net::HostPortPair& server_host_port_pair, | |
| 275 int bytes_to_send, | |
| 276 net::CompletionCallback* finished_callback) { | |
| 277 DCHECK(bytes_to_send); // We should have data to send. | |
| 278 | |
| 279 Initialize(bytes_to_send, finished_callback); | |
| 280 | |
| 281 net::HostResolver::RequestInfo request(server_host_port_pair); | |
| 282 int rv = host_resolver->Resolve(request, | |
| 283 &addresses_, | |
| 284 &resolve_callback_, | |
| 285 NULL, | |
| 286 net::BoundNetLog()); | |
| 287 if (rv == net::ERR_IO_PENDING) | |
| 288 return true; | |
| 289 return DoConnect(rv); | |
| 290 } | |
| 291 | |
| 292 void TCPStatsClient::OnResolveComplete(int result) { | |
| 293 DoConnect(result); | |
| 294 } | |
| 295 | |
| 296 bool TCPStatsClient::DoConnect(int result) { | |
| 297 if (result != net::OK) { | |
| 298 Finish(RESOLVE_FAILED, result); | |
| 299 return false; | |
| 300 } | |
| 301 | |
| 302 net::TCPClientSocket* tcp_socket = | |
| 303 new net::TCPClientSocket(addresses_, NULL, net::NetLog::Source()); | |
| 304 DCHECK(tcp_socket); | |
| 305 set_socket(tcp_socket); | |
| 306 | |
| 307 int rv = tcp_socket->Connect(&connect_callback_); | |
| 308 if (rv == net::ERR_IO_PENDING) | |
| 309 return true; | |
| 310 | |
| 311 return DoStart(rv); | |
| 312 } | |
| 313 | |
| 314 void TCPStatsClient::OnConnectComplete(int result) { | |
| 315 DoStart(result); | |
| 316 } | |
| 317 | |
| 318 void TCPStatsClient::Finish(Status status, int result) { | |
| 319 base::TimeDelta duration = base::TimeTicks::Now() - start_time(); | |
| 320 if (load_size() == kSmallTestBytesToSend) { | |
| 321 if (result == net::OK) | |
| 322 UMA_HISTOGRAM_TIMES("NetConnectivity.TCP.Success.100B.RTT", duration); | |
| 323 else | |
| 324 UMA_HISTOGRAM_TIMES("NetConnectivity.TCP.Fail.100B.RTT", duration); | |
| 325 | |
| 326 UMA_HISTOGRAM_ENUMERATION( | |
| 327 "NetConnectivity.TCP.Status.100B", status, STATUS_MAX); | |
| 328 } else { | |
| 329 if (result == net::OK) | |
| 330 UMA_HISTOGRAM_TIMES("NetConnectivity.TCP.Success.1K.RTT", duration); | |
| 331 else | |
| 332 UMA_HISTOGRAM_TIMES("NetConnectivity.TCP.Fail.1K.RTT", duration); | |
| 333 | |
| 334 UMA_HISTOGRAM_ENUMERATION( | |
| 335 "NetConnectivity.TCP.Status.1K", status, STATUS_MAX); | |
| 336 } | |
| 337 | |
| 338 DoFinishCallback(result); | |
| 339 | |
| 340 // Disconnect the socket so that there are no more IO operations. | |
| 341 net::TCPClientSocket* tcp_socket = | |
| 342 static_cast<net::TCPClientSocket*>(socket()); | |
| 343 if (tcp_socket) | |
| 344 tcp_socket->Disconnect(); | |
| 345 | |
| 346 delete this; | |
| 347 } | |
| 348 | |
| 349 // static | |
| 350 void CollectNetworkStats(const std::string& network_stats_server, | |
| 351 IOThread* io_thread) { | |
| 352 if (network_stats_server.empty()) | |
| 353 return; | |
| 354 | |
| 355 // If we are not on IO Thread, then post a task to call CollectNetworkStats on | |
| 356 // IO Thread. | |
| 357 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { | |
| 358 BrowserThread::PostTask( | |
| 359 BrowserThread::IO, | |
| 360 FROM_HERE, | |
| 361 NewRunnableFunction( | |
| 362 &CollectNetworkStats, network_stats_server, io_thread)); | |
| 363 return; | |
| 364 } | |
| 365 | |
| 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 367 | |
| 368 // Check that there is a network connection. We get called only if UMA upload | |
| 369 // to the server has succeeded. | |
| 370 DCHECK(!net::NetworkChangeNotifier::IsOffline()); | |
| 371 | |
| 372 static scoped_refptr<base::FieldTrial> trial = NULL; | |
| 373 static bool collect_stats = false; | |
| 374 | |
| 375 if (!trial.get()) { | |
| 376 // Set up a field trial to collect network stats for UDP and TCP. | |
| 377 base::FieldTrial::Probability kDivisor = 1000; | |
| 378 | |
| 379 // Enable the connectivity testing for 0.5% of the users. | |
| 380 base::FieldTrial::Probability kProbabilityPerGroup = 5; | |
| 381 | |
| 382 // After October 30, 2011 builds, it will always be in default group | |
| 383 // (disable_network_stats). | |
| 384 trial = new base::FieldTrial("NetworkConnectivity", kDivisor, | |
| 385 "disable_network_stats", 2011, 10, 30); | |
| 386 | |
| 387 // Add option to collect_stats for NetworkConnectivity. | |
| 388 int collect_stats_group = trial->AppendGroup("collect_stats", | |
| 389 kProbabilityPerGroup); | |
| 390 if (trial->group() == collect_stats_group) | |
| 391 collect_stats = true; | |
| 392 } | |
| 393 | |
| 394 if (!collect_stats) | |
| 395 return; | |
| 396 | |
| 397 // Run test kMaxNumberOfTests times. | |
| 398 size_t kMaxNumberOfTests = INT_MAX; | |
|
willchan no longer on Chromium
2011/06/08 14:07:02
const size_t
ramant (doing other things)
2011/06/08 17:47:17
Done.
| |
| 399 static size_t number_of_tests_done = 0; | |
| 400 if (number_of_tests_done > kMaxNumberOfTests) | |
| 401 return; | |
| 402 | |
| 403 ++number_of_tests_done; | |
| 404 | |
| 405 // Use SPDY's UDP port per http://www.iana.org/assignments/port-numbers. | |
| 406 // |network_stats_server| echo TCP and UDP servers listen on the following | |
| 407 // ports. | |
| 408 uint32 kTCPTestingPort = 80; | |
| 409 uint32 kUDPTestingPort = 6121; | |
| 410 | |
| 411 UDPStatsClient* small_udp_stats = new UDPStatsClient(); | |
| 412 small_udp_stats->Start( | |
| 413 network_stats_server, kUDPTestingPort, kSmallTestBytesToSend, NULL); | |
| 414 | |
| 415 UDPStatsClient* large_udp_stats = new UDPStatsClient(); | |
| 416 large_udp_stats->Start( | |
| 417 network_stats_server, kUDPTestingPort, kLargeTestBytesToSend, NULL); | |
| 418 | |
| 419 net::HostResolver* host_resolver = io_thread->globals()->host_resolver.get(); | |
| 420 DCHECK(host_resolver); | |
| 421 | |
| 422 net::HostPortPair server_address(network_stats_server, kTCPTestingPort); | |
| 423 | |
| 424 TCPStatsClient* small_tcp_client = new TCPStatsClient(); | |
| 425 small_tcp_client->Start(host_resolver, server_address, kSmallTestBytesToSend, | |
| 426 NULL); | |
| 427 | |
| 428 TCPStatsClient* large_tcp_client = new TCPStatsClient(); | |
| 429 large_tcp_client->Start(host_resolver, server_address, kLargeTestBytesToSend, | |
| 430 NULL); | |
| 431 } | |
| 432 | |
| 433 } // namespace chrome_browser_net | |
| OLD | NEW |