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

[tracing] Add a way to test perfetto traces.

Add a new abstract class TraceEventListener which is just an interface
for consuming trace events. This separates the V8-specific stuff that
an actual perfetto consumer needs to do e.g. handling the has_more flag
and signalling back to the controller with a semaphore.

This is a change from the previous plan of making the PerfettoConsumer
class sub-classable to implement custom consumption of trace events.
This will be difficult when the consumer is created outside of the
PerfettoTracingController as we can't hook up the
consumer_finished_semaphore_ that belongs to the controller.

Now the PerfettoTracingController is responsible for the Consumer life-
cycle and hides it entirely from callers. We add the
AddTraceEventListener() method to allow callers to register a listener
either for testing or a JSON listener for real tracing.

This lets us write tests that can store all the trace events in memory
without first converting them to JSON, letting us write test more
easily. There's an example test add to test-tracing - more tests using
this style will follow.

Cq-Include-Trybots: luci.v8.try:v8_linux64_perfetto_dbg_ng
Bug: v8:8339
Change-Id: I2d2b0f408b1c7bed954144163e1968f40d772c1b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1628789
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Auto-Submit: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61854}
parent 8f639193
...@@ -139,10 +139,6 @@ declare_args() { ...@@ -139,10 +139,6 @@ declare_args() {
# Use Siphash as added protection against hash flooding attacks. # Use Siphash as added protection against hash flooding attacks.
v8_use_siphash = false v8_use_siphash = false
# Use Perfetto (https://perfetto.dev) as the default TracingController. Not
# currently implemented.
v8_use_perfetto = false
# Switches off inlining in V8. # Switches off inlining in V8.
v8_no_inline = false v8_no_inline = false
...@@ -3661,10 +3657,10 @@ v8_component("v8_libplatform") { ...@@ -3661,10 +3657,10 @@ v8_component("v8_libplatform") {
] ]
if (v8_use_perfetto) { if (v8_use_perfetto) {
sources += [ sources += [
"src/libplatform/tracing/perfetto-consumer-base.cc", "src/libplatform/tracing/json-trace-event-listener.cc",
"src/libplatform/tracing/perfetto-consumer-base.h", "src/libplatform/tracing/json-trace-event-listener.h",
"src/libplatform/tracing/perfetto-json-consumer.cc", "src/libplatform/tracing/perfetto-consumer.cc",
"src/libplatform/tracing/perfetto-json-consumer.h", "src/libplatform/tracing/perfetto-consumer.h",
"src/libplatform/tracing/perfetto-producer.cc", "src/libplatform/tracing/perfetto-producer.cc",
"src/libplatform/tracing/perfetto-producer.h", "src/libplatform/tracing/perfetto-producer.h",
"src/libplatform/tracing/perfetto-shared-memory.cc", "src/libplatform/tracing/perfetto-shared-memory.cc",
...@@ -3673,6 +3669,7 @@ v8_component("v8_libplatform") { ...@@ -3673,6 +3669,7 @@ v8_component("v8_libplatform") {
"src/libplatform/tracing/perfetto-tasks.h", "src/libplatform/tracing/perfetto-tasks.h",
"src/libplatform/tracing/perfetto-tracing-controller.cc", "src/libplatform/tracing/perfetto-tracing-controller.cc",
"src/libplatform/tracing/perfetto-tracing-controller.h", "src/libplatform/tracing/perfetto-tracing-controller.h",
"src/libplatform/tracing/trace-event-listener.h",
] ]
deps += [ deps += [
"third_party/perfetto:libperfetto", "third_party/perfetto:libperfetto",
......
...@@ -63,6 +63,10 @@ declare_args() { ...@@ -63,6 +63,10 @@ declare_args() {
# Expose symbols for dynamic linking. # Expose symbols for dynamic linking.
v8_expose_symbols = false v8_expose_symbols = false
# Use Perfetto (https://perfetto.dev) as the default TracingController. Not
# currently implemented.
v8_use_perfetto = false
} }
if (v8_use_external_startup_data == "") { if (v8_use_external_startup_data == "") {
......
...@@ -24,6 +24,7 @@ namespace platform { ...@@ -24,6 +24,7 @@ namespace platform {
namespace tracing { namespace tracing {
class PerfettoTracingController; class PerfettoTracingController;
class TraceEventListener;
const int kTraceMaxNumArgs = 2; const int kTraceMaxNumArgs = 2;
...@@ -244,6 +245,9 @@ class V8_PLATFORM_EXPORT TracingController ...@@ -244,6 +245,9 @@ class V8_PLATFORM_EXPORT TracingController
// Must be called before StartTracing() if V8_USE_PERFETTO is true. Provides // Must be called before StartTracing() if V8_USE_PERFETTO is true. Provides
// the output stream for the JSON trace data. // the output stream for the JSON trace data.
void InitializeForPerfetto(std::ostream* output_stream); void InitializeForPerfetto(std::ostream* output_stream);
// Provide an optional listener for testing that will receive trace events.
// Must be called before StartTracing().
void SetTraceEventListenerForTesting(TraceEventListener* listener);
#endif #endif
// v8::TracingController implementation. // v8::TracingController implementation.
...@@ -291,6 +295,8 @@ class V8_PLATFORM_EXPORT TracingController ...@@ -291,6 +295,8 @@ class V8_PLATFORM_EXPORT TracingController
std::atomic_bool perfetto_recording_{false}; std::atomic_bool perfetto_recording_{false};
std::unique_ptr<PerfettoTracingController> perfetto_tracing_controller_; std::unique_ptr<PerfettoTracingController> perfetto_tracing_controller_;
std::ostream* output_stream_ = nullptr; std::ostream* output_stream_ = nullptr;
std::unique_ptr<TraceEventListener> json_listener_;
TraceEventListener* listener_for_testing_ = nullptr;
#endif #endif
// Disallow copy and assign // Disallow copy and assign
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "src/libplatform/tracing/perfetto-json-consumer.h" #include "src/libplatform/tracing/json-trace-event-listener.h"
#include <cmath> #include <cmath>
...@@ -15,19 +15,18 @@ namespace v8 { ...@@ -15,19 +15,18 @@ namespace v8 {
namespace platform { namespace platform {
namespace tracing { namespace tracing {
PerfettoJSONConsumer::PerfettoJSONConsumer(base::Semaphore* finished, JSONTraceEventListener::JSONTraceEventListener(std::ostream* stream)
std::ostream* stream) : stream_(stream) {
: PerfettoConsumerBase(finished), stream_(stream) {
*stream_ << "{\"traceEvents\":["; *stream_ << "{\"traceEvents\":[";
} }
PerfettoJSONConsumer::~PerfettoJSONConsumer() { *stream_ << "]}"; } JSONTraceEventListener::~JSONTraceEventListener() { *stream_ << "]}"; }
// TODO(petermarshall): Clean up this code which was copied from trace-writer.cc // TODO(petermarshall): Clean up this code which was copied from trace-writer.cc
// once we've removed that file. // once we've removed that file.
// Writes the given string, taking care to escape characters when necessary. // Writes the given string, taking care to escape characters when necessary.
void PerfettoJSONConsumer::AppendJSONString(const char* str) { void JSONTraceEventListener::AppendJSONString(const char* str) {
size_t len = strlen(str); size_t len = strlen(str);
*stream_ << "\""; *stream_ << "\"";
for (size_t i = 0; i < len; ++i) { for (size_t i = 0; i < len; ++i) {
...@@ -65,7 +64,7 @@ void PerfettoJSONConsumer::AppendJSONString(const char* str) { ...@@ -65,7 +64,7 @@ void PerfettoJSONConsumer::AppendJSONString(const char* str) {
*stream_ << "\""; *stream_ << "\"";
} }
void PerfettoJSONConsumer::AppendArgValue( void JSONTraceEventListener::AppendArgValue(
const ::perfetto::protos::ChromeTraceEvent_Arg& arg) { const ::perfetto::protos::ChromeTraceEvent_Arg& arg) {
if (arg.has_bool_value()) { if (arg.has_bool_value()) {
*stream_ << (arg.bool_value() ? "true" : "false"); *stream_ << (arg.bool_value() ? "true" : "false");
...@@ -111,7 +110,7 @@ void PerfettoJSONConsumer::AppendArgValue( ...@@ -111,7 +110,7 @@ void PerfettoJSONConsumer::AppendArgValue(
CHECK(!arg.has_traced_value()); CHECK(!arg.has_traced_value());
} }
void PerfettoJSONConsumer::ProcessPacket( void JSONTraceEventListener::ProcessPacket(
const ::perfetto::protos::ChromeTracePacket& packet) { const ::perfetto::protos::ChromeTracePacket& packet) {
for (const ::perfetto::protos::ChromeTraceEvent& event : for (const ::perfetto::protos::ChromeTraceEvent& event :
packet.chrome_events().trace_events()) { packet.chrome_events().trace_events()) {
......
...@@ -2,17 +2,16 @@ ...@@ -2,17 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef V8_LIBPLATFORM_TRACING_PERFETTO_JSON_CONSUMER_H_ #ifndef V8_LIBPLATFORM_TRACING_JSON_TRACE_EVENT_LISTENER_H_
#define V8_LIBPLATFORM_TRACING_PERFETTO_JSON_CONSUMER_H_ #define V8_LIBPLATFORM_TRACING_JSON_TRACE_EVENT_LISTENER_H_
#include <ostream> #include <ostream>
#include "src/libplatform/tracing/perfetto-consumer-base.h" #include "src/libplatform/tracing/trace-event-listener.h"
namespace perfetto { namespace perfetto {
namespace protos { namespace protos {
class ChromeTraceEvent_Arg; class ChromeTraceEvent_Arg;
class ChromeTracePacket;
} // namespace protos } // namespace protos
} // namespace perfetto } // namespace perfetto
...@@ -20,12 +19,12 @@ namespace v8 { ...@@ -20,12 +19,12 @@ namespace v8 {
namespace platform { namespace platform {
namespace tracing { namespace tracing {
// A consumer that converts the proto trace data to JSON and writes it to a // A listener that converts the proto trace data to JSON and writes it to a
// file. // file.
class PerfettoJSONConsumer final : public PerfettoConsumerBase { class JSONTraceEventListener final : public TraceEventListener {
public: public:
PerfettoJSONConsumer(base::Semaphore* finished, std::ostream* stream); explicit JSONTraceEventListener(std::ostream* stream);
~PerfettoJSONConsumer() override; ~JSONTraceEventListener() override;
private: private:
void ProcessPacket( void ProcessPacket(
...@@ -43,4 +42,4 @@ class PerfettoJSONConsumer final : public PerfettoConsumerBase { ...@@ -43,4 +42,4 @@ class PerfettoJSONConsumer final : public PerfettoConsumerBase {
} // namespace platform } // namespace platform
} // namespace v8 } // namespace v8
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_JSON_CONSUMER_H_ #endif // V8_LIBPLATFORM_TRACING_JSON_TRACE_EVENT_LISTENER_H_
...@@ -2,29 +2,32 @@ ...@@ -2,29 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "src/libplatform/tracing/perfetto-consumer-base.h" #include "src/libplatform/tracing/perfetto-consumer.h"
#include "perfetto/trace/chrome/chrome_trace_packet.pb.h" #include "perfetto/trace/chrome/chrome_trace_packet.pb.h"
#include "perfetto/tracing/core/trace_packet.h" #include "perfetto/tracing/core/trace_packet.h"
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/base/platform/semaphore.h" #include "src/base/platform/semaphore.h"
#include "src/libplatform/tracing/trace-event-listener.h"
namespace v8 { namespace v8 {
namespace platform { namespace platform {
namespace tracing { namespace tracing {
PerfettoConsumerBase::PerfettoConsumerBase(base::Semaphore* finished) PerfettoConsumer::PerfettoConsumer(base::Semaphore* finished)
: finished_semaphore_(finished) {} : finished_semaphore_(finished) {}
void PerfettoConsumerBase::OnTraceData( void PerfettoConsumer::OnTraceData(std::vector<::perfetto::TracePacket> packets,
std::vector<::perfetto::TracePacket> packets, bool has_more) { bool has_more) {
for (const ::perfetto::TracePacket& packet : packets) { for (const ::perfetto::TracePacket& packet : packets) {
perfetto::protos::ChromeTracePacket proto_packet; perfetto::protos::ChromeTracePacket proto_packet;
bool success = packet.Decode(&proto_packet); bool success = packet.Decode(&proto_packet);
USE(success); USE(success);
DCHECK(success); DCHECK(success);
ProcessPacket(proto_packet); for (TraceEventListener* listener : listeners_) {
listener->ProcessPacket(proto_packet);
}
} }
// PerfettoTracingController::StopTracing() waits on this sempahore. This is // PerfettoTracingController::StopTracing() waits on this sempahore. This is
// so that we can ensure that this consumer has finished consuming all of the // so that we can ensure that this consumer has finished consuming all of the
...@@ -32,6 +35,10 @@ void PerfettoConsumerBase::OnTraceData( ...@@ -32,6 +35,10 @@ void PerfettoConsumerBase::OnTraceData(
if (!has_more) finished_semaphore_->Signal(); if (!has_more) finished_semaphore_->Signal();
} }
void PerfettoConsumer::AddTraceEventListener(TraceEventListener* listener) {
listeners_.push_back(listener);
}
} // namespace tracing } // namespace tracing
} // namespace platform } // namespace platform
} // namespace v8 } // namespace v8
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_BASE_H_ #ifndef V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_H_
#define V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_BASE_H_ #define V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_H_
#include <memory> #include <memory>
...@@ -26,33 +26,39 @@ class Semaphore; ...@@ -26,33 +26,39 @@ class Semaphore;
namespace platform { namespace platform {
namespace tracing { namespace tracing {
// A base class for custom Consumers within V8. Implements V8-specific logic class TraceEventListener;
// for interacting with the tracing controller and leaves the consumption of
// the trace events to the subclass.
// A Perfetto Consumer gets streamed trace events from the Service via // A Perfetto Consumer gets streamed trace events from the Service via
// OnTraceData(). A Consumer can be configured (via // OnTraceData(). A Consumer can be configured (via
// service_endpoint()->EnableTracing()) to listen to various different types of // service_endpoint()->EnableTracing()) to listen to various different types of
// trace events. The Consumer is responsible for producing whatever tracing // trace events. The Consumer is responsible for producing whatever tracing
// output the system should have. // output the system should have.
class PerfettoConsumerBase : public ::perfetto::Consumer {
// 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: public:
explicit PerfettoConsumer(base::Semaphore* finished);
using ServiceEndpoint = ::perfetto::TracingService::ConsumerEndpoint; 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(); } ServiceEndpoint* service_endpoint() const { return service_endpoint_.get(); }
void set_service_endpoint(std::unique_ptr<ServiceEndpoint> endpoint) { void set_service_endpoint(std::unique_ptr<ServiceEndpoint> endpoint) {
service_endpoint_ = std::move(endpoint); service_endpoint_ = std::move(endpoint);
} }
protected:
explicit PerfettoConsumerBase(base::Semaphore* finished);
private: private:
// ::perfetto::Consumer implementation // ::perfetto::Consumer implementation
void OnConnect() override {} void OnConnect() override {}
void OnDisconnect() override {} void OnDisconnect() override {}
void OnTracingDisabled() override {} void OnTracingDisabled() override {}
void OnTraceData(std::vector<::perfetto::TracePacket> packets, void OnTraceData(std::vector<::perfetto::TracePacket> packets,
bool has_more) final; bool has_more) override;
void OnDetach(bool success) override {} void OnDetach(bool success) override {}
void OnAttach(bool success, const ::perfetto::TraceConfig&) override {} void OnAttach(bool success, const ::perfetto::TraceConfig&) override {}
void OnTraceStats(bool success, const ::perfetto::TraceStats&) override { void OnTraceStats(bool success, const ::perfetto::TraceStats&) override {
...@@ -62,16 +68,13 @@ class PerfettoConsumerBase : public ::perfetto::Consumer { ...@@ -62,16 +68,13 @@ class PerfettoConsumerBase : public ::perfetto::Consumer {
UNREACHABLE(); UNREACHABLE();
} }
// Subclasses override this method to respond to trace packets.
virtual void ProcessPacket(
const ::perfetto::protos::ChromeTracePacket& packet) = 0;
std::unique_ptr<ServiceEndpoint> service_endpoint_; std::unique_ptr<ServiceEndpoint> service_endpoint_;
base::Semaphore* finished_semaphore_; base::Semaphore* finished_semaphore_;
std::vector<TraceEventListener*> listeners_;
}; };
} // namespace tracing } // namespace tracing
} // namespace platform } // namespace platform
} // namespace v8 } // namespace v8
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_BASE_H_ #endif // V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_H_
...@@ -7,10 +7,11 @@ ...@@ -7,10 +7,11 @@
#include "perfetto/tracing/core/trace_config.h" #include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_writer.h" #include "perfetto/tracing/core/trace_writer.h"
#include "perfetto/tracing/core/tracing_service.h" #include "perfetto/tracing/core/tracing_service.h"
#include "src/libplatform/tracing/perfetto-json-consumer.h" #include "src/libplatform/tracing/perfetto-consumer.h"
#include "src/libplatform/tracing/perfetto-producer.h" #include "src/libplatform/tracing/perfetto-producer.h"
#include "src/libplatform/tracing/perfetto-shared-memory.h" #include "src/libplatform/tracing/perfetto-shared-memory.h"
#include "src/libplatform/tracing/perfetto-tasks.h" #include "src/libplatform/tracing/perfetto-tasks.h"
#include "src/libplatform/tracing/trace-event-listener.h"
namespace v8 { namespace v8 {
namespace platform { namespace platform {
...@@ -22,15 +23,12 @@ PerfettoTracingController::PerfettoTracingController() ...@@ -22,15 +23,12 @@ PerfettoTracingController::PerfettoTracingController()
consumer_finished_semaphore_(0) {} consumer_finished_semaphore_(0) {}
void PerfettoTracingController::StartTracing( void PerfettoTracingController::StartTracing(
const ::perfetto::TraceConfig& trace_config, std::ostream* output_stream) { const ::perfetto::TraceConfig& trace_config) {
DCHECK_NOT_NULL(output_stream);
DCHECK(output_stream->good());
DCHECK(!task_runner_); DCHECK(!task_runner_);
task_runner_ = base::make_unique<PerfettoTaskRunner>(); task_runner_ = base::make_unique<PerfettoTaskRunner>();
// The Perfetto service expects calls on the task runner thread which is why // The Perfetto service expects calls on the task runner thread which is why
// the setup below occurs in posted tasks. // the setup below occurs in posted tasks.
task_runner_->PostTask([&trace_config, output_stream, this] { task_runner_->PostTask([&trace_config, this] {
std::unique_ptr<::perfetto::SharedMemory::Factory> shmem_factory = std::unique_ptr<::perfetto::SharedMemory::Factory> shmem_factory =
base::make_unique<PerfettoSharedMemoryFactory>(); base::make_unique<PerfettoSharedMemoryFactory>();
...@@ -44,8 +42,12 @@ void PerfettoTracingController::StartTracing( ...@@ -44,8 +42,12 @@ void PerfettoTracingController::StartTracing(
// for now we just leak all TraceWriters. // for now we just leak all TraceWriters.
service_->SetSMBScrapingEnabled(true); service_->SetSMBScrapingEnabled(true);
producer_ = base::make_unique<PerfettoProducer>(this); producer_ = base::make_unique<PerfettoProducer>(this);
consumer_ = base::make_unique<PerfettoJSONConsumer>( consumer_ =
&consumer_finished_semaphore_, output_stream); base::make_unique<PerfettoConsumer>(&consumer_finished_semaphore_);
for (TraceEventListener* listener : listeners_) {
consumer_->AddTraceEventListener(listener);
}
producer_->set_service_endpoint(service_->ConnectProducer( producer_->set_service_endpoint(service_->ConnectProducer(
producer_.get(), 0, "v8.perfetto-producer", 0, true)); producer_.get(), 0, "v8.perfetto-producer", 0, true));
...@@ -72,7 +74,7 @@ void PerfettoTracingController::StopTracing() { ...@@ -72,7 +74,7 @@ void PerfettoTracingController::StopTracing() {
// events that have been written by still-living TraceWriters. // events that have been written by still-living TraceWriters.
consumer_->service_endpoint()->DisableTracing(); consumer_->service_endpoint()->DisableTracing();
// Trigger the consumer to finish. This can trigger multiple calls to // Trigger the consumer to finish. This can trigger multiple calls to
// PerfettoConsumerBase::OnTraceData(), with the final call passing has_more // PerfettoConsumer::OnTraceData(), with the final call passing has_more
// as false. // as false.
consumer_->service_endpoint()->ReadBuffers(); consumer_->service_endpoint()->ReadBuffers();
}); });
...@@ -91,6 +93,11 @@ void PerfettoTracingController::StopTracing() { ...@@ -91,6 +93,11 @@ void PerfettoTracingController::StopTracing() {
task_runner_.reset(); task_runner_.reset();
} }
void PerfettoTracingController::AddTraceEventListener(
TraceEventListener* listener) {
listeners_.push_back(listener);
}
PerfettoTracingController::~PerfettoTracingController() { PerfettoTracingController::~PerfettoTracingController() {
base::Thread::DeleteThreadLocalKey(writer_key_); base::Thread::DeleteThreadLocalKey(writer_key_);
} }
......
...@@ -23,9 +23,10 @@ namespace v8 { ...@@ -23,9 +23,10 @@ namespace v8 {
namespace platform { namespace platform {
namespace tracing { namespace tracing {
class PerfettoConsumerBase; class PerfettoConsumer;
class PerfettoProducer; class PerfettoProducer;
class PerfettoTaskRunner; class PerfettoTaskRunner;
class TraceEventListener;
// This is the top-level interface for performing tracing with perfetto. The // This is the top-level interface for performing tracing with perfetto. The
// user of this class should call StartTracing() to start tracing, and // user of this class should call StartTracing() to start tracing, and
...@@ -37,15 +38,19 @@ class PerfettoTracingController { ...@@ -37,15 +38,19 @@ class PerfettoTracingController {
// Blocks and sets up all required data structures for tracing. It is safe to // Blocks and sets up all required data structures for tracing. It is safe to
// call GetOrCreateThreadLocalWriter() to obtain thread-local TraceWriters for // call GetOrCreateThreadLocalWriter() to obtain thread-local TraceWriters for
// writing trace events once this call returns. Tracing output will be sent in // writing trace events once this call returns. Tracing output will be sent to
// JSON format to |output_stream|. // the TraceEventListeners registered via AddTraceEventListener().
void StartTracing(const ::perfetto::TraceConfig& trace_config, void StartTracing(const ::perfetto::TraceConfig& trace_config);
std::ostream* output_stream);
// Blocks and finishes all existing AddTraceEvent tasks. Stops the tracing // Blocks and finishes all existing AddTraceEvent tasks. Stops the tracing
// thread. // thread.
void StopTracing(); 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(); ~PerfettoTracingController();
// Each thread that wants to trace should call this to get their TraceWriter. // Each thread that wants to trace should call this to get their TraceWriter.
...@@ -61,8 +66,9 @@ class PerfettoTracingController { ...@@ -61,8 +66,9 @@ class PerfettoTracingController {
std::unique_ptr<::perfetto::TracingService> service_; std::unique_ptr<::perfetto::TracingService> service_;
std::unique_ptr<PerfettoProducer> producer_; std::unique_ptr<PerfettoProducer> producer_;
std::unique_ptr<PerfettoConsumerBase> consumer_; std::unique_ptr<PerfettoConsumer> consumer_;
std::unique_ptr<PerfettoTaskRunner> task_runner_; std::unique_ptr<PerfettoTaskRunner> task_runner_;
std::vector<TraceEventListener*> listeners_;
base::Thread::LocalStorageKey writer_key_; base::Thread::LocalStorageKey writer_key_;
// A semaphore that is signalled when StartRecording is called. StartTracing // A semaphore that is signalled when StartRecording is called. StartTracing
// waits on this semaphore to be notified when the tracing service is ready to // waits on this semaphore to be notified when the tracing service is ready to
......
// 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_TRACE_EVENT_LISTENER_H_
#define V8_LIBPLATFORM_TRACING_TRACE_EVENT_LISTENER_H_
namespace perfetto {
namespace protos {
class ChromeTracePacket;
} // namespace protos
} // namespace perfetto
namespace v8 {
namespace platform {
namespace tracing {
// A TraceEventListener is a simple interface that allows subclasses to listen
// to trace events. This interface is to hide the more complex interactions that
// the PerfettoConsumer class has to perform. Clients override ProcessPacket()
// to respond to trace events, e.g. to write them to a file as JSON or for
// testing purposes.
class TraceEventListener {
public:
virtual ~TraceEventListener() = default;
virtual void ProcessPacket(
const ::perfetto::protos::ChromeTracePacket& packet) = 0;
};
} // namespace tracing
} // namespace platform
} // namespace v8
#endif // V8_LIBPLATFORM_TRACING_TRACE_EVENT_LISTENER_H_
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "perfetto/tracing/core/trace_config.h" #include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h" #include "perfetto/tracing/core/trace_packet.h"
#include "perfetto/tracing/core/trace_writer.h" #include "perfetto/tracing/core/trace_writer.h"
#include "src/libplatform/tracing/json-trace-event-listener.h"
#include "src/libplatform/tracing/perfetto-tracing-controller.h" #include "src/libplatform/tracing/perfetto-tracing-controller.h"
#endif // V8_USE_PERFETTO #endif // V8_USE_PERFETTO
...@@ -79,6 +80,11 @@ void TracingController::InitializeForPerfetto(std::ostream* output_stream) { ...@@ -79,6 +80,11 @@ void TracingController::InitializeForPerfetto(std::ostream* output_stream) {
DCHECK_NOT_NULL(output_stream); DCHECK_NOT_NULL(output_stream);
DCHECK(output_stream->good()); DCHECK(output_stream->good());
} }
void TracingController::SetTraceEventListenerForTesting(
TraceEventListener* listener) {
listener_for_testing_ = listener;
}
#endif #endif
int64_t TracingController::CurrentTimestampMicroseconds() { int64_t TracingController::CurrentTimestampMicroseconds() {
...@@ -273,18 +279,21 @@ void TracingController::StartTracing(TraceConfig* trace_config) { ...@@ -273,18 +279,21 @@ void TracingController::StartTracing(TraceConfig* trace_config) {
#ifdef V8_USE_PERFETTO #ifdef V8_USE_PERFETTO
perfetto_tracing_controller_ = base::make_unique<PerfettoTracingController>(); 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; ::perfetto::TraceConfig perfetto_trace_config;
perfetto_trace_config.add_buffers()->set_size_kb(4096); perfetto_trace_config.add_buffers()->set_size_kb(4096);
auto* ds_config = perfetto_trace_config.add_data_sources()->mutable_config(); auto* ds_config = perfetto_trace_config.add_data_sources()->mutable_config();
ds_config->set_name("v8.trace_events"); ds_config->set_name("v8.trace_events");
DCHECK_NOT_NULL(output_stream_);
DCHECK(output_stream_->good());
// TODO(petermarshall): Set all the params from |perfetto_trace_config|. // TODO(petermarshall): Set all the params from |perfetto_trace_config|.
perfetto_tracing_controller_->StartTracing(perfetto_trace_config, perfetto_tracing_controller_->StartTracing(perfetto_trace_config);
output_stream_);
perfetto_recording_.store(true); perfetto_recording_.store(true);
#endif // V8_USE_PERFETTO #endif // V8_USE_PERFETTO
...@@ -321,6 +330,7 @@ void TracingController::StopTracing() { ...@@ -321,6 +330,7 @@ void TracingController::StopTracing() {
perfetto_recording_.store(false); perfetto_recording_.store(false);
perfetto_tracing_controller_->StopTracing(); perfetto_tracing_controller_->StopTracing();
perfetto_tracing_controller_.reset(); perfetto_tracing_controller_.reset();
json_listener_.reset();
#endif // V8_USE_PERFETTO #endif // V8_USE_PERFETTO
{ {
......
...@@ -412,6 +412,10 @@ v8_source_set("cctest_sources") { ...@@ -412,6 +412,10 @@ v8_source_set("cctest_sources") {
# MSVS wants this for gay-{precision,shortest}.cc. # MSVS wants this for gay-{precision,shortest}.cc.
cflags += [ "/bigobj" ] cflags += [ "/bigobj" ]
} }
if (v8_use_perfetto) {
deps += [ "//third_party/perfetto/protos/perfetto/trace/chrome:minimal_complete_lite" ]
}
} }
action("resources") { action("resources") {
......
include_rules = [
"+perfetto",
]
...@@ -9,6 +9,12 @@ ...@@ -9,6 +9,12 @@
#include "src/tracing/trace-event.h" #include "src/tracing/trace-event.h"
#include "test/cctest/cctest.h" #include "test/cctest/cctest.h"
#ifdef V8_USE_PERFETTO
#include "perfetto/trace/chrome/chrome_trace_event.pb.h"
#include "perfetto/trace/chrome/chrome_trace_packet.pb.h"
#include "src/libplatform/tracing/trace-event-listener.h"
#endif
namespace v8 { namespace v8 {
namespace platform { namespace platform {
namespace tracing { namespace tracing {
...@@ -525,6 +531,136 @@ TEST(AddTraceEventMultiThreaded) { ...@@ -525,6 +531,136 @@ TEST(AddTraceEventMultiThreaded) {
i::V8::SetPlatformForTesting(old_platform); i::V8::SetPlatformForTesting(old_platform);
} }
#ifdef V8_USE_PERFETTO
struct TraceEvent {
std::string name;
int64_t timestamp;
int32_t phase;
int32_t thread_id;
int64_t duration;
int64_t thread_duration;
std::string scope;
uint64_t id;
uint32_t flags;
std::string category_group_name;
int32_t process_id;
int64_t thread_timestamp;
uint64_t bind_id;
};
class TestListener : public TraceEventListener {
public:
void ProcessPacket(
const ::perfetto::protos::ChromeTracePacket& packet) override {
for (const ::perfetto::protos::ChromeTraceEvent& event :
packet.chrome_events().trace_events()) {
TraceEvent trace_event{event.name(), event.timestamp(),
event.phase(), event.thread_id(),
event.duration(), event.thread_duration(),
event.scope(), event.id(),
event.flags(), event.category_group_name(),
event.process_id(), event.thread_timestamp(),
event.bind_id()};
events_.push_back(trace_event);
}
}
TraceEvent* get_event(size_t index) { return &events_.at(index); }
size_t events_size() const { return events_.size(); }
private:
std::vector<TraceEvent> events_;
};
TEST(Perfetto) {
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
std::unique_ptr<v8::Platform> default_platform(
v8::platform::NewDefaultPlatform());
i::V8::SetPlatformForTesting(default_platform.get());
auto tracing = base::make_unique<v8::platform::tracing::TracingController>();
v8::platform::tracing::TracingController* tracing_controller = tracing.get();
static_cast<v8::platform::DefaultPlatform*>(default_platform.get())
->SetTracingController(std::move(tracing));
MockTraceWriter* writer = new MockTraceWriter();
TraceBuffer* ring_buffer =
TraceBuffer::CreateTraceBufferRingBuffer(1, writer);
tracing_controller->Initialize(ring_buffer);
TestListener listener;
std::ostringstream sstream;
tracing_controller->InitializeForPerfetto(&sstream);
tracing_controller->SetTraceEventListenerForTesting(&listener);
TraceConfig* trace_config = new TraceConfig();
trace_config->AddIncludedCategory("v8");
tracing_controller->StartTracing(trace_config);
uint64_t uint64_arg = 1024;
const char* str_arg = "str_arg";
{
TRACE_EVENT0("v8", "test1");
TRACE_EVENT1("v8", "test2", "arg1", uint64_arg);
TRACE_EVENT2("v8", "test3", "arg1", uint64_arg, "arg2", str_arg);
}
TRACE_EVENT_INSTANT0("v8", "final event not captured",
TRACE_EVENT_SCOPE_THREAD);
tracing_controller->StopTracing();
TraceEvent* event = listener.get_event(0);
int32_t thread_id = event->thread_id;
int32_t process_id = event->process_id;
CHECK_EQ("test1", event->name);
CHECK_EQ(TRACE_EVENT_PHASE_BEGIN, event->phase);
int64_t timestamp = event->timestamp;
event = listener.get_event(1);
CHECK_EQ("test2", event->name);
CHECK_EQ(TRACE_EVENT_PHASE_BEGIN, event->phase);
CHECK_EQ(thread_id, event->thread_id);
CHECK_EQ(process_id, event->process_id);
CHECK_GE(event->timestamp, timestamp);
timestamp = event->timestamp;
event = listener.get_event(2);
CHECK_EQ("test3", event->name);
CHECK_EQ(TRACE_EVENT_PHASE_BEGIN, event->phase);
CHECK_EQ(thread_id, event->thread_id);
CHECK_EQ(process_id, event->process_id);
CHECK_GE(event->timestamp, timestamp);
timestamp = event->timestamp;
event = listener.get_event(3);
CHECK_EQ(TRACE_EVENT_PHASE_END, event->phase);
CHECK_EQ(thread_id, event->thread_id);
CHECK_EQ(process_id, event->process_id);
CHECK_GE(event->timestamp, timestamp);
timestamp = event->timestamp;
event = listener.get_event(4);
CHECK_EQ(TRACE_EVENT_PHASE_END, event->phase);
CHECK_EQ(thread_id, event->thread_id);
CHECK_EQ(process_id, event->process_id);
CHECK_GE(event->timestamp, timestamp);
timestamp = event->timestamp;
event = listener.get_event(5);
CHECK_EQ(TRACE_EVENT_PHASE_END, event->phase);
CHECK_EQ(thread_id, event->thread_id);
CHECK_EQ(process_id, event->process_id);
CHECK_GE(event->timestamp, timestamp);
timestamp = event->timestamp;
CHECK_EQ(6, listener.events_size());
i::V8::SetPlatformForTesting(old_platform);
}
#endif // V8_USE_PERFETTO
} // namespace tracing } // namespace tracing
} // namespace platform } // namespace platform
} // namespace v8 } // namespace v8
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