This source file includes following definitions.
- DynamicHistogramEnumeration
- DynamicHistogramTimes
- DynamicHistogramCounts
- weak_factory_
- Start
- StartOneTest
- ResetData
- DoConnect
- ConnectComplete
- SendHelloRequest
- SendProbeRequest
- ReadData
- OnReadComplete
- ReadComplete
- UpdateReception
- SendData
- OnWriteComplete
- UpdateSendBuffer
- StartReadDataTimer
- OnReadDataTimeout
- TestPhaseComplete
- GetNextTest
- DoFinishCallback
- RecordHistograms
- RecordInterArrivalHistograms
- RecordPacketsReceivedHistograms
- RecordNATTestReceivedHistograms
- RecordPacketSizeTestReceivedHistograms
- RecordPacketLossSeriesHistograms
- RecordRTTHistograms
- RecordSendToLastRecvDelayHistograms
- has_pending_proxy_resolution_
- StartResolveProxy
- OnResolveProxyComplete
- CollectNetworkStats
- StartNetworkStatsTest
#include "chrome/browser/net/network_stats.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/common/chrome_version_info.h"
#include "content/public/browser/browser_thread.h"
#include "net/base/net_errors.h"
#include "net/base/network_change_notifier.h"
#include "net/base/test_completion_callback.h"
#include "net/dns/single_request_host_resolver.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/client_socket_factory.h"
#include "net/udp/datagram_client_socket.h"
#include "url/gurl.h"
using content::BrowserThread;
namespace chrome_browser_net {
uint32 NetworkStats::maximum_tests_ = 8;
uint32 NetworkStats::maximum_sequential_packets_ = 21;
uint32 NetworkStats::maximum_NAT_packets_ = 2;
uint32 NetworkStats::maximum_NAT_idle_seconds_ = 300;
bool NetworkStats::start_test_after_connect_ = true;
const uint32 kProbePacketBytes[] = {100, 500, 1200};
const uint32 kPacketSizeChoices = arraysize(kProbePacketBytes);
const uint16 kPorts[] = {443, 80};
const uint32 kCorrelatedLossPacketCount = 6;
const uint32 kMaxMessageSize = 1600;
const uint32 kMaxUdpReceiveBufferSize = 63000;
const uint32 kMaxUdpSendBufferSize = 4096;
const char* kTestName[] = {"TokenRequest", "StartPacket", "NonPacedPacket",
"PacedPacket", "NATBind", "PacketSizeTest"};
const uint32 kMinimumReceivedPacketsForPacingTest = 2;
const uint32 kMinimumReceivedPacketsForNATTest = 10;
const uint32 kMaximumPacingMicros = 1000000;
const uint32 kGetTokenTimeoutSeconds = 10;
const uint32 kReadDataTimeoutSeconds = 30;
const uint32 kReadNATTimeoutSeconds = 10;
const uint32 kReadPacketSizeTimeoutSeconds = 10;
uint32 kMaximumPacketSizeTestPackets = 1;
void DynamicHistogramEnumeration(const std::string& name,
uint32 sample,
uint32 boundary_value) {
base::HistogramBase* histogram_pointer = base::LinearHistogram::FactoryGet(
name,
1,
boundary_value,
boundary_value + 1,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram_pointer->Add(sample);
}
void DynamicHistogramTimes(const std::string& name,
const base::TimeDelta& sample) {
base::HistogramBase* histogram_pointer = base::Histogram::FactoryTimeGet(
name,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromSeconds(30),
50,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram_pointer->AddTime(sample);
}
void DynamicHistogramCounts(const std::string& name,
uint32 sample,
uint32 min,
uint32 max,
uint32 bucket_count) {
base::HistogramBase* histogram_pointer = base::Histogram::FactoryGet(
name, min, max, bucket_count,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram_pointer->Add(sample);
}
NetworkStats::NetworkStats(net::ClientSocketFactory* socket_factory)
: socket_factory_(socket_factory),
histogram_port_(0),
has_proxy_server_(false),
probe_packet_bytes_(0),
bytes_for_packet_size_test_(0),
current_test_index_(0),
read_state_(READ_STATE_IDLE),
write_state_(WRITE_STATE_IDLE),
weak_factory_(this) {
ResetData();
}
NetworkStats::~NetworkStats() {}
bool NetworkStats::Start(net::HostResolver* host_resolver,
const net::HostPortPair& server_host_port_pair,
uint16 histogram_port,
bool has_proxy_server,
uint32 probe_bytes,
uint32 bytes_for_packet_size_test,
const net::CompletionCallback& finished_callback) {
DCHECK(host_resolver);
histogram_port_ = histogram_port;
has_proxy_server_ = has_proxy_server;
probe_packet_bytes_ = probe_bytes;
bytes_for_packet_size_test_ = bytes_for_packet_size_test;
finished_callback_ = finished_callback;
test_sequence_.clear();
test_sequence_.push_back(TOKEN_REQUEST);
ResetData();
scoped_ptr<net::SingleRequestHostResolver> resolver(
new net::SingleRequestHostResolver(host_resolver));
net::HostResolver::RequestInfo request(server_host_port_pair);
int rv =
resolver->Resolve(request,
net::DEFAULT_PRIORITY,
&addresses_,
base::Bind(base::IgnoreResult(&NetworkStats::DoConnect),
base::Unretained(this)),
net::BoundNetLog());
if (rv == net::ERR_IO_PENDING) {
resolver_.swap(resolver);
return true;
}
return DoConnect(rv);
}
void NetworkStats::StartOneTest() {
if (test_sequence_[current_test_index_] == TOKEN_REQUEST) {
DCHECK_EQ(WRITE_STATE_IDLE, write_state_);
write_buffer_ = NULL;
SendHelloRequest();
} else {
SendProbeRequest();
}
}
void NetworkStats::ResetData() {
DCHECK_EQ(WRITE_STATE_IDLE, write_state_);
write_buffer_ = NULL;
packets_received_mask_.reset();
first_arrival_time_ = base::TimeTicks();
last_arrival_time_ = base::TimeTicks();
packet_rtt_.clear();
packet_rtt_.resize(maximum_sequential_packets_);
probe_request_time_ = base::TimeTicks();
}
bool NetworkStats::DoConnect(int result) {
if (result != net::OK) {
TestPhaseComplete(RESOLVE_FAILED, result);
return false;
}
scoped_ptr<net::DatagramClientSocket> udp_socket =
socket_factory_->CreateDatagramClientSocket(
net::DatagramSocket::DEFAULT_BIND,
net::RandIntCallback(),
NULL,
net::NetLog::Source());
DCHECK(udp_socket);
DCHECK(!socket_);
socket_ = udp_socket.Pass();
const net::IPEndPoint& endpoint = addresses_.front();
int rv = socket_->Connect(endpoint);
if (rv < 0) {
TestPhaseComplete(CONNECT_FAILED, rv);
return false;
}
socket_->SetSendBufferSize(kMaxUdpSendBufferSize);
socket_->SetReceiveBufferSize(kMaxUdpReceiveBufferSize);
return ConnectComplete(rv);
}
bool NetworkStats::ConnectComplete(int result) {
if (result < 0) {
TestPhaseComplete(CONNECT_FAILED, result);
return false;
}
if (start_test_after_connect_) {
if (ReadData() != net::ERR_IO_PENDING) {
TestPhaseComplete(READ_FAILED, result);
return false;
}
SendHelloRequest();
} else {
if (!finished_callback_.is_null())
finished_callback_.Run(result);
}
return true;
}
void NetworkStats::SendHelloRequest() {
StartReadDataTimer(kGetTokenTimeoutSeconds, current_test_index_);
ProbePacket probe_packet;
probe_message_.SetPacketHeader(ProbePacket_Type_HELLO_REQUEST, &probe_packet);
probe_packet.set_group_id(current_test_index_);
std::string output = probe_message_.MakeEncodedPacket(probe_packet);
int result = SendData(output);
if (result < 0 && result != net::ERR_IO_PENDING)
TestPhaseComplete(WRITE_FAILED, result);
}
void NetworkStats::SendProbeRequest() {
ResetData();
uint32 timeout_seconds = kReadDataTimeoutSeconds;
uint32 number_packets = maximum_sequential_packets_;
uint32 probe_bytes = probe_packet_bytes_;
pacing_interval_ = base::TimeDelta();
switch (test_sequence_[current_test_index_]) {
case START_PACKET_TEST:
case NON_PACED_PACKET_TEST:
break;
case PACED_PACKET_TEST: {
pacing_interval_ =
std::min(inter_arrival_time_,
base::TimeDelta::FromMicroseconds(kMaximumPacingMicros));
timeout_seconds += pacing_interval_.InMicroseconds() *
(maximum_sequential_packets_ - 1) / 1000000;
break;
}
case NAT_BIND_TEST: {
DCHECK_LE(maximum_NAT_idle_seconds_, 4000U);
int nat_test_idle_seconds = base::RandInt(1, maximum_NAT_idle_seconds_);
pacing_interval_ = base::TimeDelta::FromSeconds(nat_test_idle_seconds);
timeout_seconds = nat_test_idle_seconds + kReadNATTimeoutSeconds;
number_packets = maximum_NAT_packets_;
break;
}
case PACKET_SIZE_TEST: {
number_packets = kMaximumPacketSizeTestPackets;
probe_bytes = bytes_for_packet_size_test_;
timeout_seconds = kReadPacketSizeTimeoutSeconds;
break;
}
default:
NOTREACHED();
return;
}
DVLOG(1) << "NetworkStat: Probe pacing " << pacing_interval_.InMicroseconds()
<< " microseconds. Time out " << timeout_seconds << " seconds";
ProbePacket probe_packet;
probe_message_.GenerateProbeRequest(token_,
current_test_index_,
probe_bytes,
pacing_interval_.InMicroseconds(),
number_packets,
&probe_packet);
std::string output = probe_message_.MakeEncodedPacket(probe_packet);
StartReadDataTimer(timeout_seconds, current_test_index_);
probe_request_time_ = base::TimeTicks::Now();
int result = SendData(output);
if (result < 0 && result != net::ERR_IO_PENDING)
TestPhaseComplete(WRITE_FAILED, result);
}
int NetworkStats::ReadData() {
if (!socket_.get())
return 0;
if (read_state_ == READ_STATE_READ_PENDING)
return net::ERR_IO_PENDING;
int rv = 0;
while (true) {
DCHECK(!read_buffer_.get());
read_buffer_ = new net::IOBuffer(kMaxMessageSize);
rv = socket_->Read(
read_buffer_.get(),
kMaxMessageSize,
base::Bind(&NetworkStats::OnReadComplete, weak_factory_.GetWeakPtr()));
if (rv <= 0)
break;
if (ReadComplete(rv))
return rv;
};
if (rv == net::ERR_IO_PENDING)
read_state_ = READ_STATE_READ_PENDING;
return rv;
}
void NetworkStats::OnReadComplete(int result) {
DCHECK_NE(net::ERR_IO_PENDING, result);
DCHECK_EQ(READ_STATE_READ_PENDING, read_state_);
read_state_ = READ_STATE_IDLE;
if (!ReadComplete(result)) {
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(base::IgnoreResult(&NetworkStats::ReadData),
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(1));
}
}
bool NetworkStats::ReadComplete(int result) {
DCHECK(socket_.get());
DCHECK_NE(net::ERR_IO_PENDING, result);
if (result < 0) {
read_buffer_ = NULL;
TestPhaseComplete(READ_FAILED, result);
return true;
}
std::string encoded_message(read_buffer_->data(),
read_buffer_->data() + result);
read_buffer_ = NULL;
ProbePacket probe_packet;
if (!probe_message_.ParseInput(encoded_message, &probe_packet))
return false;
if (probe_packet.group_id() != current_test_index_)
return false;
bool current_test_complete = false;
switch (probe_packet.header().type()) {
case ProbePacket_Type_HELLO_REPLY:
token_ = probe_packet.token();
if (current_test_index_ == 0)
test_sequence_.push_back(START_PACKET_TEST);
current_test_complete = true;
break;
case ProbePacket_Type_PROBE_REPLY:
current_test_complete = UpdateReception(probe_packet);
break;
default:
DVLOG(1) << "Received unexpected packet type: "
<< probe_packet.header().type();
}
if (!current_test_complete) {
return false;
}
return TestPhaseComplete(SUCCESS, net::OK);
}
bool NetworkStats::UpdateReception(const ProbePacket& probe_packet) {
uint32 packet_index = probe_packet.packet_index();
if (packet_index >= packet_rtt_.size())
return false;
packets_received_mask_.set(packet_index);
TestType test_type = test_sequence_[current_test_index_];
uint32 received_packets = packets_received_mask_.count();
base::TimeTicks current_time = base::TimeTicks::Now();
last_arrival_time_ = current_time;
if (first_arrival_time_.is_null())
first_arrival_time_ = current_time;
if (test_type == NAT_BIND_TEST) {
return received_packets >= maximum_NAT_packets_;
}
if (test_type == PACKET_SIZE_TEST) {
return received_packets >= kMaximumPacketSizeTestPackets;
}
base::TimeDelta rtt =
current_time - probe_request_time_ -
base::TimeDelta::FromMicroseconds(std::max(
static_cast<int64>(0), probe_packet.server_processing_micros()));
base::TimeDelta min_rtt = base::TimeDelta::FromMicroseconds(1L);
packet_rtt_[packet_index] = (rtt >= min_rtt) ? rtt : min_rtt;
if (received_packets < maximum_sequential_packets_)
return false;
inter_arrival_time_ = (last_arrival_time_ - first_arrival_time_) /
std::max(1U, (received_packets - 1));
if (test_type == START_PACKET_TEST) {
test_sequence_.push_back(PACKET_SIZE_TEST);
test_sequence_.push_back(TOKEN_REQUEST);
test_sequence_.push_back(base::RandInt(0, 1) ? PACED_PACKET_TEST
: NON_PACED_PACKET_TEST);
test_sequence_.push_back(TOKEN_REQUEST);
test_sequence_.push_back(NAT_BIND_TEST);
test_sequence_.push_back(TOKEN_REQUEST);
}
return true;
}
int NetworkStats::SendData(const std::string& output) {
if (write_buffer_.get() || !socket_.get() ||
write_state_ == WRITE_STATE_WRITE_PENDING) {
return net::ERR_UNEXPECTED;
}
scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer(output));
write_buffer_ = new net::DrainableIOBuffer(buffer.get(), buffer->size());
int bytes_written = socket_->Write(
write_buffer_.get(),
write_buffer_->BytesRemaining(),
base::Bind(&NetworkStats::OnWriteComplete, weak_factory_.GetWeakPtr()));
if (bytes_written < 0) {
if (bytes_written == net::ERR_IO_PENDING)
write_state_ = WRITE_STATE_WRITE_PENDING;
return bytes_written;
}
UpdateSendBuffer(bytes_written);
return net::OK;
}
void NetworkStats::OnWriteComplete(int result) {
DCHECK_NE(net::ERR_IO_PENDING, result);
DCHECK_EQ(WRITE_STATE_WRITE_PENDING, write_state_);
write_state_ = WRITE_STATE_IDLE;
if (result < 0 || !socket_.get() || write_buffer_ == NULL) {
TestPhaseComplete(WRITE_FAILED, result);
return;
}
UpdateSendBuffer(result);
}
void NetworkStats::UpdateSendBuffer(int bytes_sent) {
write_buffer_->DidConsume(bytes_sent);
DCHECK_EQ(write_buffer_->BytesRemaining(), 0);
DCHECK_EQ(WRITE_STATE_IDLE, write_state_);
write_buffer_ = NULL;
}
void NetworkStats::StartReadDataTimer(uint32 seconds, uint32 test_index) {
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&NetworkStats::OnReadDataTimeout,
weak_factory_.GetWeakPtr(),
test_index),
base::TimeDelta::FromSeconds(seconds));
}
void NetworkStats::OnReadDataTimeout(uint32 test_index) {
if (test_index != current_test_index_)
return;
TestType test_type = test_sequence_[current_test_index_];
uint32 received_packets = packets_received_mask_.count();
if (received_packets >= 2) {
inter_arrival_time_ =
(last_arrival_time_ - first_arrival_time_) / (received_packets - 1);
}
if (test_type == START_PACKET_TEST) {
if (received_packets >= kMinimumReceivedPacketsForPacingTest) {
test_sequence_.push_back(TOKEN_REQUEST);
test_sequence_.push_back(PACKET_SIZE_TEST);
test_sequence_.push_back(TOKEN_REQUEST);
test_sequence_.push_back(base::RandInt(0, 1) ? PACED_PACKET_TEST
: NON_PACED_PACKET_TEST);
}
if (received_packets >= kMinimumReceivedPacketsForNATTest) {
test_sequence_.push_back(TOKEN_REQUEST);
test_sequence_.push_back(NAT_BIND_TEST);
test_sequence_.push_back(TOKEN_REQUEST);
}
}
TestPhaseComplete(READ_TIMED_OUT, net::ERR_FAILED);
}
bool NetworkStats::TestPhaseComplete(Status status, int result) {
if (write_state_ == WRITE_STATE_WRITE_PENDING) {
UMA_HISTOGRAM_BOOLEAN("NetConnectivity5.TestFailed.WritePending", true);
} else if (status == SUCCESS || status == READ_TIMED_OUT) {
TestType current_test = test_sequence_[current_test_index_];
DCHECK_LT(current_test, TEST_TYPE_MAX);
if (current_test != TOKEN_REQUEST) {
RecordHistograms(current_test);
} else if (current_test_index_ > 0) {
if (test_sequence_[current_test_index_ - 1] == NAT_BIND_TEST) {
RecordNATTestReceivedHistograms(status);
} else if (test_sequence_[current_test_index_ - 1] == PACKET_SIZE_TEST) {
RecordPacketSizeTestReceivedHistograms(status);
}
}
current_test = GetNextTest();
if (current_test_index_ <= maximum_tests_ && current_test < TEST_TYPE_MAX) {
DVLOG(1) << "NetworkStat: Start Probe test: " << current_test;
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&NetworkStats::StartOneTest, weak_factory_.GetWeakPtr()));
return false;
}
}
DoFinishCallback(result);
if (socket_.get())
socket_->Close();
DVLOG(1) << "NetworkStat: schedule delete self at test index "
<< current_test_index_;
delete this;
return true;
}
NetworkStats::TestType NetworkStats::GetNextTest() {
++current_test_index_;
if (current_test_index_ >= test_sequence_.size())
return TEST_TYPE_MAX;
return test_sequence_[current_test_index_];
}
void NetworkStats::DoFinishCallback(int result) {
if (!finished_callback_.is_null()) {
net::CompletionCallback callback = finished_callback_;
finished_callback_.Reset();
callback.Run(result);
}
}
void NetworkStats::RecordHistograms(TestType test_type) {
switch (test_type) {
case START_PACKET_TEST:
case NON_PACED_PACKET_TEST:
case PACED_PACKET_TEST: {
RecordInterArrivalHistograms(test_type);
RecordPacketLossSeriesHistograms(test_type);
RecordPacketsReceivedHistograms(test_type);
uint32 rtt_indices[] = {0, 1, 2, 9, 19};
for (uint32 i = 0; i < arraysize(rtt_indices); ++i) {
if (rtt_indices[i] < packet_rtt_.size())
RecordRTTHistograms(test_type, rtt_indices[i]);
}
RecordSendToLastRecvDelayHistograms(test_type);
return;
}
case NAT_BIND_TEST:
RecordSendToLastRecvDelayHistograms(test_type);
return;
case PACKET_SIZE_TEST:
return;
default:
DVLOG(1) << "Unexpected test type " << test_type
<< " in RecordHistograms.";
}
}
void NetworkStats::RecordInterArrivalHistograms(TestType test_type) {
DCHECK_NE(test_type, PACKET_SIZE_TEST);
std::string histogram_name =
base::StringPrintf("NetConnectivity5.%s.Sent%d.PacketDelay.%d.%dB",
kTestName[test_type],
maximum_sequential_packets_,
histogram_port_,
probe_packet_bytes_);
DynamicHistogramTimes(histogram_name, inter_arrival_time_ * 20);
}
void NetworkStats::RecordPacketsReceivedHistograms(TestType test_type) {
DCHECK_NE(test_type, PACKET_SIZE_TEST);
const char* test_name = kTestName[test_type];
std::string histogram_prefix = base::StringPrintf(
"NetConnectivity5.%s.Sent%d.", test_name, maximum_sequential_packets_);
std::string histogram_suffix =
base::StringPrintf(".%d.%dB", histogram_port_, probe_packet_bytes_);
std::string name = histogram_prefix + "GotAPacket" + histogram_suffix;
base::HistogramBase* histogram_pointer = base::BooleanHistogram::FactoryGet(
name, base::HistogramBase::kUmaTargetedHistogramFlag);
histogram_pointer->Add(packets_received_mask_.any());
DynamicHistogramEnumeration(
histogram_prefix + "PacketsRecv" + histogram_suffix,
packets_received_mask_.count(),
maximum_sequential_packets_ + 1);
if (!packets_received_mask_.any())
return;
base::HistogramBase* received_nth_packet_histogram =
base::Histogram::FactoryGet(
histogram_prefix + "RecvNthPacket" + histogram_suffix,
1,
maximum_sequential_packets_ + 1,
maximum_sequential_packets_ + 2,
base::HistogramBase::kUmaTargetedHistogramFlag);
int count = 0;
for (size_t j = 0; j < maximum_sequential_packets_; ++j) {
int packet_number = j + 1;
if (packets_received_mask_.test(j)) {
received_nth_packet_histogram->Add(packet_number);
++count;
}
std::string histogram_name =
base::StringPrintf("%sNumRecvFromFirst%02dPackets%s",
histogram_prefix.c_str(),
packet_number,
histogram_suffix.c_str());
DynamicHistogramEnumeration(histogram_name, count, packet_number + 1);
}
}
void NetworkStats::RecordNATTestReceivedHistograms(Status status) {
const char* test_name = kTestName[NAT_BIND_TEST];
bool test_result = status == SUCCESS;
std::string middle_name = test_result ? "Connectivity.Success"
: "Connectivity.Failure";
std::string histogram_name =
base::StringPrintf("NetConnectivity5.%s.Sent%d.%s.%d.%dB",
test_name,
maximum_NAT_packets_,
middle_name.c_str(),
histogram_port_,
probe_packet_bytes_);
uint32 bucket_count = std::min(maximum_NAT_idle_seconds_ + 2, 50U);
DynamicHistogramCounts(histogram_name,
pacing_interval_.InSeconds(),
1,
maximum_NAT_idle_seconds_ + 1,
bucket_count);
if (!test_result || !packets_received_mask_.test(0))
return;
middle_name = packets_received_mask_.test(1) ? "Bind.Success"
: "Bind.Failure";
histogram_name = base::StringPrintf("NetConnectivity5.%s.Sent%d.%s.%d.%dB",
test_name,
maximum_NAT_packets_,
middle_name.c_str(),
histogram_port_,
probe_packet_bytes_);
DynamicHistogramCounts(histogram_name,
pacing_interval_.InSeconds(),
1,
maximum_NAT_idle_seconds_ + 1,
bucket_count);
}
void NetworkStats::RecordPacketSizeTestReceivedHistograms(Status status) {
const char* test_name = kTestName[PACKET_SIZE_TEST];
bool test_result = (status == SUCCESS && packets_received_mask_.test(0));
std::string middle_name = test_result ? "Connectivity.Success"
: "Connectivity.Failure";
std::string histogram_name =
base::StringPrintf("NetConnectivity5.%s.%s.%d",
test_name,
middle_name.c_str(),
histogram_port_);
base::HistogramBase* histogram_pointer = base::LinearHistogram::FactoryGet(
histogram_name, kProbePacketBytes[kPacketSizeChoices - 1],
ProbeMessage::kMaxProbePacketBytes, 60,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram_pointer->Add(bytes_for_packet_size_test_);
}
void NetworkStats::RecordPacketLossSeriesHistograms(TestType test_type) {
DCHECK_NE(test_type, PACKET_SIZE_TEST);
const char* test_name = kTestName[test_type];
std::string series_acked_histogram_name =
base::StringPrintf("NetConnectivity5.%s.First6.SeriesRecv.%d.%dB",
test_name,
histogram_port_,
probe_packet_bytes_);
uint32 histogram_boundary = 1 << kCorrelatedLossPacketCount;
uint32 correlated_packet_mask =
(histogram_boundary - 1) & packets_received_mask_.to_ulong();
DynamicHistogramEnumeration(
series_acked_histogram_name, correlated_packet_mask, histogram_boundary);
if (!has_proxy_server_) {
series_acked_histogram_name.append(".NoProxy");
DynamicHistogramEnumeration(series_acked_histogram_name,
correlated_packet_mask,
histogram_boundary);
}
}
void NetworkStats::RecordRTTHistograms(TestType test_type, uint32 index) {
DCHECK_NE(test_type, PACKET_SIZE_TEST);
DCHECK_LT(index, packet_rtt_.size());
if (!packets_received_mask_.test(index))
return;
std::string rtt_histogram_name = base::StringPrintf(
"NetConnectivity5.%s.Sent%d.Success.RTT.Packet%02d.%d.%dB",
kTestName[test_type],
maximum_sequential_packets_,
index + 1,
histogram_port_,
probe_packet_bytes_);
DynamicHistogramTimes(rtt_histogram_name, packet_rtt_[index]);
}
void NetworkStats::RecordSendToLastRecvDelayHistograms(TestType test_type) {
DCHECK_NE(test_type, PACKET_SIZE_TEST);
if (packets_received_mask_.count() < 2)
return;
uint32 packets_sent = test_type == NAT_BIND_TEST
? maximum_NAT_packets_ : maximum_sequential_packets_;
std::string histogram_name = base::StringPrintf(
"NetConnectivity5.%s.Sent%d.SendToLastRecvDelay.%d.%dB",
kTestName[test_type],
packets_sent,
histogram_port_,
probe_packet_bytes_);
base::TimeDelta send_to_last_recv_time =
std::max(last_arrival_time_ - probe_request_time_ -
pacing_interval_ * (packets_sent - 1),
base::TimeDelta::FromMilliseconds(0));
DynamicHistogramTimes(histogram_name, send_to_last_recv_time);
}
ProxyDetector::ProxyDetector(net::ProxyService* proxy_service,
const net::HostPortPair& server_address,
OnResolvedCallback callback)
: proxy_service_(proxy_service),
server_address_(server_address),
callback_(callback),
has_pending_proxy_resolution_(false) {}
ProxyDetector::~ProxyDetector() {
CHECK(!has_pending_proxy_resolution_);
}
void ProxyDetector::StartResolveProxy() {
std::string url =
base::StringPrintf("https://%s", server_address_.ToString().c_str());
GURL gurl(url);
has_pending_proxy_resolution_ = true;
DCHECK(proxy_service_);
int rv = proxy_service_->ResolveProxy(
gurl,
&proxy_info_,
base::Bind(&ProxyDetector::OnResolveProxyComplete,
base::Unretained(this)),
NULL,
net::BoundNetLog());
if (rv != net::ERR_IO_PENDING)
OnResolveProxyComplete(rv);
}
void ProxyDetector::OnResolveProxyComplete(int result) {
has_pending_proxy_resolution_ = false;
bool has_proxy_server =
(result == net::OK && proxy_info_.proxy_server().is_valid() &&
!proxy_info_.proxy_server().is_direct());
OnResolvedCallback callback = callback_;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE, base::Bind(callback, has_proxy_server));
delete this;
}
void CollectNetworkStats(const std::string& network_stats_server,
IOThread* io_thread) {
if (network_stats_server.empty())
return;
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&CollectNetworkStats, network_stats_server, io_thread));
return;
}
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (net::NetworkChangeNotifier::IsOffline()) {
return;
}
CR_DEFINE_STATIC_LOCAL(scoped_refptr<base::FieldTrial>, trial, ());
static bool collect_stats = false;
if (!trial.get()) {
const base::FieldTrial::Probability kDivisor = 1000;
base::FieldTrial::Probability probability_per_group = kDivisor / 200;
chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
if (channel == chrome::VersionInfo::CHANNEL_CANARY) {
probability_per_group = kDivisor / 2;
} else if (channel == chrome::VersionInfo::CHANNEL_DEV) {
probability_per_group = kDivisor / 10;
} else if (channel == chrome::VersionInfo::CHANNEL_BETA) {
probability_per_group = kDivisor / 100;
}
trial = base::FieldTrialList::FactoryGetFieldTrial(
"NetworkConnectivity", kDivisor, "disable_network_stats",
2014, 7, 31, base::FieldTrial::SESSION_RANDOMIZED, NULL);
int collect_stats_group =
trial->AppendGroup("collect_stats", probability_per_group);
if (trial->group() == collect_stats_group)
collect_stats = true;
}
if (!collect_stats)
return;
const size_t kMaxNumberOfTests = INT_MAX;
static size_t number_of_tests_done = 0;
if (number_of_tests_done > kMaxNumberOfTests)
return;
++number_of_tests_done;
net::HostResolver* host_resolver = io_thread->globals()->host_resolver.get();
DCHECK(host_resolver);
uint32 port_index = base::RandInt(0, arraysize(kPorts) - 1);
uint16 histogram_port = kPorts[port_index];
net::HostPortPair server_address(network_stats_server, histogram_port);
net::ProxyService* proxy_service =
io_thread->globals()->system_proxy_service.get();
DCHECK(proxy_service);
ProxyDetector::OnResolvedCallback callback = base::Bind(
&StartNetworkStatsTest, host_resolver, server_address, histogram_port);
ProxyDetector* proxy_client =
new ProxyDetector(proxy_service, server_address, callback);
proxy_client->StartResolveProxy();
}
void StartNetworkStatsTest(net::HostResolver* host_resolver,
const net::HostPortPair& server_address,
uint16 histogram_port,
bool has_proxy_server) {
int probe_choice = base::RandInt(0, kPacketSizeChoices - 1);
DCHECK_LE(ProbeMessage::kMaxProbePacketBytes, kMaxMessageSize);
uint32 bytes_for_packet_size_test =
base::RandInt(kProbePacketBytes[kPacketSizeChoices - 1],
ProbeMessage::kMaxProbePacketBytes);
NetworkStats* udp_stats_client =
new NetworkStats(net::ClientSocketFactory::GetDefaultFactory());
udp_stats_client->Start(host_resolver,
server_address,
histogram_port,
has_proxy_server,
kProbePacketBytes[probe_choice],
bytes_for_packet_size_test,
net::CompletionCallback());
}
}