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

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: address Alexei comments. Still need to change test code. 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 return base::StringPrintf("WebRTC.Stun.%s.%s.%dms.%d", prefix.c_str(),
71 } 72 NatTypeNames[nat_type], interval_ms, batch_index);
72
73 void SaveHistogramData(StunProber* prober) {
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 } 73 }
128 74
129 } // namespace 75 } // namespace
130 76
131 bool ParseStunProbeParameters(const std::string& params, 77 StunProberWithWeakPtr::StunProberWithWeakPtr(StunProber* prober)
132 int* requests_per_ip, 78 : prober_(prober), weak_factory_(this) {}
133 int* interval_ms, 79
134 int* shared_socket_mode, 80 void StunProberWithWeakPtr::SetNextProber(StunProberWithWeakPtr* next_prober) {
135 std::vector<rtc::SocketAddress>* servers) { 81 DCHECK(!next_prober_);
82 next_prober_ = next_prober;
83 }
84
85 void StunProberWithWeakPtr::Start(StunProber::Observer* observer) {
86 if (next_prober_) {
87 base::MessageLoop::current()->PostDelayedTask(
88 FROM_HERE, base::Bind(&StunProberWithWeakPtr::Start,
89 next_prober_->GetWeakPtr(), observer),
90 base::TimeDelta::FromMilliseconds(prober_->estimated_execution_time()));
91 }
92 prober_->Start(observer);
93 }
94
95 bool StunProberWithWeakPtr::GetStats(StunProber::Stats* stats) {
96 return prober_->GetStats(stats);
97 }
98
99 base::WeakPtr<StunProberWithWeakPtr> StunProberWithWeakPtr::GetWeakPtr() {
100 return weak_factory_.GetWeakPtr();
101 }
102
103 StunProberWithWeakPtr::~StunProberWithWeakPtr() {
104 // DeleteSoon() on |next_prober_| instead of creating a big stack of deleting
105 // probers.
106 if (next_prober_)
107 base::MessageLoop::current()->DeleteSoon(FROM_HERE, next_prober_);
108 }
109
110 StunProberTrial::StunProberTrial(rtc::NetworkManager* network_manager,
111 const std::string& params,
112 rtc::PacketSocketFactory* factory)
113 : network_manager_(network_manager),
114 param_line_(params),
115 factory_(factory) {
116 // We have to connect to the signal to avoid a race condition if network
117 // manager hasn't received the network update when we start, the StunProber
118 // will just fail.
119 network_manager_->SignalNetworksChanged.connect(
120 this, &StunProberTrial::OnNetworksChanged);
121 network_manager_->StartUpdating();
122 }
123
124 StunProberTrial::~StunProberTrial() {
125 DCHECK(thread_checker_.CalledOnValidThread());
126 }
127
128 void StunProberTrial::SaveHistogramData() {
129 DCHECK(thread_checker_.CalledOnValidThread());
130 NatType nat_type = NAT_TYPE_MAX;
131 int interval_ms = 0;
132 int count = 0;
133 int total_sent = 0;
134 int total_recv = 0;
135 for (StunProberWithWeakPtr* prober = prober_head_.get(); prober != nullptr;
136 prober = prober->next_prober()) {
137 ++count;
138
139 // Get the stats.
140 StunProber::Stats stats;
141 if (!prober->GetStats(&stats))
142 return;
143
144 // Check if the NAT type is consistent.
145 if (nat_type == NAT_TYPE_MAX) {
146 nat_type = GetNatType(stats.nat_type);
147 // If we can't figure out the nattype at the beginning, just return.
148 if (nat_type == NAT_TYPE_UNKNOWN)
149 return;
150 } else if (nat_type != GetNatType(stats.nat_type) &&
151 nat_type != NAT_TYPE_UNKNOWN) {
152 // For subsequent probers, we might get unknown as nattype if all the
153 // bindings fail, but it's ok.
154 return;
155 }
156
157 // Check that the interval is consistent.
158 // Use the real probe interval for reporting, converting from nanosecond to
159 // millisecond at 5ms boundary.
160 int new_interval_ms = ClampProbingInterval(
161 round(static_cast<float>(stats.actual_request_interval_ns) / 5000) * 5);
162 if (interval_ms == 0) {
163 interval_ms = new_interval_ms;
164 } else if (interval_ms != new_interval_ms) {
165 return;
166 }
167
168 // Sum up the total sent and recv packets.
169 total_sent += stats.num_request_sent;
170 total_recv += stats.num_response_received;
171
172 if (count % batch_size_ > 0)
173 continue;
174
175 if (total_sent == 0) {
176 total_recv = 0;
177 continue;
178 }
179
180 int success_rate = total_recv * 100 / total_sent;
181 std::string histogram_name = HistogramName(
182 "SuccessPercent", nat_type, interval_ms, count / batch_size_);
183
184 // Mimic the same behavior as UMA_HISTOGRAM_PERCENTAGE. We can't use
185 // that macro as the histogram name is determined dynamically.
186 base::HistogramBase* histogram =
187 base::Histogram::FactoryGet(histogram_name, 1, 101, 102,
188 base::Histogram::kUmaTargetedHistogramFlag);
189 histogram->Add(success_rate);
190
191 DVLOG(1) << "Histogram '" << histogram_name.c_str()
192 << "' = " << stats.success_percent;
193
194 DVLOG(1) << "Shared Socket Mode: " << stats.shared_socket_mode;
195 DVLOG(1) << "Requests sent: " << total_sent;
196 DVLOG(1) << "Responses received: " << total_recv;
197 DVLOG(1) << "Target interval (ns): " << stats.target_request_interval_ns;
198 DVLOG(1) << "Actual interval (ns): " << stats.actual_request_interval_ns;
199 DVLOG(1) << "NAT Type: " << NatTypeNames[nat_type];
200 DVLOG(1) << "Host IP: " << stats.host_ip;
201
202 total_sent = 0;
203 total_recv = 0;
204 }
205 }
206
207 bool StunProberTrial::ParseParameters(const std::string& param_line,
208 StunProberTrial::Param* params) {
136 std::vector<std::string> stun_params = base::SplitString( 209 std::vector<std::string> stun_params = base::SplitString(
137 params, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); 210 param_line, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
138 211
139 if (stun_params.size() < 4) { 212 if (stun_params.size() < 5) {
140 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial"; 213 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial";
141 return false; 214 return false;
142 } 215 }
143 auto param = stun_params.begin(); 216 auto param = stun_params.begin();
144 217
145 if (param->empty()) { 218 if (param->empty()) {
146 *requests_per_ip = 10; 219 *params->requests_per_ip = 10;
147 } else if (!base::StringToInt(*param, requests_per_ip)) { 220 } else if (!base::StringToInt(*param, params->requests_per_ip)) {
148 DLOG(ERROR) << "Failed to parse request_per_ip in StartStunProbeTrial"; 221 DLOG(ERROR) << "Failed to parse request_per_ip in StartStunProbeTrial";
149 return false; 222 return false;
150 } 223 }
151 param++; 224 param++;
152 225
153 // Set inter-probe interval randomly from 0, 5, 10, ... 50, 100 ms. 226 // Set inter-probe interval randomly from 0, 5, 10, ... 50, 100 ms.
154 if ((*param).empty()) { 227 if ((*param).empty()) {
155 *interval_ms = base::RandInt(0, 11) * 5; 228 *params->interval_ms = base::RandInt(0, 11) * 5;
156 } else if (!base::StringToInt(*param, interval_ms)) { 229 } else if (!base::StringToInt(*param, params->interval_ms)) {
157 DLOG(ERROR) << "Failed to parse interval in StartStunProbeTrial"; 230 DLOG(ERROR) << "Failed to parse interval in StartStunProbeTrial";
158 return false; 231 return false;
159 } 232 }
160 *interval_ms = ClampProbingInterval(*interval_ms); 233 *params->interval_ms = ClampProbingInterval(*params->interval_ms);
161 param++; 234 param++;
162 235
163 if ((*param).empty()) { 236 if ((*param).empty()) {
164 *shared_socket_mode = base::RandInt(0, 1); 237 *params->shared_socket_mode = base::RandInt(0, 1);
165 } else if (!base::StringToInt(*param, shared_socket_mode)) { 238 } else if (!base::StringToInt(*param, params->shared_socket_mode)) {
166 DLOG(ERROR) << "Failed to parse shared_socket_mode in StartStunProbeTrial"; 239 DLOG(ERROR) << "Failed to parse shared_socket_mode in StartStunProbeTrial";
167 return false; 240 return false;
168 } 241 }
169 param++; 242 param++;
170 243
244 if (param->empty()) {
245 *params->batch_size = 5;
246 } else if (!base::StringToInt(*param, params->batch_size)) {
247 DLOG(ERROR) << "Failed to parse batch_size in StartStunProbeTrial";
248 return false;
249 }
250 param++;
251
252 if (param->empty()) {
253 *params->total_batches = 5;
254 } else if (!base::StringToInt(*param, params->total_batches)) {
255 DLOG(ERROR) << "Failed to parse total_batches in StartStunProbeTrial";
256 return false;
257 }
258 param++;
259
171 while (param != stun_params.end() && !param->empty()) { 260 while (param != stun_params.end() && !param->empty()) {
172 rtc::SocketAddress server; 261 rtc::SocketAddress server;
173 if (!server.FromString(*param)) { 262 if (!server.FromString(*param)) {
174 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial"; 263 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial";
175 return false; 264 return false;
176 } 265 }
177 servers->push_back(server); 266 params->servers->push_back(server);
178 param++; 267 param++;
179 } 268 }
180 269
181 return !servers->empty(); 270 return !params->servers->empty();
182 } 271 }
183 272
184 scoped_ptr<stunprober::StunProber> StartStunProbeTrial( 273 void StunProberTrial::OnNetworksChanged() {
185 const rtc::NetworkManager::NetworkList& networks, 274 DCHECK(thread_checker_.CalledOnValidThread());
186 const std::string& params, 275 DVLOG(1) << "Starting stun trial with params: " << param_line_;
187 rtc::PacketSocketFactory* factory) { 276 rtc::NetworkManager::NetworkList networks;
188 DVLOG(1) << "Starting stun trial with params: " << params; 277 network_manager_->GetNetworks(&networks);
189 278
190 // If we don't have local addresses, we won't be able to determine whether 279 // If we don't have local addresses, we won't be able to determine whether
191 // we're behind NAT or not. 280 // we're behind NAT or not.
192 if (networks.empty()) { 281 if (networks.empty()) {
193 DLOG(ERROR) << "No networks specified in StartStunProbeTrial"; 282 DLOG(ERROR) << "No networks specified in StartStunProbeTrial";
194 return nullptr; 283 return;
195 } 284 }
196 285
286 network_manager_->StopUpdating();
287 network_manager_->SignalNetworksChanged.disconnect(this);
288
197 int requests_per_ip; 289 int requests_per_ip;
290 int shared_socket_mode;
198 int interval_ms; 291 int interval_ms;
199 int shared_socket_mode; 292 int total_batches;
200 std::vector<rtc::SocketAddress> servers; 293 std::vector<rtc::SocketAddress> servers;
201 294
202 if (!ParseStunProbeParameters(params, &requests_per_ip, &interval_ms, 295 StunProberTrial::Param params(&requests_per_ip, &interval_ms,
203 &shared_socket_mode, &servers)) { 296 &shared_socket_mode, &batch_size_,
204 return nullptr; 297 &total_batches, &servers);
Alexei Svitkine (slow) 2015/10/30 18:39:47 Per my comment below, suggest this instead: StunP
guoweis_left_chromium 2015/10/30 21:47:13 Done.
298
299 if (!ParseParameters(param_line_, &params)) {
300 return;
205 } 301 }
206 302
207 scoped_ptr<StunProber> prober( 303 total_probers_ = total_batches * batch_size_;
208 new StunProber(factory, rtc::Thread::Current(), networks));
209 304
210 if (!prober->Start( 305 StunProberWithWeakPtr* prev_prober = nullptr;
211 servers, (shared_socket_mode != 0), interval_ms, requests_per_ip, 306
212 1000, 307 for (int i = 0; i < total_probers_; i++) {
213 rtc::Callback2<void, StunProber*, int>(&OnStunProbeTrialFinished))) { 308 stunprober::StunProber* prober =
214 DLOG(ERROR) << "Failed to Start in StartStunProbeTrial"; 309 new StunProber(factory_, rtc::Thread::Current(), networks);
215 OnStunProbeTrialFinished(prober.get(), StunProber::GENERIC_FAILURE); 310 scoped_ptr<StunProberWithWeakPtr> prober_wp(
216 return nullptr; 311 new StunProberWithWeakPtr(prober));
312 if (!prober->Prepare(servers, (shared_socket_mode != 0), interval_ms,
313 requests_per_ip, 1000, this)) {
314 DLOG(ERROR) << "Failed to Prepare in StartStunProbeTrial";
315 return;
316 }
317
318 if (prev_prober)
319 prev_prober->SetNextProber(prober_wp.get());
320 else
321 prober_head_.reset(prober_wp.get());
322
323 prev_prober = prober_wp.release();
217 } 324 }
218
219 return prober;
220 } 325 }
221 326
327 void StunProberTrial::OnFinished(StunProber* prober,
328 StunProber::Status result) {
329 DCHECK(thread_checker_.CalledOnValidThread());
330 if (result == StunProber::SUCCESS)
331 ++finished_probers_;
332
333 if (finished_probers_ == total_probers_)
334 SaveHistogramData();
335 }
336
337 void StunProberTrial::OnPrepared(StunProber* prober,
338 StunProber::Status result) {
339 DCHECK(thread_checker_.CalledOnValidThread());
340 if (result == StunProber::SUCCESS)
341 ++ready_probers_;
342
343 if (ready_probers_ == total_probers_)
344 prober_head_->Start(this);
345 }
Alexei Svitkine (slow) 2015/10/30 18:39:47 Nit: Add an empty line below.
guoweis_left_chromium 2015/10/30 21:47:13 Done.
222 } // namespace content 346 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698