Skip to content

Commit

Permalink
Trace PostTasks from post to run.
Browse files Browse the repository at this point in the history
To do this properly, we need a new type of trace event "FLOW" which will be drawn as lines in about:tracing from BEGIN to END.

Also instruments SequencedWorkerPool and ChannelReader::DispatchInputData, which were not currently traced.

BUG=79942

Review URL: https://codereview.chromium.org/10913242

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@157288 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
jbates@chromium.org committed Sep 18, 2012
1 parent 108a051 commit 2c391df
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 41 deletions.
178 changes: 153 additions & 25 deletions base/debug/trace_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,11 @@
#define TRACE_STR_COPY(str) \
trace_event_internal::TraceStringWithCopy(str)

// By default, uint64 ID argument values are not mangled with the Process ID in
// TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling.
#define TRACE_ID_MANGLE(id) \
trace_event_internal::TraceID::ForceMangle(id)

// Records a pair of begin and end events called "name" for the current
// scope, with 0, 1 or 2 associated arguments. If the category is not
// enabled, then this does nothing.
Expand Down Expand Up @@ -389,8 +394,9 @@
// that the same pointer on two different processes will not collide.
// An asynchronous operation can consist of multiple phases. The first phase is
// defined by the ASYNC_BEGIN calls. Additional phases can be defined using the
// ASYNC_STEP_BEGIN macros. When the operation completes, call ASYNC_END.
// An async operation can span threads and processes, but all events in that
// ASYNC_STEP macros. When the operation completes, call ASYNC_END.
// An ASYNC trace typically occur on a single thread (if not, they will only be
// drawn on the thread defined in the ASYNC_BEGIN event), but all events in that
// operation must use the same |name| and |id|. Each event can have its own
// args.
#define TRACE_EVENT_ASYNC_BEGIN0(category, name, id) \
Expand Down Expand Up @@ -466,6 +472,98 @@
arg1_name, arg1_val, arg2_name, arg2_val)


// Records a single FLOW_BEGIN event called "name" immediately, with 0, 1 or 2
// associated arguments. If the category is not enabled, then this
// does nothing.
// - category and name strings must have application lifetime (statics or
// literals). They may not include " chars.
// - |id| is used to match the FLOW_BEGIN event with the FLOW_END event. FLOW
// events are considered to match if their category, name and id values all
// match. |id| must either be a pointer or an integer value up to 64 bits. If
// it's a pointer, the bits will be xored with a hash of the process ID so
// that the same pointer on two different processes will not collide.
// FLOW events are different from ASYNC events in how they are drawn by the
// tracing UI. A FLOW defines asynchronous data flow, such as posting a task
// (FLOW_BEGIN) and later executing that task (FLOW_END). Expect FLOWs to be
// drawn as lines or arrows from FLOW_BEGIN scopes to FLOW_END scopes. Similar
// to ASYNC, a FLOW can consist of multiple phases. The first phase is defined
// by the FLOW_BEGIN calls. Additional phases can be defined using the FLOW_STEP
// macros. When the operation completes, call FLOW_END. An async operation can
// span threads and processes, but all events in that operation must use the
// same |name| and |id|. Each event can have its own args.
#define TRACE_EVENT_FLOW_BEGIN0(category, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
category, name, id, TRACE_EVENT_FLAG_NONE)
#define TRACE_EVENT_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
#define TRACE_EVENT_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \
arg2_name, arg2_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
category, name, id, TRACE_EVENT_FLAG_NONE, \
arg1_name, arg1_val, arg2_name, arg2_val)
#define TRACE_EVENT_COPY_FLOW_BEGIN0(category, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
category, name, id, TRACE_EVENT_FLAG_COPY)
#define TRACE_EVENT_COPY_FLOW_BEGIN1(category, name, id, arg1_name, arg1_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
category, name, id, TRACE_EVENT_FLAG_COPY, \
arg1_name, arg1_val)
#define TRACE_EVENT_COPY_FLOW_BEGIN2(category, name, id, arg1_name, arg1_val, \
arg2_name, arg2_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_BEGIN, \
category, name, id, TRACE_EVENT_FLAG_COPY, \
arg1_name, arg1_val, arg2_name, arg2_val)

