[tracing] Implement the default TracingController with Perfetto
Based on Primiano's prototype: https://chromium-review.googlesource.com/c/v8/v8/+/1290549 This is still behind a build flag. I'll add functionality incrementally rather than land everything in one giant CL. This CL sets up the basic classes that will be used for the Perfetto implementation, e.g. the producer, consumer, controller and task runner. This implementation produces a binary proto file in the current directory named v8_trace.proto. It doesn't yet produce JSON output, that is coming in a following CL. Currently the old tracing and perfetto tracing are both run alongside each other if the build flag is enabled. Cq-Include-Trybots: luci.v8.try:v8_linux64_perfetto_dbg_ng Bug: v8:8339 Change-Id: I0eb9ecefa191ceead60aadd5b591d75c99395a6e Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1408995 Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Commit-Queue: Peter Marshall <petermarshall@chromium.org> Cr-Commit-Position: refs/heads/master@{#61209}
This commit is contained in:
parent
75d65b6b58
commit
8f4063c6ea
11
BUILD.gn
11
BUILD.gn
@ -3612,6 +3612,17 @@ v8_component("v8_libplatform") {
|
||||
":v8_libbase",
|
||||
]
|
||||
if (v8_use_perfetto) {
|
||||
sources += [
|
||||
"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",
|
||||
]
|
||||
deps += [ "third_party/perfetto:libperfetto" ]
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ class Mutex;
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
class PerfettoTracingController;
|
||||
|
||||
const int kTraceMaxNumArgs = 2;
|
||||
|
||||
class V8_PLATFORM_EXPORT TraceObject {
|
||||
@ -280,6 +282,10 @@ class V8_PLATFORM_EXPORT TracingController
|
||||
std::unique_ptr<base::Mutex> mutex_;
|
||||
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_;
|
||||
#endif
|
||||
|
||||
// Disallow copy and assign
|
||||
TracingController(const TracingController&) = delete;
|
||||
|
4
src/libplatform/tracing/DEPS
Normal file
4
src/libplatform/tracing/DEPS
Normal file
@ -0,0 +1,4 @@
|
||||
include_rules = [
|
||||
"+perfetto",
|
||||
"+third_party/perfetto/include/perfetto/base",
|
||||
]
|
66
src/libplatform/tracing/perfetto-consumer.h
Normal file
66
src/libplatform/tracing/perfetto-consumer.h
Normal file
@ -0,0 +1,66 @@
|
||||
// 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 v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
// A dummy Consumer that does nothing because we write directly to a file using
|
||||
// the Service. This will be replaced later with a JSON consumer that writes
|
||||
// JSON to a stream, but we need a stand-in for now.
|
||||
|
||||
// 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 - e.g. converting to JSON and writing it to a
|
||||
// file.
|
||||
class PerfettoConsumer final : public ::perfetto::Consumer {
|
||||
public:
|
||||
using ServiceEndpoint = ::perfetto::TracingService::ConsumerEndpoint;
|
||||
|
||||
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 {}
|
||||
// Note: this callback will never be seen because in EnableTracing we set
|
||||
// write_into_file=true. That flag essentially tells the service to directly
|
||||
// write into the passed file descriptor, instead of returning the trace
|
||||
// contents via IPC (which is what this method does).
|
||||
void OnTraceData(std::vector<::perfetto::TracePacket> packets,
|
||||
bool has_more) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
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_;
|
||||
};
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_CONSUMER_H_
|
45
src/libplatform/tracing/perfetto-producer.cc
Normal file
45
src/libplatform/tracing/perfetto-producer.cc
Normal file
@ -0,0 +1,45 @@
|
||||
// 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
|
63
src/libplatform/tracing/perfetto-producer.h
Normal file
63
src/libplatform/tracing/perfetto-producer.h
Normal file
@ -0,0 +1,63 @@
|
||||
// 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"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
class PerfettoTracingController;
|
||||
|
||||
class PerfettoProducer final : public ::perfetto::Producer {
|
||||
public:
|
||||
typedef ::perfetto::TracingService::ProducerEndpoint ServiceEndpoint;
|
||||
|
||||
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 {}
|
||||
|
||||
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_
|
28
src/libplatform/tracing/perfetto-shared-memory.cc
Normal file
28
src/libplatform/tracing/perfetto-shared-memory.cc
Normal file
@ -0,0 +1,28 @@
|
||||
// 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-shared-memory.h"
|
||||
|
||||
#include "src/base/platform/platform.h"
|
||||
#include "src/base/template-utils.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.
|
||||
}
|
||||
|
||||
std::unique_ptr<::perfetto::SharedMemory>
|
||||
PerfettoSharedMemoryFactory::CreateSharedMemory(size_t size) {
|
||||
return base::make_unique<PerfettoSharedMemory>(size);
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
45
src/libplatform/tracing/perfetto-shared-memory.h
Normal file
45
src/libplatform/tracing/perfetto-shared-memory.h
Normal file
@ -0,0 +1,45 @@
|
||||
// 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_
|
55
src/libplatform/tracing/perfetto-tasks.cc
Normal file
55
src/libplatform/tracing/perfetto-tasks.cc
Normal file
@ -0,0 +1,55 @@
|
||||
// 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 {
|
||||
// TODO(petermarshall): Can be called from the V8 main thread in some
|
||||
// situations so we actually need to implement this properly.
|
||||
return true;
|
||||
}
|
||||
|
||||
void PerfettoTaskRunner::FinishImmediateTasks() {
|
||||
// TODO(petermarshall): Add a check !RunsTasksOnCurrentThread() once that is
|
||||
// implemented properly.
|
||||
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
|
55
src/libplatform/tracing/perfetto-tasks.h
Normal file
55
src/libplatform/tracing/perfetto-tasks.h
Normal file
@ -0,0 +1,55 @@
|
||||
// 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_
|
112
src/libplatform/tracing/perfetto-tracing-controller.cc
Normal file
112
src/libplatform/tracing/perfetto-tracing-controller.cc
Normal file
@ -0,0 +1,112 @@
|
||||
// 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"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
PerfettoTracingController::PerfettoTracingController()
|
||||
: writer_key_(base::Thread::CreateThreadLocalKey()),
|
||||
producer_ready_semaphore_(0) {}
|
||||
|
||||
void PerfettoTracingController::StartTracingToFile(
|
||||
int fd, 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([fd, &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());
|
||||
producer_ = base::make_unique<PerfettoProducer>(this);
|
||||
consumer_ = base::make_unique<PerfettoConsumer>();
|
||||
|
||||
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.
|
||||
::perfetto::base::ScopedFile scoped_file(fd);
|
||||
consumer_->service_endpoint()->EnableTracing(trace_config,
|
||||
std::move(scoped_file));
|
||||
});
|
||||
|
||||
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] {
|
||||
// Causes each thread-local writer to be deleted which will trigger a
|
||||
// final Flush() on each writer as well.
|
||||
// TODO(petermarshall): There as a race here where the writer is still being
|
||||
// used by a thread writing trace events (the thread read the value of
|
||||
// perfetto_recording_ before it was changed). We either need to synchronize
|
||||
// all tracing threads here or use TLS destructors like Chrome.
|
||||
writers_to_finalize_.clear();
|
||||
|
||||
consumer_.reset();
|
||||
producer_.reset();
|
||||
service_.reset();
|
||||
});
|
||||
|
||||
// Finish the above task, and any callbacks that were triggered.
|
||||
task_runner_->FinishImmediateTasks();
|
||||
task_runner_.reset();
|
||||
}
|
||||
|
||||
PerfettoTracingController::~PerfettoTracingController() {
|
||||
base::Thread::DeleteThreadLocalKey(writer_key_);
|
||||
}
|
||||
|
||||
::perfetto::TraceWriter*
|
||||
PerfettoTracingController::GetOrCreateThreadLocalWriter() {
|
||||
if (base::Thread::HasThreadLocal(writer_key_)) {
|
||||
return static_cast<::perfetto::TraceWriter*>(
|
||||
base::Thread::GetExistingThreadLocal(writer_key_));
|
||||
}
|
||||
|
||||
std::unique_ptr<::perfetto::TraceWriter> tw = producer_->CreateTraceWriter();
|
||||
|
||||
::perfetto::TraceWriter* writer = tw.get();
|
||||
// We don't have thread-local storage destructors but we need to delete each
|
||||
// thread local TraceWriter, so that they can release the trace buffer chunks
|
||||
// they are holding on to. To do this, we keep a vector of all writers that we
|
||||
// create so they can be deleted when tracing is stopped.
|
||||
{
|
||||
base::MutexGuard guard(&writers_mutex_);
|
||||
writers_to_finalize_.push_back(std::move(tw));
|
||||
}
|
||||
|
||||
base::Thread::SetThreadLocal(writer_key_, writer);
|
||||
return writer;
|
||||
}
|
||||
|
||||
void PerfettoTracingController::OnProducerReady() {
|
||||
producer_ready_semaphore_.Signal();
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
78
src/libplatform/tracing/perfetto-tracing-controller.h
Normal file
78
src/libplatform/tracing/perfetto-tracing-controller.h
Normal file
@ -0,0 +1,78 @@
|
||||
// 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 <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "src/base/platform/mutex.h"
|
||||
#include "src/base/platform/platform.h"
|
||||
|
||||
namespace perfetto {
|
||||
class TraceConfig;
|
||||
class TraceWriter;
|
||||
class TracingService;
|
||||
} // namespace perfetto
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
class PerfettoConsumer;
|
||||
class PerfettoProducer;
|
||||
class PerfettoTaskRunner;
|
||||
|
||||
// This is the top-level interface for performing tracing with perfetto. The
|
||||
// user of this class should call StartTracingToFile() 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.
|
||||
void StartTracingToFile(int fd, const ::perfetto::TraceConfig& trace_config);
|
||||
|
||||
// Blocks and finishes all existing AddTraceEvent tasks. Stops the tracing
|
||||
// thread.
|
||||
void StopTracing();
|
||||
|
||||
~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_;
|
||||
base::Thread::LocalStorageKey writer_key_;
|
||||
base::Mutex writers_mutex_;
|
||||
std::vector<std::unique_ptr<::perfetto::TraceWriter>> writers_to_finalize_;
|
||||
// A semaphore that is signalled when StartRecording is called.
|
||||
// StartTracingToFile waits on this semaphore to be notified when the tracing
|
||||
// service is ready to receive trace events.
|
||||
base::Semaphore producer_ready_semaphore_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(PerfettoTracingController);
|
||||
};
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_LIBPLATFORM_TRACING_PERFETTO_TRACING_CONTROLLER_H_
|
@ -12,6 +12,19 @@
|
||||
#include "src/base/platform/mutex.h"
|
||||
#include "src/base/platform/time.h"
|
||||
|
||||
#ifdef V8_USE_PERFETTO
|
||||
#include <fcntl.h>
|
||||
|
||||
#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 "src/libplatform/tracing/perfetto-tracing-controller.h"
|
||||
#endif // V8_USE_PERFETTO
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
@ -70,6 +83,58 @@ int64_t TracingController::CurrentCpuTimestampMicroseconds() {
|
||||
return base::ThreadTicks::Now().ToInternalValue();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef V8_USE_PERFETTO
|
||||
void AddArgsToTraceProto(
|
||||
::perfetto::protos::pbzero::ChromeTraceEvent* event, int num_args,
|
||||
const char** arg_names, const uint8_t* arg_types,
|
||||
const uint64_t* arg_values,
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables) {
|
||||
for (int i = 0; i < num_args; i++) {
|
||||
::perfetto::protos::pbzero::ChromeTraceEvent_Arg* arg = event->add_args();
|
||||
// TODO(petermarshall): Set name_index instead if need be.
|
||||
arg->set_name(arg_names[i]);
|
||||
|
||||
TraceObject::ArgValue arg_value;
|
||||
arg_value.as_uint = arg_values[i];
|
||||
switch (arg_types[i]) {
|
||||
case TRACE_VALUE_TYPE_CONVERTABLE: {
|
||||
// TODO(petermarshall): Support AppendToProto for Convertables.
|
||||
std::string json_value;
|
||||
arg_convertables[i]->AppendAsTraceFormat(&json_value);
|
||||
arg->set_json_value(json_value.c_str());
|
||||
break;
|
||||
}
|
||||
case TRACE_VALUE_TYPE_BOOL:
|
||||
arg->set_bool_value(arg_value.as_bool);
|
||||
break;
|
||||
case TRACE_VALUE_TYPE_UINT:
|
||||
arg->set_uint_value(arg_value.as_uint);
|
||||
break;
|
||||
case TRACE_VALUE_TYPE_INT:
|
||||
arg->set_int_value(arg_value.as_int);
|
||||
break;
|
||||
case TRACE_VALUE_TYPE_DOUBLE:
|
||||
arg->set_double_value(arg_value.as_double);
|
||||
break;
|
||||
case TRACE_VALUE_TYPE_POINTER:
|
||||
arg->set_pointer_value(arg_value.as_uint);
|
||||
break;
|
||||
// TODO(petermarshall): Treat copy strings specially.
|
||||
case TRACE_VALUE_TYPE_COPY_STRING:
|
||||
case TRACE_VALUE_TYPE_STRING:
|
||||
arg->set_string_value(arg_value.as_string);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // V8_USE_PERFETTO
|
||||
|
||||
} // namespace
|
||||
|
||||
uint64_t TracingController::AddTraceEvent(
|
||||
char phase, const uint8_t* category_enabled_flag, const char* name,
|
||||
const char* scope, uint64_t id, uint64_t bind_id, int num_args,
|
||||
@ -77,20 +142,11 @@ uint64_t TracingController::AddTraceEvent(
|
||||
const uint64_t* arg_values,
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
|
||||
unsigned int flags) {
|
||||
uint64_t handle = 0;
|
||||
if (recording_.load(std::memory_order_acquire)) {
|
||||
TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
|
||||
if (trace_object) {
|
||||
{
|
||||
base::MutexGuard lock(mutex_.get());
|
||||
trace_object->Initialize(
|
||||
phase, category_enabled_flag, name, scope, id, bind_id, num_args,
|
||||
arg_names, arg_types, arg_values, arg_convertables, flags,
|
||||
CurrentTimestampMicroseconds(), CurrentCpuTimestampMicroseconds());
|
||||
}
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
int64_t now_us = CurrentTimestampMicroseconds();
|
||||
|
||||
return AddTraceEventWithTimestamp(
|
||||
phase, category_enabled_flag, name, scope, id, bind_id, num_args,
|
||||
arg_names, arg_types, arg_values, arg_convertables, flags, now_us);
|
||||
}
|
||||
|
||||
uint64_t TracingController::AddTraceEventWithTimestamp(
|
||||
@ -100,6 +156,47 @@ uint64_t TracingController::AddTraceEventWithTimestamp(
|
||||
const uint64_t* arg_values,
|
||||
std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
|
||||
unsigned int flags, int64_t timestamp) {
|
||||
int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();
|
||||
|
||||
#ifdef V8_USE_PERFETTO
|
||||
if (perfetto_recording_.load()) {
|
||||
::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);
|
||||
// TODO(petermarshall): Deal with instant (X) vs B/E events. Need to return
|
||||
// a handle that can be used to edit the event in
|
||||
// UpdateTraceEventDuration().
|
||||
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);
|
||||
|
||||
packet->Finalize();
|
||||
}
|
||||
#endif // V8_USE_PERFETTO
|
||||
|
||||
uint64_t handle = 0;
|
||||
if (recording_.load(std::memory_order_acquire)) {
|
||||
TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
|
||||
@ -109,7 +206,7 @@ uint64_t TracingController::AddTraceEventWithTimestamp(
|
||||
trace_object->Initialize(phase, category_enabled_flag, name, scope, id,
|
||||
bind_id, num_args, arg_names, arg_types,
|
||||
arg_values, arg_convertables, flags, timestamp,
|
||||
CurrentCpuTimestampMicroseconds());
|
||||
cpu_now_us);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,10 +215,40 @@ uint64_t TracingController::AddTraceEventWithTimestamp(
|
||||
|
||||
void TracingController::UpdateTraceEventDuration(
|
||||
const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {
|
||||
int64_t now_us = CurrentTimestampMicroseconds();
|
||||
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.
|
||||
auto* writer = perfetto_tracing_controller_->GetOrCreateThreadLocalWriter();
|
||||
|
||||
auto packet = writer->NewTracePacket();
|
||||
auto* trace_event_bundle = packet->set_chrome_events();
|
||||
auto* trace_event = trace_event_bundle->add_trace_events();
|
||||
|
||||
// TODO(petermarshall): Properly deal with begin/end events by using a
|
||||
// thread-local stack of pending events like
|
||||
// chrome_bundle_thread_local_event_sink.cc does.
|
||||
trace_event->set_phase('E');
|
||||
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_name(name);
|
||||
trace_event->set_timestamp(now_us);
|
||||
trace_event->set_thread_timestamp(cpu_now_us);
|
||||
}
|
||||
#endif // V8_USE_PERFETTO
|
||||
|
||||
TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle);
|
||||
if (!trace_object) return;
|
||||
trace_object->UpdateDuration(CurrentTimestampMicroseconds(),
|
||||
CurrentCpuTimestampMicroseconds());
|
||||
trace_object->UpdateDuration(now_us, cpu_now_us);
|
||||
}
|
||||
|
||||
const char* TracingController::GetCategoryGroupName(
|
||||
@ -141,6 +268,25 @@ const char* TracingController::GetCategoryGroupName(
|
||||
}
|
||||
|
||||
void TracingController::StartTracing(TraceConfig* trace_config) {
|
||||
#ifdef V8_USE_PERFETTO
|
||||
perfetto_tracing_controller_ = base::make_unique<PerfettoTracingController>();
|
||||
|
||||
::perfetto::TraceConfig perfetto_trace_config;
|
||||
// Enable long tracing mode with continuous draining into file.
|
||||
perfetto_trace_config.set_write_into_file(true);
|
||||
perfetto_trace_config.set_file_write_period_ms(1000);
|
||||
|
||||
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 |trace_config| and don't
|
||||
// write to a file by default.
|
||||
int fd = open("v8_trace.proto", O_RDWR | O_CREAT | O_TRUNC, 0644);
|
||||
perfetto_tracing_controller_->StartTracingToFile(fd, perfetto_trace_config);
|
||||
perfetto_recording_.store(true);
|
||||
#endif // V8_USE_PERFETTO
|
||||
|
||||
trace_config_.reset(trace_config);
|
||||
std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
|
||||
{
|
||||
@ -169,6 +315,13 @@ void TracingController::StopTracing() {
|
||||
for (auto o : observers_copy) {
|
||||
o->OnTraceDisabled();
|
||||
}
|
||||
|
||||
#ifdef V8_USE_PERFETTO
|
||||
perfetto_recording_.store(false);
|
||||
perfetto_tracing_controller_->StopTracing();
|
||||
perfetto_tracing_controller_.reset();
|
||||
#endif // V8_USE_PERFETTO
|
||||
|
||||
{
|
||||
base::MutexGuard lock(mutex_.get());
|
||||
trace_buffer_->Flush();
|
||||
|
@ -451,18 +451,21 @@ class TraceWritingThread : public base::Thread {
|
||||
tracing_controller_(tracing_controller) {}
|
||||
|
||||
void Run() override {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
running_.store(true);
|
||||
while (running_.load()) {
|
||||
TRACE_EVENT0("v8", "v8.Test");
|
||||
tracing_controller_->AddTraceEvent('A', nullptr, "v8", "", 1, 1, 0,
|
||||
nullptr, nullptr, nullptr, nullptr, 0);
|
||||
tracing_controller_->AddTraceEventWithTimestamp('A', nullptr, "v8", "", 1,
|
||||
1, 0, nullptr, nullptr,
|
||||
nullptr, nullptr, 0, 0);
|
||||
base::OS::Sleep(base::TimeDelta::FromMilliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
void Stop() { running_.store(false); }
|
||||
|
||||
private:
|
||||
std::atomic_bool running_{false};
|
||||
v8::platform::tracing::TracingController* tracing_controller_;
|
||||
};
|
||||
|
||||
@ -487,10 +490,13 @@ TEST(AddTraceEventMultiThreaded) {
|
||||
|
||||
TraceWritingThread thread(tracing_controller);
|
||||
thread.StartSynchronously();
|
||||
TRACE_EVENT0("v8", "v8.Test2");
|
||||
TRACE_EVENT0("v8", "v8.Test2");
|
||||
|
||||
base::OS::Sleep(base::TimeDelta::FromMilliseconds(100));
|
||||
base::OS::Sleep(base::TimeDelta::FromMilliseconds(10));
|
||||
tracing_controller->StopTracing();
|
||||
|
||||
thread.Stop();
|
||||
thread.Join();
|
||||
|
||||
i::V8::SetPlatformForTesting(old_platform);
|
||||
|
@ -34,6 +34,8 @@ AUTO_EXCLUDE = [
|
||||
]
|
||||
AUTO_EXCLUDE_PATTERNS = [
|
||||
'src/base/atomicops_internals_.*',
|
||||
# TODO(petermarshall): Enable once Perfetto is built by default.
|
||||
'src/libplatform/tracing/perfetto*',
|
||||
] + [
|
||||
# platform-specific headers
|
||||
'\\b{}\\b'.format(p) for p in
|
||||
|
Loading…
Reference in New Issue
Block a user