[tracing] Add a configurable output stream for perfetto tracing

Add the ability to provide perfetto with an output stream for the JSON
consumer rather than hardcode it. D8 will use this interface exclusively
once the old trace controller is removed.

Also add a test for scope-managed trace events and their duration - this
was leftover from a previous CL.

Cq-Include-Trybots: luci.v8.try:v8_linux64_perfetto_dbg_ng
Bug: v8:8339
Change-Id: I1c45e17e528b549a4cfdaecabd33c7ac4ab4af77
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1611801
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#61753}
diff --git a/include/libplatform/v8-tracing.h b/include/libplatform/v8-tracing.h
index e7ff6c6..0eb7f4b 100644
--- a/include/libplatform/v8-tracing.h
+++ b/include/libplatform/v8-tracing.h
@@ -240,6 +240,11 @@
   TracingController();
   ~TracingController() override;
   void Initialize(TraceBuffer* trace_buffer);
+#ifdef V8_USE_PERFETTO
+  // Must be called before StartTracing() if V8_USE_PERFETTO is true. Provides
+  // the output stream for the JSON trace data.
+  void InitializeForPerfetto(std::ostream* output_stream);
+#endif
 
   // v8::TracingController implementation.
   const uint8_t* GetCategoryGroupEnabled(const char* category_group) override;
@@ -285,6 +290,7 @@
 #ifdef V8_USE_PERFETTO
   std::atomic_bool perfetto_recording_{false};
   std::unique_ptr<PerfettoTracingController> perfetto_tracing_controller_;
+  std::ostream* output_stream_ = nullptr;
 #endif
 
   // Disallow copy and assign
diff --git a/src/d8/d8.cc b/src/d8/d8.cc
index 6929d0f..51cc7e1 100644
--- a/src/d8/d8.cc
+++ b/src/d8/d8.cc
@@ -3323,7 +3323,6 @@
 }
 
 int Shell::Main(int argc, char* argv[]) {
-  std::ofstream trace_file;
   v8::base::EnsureConsoleOutput();
   if (!SetOptions(argc, argv)) return 1;
   v8::V8::InitializeICUDefaultLocation(argv[0], options.icu_data_file);
@@ -3334,15 +3333,26 @@
           : v8::platform::InProcessStackDumping::kEnabled;
 
   std::unique_ptr<platform::tracing::TracingController> tracing;
+  std::ofstream trace_file;
+#ifdef V8_USE_PERFETTO
+  std::ofstream perfetto_trace_file;
+#endif  // V8_USE_PERFETTO
   if (options.trace_enabled && !i::FLAG_verify_predictable) {
     tracing = base::make_unique<platform::tracing::TracingController>();
 
     trace_file.open(options.trace_path ? options.trace_path : "v8_trace.json");
+    DCHECK(trace_file.good());
     platform::tracing::TraceBuffer* trace_buffer =
         platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
             platform::tracing::TraceBuffer::kRingBufferChunks,
             platform::tracing::TraceWriter::CreateJSONTraceWriter(trace_file));
     tracing->Initialize(trace_buffer);
+
+#ifdef V8_USE_PERFETTO
+    perfetto_trace_file.open("v8_perfetto_trace.json");
+    DCHECK(trace_file.good());
+    tracing->InitializeForPerfetto(&perfetto_trace_file);
+#endif  // V8_USE_PERFETTO
   }
 
   platform::tracing::TracingController* tracing_controller = tracing.get();
@@ -3529,6 +3539,9 @@
 
   // Delete the platform explicitly here to write the tracing output to the
   // tracing file.
+  if (options.trace_enabled) {
+    tracing_controller->StopTracing();
+  }
   g_platform.reset();
   return result;
 }