// Records a single FLOW_STEP event for |step| immediately. If the category
// is not enabled, then this does nothing. The |name| and |id| must match the
// FLOW_BEGIN event above. The |step| param identifies this step within the
// async event. This should be called at the beginning of the next phase of an
// asynchronous operation.
#define TRACE_EVENT_FLOW_STEP0(category, name, id, step) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
category, name, id, TRACE_EVENT_FLAG_NONE, "step", step)
#define TRACE_EVENT_FLOW_STEP1(category, name, id, step, \
arg1_name, arg1_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
category, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \
arg1_name, arg1_val)
#define TRACE_EVENT_COPY_FLOW_STEP0(category, name, id, step) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
category, name, id, TRACE_EVENT_FLAG_COPY, "step", step)
#define TRACE_EVENT_COPY_FLOW_STEP1(category, name, id, step, \
arg1_name, arg1_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_STEP, \
category, name, id, TRACE_EVENT_FLAG_COPY, "step", step, \
arg1_name, arg1_val)

// Records a single FLOW_END event for "name" immediately. If the category
// is not enabled, then this does nothing.
#define TRACE_EVENT_FLOW_END0(category, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
category, name, id, TRACE_EVENT_FLAG_NONE)
#define TRACE_EVENT_FLOW_END1(category, name, id, arg1_name, arg1_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
category, name, id, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
#define TRACE_EVENT_FLOW_END2(category, name, id, arg1_name, arg1_val, \
arg2_name, arg2_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
category, name, id, TRACE_EVENT_FLAG_NONE, \
arg1_name, arg1_val, arg2_name, arg2_val)
#define TRACE_EVENT_COPY_FLOW_END0(category, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
category, name, id, TRACE_EVENT_FLAG_COPY)
#define TRACE_EVENT_COPY_FLOW_END1(category, name, id, arg1_name, arg1_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
category, name, id, TRACE_EVENT_FLAG_COPY, \
arg1_name, arg1_val)
#define TRACE_EVENT_COPY_FLOW_END2(category, name, id, arg1_name, arg1_val, \
arg2_name, arg2_val) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_FLOW_END, \
category, name, id, TRACE_EVENT_FLAG_COPY, \
arg1_name, arg1_val, arg2_name, arg2_val)


////////////////////////////////////////////////////////////////////////////////
// Implementation specific tracing API definitions.

Expand Down Expand Up @@ -606,6 +704,9 @@
#define TRACE_EVENT_PHASE_ASYNC_BEGIN ('S')
#define TRACE_EVENT_PHASE_ASYNC_STEP ('T')
#define TRACE_EVENT_PHASE_ASYNC_END ('F')
#define TRACE_EVENT_PHASE_FLOW_BEGIN ('s')
#define TRACE_EVENT_PHASE_FLOW_STEP ('t')
#define TRACE_EVENT_PHASE_FLOW_END ('f')
#define TRACE_EVENT_PHASE_METADATA ('M')
#define TRACE_EVENT_PHASE_COUNTER ('C')

Expand Down Expand Up @@ -638,31 +739,58 @@ const unsigned long long kNoEventId = 0;
// same pointer is used on different processes.
class TraceID {
public:
explicit TraceID(const void* id, unsigned char* flags) :
data_(static_cast<unsigned long long>(
reinterpret_cast<unsigned long>(id))) {
class ForceMangle {
public:
explicit ForceMangle(unsigned long long id) : data_(id) {}
explicit ForceMangle(unsigned long id) : data_(id) {}
explicit ForceMangle(unsigned int id) : data_(id) {}
explicit ForceMangle(unsigned short id) : data_(id) {}
explicit ForceMangle(unsigned char id) : data_(id) {}
explicit ForceMangle(long long id)
: data_(static_cast<unsigned long long>(id)) {}
explicit ForceMangle(long id)
: data_(static_cast<unsigned long long>(id)) {}
explicit ForceMangle(int id)
: data_(static_cast<unsigned long long>(id)) {}
explicit ForceMangle(short id)
: data_(static_cast<unsigned long long>(id)) {}
explicit ForceMangle(signed char id)
: data_(static_cast<unsigned long long>(id)) {}

unsigned long long data() const { return data_; }

private:
unsigned long long data_;
};

explicit TraceID(const void* id, unsigned char* flags)
: data_(static_cast<unsigned long long>(
reinterpret_cast<unsigned long>(id))) {
*flags |= TRACE_EVENT_FLAG_MANGLE_ID;
}
explicit TraceID(ForceMangle id, unsigned char* flags) : data_(id.data()) {
*flags |= TRACE_EVENT_FLAG_MANGLE_ID;
}
explicit TraceID(unsigned long long id, unsigned char* flags) :
data_(id) { (void)flags; }
explicit TraceID(unsigned long id, unsigned char* flags) :
data_(id) { (void)flags; }
explicit TraceID(unsigned int id, unsigned char* flags) :
data_(id) { (void)flags; }
explicit TraceID(unsigned short id, unsigned char* flags) :
data_(id) { (void)flags; }
explicit TraceID(unsigned char id, unsigned char* flags) :
data_(id) { (void)flags; }
explicit TraceID(long long id, unsigned char* flags) :
data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(long id, unsigned char* flags) :
data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(int id, unsigned char* flags) :
data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(short id, unsigned char* flags) :
data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(signed char id, unsigned char* flags) :
data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(unsigned long long id, unsigned char* flags)
: data_(id) { (void)flags; }
explicit TraceID(unsigned long id, unsigned char* flags)
: data_(id) { (void)flags; }
explicit TraceID(unsigned int id, unsigned char* flags)
: data_(id) { (void)flags; }
explicit TraceID(unsigned short id, unsigned char* flags)
: data_(id) { (void)flags; }
explicit TraceID(unsigned char id, unsigned char* flags)
: data_(id) { (void)flags; }
explicit TraceID(long long id, unsigned char* flags)
: data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(long id, unsigned char* flags)
: data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(int id, unsigned char* flags)
: data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(short id, unsigned char* flags)
: data_(static_cast<unsigned long long>(id)) { (void)flags; }
explicit TraceID(signed char id, unsigned char* flags)
: data_(static_cast<unsigned long long>(id)) { (void)flags; }

unsigned long long data() const { return data_; }

Expand Down
27 changes: 20 additions & 7 deletions base/message_loop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,14 @@ bool enable_histogrammer_ = false;

MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL;

// Create 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, MessageLoop* loop) {
return (static_cast<uint64>(task.sequence_num) << 32) |
static_cast<uint64>(reinterpret_cast<intptr_t>(loop));
}

} // namespace

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -434,6 +442,8 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() {
}

void MessageLoop::RunTask(const PendingTask& pending_task) {
TRACE_EVENT_FLOW_END0("task", "MessageLoop::PostTask",
TRACE_ID_MANGLE(GetTaskTraceID(pending_task, this)));
TRACE_EVENT2("task", "MessageLoop::RunTask",
"src_file", pending_task.posted_from.file_name(),
"src_func", pending_task.posted_from.function_name());
Expand Down Expand Up @@ -482,13 +492,8 @@ bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) {
}

void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) {
// Move to the delayed work queue. Initialize the sequence number
// before inserting into the delayed_work_queue_. The sequence number
// is used to faciliate FIFO sorting when two tasks have the same
// delayed_run_time value.
PendingTask new_pending_task(pending_task);
new_pending_task.sequence_num = next_sequence_num_++;
delayed_work_queue_.push(new_pending_task);
// Move to the delayed work queue.
delayed_work_queue_.push(pending_task);
}

void MessageLoop::ReloadWorkQueue() {
Expand Down Expand Up @@ -586,6 +591,14 @@ void MessageLoop::AddToIncomingQueue(PendingTask* pending_task) {
{
base::AutoLock locked(incoming_queue_lock_);

// Initialize the sequence number. The sequence number is used for delayed
// tasks (to faciliate FIFO sorting when two tasks have the same
// delayed_run_time value) and for identifying the task in about:tracing.
pending_task->sequence_num = next_sequence_num_++;

TRACE_EVENT_FLOW_BEGIN0("task", "MessageLoop::PostTask",
TRACE_ID_MANGLE(GetTaskTraceID(*pending_task, this)));

bool was_empty = incoming_queue_.empty();
incoming_queue_.push(*pending_task);
pending_task->task.Reset();
Expand Down
3 changes: 2 additions & 1 deletion base/message_loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,8 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate {
bool os_modal_loop_;
#endif

// The next sequence number to use for delayed tasks.
// The next sequence number to use for delayed tasks. Updating this counter is
// protected by incoming_queue_lock_.
int next_sequence_num_;

ObserverList<TaskObserver> task_observers_;
Expand Down
28 changes: 28 additions & 0 deletions base/threading/sequenced_worker_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/critical_closure.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/memory/linked_ptr.h"
#include "base/message_loop_proxy.h"
Expand All @@ -39,16 +40,19 @@ namespace {
struct SequencedTask : public TrackingInfo {
SequencedTask()
: sequence_token_id(0),
trace_id(0),
shutdown_behavior(SequencedWorkerPool::BLOCK_SHUTDOWN) {}

explicit SequencedTask(const tracked_objects::Location& from_here)
: base::TrackingInfo(from_here, TimeTicks()),
sequence_token_id(0),
trace_id(0),
shutdown_behavior(SequencedWorkerPool::BLOCK_SHUTDOWN) {}

~SequencedTask() {}

int sequence_token_id;
int trace_id;
SequencedWorkerPool::WorkerShutdown shutdown_behavior;
tracked_objects::Location posted_from;
Closure task;
Expand Down Expand Up @@ -208,6 +212,15 @@ bool SequencedWorkerPoolSequencedTaskRunner::PostDelayedTaskAssertZeroDelay(
token_, from_here, task, shutdown_behavior_);
}

// Create 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 SequencedTask& task,
void* pool) {
return (static_cast<uint64>(task.trace_id) << 32) |
static_cast<uint64>(reinterpret_cast<intptr_t>(pool));
}

} // namespace

// Worker ---------------------------------------------------------------------
Expand Down Expand Up @@ -396,6 +409,9 @@ class SequencedWorkerPool::Inner {
// Lists all sequence tokens currently executing.
std::set<int> current_sequences_;

// An ID for each posted task to distinguish the task from others in traces.
int trace_id_;

// Set when Shutdown is called and no further tasks should be
// allowed, though we may still be running existing tasks.
bool shutdown_called_;
Expand Down Expand Up @@ -451,6 +467,7 @@ SequencedWorkerPool::Inner::Inner(
blocking_shutdown_thread_count_(0),
pending_task_count_(0),
blocking_shutdown_pending_task_count_(0),
trace_id_(0),
shutdown_called_(false),
testing_observer_(observer) {}

Expand Down Expand Up @@ -501,6 +518,12 @@ bool SequencedWorkerPool::Inner::PostTask(
if (shutdown_called_)
return false;

// The trace_id is used for identifying the task in about:tracing.
sequenced.trace_id = trace_id_++;

TRACE_EVENT_FLOW_BEGIN0("task", "SequencedWorkerPool::PostTask",
TRACE_ID_MANGLE(GetTaskTraceID(sequenced, static_cast<void*>(this))));

// Now that we have the lock, apply the named token rules.
if (optional_token_name)
sequenced.sequence_token_id = LockedGetNamedTokenID(*optional_token_name);
Expand Down Expand Up @@ -605,6 +628,11 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
SequencedTask task;
std::vector<Closure> delete_these_outside_lock;
if (GetWork(&task, &delete_these_outside_lock)) {
TRACE_EVENT_FLOW_END0("task", "SequencedWorkerPool::PostTask",
TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this))));
TRACE_EVENT2("task", "SequencedWorkerPool::ThreadLoop",
"src_file", task.posted_from.file_name(),
"src_func", task.posted_from.function_name());
int new_thread_id = WillRunWorkerTask(task);
{
AutoUnlock unlock(lock_);
Expand Down
2 changes: 1 addition & 1 deletion ipc/ipc_channel_nacl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ bool Channel::ChannelImpl::Send(Message* message) {
Logging::GetInstance()->OnSendMessage(message_ptr.get(), "");
#endif // IPC_MESSAGE_LOG_ENABLED

message->TraceMessageStep();
message->TraceMessageBegin();
output_queue_.push_back(linked_ptr<Message>(message_ptr.release()));
if (!waiting_connect_)
return ProcessOutgoingMessages();
Expand Down
2 changes: 1 addition & 1 deletion ipc/ipc_channel_posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ bool Channel::ChannelImpl::Send(Message* message) {
Logging::GetInstance()->OnSendMessage(message, "");
#endif // IPC_MESSAGE_LOG_ENABLED

message->TraceMessageStep();
message->TraceMessageBegin();
output_queue_.push(message);
if (!is_blocked_on_write_ && !waiting_connect_) {
return ProcessOutgoingMessages();
Expand Down
1 change: 0 additions & 1 deletion ipc/ipc_channel_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,6 @@ void ChannelProxy::Close() {

bool ChannelProxy::Send(Message* message) {
DCHECK(did_init_);
message->TraceMessageStep();
if (outgoing_message_filter())
message = outgoing_message_filter()->Rewrite(message);

Expand Down
Loading

0 comments on commit 2c391df

Please sign in to comment.