diff options
author | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-02 20:10:26 +0000 |
---|---|---|
committer | rtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-02 20:10:26 +0000 |
commit | 15bb050eb0943488ee773de7943854d1c074de2a (patch) | |
tree | 87b9faef6f64e0a6be55add32518b39b202afdf6 | |
parent | 3af58c07fc124ced9d709820a02d1f884466c4ae (diff) | |
download | chromium_src-15bb050eb0943488ee773de7943854d1c074de2a.zip chromium_src-15bb050eb0943488ee773de7943854d1c074de2a.tar.gz chromium_src-15bb050eb0943488ee773de7943854d1c074de2a.tar.bz2 |
UDP network connectivity test - send multiple packets
to the server and then plot histogram on how many
packets have successfully made the round trip.
R=jar@chromium.org
TEST=browser unit tests
Review URL: http://codereview.chromium.org/10134021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134976 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/net/network_stats.cc | 302 | ||||
-rw-r--r-- | chrome/browser/net/network_stats.h | 66 | ||||
-rw-r--r-- | chrome/browser/net/network_stats_unittest.cc | 161 |
3 files changed, 370 insertions, 159 deletions
diff --git a/chrome/browser/net/network_stats.cc b/chrome/browser/net/network_stats.cc index a22e656..c0ccdd6 100644 --- a/chrome/browser/net/network_stats.cc +++ b/chrome/browser/net/network_stats.cc @@ -72,20 +72,32 @@ static const int32 kKeyMaxValue = 999999; // This specifies the starting position of the <payload> in "echo request". static const uint32 kPayloadStart = kPayloadSizeEnd; -// This specifies the starting position of the <encoded_payload> and length of -// the <encoded_payload> in "echo response". +// This specifies the length of the packet_number in the payload. +static const uint32 kPacketNumberLength = 10; + +// This specifies the starting position of the <encoded_payload> in +// "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; + +// Maximum number of packets that can be sent to the server for packet loss +// testing. +static const uint32 kMaximumPackets = 4; + // NetworkStats methods and members. NetworkStats::NetworkStats() : load_size_(0), bytes_to_read_(0), bytes_to_send_(0), - encoded_message_(""), - start_time_(base::TimeTicks::Now()), + packets_to_send_(0), + packets_sent_(0), + base_packet_number_(0), + packets_received_mask_(0), ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) { } @@ -97,10 +109,12 @@ bool NetworkStats::Start(net::HostResolver* host_resolver, const net::HostPortPair& server_host_port_pair, HistogramPortSelector histogram_port, uint32 bytes_to_send, + uint32 packets_to_send, const net::CompletionCallback& finished_callback) { DCHECK(bytes_to_send); // We should have data to send. + DCHECK_LE(packets_to_send, kMaximumPackets); - Initialize(bytes_to_send, histogram_port, finished_callback); + Initialize(bytes_to_send, histogram_port, packets_to_send, finished_callback); net::HostResolver::RequestInfo request(server_host_port_pair); int rv = host_resolver->Resolve( @@ -116,46 +130,37 @@ bool NetworkStats::Start(net::HostResolver* host_resolver, void NetworkStats::Initialize( uint32 bytes_to_send, HistogramPortSelector histogram_port, + uint32 packets_to_send, const net::CompletionCallback& finished_callback) { - DCHECK(bytes_to_send); // We should have data to send. + 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); load_size_ = bytes_to_send; - bytes_to_send_ = kVersionLength + kChecksumLength + kPayloadSizeLength + - load_size_; - bytes_to_read_ = kVersionLength + kChecksumLength + kPayloadSizeLength + - kKeyLength + load_size_; + packets_to_send_ = packets_to_send; histogram_port_ = histogram_port; finished_callback_ = finished_callback; } +uint32 SendingPacketSize(uint32 load_size) { + return kVersionLength + kChecksumLength + kPayloadSizeLength + load_size; +} + +uint32 ReceivingPacketSize(uint32 load_size) { + return kVersionLength + kChecksumLength + kPayloadSizeLength + kKeyLength + + load_size; +} + bool NetworkStats::ConnectComplete(int result) { if (result < 0) { Finish(CONNECT_FAILED, result); return false; } - DCHECK(bytes_to_send_); // We should have data to send. - + base_packet_number_ = g_packet_number_; start_time_ = base::TimeTicks::Now(); - - int rv = SendData(); - if (rv < 0) { - if (rv != net::ERR_IO_PENDING) { - Finish(WRITE_FAILED, rv); - return false; - } - } - - stream_.Reset(); - - // Timeout if we don't get response back from echo servers in 60 secs. - const int kReadDataTimeoutMs = 60000; - StartReadDataTimer(kReadDataTimeoutMs); - - ReadData(); - + SendPacket(); return true; } @@ -188,7 +193,7 @@ bool NetworkStats::ReadComplete(int result) { // No more data to read. if (!bytes_to_read_ || result == 0) { - Status status = VerifyBytes(); + Status status = VerifyPackets(); if (status == SUCCESS) Finish(status, net::OK); else @@ -202,6 +207,36 @@ void NetworkStats::OnResolveComplete(int result) { DoConnect(result); } +void NetworkStats::SendPacket() { + uint32 sending_packet_size = SendingPacketSize(load_size_); + uint32 receiving_packet_size = ReceivingPacketSize(load_size_); + + 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; + } + } + bytes_to_read_ += receiving_packet_size; + ++packets_sent_; + + DCHECK(bytes_to_send_ == 0 || rv == net::ERR_IO_PENDING); + if (rv == net::ERR_IO_PENDING) + return; + } + + // Timeout if we don't get response back from echo servers in 60 secs. + const int kReadDataTimeoutMs = 60000; + StartReadDataTimer(kReadDataTimeoutMs); + + ReadData(); +} + void NetworkStats::OnReadComplete(int result) { if (!ReadComplete(result)) { // Called ReadData() via PostDelayedTask() to avoid recursion. Added a delay @@ -236,7 +271,12 @@ void NetworkStats::OnWriteComplete(int result) { return; } } + return; } + + MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&NetworkStats::SendPacket, weak_factory_.GetWeakPtr())); } void NetworkStats::ReadData() { @@ -265,7 +305,8 @@ int NetworkStats::SendData() { DCHECK(bytes_to_send_); // We should have data to send. do { if (!write_buffer_.get()) { - scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(bytes_to_send_)); + scoped_refptr<net::IOBufferWithSize> buffer( + new net::IOBufferWithSize(bytes_to_send_)); GetEchoRequest(buffer); write_buffer_ = new net::DrainableIOBuffer(buffer, bytes_to_send_); } @@ -294,7 +335,11 @@ void NetworkStats::StartReadDataTimer(int milliseconds) { } void NetworkStats::OnReadDataTimeout() { - Finish(READ_TIMED_OUT, net::ERR_INVALID_ARGUMENT); + Status status = VerifyPackets(); + if (status == SUCCESS) + Finish(status, net::OK); + else + Finish(READ_TIMED_OUT, net::ERR_INVALID_ARGUMENT); } uint32 NetworkStats::GetChecksum(const char* message, uint32 message_length) { @@ -324,59 +369,91 @@ void NetworkStats::Crypt(const char* key, } } -void NetworkStats::GetEchoRequest(net::IOBuffer* io_buffer) { +void NetworkStats::GetEchoRequest(net::IOBufferWithSize* io_buffer) { + char* buffer = io_buffer->data(); + uint32 buffer_size = static_cast<uint32>(io_buffer->size()); + // Copy the <version> into the io_buffer starting from the kVersionStart // position. std::string version = base::StringPrintf("%02d", kVersionNumber); - char* buffer = io_buffer->data() + kVersionStart; DCHECK(kVersionLength == version.length()); - memcpy(buffer, version.c_str(), kVersionLength); + DCHECK_GE(buffer_size, kVersionStart + kVersionLength); + memcpy(buffer + kVersionStart, version.c_str(), kVersionLength); + + // Copy the packet_number into the payload. + std::string packet_number = base::StringPrintf("%010d", g_packet_number_); + DCHECK(kPacketNumberLength == packet_number.length()); + DCHECK_GE(buffer_size, kPayloadStart + kPacketNumberLength); + memcpy(buffer + kPayloadStart, packet_number.c_str(), kPacketNumberLength); - // Get the <payload> from the |stream_| and copy it into io_buffer starting - // from the kPayloadStart position. - buffer = io_buffer->data() + kPayloadStart; - stream_.GetBytes(buffer, load_size_); + // Get the <payload> from the |stream_| and copy it into io_buffer after + // packet_number. + stream_.Reset(); + DCHECK_GE(buffer_size, kPayloadStart + load_size_); + stream_.GetBytes(buffer + kPayloadStart + kPacketNumberLength, + load_size_ - kPacketNumberLength); // Calculate the <checksum> of the <payload>. - uint32 sum = GetChecksum(buffer, load_size_); + uint32 sum = GetChecksum(buffer + kPayloadStart, load_size_); // Copy the <checksum> into the io_buffer starting from the kChecksumStart // position. std::string checksum = base::StringPrintf("%010d", sum); - buffer = io_buffer->data() + kChecksumStart; DCHECK(kChecksumLength == checksum.length()); - memcpy(buffer, checksum.c_str(), kChecksumLength); + DCHECK_GE(buffer_size, kChecksumStart + kChecksumLength); + memcpy(buffer + kChecksumStart, checksum.c_str(), kChecksumLength); // Copy the size of the <payload> into the io_buffer starting from the // kPayloadSizeStart position. - buffer = io_buffer->data() + kPayloadSizeStart; std::string payload_size = base::StringPrintf("%07d", load_size_); DCHECK(kPayloadSizeLength == payload_size.length()); - memcpy(buffer, payload_size.c_str(), kPayloadSizeLength); + DCHECK_GE(buffer_size, kPayloadSizeStart + kPayloadSizeLength); + memcpy(buffer + kPayloadSizeStart, payload_size.c_str(), kPayloadSizeLength); +} + +NetworkStats::Status NetworkStats::VerifyPackets() { + uint32 packet_start = 0; + size_t message_length = encoded_message_.length(); + uint32 receiving_packet_size = ReceivingPacketSize(load_size_); + 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; + } + if (message_length > packet_start) + status = TOO_MANY_PACKETS; + return status; } -NetworkStats::Status NetworkStats::VerifyBytes() { +NetworkStats::Status NetworkStats::VerifyBytes(const std::string& response) { // If the "echo response" doesn't have enough bytes, then return false. - if (encoded_message_.length() <= kVersionStart) + if (response.length() <= kVersionStart) return ZERO_LENGTH_ERROR; - if (encoded_message_.length() <= kChecksumStart) + if (response.length() <= kChecksumStart) return NO_CHECKSUM_ERROR; - if (encoded_message_.length() <= kPayloadSizeStart) + if (response.length() <= kPayloadSizeStart) return NO_PAYLOAD_SIZE_ERROR; - if (encoded_message_.length() <= kKeyStart) + if (response.length() <= kKeyStart) return NO_KEY_ERROR; - if (encoded_message_.length() <= kEncodedPayloadStart) + if (response.length() <= kEncodedPayloadStart) return NO_PAYLOAD_ERROR; // Extract the |key| from the "echo response". - std::string key_string = encoded_message_.substr(kKeyStart, kKeyLength); + std::string key_string = response.substr(kKeyStart, kKeyLength); const char* key = key_string.c_str(); int key_value = atoi(key); if (key_value < kKeyMinValue || key_value > kKeyMaxValue) return INVALID_KEY_ERROR; - std::string encoded_payload = - encoded_message_.substr(kEncodedPayloadStart); + std::string encoded_payload = response.substr(kEncodedPayloadStart); const char* encoded_data = encoded_payload.c_str(); uint32 message_length = encoded_payload.length(); message_length = std::min(message_length, kMaxMessage); @@ -395,16 +472,28 @@ NetworkStats::Status NetworkStats::VerifyBytes() { uint32 sum = GetChecksum(decoded_data, message_length); // Extract the |checksum| from the "echo response". std::string checksum_string = - encoded_message_.substr(kChecksumStart, kChecksumLength); + response.substr(kChecksumStart, kChecksumLength); const char* checksum = checksum_string.c_str(); uint32 checksum_value = atoi(checksum); if (checksum_value != sum) return INVALID_CHECKSUM; + // Verify the packet_number. + 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_) + return INVALID_PACKET_NUMBER; + stream_.Reset(); - if (!stream_.VerifyBytes(decoded_data, message_length)) + if (!stream_.VerifyBytes(&decoded_data[kPacketNumberLength], + message_length - kPacketNumberLength)) { return PATTERN_CHANGED; + } + packets_received_mask_ |= 1 << (packet_index - 1); return SUCCESS; } @@ -414,7 +503,8 @@ void NetworkStats::GetHistogramNames(const ProtocolValue& protocol, uint32 load_size, int result, std::string* rtt_histogram_name, - std::string* status_histogram_name) { + std::string* status_histogram_name, + std::string* packet_loss_histogram_name) { CHECK_GE(port, PORT_53); CHECK_LE(port, HISTOGRAM_PORT_MAX); @@ -453,6 +543,12 @@ void NetworkStats::GetHistogramNames(const ProtocolValue& protocol, protocol_string, kPorts[port], load_size_string); + + // Build "NetConnectivity.<protocol>.PacketLoss.<port>" histogram + // name. Total number of histograms are 5 (because we do this test for UDP + // only). + *packet_loss_histogram_name = base::StringPrintf( + "NetConnectivity.%s.PacketLoss.%d", protocol_string, kPorts[port]); } void NetworkStats::RecordHistograms(const ProtocolValue& protocol, @@ -462,12 +558,26 @@ void NetworkStats::RecordHistograms(const ProtocolValue& protocol, std::string rtt_histogram_name; std::string status_histogram_name; + std::string packet_loss_histogram_name; GetHistogramNames(protocol, histogram_port_, load_size_, result, &rtt_histogram_name, - &status_histogram_name); + &status_histogram_name, + &packet_loss_histogram_name); + + // For packet loss test, just record packet loss data. + if (packets_to_send_ > 1) { + base::Histogram* packet_loss_histogram = base::LinearHistogram::FactoryGet( + packet_loss_histogram_name, + 1, + 2 << kMaximumPackets, + (2 << kMaximumPackets) + 1, + base::Histogram::kUmaTargetedHistogramFlag); + packet_loss_histogram->Add(packets_received_mask_); + return; + } if (result == net::OK) { base::Histogram* rtt_histogram = base::Histogram::FactoryTimeGet( @@ -630,8 +740,7 @@ void CollectNetworkStats(const std::string& network_stats_server, CR_DEFINE_STATIC_LOCAL(scoped_refptr<base::FieldTrial>, trial, ()); static bool collect_stats = false; - static uint32 kTCPTestingPort; - static uint32 kUDPTestingPort; + static uint32 port; static NetworkStats::HistogramPortSelector histogram_port; if (!trial.get()) { @@ -646,7 +755,7 @@ void CollectNetworkStats(const std::string& network_stats_server, probability_per_group = kDivisor; else if (channel == chrome::VersionInfo::CHANNEL_DEV) // Enable the connectivity testing for 10% of the users in dev channel. - probability_per_group = 100; + probability_per_group = 500; else if (channel == chrome::VersionInfo::CHANNEL_BETA) // Enable the connectivity testing for 5% of the users in beta channel. probability_per_group = 50; @@ -670,8 +779,7 @@ void CollectNetworkStats(const std::string& network_stats_server, base::RandInt(NetworkStats::PORT_53, NetworkStats::PORT_8080)); DCHECK_GE(histogram_port, NetworkStats::PORT_53); DCHECK_LE(histogram_port, NetworkStats::PORT_8080); - kTCPTestingPort = kPorts[histogram_port]; - kUDPTestingPort = kPorts[histogram_port]; + port = kPorts[histogram_port]; } } @@ -689,29 +797,55 @@ void CollectNetworkStats(const std::string& network_stats_server, net::HostResolver* host_resolver = io_thread->globals()->host_resolver.get(); DCHECK(host_resolver); - net::HostPortPair udp_server_address(network_stats_server, kUDPTestingPort); - - UDPStatsClient* small_udp_stats = new UDPStatsClient(); - small_udp_stats->Start( - host_resolver, udp_server_address, histogram_port, - kSmallTestBytesToSend, net::CompletionCallback()); - - UDPStatsClient* large_udp_stats = new UDPStatsClient(); - large_udp_stats->Start( - host_resolver, udp_server_address, histogram_port, - kLargeTestBytesToSend, net::CompletionCallback()); + net::HostPortPair server_address(network_stats_server, port); - net::HostPortPair tcp_server_address(network_stats_server, kTCPTestingPort); - - TCPStatsClient* small_tcp_client = new TCPStatsClient(); - small_tcp_client->Start( - host_resolver, tcp_server_address, histogram_port, - kSmallTestBytesToSend, net::CompletionCallback()); - - TCPStatsClient* large_tcp_client = new TCPStatsClient(); - large_tcp_client->Start( - host_resolver, tcp_server_address, histogram_port, - kLargeTestBytesToSend, net::CompletionCallback()); + int experiment_to_run = base::RandInt(1, 5); + switch (experiment_to_run) { + case 1: + { + UDPStatsClient* small_udp_stats = new UDPStatsClient(); + small_udp_stats->Start( + host_resolver, server_address, histogram_port, + kSmallTestBytesToSend, 1, net::CompletionCallback()); + } + break; + + case 2: + { + UDPStatsClient* large_udp_stats = new UDPStatsClient(); + large_udp_stats->Start( + host_resolver, server_address, histogram_port, + kLargeTestBytesToSend, 1, net::CompletionCallback()); + } + break; + + case 3: + { + TCPStatsClient* small_tcp_client = new TCPStatsClient(); + small_tcp_client->Start( + host_resolver, server_address, histogram_port, + kSmallTestBytesToSend, 1, net::CompletionCallback()); + } + break; + + case 4: + { + TCPStatsClient* large_tcp_client = new TCPStatsClient(); + large_tcp_client->Start( + host_resolver, server_address, histogram_port, + kLargeTestBytesToSend, 1, net::CompletionCallback()); + } + break; + + case 5: + { + UDPStatsClient* packet_loss_udp_stats = new UDPStatsClient(); + packet_loss_udp_stats->Start( + host_resolver, server_address, histogram_port, + kLargeTestBytesToSend, kMaximumPackets, net::CompletionCallback()); + } + break; + } } } // namespace chrome_browser_net diff --git a/chrome/browser/net/network_stats.h b/chrome/browser/net/network_stats.h index 8425c36..76c7d1b 100644 --- a/chrome/browser/net/network_stats.h +++ b/chrome/browser/net/network_stats.h @@ -22,7 +22,6 @@ #include "net/base/ip_endpoint.h" #include "net/base/test_data_stream.h" #include "net/socket/socket.h" - namespace chrome_browser_net { // This class is used for live experiment of network connectivity (either TCP or @@ -70,6 +69,8 @@ class NetworkStats { 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. }; @@ -101,6 +102,7 @@ class NetworkStats { const net::HostPortPair& server, HistogramPortSelector histogram_port, uint32 bytes_to_send, + uint32 packets_to_send, const net::CompletionCallback& callback); protected: @@ -114,16 +116,16 @@ class NetworkStats { // |finished_callback| is mainly useful for unittests. void Initialize(uint32 bytes_to_send, HistogramPortSelector histogram_port, + 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; - // This method is called after socket connection is completed. It will send - // |bytes_to_send| bytes to |server| by calling SendData(). After successfully - // sending data to the |server|, it calls ReadData() to read/verify the data - // from the |server|. Returns true if successful. + // 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 @@ -151,6 +153,9 @@ class NetworkStats { // Returns |addresses_|. net::AddressList GetAddressList() 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) @@ -170,6 +175,12 @@ class NetworkStats { // 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(); + // Callbacks when an internal IO is completed. void OnReadComplete(int result); void OnWriteComplete(int result); @@ -198,36 +209,42 @@ class NetworkStats { // Fills the |io_buffer| with the "echo request" message. This gets the // <payload> from |stream_| and calculates the <checksum> of the <payload> and // returns the "echo request" that has <version>, <checksum>, <payload_size> - // and <payload>. - void GetEchoRequest(net::IOBuffer* io_buffer); + // and <payload>. Every <payload> has a unique packet number stored in it. + void GetEchoRequest(net::IOBufferWithSize* io_buffer); - // Sets |encoded_message_| for testing VerifyBytes. - void set_encoded_message(const std::string& message) { - encoded_message_ = message; - } + // This method verifies that we have received all the packets we have sent. It + // verifies the |encoded_message_| by calling VerifyBytes() for each packet + // that is in it. It returns SUCCESS, if all the packets are verified. + NetworkStats::Status VerifyPackets(); - // This method parses the "echo response" message in the |encoded_message_| to - // verify that the <payload> is same as what we had sent in "echo request" - // message. It returns SUCCESS, if all the bytes are verified. - NetworkStats::Status VerifyBytes(); + // This method parses the "echo response" message in the |response| to verify + // that the <payload> is same as what we had sent in "echo request" message. + // As it verifies the response in each packet, it also extracts the packet + // number, and records that said packet number responded. It returns SUCCESS, + // if all the bytes are verified. + NetworkStats::Status VerifyBytes(const std::string& response); // Returns the histogram names for collecting network connectivity stats. // This is called by RecordHistograms. It sets the histogram names in // |rtt_histogram_name| and |status_histogram_name|. // If |result| equals to net::OK, it returns // "NetConnectivity.<protocol>.Success.<port>.<load_size>.RTT" as histogram - // name for RTT histogram and + // name for RTT histogram, // "NetConnectivity.<protocol>.Status.<port>.<load_size>" as histogram name - // for status histogram. |protocol| argument sets <protocol> in the histogram - // name. It would be either TCP or UDP. <port> is the string representation of - // |histogram_port|. |load_size| argument determines <load_size> in the - // histogram name. It would be either 100B or 1K. + // for status histogram and + // "NetConnectivity.<protocol>.PacketLoss.<port>" as histogram name + // for packet loss histogram. + // |protocol| argument sets <protocol> in the histogram name. It would be + // either TCP or UDP. <port> is the string representation of |histogram_port|. + // |load_size| argument determines <load_size> in the histogram name. It would + // be either 100B or 1K. static void GetHistogramNames(const ProtocolValue& protocol, HistogramPortSelector histogram_port, uint32 load_size, int result, std::string* rtt_histogram_name, - std::string* status_histogram_name); + std::string* status_histogram_name, + std::string* packet_loss_histogram_name); // The socket handle for this session. scoped_ptr<net::Socket> socket_; @@ -265,6 +282,13 @@ class NetworkStats { // The time when the session was started. base::TimeTicks start_time_; + // 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_; + // We use this factory to create timeout tasks for socket's ReadData. base::WeakPtrFactory<NetworkStats> weak_factory_; }; diff --git a/chrome/browser/net/network_stats_unittest.cc b/chrome/browser/net/network_stats_unittest.cc index ff1ccfb..716a2b7 100644 --- a/chrome/browser/net/network_stats_unittest.cc +++ b/chrome/browser/net/network_stats_unittest.cc @@ -36,7 +36,7 @@ class NetworkStatsTestUDP : public NetworkStatsTest { } protected: - void RunUDPEchoTest(int bytes) { + void RunUDPEchoTest(int bytes, int packets) { net::TestCompletionCallback cb; scoped_ptr<net::MockHostResolver> host_resolver( @@ -49,6 +49,7 @@ class NetworkStatsTestUDP : public NetworkStatsTest { test_server_.host_port_pair(), NetworkStats::HISTOGRAM_PORT_MAX, bytes, + packets, cb.callback())); int rv = cb.WaitForResult(); // Check there were no errors during connect/write/read to echo UDP server. @@ -67,7 +68,7 @@ class NetworkStatsTestTCP : public NetworkStatsTest { } protected: - void RunTCPEchoTest(int bytes) { + void RunTCPEchoTest(int bytes, int packets) { net::TestCompletionCallback cb; scoped_ptr<net::MockHostResolver> host_resolver( @@ -80,6 +81,7 @@ class NetworkStatsTestTCP : public NetworkStatsTest { test_server_.host_port_pair(), NetworkStats::HISTOGRAM_PORT_MAX, bytes, + packets, cb.callback())); int rv = cb.WaitForResult(); // Check there were no errors during connect/write/read to echo TCP server. @@ -91,137 +93,188 @@ class NetworkStatsTestTCP : public NetworkStatsTest { TEST_F(NetworkStatsTestUDP, UDPEcho_100B_Of_Data) { ASSERT_TRUE(test_server_.Start()); - RunUDPEchoTest(100); + RunUDPEchoTest(100, 1); } TEST_F(NetworkStatsTestUDP, UDPEcho_1K_Of_Data) { ASSERT_TRUE(test_server_.Start()); - RunUDPEchoTest(1024); + RunUDPEchoTest(1024, 1); +} + +TEST_F(NetworkStatsTestUDP, UDPEchoMultiplePackets) { + ASSERT_TRUE(test_server_.Start()); + RunUDPEchoTest(1024, 4); } TEST_F(NetworkStatsTestTCP, TCPEcho_100B_Of_Data) { ASSERT_TRUE(test_server_.Start()); - RunTCPEchoTest(100); + RunTCPEchoTest(100, 1); } TEST_F(NetworkStatsTestTCP, TCPEcho_1K_Of_Data) { ASSERT_TRUE(test_server_.Start()); - RunTCPEchoTest(1024); + RunTCPEchoTest(1024, 1); } 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; net::TestCompletionCallback cb; network_stats.Initialize(KBytesToSend, NetworkStats::HISTOGRAM_PORT_MAX, + packets_to_send, cb.callback()); std::string message; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::ZERO_LENGTH_ERROR, network_stats.VerifyBytes()); + 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 invalid_key = base::StringPrintf("%06d", -1); std::string key_string = base::StringPrintf("%06d", 899999); - std::string short_payload( + 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( + std::string long_payload(packet_number_string + "1234567890123456789012345678901234567890123456789012345678901234567890" "1234567890123456789012345678901234567890123456789012345678901234567890"); - network_stats.set_encoded_message(version); - EXPECT_EQ(NetworkStats::NO_CHECKSUM_ERROR, network_stats.VerifyBytes()); + message = version; + EXPECT_EQ(NetworkStats::NO_CHECKSUM_ERROR, + network_stats.VerifyBytes(message)); message = version + invalid_checksum; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::NO_PAYLOAD_SIZE_ERROR, network_stats.VerifyBytes()); + EXPECT_EQ(NetworkStats::NO_PAYLOAD_SIZE_ERROR, + network_stats.VerifyBytes(message)); message = version + invalid_checksum + payload_size; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::NO_KEY_ERROR, network_stats.VerifyBytes()); + EXPECT_EQ(NetworkStats::NO_KEY_ERROR, network_stats.VerifyBytes(message)); message = version + invalid_checksum + payload_size + invalid_key; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::NO_PAYLOAD_ERROR, network_stats.VerifyBytes()); + EXPECT_EQ(NetworkStats::NO_PAYLOAD_ERROR, network_stats.VerifyBytes(message)); message = version + invalid_checksum + payload_size + invalid_key + short_payload; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::INVALID_KEY_ERROR, network_stats.VerifyBytes()); + EXPECT_EQ(NetworkStats::INVALID_KEY_ERROR, + network_stats.VerifyBytes(message)); message = version + invalid_checksum + payload_size + key_string + short_payload; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::TOO_SHORT_PAYLOAD, network_stats.VerifyBytes()); + EXPECT_EQ(NetworkStats::TOO_SHORT_PAYLOAD, + network_stats.VerifyBytes(message)); message = version + invalid_checksum + payload_size + key_string + long_payload; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::TOO_LONG_PAYLOAD, network_stats.VerifyBytes()); + EXPECT_EQ(NetworkStats::TOO_LONG_PAYLOAD, network_stats.VerifyBytes(message)); + + 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); - scoped_refptr<net::IOBuffer> io_buffer(new net::IOBuffer(KBytesToSend)); - network_stats.stream_.GetBytes(io_buffer->data(), KBytesToSend); - char* decoded_data = io_buffer->data(); - uint32 sum = network_stats.GetChecksum(decoded_data, KBytesToSend); + // 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 + 1]; - memset(encoded_data, 0, KBytesToSend + 1); + char encoded_data[KBytesToSend]; + memset(encoded_data, 0, KBytesToSend); const char* key = key_string.c_str(); network_stats.Crypt( - key, key_string.length(), decoded_data, KBytesToSend, encoded_data); - std::string payload(encoded_data, KBytesToSend); - - message = version + invalid_checksum + payload_size + key_string + payload; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::INVALID_CHECKSUM, network_stats.VerifyBytes()); - - char temp_char_0 = decoded_data[0]; - decoded_data[0] = decoded_data[1]; - decoded_data[1] = temp_char_0; - char encoded_data_2[KBytesToSend + 1]; - memset(encoded_data_2, 0, KBytesToSend + 1); - network_stats.Crypt( - key, key_string.length(), decoded_data, KBytesToSend, encoded_data_2); - std::string pattern_changed_payload(encoded_data_2, KBytesToSend); + key, key_string.length(), payload_data, KBytesToSend, encoded_data); + std::string encoded_payload(encoded_data, KBytesToSend); + // Test invalid checksum error by sending wrong checksum. + 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 + - pattern_changed_payload; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::PATTERN_CHANGED, network_stats.VerifyBytes()); - - message = version + checksum + payload_size + key_string + payload; - network_stats.set_encoded_message(message); - EXPECT_EQ(NetworkStats::SUCCESS, network_stats.VerifyBytes()); + invalid_packet_number_payload; + EXPECT_EQ(NetworkStats::INVALID_PACKET_NUMBER, + network_stats.VerifyBytes(message)); + EXPECT_EQ(0u, network_stats.packets_received_mask()); + + // Corrupt the randomly generated bytes in payload data. + char corrupted_data_2[KBytesToSend]; + memcpy(corrupted_data_2, payload_data, KBytesToSend); + char temp_char_1 = corrupted_data_2[packet_number_size]; + corrupted_data_2[packet_number_size] = + corrupted_data_2[packet_number_size + 1]; + corrupted_data_2[packet_number_size + 1] = temp_char_1; + char encoded_corrupted_data_2[KBytesToSend]; + memset(encoded_corrupted_data_2, 0, KBytesToSend); + network_stats.Crypt(key, + key_string.length(), + corrupted_data_2, + KBytesToSend, + encoded_corrupted_data_2); + std::string invalid_payload(encoded_corrupted_data_2, KBytesToSend); + 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()); + + message = version + checksum + payload_size + key_string + encoded_payload; + EXPECT_EQ(NetworkStats::SUCCESS, network_stats.VerifyBytes(message)); + EXPECT_EQ(1u, network_stats.packets_received_mask()); } TEST_F(NetworkStatsTest, GetHistogramNames) { // Test TCP, large packet, success histogram name. std::string rtt_histogram_name; std::string status_histogram_name; + std::string packet_loss_histogram_name; NetworkStats::GetHistogramNames(NetworkStats::PROTOCOL_TCP, NetworkStats::PORT_53, 1024, net::OK, &rtt_histogram_name, - &status_histogram_name); + &status_histogram_name, + &packet_loss_histogram_name); EXPECT_EQ("NetConnectivity.TCP.Success.53.1K.RTT", rtt_histogram_name); EXPECT_EQ("NetConnectivity.TCP.Status.53.1K", status_histogram_name); + EXPECT_EQ("NetConnectivity.TCP.PacketLoss.53", packet_loss_histogram_name); // Test UDP, small packet, failure histogram name. std::string rtt_histogram_name1; std::string status_histogram_name1; + std::string packet_loss_histogram_name1; NetworkStats::GetHistogramNames(NetworkStats::PROTOCOL_UDP, NetworkStats::PORT_6121, 100, net::ERR_INVALID_ARGUMENT, &rtt_histogram_name1, - &status_histogram_name1); + &status_histogram_name1, + &packet_loss_histogram_name1); EXPECT_EQ("", rtt_histogram_name1); EXPECT_EQ("NetConnectivity.UDP.Status.6121.100B", status_histogram_name1); + EXPECT_EQ("NetConnectivity.UDP.PacketLoss.6121", packet_loss_histogram_name1); } } // namespace chrome_browser_net |