quic_connectivity_monitor.cc 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. // Copyright (c) 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/quic/quic_connectivity_monitor.h"
  5. #include "base/metrics/histogram_functions.h"
  6. #include "base/metrics/histogram_macros.h"
  7. namespace net {
  8. namespace {
  9. bool IsErrorRelatedToConnectivity(int error_code) {
  10. return (error_code == ERR_ADDRESS_UNREACHABLE ||
  11. error_code == ERR_ACCESS_DENIED ||
  12. error_code == ERR_INTERNET_DISCONNECTED);
  13. }
  14. } // namespace
  15. QuicConnectivityMonitor::QuicConnectivityMonitor(
  16. handles::NetworkHandle default_network)
  17. : default_network_(default_network) {}
  18. QuicConnectivityMonitor::~QuicConnectivityMonitor() = default;
  19. void QuicConnectivityMonitor::RecordConnectivityStatsToHistograms(
  20. const std::string& notification,
  21. handles::NetworkHandle affected_network) const {
  22. if (notification == "OnNetworkSoonToDisconnect" ||
  23. notification == "OnNetworkDisconnected") {
  24. // If the disconnected network is not the default network, ignore
  25. // stats collections.
  26. if (affected_network != default_network_)
  27. return;
  28. }
  29. base::ClampedNumeric<int> num_degrading_sessions = GetNumDegradingSessions();
  30. if (num_sessions_active_during_current_speculative_connectivity_failure_) {
  31. UMA_HISTOGRAM_COUNTS_100(
  32. "Net.QuicConnectivityMonitor.NumSessionsTrackedSinceSpeculativeError",
  33. num_sessions_active_during_current_speculative_connectivity_failure_
  34. .value());
  35. }
  36. UMA_HISTOGRAM_COUNTS_100(
  37. "Net.QuicConnectivityMonitor.NumActiveQuicSessionsAtNetworkChange",
  38. active_sessions_.size());
  39. int percentage = 0;
  40. if (num_sessions_active_during_current_speculative_connectivity_failure_ &&
  41. num_sessions_active_during_current_speculative_connectivity_failure_
  42. .value() > 0) {
  43. percentage = base::saturated_cast<int>(
  44. num_all_degraded_sessions_ * 100.0 /
  45. num_sessions_active_during_current_speculative_connectivity_failure_
  46. .value());
  47. }
  48. UMA_HISTOGRAM_COUNTS_100(
  49. "Net.QuicConnectivityMonitor.NumAllSessionsDegradedAtNetworkChange",
  50. num_all_degraded_sessions_);
  51. const std::string raw_histogram_name1 =
  52. "Net.QuicConnectivityMonitor.NumAllDegradedSessions." + notification;
  53. base::UmaHistogramCustomCounts(raw_histogram_name1,
  54. num_all_degraded_sessions_, 1, 100, 50);
  55. const std::string percentage_histogram_name1 =
  56. "Net.QuicConnectivityMonitor.PercentageAllDegradedSessions." +
  57. notification;
  58. base::UmaHistogramPercentageObsoleteDoNotUse(percentage_histogram_name1,
  59. percentage);
  60. // Skip degrading session collection if there are less than two sessions.
  61. if (active_sessions_.size() < 2u)
  62. return;
  63. const std::string raw_histogram_name =
  64. "Net.QuicConnectivityMonitor.NumActiveDegradingSessions." + notification;
  65. base::UmaHistogramCustomCounts(raw_histogram_name, num_degrading_sessions, 1,
  66. 100, 50);
  67. percentage = base::saturated_cast<double>(num_degrading_sessions * 100.0 /
  68. active_sessions_.size());
  69. const std::string percentage_histogram_name =
  70. "Net.QuicConnectivityMonitor.PercentageActiveDegradingSessions." +
  71. notification;
  72. base::UmaHistogramPercentageObsoleteDoNotUse(percentage_histogram_name,
  73. percentage);
  74. }
  75. size_t QuicConnectivityMonitor::GetNumDegradingSessions() const {
  76. return degrading_sessions_.size();
  77. }
  78. size_t QuicConnectivityMonitor::GetCountForWriteErrorCode(
  79. int write_error_code) const {
  80. auto it = write_error_map_.find(write_error_code);
  81. return it == write_error_map_.end() ? 0u : it->second;
  82. }
  83. void QuicConnectivityMonitor::SetInitialDefaultNetwork(
  84. handles::NetworkHandle default_network) {
  85. default_network_ = default_network;
  86. }
  87. void QuicConnectivityMonitor::OnSessionPathDegrading(
  88. QuicChromiumClientSession* session,
  89. handles::NetworkHandle network) {
  90. if (network != default_network_)
  91. return;
  92. degrading_sessions_.insert(session);
  93. num_all_degraded_sessions_++;
  94. // If the degrading session used to be on the previous default network, it is
  95. // possible that the session is no longer tracked in |active_sessions_| due
  96. // to the recent default network change.
  97. active_sessions_.insert(session);
  98. if (!num_sessions_active_during_current_speculative_connectivity_failure_) {
  99. num_sessions_active_during_current_speculative_connectivity_failure_ =
  100. active_sessions_.size();
  101. } else {
  102. // Before seeing session degrading, PACKET_WRITE_ERROR has been observed.
  103. UMA_HISTOGRAM_COUNTS_100(
  104. "Net.QuicConnectivityMonitor.NumWriteErrorsSeenBeforeDegradation",
  105. quic_error_map_[quic::QUIC_PACKET_WRITE_ERROR]);
  106. }
  107. }
  108. void QuicConnectivityMonitor::OnSessionResumedPostPathDegrading(
  109. QuicChromiumClientSession* session,
  110. handles::NetworkHandle network) {
  111. if (network != default_network_)
  112. return;
  113. degrading_sessions_.erase(session);
  114. // If the resumed session used to be on the previous default network, it is
  115. // possible that the session is no longer tracked in |active_sessions_| due
  116. // to the recent default network change.
  117. active_sessions_.insert(session);
  118. num_all_degraded_sessions_ = 0u;
  119. num_sessions_active_during_current_speculative_connectivity_failure_ =
  120. absl::nullopt;
  121. }
  122. void QuicConnectivityMonitor::OnSessionEncounteringWriteError(
  123. QuicChromiumClientSession* session,
  124. handles::NetworkHandle network,
  125. int error_code) {
  126. if (network != default_network_)
  127. return;
  128. // If the session used to be on the previous default network, it is
  129. // possible that the session is no longer tracked in |active_sessions_| due
  130. // to the recent default network change.
  131. active_sessions_.insert(session);
  132. ++write_error_map_[error_code];
  133. bool is_session_degraded =
  134. degrading_sessions_.find(session) != degrading_sessions_.end();
  135. UMA_HISTOGRAM_BOOLEAN(
  136. "Net.QuicConnectivityMonitor.SessionDegradedBeforeWriteError",
  137. is_session_degraded);
  138. if (!num_sessions_active_during_current_speculative_connectivity_failure_ &&
  139. IsErrorRelatedToConnectivity(error_code)) {
  140. num_sessions_active_during_current_speculative_connectivity_failure_ =
  141. active_sessions_.size();
  142. }
  143. }
  144. void QuicConnectivityMonitor::OnSessionClosedAfterHandshake(
  145. QuicChromiumClientSession* session,
  146. handles::NetworkHandle network,
  147. quic::ConnectionCloseSource source,
  148. quic::QuicErrorCode error_code) {
  149. if (network != default_network_)
  150. return;
  151. if (source == quic::ConnectionCloseSource::FROM_PEER) {
  152. // Connection closed by the peer post handshake with PUBLIC RESET
  153. // is most likely a NAT rebinding issue.
  154. if (error_code == quic::QUIC_PUBLIC_RESET)
  155. quic_error_map_[error_code]++;
  156. return;
  157. }
  158. if (error_code == quic::QUIC_PACKET_WRITE_ERROR ||
  159. error_code == quic::QUIC_TOO_MANY_RTOS) {
  160. // Connection close by self with PACKET_WRITE_ERROR or TOO_MANY_RTOS
  161. // is likely a connectivity issue.
  162. quic_error_map_[error_code]++;
  163. }
  164. }
  165. void QuicConnectivityMonitor::OnSessionRegistered(
  166. QuicChromiumClientSession* session,
  167. handles::NetworkHandle network) {
  168. if (network != default_network_)
  169. return;
  170. active_sessions_.insert(session);
  171. if (num_sessions_active_during_current_speculative_connectivity_failure_) {
  172. num_sessions_active_during_current_speculative_connectivity_failure_
  173. .value()++;
  174. }
  175. }
  176. void QuicConnectivityMonitor::OnSessionRemoved(
  177. QuicChromiumClientSession* session) {
  178. degrading_sessions_.erase(session);
  179. active_sessions_.erase(session);
  180. }
  181. void QuicConnectivityMonitor::OnDefaultNetworkUpdated(
  182. handles::NetworkHandle default_network) {
  183. default_network_ = default_network;
  184. active_sessions_.clear();
  185. degrading_sessions_.clear();
  186. num_sessions_active_during_current_speculative_connectivity_failure_ =
  187. absl::nullopt;
  188. write_error_map_.clear();
  189. quic_error_map_.clear();
  190. }
  191. void QuicConnectivityMonitor::OnIPAddressChanged() {
  192. // If handles::NetworkHandle is supported, connectivity monitor will receive
  193. // notifications via OnDefaultNetworkUpdated.
  194. if (NetworkChangeNotifier::AreNetworkHandlesSupported())
  195. return;
  196. DCHECK_EQ(default_network_, handles::kInvalidNetworkHandle);
  197. degrading_sessions_.clear();
  198. write_error_map_.clear();
  199. }
  200. void QuicConnectivityMonitor::OnSessionGoingAwayOnIPAddressChange(
  201. QuicChromiumClientSession* session) {
  202. // This should only be called after ConnectivityMonitor gets notified via
  203. // OnIPAddressChanged().
  204. DCHECK(degrading_sessions_.empty());
  205. // |session| that encounters IP address change will lose track which network
  206. // it is bound to. Future connectivity monitoring may be misleading.
  207. session->RemoveConnectivityObserver(this);
  208. }
  209. } // namespace net