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 |