| Index: services/resource_coordinator/tracing/coordinator_impl.cc
|
| diff --git a/services/resource_coordinator/tracing/coordinator_impl.cc b/services/resource_coordinator/tracing/coordinator_impl.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..caf526cbb9f76826b2553f99d7efa073208c0be1
|
| --- /dev/null
|
| +++ b/services/resource_coordinator/tracing/coordinator_impl.cc
|
| @@ -0,0 +1,249 @@
|
| +// Copyright 2017 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 "services/resource_coordinator/tracing/coordinator_impl.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/callback_forward.h"
|
| +#include "base/memory/ptr_util.h"
|
| +#include "base/task_scheduler/post_task.h"
|
| +#include "base/task_scheduler/task_traits.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "base/trace_event/trace_config.h"
|
| +#include "mojo/common/data_pipe_utils.h"
|
| +#include "services/resource_coordinator/public/cpp/tracing/chrome_agent.h"
|
| +#include "services/resource_coordinator/tracing/agent_set_impl.h"
|
| +#include "services/resource_coordinator/tracing/recorder_impl.h"
|
| +#include "services/service_manager/public/cpp/bind_source_info.h"
|
| +
|
| +namespace {
|
| +
|
| +resource_coordinator::tracing::CoordinatorImpl* g_coordinator_impl;
|
| +
|
| +} // namespace
|
| +
|
| +namespace resource_coordinator {
|
| +namespace tracing {
|
| +
|
| +const char CoordinatorImpl::kRequestBufferUsageType[] = "RequestBufferUsage";
|
| +
|
| +// static
|
| +CoordinatorImpl* CoordinatorImpl::GetInstance() {
|
| + return g_coordinator_impl;
|
| +}
|
| +
|
| +CoordinatorImpl::CoordinatorImpl()
|
| + : binding_(this), task_runner_(base::ThreadTaskRunnerHandle::Get()) {
|
| + DCHECK(!g_coordinator_impl);
|
| + g_coordinator_impl = this;
|
| + background_task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
|
| + base::TaskTraits().MayBlock().WithBaseSyncPrimitives().WithPriority(
|
| + base::TaskPriority::BACKGROUND));
|
| + agent_set_ = base::MakeUnique<AgentSetImpl>();
|
| + mojom::AgentSetPtr agent_set_interface_ptr;
|
| + agent_set_->BindAgentSetRequest(service_manager::BindSourceInfo(),
|
| + mojo::MakeRequest(&agent_set_interface_ptr));
|
| + resource_coordinator::tracing::ChromeAgent::InitializeIfNeeded(
|
| + std::move(agent_set_interface_ptr));
|
| +}
|
| +
|
| +CoordinatorImpl::~CoordinatorImpl() {
|
| + g_coordinator_impl = nullptr;
|
| +}
|
| +
|
| +void CoordinatorImpl::BindCoordinatorRequest(
|
| + mojom::CoordinatorRequest request) {
|
| + binding_.Bind(std::move(request));
|
| +}
|
| +
|
| +void CoordinatorImpl::StartTracing(mojo::ScopedDataPipeProducerHandle stream,
|
| + const std::string& config) {
|
| + DCHECK(!stream_.is_valid());
|
| + config_ = config;
|
| + stream_ = std::move(stream);
|
| + first_recorder_update_ = true;
|
| + recording_label_.clear();
|
| + agent_set_->SetAgentProcessingCallback(base::Bind(
|
| + &CoordinatorImpl::SendStartTracingToAgent, base::Unretained(this)));
|
| +}
|
| +
|
| +void CoordinatorImpl::StopAndFlush() {
|
| + DCHECK(IsTracing());
|
| + agent_set_->ResetAgentProcessingCallback();
|
| + agent_set_->ForAllAgents(
|
| + [](AgentSetImpl::Entry* entry) { entry->agent()->StopAndFlush(); });
|
| + if (recorders_.size() == 0)
|
| + OnFlushDone();
|
| +}
|
| +
|
| +void CoordinatorImpl::IsTracing(const IsTracingCallback& callback) {
|
| + callback.Run(IsTracing());
|
| +}
|
| +
|
| +bool CoordinatorImpl::IsTracing() {
|
| + return stream_.is_valid() || !get_categories_callback_.is_null();
|
| +}
|
| +
|
| +void CoordinatorImpl::RequestBufferUsage(
|
| + const RequestBufferUsageCallback& callback) {
|
| + if (!request_buffer_usage_callback_.is_null()) {
|
| + callback.Run(false, 0, 0);
|
| + return;
|
| + }
|
| +
|
| + maximum_trace_buffer_usage_ = 0;
|
| + approximate_event_count_ = 0;
|
| + request_buffer_usage_callback_ = callback;
|
| + pending_request_buffer_status_count_ = 0;
|
| + agent_set_->ForAllAgents([this](AgentSetImpl::Entry* entry) {
|
| + entry->AddDisconnectClosure(
|
| + kRequestBufferUsageType,
|
| + base::BindOnce(&CoordinatorImpl::OnRequestBufferStatusResponse,
|
| + base::Unretained(this), entry, 0, 0));
|
| + entry->agent()->RequestBufferStatus(
|
| + base::BindRepeating(&CoordinatorImpl::OnRequestBufferStatusResponse,
|
| + base::Unretained(this), entry));
|
| + pending_request_buffer_status_count_++;
|
| + });
|
| +}
|
| +
|
| +void CoordinatorImpl::OnRequestBufferStatusResponse(AgentSetImpl::Entry* entry,
|
| + uint32_t capacity,
|
| + uint32_t count) {
|
| + if (!entry->RemoveDisconnectClosure(kRequestBufferUsageType))
|
| + return;
|
| +
|
| + DCHECK(pending_request_buffer_status_count_);
|
| + if (capacity > 0) {
|
| + float percent_full =
|
| + static_cast<float>(static_cast<double>(count) / capacity);
|
| + maximum_trace_buffer_usage_ =
|
| + std::max(maximum_trace_buffer_usage_, percent_full);
|
| + approximate_event_count_ += count;
|
| + }
|
| +
|
| + if (--pending_request_buffer_status_count_ == 0) {
|
| + request_buffer_usage_callback_.Run(true, maximum_trace_buffer_usage_,
|
| + approximate_event_count_);
|
| + request_buffer_usage_callback_.Reset();
|
| + }
|
| +}
|
| +
|
| +void CoordinatorImpl::SendStartTracingToAgent(AgentSetImpl::Entry* entry) {
|
| + bool is_array = entry->type() == mojom::TraceDataType::ARRAY;
|
| + mojom::RecorderPtr ptr;
|
| + recorders_[entry->label()].insert(base::MakeUnique<RecorderImpl>(
|
| + MakeRequest(&ptr), is_array,
|
| + base::BindRepeating(&CoordinatorImpl::OnRecorderUpdated,
|
| + base::Unretained(this), entry->label()),
|
| + background_task_runner_));
|
| + DCHECK(is_array || recorders_[entry->label()].size() == 1);
|
| + entry->agent()->StartTracing(config_, std::move(ptr),
|
| + !get_categories_callback_.is_null(),
|
| + base::Closure());
|
| +}
|
| +
|
| +void CoordinatorImpl::OnFlushDone() {
|
| + if (!get_categories_callback_.is_null()) {
|
| + std::set<std::string> category_set;
|
| + for (const auto& key_value : recorders_) {
|
| + for (const auto& recorder : key_value.second) {
|
| + const auto& recorder_categories = recorder->category_set();
|
| + category_set.insert(recorder_categories.begin(),
|
| + recorder_categories.end());
|
| + }
|
| + }
|
| +
|
| + std::string categories_str;
|
| + bool first = true;
|
| + for (const auto& category : category_set) {
|
| + if (!first)
|
| + categories_str += ",";
|
| + first = false;
|
| + categories_str += category;
|
| + }
|
| + get_categories_callback_.Run(categories_str);
|
| + get_categories_callback_.Reset();
|
| + }
|
| +
|
| + recorders_.clear();
|
| + stream_.reset();
|
| +}
|
| +
|
| +void CoordinatorImpl::OnRecorderUpdated(const std::string& label) {
|
| + DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
|
| + if (!recording_label_.empty() && recording_label_ != label)
|
| + return;
|
| + while (true) {
|
| + if (!recording_label_.empty() && ReadFromRecorders())
|
| + return;
|
| + recording_label_.clear();
|
| + bool all_finished = true;
|
| + for (const auto& key_value : recorders_) {
|
| + for (const auto& recorder : key_value.second) {
|
| + all_finished &= !recorder->is_recording();
|
| + if (recorder->data().size() > 0) {
|
| + recording_label_ = key_value.first;
|
| + write_label_ = true;
|
| + break;
|
| + }
|
| + }
|
| + if (recording_label_.size() != 0)
|
| + break;
|
| + }
|
| + if (recording_label_.size() == 0) {
|
| + // No recorder has any data for us, right now.
|
| + if (all_finished) {
|
| + if (!first_recorder_update_)
|
| + mojo::common::BlockingCopyFromString("}", stream_);
|
| + // Recorder connections should be closed on their binding thread.
|
| + task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&CoordinatorImpl::OnFlushDone, base::Unretained(this)));
|
| + }
|
| + return;
|
| + }
|
| + }
|
| +}
|
| +
|
| +bool CoordinatorImpl::ReadFromRecorders() {
|
| + DCHECK(background_task_runner_->RunsTasksOnCurrentThread());
|
| + bool all_finished = true;
|
| + bool is_array = (*recorders_[recording_label_].begin())->is_array();
|
| + for (const auto& recorder : recorders_[recording_label_]) {
|
| + all_finished &= !recorder->is_recording();
|
| + if (recorder->data().size() == 0)
|
| + continue;
|
| + DCHECK(stream_.is_valid());
|
| + if (write_label_) {
|
| + std::string prefix = first_recorder_update_ ? "{\"" : "\"";
|
| + prefix += recording_label_ + "\":" + (is_array ? "[" : "\"");
|
| + mojo::common::BlockingCopyFromString(prefix, stream_);
|
| + write_label_ = false;
|
| + first_recorder_update_ = false;
|
| + } else {
|
| + if (recorder->is_array())
|
| + mojo::common::BlockingCopyFromString(",", stream_);
|
| + }
|
| + mojo::common::BlockingCopyFromString(recorder->data(), stream_);
|
| + recorder->clear_data();
|
| + }
|
| + if (all_finished) {
|
| + if (!write_label_)
|
| + mojo::common::BlockingCopyFromString(is_array ? "]" : "\"", stream_);
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void CoordinatorImpl::GetCategories(const GetCategoriesCallback& callback) {
|
| + get_categories_callback_ = callback;
|
| + StartTracing(mojo::ScopedDataPipeProducerHandle(),
|
| + base::trace_event::TraceConfig("*", "").ToString());
|
| + StopAndFlush();
|
| +}
|
| +
|
| +} // namespace tracing
|
| +} // namespace resource_coordinator
|
|
|