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