1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/tracing/perfetto_task_runner.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/auto_reset.h"
11 #include "base/containers/contains.h"
12 #include "base/functional/bind.h"
13 #include "base/notreached.h"
14 #include "base/task/common/checked_lock_impl.h"
15 #include "base/task/common/scoped_defer_task_posting.h"
16 #include "base/task/sequenced_task_runner.h"
17 #include "base/task/thread_pool.h"
18 #include "base/task/thread_pool/thread_pool_instance.h"
19 #include "base/tracing/tracing_tls.h"
20 #include "build/build_config.h"
21
22 #include "base/debug/stack_trace.h"
23
24 namespace base {
25 namespace tracing {
26
PerfettoTaskRunner(scoped_refptr<base::SequencedTaskRunner> task_runner)27 PerfettoTaskRunner::PerfettoTaskRunner(
28 scoped_refptr<base::SequencedTaskRunner> task_runner)
29 : task_runner_(std::move(task_runner)) {}
30
~PerfettoTaskRunner()31 PerfettoTaskRunner::~PerfettoTaskRunner() {
32 DCHECK(GetOrCreateTaskRunner()->RunsTasksInCurrentSequence());
33 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
34 fd_controllers_.clear();
35 #endif // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
36 }
37
PostTask(std::function<void ()> task)38 void PerfettoTaskRunner::PostTask(std::function<void()> task) {
39 PostDelayedTask(task, /* delay_ms */ 0);
40 }
41
PostDelayedTask(std::function<void ()> task,uint32_t delay_ms)42 void PerfettoTaskRunner::PostDelayedTask(std::function<void()> task,
43 uint32_t delay_ms) {
44 base::ScopedDeferTaskPosting::PostOrDefer(
45 GetOrCreateTaskRunner(), FROM_HERE,
46 base::BindOnce(
47 [](std::function<void()> task) {
48 // We block any trace events that happens while any
49 // Perfetto task is running, or we'll get deadlocks in
50 // situations where the StartupTraceWriterRegistry tries
51 // to bind a writer which in turn causes a PostTask where
52 // a trace event can be emitted, which then deadlocks as
53 // it needs a new chunk from the same StartupTraceWriter
54 // which we're trying to bind and are keeping the lock
55 // to.
56 // TODO(oysteine): Try to see if we can be more selective
57 // about this.
58 const AutoReset<bool> resetter(GetThreadIsInTraceEvent(), true,
59 false);
60 task();
61 },
62 task),
63 base::Milliseconds(delay_ms));
64 }
65
RunsTasksOnCurrentThread() const66 bool PerfettoTaskRunner::RunsTasksOnCurrentThread() const {
67 DCHECK(task_runner_);
68 return task_runner_->RunsTasksInCurrentSequence();
69 }
70
71 // PlatformHandle is an int on POSIX, a HANDLE on Windows.
AddFileDescriptorWatch(perfetto::base::PlatformHandle fd,std::function<void ()> callback)72 void PerfettoTaskRunner::AddFileDescriptorWatch(
73 perfetto::base::PlatformHandle fd,
74 std::function<void()> callback) {
75 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
76 DCHECK(GetOrCreateTaskRunner()->RunsTasksInCurrentSequence());
77 DCHECK(!base::Contains(fd_controllers_, fd));
78 // Set up the |fd| in the map to signal intent to add a watch. We need to
79 // PostTask the WatchReadable creation because if we do it in this task we'll
80 // race with perfetto setting up the connection on this task and the IO thread
81 // setting up epoll on the |fd|. Using a CancelableOnceClosure ensures that
82 // the |fd| won't be added for watch if RemoveFileDescriptorWatch is called.
83 fd_controllers_[fd].callback.Reset(
84 base::BindOnce(
85 [](PerfettoTaskRunner* perfetto_runner, int fd,
86 std::function<void()> callback) {
87 DCHECK(perfetto_runner->GetOrCreateTaskRunner()
88 ->RunsTasksInCurrentSequence());
89 // When this callback runs, we must not have removed |fd|'s watch.
90 CHECK(base::Contains(perfetto_runner->fd_controllers_, fd));
91 auto& controller_and_cb = perfetto_runner->fd_controllers_[fd];
92 // We should never overwrite an existing watch.
93 CHECK(!controller_and_cb.controller);
94 controller_and_cb.controller =
95 base::FileDescriptorWatcher::WatchReadable(
96 fd, base::BindRepeating(
97 [](std::function<void()> callback) { callback(); },
98 std::move(callback)));
99 },
100 base::Unretained(this), fd, std::move(callback)));
101 task_runner_->PostTask(FROM_HERE, fd_controllers_[fd].callback.callback());
102 #else // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
103 NOTREACHED();
104 #endif // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
105 }
106
RemoveFileDescriptorWatch(perfetto::base::PlatformHandle fd)107 void PerfettoTaskRunner::RemoveFileDescriptorWatch(
108 perfetto::base::PlatformHandle fd) {
109 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
110 DCHECK(GetOrCreateTaskRunner()->RunsTasksInCurrentSequence());
111 DCHECK(base::Contains(fd_controllers_, fd));
112 // This also cancels the base::FileDescriptorWatcher::WatchReadable() task if
113 // it's pending.
114 fd_controllers_.erase(fd);
115 #else // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
116 NOTREACHED();
117 #endif // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
118 }
119
ResetTaskRunnerForTesting(scoped_refptr<base::SequencedTaskRunner> task_runner)120 void PerfettoTaskRunner::ResetTaskRunnerForTesting(
121 scoped_refptr<base::SequencedTaskRunner> task_runner) {
122 task_runner_ = std::move(task_runner);
123 }
124
SetTaskRunner(scoped_refptr<base::SequencedTaskRunner> task_runner)125 void PerfettoTaskRunner::SetTaskRunner(
126 scoped_refptr<base::SequencedTaskRunner> task_runner) {
127 DCHECK(!task_runner_);
128 task_runner_ = std::move(task_runner);
129 }
130
131 scoped_refptr<base::SequencedTaskRunner>
GetOrCreateTaskRunner()132 PerfettoTaskRunner::GetOrCreateTaskRunner() {
133 // TODO(eseckler): This is not really thread-safe. We should probably add a
134 // lock around this. At the moment we can get away without one because this
135 // method is called for the first time on the process's main thread before the
136 // tracing service connects.
137 if (!task_runner_) {
138 DCHECK(base::ThreadPoolInstance::Get());
139 task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
140 {base::MayBlock(), base::TaskPriority::USER_BLOCKING});
141 }
142
143 return task_runner_;
144 }
145
146 #if (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
147 PerfettoTaskRunner::FDControllerAndCallback::FDControllerAndCallback() =
148 default;
149
150 PerfettoTaskRunner::FDControllerAndCallback::~FDControllerAndCallback() =
151 default;
152 #endif // (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)) || BUILDFLAG(IS_FUCHSIA)
153
154 } // namespace tracing
155 } // namespace base
156