base: Factor task debug annotations out of MessageLoop
The MessageLoop has some code for adding debug annotations such as trace
flows and memory usage tags to posted tasks. Since we want to use these
annotations more generally for tasks that are scheduled outside the base
message loop, this patch factors it out into a standalone class.
TEST=TaskAnnotatorTest
BUG=391005
Review URL: https://codereview.chromium.org/455833004
Cr-Commit-Position: refs/heads/master@{#289560}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289560 0039d316-1c4b-4281-b951-d872f2087c98
CrOS-Libchrome-Original-Commit: ad8fb459e07068582588d72fd5dabdb72e70b689
diff --git a/base/base.gyp b/base/base.gyp
index ce2c54f..779564a 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -447,6 +447,7 @@
'debug/leak_tracker_unittest.cc',
'debug/proc_maps_linux_unittest.cc',
'debug/stack_trace_unittest.cc',
+ 'debug/task_annotator_unittest.cc',
'debug/trace_event_argument_unittest.cc',
'debug/trace_event_memory_unittest.cc',
'debug/trace_event_synthetic_delay_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index bf8bd59..8a3a56c 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -158,6 +158,8 @@
'debug/stack_trace_android.cc',
'debug/stack_trace_posix.cc',
'debug/stack_trace_win.cc',
+ 'debug/task_annotator.cc',
+ 'debug/task_annotator.h',
'debug/trace_event.h',
'debug/trace_event_android.cc',
'debug/trace_event_argument.cc',
diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc
new file mode 100644
index 0000000..c6576c8
--- /dev/null
+++ b/base/debug/task_annotator.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 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/debug/task_annotator.h"
+
+#include "base/debug/alias.h"
+#include "base/debug/trace_event.h"
+#include "base/pending_task.h"
+#include "base/tracked_objects.h"
+
+namespace base {
+namespace debug {
+
+TaskAnnotator::TaskAnnotator() {
+}
+
+TaskAnnotator::~TaskAnnotator() {
+}
+
+void TaskAnnotator::DidQueueTask(const char* queue_function,
+ const PendingTask& pending_task) {
+ TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ queue_function,
+ TRACE_ID_MANGLE(GetTaskTraceID(pending_task)));
+}
+
+void TaskAnnotator::RunTask(const char* queue_function,
+ const char* run_function,
+ const PendingTask& pending_task) {
+ tracked_objects::TrackedTime start_time =
+ tracked_objects::ThreadData::NowForStartOfRun(pending_task.birth_tally);
+ tracked_objects::Duration queue_duration =
+ start_time - pending_task.EffectiveTimePosted();
+
+ TRACE_EVENT_FLOW_END1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ queue_function,
+ TRACE_ID_MANGLE(GetTaskTraceID(pending_task)),
+ "queue_duration",
+ queue_duration.InMilliseconds());
+
+ // When tracing memory for posted tasks it's more valuable to attribute the
+ // memory allocations to the source function than generically to the task
+ // runner.
+ TRACE_EVENT_WITH_MEMORY_TAG2(
+ "toplevel",
+ run_function,
+ pending_task.posted_from.function_name(), // Name for memory tracking.
+ "src_file",
+ pending_task.posted_from.file_name(),
+ "src_func",
+ pending_task.posted_from.function_name());
+
+ // Before running the task, store the program counter where it was posted
+ // and deliberately alias it to ensure it is on the stack if the task
+ // crashes. Be careful not to assume that the variable itself will have the
+ // expected value when displayed by the optimizer in an optimized build.
+ // Look at a memory dump of the stack.
+ const void* program_counter = pending_task.posted_from.program_counter();
+ debug::Alias(&program_counter);
+
+ pending_task.task.Run();
+
+ tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(
+ pending_task, start_time, tracked_objects::ThreadData::NowForEndOfRun());
+}
+
+uint64 TaskAnnotator::GetTaskTraceID(const PendingTask& task) const {
+ return (static_cast<uint64>(task.sequence_num) << 32) |
+ ((static_cast<uint64>(reinterpret_cast<intptr_t>(this)) << 32) >> 32);
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/debug/task_annotator.h b/base/debug/task_annotator.h
new file mode 100644
index 0000000..aa5f17b
--- /dev/null
+++ b/base/debug/task_annotator.h
@@ -0,0 +1,46 @@
+// Copyright 2014 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.
+
+#ifndef BASE_DEBUG_TASK_ANNOTATOR_H_
+#define BASE_DEBUG_TASK_ANNOTATOR_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+namespace base {
+struct PendingTask;
+namespace debug {
+
+// Implements common debug annotations for posted tasks. This includes data
+// such as task origins, queueing durations and memory usage.
+class BASE_EXPORT TaskAnnotator {
+ public:
+ TaskAnnotator();
+ ~TaskAnnotator();
+
+ // Called to indicate that a task has been queued to run in the future.
+ // |queue_function| is used as the trace flow event name.
+ void DidQueueTask(const char* queue_function,
+ const PendingTask& pending_task);
+
+ // Run a previously queued task. |queue_function| should match what was
+ // passed into |DidQueueTask| for this task. |run_function| is used as the
+ // name for the trace event that surrounds the task's execution.
+ void RunTask(const char* queue_function,
+ const char* run_function,
+ const PendingTask& pending_task);
+
+ private:
+ // Creates a process-wide unique ID to represent this task in trace events.
+ // This will be mangled with a Process ID hash to reduce the likelyhood of
+ // colliding with TaskAnnotator pointers on other processes.
+ uint64 GetTaskTraceID(const PendingTask& task) const;
+
+ DISALLOW_COPY_AND_ASSIGN(TaskAnnotator);
+};
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_TASK_ANNOTATOR_H_
diff --git a/base/debug/task_annotator_unittest.cc b/base/debug/task_annotator_unittest.cc
new file mode 100644
index 0000000..ddffc21
--- /dev/null
+++ b/base/debug/task_annotator_unittest.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 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/debug/task_annotator.h"
+#include "base/bind.h"
+#include "base/pending_task.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace debug {
+namespace {
+
+void TestTask(int* result) {
+ *result = 123;
+}
+
+} // namespace
+
+TEST(TaskAnnotatorTest, QueueAndRunTask) {
+ int result = 0;
+ PendingTask pending_task(FROM_HERE, Bind(&TestTask, &result));
+
+ TaskAnnotator annotator;
+ annotator.DidQueueTask("TaskAnnotatorTest::Queue", pending_task);
+ EXPECT_EQ(0, result);
+ annotator.RunTask(
+ "TaskAnnotatorTest::Queue", "TaskAnnotatorTest::Run", pending_task);
+ EXPECT_EQ(123, result);
+}
+
+} // namespace debug
+} // namespace base
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc
index bcc712b..9bd12ee 100644
--- a/base/message_loop/incoming_task_queue.cc
+++ b/base/message_loop/incoming_task_queue.cc
@@ -4,7 +4,6 @@
#include "base/message_loop/incoming_task_queue.h"
-#include "base/debug/trace_event.h"
#include "base/location.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
@@ -130,9 +129,8 @@
// delayed_run_time value) and for identifying the task in about:tracing.
pending_task->sequence_num = next_sequence_num_++;
- TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
- "MessageLoop::PostTask",
- TRACE_ID_MANGLE(message_loop_->GetTaskTraceID(*pending_task)));
+ message_loop_->task_annotator()->DidQueueTask("MessageLoop::PostTask",
+ *pending_task);
bool was_empty = incoming_queue_.empty();
incoming_queue_.push(*pending_task);
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index c0d9b0e..69a02a3 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -8,8 +8,6 @@
#include "base/bind.h"
#include "base/compiler_specific.h"
-#include "base/debug/alias.h"
-#include "base/debug/trace_event.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
@@ -423,45 +421,20 @@
}
void MessageLoop::RunTask(const PendingTask& pending_task) {
- tracked_objects::TrackedTime start_time =
- tracked_objects::ThreadData::NowForStartOfRun(pending_task.birth_tally);
-
- TRACE_EVENT_FLOW_END1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
- "MessageLoop::PostTask", TRACE_ID_MANGLE(GetTaskTraceID(pending_task)),
- "queue_duration",
- (start_time - pending_task.EffectiveTimePosted()).InMilliseconds());
- // When tracing memory for posted tasks it's more valuable to attribute the
- // memory allocations to the source function than generically to "RunTask".
- TRACE_EVENT_WITH_MEMORY_TAG2(
- "toplevel", "MessageLoop::RunTask",
- pending_task.posted_from.function_name(), // Name for memory tracking.
- "src_file", pending_task.posted_from.file_name(),
- "src_func", pending_task.posted_from.function_name());
-
DCHECK(nestable_tasks_allowed_);
+
// Execute the task and assume the worst: It is probably not reentrant.
nestable_tasks_allowed_ = false;
- // Before running the task, store the program counter where it was posted
- // and deliberately alias it to ensure it is on the stack if the task
- // crashes. Be careful not to assume that the variable itself will have the
- // expected value when displayed by the optimizer in an optimized build.
- // Look at a memory dump of the stack.
- const void* program_counter =
- pending_task.posted_from.program_counter();
- debug::Alias(&program_counter);
-
HistogramEvent(kTaskRunEvent);
FOR_EACH_OBSERVER(TaskObserver, task_observers_,
WillProcessTask(pending_task));
- pending_task.task.Run();
+ task_annotator_.RunTask(
+ "MessageLoop::PostTask", "MessageLoop::RunTask", pending_task);
FOR_EACH_OBSERVER(TaskObserver, task_observers_,
DidProcessTask(pending_task));
- tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(pending_task,
- start_time, tracked_objects::ThreadData::NowForEndOfRun());
-
nestable_tasks_allowed_ = true;
}
@@ -513,11 +486,6 @@
return did_work;
}
-uint64 MessageLoop::GetTaskTraceID(const PendingTask& task) {
- return (static_cast<uint64>(task.sequence_num) << 32) |
- ((static_cast<uint64>(reinterpret_cast<intptr_t>(this)) << 32) >> 32);
-}
-
void MessageLoop::ReloadWorkQueue() {
// We can improve performance of our loading tasks from the incoming queue to
// |*work_queue| by waiting until the last minute (|*work_queue| is empty) to
diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h
index 1d13b12..39a1b68 100644
--- a/base/message_loop/message_loop.h
+++ b/base/message_loop/message_loop.h
@@ -11,6 +11,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/callback_forward.h"
+#include "base/debug/task_annotator.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
@@ -426,10 +427,9 @@
// true if some work was done.
bool DeletePendingTasks();
- // Creates a process-wide unique ID to represent this task in trace events.
- // This will be mangled with a Process ID hash to reduce the likelyhood of
- // colliding with MessageLoop pointers on other processes.
- uint64 GetTaskTraceID(const PendingTask& task);
+ // Returns the TaskAnnotator which is used to add debug information to posted
+ // tasks.
+ debug::TaskAnnotator* task_annotator() { return &task_annotator_; }
// Loads tasks from the incoming queue to |work_queue_| if the latter is
// empty.
@@ -490,6 +490,8 @@
ObserverList<TaskObserver> task_observers_;
+ debug::TaskAnnotator task_annotator_;
+
scoped_refptr<internal::IncomingTaskQueue> incoming_task_queue_;
// The message loop proxy associated with this message loop.