summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-02 20:10:26 +0000
committerrtenneti@chromium.org <rtenneti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-02 20:10:26 +0000
commit15bb050eb0943488ee773de7943854d1c074de2a (patch)
tree87b9faef6f64e0a6be55add32518b39b202afdf6
parent3af58c07fc124ced9d709820a02d1f884466c4ae (diff)
downloadchromium_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.cc302
-rw-r--r--chrome/browser/net/network_stats.h66
-rw-r--r--chrome/browser/net/network_stats_unittest.cc161
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