diff --git a/src/libplatform/tracing/perfetto-tracing-controller.cc b/src/libplatform/tracing/perfetto-tracing-controller.cc
index 3c701f5..1a4f159 100644
--- a/src/libplatform/tracing/perfetto-tracing-controller.cc
+++ b/src/libplatform/tracing/perfetto-tracing-controller.cc
@@ -22,16 +22,15 @@
       consumer_finished_semaphore_(0) {}
 
 void PerfettoTracingController::StartTracing(
-    const ::perfetto::TraceConfig& trace_config) {
-  DCHECK(!trace_file_.is_open());
-  trace_file_.open("v8_perfetto_trace.json");
-  CHECK(trace_file_.good());
+    const ::perfetto::TraceConfig& trace_config, std::ostream* output_stream) {
+  DCHECK_NOT_NULL(output_stream);
+  DCHECK(output_stream->good());
 
   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] {
+  task_runner_->PostTask([&trace_config, output_stream, this] {
     std::unique_ptr<::perfetto::SharedMemory::Factory> shmem_factory =
         base::make_unique<PerfettoSharedMemoryFactory>();
 
@@ -46,7 +45,7 @@
     service_->SetSMBScrapingEnabled(true);
     producer_ = base::make_unique<PerfettoProducer>(this);
     consumer_ = base::make_unique<PerfettoJSONConsumer>(
-        &trace_file_, &consumer_finished_semaphore_);
+        output_stream, &consumer_finished_semaphore_);
 
     producer_->set_service_endpoint(service_->ConnectProducer(
         producer_.get(), 0, "v8.perfetto-producer", 0, true));
@@ -90,9 +89,6 @@
   // Finish the above task, and any callbacks that were triggered.
   task_runner_->FinishImmediateTasks();
   task_runner_.reset();
-
-  DCHECK(trace_file_.is_open());
-  trace_file_.close();
 }
 
 PerfettoTracingController::~PerfettoTracingController() {
diff --git a/src/libplatform/tracing/perfetto-tracing-controller.h b/src/libplatform/tracing/perfetto-tracing-controller.h
index 3ab933b..270dcc0 100644
--- a/src/libplatform/tracing/perfetto-tracing-controller.h
+++ b/src/libplatform/tracing/perfetto-tracing-controller.h
@@ -37,8 +37,10 @@
 
   // 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 StartTracing(const ::perfetto::TraceConfig& trace_config);
+  // writing trace events once this call returns. Tracing output will be sent in
+  // JSON format to |output_stream|.
+  void StartTracing(const ::perfetto::TraceConfig& trace_config,
+                    std::ostream* output_stream);
 
   // Blocks and finishes all existing AddTraceEvent tasks. Stops the tracing
   // thread.
@@ -68,9 +70,6 @@
   base::Semaphore producer_ready_semaphore_;
   base::Semaphore consumer_finished_semaphore_;
 
-  // TODO(petermarshall): pass this in instead.
-  std::ofstream trace_file_;
-
   DISALLOW_COPY_AND_ASSIGN(PerfettoTracingController);
 };
 
diff --git a/src/libplatform/tracing/tracing-controller.cc b/src/libplatform/tracing/tracing-controller.cc
index ef099eb..7ab8702 100644
--- a/src/libplatform/tracing/tracing-controller.cc
+++ b/src/libplatform/tracing/tracing-controller.cc
@@ -73,6 +73,14 @@
   mutex_.reset(new base::Mutex());
 }
 
+#ifdef V8_USE_PERFETTO
+void TracingController::InitializeForPerfetto(std::ostream* output_stream) {
+  output_stream_ = output_stream;
+  DCHECK_NOT_NULL(output_stream);
+  DCHECK(output_stream->good());
+}
+#endif
+
 int64_t TracingController::CurrentTimestampMicroseconds() {
   return base::TimeTicks::HighResolutionNow().ToInternalValue();
 }
@@ -271,8 +279,12 @@
   auto* ds_config = perfetto_trace_config.add_data_sources()->mutable_config();
   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|.
-  perfetto_tracing_controller_->StartTracing(perfetto_trace_config);
+  perfetto_tracing_controller_->StartTracing(perfetto_trace_config,
+                                             output_stream_);
   perfetto_recording_.store(true);
 #endif  // V8_USE_PERFETTO
 
diff --git a/test/cctest/libplatform/test-tracing.cc b/test/cctest/libplatform/test-tracing.cc
index 93f5721..3f78c92 100644
--- a/test/cctest/libplatform/test-tracing.cc
+++ b/test/cctest/libplatform/test-tracing.cc
@@ -81,6 +81,20 @@
   std::vector<std::string> events_;
 };
 
