Commit edd383fb authored by Peter Marshall's avatar Peter Marshall Committed by Commit Bot

[tracing] Use the new perfetto client API

The client API provides a much simpler interface so that we don't have
to deal with producers, consumers etc. directly. This CL removes all the
code that dealt with the more complex API used previously.

The architecture used here requires that the embedder call into
Tracing::Initialize() to set up the tracing backend. The tracing
controller then connects to this backend when calling
DataSource::Register() and Tracing::NewTrace(). This will ultimately
avoid the need for a virtual call (or two) for every trace event that
need to be dispatched over the API - chrome can provide a backend
and V8 will connect to it opaquely with the same code when tracing is
enabled.

Cq-Include-Trybots: luci.v8.try:v8_linux64_perfetto_dbg_ng
Bug: v8:8339
Change-Id: I6b74fbb49ffcc89638caeb59ed3d5cc81238f3e8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1634916Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62568}
parent 326f2378
......@@ -3682,21 +3682,15 @@ v8_component("v8_libplatform") {
sources += [
"src/libplatform/tracing/json-trace-event-listener.cc",
"src/libplatform/tracing/json-trace-event-listener.h",
"src/libplatform/tracing/perfetto-consumer.cc",
"src/libplatform/tracing/perfetto-consumer.h",
"src/libplatform/tracing/perfetto-producer.cc",
"src/libplatform/tracing/perfetto-producer.h",
"src/libplatform/tracing/perfetto-shared-memory.cc",
"src/libplatform/tracing/perfetto-shared-memory.h",
"src/libplatform/tracing/perfetto-tasks.cc",
"src/libplatform/tracing/perfetto-tasks.h",
"src/libplatform/tracing/perfetto-tracing-controller.cc",
"src/libplatform/tracing/perfetto-tracing-controller.h",
"src/libplatform/tracing/trace-event-listener.cc",
"src/libplatform/tracing/trace-event-listener.h",
]
deps += [
"//third_party/perfetto:libperfetto",
"//third_party/perfetto/protos/perfetto/trace:lite",
"//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite",
"//third_party/perfetto/protos/perfetto/trace/chrome:zero",
"//third_party/perfetto/src/tracing:client_api",
"//third_party/perfetto/src/tracing:platform_posix",
]
}
}
......@@ -4164,6 +4158,10 @@ v8_executable("d8") {
if (v8_enable_vtunejit) {
deps += [ "src/third_party/vtune:v8_vtune" ]
}
if (v8_use_perfetto) {
deps += [ "//third_party/perfetto/include/perfetto/tracing" ]
}
}
v8_executable("v8_hello_world") {
......
......@@ -233,7 +233,7 @@ deps = {
'v8/test/wasm-js/data':
Var('chromium_url') + '/external/github.com/WebAssembly/spec.git' + '@' + '666dc4cb8d4a81d386a7a716000bb85fbbbd06a2',
'v8/third_party/perfetto':
Var('android_url') + '/platform/external/perfetto.git' + '@' + '10c98fe0cfae669f71610d97e9da94260a6da173',
Var('android_url') + '/platform/external/perfetto.git' + '@' + '0e8281399fd854de13461f2c1c9f2fb0b8e9c3ae',
'v8/third_party/protobuf':
Var('chromium_url') + '/external/github.com/google/protobuf'+ '@' + 'b68a347f56137b4b1a746e8c7438495a6ac1bd91',
}
......
......@@ -14,6 +14,10 @@
#include "libplatform/libplatform-export.h"
#include "v8-platform.h" // NOLINT(build/include)
namespace perfetto {
class TracingSession;
}
namespace v8 {
namespace base {
......@@ -23,8 +27,8 @@ class Mutex;
namespace platform {
namespace tracing {
class PerfettoTracingController;
class TraceEventListener;
class JSONTraceEventListener;
const int kTraceMaxNumArgs = 2;
......@@ -292,11 +296,10 @@ class V8_PLATFORM_EXPORT TracingController
std::unordered_set<v8::TracingController::TraceStateObserver*> observers_;
std::atomic_bool recording_{false};
#ifdef V8_USE_PERFETTO
std::atomic_bool perfetto_recording_{false};
std::unique_ptr<PerfettoTracingController> perfetto_tracing_controller_;
std::ostream* output_stream_ = nullptr;
std::unique_ptr<TraceEventListener> json_listener_;
std::unique_ptr<JSONTraceEventListener> json_listener_;
TraceEventListener* listener_for_testing_ = nullptr;
std::unique_ptr<perfetto::TracingSession> tracing_session_;
#endif
// Disallow copy and assign
......
......@@ -49,5 +49,6 @@ specific_include_rules = {
"d8\.cc": [
"+include/libplatform/libplatform.h",
"+include/libplatform/v8-tracing.h",
"+perfetto/tracing.h"
],
}
......@@ -48,6 +48,10 @@
#include "src/utils/utils.h"
#include "src/wasm/wasm-engine.h"
#ifdef V8_USE_PERFETTO
#include "perfetto/tracing.h"
#endif // V8_USE_PERFETTO
#ifdef V8_INTL_SUPPORT
#include "unicode/locid.h"
#endif // V8_INTL_SUPPORT
......@@ -3366,6 +3370,10 @@ int Shell::Main(int argc, char* argv[]) {
tracing->Initialize(trace_buffer);
#ifdef V8_USE_PERFETTO
perfetto::TracingInitArgs init_args;
init_args.backends = perfetto::BackendType::kInProcessBackend;
perfetto::Tracing::Initialize(init_args);
perfetto_trace_file.open("v8_perfetto_trace.json");
DCHECK(trace_file.good());
tracing->InitializeForPerfetto(&perfetto_trace_file);
......
......@@ -8,6 +8,8 @@
#include "base/trace_event/common/trace_event_common.h"
#include "perfetto/trace/chrome/chrome_trace_packet.pb.h"
#include "perfetto/trace/trace.pb.h"
#include "perfetto/tracing.h"
#include "src/base/logging.h"
#include "src/base/macros.h"
......@@ -111,7 +113,7 @@ void JSONTraceEventListener::AppendArgValue(
}
void JSONTraceEventListener::ProcessPacket(
const ::perfetto::protos::ChromeTracePacket& packet) {
const ::perfetto::protos::TracePacket& packet) {
for (const ::perfetto::protos::ChromeTraceEvent& event :
packet.chrome_events().trace_events()) {
if (append_comma_) *stream_ << ",";
......
......@@ -26,10 +26,9 @@ class JSONTraceEventListener final : public TraceEventListener {
explicit JSONTraceEventListener(std::ostream* stream);
~JSONTraceEventListener() override;
private:
void ProcessPacket(
const ::perfetto::protos::ChromeTracePacket& packet) override;
void ProcessPacket(const ::perfetto::protos::TracePacket& packet) override;
private:
// Internal implementation
void AppendJSONString(const char* str);
void AppendArgValue(const ::perfetto::protos::ChromeTraceEvent_Arg& arg);
......
// Copyright 2019 the V8 project 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 "src/libplatform/tracing/perfetto-consumer.h"
#include "perfetto/trace/chrome/chrome_trace_packet.pb.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "src/base/macros.h"
#include "src/base/platform/semaphore.h"
#include "src/libplatform/tracing/trace-event-listener.h"
namespace v8 {
namespace platform {
namespace tracing {
PerfettoConsumer::PerfettoConsumer(base::Semaphore* finished)
: finished_semaphore_(finished) {}
void PerfettoConsumer::OnTraceData(std::vector<::perfetto::TracePacket> packets,
bool has_more) {
for (const ::perfetto::TracePacket& packet : packets) {
perfetto::protos::ChromeTracePacket proto_packet;
bool success = packet.Decode(&proto_packet);
USE(success);
DCHECK(success);
for (TraceEventListener* listener : listeners_) {
listener->ProcessPacket(proto_packet);
}
}
// PerfettoTracingController::StopTracing() waits on this sempahore. This is
// so that we can ensure that this consumer has finished consuming all of the
// trace events from the buffer before the buffer is destroyed.
if (!has_more) finished_semaphore_->Signal();
}
void PerfettoConsumer::AddTraceEventListener(TraceEventListener* listener) {
listeners_.push_back(listener);
}
} // namespace tracing
} // namespace platform
} // namespace v8
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_H_
#define V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_H_
#include <memory>
#include "perfetto/tracing/core/consumer.h"
#include "perfetto/tracing/core/tracing_service.h"
#include "src/base/logging.h"
namespace perfetto {
namespace protos {
class ChromeTracePacket;
} // namespace protos
} // namespace perfetto
namespace v8 {
namespace base {
class Semaphore;
}
namespace platform {
namespace tracing {
class TraceEventListener;
// A Perfetto Consumer gets streamed trace events from the Service via
// OnTraceData(). A Consumer can be configured (via
// service_endpoint()->EnableTracing()) to listen to various different types of
// trace events. The Consumer is responsible for producing whatever tracing
// output the system should have.
// Implements the V8-specific logic for interacting with the tracing controller
// and directs trace events to the added TraceEventListeners.
class PerfettoConsumer final : public ::perfetto::Consumer {
public:
explicit PerfettoConsumer(base::Semaphore* finished);
using ServiceEndpoint = ::perfetto::TracingService::ConsumerEndpoint;
// Register a trace event listener that will receive trace events from this
// consumer. This can be called multiple times to register multiple listeners,
// but must be called before starting tracing.
void AddTraceEventListener(TraceEventListener* listener);
ServiceEndpoint* service_endpoint() const { return service_endpoint_.get(); }
void set_service_endpoint(std::unique_ptr<ServiceEndpoint> endpoint) {
service_endpoint_ = std::move(endpoint);
}
private:
// ::perfetto::Consumer implementation
void OnConnect() override {}
void OnDisconnect() override {}
void OnTracingDisabled() override {}
void OnTraceData(std::vector<::perfetto::TracePacket> packets,
bool has_more) override;
void OnDetach(bool success) override {}
void OnAttach(bool success, const ::perfetto::TraceConfig&) override {}
void OnTraceStats(bool success, const ::perfetto::TraceStats&) override {
UNREACHABLE();
}
void OnObservableEvents(const ::perfetto::ObservableEvents&) override {
UNREACHABLE();
}
std::unique_ptr<ServiceEndpoint> service_endpoint_;
base::Semaphore* finished_semaphore_;
std::vector<TraceEventListener*> listeners_;
};
} // namespace tracing
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_H_
// Copyright 2019 the V8 project 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 "src/libplatform/tracing/perfetto-producer.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/data_source_descriptor.h"
#include "perfetto/tracing/core/trace_writer.h"
#include "src/libplatform/tracing/perfetto-tasks.h"
#include "src/libplatform/tracing/perfetto-tracing-controller.h"
namespace v8 {
namespace platform {
namespace tracing {
void PerfettoProducer::OnConnect() {
::perfetto::DataSourceDescriptor ds_desc;
ds_desc.set_name("v8.trace_events");
service_endpoint_->RegisterDataSource(ds_desc);
}
void PerfettoProducer::StartDataSource(
::perfetto::DataSourceInstanceID, const ::perfetto::DataSourceConfig& cfg) {
target_buffer_ = cfg.target_buffer();
tracing_controller_->OnProducerReady();
}
void PerfettoProducer::StopDataSource(::perfetto::DataSourceInstanceID) {
target_buffer_ = 0;
}
std::unique_ptr<::perfetto::TraceWriter> PerfettoProducer::CreateTraceWriter()
const {
CHECK_NE(0, target_buffer_);
return service_endpoint_->CreateTraceWriter(target_buffer_);
}
PerfettoProducer::PerfettoProducer(
PerfettoTracingController* tracing_controller)
: tracing_controller_(tracing_controller) {}
} // namespace tracing
} // namespace platform
} // namespace v8
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_LIBPLATFORM_TRACING_PERFETTO_PRODUCER_H_
#define V8_LIBPLATFORM_TRACING_PERFETTO_PRODUCER_H_
#include <atomic>
#include <memory>
#include "perfetto/tracing/core/producer.h"
#include "perfetto/tracing/core/tracing_service.h"
#include "src/base/logging.h"
namespace v8 {
namespace platform {
namespace tracing {
class PerfettoTracingController;
class PerfettoProducer final : public ::perfetto::Producer {
public:
using ServiceEndpoint = ::perfetto::TracingService::ProducerEndpoint;
explicit PerfettoProducer(PerfettoTracingController* tracing_controller);
ServiceEndpoint* service_endpoint() const { return service_endpoint_.get(); }
void set_service_endpoint(std::unique_ptr<ServiceEndpoint> endpoint) {
service_endpoint_ = std::move(endpoint);
}
// Create a TraceWriter for the calling thread. The TraceWriter is a
// thread-local object that writes data into a buffer which is shared between
// all TraceWriters for a given PerfettoProducer instance. Can only be called
// after the StartDataSource() callback has been received from the service, as
// this provides the buffer.
std::unique_ptr<::perfetto::TraceWriter> CreateTraceWriter() const;
private:
// ::perfetto::Producer implementation
void OnConnect() override;
void OnDisconnect() override {}
void OnTracingSetup() override {}
void SetupDataSource(::perfetto::DataSourceInstanceID,
const ::perfetto::DataSourceConfig&) override {}
void StartDataSource(::perfetto::DataSourceInstanceID,
const ::perfetto::DataSourceConfig& cfg) override;
void StopDataSource(::perfetto::DataSourceInstanceID) override;
// TODO(petermarshall): Implement Flush(). A final flush happens when the
// TraceWriter object for each thread is destroyed, but this will be more
// efficient.
void Flush(::perfetto::FlushRequestID,
const ::perfetto::DataSourceInstanceID*, size_t) override {}
void ClearIncrementalState(
const ::perfetto::DataSourceInstanceID* data_source_ids,
size_t num_data_sources) override {
UNREACHABLE();
}
std::unique_ptr<ServiceEndpoint> service_endpoint_;
uint32_t target_buffer_ = 0;
PerfettoTracingController* tracing_controller_;
};
} // namespace tracing
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_PRODUCER_H_
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_LIBPLATFORM_TRACING_PERFETTO_SHARED_MEMORY_H_
#define V8_LIBPLATFORM_TRACING_PERFETTO_SHARED_MEMORY_H_
#include "perfetto/tracing/core/shared_memory.h"
#include "third_party/perfetto/include/perfetto/base/paged_memory.h"
namespace v8 {
namespace platform {
namespace tracing {
// Perfetto requires a shared memory implementation for multi-process embedders
// but V8 is single process. We implement it here using PagedMemory from
// perfetto.
class PerfettoSharedMemory : public ::perfetto::SharedMemory {
public:
explicit PerfettoSharedMemory(size_t size);
// The PagedMemory destructor will free the underlying memory when this object
// is destroyed.
void* start() const override { return paged_memory_.Get(); }
size_t size() const override { return size_; }
private:
size_t size_;
::perfetto::base::PagedMemory paged_memory_;
};
class PerfettoSharedMemoryFactory : public ::perfetto::SharedMemory::Factory {
public:
~PerfettoSharedMemoryFactory() override = default;
std::unique_ptr<::perfetto::SharedMemory> CreateSharedMemory(
size_t size) override;
};
} // namespace tracing
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_SHARED_MEMORY_H_
// Copyright 2019 the V8 project 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 "src/libplatform/tracing/perfetto-tasks.h"
#include "src/base/platform/semaphore.h"
#include "src/base/platform/time.h"
namespace v8 {
namespace platform {
namespace tracing {
PerfettoTaskRunner::PerfettoTaskRunner() : runner_(1, DefaultTimeFunction) {}
PerfettoTaskRunner::~PerfettoTaskRunner() { runner_.Terminate(); }
// static
double PerfettoTaskRunner::DefaultTimeFunction() {
return (base::TimeTicks::HighResolutionNow() - base::TimeTicks())
.InSecondsF();
}
void PerfettoTaskRunner::PostTask(std::function<void()> f) {
runner_.PostTask(base::make_unique<TracingTask>(std::move(f)));
}
void PerfettoTaskRunner::PostDelayedTask(std::function<void()> f,
uint32_t delay_ms) {
double delay_in_seconds =
delay_ms / static_cast<double>(base::Time::kMillisecondsPerSecond);
runner_.PostDelayedTask(base::make_unique<TracingTask>(std::move(f)),
delay_in_seconds);
}
bool PerfettoTaskRunner::RunsTasksOnCurrentThread() const {
return runner_.RunsTasksOnCurrentThread();
}
void PerfettoTaskRunner::FinishImmediateTasks() {
DCHECK(!RunsTasksOnCurrentThread());
base::Semaphore semaphore(0);
// PostTask has guaranteed ordering so this will be the last task executed.
runner_.PostTask(
base::make_unique<TracingTask>([&semaphore] { semaphore.Signal(); }));
semaphore.Wait();
}
} // namespace tracing
} // namespace platform
} // namespace v8
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_LIBPLATFORM_TRACING_PERFETTO_TASKS_H_
#define V8_LIBPLATFORM_TRACING_PERFETTO_TASKS_H_
#include <functional>
#include "include/v8-platform.h"
#include "perfetto/base/task_runner.h"
#include "src/libplatform/default-worker-threads-task-runner.h"
namespace v8 {
namespace platform {
namespace tracing {
class TracingTask : public Task {
public:
explicit TracingTask(std::function<void()> f) : f_(std::move(f)) {}
void Run() override { f_(); }
private:
std::function<void()> f_;
};
class PerfettoTaskRunner : public ::perfetto::base::TaskRunner {
public:
PerfettoTaskRunner();
~PerfettoTaskRunner() override;
// ::perfetto::base::TaskRunner implementation
void PostTask(std::function<void()> f) override;
void PostDelayedTask(std::function<void()> f, uint32_t delay_ms) override;
void AddFileDescriptorWatch(int fd, std::function<void()>) override {
UNREACHABLE();
}
void RemoveFileDescriptorWatch(int fd) override { UNREACHABLE(); }
bool RunsTasksOnCurrentThread() const override;
// PerfettoTaskRunner implementation
void FinishImmediateTasks();
private:
static double DefaultTimeFunction();
DefaultWorkerThreadsTaskRunner runner_;
};
} // namespace tracing
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_TASKS_H_
// Copyright 2019 the V8 project 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 "src/libplatform/tracing/perfetto-tracing-controller.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_writer.h"
#include "perfetto/tracing/core/tracing_service.h"
#include "src/libplatform/tracing/perfetto-consumer.h"
#include "src/libplatform/tracing/perfetto-producer.h"
#include "src/libplatform/tracing/perfetto-shared-memory.h"
#include "src/libplatform/tracing/perfetto-tasks.h"
#include "src/libplatform/tracing/trace-event-listener.h"
namespace v8 {
namespace platform {
namespace tracing {
PerfettoTracingController::PerfettoTracingController()
: writer_key_(base::Thread::CreateThreadLocalKey()),
producer_ready_semaphore_(0),
consumer_finished_semaphore_(0) {}
void PerfettoTracingController::StartTracing(
const ::perfetto::TraceConfig& trace_config) {
DCHECK(!task_runner_);
task_runner_ = base::make_unique<PerfettoTaskRunner>();
// The Perfetto service expects calls on the task runner thread which is why
// the setup below occurs in posted tasks.
task_runner_->PostTask([&trace_config, this] {
std::unique_ptr<::perfetto::SharedMemory::Factory> shmem_factory =
base::make_unique<PerfettoSharedMemoryFactory>();
service_ = ::perfetto::TracingService::CreateInstance(
std::move(shmem_factory), task_runner_.get());
// This allows Perfetto to recover trace events that were written by
// TraceWriters which have not yet been deleted. This allows us to keep
// TraceWriters alive past the end of tracing, rather than having to delete
// them all when tracing stops which would require synchronization on every
// trace event. Eventually we will delete TraceWriters when threads die, but
// for now we just leak all TraceWriters.
service_->SetSMBScrapingEnabled(true);
producer_ = base::make_unique<PerfettoProducer>(this);
consumer_ =
base::make_unique<PerfettoConsumer>(&consumer_finished_semaphore_);
for (TraceEventListener* listener : listeners_) {
consumer_->AddTraceEventListener(listener);
}
producer_->set_service_endpoint(service_->ConnectProducer(
producer_.get(), 0, "v8.perfetto-producer", 0, true));
consumer_->set_service_endpoint(
service_->ConnectConsumer(consumer_.get(), 0));
// We need to wait for the OnConnected() callbacks of the producer and
// consumer to be called.
consumer_->service_endpoint()->EnableTracing(trace_config);
});
producer_ready_semaphore_.Wait();
}
void PerfettoTracingController::StopTracing() {
// Finish all of the tasks such as existing AddTraceEvent calls. These
// require the data structures below to work properly, so keep them alive
// until the tasks are done.
task_runner_->FinishImmediateTasks();
task_runner_->PostTask([this] {
// Trigger shared memory buffer scraping which will get all pending trace
// events that have been written by still-living TraceWriters.
consumer_->service_endpoint()->DisableTracing();
// Trigger the consumer to finish. This can trigger multiple calls to
// PerfettoConsumer::OnTraceData(), with the final call passing has_more
// as false.
consumer_->service_endpoint()->ReadBuffers();
});
// Wait until the final OnTraceData() call with has_more=false has completed.
consumer_finished_semaphore_.Wait();
task_runner_->PostTask([this] {
consumer_.reset();
producer_.reset();
service_.reset();
});
// Finish the above task, and any callbacks that were triggered.
task_runner_->FinishImmediateTasks();
task_runner_.reset();
}
void PerfettoTracingController::AddTraceEventListener(
TraceEventListener* listener) {
listeners_.push_back(listener);
}
PerfettoTracingController::~PerfettoTracingController() {
base::Thread::DeleteThreadLocalKey(writer_key_);
}
::perfetto::TraceWriter*
PerfettoTracingController::GetOrCreateThreadLocalWriter() {
// TODO(petermarshall): Use some form of thread-local destructor so that
// repeatedly created threads don't cause excessive leaking of TraceWriters.
if (base::Thread::HasThreadLocal(writer_key_)) {
return static_cast<::perfetto::TraceWriter*>(
base::Thread::GetExistingThreadLocal(writer_key_));
}
// We leak the TraceWriter objects created for each thread. Perfetto has a
// way of getting events from leaked TraceWriters and we can avoid needing a
// lock on every trace event this way.
std::unique_ptr<::perfetto::TraceWriter> tw = producer_->CreateTraceWriter();
::perfetto::TraceWriter* writer = tw.release();
base::Thread::SetThreadLocal(writer_key_, writer);
return writer;
}
void PerfettoTracingController::OnProducerReady() {
producer_ready_semaphore_.Signal();
}
} // namespace tracing
} // namespace platform
} // namespace v8
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_LIBPLATFORM_TRACING_PERFETTO_TRACING_CONTROLLER_H_
#define V8_LIBPLATFORM_TRACING_PERFETTO_TRACING_CONTROLLER_H_
#include <atomic>
#include <fstream>
#include <memory>
#include <vector>
#include "src/base/platform/platform.h"
#include "src/base/platform/semaphore.h"
namespace perfetto {
class TraceConfig;
class TraceWriter;
class TracingService;
} // namespace perfetto
namespace v8 {
namespace platform {
namespace tracing {
class PerfettoConsumer;
class PerfettoProducer;
class PerfettoTaskRunner;
class TraceEventListener;
// This is the top-level interface for performing tracing with perfetto. The
// user of this class should call StartTracing() to start tracing, and
// StopTracing() to stop it. To write trace events, the user can obtain a
// thread-local TraceWriter object using GetOrCreateThreadLocalWriter().
class PerfettoTracingController {
public:
PerfettoTracingController();
// Blocks and sets up all required data structures for tracing. It is safe to
// call GetOrCreateThreadLocalWriter() to obtain thread-local TraceWriters for
// writing trace events once this call returns. Tracing output will be sent to
// the TraceEventListeners registered via AddTraceEventListener().
void StartTracing(const ::perfetto::TraceConfig& trace_config);
// Blocks and finishes all existing AddTraceEvent tasks. Stops the tracing
// thread.
void StopTracing();
// Register a trace event listener that will receive trace events. This can be
// called multiple times to register multiple listeners, but must be called
// before starting tracing.
void AddTraceEventListener(TraceEventListener* listener);
~PerfettoTracingController();
// Each thread that wants to trace should call this to get their TraceWriter.
// PerfettoTracingController creates and owns the TraceWriter.
::perfetto::TraceWriter* GetOrCreateThreadLocalWriter();
private:
// Signals the producer_ready_semaphore_.
void OnProducerReady();
// PerfettoProducer is the only class allowed to call OnProducerReady().
friend class PerfettoProducer;
std::unique_ptr<::perfetto::TracingService> service_;
std::unique_ptr<PerfettoProducer> producer_;
std::unique_ptr<PerfettoConsumer> consumer_;
std::unique_ptr<PerfettoTaskRunner> task_runner_;
std::vector<TraceEventListener*> listeners_;
base::Thread::LocalStorageKey writer_key_;
// A semaphore that is signalled when StartRecording is called. StartTracing
// waits on this semaphore to be notified when the tracing service is ready to
// receive trace events.
base::Semaphore producer_ready_semaphore_;
base::Semaphore consumer_finished_semaphore_;
DISALLOW_COPY_AND_ASSIGN(PerfettoTracingController);
};
} // namespace tracing
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_TRACING_CONTROLLER_H_
......@@ -2,25 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/libplatform/tracing/perfetto-shared-memory.h"
#include "src/libplatform/tracing/trace-event-listener.h"
#include "src/base/platform/platform.h"
#include "src/base/template-utils.h"
#include "perfetto/trace/trace.pb.h"
#include "src/base/logging.h"
namespace v8 {
namespace platform {
namespace tracing {
PerfettoSharedMemory::PerfettoSharedMemory(size_t size)
: size_(size),
paged_memory_(::perfetto::base::PagedMemory::Allocate(size)) {
// TODO(956543): Find a cross-platform solution.
// TODO(petermarshall): Don't assume that size is page-aligned.
}
void TraceEventListener::ParseFromArray(const std::vector<char>& array) {
perfetto::protos::Trace trace;
CHECK(trace.ParseFromArray(array.data(), static_cast<int>(array.size())));
std::unique_ptr<::perfetto::SharedMemory>
PerfettoSharedMemoryFactory::CreateSharedMemory(size_t size) {
return base::make_unique<PerfettoSharedMemory>(size);
for (int i = 0; i < trace.packet_size(); i++) {
// TODO(petermarshall): ChromeTracePacket instead.
const perfetto::protos::TracePacket& packet = trace.packet(i);
ProcessPacket(packet);
}
}
} // namespace tracing
......
......@@ -5,9 +5,11 @@
#ifndef V8_LIBPLATFORM_TRACING_TRACE_EVENT_LISTENER_H_
#define V8_LIBPLATFORM_TRACING_TRACE_EVENT_LISTENER_H_
#include <vector>
namespace perfetto {
namespace protos {
class ChromeTracePacket;
class TracePacket;
} // namespace protos
} // namespace perfetto
......@@ -23,8 +25,9 @@ namespace tracing {
class TraceEventListener {
public:
virtual ~TraceEventListener() = default;
virtual void ProcessPacket(
const ::perfetto::protos::ChromeTracePacket& packet) = 0;
virtual void ProcessPacket(const ::perfetto::protos::TracePacket& packet) = 0;
void ParseFromArray(const std::vector<char>& array);
};
} // namespace tracing
......
......@@ -16,12 +16,25 @@
#include "base/trace_event/common/trace_event_common.h"
#include "perfetto/trace/chrome/chrome_trace_event.pbzero.h"
#include "perfetto/trace/trace_packet.pbzero.h"
#include "perfetto/tracing/core/data_source_config.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
#include "perfetto/tracing/core/trace_writer.h"
#include "perfetto/tracing.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/semaphore.h"
#include "src/libplatform/tracing/json-trace-event-listener.h"
#include "src/libplatform/tracing/perfetto-tracing-controller.h"
#endif // V8_USE_PERFETTO
#ifdef V8_USE_PERFETTO
class V8DataSource : public perfetto::DataSource<V8DataSource> {
public:
void OnSetup(const SetupArgs&) override {}
void OnStart(const StartArgs&) override { started_.Signal(); }
void OnStop(const StopArgs&) override {}
static v8::base::Semaphore started_;
};
v8::base::Semaphore V8DataSource::started_{0};
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(V8DataSource);
#endif // V8_USE_PERFETTO
namespace v8 {
......@@ -171,43 +184,38 @@ uint64_t TracingController::AddTraceEventWithTimestamp(
int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
#ifdef V8_USE_PERFETTO
if (perfetto_recording_.load()) {
// Don't use COMPLETE events with perfetto - instead transform them into
// BEGIN/END pairs. This avoids the need for a thread-local stack of pending
// trace events as perfetto does not support handles into the trace buffer.
if (phase == TRACE_EVENT_PHASE_COMPLETE) phase = TRACE_EVENT_PHASE_BEGIN;
::perfetto::TraceWriter* writer =
perfetto_tracing_controller_->GetOrCreateThreadLocalWriter();
// TODO(petermarshall): We shouldn't start one packet for each event.
// We should try to bundle them together in one bundle.
auto packet = writer->NewTracePacket();
auto* trace_event_bundle = packet->set_chrome_events();
auto* trace_event = trace_event_bundle->add_trace_events();
trace_event->set_name(name);
trace_event->set_timestamp(timestamp);
trace_event->set_phase(phase);
trace_event->set_thread_id(base::OS::GetCurrentThreadId());
trace_event->set_duration(0);
trace_event->set_thread_duration(0);
if (scope) trace_event->set_scope(scope);
trace_event->set_id(id);
trace_event->set_flags(flags);
if (category_enabled_flag) {
const char* category_group_name =
GetCategoryGroupName(category_enabled_flag);
DCHECK_NOT_NULL(category_group_name);
trace_event->set_category_group_name(category_group_name);
}
trace_event->set_process_id(base::OS::GetCurrentProcessId());
trace_event->set_thread_timestamp(cpu_now_us);
trace_event->set_bind_id(bind_id);
AddArgsToTraceProto(trace_event, num_args, arg_names, arg_types, arg_values,
arg_convertables);
V8DataSource::Trace([&](V8DataSource::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
auto* trace_event_bundle = packet->set_chrome_events();
auto* trace_event = trace_event_bundle->add_trace_events();
trace_event->set_name(name);
trace_event->set_timestamp(timestamp);
trace_event->set_phase(phase);
trace_event->set_thread_id(base::OS::GetCurrentThreadId());
trace_event->set_duration(0);
trace_event->set_thread_duration(0);
if (scope) trace_event->set_scope(scope);
trace_event->set_id(id);
trace_event->set_flags(flags);
if (category_enabled_flag) {
const char* category_group_name =
GetCategoryGroupName(category_enabled_flag);
DCHECK_NOT_NULL(category_group_name);
trace_event->set_category_group_name(category_group_name);
}
trace_event->set_process_id(base::OS::GetCurrentProcessId());
trace_event->set_thread_timestamp(cpu_now_us);
trace_event->set_bind_id(bind_id);
packet->Finalize();
}
AddArgsToTraceProto(trace_event, num_args, arg_names, arg_types,
arg_values, arg_convertables);
});
#endif // V8_USE_PERFETTO
uint64_t handle = 0;
......@@ -232,15 +240,8 @@ void TracingController::UpdateTraceEventDuration(
int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
#ifdef V8_USE_PERFETTO
// TODO(petermarshall): Should we still record the end of unfinished events
// when tracing has stopped?
if (perfetto_recording_.load()) {
// TODO(petermarshall): We shouldn't start one packet for each event. We
// should try to bundle them together in one bundle.
::perfetto::TraceWriter* writer =
perfetto_tracing_controller_->GetOrCreateThreadLocalWriter();
auto packet = writer->NewTracePacket();
V8DataSource::Trace([&](V8DataSource::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
auto* trace_event_bundle = packet->set_chrome_events();
auto* trace_event = trace_event_bundle->add_trace_events();
......@@ -249,9 +250,7 @@ void TracingController::UpdateTraceEventDuration(
trace_event->set_timestamp(now_us);
trace_event->set_process_id(base::OS::GetCurrentProcessId());
trace_event->set_thread_timestamp(cpu_now_us);
packet->Finalize();
}
});
#endif // V8_USE_PERFETTO
TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle);
......@@ -277,24 +276,27 @@ const char* TracingController::GetCategoryGroupName(
void TracingController::StartTracing(TraceConfig* trace_config) {
#ifdef V8_USE_PERFETTO
perfetto_tracing_controller_ = base::make_unique<PerfettoTracingController>();
if (listener_for_testing_) {
perfetto_tracing_controller_->AddTraceEventListener(listener_for_testing_);
}
DCHECK_NOT_NULL(output_stream_);
DCHECK(output_stream_->good());
json_listener_ = base::make_unique<JSONTraceEventListener>(output_stream_);
perfetto_tracing_controller_->AddTraceEventListener(json_listener_.get());
::perfetto::TraceConfig perfetto_trace_config;
// TODO(petermarshall): Set other the params for the config.
::perfetto::TraceConfig perfetto_trace_config;
perfetto_trace_config.add_buffers()->set_size_kb(4096);
auto* ds_config = perfetto_trace_config.add_data_sources()->mutable_config();
ds_config->set_name("v8.trace_events");
// TODO(petermarshall): Set all the params from |perfetto_trace_config|.
perfetto_tracing_controller_->StartTracing(perfetto_trace_config);
perfetto_recording_.store(true);
perfetto::DataSourceDescriptor dsd;
dsd.set_name("v8.trace_events");
V8DataSource::Register(dsd);
tracing_session_ =
perfetto::Tracing::NewTrace(perfetto::BackendType::kUnspecifiedBackend);
tracing_session_->Setup(perfetto_trace_config);
// TODO(petermarshall): Switch to StartBlocking when available.
tracing_session_->Start();
V8DataSource::started_.Wait();
#endif // V8_USE_PERFETTO
trace_config_.reset(trace_config);
......@@ -327,10 +329,17 @@ void TracingController::StopTracing() {
}
#ifdef V8_USE_PERFETTO
perfetto_recording_.store(false);
perfetto_tracing_controller_->StopTracing();
perfetto_tracing_controller_.reset();
base::Semaphore stopped_{0};
tracing_session_->SetOnStopCallback([&stopped_]() { stopped_.Signal(); });
tracing_session_->Stop();
stopped_.Wait();
std::vector<char> trace = tracing_session_->ReadTraceBlocking();
json_listener_->ParseFromArray(trace);
if (listener_for_testing_) listener_for_testing_->ParseFromArray(trace);
json_listener_.reset();
#endif // V8_USE_PERFETTO
{
......
......@@ -32,6 +32,10 @@ v8_executable("cctest") {
ldflags = []
if (v8_use_perfetto) {
deps += [ "//third_party/perfetto/include/perfetto/tracing" ]
}
# TODO(machenbach): Translate from gyp.
#["OS=="aix"", {
# "ldflags": [ "-Wl,-bbigtoc" ],
......@@ -417,7 +421,11 @@ v8_source_set("cctest_sources") {
}
if (v8_use_perfetto) {
deps += [ "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite" ]
deps += [
"//third_party/perfetto/include/perfetto/tracing",
"//third_party/perfetto/protos/perfetto/trace/chrome:lite",
"//third_party/perfetto/protos/perfetto/trace/chrome:zero",
]
}
}
......
include_rules = [
"+src",
"+torque-generated"
"+torque-generated",
"+perfetto/tracing.h"
]
......@@ -39,6 +39,10 @@
#include "test/cctest/profiler-extension.h"
#include "test/cctest/trace-extension.h"
#ifdef V8_USE_PERFETTO
#include "perfetto/tracing.h"
#endif // V8_USE_PERFETTO
#if V8_OS_WIN
#include <windows.h> // NOLINT
#if V8_CC_MSVC
......@@ -270,7 +274,6 @@ static void SuggestTestHarness(int tests) {
"bogus failure. Consider using tools/run-tests.py instead.\n");
}
int main(int argc, char* argv[]) {
#if V8_OS_WIN
UINT new_flags =
......@@ -302,6 +305,12 @@ int main(int argc, char* argv[]) {
}
}
#ifdef V8_USE_PERFETTO
perfetto::TracingInitArgs init_args;
init_args.backends = perfetto::BackendType::kInProcessBackend;
perfetto::Tracing::Initialize(init_args);
#endif // V8_USE_PERFETTO
v8::V8::InitializeICUDefaultLocation(argv[0]);
std::unique_ptr<v8::Platform> platform(v8::platform::NewDefaultPlatform());
v8::V8::InitializePlatform(platform.get());
......
......@@ -11,9 +11,28 @@
#ifdef V8_USE_PERFETTO
#include "perfetto/trace/chrome/chrome_trace_event.pb.h"
#include "perfetto/trace/chrome/chrome_trace_event.pbzero.h"
#include "perfetto/trace/chrome/chrome_trace_packet.pb.h"
#include "perfetto/trace/trace.pb.h"
#include "perfetto/tracing.h"
#include "src/libplatform/tracing/json-trace-event-listener.h"
#include "src/libplatform/tracing/trace-event-listener.h"
#endif
#endif // V8_USE_PERFETTO
#ifdef V8_USE_PERFETTO
class TestDataSource : public perfetto::DataSource<TestDataSource> {
public:
void OnSetup(const SetupArgs&) override {}
void OnStart(const StartArgs&) override { started_.Signal(); }
void OnStop(const StopArgs&) override {}
static v8::base::Semaphore started_;
};
v8::base::Semaphore TestDataSource::started_{0};
PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(TestDataSource);
#endif // V8_USE_PERFETTO
namespace v8 {
namespace platform {
......@@ -552,8 +571,7 @@ struct TraceEvent {
class TestListener : public TraceEventListener {
public:
void ProcessPacket(
const ::perfetto::protos::ChromeTracePacket& packet) override {
void ProcessPacket(const ::perfetto::protos::TracePacket& packet) {
for (const ::perfetto::protos::ChromeTraceEvent& event :
packet.chrome_events().trace_events()) {
TraceEvent trace_event{event.name(), event.timestamp(),
......@@ -683,6 +701,54 @@ TEST(Perfetto) {
CHECK_EQ(6, harness.events_size());
}
TEST(TracingPerfetto) {
::perfetto::TraceConfig perfetto_trace_config;
perfetto_trace_config.add_buffers()->set_size_kb(4096);
auto* ds_config = perfetto_trace_config.add_data_sources()->mutable_config();
ds_config->set_name("v8.trace_events");
perfetto::DataSourceDescriptor dsd;
dsd.set_name("v8.trace_events");
TestDataSource::Register(dsd);
auto tracing_session_ =
perfetto::Tracing::NewTrace(perfetto::BackendType::kInProcessBackend);
tracing_session_->Setup(perfetto_trace_config);
tracing_session_->Start();
TestDataSource::started_.Wait();
for (int i = 0; i < 15; i++) {
TestDataSource::Trace([&](TestDataSource::TraceContext ctx) {
auto packet = ctx.NewTracePacket();
auto* trace_event_bundle = packet->set_chrome_events();
auto* trace_event = trace_event_bundle->add_trace_events();
trace_event->set_phase('c');
trace_event->set_thread_id(v8::base::OS::GetCurrentThreadId());
trace_event->set_timestamp(123);
trace_event->set_process_id(v8::base::OS::GetCurrentProcessId());
trace_event->set_thread_timestamp(123);
});
}
v8::base::Semaphore stopped_{0};
tracing_session_->SetOnStopCallback([&stopped_]() { stopped_.Signal(); });
tracing_session_->Stop();
stopped_.Wait();
std::ostringstream perfetto_json_stream_;
{
v8::platform::tracing::JSONTraceEventListener json_listener_(
&perfetto_json_stream_);
std::vector<char> trace = tracing_session_->ReadTraceBlocking();
json_listener_.ParseFromArray(trace);
}
printf("%s\n", perfetto_json_stream_.str().c_str());
CHECK_GT(perfetto_json_stream_.str().length(), 0);
}
#endif // V8_USE_PERFETTO
} // namespace tracing
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment