• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/task/common/task_annotator.h"
6 
7 #include <stdint.h>
8 #include <algorithm>
9 #include <array>
10 
11 #include "base/auto_reset.h"
12 #include "base/check_op.h"
13 #include "base/compiler_specific.h"
14 #include "base/debug/alias.h"
15 #include "base/hash/md5.h"
16 #include "base/logging.h"
17 #include "base/ranges/algorithm.h"
18 #include "base/sys_byteorder.h"
19 #include "base/time/time.h"
20 #include "base/trace_event/base_tracing.h"
21 #include "base/tracing_buildflags.h"
22 #include "build/build_config.h"
23 #include "third_party/abseil-cpp/absl/base/attributes.h"
24 
25 #if BUILDFLAG(ENABLE_BASE_TRACING)
26 #include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.h"  // nogncheck
27 #endif
28 
29 namespace base {
30 
31 namespace {
32 
33 TaskAnnotator::ObserverForTesting* g_task_annotator_observer = nullptr;
34 
35 // The PendingTask currently in progress on each thread. Used to allow creating
36 // a breadcrumb of program counters on the stack to help identify a task's
37 // origin in crashes.
38 ABSL_CONST_INIT thread_local PendingTask* current_pending_task = nullptr;
39 
40 // Scoped IPC-related data (IPC hash and/or IPC interface name). IPC hash or
41 // interface name can be known before the associated task object is created;
42 // thread-local so that this data can be affixed to the associated task.
43 ABSL_CONST_INIT thread_local TaskAnnotator::ScopedSetIpcHash*
44     current_scoped_ipc_hash = nullptr;
45 
46 ABSL_CONST_INIT thread_local TaskAnnotator::LongTaskTracker*
47     current_long_task_tracker = nullptr;
48 
49 // These functions can be removed, and the calls below replaced with direct
50 // variable accesses, once the MSAN workaround is not necessary.
GetCurrentScopedIpcHash()51 TaskAnnotator::ScopedSetIpcHash* GetCurrentScopedIpcHash() {
52   // Workaround false-positive MSAN use-of-uninitialized-value on
53   // thread_local storage for loaded libraries:
54   // https://github.com/google/sanitizers/issues/1265
55   MSAN_UNPOISON(&current_scoped_ipc_hash,
56                 sizeof(TaskAnnotator::ScopedSetIpcHash*));
57 
58   return current_scoped_ipc_hash;
59 }
60 
GetCurrentLongTaskTracker()61 TaskAnnotator::LongTaskTracker* GetCurrentLongTaskTracker() {
62   // Workaround false-positive MSAN use-of-uninitialized-value on
63   // thread_local storage for loaded libraries:
64   // https://github.com/google/sanitizers/issues/1265
65   MSAN_UNPOISON(&current_long_task_tracker,
66                 sizeof(TaskAnnotator::LongTaskTracker*));
67 
68   return current_long_task_tracker;
69 }
70 
71 }  // namespace
72 
CurrentTaskForThread()73 const PendingTask* TaskAnnotator::CurrentTaskForThread() {
74   // Workaround false-positive MSAN use-of-uninitialized-value on
75   // thread_local storage for loaded libraries:
76   // https://github.com/google/sanitizers/issues/1265
77   MSAN_UNPOISON(&current_pending_task, sizeof(PendingTask*));
78 
79   return current_pending_task;
80 }
81 
OnIPCReceived(const char * interface_name,uint32_t (* method_info)(),bool is_response)82 void TaskAnnotator::OnIPCReceived(const char* interface_name,
83                                   uint32_t (*method_info)(),
84                                   bool is_response) {
85   auto* const tracker = GetCurrentLongTaskTracker();
86   if (!tracker) {
87     return;
88   }
89 
90   tracker->SetIpcDetails(interface_name, method_info, is_response);
91 }
92 
MarkCurrentTaskAsInterestingForTracing()93 void TaskAnnotator::MarkCurrentTaskAsInterestingForTracing() {
94   auto* const tracker = GetCurrentLongTaskTracker();
95   if (!tracker) {
96     return;
97   }
98 
99   tracker->is_interesting_task = true;
100 }
101 
102 TaskAnnotator::TaskAnnotator() = default;
103 TaskAnnotator::~TaskAnnotator() = default;
104 
WillQueueTask(perfetto::StaticString trace_event_name,PendingTask * pending_task)105 void TaskAnnotator::WillQueueTask(perfetto::StaticString trace_event_name,
106                                   PendingTask* pending_task) {
107   DCHECK(pending_task);
108   TRACE_EVENT_INSTANT(
109       "toplevel.flow", trace_event_name,
110       perfetto::Flow::ProcessScoped(GetTaskTraceID(*pending_task)));
111 
112   DCHECK(!pending_task->task_backtrace[0])
113       << "Task backtrace was already set, task posted twice??";
114   if (pending_task->task_backtrace[0])
115     return;
116 
117   DCHECK(!pending_task->ipc_interface_name);
118   DCHECK(!pending_task->ipc_hash);
119   const auto* const hash = GetCurrentScopedIpcHash();
120   if (hash) {
121     pending_task->ipc_interface_name = hash->GetIpcInterfaceName();
122     pending_task->ipc_hash = hash->GetIpcHash();
123   }
124 
125   const auto* parent_task = CurrentTaskForThread();
126   if (!parent_task)
127     return;
128 
129   pending_task->task_backtrace[0] = parent_task->posted_from.program_counter();
130   std::copy(parent_task->task_backtrace.begin(),
131             parent_task->task_backtrace.end() - 1,
132             pending_task->task_backtrace.begin() + 1);
133   pending_task->task_backtrace_overflow =
134       parent_task->task_backtrace_overflow ||
135       parent_task->task_backtrace.back() != nullptr;
136 }
137 
RunTaskImpl(PendingTask & pending_task)138 void TaskAnnotator::RunTaskImpl(PendingTask& pending_task) {
139   TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION(
140       pending_task.posted_from.file_name());
141 
142   // Before running the task, store the IPC context and the task backtrace with
143   // the chain of PostTasks that resulted in this call and deliberately alias it
144   // to ensure it is on the stack if the task crashes. Be careful not to assume
145   // that the variable itself will have the expected value when displayed by the
146   // optimizer in an optimized build. Look at a memory dump of the stack.
147   static constexpr int kStackTaskTraceSnapshotSize =
148       PendingTask::kTaskBacktraceLength + 4;
149   std::array<const void*, kStackTaskTraceSnapshotSize> task_backtrace;
150 
151   // Store a marker to locate |task_backtrace| content easily on a memory
152   // dump. The layout is as follows:
153   //
154   // +------------ +----+---------+-----+-----------+----------+-------------+
155   // | Head Marker | PC | frame 0 | ... | frame N-1 | IPC hash | Tail Marker |
156   // +------------ +----+---------+-----+-----------+----------+-------------+
157   //
158   // Markers glossary (compliments of wez):
159   //      cool code,do it dude!
160   //   0x c001 c0de d0 17 d00d
161   //      o dude,i did it biig
162   //   0x 0 d00d 1 d1d 17 8119
163   task_backtrace.front() = reinterpret_cast<void*>(0xc001c0ded017d00d);
164   task_backtrace.back() = reinterpret_cast<void*>(0x0d00d1d1d178119);
165 
166   task_backtrace[1] = pending_task.posted_from.program_counter();
167   ranges::copy(pending_task.task_backtrace, task_backtrace.begin() + 2);
168   task_backtrace[kStackTaskTraceSnapshotSize - 2] =
169       reinterpret_cast<void*>(pending_task.ipc_hash);
170   debug::Alias(&task_backtrace);
171 
172   // Record the task time in convenient units. This can be compared to times
173   // stored in places like ReportThreadHang() and BrowserMain() when analyzing
174   // hangs.
175   const int64_t task_time =
176       pending_task.GetDesiredExecutionTime().since_origin().InSeconds();
177   base::debug::Alias(&task_time);
178 
179   {
180     const AutoReset<PendingTask*> resetter(&current_pending_task,
181                                            &pending_task);
182 
183     if (g_task_annotator_observer) {
184       g_task_annotator_observer->BeforeRunTask(&pending_task);
185     }
186     std::move(pending_task.task).Run();
187 #if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_FAMILY)
188     // Some tasks on some machines clobber the non-volatile XMM registers in
189     // violation of the Windows ABI. This empty assembly language block with
190     // clobber directives tells the compiler to assume that these registers
191     // may have lost their values. This ensures that this function will not rely
192     // on the registers retaining their values, and it ensures that it will
193     // restore the values when this function ends. This is needed because the
194     // code-gen for at least one caller of this function in official builds
195     // relies on an XMM register (usually XMM7, cleared to zero) maintaining its
196     // value as multiple tasks are run, which causes crashes if it is corrupted,
197     // since "zeroed" variables end up not being zeroed. The third-party issue
198     // is believed to be fixed but will take a while to propagate to users which
199     // is why this mitigation is needed. For details see
200     // https://crbug.com/1218384.
201     asm(""
202         :
203         :
204         : "%xmm6", "%xmm7", "%xmm8", "%xmm9", "%xmm10", "%xmm11", "%xmm12",
205           "%xmm13", "%xmm14", "%xmm15");
206 #endif
207   }
208 
209   // Stomp the markers. Otherwise they can stick around on the unused parts of
210   // stack and cause |task_backtrace| to be associated with an unrelated stack
211   // sample on this thread later in the event of a crash. Alias once again after
212   // these writes to make sure the compiler doesn't optimize them out (unused
213   // writes to a local variable).
214   task_backtrace.front() = nullptr;
215   task_backtrace.back() = nullptr;
216   debug::Alias(&task_backtrace);
217 }
218 
GetTaskTraceID(const PendingTask & task) const219 uint64_t TaskAnnotator::GetTaskTraceID(const PendingTask& task) const {
220   return (static_cast<uint64_t>(task.sequence_num) << 32) |
221          ((static_cast<uint64_t>(reinterpret_cast<intptr_t>(this)) << 32) >>
222           32);
223 }
224 
225 // static
RegisterObserverForTesting(ObserverForTesting * observer)226 void TaskAnnotator::RegisterObserverForTesting(ObserverForTesting* observer) {
227   DCHECK(!g_task_annotator_observer);
228   g_task_annotator_observer = observer;
229 }
230 
231 // static
ClearObserverForTesting()232 void TaskAnnotator::ClearObserverForTesting() {
233   g_task_annotator_observer = nullptr;
234 }
235 
236 #if BUILDFLAG(ENABLE_BASE_TRACING)
237 // TRACE_EVENT argument helper, writing the task location data into
238 // EventContext.
EmitTaskLocation(perfetto::EventContext & ctx,const PendingTask & task)239 void TaskAnnotator::EmitTaskLocation(perfetto::EventContext& ctx,
240                                      const PendingTask& task) {
241   ctx.event()->set_task_execution()->set_posted_from_iid(
242       base::trace_event::InternedSourceLocation::Get(&ctx, task.posted_from));
243 }
244 
245 // TRACE_EVENT argument helper, writing the incoming task flow information
246 // into EventContext if toplevel.flow category is enabled.
MaybeEmitIncomingTaskFlow(perfetto::EventContext & ctx,const PendingTask & task) const247 void TaskAnnotator::MaybeEmitIncomingTaskFlow(perfetto::EventContext& ctx,
248                                               const PendingTask& task) const {
249   static const uint8_t* flow_enabled =
250       TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("toplevel.flow");
251   if (!*flow_enabled)
252     return;
253 
254   perfetto::TerminatingFlow::ProcessScoped(GetTaskTraceID(task))(ctx);
255 }
256 
MaybeEmitIPCHashAndDelay(perfetto::EventContext & ctx,const PendingTask & task) const257 void TaskAnnotator::MaybeEmitIPCHashAndDelay(perfetto::EventContext& ctx,
258                                              const PendingTask& task) const {
259   static const uint8_t* toplevel_ipc_enabled =
260       TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(
261           TRACE_DISABLED_BY_DEFAULT("toplevel.ipc"));
262   if (!*toplevel_ipc_enabled)
263     return;
264 
265   auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
266   auto* annotator = event->set_chrome_task_annotator();
267   annotator->set_ipc_hash(task.ipc_hash);
268   if (!task.delayed_run_time.is_null()) {
269     annotator->set_task_delay_us(static_cast<uint64_t>(
270         (task.delayed_run_time - task.queue_time).InMicroseconds()));
271   }
272 }
273 #endif  //  BUILDFLAG(ENABLE_BASE_TRACING)
274 
ScopedSetIpcHash(uint32_t ipc_hash)275 TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash(uint32_t ipc_hash)
276     : ScopedSetIpcHash(ipc_hash, nullptr) {}
277 
ScopedSetIpcHash(const char * ipc_interface_name)278 TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash(
279     const char* ipc_interface_name)
280     : ScopedSetIpcHash(0, ipc_interface_name) {}
281 
ScopedSetIpcHash(uint32_t ipc_hash,const char * ipc_interface_name)282 TaskAnnotator::ScopedSetIpcHash::ScopedSetIpcHash(
283     uint32_t ipc_hash,
284     const char* ipc_interface_name)
285     : resetter_(&current_scoped_ipc_hash, this),
286       ipc_hash_(ipc_hash),
287       ipc_interface_name_(ipc_interface_name) {}
288 
289 // Static
MD5HashMetricName(base::StringPiece name)290 uint32_t TaskAnnotator::ScopedSetIpcHash::MD5HashMetricName(
291     base::StringPiece name) {
292   base::MD5Digest digest;
293   base::MD5Sum(name.data(), name.size(), &digest);
294   uint32_t value;
295   DCHECK_GE(sizeof(digest.a), sizeof(value));
296   memcpy(&value, digest.a, sizeof(value));
297   return base::NetToHost32(value);
298 }
299 
~ScopedSetIpcHash()300 TaskAnnotator::ScopedSetIpcHash::~ScopedSetIpcHash() {
301   DCHECK_EQ(this, GetCurrentScopedIpcHash());
302 }
303 
LongTaskTracker(const TickClock * tick_clock,PendingTask & pending_task,TaskAnnotator * task_annotator)304 TaskAnnotator::LongTaskTracker::LongTaskTracker(const TickClock* tick_clock,
305                                                 PendingTask& pending_task,
306                                                 TaskAnnotator* task_annotator)
307     : resetter_(&current_long_task_tracker, this),
308       tick_clock_(tick_clock),
309       pending_task_(pending_task),
310       task_annotator_(task_annotator) {
311   TRACE_EVENT_CATEGORY_GROUP_ENABLED("scheduler.long_tasks", &is_tracing_);
312   if (is_tracing_) {
313     task_start_time_ = tick_clock_->NowTicks();
314   }
315 }
316 
~LongTaskTracker()317 TaskAnnotator::LongTaskTracker::~LongTaskTracker() {
318   DCHECK_EQ(this, GetCurrentLongTaskTracker());
319 
320   if (!is_tracing_)
321     return;
322 
323   task_end_time_ = tick_clock_->NowTicks();
324   MaybeTraceInterestingTaskDetails();
325 
326   if ((task_end_time_ - task_start_time_) >= kMaxTaskDurationTimeDelta) {
327     TRACE_EVENT_BEGIN("scheduler.long_tasks", "LongTaskTracker",
328                       perfetto::Track::ThreadScoped(task_annotator_),
329                       task_start_time_, [&](perfetto::EventContext& ctx) {
330                         TaskAnnotator::EmitTaskLocation(ctx, *pending_task_);
331                         EmitReceivedIPCDetails(ctx);
332                       });
333     TRACE_EVENT_END("scheduler.long_tasks",
334                     perfetto::Track::ThreadScoped(task_annotator_),
335                     task_end_time_);
336   }
337 }
338 
SetIpcDetails(const char * interface_name,uint32_t (* method_info)(),bool is_response)339 void TaskAnnotator::LongTaskTracker::SetIpcDetails(const char* interface_name,
340                                                    uint32_t (*method_info)(),
341                                                    bool is_response) {
342   ipc_interface_name_ = interface_name;
343   is_response_ = is_response;
344 
345   if (!method_info)
346     return;
347 
348   ipc_hash_ = (*method_info)();
349   ipc_method_info_ = method_info;
350 }
351 
EmitReceivedIPCDetails(perfetto::EventContext & ctx)352 void TaskAnnotator::LongTaskTracker::EmitReceivedIPCDetails(
353     perfetto::EventContext& ctx) {
354   if (!ipc_interface_name_ || !ipc_hash_ || !ipc_method_info_)
355     return;
356 #if BUILDFLAG(ENABLE_BASE_TRACING) && !BUILDFLAG(IS_NACL)
357   // Emit all of the IPC hash information if this task
358   // comes from a mojo interface.
359   auto* info = ctx.event()->set_chrome_mojo_event_info();
360   info->set_mojo_interface_tag(ipc_interface_name_);
361   info->set_ipc_hash(ipc_hash_);
362   info->set_is_reply(is_response_);
363 
364   // The Native client will not build as the relevant implementation of
365   // base::ModuleCache::CreateModuleForAddress is not implemented for it.
366   // Thus the below code must be included on a conditional basis.
367   const auto ipc_method_address = reinterpret_cast<uintptr_t>(ipc_method_info_);
368   const absl::optional<size_t> location_iid =
369       base::trace_event::InternedUnsymbolizedSourceLocation::Get(
370           &ctx, ipc_method_address);
371   if (location_iid) {
372     info->set_mojo_interface_method_iid(*location_iid);
373   }
374 #endif
375 }
376 
377 // This method is used to record the queueing time and task start time for tasks
378 // that may be of interest during a trace, even if they are not considered long
379 // tasks. For example, input - the queue time and flow information is required
380 // to calculate chrome input to browser intervals in perfetto, and further
381 // calculate the chrome tasks blocking input. We need LatencyInfo slices to be
382 // associated with the correct input IPCs, hence record in the LongTaskTracker.
MaybeTraceInterestingTaskDetails()383 void TaskAnnotator::LongTaskTracker::MaybeTraceInterestingTaskDetails() {
384   if (is_interesting_task && ipc_interface_name_) {
385     // Record the equivalent of a delayed instant trace event, acting as the
386     // start of the flow between task queue time and task execution start time.
387     TRACE_EVENT_INSTANT("scheduler.long_tasks", "InterestingTask_QueueingTime",
388                         perfetto::Track::ThreadScoped(task_annotator_),
389                         pending_task_->queue_time,
390                         perfetto::Flow::ProcessScoped(
391                             task_annotator_->GetTaskTraceID(*pending_task_)));
392 
393     // Record the equivalent of a top-level event with enough IPC information
394     // to calculate the input to browser interval. This event will be the
395     // termination of the event above, aka the start of task execution.
396     TRACE_EVENT_BEGIN(
397         "scheduler.long_tasks", "InterestingTask_ProcessingTime",
398         perfetto::Track::ThreadScoped(task_annotator_), task_start_time_,
399         [&](perfetto::EventContext& ctx) {
400           perfetto::TerminatingFlow::ProcessScoped(
401               task_annotator_->GetTaskTraceID(*pending_task_))(ctx);
402           auto* info = ctx.event()->set_chrome_mojo_event_info();
403           info->set_mojo_interface_tag(ipc_interface_name_);
404         });
405 
406     TRACE_EVENT_END("scheduler.long_tasks",
407                     perfetto::Track::ThreadScoped(task_annotator_),
408                     task_end_time_);
409   }
410 }
411 
412 }  // namespace base
413