Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1319)

Side by Side Diff: content/renderer/media/webrtc/stun_field_trial.cc

Issue 1417663004: Create an experiment to study whether too many bindings cause NAT failure (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix windows build. Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/renderer/media/webrtc/stun_field_trial.h" 5 #include "content/renderer/media/webrtc/stun_field_trial.h"
6 6
7 #include <math.h> 7 #include <math.h>
8 8
9 #include "base/bind.h"
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "base/macros.h" 11 #include "base/macros.h"
12 #include "base/message_loop/message_loop.h"
11 #include "base/metrics/histogram.h" 13 #include "base/metrics/histogram.h"
12 #include "base/rand_util.h" 14 #include "base/rand_util.h"
13 #include "base/strings/string_number_conversions.h" 15 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h" 16 #include "base/strings/string_split.h"
15 #include "base/strings/stringprintf.h" 17 #include "base/strings/stringprintf.h"
16 #include "base/time/time.h" 18 #include "base/time/time.h"
17 #include "third_party/webrtc/base/asyncpacketsocket.h" 19 #include "third_party/webrtc/base/asyncpacketsocket.h"
18 #include "third_party/webrtc/base/asyncresolverinterface.h" 20 #include "third_party/webrtc/base/asyncresolverinterface.h"
19 #include "third_party/webrtc/base/ipaddress.h" 21 #include "third_party/webrtc/base/ipaddress.h"
20 #include "third_party/webrtc/base/network.h" 22 #include "third_party/webrtc/base/network.h"
21 #include "third_party/webrtc/base/socketaddress.h" 23 #include "third_party/webrtc/base/socketaddress.h"
22 #include "third_party/webrtc/base/thread.h" 24 #include "third_party/webrtc/base/thread.h"
23 #include "third_party/webrtc/p2p/base/packetsocketfactory.h" 25 #include "third_party/webrtc/p2p/base/packetsocketfactory.h"
24 #include "third_party/webrtc/p2p/stunprober/stunprober.h"
25 26
26 using stunprober::StunProber; 27 using stunprober::StunProber;
27 28
28 namespace content { 29 namespace content {
29 30
30 namespace { 31 namespace {
31 32
32 // This needs to be the same as NatTypeCounters in histograms.xml. 33 // This needs to be the same as NatTypeCounters in histograms.xml.
33 enum NatType { 34 enum NatType {
34 NAT_TYPE_NONE, 35 NAT_TYPE_NONE,
(...skipping 23 matching lines...) Expand all
58 } 59 }
59 60
60 // Below 50ms, we are doing experiments at each 5ms interval. Beyond 50ms, only 61 // Below 50ms, we are doing experiments at each 5ms interval. Beyond 50ms, only
61 // one experiment of 100ms. 62 // one experiment of 100ms.
62 int ClampProbingInterval(int interval_ms) { 63 int ClampProbingInterval(int interval_ms) {
63 return interval_ms > 50 ? 100 : interval_ms; 64 return interval_ms > 50 ? 100 : interval_ms;
64 } 65 }
65 66
66 std::string HistogramName(const std::string& prefix, 67 std::string HistogramName(const std::string& prefix,
67 NatType nat_type, 68 NatType nat_type,
68 int interval_ms) { 69 int interval_ms,
69 return base::StringPrintf("WebRTC.Stun.%s.%s.%dms", prefix.c_str(), 70 int batch_index,
70 NatTypeNames[nat_type], interval_ms); 71 int total_batches) {
71 } 72 return base::StringPrintf("WebRTC.Stun.%s.%s.%dms.%d.%d", prefix.c_str(),
72 73 NatTypeNames[nat_type], interval_ms, batch_index,
73 void SaveHistogramData(StunProber* prober) { 74 total_batches);
74 StunProber::Stats stats;
75 if (!prober->GetStats(&stats))
76 return;
77
78 NatType nat_type = GetNatType(stats.nat_type);
79
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;
84
85 interval_ms = ClampProbingInterval(interval_ms);
86
87 UMA_HISTOGRAM_ENUMERATION("WebRTC.NAT.Metrics", nat_type, NAT_TYPE_MAX);
88
89 std::string histogram_name =
90 HistogramName("SuccessPercent", nat_type, interval_ms);
91
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);
97
98 DVLOG(1) << "Histogram '" << histogram_name.c_str()
99 << "' = " << stats.success_percent;
100
101 histogram_name = HistogramName("ResponseLatency", nat_type, interval_ms);
102
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));
108
109 DVLOG(1) << "Histogram '" << histogram_name.c_str()
110 << "' = " << stats.average_rtt_ms << " ms";
111
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;
122 }
123
124 void OnStunProbeTrialFinished(StunProber* prober, int result) {
125 if (result == StunProber::SUCCESS)
126 SaveHistogramData(prober);
127 } 75 }
128 76
129 } // namespace 77 } // namespace
130 78
131 bool ParseStunProbeParameters(const std::string& params, 79 StunProberWithWeakPtr::StunProberWithWeakPtr(StunProber* prober)
132 int* requests_per_ip, 80 : prober_(prober), weak_factory_(this) {}
133 int* interval_ms, 81
134 int* shared_socket_mode, 82 void StunProberWithWeakPtr::set_next_prober(
135 std::vector<rtc::SocketAddress>* servers) { 83 StunProberWithWeakPtr* next_prober) {
84 DCHECK(!next_prober_);
85 next_prober_ = next_prober;
86 }
87
88 void StunProberWithWeakPtr::Start(StunProber::Observer* observer) {
89 if (next_prober_) {
90 base::MessageLoop::current()->PostDelayedTask(
91 FROM_HERE, base::Bind(&StunProberWithWeakPtr::Start,
92 next_prober_->GetWeakPtr(), observer),
93 base::TimeDelta::FromMilliseconds(prober_->estimated_execution_time()));
94 }
95 prober_->Start(observer);
96 }
97
98 StunProberWithWeakPtr::~StunProberWithWeakPtr() {
99 if (next_prober_) {
100 base::MessageLoop::current()->DeleteSoon(FROM_HERE, next_prober_);
101 }
102 }
103
104 StunProberTrial::StunProberTrial(rtc::NetworkManager* network_manager,
105 const std::string& params,
106 rtc::PacketSocketFactory* factory)
107 : network_manager_(network_manager), params_(params), factory_(factory) {
108 // We have to connect to the signal to avoid a race condition if network
109 // manager hasn't received the network update when we start, the StunProber
110 // will just fail.
111 network_manager_->SignalNetworksChanged.connect(
112 this, &StunProberTrial::OnNetworksChanged);
113 network_manager_->StartUpdating();
114 }
115
116 StunProberTrial::~StunProberTrial() {
117 DCHECK(thread_checker_.CalledOnValidThread());
118 }
119
120 void StunProberTrial::SaveHistogramData() {
121 DCHECK(thread_checker_.CalledOnValidThread());
122 NatType nat_type = NAT_TYPE_MAX;
123 int interval_ms = 0;
124 int count = 0;
125 int total_sent = 0;
126 int total_recv = 0;
127 for (StunProberWithWeakPtr* prober = prober_head_.get(); prober != nullptr;
128 prober = prober->GetNextProber()) {
129 ++count;
130
131 // Get the stats.
132 StunProber::Stats stats;
133 if (!prober->GetStats(&stats))
134 return;
135
136 // Check if the NAT type is consistent.
137 if (nat_type == NAT_TYPE_MAX) {
138 nat_type = GetNatType(stats.nat_type);
139 // If we can't figure out the nattype at the beginning, just return.
140 if (nat_type == NAT_TYPE_UNKNOWN)
141 return;
142 }
143 // For subsequent probers, we might get unknown as nattype if all the
144 // bindings fail, but it's ok.
145 else if (nat_type != GetNatType(stats.nat_type) &&
146 nat_type != NAT_TYPE_UNKNOWN)
147 return;
148
149 // Check the interval is consistent.
pthatcher2 2015/10/29 14:13:13 Check the => Check that the
guoweis_webrtc 2015/10/29 21:45:29 Done.
150 // Use the real probe interval for reporting, converting from nanosecond to
151 // millisecond at 5ms boundary.
152 int new_interval_ms = ClampProbingInterval(
153 round(static_cast<float>(stats.actual_request_interval_ns) / 5000) * 5);
154 if (interval_ms == 0) {
155 interval_ms = new_interval_ms;
156 } else if (interval_ms != new_interval_ms)
157 return;
158
159 // Sum up the total sent and recv packets.
160 total_sent += stats.num_request_sent;
161 total_recv += stats.num_response_received;
162
163 // At the batch boundary, reporting it.
164 if (count % batch_size_ == 0) {
165 if (total_sent != 0) {
pthatcher2 2015/10/29 14:13:13 Can you use an early return? if (count % batch_si
guoweis_webrtc 2015/10/29 21:45:30 Done.
166 int success_rate = total_recv * 100 / total_sent;
167 std::string histogram_name =
168 HistogramName("SuccessPercent", nat_type, interval_ms,
169 count / batch_size_, total_probers_ / batch_size_);
170
171 // Mimic the same behavior as UMA_HISTOGRAM_PERCENTAGE. We can't use
172 // that macro as the histogram name is determined dynamically.
173 base::HistogramBase* histogram = base::Histogram::FactoryGet(
174 histogram_name, 1, 101, 102,
175 base::Histogram::kUmaTargetedHistogramFlag);
176 histogram->Add(success_rate);
177
178 DVLOG(1) << "Histogram '" << histogram_name.c_str()
179 << "' = " << stats.success_percent;
180
181 DVLOG(1) << "Shared Socket Mode: " << stats.shared_socket_mode;
182 DVLOG(1) << "Requests sent: " << total_sent;
183 DVLOG(1) << "Responses received: " << total_recv;
184 DVLOG(1) << "Target interval (ns): "
185 << stats.target_request_interval_ns;
186 DVLOG(1) << "Actual interval (ns): "
187 << stats.actual_request_interval_ns;
188 DVLOG(1) << "NAT Type: " << NatTypeNames[nat_type];
189 DVLOG(1) << "Host IP: " << stats.host_ip;
190 }
191 total_sent = 0;
192 total_recv = 0;
193 }
194 }
195 }
196
197 bool StunProberTrial::ParseParameters(
198 const std::string& params,
199 int* requests_per_ip,
200 int* interval_ms,
201 int* shared_socket_mode,
202 int* batch_size,
203 int* total_batches,
204 std::vector<rtc::SocketAddress>* servers) {
136 std::vector<std::string> stun_params = base::SplitString( 205 std::vector<std::string> stun_params = base::SplitString(
137 params, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); 206 params, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
138 207
139 if (stun_params.size() < 4) { 208 if (stun_params.size() < 5) {
140 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial"; 209 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial";
141 return false; 210 return false;
142 } 211 }
143 auto param = stun_params.begin(); 212 auto param = stun_params.begin();
144 213
145 if (param->empty()) { 214 if (param->empty()) {
146 *requests_per_ip = 10; 215 *requests_per_ip = 10;
147 } else if (!base::StringToInt(*param, requests_per_ip)) { 216 } else if (!base::StringToInt(*param, requests_per_ip)) {
148 DLOG(ERROR) << "Failed to parse request_per_ip in StartStunProbeTrial"; 217 DLOG(ERROR) << "Failed to parse request_per_ip in StartStunProbeTrial";
149 return false; 218 return false;
(...skipping 11 matching lines...) Expand all
161 param++; 230 param++;
162 231
163 if ((*param).empty()) { 232 if ((*param).empty()) {
164 *shared_socket_mode = base::RandInt(0, 1); 233 *shared_socket_mode = base::RandInt(0, 1);
165 } else if (!base::StringToInt(*param, shared_socket_mode)) { 234 } else if (!base::StringToInt(*param, shared_socket_mode)) {
166 DLOG(ERROR) << "Failed to parse shared_socket_mode in StartStunProbeTrial"; 235 DLOG(ERROR) << "Failed to parse shared_socket_mode in StartStunProbeTrial";
167 return false; 236 return false;
168 } 237 }
169 param++; 238 param++;
170 239
240 if (param->empty()) {
241 *batch_size = 5;
242 } else if (!base::StringToInt(*param, batch_size)) {
243 DLOG(ERROR) << "Failed to parse batch_size in StartStunProbeTrial";
244 return false;
245 }
246 param++;
247
248 if (param->empty()) {
249 *total_batches = 5;
250 } else if (!base::StringToInt(*param, total_batches)) {
251 DLOG(ERROR) << "Failed to parse total_batches in StartStunProbeTrial";
252 return false;
253 }
254 param++;
255
171 while (param != stun_params.end() && !param->empty()) { 256 while (param != stun_params.end() && !param->empty()) {
172 rtc::SocketAddress server; 257 rtc::SocketAddress server;
173 if (!server.FromString(*param)) { 258 if (!server.FromString(*param)) {
174 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial"; 259 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial";
175 return false; 260 return false;
176 } 261 }
177 servers->push_back(server); 262 servers->push_back(server);
178 param++; 263 param++;
179 } 264 }
180 265
181 return !servers->empty(); 266 return !servers->empty();
182 } 267 }
183 268
184 scoped_ptr<stunprober::StunProber> StartStunProbeTrial( 269 void StunProberTrial::OnNetworksChanged() {
185 const rtc::NetworkManager::NetworkList& networks, 270 DCHECK(thread_checker_.CalledOnValidThread());
186 const std::string& params, 271 DVLOG(1) << "Starting stun trial with params: " << params_;
187 rtc::PacketSocketFactory* factory) { 272 rtc::NetworkManager::NetworkList networks;
188 DVLOG(1) << "Starting stun trial with params: " << params; 273 network_manager_->GetNetworks(&networks);
189 274
190 // If we don't have local addresses, we won't be able to determine whether 275 // If we don't have local addresses, we won't be able to determine whether
191 // we're behind NAT or not. 276 // we're behind NAT or not.
192 if (networks.empty()) { 277 if (networks.empty()) {
193 DLOG(ERROR) << "No networks specified in StartStunProbeTrial"; 278 DLOG(ERROR) << "No networks specified in StartStunProbeTrial";
194 return nullptr; 279 return;
195 } 280 }
196 281
282 network_manager_->StopUpdating();
283 network_manager_->SignalNetworksChanged.disconnect(this);
284
197 int requests_per_ip; 285 int requests_per_ip;
286 int shared_socket_mode;
198 int interval_ms; 287 int interval_ms;
199 int shared_socket_mode; 288 int total_batches;
200 std::vector<rtc::SocketAddress> servers; 289 std::vector<rtc::SocketAddress> servers;
201 290
202 if (!ParseStunProbeParameters(params, &requests_per_ip, &interval_ms, 291 if (!ParseParameters(params_, &requests_per_ip, &interval_ms,
203 &shared_socket_mode, &servers)) { 292 &shared_socket_mode, &batch_size_, &total_batches,
204 return nullptr; 293 &servers)) {
294 return;
205 } 295 }
206 296
207 scoped_ptr<StunProber> prober( 297 total_probers_ = total_batches * batch_size_;
208 new StunProber(factory, rtc::Thread::Current(), networks));
209 298
210 if (!prober->Start( 299 StunProberWithWeakPtr* prev_prober = nullptr;
211 servers, (shared_socket_mode != 0), interval_ms, requests_per_ip, 300
212 1000, 301 for (int i = 0; i < total_probers_; i++) {
213 rtc::Callback2<void, StunProber*, int>(&OnStunProbeTrialFinished))) { 302 stunprober::StunProber* prober =
214 DLOG(ERROR) << "Failed to Start in StartStunProbeTrial"; 303 new StunProber(factory_, rtc::Thread::Current(), networks);
215 OnStunProbeTrialFinished(prober.get(), StunProber::GENERIC_FAILURE); 304 scoped_ptr<StunProberWithWeakPtr> prober_wp(
216 return nullptr; 305 new StunProberWithWeakPtr(prober));
306 if (!prober->Prepare(servers, (shared_socket_mode != 0), interval_ms,
307 requests_per_ip, 1000, this)) {
308 DLOG(ERROR) << "Failed to Prepare in StartStunProbeTrial";
309 return;
310 }
311
312 if (prev_prober)
313 prev_prober->set_next_prober(prober_wp.get());
314 else
315 prober_head_.reset(prober_wp.get());
316
317 prev_prober = prober_wp.release();
217 } 318 }
218
219 return prober;
220 } 319 }
221 320
321 void StunProberTrial::OnFinished(StunProber* prober,
322 StunProber::Status result) {
323 DCHECK(thread_checker_.CalledOnValidThread());
324 if (result == StunProber::SUCCESS)
325 ++finished_probers_;
326
327 if (finished_probers_ == total_probers_)
328 SaveHistogramData();
329 }
330
331 void StunProberTrial::OnPrepared(StunProber* prober,
332 StunProber::Status result) {
333 DCHECK(thread_checker_.CalledOnValidThread());
334 if (result == StunProber::SUCCESS)
335 ++ready_probers_;
336
337 if (ready_probers_ == total_probers_) {
338 DCHECK(prober_head_);
339 prober_head_->Start(this);
340 }
341 }
222 } // namespace content 342 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698