diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-06 03:09:29 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-06 03:09:29 +0000 |
commit | 4baf37d74ab7e5074499618a11669297caa17321 (patch) | |
tree | 337017079d5637e19c6dc169ba0f39fc5d2de1ab /chrome/browser/net | |
parent | 1f7893fa86a8e84abbc460dca12c87346bb2d758 (diff) | |
download | chromium_src-4baf37d74ab7e5074499618a11669297caa17321.zip chromium_src-4baf37d74ab7e5074499618a11669297caa17321.tar.gz chromium_src-4baf37d74ab7e5074499618a11669297caa17321.tar.bz2 |
Network connectivity - packet pacing and FEC tests for packet loss.
After finishing the regular experiment for 21 packets loss test, we
start one of the following two new tests.
1) Pace the packets. The delay between the packets is calculated from
the total time to receive packets in the first experiment (total time
is the time from when we recieved the first packet to the time it has
taken to recieve the last packet). If the total time is 20 secs, then
we send each packet with a delay of 1 sec.
2) Non-paced test. Will send packets as soon as possible.
Deleted TCPStatsClient class and thus no TCP connectivity
tests. Merged UDPStatsClient into NetworkStats class.
R=jar
TEST=unit tests
Review URL: https://chromiumcodereview.appspot.com/10913037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@155111 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/net')
-rw-r--r-- | chrome/browser/net/network_stats.cc | 594 | ||||
-rw-r--r-- | chrome/browser/net/network_stats.h | 355 | ||||
-rw-r--r-- | chrome/browser/net/network_stats_unittest.cc | 286 |
3 files changed, 735 insertions, 500 deletions
diff --git a/chrome/browser/net/network_stats.cc b/chrome/browser/net/network_stats.cc index af7f299..c134f09 100644 --- a/chrome/browser/net/network_stats.cc +++ b/chrome/browser/net/network_stats.cc @@ -22,7 +22,6 @@ #include "net/base/network_change_notifier.h" #include "net/base/test_completion_callback.h" #include "net/proxy/proxy_service.h" -#include "net/socket/tcp_client_socket.h" #include "net/udp/udp_client_socket.h" #include "net/udp/udp_server_socket.h" @@ -30,21 +29,18 @@ using content::BrowserThread; namespace chrome_browser_net { -// This specifies the number of bytes to be sent to the TCP/UDP servers as part +// This specifies the number of bytes to be sent to the UDP echo servers as part // of small packet size test. static const uint32 kSmallTestBytesToSend = 100; -// This specifies the number of bytes to be sent to the TCP/UDP servers as part +// This specifies the number of bytes to be sent to the UDP echo servers as part // of medium packet size test. static const uint32 kMediumTestBytesToSend = 500; -// This specifies the number of bytes to be sent to the TCP/UDP servers as part +// This specifies the number of bytes to be sent to the UDP echo servers as part // of large packet size test. static const uint32 kLargeTestBytesToSend = 1200; -// This specifies the maximum message (payload) size. -static const uint32 kMaxMessage = 2048; - // This specifies starting position of the <version> and length of the // <version> in "echo request" and "echo response". static const uint32 kVersionNumber = 1; @@ -84,11 +80,9 @@ static const uint32 kPacketNumberLength = 10; // "echo response". static const uint32 kEncodedPayloadStart = kKeyEnd; -// HistogramPortSelector and kPorts should be kept in sync. -static const int32 kPorts[] = {53, 80, 587, 6121, 8080, 999999}; - -// The packet number that is to be sent to the server. -static uint32 g_packet_number_ = 0; +// HistogramPortSelector and kPorts should be kept in sync. Port 999999 is +// used by the unit tests. +static const int32 kPorts[] = {6121, 999999}; // Maximum number of packets that can be sent to the server for packet loss // correlation test. @@ -97,16 +91,26 @@ static const uint32 kMaximumCorrelationPackets = 6; // Maximum number of packets that can be sent to the server. static const uint32 kMaximumSequentialPackets = 21; +// This specifies the maximum message (payload) size. +static const uint32 kMaxMessage = kMaximumSequentialPackets * 2048; + // NetworkStats methods and members. NetworkStats::NetworkStats() - : load_size_(0), + : read_buffer_(NULL), + write_buffer_(NULL), + load_size_(0), bytes_to_read_(0), bytes_to_send_(0), has_proxy_server_(false), packets_to_send_(0), packets_sent_(0), - base_packet_number_(0), + packets_received_(0), packets_received_mask_(0), + packet_number_(0), + base_packet_number_(0), + sending_complete_(false), + current_test_(START_PACKET_TEST), + next_test_(TEST_TYPE_MAX), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { } @@ -142,6 +146,17 @@ bool NetworkStats::Start(net::HostResolver* host_resolver, return DoConnect(rv); } +void NetworkStats::RestartPacketTest() { + ResetData(); + current_test_ = next_test_; + next_test_ = TEST_TYPE_MAX; + if (!bytes_to_read_) { + read_buffer_ = NULL; + ReadData(); + } + SendPacket(); +} + void NetworkStats::Initialize( uint32 bytes_to_send, HistogramPortSelector histogram_port, @@ -151,13 +166,65 @@ void NetworkStats::Initialize( DCHECK(bytes_to_send); // We should have data to send. DCHECK(packets_to_send); // We should send at least 1 packet. DCHECK_LE(bytes_to_send, kLargeTestBytesToSend); + DCHECK_LE(packets_to_send, 8 * sizeof(packets_received_mask_)); load_size_ = bytes_to_send; packets_to_send_ = packets_to_send; - histogram_port_ = histogram_port; has_proxy_server_ = has_proxy_server; finished_callback_ = finished_callback; + ResetData(); + packet_number_ = base::RandInt(1 << 28, INT_MAX); +} + +void NetworkStats::ResetData() { + write_buffer_ = NULL; + bytes_to_send_ = 0; + packet_status_.clear(); + packet_status_.resize(packets_to_send_); + packets_sent_ = 0; + packets_received_ = 0; + packets_received_mask_ = 0; + sending_complete_ = false; +} + +void NetworkStats::OnResolveComplete(int result) { + DoConnect(result); +} + +bool NetworkStats::DoConnect(int result) { + if (result != net::OK) { + Finish(RESOLVE_FAILED, result); + return false; + } + + net::UDPClientSocket* udp_socket = + new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND, + net::RandIntCallback(), + NULL, + net::NetLog::Source()); + if (!udp_socket) { + Finish(SOCKET_CREATE_FAILED, net::ERR_INVALID_ARGUMENT); + return false; + } + set_socket(udp_socket); + + if (addresses().empty()) { + Finish(RESOLVE_FAILED, net::ERR_INVALID_ARGUMENT); + return false; + } + + const net::IPEndPoint& endpoint = addresses().front(); + int rv = udp_socket->Connect(endpoint); + if (rv < 0) { + Finish(CONNECT_FAILED, rv); + return false; + } + + const int kSocketBufferSize = 2 * packets_to_send_ * 2048; + udp_socket->SetSendBufferSize(kSocketBufferSize); + udp_socket->SetReceiveBufferSize(kSocketBufferSize); + return ConnectComplete(rv); } bool NetworkStats::ConnectComplete(int result) { @@ -166,24 +233,52 @@ bool NetworkStats::ConnectComplete(int result) { return false; } - base_packet_number_ = g_packet_number_; - start_time_ = base::TimeTicks::Now(); + ReadData(); SendPacket(); return true; } -void NetworkStats::DoFinishCallback(int result) { - if (!finished_callback_.is_null()) { - net::CompletionCallback callback = finished_callback_; - finished_callback_.Reset(); - callback.Run(result); - } +void NetworkStats::SendPacket() { + while (true) { + if (bytes_to_send_ == 0u) { + if (packets_sent_ >= packets_to_send_) { + // Timeout if we don't get response back from echo servers in 30 secs. + sending_complete_ = true; + const int kReadDataTimeoutMs = 30000; + StartReadDataTimer(kReadDataTimeoutMs); + break; + } + + ++packet_number_; + if (packets_sent_ == 0) + base_packet_number_ = packet_number_; + bytes_to_send_ = SendingPacketSize(); + SendNextPacketAfterDelay(); + break; + } + + int rv = SendData(); + if (rv < 0) { + if (rv != net::ERR_IO_PENDING) + Finish(WRITE_FAILED, rv); + break; + } + DCHECK_EQ(bytes_to_send_, 0u); + }; } -void NetworkStats::set_socket(net::Socket* socket) { - DCHECK(socket); - DCHECK(!socket_.get()); - socket_.reset(socket); +void NetworkStats::SendNextPacketAfterDelay() { + if (current_test_ == PACED_PACKET_TEST) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&NetworkStats::SendPacket, weak_factory_.GetWeakPtr()), + average_time_); + return; + } + + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&NetworkStats::SendPacket, weak_factory_.GetWeakPtr())); } bool NetworkStats::ReadComplete(int result) { @@ -194,13 +289,29 @@ bool NetworkStats::ReadComplete(int result) { return true; } - encoded_message_.append(read_buffer_->data(), result); + if (result > 0) { + std::string encoded_message; + encoded_message.append(read_buffer_->data(), result); + if (VerifyBytes(encoded_message) == SUCCESS) { + base::TimeTicks now = base::TimeTicks::Now(); + if (packets_received_ == 0) + packet_1st_byte_read_time_ = now; + packet_last_byte_read_time_ = now; + + DCHECK_GE(bytes_to_read_, static_cast<uint32>(result)); + if (bytes_to_read_ >= static_cast<uint32>(result)) + bytes_to_read_ -= result; + ++packets_received_; + } + } read_buffer_ = NULL; - bytes_to_read_ -= result; // No more data to read. if (!bytes_to_read_ || result == 0) { + if (!sending_complete_) + return false; + Status status = VerifyPackets(); if (status == SUCCESS) Finish(status, net::OK); @@ -211,35 +322,6 @@ bool NetworkStats::ReadComplete(int result) { return false; } -void NetworkStats::OnResolveComplete(int result) { - DoConnect(result); -} - -void NetworkStats::SendPacket() { - DCHECK_EQ(bytes_to_send_, 0u); - uint32 sending_packet_size = SendingPacketSize(); - - while (packets_to_send_ > packets_sent_) { - ++g_packet_number_; - - bytes_to_send_ = sending_packet_size; - int rv = SendData(); - if (rv < 0) { - if (rv != net::ERR_IO_PENDING) - Finish(WRITE_FAILED, rv); - return; - } - DCHECK_EQ(bytes_to_send_, 0u); - } - - // Timeout if we don't get response back from echo servers in 60 secs. - const int kReadDataTimeoutMs = 60000; - StartReadDataTimer(kReadDataTimeoutMs); - - bytes_to_read_ = packets_sent_ * ReceivingPacketSize(); - ReadData(); -} - void NetworkStats::OnReadComplete(int result) { if (!ReadComplete(result)) { // Called ReadData() via PostDelayedTask() to avoid recursion. Added a delay @@ -282,7 +364,7 @@ void NetworkStats::ReadData() { int rv; do { if (!socket_.get()) - return; + break; DCHECK(!read_buffer_.get()); @@ -293,10 +375,11 @@ void NetworkStats::ReadData() { base::Bind(&NetworkStats::OnReadComplete, base::Unretained(this))); if (rv == net::ERR_IO_PENDING) - return; + break; + // If we have read all the data then return. if (ReadComplete(rv)) - return; + break; } while (rv > 0); } @@ -304,10 +387,23 @@ int NetworkStats::SendData() { DCHECK(bytes_to_send_); // We should have data to send. do { if (!write_buffer_.get()) { + // Send a new packet. scoped_refptr<net::IOBufferWithSize> buffer( new net::IOBufferWithSize(bytes_to_send_)); GetEchoRequest(buffer); write_buffer_ = new net::DrainableIOBuffer(buffer, bytes_to_send_); + + // As soon as we write, a read could happen. Thus update all the book + // keeping data. + bytes_to_read_ += ReceivingPacketSize(); + ++packets_sent_; + if (packets_sent_ >= packets_to_send_) + sending_complete_ = true; + + uint32 packet_index = packet_number_ - base_packet_number_; + DCHECK_GE(packet_index, 0u); + DCHECK_LT(packet_index, packet_status_.size()); + packet_status_[packet_index].start_time_ = base::TimeTicks::Now(); } if (!socket_.get()) @@ -337,18 +433,21 @@ void NetworkStats::DidSendData(int bytes_sent) { if (!write_buffer_->BytesRemaining()) write_buffer_ = NULL; bytes_to_send_ -= bytes_sent; - if (bytes_to_send_ == 0) - ++packets_sent_; } void NetworkStats::StartReadDataTimer(int milliseconds) { MessageLoop::current()->PostDelayedTask( FROM_HERE, - base::Bind(&NetworkStats::OnReadDataTimeout, weak_factory_.GetWeakPtr()), + base::Bind(&NetworkStats::OnReadDataTimeout, + weak_factory_.GetWeakPtr(), + base_packet_number_), base::TimeDelta::FromMilliseconds(milliseconds)); } -void NetworkStats::OnReadDataTimeout() { +void NetworkStats::OnReadDataTimeout(uint32 test_base_packet_number) { + if (test_base_packet_number != base_packet_number_) + return; + Status status = VerifyPackets(); if (status == SUCCESS) Finish(status, net::OK); @@ -395,7 +494,7 @@ void NetworkStats::GetEchoRequest(net::IOBufferWithSize* io_buffer) { memcpy(buffer + kVersionStart, version.c_str(), kVersionLength); // Copy the packet_number into the payload. - std::string packet_number = base::StringPrintf("%010d", g_packet_number_); + std::string packet_number = base::StringPrintf("%010d", packet_number_); DCHECK(kPacketNumberLength == packet_number.length()); DCHECK_GE(buffer_size, kPayloadStart + kPacketNumberLength); memcpy(buffer + kPayloadStart, packet_number.c_str(), kPacketNumberLength); @@ -426,24 +525,48 @@ void NetworkStats::GetEchoRequest(net::IOBufferWithSize* io_buffer) { } NetworkStats::Status NetworkStats::VerifyPackets() { - uint32 packet_start = 0; - size_t message_length = encoded_message_.length(); - uint32 receiving_packet_size = ReceivingPacketSize(); Status status = SUCCESS; - for (uint32 i = 0; i < packets_to_send_; i++) { - if (message_length <= packet_start) { - status = ZERO_LENGTH_ERROR; - break; - } - std::string response = - encoded_message_.substr(packet_start, receiving_packet_size); - Status packet_status = VerifyBytes(response); - if (packet_status != SUCCESS) - status = packet_status; - packet_start += receiving_packet_size; + uint32 successful_packets = 0; + + for (uint32 i = 0; i < packet_status_.size(); i++) { + if (packets_received_mask_ & (1 << i)) + ++successful_packets; } - if (message_length > packet_start) + + if (packets_received_ > packets_to_send_) status = TOO_MANY_PACKETS; + + if (packets_to_send_ > successful_packets) + status = SOME_PACKETS_NOT_VERIFIED; + + if (current_test_ == START_PACKET_TEST && + packets_to_send_ == kMaximumSequentialPackets && + successful_packets > 1) { + base::TimeDelta total_time; + if (packet_last_byte_read_time_ > packet_1st_byte_read_time_) { + total_time = + packet_last_byte_read_time_ - packet_1st_byte_read_time_; + } + average_time_ = total_time / (successful_packets - 1); + std::string histogram_name = base::StringPrintf( + "NetConnectivity3.%s.Sent%02d.%d.%dB.PacketDelay", + TestName(), + kMaximumSequentialPackets, + kPorts[histogram_port_], + load_size_); + base::Histogram* histogram = base::Histogram::FactoryTimeGet( + histogram_name, base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromSeconds(30), 50, + base::Histogram::kUmaTargetedHistogramFlag); + histogram->AddTime(total_time); + + int experiment_to_run = base::RandInt(1, 2); + if (experiment_to_run == 1) + next_test_ = NON_PACED_PACKET_TEST; + else + next_test_ = PACED_PACKET_TEST; + } + return status; } @@ -496,9 +619,11 @@ NetworkStats::Status NetworkStats::VerifyBytes(const std::string& response) { char packet_number_data[kPacketNumberLength + 1]; memset(packet_number_data, 0, kPacketNumberLength + 1); memcpy(packet_number_data, decoded_data, kPacketNumberLength); - uint32 packet_number = atoi(packet_number_data); - uint32 packet_index = packet_number - base_packet_number_; - if (packet_index > packets_to_send_) + uint32 packet_number_received = atoi(packet_number_data); + if (packet_number_received < base_packet_number_) + return PREVIOUS_PACKET_NUMBER; + uint32 packet_index = packet_number_received - base_packet_number_; + if (packet_index >= packets_to_send_) return INVALID_PACKET_NUMBER; stream_.Reset(); @@ -507,26 +632,100 @@ NetworkStats::Status NetworkStats::VerifyBytes(const std::string& response) { return PATTERN_CHANGED; } - packets_received_mask_ |= 1 << (packet_index - 1); + if (packets_received_mask_ & (1 << packet_index)) + return DUPLICATE_PACKET; + + packets_received_mask_ |= 1 << packet_index; + DCHECK_GE(packet_index, 0u); + DCHECK_LT(packet_index, packet_status_.size()); + packet_status_[packet_index].end_time_ = base::TimeTicks::Now(); return SUCCESS; } -void NetworkStats::RecordAcksReceivedHistograms(const char* load_size_string) { +void NetworkStats::Finish(Status status, int result) { + // Set the base_packet_number_ for the start of next test. Changing the + // |base_packet_number_| indicates to OnReadDataTimeout that the Finish has + // already been called for the test and that it doesn't need to call Finish + // again. + base_packet_number_ = packet_number_ + 1; + RecordHistograms(PROTOCOL_UDP, status, result); + + if (next_test() == NON_PACED_PACKET_TEST || + next_test() == PACED_PACKET_TEST) { + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&NetworkStats::RestartPacketTest, + weak_factory_.GetWeakPtr())); + return; + } + + DoFinishCallback(result); + + // Close the socket so that there are no more IO operations. + net::UDPClientSocket* udp_socket = + static_cast<net::UDPClientSocket*>(socket()); + if (udp_socket) + udp_socket->Close(); + + delete this; +} + +void NetworkStats::DoFinishCallback(int result) { + if (!finished_callback_.is_null()) { + net::CompletionCallback callback = finished_callback_; + finished_callback_.Reset(); + callback.Run(result); + } +} + +void NetworkStats::RecordHistograms(const ProtocolValue& protocol, + const Status& status, + int result) { + if (packets_to_send_ != kMaximumSequentialPackets && + packets_to_send_ != kMaximumCorrelationPackets) { + return; + } + + std::string load_size_string = base::StringPrintf("%dB", load_size_); + + if (packets_to_send_ == kMaximumCorrelationPackets) { + RecordPacketLossSeriesHistograms( + protocol, load_size_string, status, result); + return; + } + + for (uint32 i = 0; i < 3; i++) + RecordRTTHistograms(protocol, load_size_string, i); + + RecordRTTHistograms(protocol, load_size_string, 9); + RecordRTTHistograms(protocol, load_size_string, 19); + + RecordAcksReceivedHistograms(load_size_string); +} + +void NetworkStats::RecordAcksReceivedHistograms( + const std::string& load_size_string) { + DCHECK_EQ(packets_to_send_, kMaximumSequentialPackets); + + const char* test_name = TestName(); bool received_atleast_one_packet = packets_received_mask_ > 0; + std::string histogram_name = base::StringPrintf( - "NetConnectivity2.Sent%d.GotAnAck.%d.%s", + "NetConnectivity3.%s.Sent%02d.GotAnAck.%d.%s", + test_name, kMaximumSequentialPackets, kPorts[histogram_port_], - load_size_string); + load_size_string.c_str()); base::Histogram* got_an_ack_histogram = base::BooleanHistogram::FactoryGet( histogram_name, base::Histogram::kUmaTargetedHistogramFlag); got_an_ack_histogram->AddBoolean(received_atleast_one_packet); histogram_name = base::StringPrintf( - "NetConnectivity2.Sent%d.PacketsSent.%d.%s", + "NetConnectivity3.%s.Sent%02d.PacketsSent.%d.%s", + test_name, kMaximumSequentialPackets, kPorts[histogram_port_], - load_size_string); + load_size_string.c_str()); base::Histogram* packets_sent_histogram = base::Histogram::FactoryGet( histogram_name, @@ -538,14 +737,15 @@ void NetworkStats::RecordAcksReceivedHistograms(const char* load_size_string) { return; histogram_name = base::StringPrintf( - "NetConnectivity2.Sent%d.AckReceivedForNthPacket.%d.%s", + "NetConnectivity3.%s.Sent%02d.AckReceivedForNthPacket.%02d.%s", + test_name, kMaximumSequentialPackets, kPorts[histogram_port_], - load_size_string); + load_size_string.c_str()); base::Histogram* ack_received_for_nth_packet_histogram = base::Histogram::FactoryGet( histogram_name, - 1, kMaximumSequentialPackets, kMaximumSequentialPackets + 1, + 1, kMaximumSequentialPackets + 1, kMaximumSequentialPackets + 2, base::Histogram::kUmaTargetedHistogramFlag); int count = 0; @@ -558,11 +758,12 @@ void NetworkStats::RecordAcksReceivedHistograms(const char* load_size_string) { if (packet_number < 2) continue; histogram_name = base::StringPrintf( - "NetConnectivity2.Sent%d.AcksReceivedFromFirst%dPackets.%d.%s", + "NetConnectivity3.%s.Sent%02d.AcksReceivedFromFirst%02dPackets.%d.%s", + test_name, kMaximumSequentialPackets, packet_number, kPorts[histogram_port_], - load_size_string); + load_size_string.c_str()); base::Histogram* acks_received_count_histogram = base::Histogram::FactoryGet( histogram_name, 1, packet_number, packet_number + 1, @@ -573,24 +774,27 @@ void NetworkStats::RecordAcksReceivedHistograms(const char* load_size_string) { void NetworkStats::RecordPacketLossSeriesHistograms( const ProtocolValue& protocol, + const std::string& load_size_string, const Status& status, - const char* load_size_string, int result) { - if (packets_to_send_ < 2 || protocol != PROTOCOL_UDP) - return; + DCHECK_EQ(packets_to_send_, kMaximumCorrelationPackets); - // Build "NetConnectivity2.Send6.SeriesAcked.<port>.<load_size>" histogram + const char* test_name = TestName(); + + // Build "NetConnectivity3.Send6.SeriesAcked.<port>.<load_size>" histogram // name. Total number of histograms are 5*2. std::string series_acked_histogram_name = base::StringPrintf( - "NetConnectivity2.Send6.SeriesAcked.%d.%s", + "NetConnectivity3.%s.Send6.SeriesAcked.%d.%s", + test_name, kPorts[histogram_port_], - load_size_string); - // Build "NetConnectivity2.Send6.PacketsSent.<port>.<load_size>" histogram + load_size_string.c_str()); + // Build "NetConnectivity3.Send6.PacketsSent.<port>.<load_size>" histogram // name. Total number of histograms are 5*2. std::string packets_sent_histogram_name = base::StringPrintf( - "NetConnectivity2.Send6.PacketsSent.%d.%s", + "NetConnectivity3.%s.Send6.PacketsSent.%d.%s", + test_name, kPorts[histogram_port_], - load_size_string); + load_size_string.c_str()); // If we are running without a proxy, we'll generate 2 distinct histograms in // each case, one will have the ".NoProxy" suffix. @@ -616,144 +820,48 @@ void NetworkStats::RecordPacketLossSeriesHistograms( } } -void NetworkStats::RecordHistograms(const ProtocolValue& protocol, - const Status& status, - int result) { - // Build <load_size> string. - const char* kSmallLoadString = "100B"; - const char* kMediumLoadString = "500B"; - const char* kLargeLoadString = "1K"; - const char* load_size_string; - if (load_size_ == kSmallTestBytesToSend) - load_size_string = kSmallLoadString; - else if (load_size_ == kMediumTestBytesToSend) - load_size_string = kMediumLoadString; - else - load_size_string = kLargeLoadString; - - if (packets_to_send_ == kMaximumSequentialPackets) { - RecordAcksReceivedHistograms(load_size_string); - return; - } - RecordPacketLossSeriesHistograms(protocol, status, load_size_string, result); -} - -// UDPStatsClient methods and members. -UDPStatsClient::UDPStatsClient() - : NetworkStats() { -} - -UDPStatsClient::~UDPStatsClient() { -} - -bool UDPStatsClient::DoConnect(int result) { - if (result != net::OK) { - Finish(RESOLVE_FAILED, result); - return false; - } - - net::UDPClientSocket* udp_socket = - new net::UDPClientSocket(net::DatagramSocket::DEFAULT_BIND, - net::RandIntCallback(), - NULL, - net::NetLog::Source()); - if (!udp_socket) { - Finish(SOCKET_CREATE_FAILED, net::ERR_INVALID_ARGUMENT); - return false; - } - set_socket(udp_socket); - - if (addresses().empty()) { - Finish(RESOLVE_FAILED, net::ERR_INVALID_ARGUMENT); - return false; - } - - const net::IPEndPoint& endpoint = addresses().front(); - int rv = udp_socket->Connect(endpoint); - if (rv < 0) { - Finish(CONNECT_FAILED, rv); - return false; - } - return NetworkStats::ConnectComplete(rv); -} - -bool UDPStatsClient::ReadComplete(int result) { - DCHECK_NE(net::ERR_IO_PENDING, result); - if (result <= 0) { - Finish(READ_FAILED, result); - return true; - } - return NetworkStats::ReadComplete(result); -} - -void UDPStatsClient::Finish(Status status, int result) { - RecordHistograms(PROTOCOL_UDP, status, result); - - DoFinishCallback(result); - - // Close the socket so that there are no more IO operations. - net::UDPClientSocket* udp_socket = - static_cast<net::UDPClientSocket*>(socket()); - if (udp_socket) - udp_socket->Close(); - - delete this; -} - -// TCPStatsClient methods and members. -TCPStatsClient::TCPStatsClient() { -} - -TCPStatsClient::~TCPStatsClient() { -} - -bool TCPStatsClient::DoConnect(int result) { - if (result != net::OK) { - Finish(RESOLVE_FAILED, result); - return false; - } - - net::TCPClientSocket* tcp_socket = - new net::TCPClientSocket(addresses(), NULL, net::NetLog::Source()); - if (!tcp_socket) { - Finish(SOCKET_CREATE_FAILED, net::ERR_INVALID_ARGUMENT); - return false; - } - set_socket(tcp_socket); - - int rv = tcp_socket->Connect(base::Bind(&TCPStatsClient::OnConnectComplete, - base::Unretained(this))); - if (rv == net::ERR_IO_PENDING) - return true; - - return NetworkStats::ConnectComplete(rv); -} - -void TCPStatsClient::OnConnectComplete(int result) { - NetworkStats::ConnectComplete(result); +void NetworkStats::RecordRTTHistograms(const ProtocolValue& protocol, + const std::string& load_size_string, + uint32 index) { + DCHECK_GE(index, 0u); + DCHECK_LT(index, packet_status_.size()); + + const char* test_name = TestName(); + std::string rtt_histogram_name = base::StringPrintf( + "NetConnectivity3.%s.Sent%02d.Success.RTT.Packet%02d.%d.%s", + test_name, + packets_to_send_, + index + 1, + kPorts[histogram_port_], + load_size_string.c_str()); + base::Histogram* rtt_histogram = base::Histogram::FactoryTimeGet( + rtt_histogram_name, + base::TimeDelta::FromMilliseconds(10), + base::TimeDelta::FromSeconds(30), 50, + base::Histogram::kUmaTargetedHistogramFlag); + base::TimeDelta duration = + packet_status_[index].end_time_ - packet_status_[index].start_time_; + rtt_histogram->AddTime(duration); } -bool TCPStatsClient::ReadComplete(int result) { - DCHECK_NE(net::ERR_IO_PENDING, result); - if (result < 0) { - Finish(READ_FAILED, result); - return true; +const char* NetworkStats::TestName() { + switch (current_test_) { + case START_PACKET_TEST: + return "StartPacket"; + case NON_PACED_PACKET_TEST: + return "NonPacedPacket"; + case PACED_PACKET_TEST: + return "PacedPacket"; + default: + NOTREACHED(); + return "None"; } - return NetworkStats::ReadComplete(result); } -void TCPStatsClient::Finish(Status status, int result) { - RecordHistograms(PROTOCOL_TCP, status, result); - - DoFinishCallback(result); - - // Disconnect the socket so that there are no more IO operations. - net::TCPClientSocket* tcp_socket = - static_cast<net::TCPClientSocket*>(socket()); - if (tcp_socket) - tcp_socket->Disconnect(); - - delete this; +void NetworkStats::set_socket(net::Socket* socket) { + DCHECK(socket); + DCHECK(!socket_.get()); + socket_.reset(socket); } // ProxyDetector methods and members. @@ -834,7 +942,7 @@ void CollectNetworkStats(const std::string& network_stats_server, NetworkStats::PORT_6121; if (!trial.get()) { - // Set up a field trial to collect network stats for UDP and TCP. + // Set up a field trial to collect network stats for UDP. const base::FieldTrial::Probability kDivisor = 1000; // Enable the connectivity testing for 0.5% of the users in stable channel. @@ -902,16 +1010,16 @@ void StartNetworkStatsTest(net::HostResolver* host_resolver, switch (experiment_to_run) { case 1: { - UDPStatsClient* udp_stats_client = new UDPStatsClient(); + NetworkStats* udp_stats_client = new NetworkStats(); udp_stats_client->Start( host_resolver, server_address, histogram_port, has_proxy_server, - kSmallTestBytesToSend, kMaximumCorrelationPackets, + kLargeTestBytesToSend, kMaximumCorrelationPackets, net::CompletionCallback()); } break; case 2: { - UDPStatsClient* udp_stats_client = new UDPStatsClient(); + NetworkStats* udp_stats_client = new NetworkStats(); udp_stats_client->Start( host_resolver, server_address, histogram_port, has_proxy_server, kSmallTestBytesToSend, kMaximumSequentialPackets, @@ -920,7 +1028,7 @@ void StartNetworkStatsTest(net::HostResolver* host_resolver, break; case 3: { - UDPStatsClient* udp_stats_client = new UDPStatsClient(); + NetworkStats* udp_stats_client = new NetworkStats(); udp_stats_client->Start( host_resolver, server_address, histogram_port, has_proxy_server, kMediumTestBytesToSend, kMaximumSequentialPackets, @@ -929,7 +1037,7 @@ void StartNetworkStatsTest(net::HostResolver* host_resolver, break; case 4: { - UDPStatsClient* udp_stats_client = new UDPStatsClient(); + NetworkStats* udp_stats_client = new NetworkStats(); udp_stats_client->Start( host_resolver, server_address, histogram_port, has_proxy_server, kLargeTestBytesToSend, kMaximumSequentialPackets, diff --git a/chrome/browser/net/network_stats.h b/chrome/browser/net/network_stats.h index cb37c35..7e008d9 100644 --- a/chrome/browser/net/network_stats.h +++ b/chrome/browser/net/network_stats.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_NET_NETWORK_STATS_H_ #include <string> +#include <vector> #include "base/basictypes.h" #include "base/memory/ref_counted.h" @@ -22,27 +23,26 @@ #include "net/base/test_data_stream.h" #include "net/proxy/proxy_info.h" #include "net/socket/socket.h" + namespace chrome_browser_net { -// This class is used for live experiment of network connectivity (either TCP or -// UDP) metrics. A small percentage of users participate in this experiment. All +// This class is used for live experiment of network connectivity (for UDP) +// metrics. A small percentage of users participate in this experiment. All // users (who are in the experiment) must have enabled "UMA upload". // // This class collects the following stats from users who have opted in. -// a) What percentage of users can get a message end-to-end to a UDP server? -// b) What percentage of users can get a message end-to-end to a TCP server? -// c) What is the latency for UDP and TCP. -// d) If connectivity failed, at what stage (Connect or Write or Read) did it -// fail? +// a) Success/failure, to estimate reachability for users. +// b) RTT for some select set of messages. +// c) Perform packet loss tests for correlation and FEC experiments. // // The following is the overview of the echo message protocol. // -// We send the "echo request" to the TCP/UDP servers in the following format: +// We send the "echo request" to the UDP echo servers in the following format: // <version><checksum><payload_size><payload>. <version> is the version number // of the "echo request". <checksum> is the checksum of the <payload>. // <payload_size> specifies the number of bytes in the <payload>. // -// TCP/UDP servers respond to the "echo request" by returning "echo response". +// UDP echo servers respond to the "echo request" by returning "echo response". // "echo response" is of the format: // "<version><checksum><payload_size><key><encoded_payload>". <payload_size> // specifies the number of bytes in the <encoded_payload>. <key> is used to @@ -50,47 +50,66 @@ namespace chrome_browser_net { class NetworkStats { public: - enum Status { // Used in HISTOGRAM_ENUMERATION. - SUCCESS, // Successfully received bytes from the server. - IP_STRING_PARSE_FAILED, // Parsing of IP string failed. - SOCKET_CREATE_FAILED, // Socket creation failed. - RESOLVE_FAILED, // Host resolution failed. - CONNECT_FAILED, // Connection to the server failed. - WRITE_FAILED, // Sending an echo message to the server failed. - READ_TIMED_OUT, // Reading the reply from the server timed out. - READ_FAILED, // Reading the reply from the server failed. - ZERO_LENGTH_ERROR, // Zero length message. - NO_CHECKSUM_ERROR, // Message doesn't have a checksum. - NO_KEY_ERROR, // Message doesn't have a key. - NO_PAYLOAD_SIZE_ERROR, // Message doesn't have a payload size. - NO_PAYLOAD_ERROR, // Message doesn't have a payload. - INVALID_KEY_ERROR, // Invalid key in the message. - TOO_SHORT_PAYLOAD, // Message is shorter than payload. - TOO_LONG_PAYLOAD, // Message is longer than payload. - INVALID_CHECKSUM, // Checksum verification failed. - PATTERN_CHANGED, // Pattern in payload has changed. - INVALID_PACKET_NUMBER, // Packet number didn't match. - TOO_MANY_PACKETS, // Received more packets than the packets sent. - STATUS_MAX, // Bounding value. + enum Status { // Used in HISTOGRAM_ENUMERATION. + SUCCESS, // Successfully received bytes from the server. + IP_STRING_PARSE_FAILED, // Parsing of IP string failed. + SOCKET_CREATE_FAILED, // Socket creation failed. + RESOLVE_FAILED, // Host resolution failed. + CONNECT_FAILED, // Connection to the server failed. + WRITE_FAILED, // Sending an echo message to the server failed. + READ_TIMED_OUT, // Reading the reply from the server timed out. + READ_FAILED, // Reading the reply from the server failed. + ZERO_LENGTH_ERROR, // Zero length message. + NO_CHECKSUM_ERROR, // Message doesn't have a checksum. + NO_KEY_ERROR, // Message doesn't have a key. + NO_PAYLOAD_SIZE_ERROR, // Message doesn't have a payload size. + NO_PAYLOAD_ERROR, // Message doesn't have a payload. + INVALID_KEY_ERROR, // Invalid key in the message. + TOO_SHORT_PAYLOAD, // Message is shorter than payload. + TOO_LONG_PAYLOAD, // Message is longer than payload. + INVALID_CHECKSUM, // Checksum verification failed. + PATTERN_CHANGED, // Pattern in payload has changed. + INVALID_PACKET_NUMBER, // Packet number didn't match. + TOO_MANY_PACKETS, // Received more packets than the packets sent. + PREVIOUS_PACKET_NUMBER, // Received a packet from an earlier test. + DUPLICATE_PACKET, // Received a duplicate packet. + SOME_PACKETS_NOT_VERIFIED, // Some packets have failed verification. + STATUS_MAX, // Bounding value. }; // |ProtocolValue| enumerates different protocols that are being tested. enum ProtocolValue { - PROTOCOL_TCP, PROTOCOL_UDP, }; // |HistogramPortSelector| enumerates list of ports that are used for network - // connectivity tests (either TCP or UDP). + // connectivity tests (for UDP). Currently we are testing port 6121 only. enum HistogramPortSelector { - PORT_53 = 0, // DNS - PORT_80, // HTTP - PORT_587, // SMTP Submission. - PORT_6121, // SPDY - PORT_8080, // High order webserver. + PORT_6121 = 0, // SPDY HISTOGRAM_PORT_MAX, }; + // |TestType| specifies the |current_test_| test we are currently running or + // the |next_test_| test we need to run next. + enum TestType { + START_PACKET_TEST, // Initial packet loss test. + NON_PACED_PACKET_TEST, // Packet loss test with no pacing. + PACED_PACKET_TEST, // Packet loss test with pacing. + TEST_TYPE_MAX, + }; + + // |PacketStatus| collects the Status and RTT statistics for each packet. + struct PacketStatus { + base::TimeTicks start_time_; + base::TimeTicks end_time_; + }; + + // Constructs a NetworkStats object that collects metrics for network + // connectivity (for UDP). + NetworkStats(); + // NetworkStats is deleted when Finish() is called. + ~NetworkStats(); + // Starts the client, connecting to |server|. // Client will send |bytes_to_send| bytes to |server|. // When client has received all echoed bytes from the server, or @@ -106,12 +125,21 @@ class NetworkStats { uint32 packets_to_send, const net::CompletionCallback& callback); - protected: - // Constructs an NetworkStats object that collects metrics for network - // connectivity (either TCP or UDP). - NetworkStats(); - // NetworkStats is deleted when Finish() is called. - virtual ~NetworkStats(); + private: + friend class NetworkStatsTest; + + // Allow tests to access our innards for testing purposes. + FRIEND_TEST_ALL_PREFIXES(NetworkStatsTest, GetHistogramNames); + FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesInvalidHeaders); + FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesInvalidChecksum); + FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesPreviousPacket); + FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesInvalidPacketNumber); + FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytesPatternChanged); + FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestUDP, VerifyBytes); + + // Starts the test specified by the |next_test_|. It also resets all the book + // keeping data, before starting the new test. + void RestartPacketTest(); // Initializes |finished_callback_| and the number of bytes to send to the // server. |finished_callback| is called when we are done with the test. @@ -122,70 +150,39 @@ class NetworkStats { uint32 packets_to_send, const net::CompletionCallback& finished_callback); - // Called after host is resolved. UDPStatsClient and TCPStatsClient implement - // this method. They create the socket and connect to the server. - virtual bool DoConnect(int result) = 0; + // Resets all the counters and the collected stats. + void ResetData(); + + // Callback that is called when host resolution is completed. + void OnResolveComplete(int result); + + // Called after host is resolved. Creates UDPClientSocket and connects to the + // server. If successfully connected, then calls ConnectComplete() to start + // the network connectivity tests. Returns |false| if there is any error. + bool DoConnect(int result); // This method is called after socket connection is completed. It will start // the process of sending packets to |server| by calling SendPacket(). Returns // false if connection is not established (result is less than 0). bool ConnectComplete(int result); - // Collects network connectivity stats. This is called when all the data from - // server is read or when there is a failure during connect/read/write. - virtual void Finish(Status status, int result) {} - - // This method is called from Finish() and calls |finished_callback_| callback - // to indicate that the test has finished. - void DoFinishCallback(int result); - - // Verifies the data and calls Finish() if there is an error or if all bytes - // are read. Returns true if Finish() is called otherwise returns false. - virtual bool ReadComplete(int result); - - // Returns the number of bytes to be sent to the |server|. - uint32 load_size() const { return load_size_; } - - // Helper methods to get and set |socket_|. - net::Socket* socket() { return socket_.get(); } - void set_socket(net::Socket* socket); - - // Returns |start_time_| (used by histograms). - base::TimeTicks start_time() const { return start_time_; } - - // Returns |addresses_|. - const net::AddressList& addresses() const { return addresses_; } - - // Returns packets_received_mask_ (used by unit tests). - uint32 packets_received_mask() const { return packets_received_mask_; } - - // Collect the following network connectivity stats. - // a) What percentage of users can get a message end-to-end to a TCP/UDP - // server and if connectivity failed, at what stage (Connect or Write or Read) - // did it fail? - // b) What is RTT for the echo message. - // c) Packet loss correlation and other network connectivity data by calling - // RecordAcksReceivedHistograms() and RecordStatusAndRTTHistograms(). - void RecordHistograms(const ProtocolValue& protocol, - const Status& status, - int result); - - private: - friend class NetworkStatsTest; - - // Allow tests to access our innards for testing purposes. - FRIEND_TEST_ALL_PREFIXES(NetworkStatsTest, GetHistogramNames); - FRIEND_TEST_ALL_PREFIXES(NetworkStatsTestTCP, VerifyBytes); - - // Callback that is called when host resolution is completed. - void OnResolveComplete(int result); - // This method is called whenever we need to send a packet. It is called from // either ConnectComplete or OnWriteComplete. It will send a packet, based on // load_size_, to |server| by calling SendData(). If there are no more packets // to send, it calls ReadData() to read/verify the data from the |server|. void SendPacket(); + // Sends the next packet by calling |SendPacket| after a delay. Delay time is + // calculated based on the remaining packets and the remaining time. + // For START_PACKET_TEST and NON_PACED_PACKET_TEST delay is zero. It also + // yields for packets received between sends. + void SendNextPacketAfterDelay(); + + // Verifies the data and calls Finish() if there is a significant network + // error or if all packets are sent. Returns true if Finish() is called + // otherwise returns false. + bool ReadComplete(int result); + // Callbacks when an internal IO is completed. void OnReadComplete(int result); void OnWriteComplete(int result); @@ -208,7 +205,13 @@ class NetworkStats { // We set a timeout for responses from the echo servers. void StartReadDataTimer(int milliseconds); - void OnReadDataTimeout(); // Called when the ReadData Timer fires. + + // Called when the ReadData Timer fires. |test_base_packet_number| specifies + // the |base_packet_number_| when we have started the timer. If we haven't + // received all the packets for the test (test took longer than 30 secs to + // run), then we call Finish() to finish processing. If we have already + // received all packets from the server, then this method is a no-op. + void OnReadDataTimeout(uint32 test_base_packet_number); // Returns the checksum for the message. uint32 GetChecksum(const char* message, uint32 message_length); @@ -239,25 +242,66 @@ class NetworkStats { // if all the bytes are verified. NetworkStats::Status VerifyBytes(const std::string& response); + // Collects network connectivity stats. This is called when all the data from + // server is read or when there is a failure during connect/read/write. It + // will either restart the second phase of the test, or it will self destruct + // at the end of this method. + void Finish(Status status, int result); + + // This method is called from Finish() and calls |finished_callback_| callback + // to indicate that the test has finished. + void DoFinishCallback(int result); + + // Collect the network connectivity stats by calling RecordRTTHistograms(), + // RecordAcksReceivedHistograms() and RecordPacketLossSeriesHistograms(). See + // below for details. + void RecordHistograms(const ProtocolValue& protocol, + const Status& status, + int result); + // Collect the following network connectivity stats when // kMaximumSequentialPackets (21) packets are sent. // a) Received the "echo response" for at least one packet. // b) Received the "echo response" for the nth packet. // c) Count the number of "echo responses" received for each of the initial // sequences of packets 1...n. - void RecordAcksReceivedHistograms(const char* load_size_string); - - // Collect the following network connectivity stats. - // a) What percentage of users can get a message end-to-end to a TCP/UDP - // server and if connectivity failed, at what stage (Connect or Write or Read) - // did it fail? - // b) What is RTT for the echo message. - // c) Records if there is a probabalistic dependency in packet loss when + void RecordAcksReceivedHistograms(const std::string& load_size_string); + + // Collect the following network connectivity stats for + // kMaximumCorrelationPackets (6) packets test. + // a) Success/failure of each packet, to estimate reachability for users. + // b) Records if there is a probabalistic dependency in packet loss when // kMaximumCorrelationPackets packets are sent consecutively. void RecordPacketLossSeriesHistograms(const ProtocolValue& protocol, + const std::string& load_size_string, const Status& status, - const char* load_size_string, int result); + + // Collects the RTT for the packet specified by the |index|. + void RecordRTTHistograms(const ProtocolValue& protocol, + const std::string& load_size_string, + uint32 index); + + uint32 load_size() const { return load_size_; } + + // Returns string representation of test_type_. + const char* TestName(); + + uint32 packet_number() const { return packet_number_; } + uint32 base_packet_number() const { return base_packet_number_; } + uint32 set_base_packet_number(uint32 packet_number) { + return base_packet_number_ = packet_number; + } + + net::Socket* socket() { return socket_.get(); } + void set_socket(net::Socket* socket); + + const net::AddressList& addresses() const { return addresses_; } + + uint32 packets_received_mask() const { return packets_received_mask_; } + + TestType next_test() const { return next_test_; } + // The socket handle for this session. scoped_ptr<net::Socket> socket_; @@ -272,9 +316,6 @@ class NetworkStats { uint32 bytes_to_read_; uint32 bytes_to_send_; - // The encoded message read from the server. - std::string encoded_message_; - // |stream_| is used to generate data to be sent to the server and it is also // used to verify the data received from the server. net::TestDataStream stream_; @@ -294,77 +335,33 @@ class NetworkStats { // round trip is finished). net::CompletionCallback finished_callback_; - // The time when the session was started. - base::TimeTicks start_time_; + // Collects the Status and RTT for each packet. + std::vector<PacketStatus> packet_status_; - // Data to track number of packets to send to the server and the packets we - // have received from the server. - uint32 packets_to_send_; - uint32 packets_sent_; - uint32 base_packet_number_; - uint32 packets_received_mask_; + // The time when we have received 1st byte from the server. + base::TimeTicks packet_1st_byte_read_time_; - // We use this factory to create timeout tasks for socket's ReadData. - base::WeakPtrFactory<NetworkStats> weak_factory_; -}; + // The last time when we have received data from the server. + base::TimeTicks packet_last_byte_read_time_; -class UDPStatsClient : public NetworkStats { - public: - // Constructs an UDPStatsClient object that collects metrics for UDP - // connectivity. - UDPStatsClient(); - // UDPStatsClient is deleted when Finish() is called. - virtual ~UDPStatsClient(); - - protected: - // Allow tests to access our innards for testing purposes. - friend class NetworkStatsTestUDP; + // This is the average time it took to receive a packet from the server. + base::TimeDelta average_time_; - // Called after host is resolved. Creates UDClientSocket and connects to the - // server. If successfully connected, then calls ConnectComplete() to start - // the echo protocol. Returns |false| if there is any error. - virtual bool DoConnect(int result) OVERRIDE; - - // This method calls NetworkStats::ReadComplete() to verify the data and calls - // Finish() if there is an error or if read callback didn't return any data - // (|result| is less than or equal to 0). - virtual bool ReadComplete(int result) OVERRIDE; - - // Collects stats for UDP connectivity. This is called when all the data from - // server is read or when there is a failure during connect/read/write. This - // object is deleted at the end of this method. - virtual void Finish(Status status, int result) OVERRIDE; -}; - -class TCPStatsClient : public NetworkStats { - public: - // Constructs a TCPStatsClient object that collects metrics for TCP - // connectivity. - TCPStatsClient(); - // TCPStatsClient is deleted when Finish() is called. - virtual ~TCPStatsClient(); - - protected: - // Allow tests to access our innards for testing purposes. - friend class NetworkStatsTestTCP; - - // Called after host is resolved. Creates TCPClientSocket and connects to the - // server. - virtual bool DoConnect(int result) OVERRIDE; - - // This method calls NetworkStats::ReadComplete() to verify the data and calls - // Finish() if there is an error (|result| is less than 0). - virtual bool ReadComplete(int result) OVERRIDE; + // Data to track number of packets to send to the server and the packets we + // have received from the server. + uint32 packets_to_send_; // Numbers of packets that are to be sent. + uint32 packets_sent_; // Numbers of packets sent to the server. + uint32 packets_received_; // Number of packets successfully received. + uint32 packets_received_mask_; // Tracks the received status of each packet. + uint32 packet_number_; // The packet number being sent to the server. + uint32 base_packet_number_; // The packet number before the test. + bool sending_complete_; // Finished sending all packets. - // Collects stats for TCP connectivity. This is called when all the data from - // server is read or when there is a failure during connect/read/write. This - // object is deleted at the end of this method. - virtual void Finish(Status status, int result) OVERRIDE; + TestType current_test_; // Current test that is running. + TestType next_test_; // Next test that is to be run. - private: - // Callback that is called when connect is completed and calls - // ConnectComplete() to start the echo protocol. - void OnConnectComplete(int result); + // We use this factory to create timeout tasks for socket's ReadData. + base::WeakPtrFactory<NetworkStats> weak_factory_; }; class ProxyDetector { @@ -410,17 +407,17 @@ class ProxyDetector { bool has_pending_proxy_resolution_; }; -// This collects the network connectivity stats for UDP and TCP for small +// This collects the network connectivity stats for UDP protocol for small // percentage of users who are participating in the experiment. All users must // have enabled "UMA upload". This method gets called only if UMA upload to the // server has succeeded. void CollectNetworkStats(const std::string& network_stats_server_url, IOThread* io_thread); -// This starts a test randomly selected among "TCP test with small packet size", -// "TCP test with large packet size", "UDP test with small packet size", "UDP -// test with large packet size" and "UDP multi packet loss" tests to collect the -// network connectivity stats. +// This starts a test randomly selected among "6 packets correlation test for +// 1200 bytes packet", "21 packets bursty test with 1200 bytes packet", +// "21 packets bursty test with 500 bytes packet", and "21 packets bursty test +// with 100 bytes packet" tests. void StartNetworkStatsTest(net::HostResolver* host_resolver, const net::HostPortPair& server_address, NetworkStats::HistogramPortSelector histogram_port, diff --git a/chrome/browser/net/network_stats_unittest.cc b/chrome/browser/net/network_stats_unittest.cc index 84a1cbd..42a3f2e 100644 --- a/chrome/browser/net/network_stats_unittest.cc +++ b/chrome/browser/net/network_stats_unittest.cc @@ -44,7 +44,7 @@ class NetworkStatsTestUDP : public NetworkStatsTest { // We will use HISTOGRAM_PORT_MAX as the PortIndex enum for testing // purposes. - UDPStatsClient* udp_stats_client = new UDPStatsClient(); + NetworkStats* udp_stats_client = new NetworkStats(); EXPECT_TRUE(udp_stats_client->Start(host_resolver.get(), test_server_.host_port_pair(), NetworkStats::HISTOGRAM_PORT_MAX, @@ -60,39 +60,6 @@ class NetworkStatsTestUDP : public NetworkStatsTest { net::TestServer test_server_; }; -class NetworkStatsTestTCP : public NetworkStatsTest { - public: - NetworkStatsTestTCP() - : test_server_(net::TestServer::TYPE_TCP_ECHO, - net::TestServer::kLocalhost, - FilePath(FILE_PATH_LITERAL("net/data"))) { - } - - protected: - void RunTCPEchoTest(int bytes, int packets, bool has_proxy) { - net::TestCompletionCallback cb; - - scoped_ptr<net::MockHostResolver> host_resolver( - new net::MockHostResolver()); - - // We will use HISTOGRAM_PORT_MAX as the PortIndex enum for testing - // purposes. - TCPStatsClient* tcp_stats_client = new TCPStatsClient(); - EXPECT_TRUE(tcp_stats_client->Start(host_resolver.get(), - test_server_.host_port_pair(), - NetworkStats::HISTOGRAM_PORT_MAX, - has_proxy, - bytes, - packets, - cb.callback())); - int rv = cb.WaitForResult(); - // Check there were no errors during connect/write/read to echo TCP server. - EXPECT_EQ(0, rv); - } - - net::TestServer test_server_; -}; - TEST_F(NetworkStatsTestUDP, UDPEcho100BytesHasProxy) { ASSERT_TRUE(test_server_.Start()); RunUDPEchoTest(100, 1, true); @@ -133,37 +100,18 @@ TEST_F(NetworkStatsTestUDP, UDPEchoMultiplePacketsNoProxy) { RunUDPEchoTest(1024, 4, false); } -TEST_F(NetworkStatsTestTCP, TCPEcho100BytesHasProxy) { - ASSERT_TRUE(test_server_.Start()); - RunTCPEchoTest(100, 1, true); -} - -TEST_F(NetworkStatsTestTCP, TCPEcho100BytesNoProxy) { - ASSERT_TRUE(test_server_.Start()); - RunTCPEchoTest(100, 1, false); -} - -TEST_F(NetworkStatsTestTCP, TCPEcho1KBytesHasProxy) { - ASSERT_TRUE(test_server_.Start()); - RunTCPEchoTest(1024, 1, true); -} - -TEST_F(NetworkStatsTestTCP, TCPEcho1KBytesNoProxy) { - ASSERT_TRUE(test_server_.Start()); - RunTCPEchoTest(1024, 1, false); -} - -TEST_F(NetworkStatsTestTCP, VerifyBytes) { - static const uint32 KBytesToSend = 100; - static const uint32 packets_to_send = 1; - static const uint32 packet_number = 1; - TCPStatsClient network_stats; +TEST_F(NetworkStatsTestUDP, VerifyBytesInvalidHeaders) { + const uint32 KBytesToSend = 100; + const uint32 packets_to_send = 1; + NetworkStats network_stats; net::TestCompletionCallback cb; network_stats.Initialize(KBytesToSend, NetworkStats::HISTOGRAM_PORT_MAX, true, packets_to_send, cb.callback()); + uint32 packet_number = network_stats.packet_number(); + network_stats.set_base_packet_number(packet_number); std::string message; EXPECT_EQ(NetworkStats::ZERO_LENGTH_ERROR, @@ -175,7 +123,6 @@ TEST_F(NetworkStatsTestTCP, VerifyBytes) { std::string invalid_key = base::StringPrintf("%06d", -1); std::string key_string = base::StringPrintf("%06d", 899999); std::string packet_number_string = base::StringPrintf("%010d", packet_number); - std::string invalid_packet_number_string = base::StringPrintf("%010d", -1); std::string short_payload(packet_number_string + "1234567890123456789012345678901234567890123456789012345678901234567890"); std::string long_payload(packet_number_string + @@ -209,6 +156,31 @@ TEST_F(NetworkStatsTestTCP, VerifyBytes) { message = version + invalid_checksum + payload_size + key_string + long_payload; EXPECT_EQ(NetworkStats::TOO_LONG_PAYLOAD, network_stats.VerifyBytes(message)); +} + +TEST_F(NetworkStatsTestUDP, VerifyBytesInvalidChecksum) { + const uint32 KBytesToSend = 100; + const uint32 packets_to_send = 1; + NetworkStats network_stats; + net::TestCompletionCallback cb; + + network_stats.Initialize(KBytesToSend, + NetworkStats::HISTOGRAM_PORT_MAX, + true, + packets_to_send, + cb.callback()); + uint32 packet_number = network_stats.packet_number(); + network_stats.set_base_packet_number(packet_number); + + std::string message; + EXPECT_EQ(NetworkStats::ZERO_LENGTH_ERROR, + network_stats.VerifyBytes(message)); + + std::string version = base::StringPrintf("%02d", 1); + std::string invalid_checksum = base::StringPrintf("%010d", 0); + std::string payload_size = base::StringPrintf("%07d", KBytesToSend); + std::string key_string = base::StringPrintf("%06d", 899999); + std::string packet_number_string = base::StringPrintf("%010d", packet_number); scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(KBytesToSend)); @@ -236,28 +208,142 @@ TEST_F(NetworkStatsTestTCP, VerifyBytes) { message = version + invalid_checksum + payload_size + key_string + encoded_payload; EXPECT_EQ(NetworkStats::INVALID_CHECKSUM, network_stats.VerifyBytes(message)); +} - // Corrupt the packet_number in payload data. - char corrupted_data_1[KBytesToSend]; - memcpy(corrupted_data_1, payload_data, KBytesToSend); - char temp_char_0 = corrupted_data_1[packet_number_size - 1]; - corrupted_data_1[packet_number_size - 1] = - corrupted_data_1[packet_number_size - 2]; - corrupted_data_1[packet_number_size - 2] = temp_char_0; - char encoded_corrupted_data_1[KBytesToSend]; - memset(encoded_corrupted_data_1, 0, KBytesToSend); - network_stats.Crypt(key, - key_string.length(), - corrupted_data_1, - KBytesToSend, - encoded_corrupted_data_1); - std::string invalid_packet_number_payload(encoded_corrupted_data_1, - KBytesToSend); - message = version + checksum + payload_size + key_string + - invalid_packet_number_payload; +TEST_F(NetworkStatsTestUDP, VerifyBytesPreviousPacket) { + const uint32 KBytesToSend = 100; + const uint32 packets_to_send = 1; + NetworkStats network_stats; + net::TestCompletionCallback cb; + + network_stats.Initialize(KBytesToSend, + NetworkStats::HISTOGRAM_PORT_MAX, + true, + packets_to_send, + cb.callback()); + uint32 packet_number = network_stats.packet_number(); + network_stats.set_base_packet_number(packet_number); + uint32 prev_packet_number = packet_number - 1; + + std::string message; + std::string version = base::StringPrintf("%02d", 1); + std::string payload_size = base::StringPrintf("%07d", KBytesToSend); + std::string key_string = base::StringPrintf("%06d", 899999); + std::string prev_packet_number_string = + base::StringPrintf("%010d", prev_packet_number); + + // Copy the previous packet number into the payload. + scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(KBytesToSend)); + size_t packet_number_size = prev_packet_number_string.size(); + memcpy(payload->data(), prev_packet_number_string.c_str(), + packet_number_size); + + // Get random bytes to fill rest of the payload. + network_stats.stream_.GetBytes(payload->data() + packet_number_size, + KBytesToSend - packet_number_size); + + // Calculate checksum for the previous packet number payload. + char* payload_data = payload->data(); + uint32 sum = network_stats.GetChecksum(payload_data, KBytesToSend); + std::string checksum = base::StringPrintf("%010d", sum); + + char encoded_data[KBytesToSend]; + memset(encoded_data, 0, KBytesToSend); + const char* key = key_string.c_str(); + network_stats.Crypt( + key, key_string.length(), payload_data, KBytesToSend, encoded_data); + std::string encoded_payload(encoded_data, KBytesToSend); + + message = version + checksum + payload_size + key_string + encoded_payload; + EXPECT_EQ(NetworkStats::PREVIOUS_PACKET_NUMBER, + network_stats.VerifyBytes(message)); + EXPECT_EQ(0u, network_stats.packets_received_mask()); +} + +TEST_F(NetworkStatsTestUDP, VerifyBytesInvalidPacketNumber) { + const uint32 KBytesToSend = 100; + const uint32 packets_to_send = 1; + NetworkStats network_stats; + net::TestCompletionCallback cb; + + network_stats.Initialize(KBytesToSend, + NetworkStats::HISTOGRAM_PORT_MAX, + true, + packets_to_send, + cb.callback()); + uint32 packet_number = network_stats.packet_number(); + network_stats.set_base_packet_number(packet_number); + uint32 corrupt_packet_number = packet_number + 1; + + std::string message; + std::string version = base::StringPrintf("%02d", 1); + std::string payload_size = base::StringPrintf("%07d", KBytesToSend); + std::string key_string = base::StringPrintf("%06d", 899999); + std::string corrupt_packet_number_string = + base::StringPrintf("%010d", corrupt_packet_number); + + scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(KBytesToSend)); + size_t packet_number_size = corrupt_packet_number_string.size(); + memcpy(payload->data(), corrupt_packet_number_string.c_str(), + packet_number_size); + + // Get random bytes to fill rest of the payload. + network_stats.stream_.GetBytes(payload->data() + packet_number_size, + KBytesToSend - packet_number_size); + + // Calculate checksum for the corrupt packet number payload. + char* payload_data = payload->data(); + uint32 sum = network_stats.GetChecksum(payload_data, KBytesToSend); + std::string checksum = base::StringPrintf("%010d", sum); + + char encoded_data[KBytesToSend]; + memset(encoded_data, 0, KBytesToSend); + const char* key = key_string.c_str(); + network_stats.Crypt( + key, key_string.length(), payload_data, KBytesToSend, encoded_data); + std::string encoded_payload(encoded_data, KBytesToSend); + message = version + checksum + payload_size + key_string + encoded_payload; EXPECT_EQ(NetworkStats::INVALID_PACKET_NUMBER, network_stats.VerifyBytes(message)); EXPECT_EQ(0u, network_stats.packets_received_mask()); +} + +TEST_F(NetworkStatsTestUDP, VerifyBytesPatternChanged) { + const uint32 KBytesToSend = 100; + const uint32 packets_to_send = 1; + NetworkStats network_stats; + net::TestCompletionCallback cb; + + network_stats.Initialize(KBytesToSend, + NetworkStats::HISTOGRAM_PORT_MAX, + true, + packets_to_send, + cb.callback()); + uint32 packet_number = network_stats.packet_number(); + network_stats.set_base_packet_number(packet_number); + + std::string message; + std::string version = base::StringPrintf("%02d", 1); + std::string payload_size = base::StringPrintf("%07d", KBytesToSend); + std::string key_string = base::StringPrintf("%06d", 899999); + std::string packet_number_string = base::StringPrintf("%010d", packet_number); + + scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(KBytesToSend)); + + // Copy the Packet number into the payload. + size_t packet_number_size = packet_number_string.size(); + memcpy(payload->data(), packet_number_string.c_str(), packet_number_size); + + // Get random bytes to fill rest of the payload. + network_stats.stream_.GetBytes(payload->data() + packet_number_size, + KBytesToSend - packet_number_size); + + // Calculate checksum for the payload. + char* payload_data = payload->data(); + uint32 sum = network_stats.GetChecksum(payload_data, KBytesToSend); + std::string checksum = base::StringPrintf("%010d", sum); + + const char* key = key_string.c_str(); // Corrupt the randomly generated bytes in payload data. char corrupted_data_2[KBytesToSend]; @@ -277,6 +363,50 @@ TEST_F(NetworkStatsTestTCP, VerifyBytes) { message = version + checksum + payload_size + key_string + invalid_payload; EXPECT_EQ(NetworkStats::PATTERN_CHANGED, network_stats.VerifyBytes(message)); EXPECT_EQ(0u, network_stats.packets_received_mask()); +} + + +TEST_F(NetworkStatsTestUDP, VerifyBytes) { + const uint32 KBytesToSend = 100; + const uint32 packets_to_send = 1; + NetworkStats network_stats; + net::TestCompletionCallback cb; + + network_stats.Initialize(KBytesToSend, + NetworkStats::HISTOGRAM_PORT_MAX, + true, + packets_to_send, + cb.callback()); + uint32 packet_number = network_stats.packet_number(); + network_stats.set_base_packet_number(packet_number); + + std::string message; + std::string version = base::StringPrintf("%02d", 1); + std::string payload_size = base::StringPrintf("%07d", KBytesToSend); + std::string key_string = base::StringPrintf("%06d", 899999); + std::string packet_number_string = base::StringPrintf("%010d", packet_number); + + scoped_refptr<net::IOBuffer> payload(new net::IOBuffer(KBytesToSend)); + + // Copy the Packet number into the payload. + size_t packet_number_size = packet_number_string.size(); + memcpy(payload->data(), packet_number_string.c_str(), packet_number_size); + + // Get random bytes to fill rest of the payload. + network_stats.stream_.GetBytes(payload->data() + packet_number_size, + KBytesToSend - packet_number_size); + + // Calculate checksum for the payload. + char* payload_data = payload->data(); + uint32 sum = network_stats.GetChecksum(payload_data, KBytesToSend); + std::string checksum = base::StringPrintf("%010d", sum); + + char encoded_data[KBytesToSend]; + memset(encoded_data, 0, KBytesToSend); + const char* key = key_string.c_str(); + network_stats.Crypt( + key, key_string.length(), payload_data, KBytesToSend, encoded_data); + std::string encoded_payload(encoded_data, KBytesToSend); message = version + checksum + payload_size + key_string + encoded_payload; EXPECT_EQ(NetworkStats::SUCCESS, network_stats.VerifyBytes(message)); |