With IPv6 enabled, it's important to know whether IPv6 is really used or not. BestConnection is tracked for this purpose. Also added a test case to verify the end to end behavior.
BUG=411086 R=pthatcher@webrtc.org Review URL: https://webrtc-codereview.appspot.com/30919005 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7814 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
369746bcb8
commit
7169afd9d5
@ -682,6 +682,11 @@ bool PeerConnection::AddIceCandidate(
|
||||
|
||||
void PeerConnection::RegisterUMAObserver(UMAObserver* observer) {
|
||||
uma_observer_ = observer;
|
||||
|
||||
if (session_) {
|
||||
session_->set_metrics_observer(uma_observer_);
|
||||
}
|
||||
|
||||
// Send information about IPv4/IPv6 status.
|
||||
if (uma_observer_ && port_allocator_) {
|
||||
if (port_allocator_->flags() & cricket::PORTALLOCATOR_ENABLE_IPV6) {
|
||||
|
@ -130,16 +130,18 @@ class StatsObserver : public rtc::RefCountInterface {
|
||||
virtual ~StatsObserver() {}
|
||||
};
|
||||
|
||||
class UMAObserver : public rtc::RefCountInterface {
|
||||
class MetricsObserverInterface : public rtc::RefCountInterface {
|
||||
public:
|
||||
virtual void IncrementCounter(PeerConnectionUMAMetricsCounter type) = 0;
|
||||
virtual void AddHistogramSample(PeerConnectionUMAMetricsName type,
|
||||
virtual void IncrementCounter(PeerConnectionMetricsCounter type) = 0;
|
||||
virtual void AddHistogramSample(PeerConnectionMetricsName type,
|
||||
int value) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~UMAObserver() {}
|
||||
virtual ~MetricsObserverInterface() {}
|
||||
};
|
||||
|
||||
typedef MetricsObserverInterface UMAObserver;
|
||||
|
||||
class PeerConnectionInterface : public rtc::RefCountInterface {
|
||||
public:
|
||||
// See http://dev.w3.org/2011/webrtc/editor/webrtc.html#state-definitions .
|
||||
|
29
talk/app/webrtc/umametrics.h
Executable file → Normal file
29
talk/app/webrtc/umametrics.h
Executable file → Normal file
@ -35,25 +35,42 @@ namespace webrtc {
|
||||
// Currently this contains information related to WebRTC network/transport
|
||||
// information.
|
||||
|
||||
// The difference between PeerConnectionMetricsCounter and
|
||||
// PeerConnectionMetricsName is that the "Counter" is only counting the
|
||||
// occurrences of events, while "Name" has a value associated with it which is
|
||||
// used to form a histogram.
|
||||
|
||||
// This enum is backed by Chromium's histograms.xml,
|
||||
// chromium/src/tools/metrics/histograms/histograms.xml
|
||||
// Existing values cannot be re-ordered and new enums must be added
|
||||
// before kBoundary.
|
||||
enum PeerConnectionUMAMetricsCounter {
|
||||
enum PeerConnectionMetricsCounter {
|
||||
kPeerConnection_IPv4,
|
||||
kPeerConnection_IPv6,
|
||||
kBestConnections_IPv4,
|
||||
kBestConnections_IPv6,
|
||||
kBoundary,
|
||||
kPeerConnectionMetricsCounter_Max,
|
||||
};
|
||||
|
||||
// TODO(guoweis): Keep previous name here until all references are renamed.
|
||||
#define kBoundary kPeerConnectionMetricsCounter_Max
|
||||
|
||||
// TODO(guoweis): Keep previous name here until all references are renamed.
|
||||
typedef PeerConnectionMetricsCounter PeerConnectionUMAMetricsCounter;
|
||||
|
||||
// This enum defines types for UMA samples, which will have a range.
|
||||
enum PeerConnectionUMAMetricsName {
|
||||
kNetworkInterfaces_IPv4, // Number of IPv4 interfaces.
|
||||
kNetworkInterfaces_IPv6, // Number of IPv6 interfaces.
|
||||
kTimeToConnect, // In milliseconds.
|
||||
enum PeerConnectionMetricsName {
|
||||
kNetworkInterfaces_IPv4, // Number of IPv4 interfaces.
|
||||
kNetworkInterfaces_IPv6, // Number of IPv6 interfaces.
|
||||
kTimeToConnect, // In milliseconds.
|
||||
kLocalCandidates_IPv4, // Number of IPv4 local candidates.
|
||||
kLocalCandidates_IPv6, // Number of IPv6 local candidates.
|
||||
kPeerConnectionMetricsName_Max
|
||||
};
|
||||
|
||||
// TODO(guoweis): Keep previous name here until all references are renamed.
|
||||
typedef PeerConnectionMetricsName PeerConnectionUMAMetricsName;
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // TALK_APP_WEBRTC_UMA6METRICS_H_
|
||||
|
@ -461,16 +461,17 @@ class IceRestartAnswerLatch {
|
||||
bool ice_restart_;
|
||||
};
|
||||
|
||||
WebRtcSession::WebRtcSession(
|
||||
cricket::ChannelManager* channel_manager,
|
||||
rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
MediaStreamSignaling* mediastream_signaling)
|
||||
: cricket::BaseSession(signaling_thread, worker_thread, port_allocator,
|
||||
rtc::ToString(rtc::CreateRandomId64() &
|
||||
LLONG_MAX),
|
||||
cricket::NS_JINGLE_RTP, false),
|
||||
WebRtcSession::WebRtcSession(cricket::ChannelManager* channel_manager,
|
||||
rtc::Thread* signaling_thread,
|
||||
rtc::Thread* worker_thread,
|
||||
cricket::PortAllocator* port_allocator,
|
||||
MediaStreamSignaling* mediastream_signaling)
|
||||
: cricket::BaseSession(signaling_thread,
|
||||
worker_thread,
|
||||
port_allocator,
|
||||
rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX),
|
||||
cricket::NS_JINGLE_RTP,
|
||||
false),
|
||||
// RFC 3264: The numeric value of the session id and version in the
|
||||
// o line MUST be representable with a "64 bit signed integer".
|
||||
// Due to this constraint session id |sid_| is max limited to LLONG_MAX.
|
||||
@ -481,7 +482,8 @@ WebRtcSession::WebRtcSession(
|
||||
older_version_remote_peer_(false),
|
||||
dtls_enabled_(false),
|
||||
data_channel_type_(cricket::DCT_NONE),
|
||||
ice_restart_latch_(new IceRestartAnswerLatch) {
|
||||
ice_restart_latch_(new IceRestartAnswerLatch),
|
||||
metrics_observer_(NULL) {
|
||||
}
|
||||
|
||||
WebRtcSession::~WebRtcSession() {
|
||||
@ -1299,7 +1301,12 @@ void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
|
||||
|
||||
void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) {
|
||||
ASSERT(signaling_thread()->IsCurrent());
|
||||
PeerConnectionInterface::IceConnectionState old_state = ice_connection_state_;
|
||||
SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
|
||||
// Only report once when Ice connection is completed.
|
||||
if (old_state != PeerConnectionInterface::kIceConnectionCompleted) {
|
||||
ReportBestConnectionState(transport);
|
||||
}
|
||||
}
|
||||
|
||||
void WebRtcSession::OnTransportFailed(cricket::Transport* transport) {
|
||||
@ -1740,4 +1747,38 @@ bool WebRtcSession::ReadyToUseRemoteCandidate(
|
||||
transport_proxy->remote_description_set();
|
||||
}
|
||||
|
||||
// Walk through the ConnectionInfos to gather best connection usage
|
||||
// for IPv4 and IPv6.
|
||||
void WebRtcSession::ReportBestConnectionState(cricket::Transport* transport) {
|
||||
if (!metrics_observer_) {
|
||||
return;
|
||||
}
|
||||
|
||||
cricket::TransportStats stats;
|
||||
if (!transport->GetStats(&stats)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (cricket::TransportChannelStatsList::const_iterator it =
|
||||
stats.channel_stats.begin();
|
||||
it != stats.channel_stats.end(); ++it) {
|
||||
for (cricket::ConnectionInfos::const_iterator it_info =
|
||||
it->connection_infos.begin();
|
||||
it_info != it->connection_infos.end(); ++it_info) {
|
||||
if (!it_info->best_connection) {
|
||||
continue;
|
||||
}
|
||||
if (it_info->local_candidate.address().family() == AF_INET) {
|
||||
metrics_observer_->IncrementCounter(kBestConnections_IPv4);
|
||||
} else if (it_info->local_candidate.address().family() ==
|
||||
AF_INET6) {
|
||||
metrics_observer_->IncrementCounter(kBestConnections_IPv6);
|
||||
} else {
|
||||
ASSERT(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -225,6 +225,11 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
// For unit test.
|
||||
bool waiting_for_identity() const;
|
||||
|
||||
void set_metrics_observer(
|
||||
webrtc::MetricsObserverInterface* metrics_observer) {
|
||||
metrics_observer_ = metrics_observer;
|
||||
}
|
||||
|
||||
private:
|
||||
// Indicates the type of SessionDescription in a call to SetLocalDescription
|
||||
// and SetRemoteDescription.
|
||||
@ -323,6 +328,10 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
|
||||
std::string GetSessionErrorMsg();
|
||||
|
||||
// Invoked when OnTransportCompleted is signaled to gather the usage
|
||||
// of IPv4/IPv6 as best connection.
|
||||
void ReportBestConnectionState(cricket::Transport* transport);
|
||||
|
||||
rtc::scoped_ptr<cricket::VoiceChannel> voice_channel_;
|
||||
rtc::scoped_ptr<cricket::VideoChannel> video_channel_;
|
||||
rtc::scoped_ptr<cricket::DataChannel> data_channel_;
|
||||
@ -357,6 +366,7 @@ class WebRtcSession : public cricket::BaseSession,
|
||||
// Member variables for caching global options.
|
||||
cricket::AudioOptions audio_options_;
|
||||
cricket::VideoOptions video_options_;
|
||||
MetricsObserverInterface* metrics_observer_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WebRtcSession);
|
||||
};
|
||||
|
@ -104,6 +104,8 @@ typedef PeerConnectionInterface::RTCOfferAnswerOptions RTCOfferAnswerOptions;
|
||||
|
||||
static const int kClientAddrPort = 0;
|
||||
static const char kClientAddrHost1[] = "11.11.11.11";
|
||||
static const char kClientIPv6AddrHost1[] =
|
||||
"2620:0:aaaa:bbbb:cccc:dddd:eeee:ffff";
|
||||
static const char kClientAddrHost2[] = "22.22.22.22";
|
||||
static const char kStunAddrHost[] = "99.99.99.1";
|
||||
static const SocketAddress kTurnUdpIntAddr("99.99.99.4", 3478);
|
||||
@ -142,6 +144,34 @@ static void InjectAfter(const std::string& line,
|
||||
tmp.c_str(), tmp.length(), message);
|
||||
}
|
||||
|
||||
class FakeMetricsObserver : public webrtc::MetricsObserverInterface {
|
||||
public:
|
||||
FakeMetricsObserver() { Reset(); }
|
||||
void Reset() {
|
||||
memset(peer_connection_metrics_counters_, 0,
|
||||
sizeof(peer_connection_metrics_counters_));
|
||||
memset(peer_connection_metrics_name_, 0,
|
||||
sizeof(peer_connection_metrics_name_));
|
||||
}
|
||||
|
||||
virtual void IncrementCounter(
|
||||
webrtc::PeerConnectionMetricsCounter type) OVERRIDE {
|
||||
peer_connection_metrics_counters_[type]++;
|
||||
}
|
||||
virtual void AddHistogramSample(webrtc::PeerConnectionMetricsName type,
|
||||
int value) OVERRIDE {
|
||||
ASSERT(peer_connection_metrics_name_[type] == 0);
|
||||
peer_connection_metrics_name_[type] = value;
|
||||
}
|
||||
|
||||
int peer_connection_metrics_counters_
|
||||
[webrtc::kPeerConnectionMetricsCounter_Max];
|
||||
int peer_connection_metrics_name_[webrtc::kPeerConnectionMetricsCounter_Max];
|
||||
|
||||
virtual int AddRef() OVERRIDE { return 1; }
|
||||
virtual int Release() OVERRIDE { return 1; }
|
||||
};
|
||||
|
||||
class MockIceObserver : public webrtc::IceObserver {
|
||||
public:
|
||||
MockIceObserver()
|
||||
@ -353,6 +383,7 @@ class WebRtcSessionTest : public testing::Test {
|
||||
|
||||
EXPECT_TRUE(session_->Initialize(options_, constraints_.get(),
|
||||
identity_service, ice_type_));
|
||||
session_->set_metrics_observer(&metrics_observer_);
|
||||
}
|
||||
|
||||
void InitWithDtmfCodec() {
|
||||
@ -919,6 +950,90 @@ class WebRtcSessionTest : public testing::Test {
|
||||
EXPECT_EQ(can, session_->CanInsertDtmf(kAudioTrack1));
|
||||
}
|
||||
|
||||
// Helper class to configure loopback network and verify Best
|
||||
// Connection using right IP protocol for TestLoopbackCall
|
||||
// method. LoopbackNetworkManager applies firewall rules to block
|
||||
// all ping traffic once ICE completed, and remove them to observe
|
||||
// ICE reconnected again. This LoopbackNetworkConfiguration struct
|
||||
// verifies the best connection is using the right IP protocol after
|
||||
// initial ICE convergences.
|
||||
|
||||
class LoopbackNetworkConfiguration {
|
||||
public:
|
||||
LoopbackNetworkConfiguration()
|
||||
: test_ipv6_network_(false),
|
||||
test_extra_ipv4_network_(false),
|
||||
best_connection_after_initial_ice_converged_(1, 0) {}
|
||||
|
||||
// Used to track the expected best connection count in each IP protocol.
|
||||
struct ExpectedBestConnection {
|
||||
ExpectedBestConnection(int ipv4_count, int ipv6_count)
|
||||
: ipv4_count_(ipv4_count),
|
||||
ipv6_count_(ipv6_count) {}
|
||||
|
||||
int ipv4_count_;
|
||||
int ipv6_count_;
|
||||
};
|
||||
|
||||
bool test_ipv6_network_;
|
||||
bool test_extra_ipv4_network_;
|
||||
ExpectedBestConnection best_connection_after_initial_ice_converged_;
|
||||
|
||||
void VerifyBestConnectionAfterIceConverge(
|
||||
const FakeMetricsObserver& metrics_observer) const {
|
||||
Verify(metrics_observer, best_connection_after_initial_ice_converged_);
|
||||
}
|
||||
|
||||
private:
|
||||
void Verify(const FakeMetricsObserver& metrics_observer,
|
||||
const ExpectedBestConnection& expected) const {
|
||||
EXPECT_EQ(
|
||||
metrics_observer
|
||||
.peer_connection_metrics_counters_[webrtc::kBestConnections_IPv4],
|
||||
expected.ipv4_count_);
|
||||
EXPECT_EQ(
|
||||
metrics_observer
|
||||
.peer_connection_metrics_counters_[webrtc::kBestConnections_IPv6],
|
||||
expected.ipv6_count_);
|
||||
}
|
||||
};
|
||||
|
||||
class LoopbackNetworkManager {
|
||||
public:
|
||||
LoopbackNetworkManager(WebRtcSessionTest* session,
|
||||
const LoopbackNetworkConfiguration& config)
|
||||
: config_(config) {
|
||||
session->AddInterface(
|
||||
rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
if (config_.test_extra_ipv4_network_) {
|
||||
session->AddInterface(
|
||||
rtc::SocketAddress(kClientAddrHost2, kClientAddrPort));
|
||||
}
|
||||
if (config_.test_ipv6_network_) {
|
||||
session->AddInterface(
|
||||
rtc::SocketAddress(kClientIPv6AddrHost1, kClientAddrPort));
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyFirewallRules(rtc::FirewallSocketServer* fss) {
|
||||
fss->AddRule(false, rtc::FP_ANY, rtc::FD_ANY,
|
||||
rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
if (config_.test_extra_ipv4_network_) {
|
||||
fss->AddRule(false, rtc::FP_ANY, rtc::FD_ANY,
|
||||
rtc::SocketAddress(kClientAddrHost2, kClientAddrPort));
|
||||
}
|
||||
if (config_.test_ipv6_network_) {
|
||||
fss->AddRule(false, rtc::FP_ANY, rtc::FD_ANY,
|
||||
rtc::SocketAddress(kClientIPv6AddrHost1, kClientAddrPort));
|
||||
}
|
||||
}
|
||||
|
||||
void ClearRules(rtc::FirewallSocketServer* fss) { fss->ClearRules(); }
|
||||
|
||||
private:
|
||||
LoopbackNetworkConfiguration config_;
|
||||
};
|
||||
|
||||
// The method sets up a call from the session to itself, in a loopback
|
||||
// arrangement. It also uses a firewall rule to create a temporary
|
||||
// disconnection, and then a permanent disconnection.
|
||||
@ -931,8 +1046,9 @@ class WebRtcSessionTest : public testing::Test {
|
||||
// New -> Checking -> (Connected) -> Completed -> Disconnected -> Completed
|
||||
// -> Failed.
|
||||
// The Gathering state should go: New -> Gathering -> Completed.
|
||||
void TestLoopbackCall() {
|
||||
AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
|
||||
void TestLoopbackCall(const LoopbackNetworkConfiguration& config) {
|
||||
LoopbackNetworkManager loopback_network_manager(this, config);
|
||||
Init(NULL);
|
||||
mediastream_signaling_.SendAudioVideoStream1();
|
||||
SessionDescriptionInterface* offer = CreateOffer();
|
||||
@ -966,21 +1082,26 @@ class WebRtcSessionTest : public testing::Test {
|
||||
observer_.ice_connection_state_,
|
||||
kIceCandidatesTimeout);
|
||||
|
||||
config.VerifyBestConnectionAfterIceConverge(metrics_observer_);
|
||||
// Adding firewall rule to block ping requests, which should cause
|
||||
// transport channel failure.
|
||||
fss_->AddRule(false,
|
||||
rtc::FP_ANY,
|
||||
rtc::FD_ANY,
|
||||
rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
|
||||
loopback_network_manager.ApplyFirewallRules(fss_.get());
|
||||
|
||||
LOG(LS_INFO) << "Firewall Rules applied";
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionDisconnected,
|
||||
observer_.ice_connection_state_,
|
||||
kIceCandidatesTimeout);
|
||||
|
||||
metrics_observer_.Reset();
|
||||
|
||||
// Clearing the rules, session should move back to completed state.
|
||||
fss_->ClearRules();
|
||||
loopback_network_manager.ClearRules(fss_.get());
|
||||
// Session is automatically calling OnSignalingReady after creation of
|
||||
// new portallocator session which will allocate new set of candidates.
|
||||
|
||||
LOG(LS_INFO) << "Firewall Rules cleared";
|
||||
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
|
||||
observer_.ice_connection_state_,
|
||||
kIceCandidatesTimeout);
|
||||
@ -989,15 +1110,19 @@ class WebRtcSessionTest : public testing::Test {
|
||||
// to the Failed state. This will take at least 30 seconds because it must
|
||||
// wait for the Port to timeout.
|
||||
int port_timeout = 30000;
|
||||
fss_->AddRule(false,
|
||||
rtc::FP_ANY,
|
||||
rtc::FD_ANY,
|
||||
rtc::SocketAddress(kClientAddrHost1, kClientAddrPort));
|
||||
|
||||
loopback_network_manager.ApplyFirewallRules(fss_.get());
|
||||
LOG(LS_INFO) << "Firewall Rules applied again";
|
||||
EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
|
||||
observer_.ice_connection_state_,
|
||||
kIceCandidatesTimeout + port_timeout);
|
||||
}
|
||||
|
||||
void TestLoopbackCall() {
|
||||
LoopbackNetworkConfiguration config;
|
||||
TestLoopbackCall(config);
|
||||
}
|
||||
|
||||
void VerifyTransportType(const std::string& content_name,
|
||||
cricket::TransportProtocol protocol) {
|
||||
const cricket::Transport* transport = session_->GetTransport(content_name);
|
||||
@ -1114,6 +1239,7 @@ class WebRtcSessionTest : public testing::Test {
|
||||
cricket::FakeVideoMediaChannel* video_channel_;
|
||||
cricket::FakeVoiceMediaChannel* voice_channel_;
|
||||
PeerConnectionInterface::IceTransportsType ice_type_;
|
||||
FakeMetricsObserver metrics_observer_;
|
||||
};
|
||||
|
||||
TEST_F(WebRtcSessionTest, TestInitializeWithDtls) {
|
||||
@ -3024,12 +3150,28 @@ TEST_F(WebRtcSessionTest, TestSessionContentError) {
|
||||
TEST_F(WebRtcSessionTest, TestIceStatesBasic) {
|
||||
// Lets try with only UDP ports.
|
||||
allocator_->set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
|
||||
cricket::PORTALLOCATOR_DISABLE_TCP |
|
||||
cricket::PORTALLOCATOR_DISABLE_STUN |
|
||||
cricket::PORTALLOCATOR_DISABLE_RELAY);
|
||||
cricket::PORTALLOCATOR_DISABLE_TCP |
|
||||
cricket::PORTALLOCATOR_DISABLE_STUN |
|
||||
cricket::PORTALLOCATOR_DISABLE_RELAY);
|
||||
TestLoopbackCall();
|
||||
}
|
||||
|
||||
TEST_F(WebRtcSessionTest, TestIceStatesBasicIPv6) {
|
||||
allocator_->set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
|
||||
cricket::PORTALLOCATOR_DISABLE_TCP |
|
||||
cricket::PORTALLOCATOR_DISABLE_STUN |
|
||||
cricket::PORTALLOCATOR_ENABLE_IPV6 |
|
||||
cricket::PORTALLOCATOR_DISABLE_RELAY);
|
||||
|
||||
// best connection is IPv6 since it has higher network preference.
|
||||
LoopbackNetworkConfiguration config;
|
||||
config.test_ipv6_network_ = true;
|
||||
config.best_connection_after_initial_ice_converged_ =
|
||||
LoopbackNetworkConfiguration::ExpectedBestConnection(0, 1);
|
||||
|
||||
TestLoopbackCall(config);
|
||||
}
|
||||
|
||||
// Runs the loopback call test with BUNDLE and STUN enabled.
|
||||
TEST_F(WebRtcSessionTest, TestIceStatesBundle) {
|
||||
allocator_->set_flags(cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
|
||||
|
Loading…
x
Reference in New Issue
Block a user