| // Copyright 2019 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 "content/browser/tracing/tracing_service_controller.h" |
| |
| #include <utility> |
| |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/time.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/service_process_host.h" |
| #include "content/public/browser/tracing_service.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "services/tracing/public/cpp/traced_process.h" |
| #include "services/tracing/public/cpp/tracing_features.h" |
| #include "services/tracing/tracing_service.h" |
| |
| namespace content { |
| |
| namespace { |
| |
| void BindNewInProcessInstance( |
| mojo::PendingReceiver<tracing::mojom::TracingService> receiver) { |
| mojo::MakeSelfOwnedReceiver(std::make_unique<tracing::TracingService>(), |
| std::move(receiver)); |
| } |
| |
| } // namespace |
| |
| TracingServiceController::ClientRegistration::ClientRegistration( |
| util::PassKey<TracingServiceController>, |
| base::OnceClosure unregister) |
| : unregister_(std::move(unregister)) {} |
| |
| TracingServiceController::ClientRegistration::~ClientRegistration() { |
| std::move(unregister_).Run(); |
| } |
| |
| TracingServiceController::TracingServiceController() = default; |
| |
| TracingServiceController::~TracingServiceController() = default; |
| |
| // static |
| TracingServiceController& TracingServiceController::Get() { |
| static base::NoDestructor<TracingServiceController> controller; |
| return *controller; |
| } |
| |
| std::unique_ptr<TracingServiceController::ClientRegistration> |
| TracingServiceController::RegisterClient(base::ProcessId pid, |
| EnableTracingCallback callback) { |
| base::OnceClosure unregister = |
| base::BindOnce(&TracingServiceController::RemoveClient, |
| base::Unretained(&TracingServiceController::Get()), pid); |
| auto registration = std::make_unique<ClientRegistration>( |
| util::PassKey<TracingServiceController>(), std::move(unregister)); |
| |
| if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| // Force registration to happen on the UI thread. |
| base::PostTask( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce(&TracingServiceController::RegisterClientOnUIThread, |
| base::Unretained(this), pid, std::move(callback))); |
| } else { |
| RegisterClientOnUIThread(pid, std::move(callback)); |
| } |
| |
| return registration; |
| } |
| |
| tracing::mojom::TracingService& TracingServiceController::GetService() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!service_) { |
| auto receiver = service_.BindNewPipeAndPassReceiver(); |
| if (base::FeatureList::IsEnabled(features::kTracingServiceInProcess)) { |
| base::ThreadPool::CreateSequencedTaskRunner( |
| {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, |
| base::WithBaseSyncPrimitives(), base::TaskPriority::USER_BLOCKING}) |
| ->PostTask(FROM_HERE, base::BindOnce(&BindNewInProcessInstance, |
| std::move(receiver))); |
| } else { |
| ServiceProcessHost::Launch( |
| std::move(receiver), |
| ServiceProcessHost::Options() |
| .WithDisplayName("Tracing Service") |
| .Pass()); |
| } |
| service_.reset_on_disconnect(); |
| |
| // Initialize the new service instance by pushing a pipe to each currently |
| // registered client, including the browser process itself. |
| std::vector<tracing::mojom::ClientInfoPtr> initial_clients; |
| mojo::PendingRemote<tracing::mojom::TracedProcess> browser_remote; |
| tracing::TracedProcess::ResetTracedProcessReceiver(); |
| tracing::TracedProcess::OnTracedProcessRequest( |
| browser_remote.InitWithNewPipeAndPassReceiver()); |
| initial_clients.push_back(tracing::mojom::ClientInfo::New( |
| base::GetCurrentProcId(), std::move(browser_remote))); |
| for (const std::pair<const base::ProcessId, EnableTracingCallback>& entry : |
| clients_) { |
| mojo::PendingRemote<tracing::mojom::TracedProcess> remote_process; |
| entry.second.Run(remote_process.InitWithNewPipeAndPassReceiver()); |
| initial_clients.push_back(tracing::mojom::ClientInfo::New( |
| /*pid=*/entry.first, std::move(remote_process))); |
| } |
| service_->Initialize(std::move(initial_clients)); |
| } |
| |
| return *service_.get(); |
| } |
| |
| void TracingServiceController::RegisterClientOnUIThread( |
| base::ProcessId pid, |
| EnableTracingCallback callback) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // If the service is currently running, immediately connect the new client. |
| if (service_) { |
| mojo::PendingRemote<tracing::mojom::TracedProcess> remote_process; |
| callback.Run(remote_process.InitWithNewPipeAndPassReceiver()); |
| service_->AddClient( |
| tracing::mojom::ClientInfo::New(pid, std::move(remote_process))); |
| } |
| |
| clients_.emplace(pid, std::move(callback)); |
| } |
| |
| void TracingServiceController::RemoveClient(base::ProcessId pid) { |
| if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { |
| base::PostTask(FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce(&TracingServiceController::RemoveClient, |
| base::Unretained(this), pid)); |
| return; |
| } |
| |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| clients_.erase(pid); |
| } |
| |
| tracing::mojom::TracingService& GetTracingService() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| return TracingServiceController::Get().GetService(); |
| } |
| |
| } // namespace content |