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

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: Created 5 years, 2 months 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
33 // Global states to manage the trial. This trial is only run on the first
34 // renderer so this is ok.
35 int total_probers = 0;
36 int reporting_batch_size = 0;
37 int ready_probers = 0;
38 int finished_probers = 0;
39 StunProberWithWeakPtr* prober_head = nullptr;
pthatcher2 2015/10/22 05:27:51 Instead of having global state, can we instead hav
40
32 // This needs to be the same as NatTypeCounters in histograms.xml. 41 // This needs to be the same as NatTypeCounters in histograms.xml.
33 enum NatType { 42 enum NatType {
34 NAT_TYPE_NONE, 43 NAT_TYPE_NONE,
35 NAT_TYPE_UNKNOWN, 44 NAT_TYPE_UNKNOWN,
36 NAT_TYPE_SYMMETRIC, 45 NAT_TYPE_SYMMETRIC,
37 NAT_TYPE_NON_SYMMETRIC, 46 NAT_TYPE_NON_SYMMETRIC,
38 NAT_TYPE_MAX 47 NAT_TYPE_MAX
39 }; 48 };
40 49
41 // This needs to match "NatType" in histograms.xml. 50 // This needs to match "NatType" in histograms.xml.
(...skipping 16 matching lines...) Expand all
58 } 67 }
59 68
60 // Below 50ms, we are doing experiments at each 5ms interval. Beyond 50ms, only 69 // Below 50ms, we are doing experiments at each 5ms interval. Beyond 50ms, only
61 // one experiment of 100ms. 70 // one experiment of 100ms.
62 int ClampProbingInterval(int interval_ms) { 71 int ClampProbingInterval(int interval_ms) {
63 return interval_ms > 50 ? 100 : interval_ms; 72 return interval_ms > 50 ? 100 : interval_ms;
64 } 73 }
65 74
66 std::string HistogramName(const std::string& prefix, 75 std::string HistogramName(const std::string& prefix,
67 NatType nat_type, 76 NatType nat_type,
68 int interval_ms) { 77 int interval_ms,
69 return base::StringPrintf("WebRTC.Stun.%s.%s.%dms", prefix.c_str(), 78 int current_batch,
pthatcher2 2015/10/22 05:27:51 Should this be batch_index?
guoweis_left_chromium 2015/10/27 17:19:06 Done.
70 NatTypeNames[nat_type], interval_ms); 79 int total_batch) {
pthatcher2 2015/10/22 05:27:51 And this batch_count or total_batches?
guoweis_left_chromium 2015/10/27 17:19:05 Done.
80 return base::StringPrintf("WebRTC.Stun.%s.%s.%dms.%d.%d", prefix.c_str(),
81 NatTypeNames[nat_type], interval_ms, current_batch,
82 total_batch);
71 } 83 }
72 84
73 void SaveHistogramData(StunProber* prober) { 85 void SaveHistogramData(StunProberWithWeakPtr* prober) {
74 StunProber::Stats stats; 86 NatType nat_type = NAT_TYPE_MAX;
75 if (!prober->GetStats(&stats)) 87 int interval_ms = 0;
76 return; 88 int count = 0;
89 int total_sent = 0;
90 int total_recv = 0;
91 while (prober != nullptr) {
pthatcher2 2015/10/22 05:27:52 Wouldn't this be more clear as for (; prober != n
guoweis_left_chromium 2015/10/27 17:19:06 Done.
92 ++count;
77 93
78 NatType nat_type = GetNatType(stats.nat_type); 94 // Get the stats.
95 StunProber::Stats stats;
96 if (!prober->GetStats(&stats))
97 return;
79 98
80 // Use the real probe interval for reporting, converting from nanosecond to 99 // Check if the NAT type is consistent.
81 // millisecond at 5ms boundary. 100 if (nat_type == NAT_TYPE_MAX) {
82 int interval_ms = 101 nat_type = GetNatType(stats.nat_type);
83 round(static_cast<float>(stats.actual_request_interval_ns) / 5000) * 5; 102 // If we can't figure out the nattype at the beginning, just return.
103 if (nat_type == NAT_TYPE_UNKNOWN)
104 return;
105 }
106 // For subsequent probers, we might get unknown as nattype if all the
107 // bindings fail, but it's ok.
108 else if (nat_type != GetNatType(stats.nat_type) &&
109 nat_type != NAT_TYPE_UNKNOWN)
110 return;
84 111
85 interval_ms = ClampProbingInterval(interval_ms); 112 // Check the interval is consistent.
113 // Use the real probe interval for reporting, converting from nanosecond to
114 // millisecond at 5ms boundary.
115 int new_interval_ms = ClampProbingInterval(
116 round(static_cast<float>(stats.actual_request_interval_ns) / 5000) * 5);
117 if (interval_ms == 0) {
118 interval_ms = new_interval_ms;
119 } else if (interval_ms != new_interval_ms)
120 return;
86 121
87 UMA_HISTOGRAM_ENUMERATION("WebRTC.NAT.Metrics", nat_type, NAT_TYPE_MAX); 122 // Sum up the total sent and recv packets.
123 total_sent += stats.num_request_sent;
124 total_recv += stats.num_response_received;
88 125
89 std::string histogram_name = 126 // At the batch boundary, reporting it.
90 HistogramName("SuccessPercent", nat_type, interval_ms); 127 if (count % reporting_batch_size == 0) {
128 if (total_sent != 0) {
129 int success_rate = total_recv * 100 / total_sent;
130 std::string histogram_name = HistogramName(
131 "SuccessRate", nat_type, interval_ms, count / reporting_batch_size,
132 total_probers / reporting_batch_size);
91 133
92 // Mimic the same behavior as UMA_HISTOGRAM_PERCENTAGE. We can't use that 134 // Mimic the same behavior as UMA_HISTOGRAM_PERCENTAGE. We can't use
93 // macro as the histogram name is determined dynamically. 135 // that macro as the histogram name is determined dynamically.
94 base::HistogramBase* histogram = base::Histogram::FactoryGet( 136 base::HistogramBase* histogram = base::Histogram::FactoryGet(
95 histogram_name, 1, 101, 102, base::Histogram::kUmaTargetedHistogramFlag); 137 histogram_name, 1, 101, 102,
96 histogram->Add(stats.success_percent); 138 base::Histogram::kUmaTargetedHistogramFlag);
139 histogram->Add(success_rate);
97 140
98 DVLOG(1) << "Histogram '" << histogram_name.c_str() 141 DVLOG(1) << "Histogram '" << histogram_name.c_str()
99 << "' = " << stats.success_percent; 142 << "' = " << stats.success_percent;
100 143
101 histogram_name = HistogramName("ResponseLatency", nat_type, interval_ms); 144 DVLOG(1) << "Shared Socket Mode: " << stats.shared_socket_mode;
102 145 DVLOG(1) << "Requests sent: " << total_sent;
103 histogram = base::Histogram::FactoryTimeGet( 146 DVLOG(1) << "Responses received: " << total_recv;
104 histogram_name, base::TimeDelta::FromMilliseconds(1), 147 DVLOG(1) << "Target interval (ns): "
105 base::TimeDelta::FromSeconds(10), 50, 148 << stats.target_request_interval_ns;
106 base::Histogram::kUmaTargetedHistogramFlag); 149 DVLOG(1) << "Actual interval (ns): "
107 histogram->AddTime(base::TimeDelta::FromMilliseconds(stats.average_rtt_ms)); 150 << stats.actual_request_interval_ns;
108 151 DVLOG(1) << "NAT Type: " << NatTypeNames[nat_type];
109 DVLOG(1) << "Histogram '" << histogram_name.c_str() 152 DVLOG(1) << "Host IP: " << stats.host_ip;
110 << "' = " << stats.average_rtt_ms << " ms"; 153 }
111 154 total_sent = 0;
112 DVLOG(1) << "Shared Socket Mode: " << stats.shared_socket_mode; 155 total_recv = 0;
113 DVLOG(1) << "Requests sent: " << stats.num_request_sent; 156 }
114 DVLOG(1) << "Responses received: " << stats.num_response_received; 157 prober = prober->GetNextProber();
115 DVLOG(1) << "Target interval (ns): " << stats.target_request_interval_ns; 158 }
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 } 159 }
123 160
124 void OnStunProbeTrialFinished(StunProber* prober, int result) { 161 void OnStunProbeTrialFinished(StunProber* prober, int result) {
125 if (result == StunProber::SUCCESS) 162 if (result == StunProber::SUCCESS)
126 SaveHistogramData(prober); 163 ++finished_probers;
164
165 if (finished_probers == total_probers)
166 SaveHistogramData(prober_head);
pthatcher2 2015/10/22 05:27:51 If any of them fail, we don't record anything? Wh
guoweis_left_chromium 2015/10/27 17:19:06 I think the chance for this to happen is low. If i
167 }
168
169 void OnStunProberPrepared(StunProber* prober, int result) {
170 if (result == StunProber::SUCCESS) {
171 ++ready_probers;
172 }
pthatcher2 2015/10/22 05:27:51 If on prober fails to prepare, then none of them g
guoweis_left_chromium 2015/10/27 17:19:06 yes, I think that should be fine. If we can't DNS
173 if (ready_probers == total_probers) {
174 DCHECK(prober_head);
175 prober_head->Start(stunprober::AsyncCallback(&OnStunProbeTrialFinished));
pthatcher2 2015/10/22 05:27:51 Shouldn't the callbeack be called OnStunProberFini
guoweis_left_chromium 2015/10/27 17:19:05 Done.
176 }
127 } 177 }
128 178
129 } // namespace 179 } // namespace
130 180
181 StunProberWithWeakPtr::StunProberWithWeakPtr(StunProber* prober)
182 : prober_(prober), weak_factory_(this) {}
183
184 void StunProberWithWeakPtr::set_next_prober(
185 StunProberWithWeakPtr* next_prober) {
186 next_prober_ = next_prober;
187 }
188
189 void StunProberWithWeakPtr::Start(stunprober::AsyncCallback callback) {
190 base::MessageLoop::current()->PostDelayedTask(
191 FROM_HERE, base::Bind(&StunProberWithWeakPtr::Start,
192 next_prober_->GetWeakPtr(), callback),
193 base::TimeDelta::FromMilliseconds(prober_->estimated_execution_time()));
194 prober_->Run(callback);
195 }
196
197 StunProberWithWeakPtr::~StunProberWithWeakPtr() {}
198
131 bool ParseStunProbeParameters(const std::string& params, 199 bool ParseStunProbeParameters(const std::string& params,
132 int* requests_per_ip, 200 int* requests_per_ip,
133 int* interval_ms, 201 int* interval_ms,
134 int* shared_socket_mode, 202 int* shared_socket_mode,
203 int* reporting_batch_size,
204 int* rounds,
pthatcher2 2015/10/22 05:27:51 Do these two variables never get parsed?
135 std::vector<rtc::SocketAddress>* servers) { 205 std::vector<rtc::SocketAddress>* servers) {
136 std::vector<std::string> stun_params = base::SplitString( 206 std::vector<std::string> stun_params = base::SplitString(
137 params, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); 207 params, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
138 208
139 if (stun_params.size() < 4) { 209 if (stun_params.size() < 4) {
140 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial"; 210 DLOG(ERROR) << "Not enough parameters specified in StartStunProbeTrial";
141 return false; 211 return false;
142 } 212 }
143 auto param = stun_params.begin(); 213 auto param = stun_params.begin();
144 214
(...skipping 29 matching lines...) Expand all
174 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial"; 244 DLOG(ERROR) << "Failed to parse address in StartStunProbeTrial";
175 return false; 245 return false;
176 } 246 }
177 servers->push_back(server); 247 servers->push_back(server);
178 param++; 248 param++;
179 } 249 }
180 250
181 return !servers->empty(); 251 return !servers->empty();
182 } 252 }
183 253
184 scoped_ptr<stunprober::StunProber> StartStunProbeTrial( 254 void StartStunProbeTrial(const rtc::NetworkManager::NetworkList& networks,
185 const rtc::NetworkManager::NetworkList& networks, 255 const std::string& params,
186 const std::string& params, 256 rtc::PacketSocketFactory* factory,
187 rtc::PacketSocketFactory* factory) { 257 ListOfStunProbers* probers) {
188 DVLOG(1) << "Starting stun trial with params: " << params; 258 DVLOG(1) << "Starting stun trial with params: " << params;
189 259
190 // If we don't have local addresses, we won't be able to determine whether 260 // If we don't have local addresses, we won't be able to determine whether
191 // we're behind NAT or not. 261 // we're behind NAT or not.
192 if (networks.empty()) { 262 if (networks.empty()) {
193 DLOG(ERROR) << "No networks specified in StartStunProbeTrial"; 263 DLOG(ERROR) << "No networks specified in StartStunProbeTrial";
194 return nullptr; 264 return;
195 } 265 }
196 266
197 int requests_per_ip; 267 int requests_per_ip;
268 int shared_socket_mode;
198 int interval_ms; 269 int interval_ms;
199 int shared_socket_mode;
200 std::vector<rtc::SocketAddress> servers; 270 std::vector<rtc::SocketAddress> servers;
201 271
202 if (!ParseStunProbeParameters(params, &requests_per_ip, &interval_ms, 272 if (!ParseStunProbeParameters(params, &requests_per_ip, &interval_ms,
203 &shared_socket_mode, &servers)) { 273 &shared_socket_mode, &reporting_batch_size,
204 return nullptr; 274 &total_probers, &servers)) {
275 return;
205 } 276 }
206 277
207 scoped_ptr<StunProber> prober( 278 int rounds = total_probers;
208 new StunProber(factory, rtc::Thread::Current(), networks));
209 279
210 if (!prober->Start( 280 StunProberWithWeakPtr* prev_prober = nullptr;
211 servers, (shared_socket_mode != 0), interval_ms, requests_per_ip, 281
212 1000, 282 while (rounds-- > 0) {
pthatcher2 2015/10/22 05:27:51 Wouldn't this be more clear as: for (int i = 0; i
guoweis_left_chromium 2015/10/27 17:19:06 Done.
213 rtc::Callback2<void, StunProber*, int>(&OnStunProbeTrialFinished))) { 283 stunprober::StunProber* prober =
214 DLOG(ERROR) << "Failed to Start in StartStunProbeTrial"; 284 new StunProber(factory, rtc::Thread::Current(), networks);
215 OnStunProbeTrialFinished(prober.get(), StunProber::GENERIC_FAILURE); 285 scoped_ptr<StunProberWithWeakPtr> prober_wp(
216 return nullptr; 286 new StunProberWithWeakPtr(prober));
287 if (!prober->Prepare(servers, (shared_socket_mode != 0), interval_ms,
288 requests_per_ip, 1000,
289 stunprober::AsyncCallback(&OnStunProberPrepared))) {
290 DLOG(ERROR) << "Failed to Prepare in StartStunProbeTrial";
291 for (auto prober : *probers) {
292 if (prober) {
293 delete prober;
294 }
295 }
pthatcher2 2015/10/22 05:27:51 I've seen this a few times. Should we have a "Del
296 probers->clear();
297 return;
298 } else {
pthatcher2 2015/10/22 05:27:52 You don't need an else. Since it's an early retur
guoweis_left_chromium 2015/10/27 17:19:05 Done.
299 if (prev_prober) {
300 prev_prober->set_next_prober(prober_wp.get());
301 } else {
302 prober_head = prober_wp.get();
303 }
304 prev_prober = prober_wp.release();
305 probers->push_back(prev_prober);
306 }
217 } 307 }
218
219 return prober;
220 } 308 }
221 309
222 } // namespace content 310 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698