Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / renderer / media / webrtc / stun_field_trial.cc
blob41be3588a749b09ce5071862cd892effe1095dca
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/renderer/media/webrtc/stun_field_trial.h"
7 #include <math.h>
9 #include "base/logging.h"
10 #include "base/macros.h"
11 #include "base/metrics/histogram.h"
12 #include "base/rand_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/time.h"
17 #include "third_party/webrtc/base/asyncpacketsocket.h"
18 #include "third_party/webrtc/base/asyncresolverinterface.h"
19 #include "third_party/webrtc/base/ipaddress.h"
20 #include "third_party/webrtc/base/network.h"
21 #include "third_party/webrtc/base/socketaddress.h"
22 #include "third_party/webrtc/base/thread.h"
23 #include "third_party/webrtc/p2p/base/packetsocketfactory.h"
24 #include "third_party/webrtc/p2p/stunprober/stunprober.h"
26 using stunprober::StunProber;
28 namespace content {
30 namespace {
32 // This needs to be the same as NatTypeCounters in histograms.xml.
33 enum NatType {
34 NAT_TYPE_NONE,
35 NAT_TYPE_UNKNOWN,
36 NAT_TYPE_SYMMETRIC,
37 NAT_TYPE_NON_SYMMETRIC,
38 NAT_TYPE_MAX
41 // This needs to match "NatType" in histograms.xml.
42 const char* NatTypeNames[] = {"NoNAT", "UnknownNAT", "SymNAT", "NonSymNAT"};
43 COMPILE_ASSERT(arraysize(NatTypeNames) == NAT_TYPE_MAX, NamesArraySizeNotMatch);
45 NatType GetNatType(stunprober::NatType nat_type) {
46 switch (nat_type) {
47 case stunprober::NATTYPE_NONE:
48 return NAT_TYPE_NONE;
49 case stunprober::NATTYPE_UNKNOWN:
50 return NAT_TYPE_UNKNOWN;
51 case stunprober::NATTYPE_SYMMETRIC:
52 return NAT_TYPE_SYMMETRIC;
53 case stunprober::NATTYPE_NON_SYMMETRIC:
54 return NAT_TYPE_NON_SYMMETRIC;
55 default:
56 return NAT_TYPE_MAX;
60 // Below 50ms, we are doing experiments at each 5ms interval. Beyond 50ms, only
61 // one experiment of 100ms.
62 int ClampProbingInterval(int interval_ms) {
63 return interval_ms > 50 ? 100 : interval_ms;
66 std::string HistogramName(const std::string& prefix,
67 NatType nat_type,
68 int interval_ms) {
69 return base::StringPrintf("WebRTC.Stun.%s.%s.%dms", prefix.c_str(),
70 NatTypeNames[nat_type], interval_ms);
73 void SaveHistogramData(StunProber* prober) {
74 StunProber::Stats stats;
75 if (!prober->GetStats(&stats))
76 return;
78 NatType nat_type = GetNatType(stats.nat_type);
80 // Use the real probe interval for reporting, converting from nanosecond to
81 // millisecond at 5ms boundary.
82 int interval_ms =
83 round(static_cast<float>(stats.actual_request_interval_ns) / 5000) * 5;
85 interval_ms = ClampProbingInterval(interval_ms);
87 UMA_HISTOGRAM_ENUMERATION("WebRTC.NAT.Metrics", nat_type, NAT_TYPE_MAX);
89 std::string histogram_name =
90 HistogramName("SuccessPercent", nat_type, interval_ms);
92 // Mimic the same behavior as UMA_HISTOGRAM_PERCENTAGE. We can't use that
93 // macro as the histogram name is determined dynamically.
94 base::HistogramBase* histogram = base::Histogram::FactoryGet(
95 histogram_name, 1, 101, 102, base::Histogram::kUmaTargetedHistogramFlag);
96 histogram->Add(stats.success_percent);
98 DVLOG(1) << "Histogram '" << histogram_name.c_str()
99 << "' = " << stats.success_percent;
101 histogram_name = HistogramName("ResponseLatency", nat_type, interval_ms);
103 histogram = base::Histogram::FactoryTimeGet(
104 histogram_name, base::TimeDelta::FromMilliseconds(1),
105 base::TimeDelta::FromSeconds(10), 50,
106 base::Histogram::kUmaTargetedHistogramFlag);
107 histogram->AddTime(base::TimeDelta::FromMilliseconds(stats.average_rtt_ms));
109 DVLOG(1) << "Histogram '" << histogram_name.c_str()
110 << "' = " << stats.average_rtt_ms << " ms";
112 DVLOG(1) << "Shared Socket Mode: " << stats.shared_socket_mode;
113 DVLOG(1) << "Requests sent: " << stats.num_request_sent;
114 DVLOG(1) << "Responses received: " << stats.num_response_received;
115 DVLOG(1) << "Target interval (ns): " << stats.target_request_interval_ns;
116 DVLOG(1) << "Actual interval (ns): " << stats.actual_request_interval_ns;
117 DVLOG(1) << "NAT Type: " << NatTypeNames[nat_type];
118 DVLOG(1) << "Host IP: " << stats.host_ip;
119 DVLOG(1) << "Server-reflexive ips: ";
120 for (const auto& ip : stats.srflx_addrs)
121 DVLOG(1) << "\t" << ip;
124 void OnStunProbeTrialFinished(StunProber* prober, int result) {
125 if (result == StunProber::SUCCESS)
126 SaveHistogramData(prober);
129 } // namespace
131 bool ParseStunProbeParameters(const std::string& params,
132 int* requests_per_ip,
133 int* interval_ms,
134 int* shared_socket_mode,
135 std::vector<rtc::SocketAddress>* servers) {
136 std::vector<std::string> stun_params = base::SplitString(
137 params, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
139 if (stun_params.size() < 4) {
140 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial";
141 return false;
143 auto param = stun_params.begin();
145 if (param->empty()) {
146 *requests_per_ip = 10;
147 } else if (!base::StringToInt(*param, requests_per_ip)) {
148 DLOG(ERROR) << "Failed to parse request_per_ip in StartStunProbeTrial";
149 return false;
151 param++;
153 // Set inter-probe interval randomly from 0, 5, 10, ... 50, 100 ms.
154 if ((*param).empty()) {
155 *interval_ms = base::RandInt(0, 11) * 5;
156 } else if (!base::StringToInt(*param, interval_ms)) {
157 DLOG(ERROR) << "Failed to parse interval in StartStunProbeTrial";
158 return false;
160 *interval_ms = ClampProbingInterval(*interval_ms);
161 param++;
163 if ((*param).empty()) {
164 *shared_socket_mode = base::RandInt(0, 1);
165 } else if (!base::StringToInt(*param, shared_socket_mode)) {
166 DLOG(ERROR) << "Failed to parse shared_socket_mode in StartStunProbeTrial";
167 return false;
169 param++;
171 while (param != stun_params.end() && !param->empty()) {
172 rtc::SocketAddress server;
173 if (!server.FromString(*param)) {
174 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial";
175 return false;
177 servers->push_back(server);
178 param++;
181 return !servers->empty();
184 scoped_ptr<stunprober::StunProber> StartStunProbeTrial(
185 const rtc::NetworkManager::NetworkList& networks,
186 const std::string& params,
187 rtc::PacketSocketFactory* factory) {
188 DVLOG(1) << "Starting stun trial with params: " << params;
190 // If we don't have local addresses, we won't be able to determine whether
191 // we're behind NAT or not.
192 if (networks.empty()) {
193 DLOG(ERROR) << "No networks specified in StartStunProbeTrial";
194 return nullptr;
197 int requests_per_ip;
198 int interval_ms;
199 int shared_socket_mode;
200 std::vector<rtc::SocketAddress> servers;
202 if (!ParseStunProbeParameters(params, &requests_per_ip, &interval_ms,
203 &shared_socket_mode, &servers)) {
204 return nullptr;
207 scoped_ptr<StunProber> prober(
208 new StunProber(factory, rtc::Thread::Current(), networks));
210 if (!prober->Start(
211 servers, (shared_socket_mode != 0), interval_ms, requests_per_ip,
212 1000,
213 rtc::Callback2<void, StunProber*, int>(&OnStunProbeTrialFinished))) {
214 DLOG(ERROR) << "Failed to Start in StartStunProbeTrial";
215 OnStunProbeTrialFinished(prober.get(), StunProber::GENERIC_FAILURE);
216 return nullptr;
219 return prober;
222 } // namespace content