1 // Copyright 2014 The Chromium Authors. All rights reserved.
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/task/common/task_annotator.h"
6
7 #include <array>
8
9 #include "base/debug/activity_tracker.h"
10 #include "base/debug/alias.h"
11 #include "base/no_destructor.h"
12 #include "base/threading/thread_local.h"
13 #include "base/trace_event/trace_event.h"
14
15 namespace base {
16
17 namespace {
18
19 TaskAnnotator::ObserverForTesting* g_task_annotator_observer = nullptr;
20
21 // Used as a sentinel to determine if a TLS-stored PendingTask is a dummy one.
22 static constexpr int kSentinelSequenceNum =
23 static_cast<int>(0xF00DBAADF00DBAAD);
24
25 // Returns the TLS slot that stores the PendingTask currently in progress on
26 // each thread. Used to allow creating a breadcrumb of program counters on the
27 // stack to help identify a task's origin in crashes.
GetTLSForCurrentPendingTask()28 ThreadLocalPointer<PendingTask>* GetTLSForCurrentPendingTask() {
29 static NoDestructor<ThreadLocalPointer<PendingTask>> instance;
30 return instance.get();
31 }
32
33 // Determines whether or not the given |task| is a dummy pending task that has
34 // been injected by ScopedSetIpcHash solely for the purposes of
35 // tracking IPC context.
IsDummyPendingTask(const PendingTask * task)36 bool IsDummyPendingTask(const PendingTask* task) {
37 if (task->sequence_num == kSentinelSequenceNum &&
38 !task->posted_from.has_source_info() &&
39 !task->posted_from.program_counter()) {
40 return true;
41 }
42 return false;
43 }
44
45 } // namespace
46
CurrentTaskForThread()47 const PendingTask* TaskAnnotator::CurrentTaskForThread() {
48 auto* current_task = GetTLSForCurrentPendingTask()->Get();
49
50 // Don't return "dummy" current tasks that are only used for storing IPC
51 // context.
52 if (current_task && IsDummyPendingTask(current_task))
53 return nullptr;
54 return current_task;
55 }
56
57 TaskAnnotator::TaskAnnotator() = default;
58
59 TaskAnnotator::~TaskAnnotator() = default;
60
WillQueueTask(const char * trace_event_name,PendingTask * pending_task,const char * task_queue_name)61 void TaskAnnotator::WillQueueTask(const char* trace_event_name,
62 PendingTask* pending_task,
63 const char* task_queue_name) {
64 DCHECK(trace_event_name);
65 DCHECK(pending_task);
66 DCHECK(task_queue_name);
67 TRACE_EVENT_WITH_FLOW1(
68 TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), trace_event_name,
69 TRACE_ID_LOCAL(GetTaskTraceID(*pending_task)), TRACE_EVENT_FLAG_FLOW_OUT,
70 "task_queue_name", task_queue_name);
71
72 DCHECK(!pending_task->task_backtrace[0])
73 << "Task backtrace was already set, task posted twice??";
74 if (pending_task->task_backtrace[0])
75 return;
76
77 const auto* parent_task = CurrentTaskForThread();
78 if (!parent_task)
79 return;
80
81 pending_task->ipc_hash = parent_task->ipc_hash;
82 pending_task->task_backtrace[0] = parent_task->posted_from.program_counter();
83 std::copy(parent_task->task_backtrace.begin(),
84 parent_task->task_backtrace.end() - 1,
85 pending_task->task_backtrace.begin() + 1);
86 pending_task->task_backtrace_overflow =
87 parent_task->task_backtrace_overflow ||
88 parent_task->task_backtrace.back() != nullptr;
89 }
90
RunTask(const char * trace_event_name,PendingTask * pending_task)91 void TaskAnnotator::RunTask(const char* trace_event_name,
92 PendingTask* pending_task) {
93 DCHECK(trace_event_name);
94 DCHECK(pending_task);
95
96 debug::ScopedTaskRunActivity task_activity(*pending_task);
97
98 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("toplevel.ipc"),
99 "TaskAnnotator::RunTask", "ipc_hash", pending_task->ipc_hash);
100
101 TRACE_EVENT_WITH_FLOW0(
102 TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), trace_event_name,
103 TRACE_ID_LOCAL(GetTaskTraceID(*pending_task)), TRACE_EVENT_FLAG_FLOW_IN);
104
105 // Before running the task, store the IPC context and the task backtrace with
106 // the chain of PostTasks that resulted in this call and deliberately alias it
107 // to ensure it is on the stack if the task crashes. Be careful not to assume
108 // that the variable itself will have the expected value when displayed by the
109 // optimizer in an optimized build. Look at a memory dump of the stack.
110 static constexpr int kStackTaskTraceSnapshotSize =
111 PendingTask::kTaskBacktraceLength + 4;
112 std::array<const void*, kStackTaskTraceSnapshotSize> task_backtrace;
113
114 // Store a marker to locate |task_backtrace| content easily on a memory
115 // dump. The layout is as follows:
116 //
117 // +------------ +----+---------+-----+-----------+----------+-------------+
118 // | Head Marker | PC | frame 0 | ... | frame N-1 | IPC hash | Tail Marker |
119 // +------------ +----+---------+-----+-----------+----------+-------------+
120 //
121 // Markers glossary (compliments of wez):
122 // cool code,do it dude!
123 // 0x c001 c0de d0 17 d00d
124 // o dude,i did it biig
125 // 0x 0 d00d 1 d1d 17 8119
126 task_backtrace.front() = reinterpret_cast<void*>(0xc001c0ded017d00d);
127 task_backtrace.back() = reinterpret_cast<void*>(0x0d00d1d1d178119);
128
129 task_backtrace[1] = pending_task->posted_from.program_counter();
130 std::copy(pending_task->task_backtrace.begin(),
131 pending_task->task_backtrace.end(), task_backtrace.begin() + 2);
132 task_backtrace[kStackTaskTraceSnapshotSize - 2] =
133 reinterpret_cast<void*>(pending_task->ipc_hash);
134 debug::Alias(&task_backtrace);
135
136 auto* tls = GetTLSForCurrentPendingTask();
137 auto* previous_pending_task = tls->Get();
138 tls->Set(pending_task);
139
140 if (g_task_annotator_observer)
141 g_task_annotator_observer->BeforeRunTask(pending_task);
142 std::move(pending_task->task).Run();
143
144 tls->Set(previous_pending_task);
145 }
146
GetTaskTraceID(const PendingTask & task) const147 uint64_t TaskAnnotator::GetTaskTraceID(const PendingTask& task) const {
148 return (static_cast<uint64_t>(task.sequence_num) << 32) |
149 ((static_cast<uint64_t>(reinterpret_cast<intptr_t>(this)) << 32) >>
150 32);
151 }
152
153 // static
RegisterObserverForTesting(ObserverForTesting * observer)154 void TaskAnnotator::RegisterObserverForTesting(ObserverForTesting* observer) {
155 DCHECK(!g_task_annotator_observer);
156 g_task_annotator_observer = observer;
157 }
158
159 // static
ClearObserverForTesting()160 void TaskAnnotator::ClearObserverForTesting() {
161 g_task_annotator_observer = nullptr;
162 }
163
ScopedSetIpcHash(uint32_t ipc_hash)164 TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash(uint32_t ipc_hash) {
165 // We store the IPC context in the currently running task. If there is none
166 // then introduce a dummy task.
167 auto* tls = GetTLSForCurrentPendingTask();
168 auto* current_task = tls->Get();
169 if (!current_task) {
170 dummy_pending_task_ = std::make_unique<PendingTask>();
171 dummy_pending_task_->sequence_num = kSentinelSequenceNum;
172 current_task = dummy_pending_task_.get();
173 tls->Set(current_task);
174 }
175
176 old_ipc_hash_ = current_task->ipc_hash;
177 current_task->ipc_hash = ipc_hash;
178 }
179
~ScopedSetIpcHash()180 TaskAnnotator::ScopedSetIpcHash::~ScopedSetIpcHash() {
181 auto* tls = GetTLSForCurrentPendingTask();
182 auto* current_task = tls->Get();
183 DCHECK(current_task);
184 if (current_task == dummy_pending_task_.get()) {
185 tls->Set(nullptr);
186 } else {
187 current_task->ipc_hash = old_ipc_hash_;
188 }
189 }
190
191 } // namespace base
192