legacymetrics_client.cc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. // Copyright 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 "components/fuchsia_legacymetrics/legacymetrics_client.h"
  5. #include <lib/fit/function.h>
  6. #include <lib/sys/cpp/component_context.h>
  7. #include <zircon/errors.h>
  8. #include <algorithm>
  9. #include <memory>
  10. #include <utility>
  11. #include <vector>
  12. #include "base/callback_helpers.h"
  13. #include "base/fuchsia/fuchsia_logging.h"
  14. #include "base/fuchsia/process_context.h"
  15. #include "base/logging.h"
  16. #include "base/threading/thread_task_runner_handle.h"
  17. #include "base/time/time.h"
  18. #include "components/fuchsia_legacymetrics/legacymetrics_histogram_flattener.h"
  19. namespace fuchsia_legacymetrics {
  20. constexpr size_t LegacyMetricsClient::kMaxBatchSize;
  21. constexpr base::TimeDelta LegacyMetricsClient::kInitialReconnectDelay;
  22. constexpr base::TimeDelta LegacyMetricsClient::kMaxReconnectDelay;
  23. constexpr size_t LegacyMetricsClient::kReconnectBackoffFactor;
  24. LegacyMetricsClient::LegacyMetricsClient() = default;
  25. LegacyMetricsClient::~LegacyMetricsClient() {
  26. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  27. }
  28. void LegacyMetricsClient::DisableAutoConnect() {
  29. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  30. DCHECK(auto_connect_);
  31. DCHECK_EQ(report_interval_, base::TimeDelta())
  32. << "DisableAutoConnect() must be called before Start().";
  33. auto_connect_ = false;
  34. }
  35. void LegacyMetricsClient::SetMetricsRecorder(
  36. fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
  37. metrics_recorder) {
  38. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  39. DCHECK(!auto_connect_);
  40. auto weak_this = weak_factory_.GetWeakPtr();
  41. ResetMetricsRecorderState();
  42. // ResetMetricsRecorderState() may call |on_flush_complete_closures_|, which
  43. // may destroy LegacyMetricsClient.
  44. if (!weak_this)
  45. return;
  46. SetMetricsRecorderInternal(std::move(metrics_recorder));
  47. if (report_interval_.is_positive())
  48. ScheduleNextReport();
  49. }
  50. void LegacyMetricsClient::Start(base::TimeDelta report_interval) {
  51. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  52. DCHECK_GT(report_interval, base::Seconds(0));
  53. // Start recording user events.
  54. user_events_recorder_ = std::make_unique<LegacyMetricsUserActionRecorder>();
  55. report_interval_ = report_interval;
  56. if (auto_connect_)
  57. ConnectFromComponentContext();
  58. if (metrics_recorder_)
  59. ScheduleNextReport();
  60. }
  61. void LegacyMetricsClient::SetReportAdditionalMetricsCallback(
  62. ReportAdditionalMetricsCallback callback) {
  63. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  64. DCHECK(!metrics_recorder_)
  65. << "SetReportAdditionalMetricsCallback() must be called before Start().";
  66. DCHECK(!report_additional_callback_);
  67. DCHECK(callback);
  68. report_additional_callback_ = std::move(callback);
  69. }
  70. void LegacyMetricsClient::SetNotifyFlushCallback(NotifyFlushCallback callback) {
  71. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  72. DCHECK(callback);
  73. DCHECK(!metrics_recorder_)
  74. << "SetNotifyFlushCallback() must be called before Start().";
  75. notify_flush_callback_ = std::move(callback);
  76. }
  77. void LegacyMetricsClient::ConnectFromComponentContext() {
  78. DCHECK(!metrics_recorder_) << "Trying to connect when already connected.";
  79. DVLOG(1) << "Trying to connect to MetricsRecorder service.";
  80. DCHECK(auto_connect_);
  81. fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
  82. metrics_recorder;
  83. base::ComponentContextForProcess()->svc()->Connect(
  84. metrics_recorder.NewRequest());
  85. SetMetricsRecorderInternal(std::move(metrics_recorder));
  86. ScheduleNextReport();
  87. }
  88. void LegacyMetricsClient::SetMetricsRecorderInternal(
  89. fidl::InterfaceHandle<fuchsia::legacymetrics::MetricsRecorder>
  90. metrics_recorder) {
  91. metrics_recorder_.Bind(std::move(metrics_recorder));
  92. metrics_recorder_.set_error_handler(fit::bind_member(
  93. this, &LegacyMetricsClient::OnMetricsRecorderDisconnected));
  94. metrics_recorder_.events().OnCloseSoon =
  95. fit::bind_member(this, &LegacyMetricsClient::OnCloseSoon);
  96. }
  97. void LegacyMetricsClient::ScheduleNextReport() {
  98. DCHECK(!is_flushing_);
  99. if (report_timer_.IsRunning())
  100. return;
  101. DVLOG(1) << "Scheduling next report in " << report_interval_.InSeconds()
  102. << "seconds.";
  103. report_timer_.Start(FROM_HERE, report_interval_, this,
  104. &LegacyMetricsClient::StartReport);
  105. }
  106. void LegacyMetricsClient::StartReport() {
  107. if (!report_additional_callback_) {
  108. Report({});
  109. return;
  110. }
  111. report_additional_callback_.Run(
  112. base::BindOnce(&LegacyMetricsClient::Report, weak_factory_.GetWeakPtr()));
  113. }
  114. void LegacyMetricsClient::Report(
  115. std::vector<fuchsia::legacymetrics::Event> events) {
  116. DVLOG(1) << __func__ << " called.";
  117. // The connection might have dropped while additional metrics were being
  118. // collected. Continue recording events and cache them locally in memory until
  119. // connection is reestablished.
  120. if (!metrics_recorder_)
  121. return;
  122. // Include histograms.
  123. for (auto& histogram : GetLegacyMetricsDeltas()) {
  124. fuchsia::legacymetrics::Event histogram_event;
  125. histogram_event.set_histogram(std::move(histogram));
  126. events.push_back(std::move(histogram_event));
  127. }
  128. // Include user events.
  129. if (user_events_recorder_->HasEvents()) {
  130. for (auto& event : user_events_recorder_->TakeEvents()) {
  131. fuchsia::legacymetrics::Event user_event;
  132. user_event.set_user_action_event(std::move(event));
  133. events.push_back(std::move(user_event));
  134. }
  135. }
  136. std::move(events.begin(), events.end(), std::back_inserter(to_send_));
  137. DrainBuffer();
  138. }
  139. void LegacyMetricsClient::DrainBuffer() {
  140. DVLOG(1) << __func__ << " called.";
  141. if (record_ack_pending_) {
  142. // There is a Record() call already inflight. When it is acknowledged,
  143. // buffer draining will continue.
  144. return;
  145. }
  146. if (to_send_.empty()) {
  147. DVLOG(1) << "Buffer drained.";
  148. if (is_flushing_) {
  149. metrics_recorder_.Unbind();
  150. CompleteFlush();
  151. return;
  152. }
  153. ScheduleNextReport();
  154. return;
  155. }
  156. // Since ordering doesn't matter, we can efficiently drain |to_send_| by
  157. // repeatedly sending and truncating its tail.
  158. const size_t batch_size = std::min(to_send_.size(), kMaxBatchSize);
  159. const size_t batch_start_idx = to_send_.size() - batch_size;
  160. std::vector<fuchsia::legacymetrics::Event> batch;
  161. batch.resize(batch_size);
  162. std::move(to_send_.begin() + batch_start_idx, to_send_.end(), batch.begin());
  163. to_send_.resize(to_send_.size() - batch_size);
  164. record_ack_pending_ = true;
  165. metrics_recorder_->Record(std::move(batch), [this]() {
  166. record_ack_pending_ = false;
  167. // Reset the reconnect delay after a successful Record() call.
  168. reconnect_delay_ = kInitialReconnectDelay;
  169. DrainBuffer();
  170. });
  171. }
  172. void LegacyMetricsClient::OnMetricsRecorderDisconnected(zx_status_t status) {
  173. ZX_LOG(ERROR, status) << "MetricsRecorder connection lost.";
  174. if (auto_connect_ && status == ZX_ERR_PEER_CLOSED) {
  175. DVLOG(1) << "Scheduling reconnect after " << reconnect_delay_;
  176. // Try to reconnect with exponential backoff.
  177. reconnect_timer_.Start(FROM_HERE, reconnect_delay_, this,
  178. &LegacyMetricsClient::ReconnectMetricsRecorder);
  179. // Increase delay exponentially. No random jittering since we don't expect
  180. // many clients overloading the service with simultaneous reconnections.
  181. reconnect_delay_ = std::min(reconnect_delay_ * kReconnectBackoffFactor,
  182. kMaxReconnectDelay);
  183. }
  184. ResetMetricsRecorderState();
  185. }
  186. void LegacyMetricsClient::ReconnectMetricsRecorder() {
  187. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  188. DVLOG(1) << __func__ << " called.";
  189. ConnectFromComponentContext();
  190. ScheduleNextReport();
  191. }
  192. void LegacyMetricsClient::FlushAndDisconnect(
  193. base::OnceClosure on_flush_complete) {
  194. DVLOG(1) << __func__ << " called.";
  195. if (on_flush_complete)
  196. on_flush_complete_closures_.push_back(std::move(on_flush_complete));
  197. if (is_flushing_)
  198. return;
  199. report_timer_.AbandonAndStop();
  200. is_flushing_ = true;
  201. if (notify_flush_callback_) {
  202. // Defer reporting until the flush operation has finished.
  203. std::move(notify_flush_callback_)
  204. .Run(base::BindOnce(&LegacyMetricsClient::StartReport,
  205. weak_factory_.GetWeakPtr()));
  206. } else {
  207. StartReport();
  208. }
  209. }
  210. void LegacyMetricsClient::OnCloseSoon() {
  211. FlushAndDisconnect(base::OnceClosure());
  212. }
  213. void LegacyMetricsClient::CompleteFlush() {
  214. DCHECK(is_flushing_);
  215. is_flushing_ = false;
  216. // One of the callbacks may destroy |this|, so move them all to the stack
  217. // first.
  218. std::vector<base::OnceClosure> on_flush_complete_closures;
  219. on_flush_complete_closures.swap(on_flush_complete_closures_);
  220. for (auto& closure : on_flush_complete_closures) {
  221. std::move(closure).Run();
  222. }
  223. }
  224. void LegacyMetricsClient::ResetMetricsRecorderState() {
  225. // Stop reporting metric events.
  226. report_timer_.AbandonAndStop();
  227. record_ack_pending_ = false;
  228. if (is_flushing_)
  229. CompleteFlush();
  230. }
  231. } // namespace fuchsia_legacymetrics