// 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