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

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: Remove StunProberWithWeakPtr and replace with a timer 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"
10 #include "base/location.h"
9 #include "base/logging.h" 11 #include "base/logging.h"
10 #include "base/macros.h" 12 #include "base/macros.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 StunProberTrial::Param::Param() {}
132 int* requests_per_ip, 78
133 int* interval_ms, 79 StunProberTrial::Param::~Param() {}
134 int* shared_socket_mode, 80
135 std::vector<rtc::SocketAddress>* servers) { 81 StunProberTrial::StunProberTrial(rtc::NetworkManager* network_manager,
82 const std::string& params,
83 rtc::PacketSocketFactory* factory)
84 : network_manager_(network_manager),
85 param_line_(params),
86 factory_(factory) {
87 // We have to connect to the signal to avoid a race condition if network
88 // manager hasn't received the network update when we start, the StunProber
89 // will just fail.
90 network_manager_->SignalNetworksChanged.connect(
91 this, &StunProberTrial::OnNetworksChanged);
92 network_manager_->StartUpdating();
93 }
94
95 StunProberTrial::~StunProberTrial() {}
96
97 void StunProberTrial::SaveHistogramData() {
98 DCHECK(thread_checker_.CalledOnValidThread());
99 NatType nat_type = NAT_TYPE_MAX;
100 int interval_ms = 0;
101 int count = 0;
102 int total_sent = 0;
103 int total_recv = 0;
104 for (auto& prober : probers_) {
105 ++count;
106
107 // Get the stats.
108 StunProber::Stats stats;
109 if (!prober->GetStats(&stats))
110 return;
111
112 // Check if the NAT type is consistent.
113 if (nat_type == NAT_TYPE_MAX) {
114 nat_type = GetNatType(stats.nat_type);
115 // If we can't figure out the nattype at the beginning, just return.
116 if (nat_type == NAT_TYPE_UNKNOWN)
117 return;
118 } else if (nat_type != GetNatType(stats.nat_type) &&
119 nat_type != NAT_TYPE_UNKNOWN) {
120 // For subsequent probers, we might get unknown as nattype if all the
121 // bindings fail, but it's ok.
122 return;
123 }
124
125 // Check that the interval is consistent.
126 // Use the real probe interval for reporting, converting from nanosecond to
127 // millisecond at 5ms boundary.
128 int new_interval_ms = ClampProbingInterval(
129 round(static_cast<float>(stats.actual_request_interval_ns) / 5000) * 5);
130 if (interval_ms == 0) {
131 interval_ms = new_interval_ms;
132 } else if (interval_ms != new_interval_ms) {
133 return;
134 }
135
136 // Sum up the total sent and recv packets.
137 total_sent += stats.num_request_sent;
138 total_recv += stats.num_response_received;
139
140 if (count % batch_size_ > 0)
141 continue;
142
143 if (total_sent == 0) {
144 total_recv = 0;
145 continue;
146 }
147
148 int success_rate = total_recv * 100 / total_sent;
149 std::string histogram_name = HistogramName(
150 "SuccessPercent", nat_type, interval_ms, count / batch_size_);
151
152 // Mimic the same behavior as UMA_HISTOGRAM_PERCENTAGE. We can't use
153 // that macro as the histogram name is determined dynamically.
154 base::HistogramBase* histogram =
155 base::Histogram::FactoryGet(histogram_name, 1, 101, 102,
156 base::Histogram::kUmaTargetedHistogramFlag);
157 histogram->Add(success_rate);
158
159 DVLOG(1) << "Histogram '" << histogram_name.c_str()
160 << "' = " << stats.success_percent;
161
162 DVLOG(1) << "Shared Socket Mode: " << stats.shared_socket_mode;
163 DVLOG(1) << "Requests sent: " << total_sent;
164 DVLOG(1) << "Responses received: " << total_recv;
165 DVLOG(1) << "Target interval (ns): " << stats.target_request_interval_ns;
166 DVLOG(1) << "Actual interval (ns): " << stats.actual_request_interval_ns;
167 DVLOG(1) << "NAT Type: " << NatTypeNames[nat_type];
168 DVLOG(1) << "Host IP: " << stats.host_ip;
169
170 total_sent = 0;
171 total_recv = 0;
172 }
173 }
174
175 bool StunProberTrial::ParseParameters(const std::string& param_line,
176 StunProberTrial::Param* params) {
136 std::vector<std::string> stun_params = base::SplitString( 177 std::vector<std::string> stun_params = base::SplitString(
137 params, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); 178 param_line, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
138 179
139 if (stun_params.size() < 4) { 180 if (stun_params.size() < 5) {
140 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial"; 181 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial";
141 return false; 182 return false;
142 } 183 }
143 auto param = stun_params.begin(); 184 auto param = stun_params.begin();
144 185
145 if (param->empty()) { 186 if (param->empty()) {
146 *requests_per_ip = 10; 187 params->requests_per_ip = 10;
147 } else if (!base::StringToInt(*param, requests_per_ip)) { 188 } else if (!base::StringToInt(*param, &params->requests_per_ip)) {
148 DLOG(ERROR) << "Failed to parse request_per_ip in StartStunProbeTrial"; 189 DLOG(ERROR) << "Failed to parse request_per_ip in StartStunProbeTrial";
149 return false; 190 return false;
150 } 191 }
151 param++; 192 param++;
152 193
153 // Set inter-probe interval randomly from 0, 5, 10, ... 50, 100 ms. 194 // Set inter-probe interval randomly from 0, 5, 10, ... 50, 100 ms.
154 if ((*param).empty()) { 195 if ((*param).empty()) {
155 *interval_ms = base::RandInt(0, 11) * 5; 196 params->interval_ms = base::RandInt(0, 11) * 5;
156 } else if (!base::StringToInt(*param, interval_ms)) { 197 } else if (!base::StringToInt(*param, &params->interval_ms)) {
157 DLOG(ERROR) << "Failed to parse interval in StartStunProbeTrial"; 198 DLOG(ERROR) << "Failed to parse interval in StartStunProbeTrial";
158 return false; 199 return false;
159 } 200 }
160 *interval_ms = ClampProbingInterval(*interval_ms); 201 params->interval_ms = ClampProbingInterval(params->interval_ms);
161 param++; 202 param++;
162 203
163 if ((*param).empty()) { 204 if ((*param).empty()) {
164 *shared_socket_mode = base::RandInt(0, 1); 205 params->shared_socket_mode = base::RandInt(0, 1);
165 } else if (!base::StringToInt(*param, shared_socket_mode)) { 206 } else if (!base::StringToInt(*param, &params->shared_socket_mode)) {
166 DLOG(ERROR) << "Failed to parse shared_socket_mode in StartStunProbeTrial"; 207 DLOG(ERROR) << "Failed to parse shared_socket_mode in StartStunProbeTrial";
167 return false; 208 return false;
168 } 209 }
169 param++; 210 param++;
170 211
212 if (param->empty()) {
213 params->batch_size = 5;
214 } else if (!base::StringToInt(*param, &params->batch_size)) {
215 DLOG(ERROR) << "Failed to parse batch_size in StartStunProbeTrial";
216 return false;
217 }
218 param++;
219
220 if (param->empty()) {
221 params->total_batches = 5;
222 } else if (!base::StringToInt(*param, &params->total_batches)) {
223 DLOG(ERROR) << "Failed to parse total_batches in StartStunProbeTrial";
224 return false;
225 }
226 param++;
227
171 while (param != stun_params.end() && !param->empty()) { 228 while (param != stun_params.end() && !param->empty()) {
172 rtc::SocketAddress server; 229 rtc::SocketAddress server;
173 if (!server.FromString(*param)) { 230 if (!server.FromString(*param)) {
174 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial"; 231 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial";
175 return false; 232 return false;
176 } 233 }
177 servers->push_back(server); 234 params->servers.push_back(server);
178 param++; 235 param++;
179 } 236 }
180 237
181 return !servers->empty(); 238 return !params->servers.empty();
182 } 239 }
183 240
184 scoped_ptr<stunprober::StunProber> StartStunProbeTrial( 241 void StunProberTrial::OnNetworksChanged() {
185 const rtc::NetworkManager::NetworkList& networks, 242 DCHECK(thread_checker_.CalledOnValidThread());
186 const std::string& params, 243 DVLOG(1) << "Starting stun trial with params: " << param_line_;
187 rtc::PacketSocketFactory* factory) { 244 rtc::NetworkManager::NetworkList networks;
188 DVLOG(1) << "Starting stun trial with params: " << params; 245 network_manager_->GetNetworks(&networks);
189 246
190 // If we don't have local addresses, we won't be able to determine whether 247 // If we don't have local addresses, we won't be able to determine whether
191 // we're behind NAT or not. 248 // we're behind NAT or not.
192 if (networks.empty()) { 249 if (networks.empty()) {
193 DLOG(ERROR) << "No networks specified in StartStunProbeTrial"; 250 DLOG(ERROR) << "No networks specified in StartStunProbeTrial";
194 return nullptr; 251 return;
195 } 252 }
196 253
197 int requests_per_ip; 254 network_manager_->StopUpdating();
198 int interval_ms; 255 network_manager_->SignalNetworksChanged.disconnect(this);
199 int shared_socket_mode;
200 std::vector<rtc::SocketAddress> servers;
201 256
202 if (!ParseStunProbeParameters(params, &requests_per_ip, &interval_ms, 257 StunProberTrial::Param params;
203 &shared_socket_mode, &servers)) { 258 if (!ParseParameters(param_line_, &params)) {
204 return nullptr; 259 return;
205 } 260 }
206 261
207 scoped_ptr<StunProber> prober( 262 batch_size_ = params.batch_size;
208 new StunProber(factory, rtc::Thread::Current(), networks)); 263 total_probers_ = params.total_batches * batch_size_;
209 264
210 if (!prober->Start( 265 for (int i = 0; i < total_probers_; i++) {
211 servers, (shared_socket_mode != 0), interval_ms, requests_per_ip, 266 scoped_ptr<StunProber> prober(
212 1000, 267 new StunProber(factory_, rtc::Thread::Current(), networks));
213 rtc::Callback2<void, StunProber*, int>(&OnStunProbeTrialFinished))) { 268 if (!prober->Prepare(params.servers, (params.shared_socket_mode != 0),
214 DLOG(ERROR) << "Failed to Start in StartStunProbeTrial"; 269 params.interval_ms, params.requests_per_ip, 1000,
215 OnStunProbeTrialFinished(prober.get(), StunProber::GENERIC_FAILURE); 270 this)) {
216 return nullptr; 271 DLOG(ERROR) << "Failed to Prepare in StartStunProbeTrial";
272 return;
273 }
274
275 probers_.push_back(prober.release());
217 } 276 }
277 }
218 278
219 return prober; 279 void StunProberTrial::OnFinished(StunProber* prober,
280 StunProber::Status result) {
281 DCHECK(thread_checker_.CalledOnValidThread());
282 if (result == StunProber::SUCCESS)
283 ++finished_probers_;
284
285 if (finished_probers_ == total_probers_)
286 SaveHistogramData();
287 }
288
289 void StunProberTrial::OnPrepared(StunProber* prober,
290 StunProber::Status result) {
291 DCHECK(thread_checker_.CalledOnValidThread());
292 if (result == StunProber::SUCCESS)
293 ++ready_probers_;
294
295 if (ready_probers_ == total_probers_) {
296 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
Sergey Ulanov 2015/10/30 22:50:06 I'm not sure I understand the reason you need this
guoweis_left_chromium 2015/10/30 23:14:59 added a comment there.
297 probers_.front()->estimated_execution_time()),
Sergey Ulanov 2015/10/30 22:50:06 Is it always the case that all probers have the sa
guoweis_left_chromium 2015/10/30 23:14:59 yes, it's the interval * number of request_per_ip
298 this, &StunProberTrial::OnTimer);
299 }
300 }
301
302 void StunProberTrial::OnTimer() {
303 DCHECK(thread_checker_.CalledOnValidThread());
304 probers_[started_probers_++]->Start(this);
Sergey Ulanov 2015/10/30 22:50:06 nit: move increment to a separate line for readabi
guoweis_left_chromium 2015/10/30 23:14:59 Done.
305
306 if (started_probers_ == total_probers_)
307 timer_.Stop();
220 } 308 }
221 309
222 } // namespace content 310 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698