// Copyright 2018 The Chromium 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 "base/tracing/perfetto_task_runner.h" #include #include #include "base/bind.h" #include "base/containers/contains.h" #include "base/task/common/checked_lock_impl.h" #include "base/task/common/scoped_defer_task_posting.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/task/thread_pool/thread_pool_instance.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/thread_local.h" #include "base/threading/thread_local_storage.h" #include "base/tracing/tracing_tls.h" #include "build/build_config.h" namespace base { namespace tracing { PerfettoTaskRunner::PerfettoTaskRunner( scoped_refptr task_runner) : task_runner_(std::move(task_runner)) {} PerfettoTaskRunner::~PerfettoTaskRunner() { DCHECK(GetOrCreateTaskRunner()->RunsTasksInCurrentSequence()); #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) fd_controllers_.clear(); #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) } void PerfettoTaskRunner::PostTask(std::function task) { PostDelayedTask(task, /* delay_ms */ 0); } void PerfettoTaskRunner::PostDelayedTask(std::function task, uint32_t delay_ms) { base::ScopedDeferTaskPosting::PostOrDefer( GetOrCreateTaskRunner(), FROM_HERE, base::BindOnce( [](std::function task) { // We block any trace events that happens while any // Perfetto task is running, or we'll get deadlocks in // situations where the StartupTraceWriterRegistry tries // to bind a writer which in turn causes a PostTask where // a trace event can be emitted, which then deadlocks as // it needs a new chunk from the same StartupTraceWriter // which we're trying to bind and are keeping the lock // to. // TODO(oysteine): Try to see if we can be more selective // about this. AutoThreadLocalBoolean thread_is_in_trace_event( GetThreadIsInTraceEventTLS()); task(); }, task), base::Milliseconds(delay_ms)); } bool PerfettoTaskRunner::RunsTasksOnCurrentThread() const { DCHECK(task_runner_); return task_runner_->RunsTasksInCurrentSequence(); } // PlatformHandle is an int on POSIX, a HANDLE on Windows. void PerfettoTaskRunner::AddFileDescriptorWatch( perfetto::base::PlatformHandle fd, std::function callback) { #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) DCHECK(GetOrCreateTaskRunner()->RunsTasksInCurrentSequence()); DCHECK(!base::Contains(fd_controllers_, fd)); // Set up the |fd| in the map to signal intent to add a watch. We need to // PostTask the WatchReadable creation because if we do it in this task we'll // race with perfetto setting up the connection on this task and the IO thread // setting up epoll on the |fd|. Using a CancelableOnceClosure ensures that // the |fd| won't be added for watch if RemoveFileDescriptorWatch is called. fd_controllers_[fd].callback.Reset( base::BindOnce( [](PerfettoTaskRunner* perfetto_runner, int fd, std::function callback) { DCHECK(perfetto_runner->GetOrCreateTaskRunner() ->RunsTasksInCurrentSequence()); // When this callback runs, we must not have removed |fd|'s watch. CHECK(base::Contains(perfetto_runner->fd_controllers_, fd)); auto& controller_and_cb = perfetto_runner->fd_controllers_[fd]; // We should never overwrite an existing watch. CHECK(!controller_and_cb.controller); controller_and_cb.controller = base::FileDescriptorWatcher::WatchReadable( fd, base::BindRepeating( [](std::function callback) { callback(); }, std::move(callback))); }, base::Unretained(this), fd, std::move(callback))); task_runner_->PostTask(FROM_HERE, fd_controllers_[fd].callback.callback()); #else // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) NOTREACHED(); #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) } void PerfettoTaskRunner::RemoveFileDescriptorWatch( perfetto::base::PlatformHandle fd) { #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) DCHECK(GetOrCreateTaskRunner()->RunsTasksInCurrentSequence()); DCHECK(base::Contains(fd_controllers_, fd)); // This also cancels the base::FileDescriptorWatcher::WatchReadable() task if // it's pending. fd_controllers_.erase(fd); #else // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) NOTREACHED(); #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) } void PerfettoTaskRunner::ResetTaskRunnerForTesting( scoped_refptr task_runner) { task_runner_ = std::move(task_runner); } void PerfettoTaskRunner::SetTaskRunner( scoped_refptr task_runner) { DCHECK(!task_runner_); task_runner_ = std::move(task_runner); } scoped_refptr PerfettoTaskRunner::GetOrCreateTaskRunner() { // TODO(eseckler): This is not really thread-safe. We should probably add a // lock around this. At the moment we can get away without one because this // method is called for the first time on the process's main thread before the // tracing service connects. if (!task_runner_) { DCHECK(base::ThreadPoolInstance::Get()); task_runner_ = base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::USER_BLOCKING}); } return task_runner_; } #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) PerfettoTaskRunner::FDControllerAndCallback::FDControllerAndCallback() = default; PerfettoTaskRunner::FDControllerAndCallback::~FDControllerAndCallback() = default; #endif } // namespace tracing } // namespace base