// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "fuchsia/base/legacymetrics_client.h" #include #include #include #include #include #include #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/process_context.h" #include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "fuchsia/base/legacymetrics_histogram_flattener.h" namespace cr_fuchsia { constexpr size_t LegacyMetricsClient::kMaxBatchSize; LegacyMetricsClient::LegacyMetricsClient() = default; LegacyMetricsClient::~LegacyMetricsClient() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } void LegacyMetricsClient::Start(base::TimeDelta report_interval) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_GT(report_interval, base::TimeDelta::FromSeconds(0)); DCHECK(!metrics_recorder_) << "Start() called more than once."; report_interval_ = report_interval; metrics_recorder_ = base::ComponentContextForProcess() ->svc() ->Connect(); metrics_recorder_.set_error_handler(fit::bind_member( this, &LegacyMetricsClient::OnMetricsRecorderDisconnected)); metrics_recorder_.events().OnCloseSoon = fit::bind_member(this, &LegacyMetricsClient::OnCloseSoon); user_events_recorder_ = std::make_unique(); ScheduleNextReport(); } void LegacyMetricsClient::SetReportAdditionalMetricsCallback( ReportAdditionalMetricsCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!metrics_recorder_) << "SetReportAdditionalMetricsCallback() must be called before Start()."; DCHECK(!report_additional_callback_); DCHECK(callback); report_additional_callback_ = std::move(callback); } void LegacyMetricsClient::SetNotifyFlushCallback(NotifyFlushCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(callback); DCHECK(!metrics_recorder_) << "SetNotifyFlushCallback() must be called before Start()."; notify_flush_callback_ = std::move(callback); } void LegacyMetricsClient::ScheduleNextReport() { DCHECK(!is_flushing_); DVLOG(1) << "Scheduling next report in " << report_interval_.InSeconds() << "seconds."; timer_.Start(FROM_HERE, report_interval_, this, &LegacyMetricsClient::StartReport); } void LegacyMetricsClient::StartReport() { if (!report_additional_callback_) { Report({}); return; } report_additional_callback_.Run( base::BindOnce(&LegacyMetricsClient::Report, weak_factory_.GetWeakPtr())); } void LegacyMetricsClient::Report( std::vector events) { DCHECK(metrics_recorder_); DVLOG(1) << __func__ << " called."; // Include histograms. for (auto& histogram : GetLegacyMetricsDeltas()) { fuchsia::legacymetrics::Event histogram_event; histogram_event.set_histogram(std::move(histogram)); events.push_back(std::move(histogram_event)); } // Include user events. if (user_events_recorder_->HasEvents()) { for (auto& event : user_events_recorder_->TakeEvents()) { fuchsia::legacymetrics::Event user_event; user_event.set_user_action_event(std::move(event)); events.push_back(std::move(user_event)); } } std::move(events.begin(), events.end(), std::back_inserter(to_send_)); DrainBuffer(); } void LegacyMetricsClient::DrainBuffer() { DVLOG(1) << __func__ << " called."; if (record_ack_pending_) { // There is a Record() call already inflight. When it is acknowledged, // buffer draining will continue. return; } if (to_send_.empty()) { DVLOG(1) << "Buffer drained."; if (is_flushing_) { metrics_recorder_.Unbind(); } else { ScheduleNextReport(); } return; } // Since ordering doesn't matter, we can efficiently drain |to_send_| by // repeatedly sending and truncating its tail. const size_t batch_size = std::min(to_send_.size(), kMaxBatchSize); const size_t batch_start_idx = to_send_.size() - batch_size; std::vector batch; batch.resize(batch_size); std::move(to_send_.begin() + batch_start_idx, to_send_.end(), batch.begin()); to_send_.resize(to_send_.size() - batch_size); record_ack_pending_ = true; metrics_recorder_->Record(std::move(batch), [this]() { record_ack_pending_ = false; DrainBuffer(); }); } void LegacyMetricsClient::OnMetricsRecorderDisconnected(zx_status_t status) { ZX_LOG(ERROR, status) << "MetricsRecorder connection lost."; // Stop recording & reporting user events. user_events_recorder_.reset(); timer_.AbandonAndStop(); } void LegacyMetricsClient::OnCloseSoon() { DVLOG(1) << __func__ << " called."; timer_.AbandonAndStop(); is_flushing_ = true; if (notify_flush_callback_) { // Defer reporting until the flush operation has finished. std::move(notify_flush_callback_) .Run(base::BindOnce(&LegacyMetricsClient::StartReport, weak_factory_.GetWeakPtr())); } else { StartReport(); } } } // namespace cr_fuchsia