blob: 431c2f73cf21bda1bac2056e0d2d75adaab675d9 [file] [log] [blame]
Kevin Marshall05e29bd2020-03-19 21:55:441// 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
5#include "fuchsia/base/legacymetrics_client.h"
6
7#include <lib/fit/function.h>
8#include <lib/sys/cpp/component_context.h>
Akira Baruah7b75418b2020-12-11 01:35:099#include <zircon/errors.h>
10
Kevin Marshallebe60c42020-04-14 22:01:2411#include <algorithm>
Kevin Marshall05e29bd2020-03-19 21:55:4412#include <memory>
13#include <utility>
14#include <vector>
15
Kevin Marshall05e29bd2020-03-19 21:55:4416#include "base/fuchsia/fuchsia_logging.h"
Sharon Yangb2ff20e2020-06-19 12:54:0117#include "base/fuchsia/process_context.h"
Kevin Marshall05e29bd2020-03-19 21:55:4418#include "base/logging.h"
19#include "base/threading/thread_task_runner_handle.h"
20#include "base/time/time.h"
21#include "fuchsia/base/legacymetrics_histogram_flattener.h"
22
23namespace cr_fuchsia {
24
Kevin Marshallebe60c42020-04-14 22:01:2425constexpr size_t LegacyMetricsClient::kMaxBatchSize;
26
Akira Baruah7b75418b2020-12-11 01:35:0927constexpr base::TimeDelta LegacyMetricsClient::kInitialReconnectDelay;
28constexpr base::TimeDelta LegacyMetricsClient::kMaxReconnectDelay;
29constexpr size_t LegacyMetricsClient::kReconnectBackoffFactor;
30
Kevin Marshall05e29bd2020-03-19 21:55:4431LegacyMetricsClient::LegacyMetricsClient() = default;
32
33LegacyMetricsClient::~LegacyMetricsClient() {
34 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
35}
36
37void LegacyMetricsClient::Start(base::TimeDelta report_interval) {
38 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
39 DCHECK_GT(report_interval, base::TimeDelta::FromSeconds(0));
Akira Baruah7b75418b2020-12-11 01:35:0940
41 // Start recording user events.
42 user_events_recorder_ = std::make_unique<LegacyMetricsUserActionRecorder>();
Kevin Marshall05e29bd2020-03-19 21:55:4443
44 report_interval_ = report_interval;
Akira Baruah7b75418b2020-12-11 01:35:0945 ConnectAndStartReporting();
Kevin Marshall05e29bd2020-03-19 21:55:4446}
47
48void LegacyMetricsClient::SetReportAdditionalMetricsCallback(
49 ReportAdditionalMetricsCallback callback) {
50 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
51 DCHECK(!metrics_recorder_)
52 << "SetReportAdditionalMetricsCallback() must be called before Start().";
53 DCHECK(!report_additional_callback_);
54 DCHECK(callback);
55
56 report_additional_callback_ = std::move(callback);
57}
58
Kevin Marshallaa7f5162020-06-04 23:32:3159void LegacyMetricsClient::SetNotifyFlushCallback(NotifyFlushCallback callback) {
60 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
61 DCHECK(callback);
62 DCHECK(!metrics_recorder_)
63 << "SetNotifyFlushCallback() must be called before Start().";
64
65 notify_flush_callback_ = std::move(callback);
66}
67
Akira Baruah7b75418b2020-12-11 01:35:0968void LegacyMetricsClient::ConnectAndStartReporting() {
69 DCHECK(!metrics_recorder_) << "Trying to connect when already connected.";
70 DVLOG(1) << "Trying to connect to MetricsRecorder service.";
71 metrics_recorder_ = base::ComponentContextForProcess()
72 ->svc()
73 ->Connect<fuchsia::legacymetrics::MetricsRecorder>();
74 metrics_recorder_.set_error_handler(fit::bind_member(
75 this, &LegacyMetricsClient::OnMetricsRecorderDisconnected));
76 metrics_recorder_.events().OnCloseSoon =
77 fit::bind_member(this, &LegacyMetricsClient::OnCloseSoon);
78 ScheduleNextReport();
79}
80
Kevin Marshall05e29bd2020-03-19 21:55:4481void LegacyMetricsClient::ScheduleNextReport() {
Kevin Marshallaa7f5162020-06-04 23:32:3182 DCHECK(!is_flushing_);
83
Kevin Marshall05e29bd2020-03-19 21:55:4484 DVLOG(1) << "Scheduling next report in " << report_interval_.InSeconds()
85 << "seconds.";
Akira Baruah7b75418b2020-12-11 01:35:0986 report_timer_.Start(FROM_HERE, report_interval_, this,
87 &LegacyMetricsClient::StartReport);
Kevin Marshall05e29bd2020-03-19 21:55:4488}
89
Kevin Marshallebe60c42020-04-14 22:01:2490void LegacyMetricsClient::StartReport() {
91 if (!report_additional_callback_) {
92 Report({});
93 return;
94 }
95 report_additional_callback_.Run(
96 base::BindOnce(&LegacyMetricsClient::Report, weak_factory_.GetWeakPtr()));
97}
98
99void LegacyMetricsClient::Report(
100 std::vector<fuchsia::legacymetrics::Event> events) {
Kevin Marshallebe60c42020-04-14 22:01:24101 DVLOG(1) << __func__ << " called.";
Kevin Marshall05e29bd2020-03-19 21:55:44102
Kevin Marshalla65430552020-11-18 19:55:53103 // The connection might have dropped while additional metrics were being
Akira Baruah7b75418b2020-12-11 01:35:09104 // collected. Continue recording events and cache them locally in memory until
105 // connection is reestablished.
Kevin Marshalla65430552020-11-18 19:55:53106 if (!metrics_recorder_)
107 return;
108
Kevin Marshall05e29bd2020-03-19 21:55:44109 // Include histograms.
110 for (auto& histogram : GetLegacyMetricsDeltas()) {
111 fuchsia::legacymetrics::Event histogram_event;
112 histogram_event.set_histogram(std::move(histogram));
113 events.push_back(std::move(histogram_event));
114 }
115
116 // Include user events.
117 if (user_events_recorder_->HasEvents()) {
118 for (auto& event : user_events_recorder_->TakeEvents()) {
119 fuchsia::legacymetrics::Event user_event;
120 user_event.set_user_action_event(std::move(event));
121 events.push_back(std::move(user_event));
122 }
123 }
124
Kevin Marshallaa7f5162020-06-04 23:32:31125 std::move(events.begin(), events.end(), std::back_inserter(to_send_));
Kevin Marshall05e29bd2020-03-19 21:55:44126
Kevin Marshallaa7f5162020-06-04 23:32:31127 DrainBuffer();
Kevin Marshallebe60c42020-04-14 22:01:24128}
129
Kevin Marshallaa7f5162020-06-04 23:32:31130void LegacyMetricsClient::DrainBuffer() {
131 DVLOG(1) << __func__ << " called.";
132
133 if (record_ack_pending_) {
134 // There is a Record() call already inflight. When it is acknowledged,
135 // buffer draining will continue.
Kevin Marshallebe60c42020-04-14 22:01:24136 return;
137 }
138
Kevin Marshallaa7f5162020-06-04 23:32:31139 if (to_send_.empty()) {
140 DVLOG(1) << "Buffer drained.";
141
142 if (is_flushing_) {
143 metrics_recorder_.Unbind();
Hai Bid0224f92020-10-19 23:47:48144 std::move(on_flush_complete_).Run();
Kevin Marshallaa7f5162020-06-04 23:32:31145 } else {
146 ScheduleNextReport();
147 }
148
149 return;
150 }
151
152 // Since ordering doesn't matter, we can efficiently drain |to_send_| by
Kevin Marshallebe60c42020-04-14 22:01:24153 // repeatedly sending and truncating its tail.
Kevin Marshallaa7f5162020-06-04 23:32:31154 const size_t batch_size = std::min(to_send_.size(), kMaxBatchSize);
155 const size_t batch_start_idx = to_send_.size() - batch_size;
Kevin Marshallebe60c42020-04-14 22:01:24156 std::vector<fuchsia::legacymetrics::Event> batch;
157 batch.resize(batch_size);
Kevin Marshallaa7f5162020-06-04 23:32:31158 std::move(to_send_.begin() + batch_start_idx, to_send_.end(), batch.begin());
159 to_send_.resize(to_send_.size() - batch_size);
Kevin Marshallebe60c42020-04-14 22:01:24160
Kevin Marshallaa7f5162020-06-04 23:32:31161 record_ack_pending_ = true;
162 metrics_recorder_->Record(std::move(batch), [this]() {
163 record_ack_pending_ = false;
Akira Baruah7b75418b2020-12-11 01:35:09164
165 // Reset the reconnect delay after a successful Record() call.
166 reconnect_delay_ = kInitialReconnectDelay;
167
Kevin Marshallaa7f5162020-06-04 23:32:31168 DrainBuffer();
169 });
Kevin Marshall05e29bd2020-03-19 21:55:44170}
171
172void LegacyMetricsClient::OnMetricsRecorderDisconnected(zx_status_t status) {
Wez1f809762020-06-11 19:08:44173 ZX_LOG(ERROR, status) << "MetricsRecorder connection lost.";
Kevin Marshall05e29bd2020-03-19 21:55:44174
Akira Baruah7b75418b2020-12-11 01:35:09175 // Stop reporting metric events.
176 report_timer_.AbandonAndStop();
177
178 if (status == ZX_ERR_PEER_CLOSED) {
179 DVLOG(1) << "Scheduling reconnect after " << reconnect_delay_;
180
181 // Try to reconnect with exponential backoff.
182 reconnect_timer_.Start(FROM_HERE, reconnect_delay_, this,
183 &LegacyMetricsClient::ConnectAndStartReporting);
184
185 // Increase delay exponentially. No random jittering since we don't expect
186 // many clients overloading the service with simultaneous reconnections.
187 reconnect_delay_ = std::min(reconnect_delay_ * kReconnectBackoffFactor,
188 kMaxReconnectDelay);
189 }
Kevin Marshall05e29bd2020-03-19 21:55:44190}
191
Hai Bid0224f92020-10-19 23:47:48192void LegacyMetricsClient::FlushAndDisconnect(
193 base::OnceClosure on_flush_complete) {
Kevin Marshallaa7f5162020-06-04 23:32:31194 DVLOG(1) << __func__ << " called.";
Hai Bid0224f92020-10-19 23:47:48195 DCHECK(on_flush_complete);
196 if (is_flushing_)
197 return;
Kevin Marshallaa7f5162020-06-04 23:32:31198
Hai Bid0224f92020-10-19 23:47:48199 on_flush_complete_ = std::move(on_flush_complete);
Akira Baruah7b75418b2020-12-11 01:35:09200 report_timer_.AbandonAndStop();
Kevin Marshallaa7f5162020-06-04 23:32:31201
202 is_flushing_ = true;
203 if (notify_flush_callback_) {
204 // Defer reporting until the flush operation has finished.
205 std::move(notify_flush_callback_)
206 .Run(base::BindOnce(&LegacyMetricsClient::StartReport,
207 weak_factory_.GetWeakPtr()));
208 } else {
209 StartReport();
210 }
211}
212
Hai Bid0224f92020-10-19 23:47:48213void LegacyMetricsClient::OnCloseSoon() {
214 FlushAndDisconnect(base::DoNothing::Once());
215}
216
Kevin Marshall05e29bd2020-03-19 21:55:44217} // namespace cr_fuchsia