+class MockTraceWriterFullTraceObject : public TraceWriter {
+ public:
+  void AppendTraceEvent(TraceObject* trace_event) override {
+    events_.push_back(trace_event);
+  }
+
+  void Flush() override {}
+
+  const std::vector<TraceObject*>& events() const { return events_; }
+
+ private:
+  std::vector<TraceObject*> events_;
+};
+
 TEST(TestTraceBufferRingBuffer) {
   // We should be able to add kChunkSize * 2 + 1 trace events.
   const int HANDLES_COUNT = TraceBufferChunk::kChunkSize * 2 + 1;
@@ -144,6 +158,10 @@
   TraceBuffer* ring_buffer =
       TraceBuffer::CreateTraceBufferRingBuffer(1, writer);
   tracing_controller->Initialize(ring_buffer);
+#ifdef V8_USE_PERFETTO
+  std::ostringstream sstream;
+  tracing_controller->InitializeForPerfetto(&sstream);
+#endif
   TraceConfig* trace_config = new TraceConfig();
   trace_config->AddIncludedCategory("v8-cat");
   tracing_controller->StartTracing(trace_config);
@@ -211,6 +229,10 @@
   TraceBuffer* ring_buffer =
       TraceBuffer::CreateTraceBufferRingBuffer(1, writer);
   tracing_controller->Initialize(ring_buffer);
+#ifdef V8_USE_PERFETTO
+  std::ostringstream sstream;
+  tracing_controller->InitializeForPerfetto(&sstream);
+#endif
   TraceConfig* trace_config = new TraceConfig();
   trace_config->AddIncludedCategory("v8");
   tracing_controller->StartTracing(trace_config);
@@ -243,7 +265,7 @@
 }
 
 TEST(TestTracingControllerMultipleArgsAndCopy) {
-  std::ostringstream stream;
+  std::ostringstream stream, perfetto_stream;
   uint64_t aa = 11;
   unsigned int bb = 22;
   uint16_t cc = 33;
@@ -282,6 +304,9 @@
     TraceBuffer* ring_buffer =
         TraceBuffer::CreateTraceBufferRingBuffer(1, writer);
     tracing_controller->Initialize(ring_buffer);
+#ifdef V8_USE_PERFETTO
+    tracing_controller->InitializeForPerfetto(&perfetto_stream);
+#endif
     TraceConfig* trace_config = new TraceConfig();
     trace_config->AddIncludedCategory("v8");
     tracing_controller->StartTracing(trace_config);
@@ -399,6 +424,10 @@
       v8::platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(1,
                                                                       writer);
   tracing_controller->Initialize(ring_buffer);
+#ifdef V8_USE_PERFETTO
+  std::ostringstream sstream;
+  tracing_controller->InitializeForPerfetto(&sstream);
+#endif
   v8::platform::tracing::TraceConfig* trace_config =
       new v8::platform::tracing::TraceConfig();
   trace_config->AddIncludedCategory("v8");
@@ -488,6 +517,10 @@
   TraceBuffer* ring_buffer =
       TraceBuffer::CreateTraceBufferRingBuffer(1, writer);
   tracing_controller->Initialize(ring_buffer);
+#ifdef V8_USE_PERFETTO
+  std::ostringstream sstream;
+  tracing_controller->InitializeForPerfetto(&sstream);
+#endif
   TraceConfig* trace_config = new TraceConfig();
   trace_config->AddIncludedCategory("v8");
   tracing_controller->StartTracing(trace_config);
@@ -506,6 +539,43 @@
   i::V8::SetPlatformForTesting(old_platform);
 }
 
+TEST(ScopedEventDuration) {
+  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));
+
+  MockTraceWriterFullTraceObject* writer = new MockTraceWriterFullTraceObject();
+  TraceBuffer* ring_buffer =
+      TraceBuffer::CreateTraceBufferRingBuffer(1, writer);
+  tracing_controller->Initialize(ring_buffer);
+#ifdef V8_USE_PERFETTO
+  std::ostringstream sstream;
+  tracing_controller->InitializeForPerfetto(&sstream);
+#endif
+  TraceConfig* trace_config = new TraceConfig();
+  trace_config->AddIncludedCategory("v8");
+  tracing_controller->StartTracing(trace_config);
+
+  {
+    TRACE_EVENT0("v8", "v8.Test.Scoped");
+    base::OS::Sleep(base::TimeDelta::FromMilliseconds(10));
+  }
+
+  tracing_controller->StopTracing();
+
+  CHECK_EQ(1u, writer->events().size());
+  CHECK_EQ(std::string("v8.Test.Scoped"), writer->events()[0]->name());
+  CHECK_GT(writer->events()[0]->duration(), 9000);
+
+  i::V8::SetPlatformForTesting(old_platform);
+}
+
 }  // namespace tracing
 }  // namespace platform
 }  // namespace v8
diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc
index 1eb4c80..a05c15b 100644
--- a/test/cctest/test-cpu-profiler.cc
+++ b/test/cctest/test-cpu-profiler.cc
@@ -2665,6 +2665,11 @@
           i::V8::GetCurrentPlatform()->GetTracingController());
   tracing_controller->Initialize(ring_buffer);
 
+#ifdef V8_USE_PERFETTO
+  std::ostringstream perfetto_output;
+  tracing_controller->InitializeForPerfetto(&perfetto_output);
+#endif
+
   bool result = false;
   for (int run_duration = 50; !result; run_duration += 50) {
     TraceConfig* trace_config = new TraceConfig();