blob: c3f0658f73dc3dcd21a6e12d4f08b1b2e82f01cd [file] [log] [blame]
// 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 "chrome/browser/chromeos/arc/tracing/arc_tracing_bridge.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_service_manager.h"
#include "content/public/browser/browser_thread.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace arc {
namespace {
// The prefix of the categories to be shown on the trace selection UI.
// The space at the end of the string is intentional as the separator between
// the prefix and the real categories.
constexpr char kCategoryPrefix[] = TRACE_DISABLED_BY_DEFAULT("android ");
// Singleton factory for ArcTracingBridge.
class ArcTracingBridgeFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
ArcTracingBridge,
ArcTracingBridgeFactory> {
public:
// Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
static constexpr const char* kName = "ArcTracingBridgeFactory";
static ArcTracingBridgeFactory* GetInstance() {
return base::Singleton<ArcTracingBridgeFactory>::get();
}
private:
friend base::DefaultSingletonTraits<ArcTracingBridgeFactory>;
ArcTracingBridgeFactory() = default;
~ArcTracingBridgeFactory() override = default;
};
} // namespace
struct ArcTracingBridge::Category {
// The name used by Android to trigger tracing.
std::string name;
// The full name shown in the tracing UI in chrome://tracing.
std::string full_name;
};
// static
ArcTracingBridge* ArcTracingBridge::GetForBrowserContext(
content::BrowserContext* context) {
return ArcTracingBridgeFactory::GetForBrowserContext(context);
}
ArcTracingBridge::ArcTracingBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service)
: arc_bridge_service_(bridge_service), weak_ptr_factory_(this) {
arc_bridge_service_->tracing()->AddObserver(this);
content::ArcTracingAgent::GetInstance()->SetDelegate(this);
}
ArcTracingBridge::~ArcTracingBridge() {
content::ArcTracingAgent::GetInstance()->SetDelegate(nullptr);
arc_bridge_service_->tracing()->RemoveObserver(this);
}
void ArcTracingBridge::OnConnectionReady() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
mojom::TracingInstance* tracing_instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->tracing(), QueryAvailableCategories);
if (!tracing_instance)
return;
tracing_instance->QueryAvailableCategories(base::Bind(
&ArcTracingBridge::OnCategoriesReady, weak_ptr_factory_.GetWeakPtr()));
}
void ArcTracingBridge::OnCategoriesReady(
const std::vector<std::string>& categories) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// There is no API in TraceLog to remove a category from the UI. As an
// alternative, the old category that is no longer in |categories_| will be
// ignored when calling |StartTracing|.
categories_.clear();
for (const auto& category : categories) {
categories_.emplace_back(Category{category, kCategoryPrefix + category});
// Show the category name in the selection UI.
base::trace_event::TraceLog::GetCategoryGroupEnabled(
categories_.back().full_name.c_str());
}
}
bool ArcTracingBridge::StartTracing(
const base::trace_event::TraceConfig& trace_config,
base::ScopedFD write_fd,
const StartTracingCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
mojom::TracingInstance* tracing_instance =
ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->tracing(), StartTracing);
if (!tracing_instance) {
// Use PostTask as the convention of TracingAgent. The caller expects
// callback to be called after this function returns.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(callback, false));
return false;
}
std::vector<std::string> selected_categories;
for (const auto& category : categories_) {
if (trace_config.IsCategoryGroupEnabled(category.full_name))
selected_categories.push_back(category.name);
}
tracing_instance->StartTracing(selected_categories,
mojo::WrapPlatformFile(write_fd.release()),
callback);
return true;
}
void ArcTracingBridge::StopTracing(const StopTracingCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
mojom::TracingInstance* tracing_instance =
ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->tracing(), StopTracing);
if (!tracing_instance) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(callback, false));
return;
}
tracing_instance->StopTracing(callback);
}
} // namespace arc