OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |