Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3360)

Unified Diff: base/message_loop/message_pump_mac.mm

Issue 22911026: Add instrumentation to the MessagePumpMac family of classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: iOS compile Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « base/message_loop/message_pump_mac.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: base/message_loop/message_pump_mac.mm
diff --git a/base/message_loop/message_pump_mac.mm b/base/message_loop/message_pump_mac.mm
index f885fbf5f6bbb9d53fc1d24363c0f427699ace6c..fa959479a2581059fbfcf7720a47d0166823a865 100644
--- a/base/message_loop/message_pump_mac.mm
+++ b/base/message_loop/message_pump_mac.mm
@@ -7,10 +7,14 @@
#import <Foundation/Foundation.h>
#include <limits>
+#include <stack>
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
#include "base/time/time.h"
+#include "base/timer/timer.h"
#if !defined(OS_IOS)
#import <AppKit/AppKit.h>
@@ -51,6 +55,181 @@ class MessagePumpScopedAutoreleasePool {
DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
};
+// This class is used to instrument the MessagePump to gather various timing
+// data about when the underlying run loop is entered, when it is waiting, and
+// when it is servicing its delegate.
+//
+// The metrics are gathered as UMA-tracked histograms. To gather the data over
+// time, sampling is used, such that a new histogram is created for each metric
+// every |sampling_interval| for |sampling_duration|. After sampling is
+// complete, this class deletes itself.
+class MessagePumpInstrumentation {
+ public:
+ // Creates an instrument for the MessagePump on the current thread. Every
+ // |sampling_interval|, a new histogram will be created to track the metrics
+ // over time. After |sampling_duration|, this will delete itself, causing the
+ // WeakPtr to go NULL.
+ static WeakPtr<MessagePumpInstrumentation> Create(
+ const TimeDelta& sampling_interval,
+ const TimeDelta& sampling_duration) {
+ MessagePumpInstrumentation* instrument =
+ new MessagePumpInstrumentation(sampling_interval, sampling_duration);
+ return instrument->weak_ptr_factory_.GetWeakPtr();
+ }
+
+ // Starts the timer that runs the sampling instrumentation. Can be called
+ // multiple times as a noop.
+ void StartIfNeeded() {
+ if (timer_.IsRunning())
+ return;
+ timer_.Start(FROM_HERE, sampling_interval_,
+ base::Bind(&MessagePumpInstrumentation::TimerFired,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+
+ // Used to track kCFRunLoopEntry.
+ void LoopEntered() {
+ loop_run_times_.push(TimeTicks::Now());
+ }
+
+ // Used to track kCFRunLoopExit.
+ void LoopExited() {
+ TimeDelta duration = TimeTicks::Now() - loop_run_times_.top();
+ loop_run_times_.pop();
+ GetHistogram(LOOP_CYCLE)->AddTime(duration);
+ }
+
+ // Used to track kCFRunLoopBeforeWaiting.
+ void WaitingStarted() {
+ loop_wait_times_.push(TimeTicks::Now());
+ }
+
+ // Used to track kCFRunLoopAfterWaiting.
+ void WaitingFinished() {
+ TimeDelta duration = TimeTicks::Now() - loop_wait_times_.top();
+ loop_wait_times_.pop();
+ GetHistogram(LOOP_WAIT)->AddTime(duration);
+ }
+
+ // Used to track when the MessagePump will invoke its |delegate|.
+ void WorkSourceEntered(MessagePump::Delegate* delegate) {
+ work_source_times_.push(TimeTicks::Now());
+ if (delegate) {
+ size_t queue_size;
+ TimeDelta queuing_delay;
+ delegate->GetQueueingInformation(&queue_size, &queuing_delay);
+ GetHistogram(QUEUE_SIZE)->Add(queue_size);
+ GetHistogram(QUEUE_DELAY)->AddTime(queuing_delay);
+ }
+ }
+
+ // Used to track the completion of servicing the MessagePump::Delegate.
+ void WorkSourceExited() {
+ TimeDelta duration = TimeTicks::Now() - work_source_times_.top();
+ work_source_times_.pop();
+ GetHistogram(WORK_SOURCE)->AddTime(duration);
+ }
+
+ private:
+ enum HistogramEvent {
+ // Time-based histograms:
+ LOOP_CYCLE, // LoopEntered/LoopExited
+ LOOP_WAIT, // WaitingStarted/WaitingEnded
+ WORK_SOURCE, // WorkSourceExited
+ QUEUE_DELAY, // WorkSourceEntered
+
+ // Value-based histograms:
+ QUEUE_SIZE, // WorkSourceEntered
+
+ HISTOGRAM_EVENT_MAX,
+ };
+
+ MessagePumpInstrumentation(const TimeDelta& sampling_interval,
+ const TimeDelta& sampling_duration)
+ : weak_ptr_factory_(this),
+ sampling_interval_(sampling_interval),
+ sampling_duration_(sampling_duration),
+ sample_count_(0),
+ timer_(true, true) {
+ const char kHistogramName[] = "MessagePumpMac.%s.SampleMs.%lld";
+
+ // Create all the histogram objects that will be used for sampling.
+ for (TimeDelta i; i < sampling_duration_; i += sampling_interval_) {
+ int64 sample = i.InMilliseconds();
+
+ // Generate the time-based histograms.
+ for (HistogramEvent j = LOOP_CYCLE; j < QUEUE_SIZE; ++j) {
Mark Mentovai 2013/08/20 22:44:34 There’s no indication in the enum above that QUEUE
Robert Sesek 2013/08/22 17:49:15 "Solved" with a comment. Fixing this with an in-be
+ std::string name = StringPrintf(kHistogramName, NameForEnum(j), sample);
+ histograms_[j].push_back(
+ Histogram::FactoryTimeGet(name, TimeDelta::FromMilliseconds(1),
+ sampling_interval_, 50,
+ HistogramBase::kUmaTargetedHistogramFlag));
+ }
+
+ // Generate the value-based histograms.
+ for (HistogramEvent j = QUEUE_SIZE; j < HISTOGRAM_EVENT_MAX; ++j) {
+ std::string name = StringPrintf(kHistogramName, NameForEnum(j), sample);
+ histograms_[j].push_back(
+ Histogram::FactoryGet(name, 1, 10000, 50,
+ HistogramBase::kUmaTargetedHistogramFlag));
+ }
+ }
Mark Mentovai 2013/08/20 22:44:34 That was only 75 histograms.
Robert Sesek 2013/08/22 17:49:15 I'll leave it to jar@ to comment on the implicatio
jar (doing other things) 2013/08/22 23:03:39 I'm not convinced that all these histogtrams are u
+ }
+
+ const char* NameForEnum(HistogramEvent event) {
+ switch (event) {
+ case LOOP_CYCLE: return "LoopCycle";
+ case LOOP_WAIT: return "Waiting";
+ case WORK_SOURCE: return "WorkSource";
+ case QUEUE_DELAY: return "QueueingDelay";
+ case QUEUE_SIZE: return "QueueSize";
+ case HISTOGRAM_EVENT_MAX:
Mark Mentovai 2013/08/20 22:44:34 This line is not needed. (Some compiler with lots
Robert Sesek 2013/08/22 17:49:15 Done.
+ default:
+ NOTREACHED();
+ return NULL;
+ }
+ }
+
+ // Called when |sampling_interval| has elapsed.
+ void TimerFired() {
+ ++sample_count_;
+
+ if (sampling_interval_ * sample_count_ >= sampling_duration_) {
+ timer_.Stop();
+ delete this;
+ }
+ }
+
+ HistogramBase* GetHistogram(HistogramEvent event) {
+ return histograms_[event][sample_count_];
+ }
+
+ // Vends the pointer to the Create()er.
+ WeakPtrFactory<MessagePumpInstrumentation> weak_ptr_factory_;
+
+ // The interval and duration of the sampling, as well as the number of
+ // times the timer has fired.
+ TimeDelta sampling_interval_;
+ TimeDelta sampling_duration_;
+ size_t sample_count_;
+
+ // The timer that invokes TimerFired() to advance the |sample_count_|.
+ Timer timer_;
Mark Mentovai 2013/08/20 22:44:34 …this is running on a Timer. And the timers are di
Robert Sesek 2013/08/22 17:49:15 Sigh, I know.
+
+ // The two-dimensional array of histograms. The first dimension is the
+ // HistogramEvent type. The second is for the sampling intervals.
+ std::vector<HistogramBase*> histograms_[HISTOGRAM_EVENT_MAX];
+
+ // MessagePump activations can be nested. Use a stack for each of the
+ // possibly reentrant HistogramEvent types to properly balance and calculate
+ // the timing information.
+ std::stack<TimeTicks> loop_run_times_;
+ std::stack<TimeTicks> loop_wait_times_;
+ std::stack<TimeTicks> work_source_times_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpInstrumentation);
+};
+
// Must be called on the run loop thread.
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
: delegate_(NULL),
@@ -101,7 +280,8 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
observer_context.info = this;
pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator
- kCFRunLoopBeforeWaiting,
+ kCFRunLoopBeforeWaiting |
+ kCFRunLoopAfterWaiting,
true, // repeat
0, // priority
PreWaitObserver,
Mark Mentovai 2013/08/20 22:44:34 Either your new code is temporary (and you should
Robert Sesek 2013/08/22 17:49:15 Done.
@@ -193,6 +373,11 @@ void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
}
}
+void MessagePumpCFRunLoopBase::EnableInstrumentation() {
+ instrumentation_ = MessagePumpInstrumentation::Create(
+ TimeDelta::FromSeconds(1), TimeDelta::FromSeconds(15));
+}
+
// May be called on any thread.
void MessagePumpCFRunLoopBase::ScheduleWork() {
CFRunLoopSourceSignal(work_source_);
@@ -240,6 +425,9 @@ bool MessagePumpCFRunLoopBase::RunWork() {
return false;
}
+ if (instrumentation_)
+ instrumentation_->WorkSourceEntered(delegate_);
+
// The NSApplication-based run loop only drains the autorelease pool at each
// UI event (NSEvent). The autorelease pool is not drained for each
// CFRunLoopSource target that's run. Use a local pool for any autoreleased
@@ -279,6 +467,9 @@ bool MessagePumpCFRunLoopBase::RunWork() {
CFRunLoopSourceSignal(work_source_);
}
+ if (instrumentation_)
+ instrumentation_->WorkSourceExited();
+
return resignal_work_source;
}
@@ -368,6 +559,12 @@ void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
void* info) {
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
+ if (activity == kCFRunLoopAfterWaiting) {
+ if (self->instrumentation_)
+ self->instrumentation_->WaitingFinished();
+ return;
+ }
+
// Attempt to do some idle work before going to sleep.
self->RunIdleWork();
@@ -376,6 +573,9 @@ void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
// nesting-deferred work may have accumulated. Schedule it for processing
// if appropriate.
self->MaybeScheduleNestingDeferredWork();
+
+ if (self->instrumentation_)
+ self->instrumentation_->WaitingStarted();
}
// Called from the run loop.
@@ -402,6 +602,9 @@ void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
switch (activity) {
case kCFRunLoopEntry:
+ if (self->instrumentation_)
+ self->instrumentation_->LoopEntered();
+
++self->nesting_level_;
if (self->nesting_level_ > self->deepest_nesting_level_) {
self->deepest_nesting_level_ = self->nesting_level_;
@@ -427,6 +630,9 @@ void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
// loop.
self->MaybeScheduleNestingDeferredWork();
--self->nesting_level_;
+
+ if (self->instrumentation_)
+ self->instrumentation_->LoopExited();
break;
default:
@@ -556,11 +762,15 @@ void MessagePumpUIApplication::Attach(Delegate* delegate) {
MessagePumpNSApplication::MessagePumpNSApplication()
: keep_running_(true),
running_own_loop_(false) {
+ EnableInstrumentation();
}
MessagePumpNSApplication::~MessagePumpNSApplication() {}
void MessagePumpNSApplication::DoRun(Delegate* delegate) {
+ if (instrumentation_)
+ instrumentation_->StartIfNeeded();
+
bool last_running_own_loop_ = running_own_loop_;
// NSApp must be initialized by calling:
« no previous file with comments | « base/message_loop/message_pump_mac.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698