Index: base/debug/trace_event_synthetic_delay.cc |
diff --git a/base/debug/trace_event_synthetic_delay.cc b/base/debug/trace_event_synthetic_delay.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f4f8a1f8e66b01fcde439862b9bd04fa6f4aa51e |
--- /dev/null |
+++ b/base/debug/trace_event_synthetic_delay.cc |
@@ -0,0 +1,236 @@ |
+// Copyright (c) 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/trace_event_synthetic_delay.h" |
+#include "base/memory/singleton.h" |
+#include "base/threading/platform_thread.h" |
+#include "base/threading/thread_local.h" |
+ |
+namespace { |
+const int kMaxSyntheticDelays = 32; |
+} // namespace |
+ |
+namespace base { |
+namespace debug { |
+ |
+TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {} |
+TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {} |
+ |
+// Thread-local state for each synthetic delay point. This allows the same delay |
+// to be applied to multiple threads simultaneously. |
+struct ThreadState { |
+ ThreadState(); |
+ |
+ base::TimeTicks start_time; |
+ unsigned trigger_count; |
+ int generation; |
+}; |
+ |
+ThreadState::ThreadState() : trigger_count(0u), generation(0) {} |
+ |
+class TraceEventSyntheticDelayRegistry : public TraceEventSyntheticDelayClock { |
+ public: |
+ static TraceEventSyntheticDelayRegistry* GetInstance(); |
+ |
+ TraceEventSyntheticDelay* GetOrCreateDelay(const char* name); |
+ ThreadState* GetThreadState(int index); |
+ |
+ // TraceEventSyntheticDelayClock implementation. |
+ virtual base::TimeTicks Now() OVERRIDE; |
+ |
+ private: |
+ TraceEventSyntheticDelayRegistry(); |
+ |
+ friend struct DefaultSingletonTraits<TraceEventSyntheticDelayRegistry>; |
+ |
+ Lock lock_; |
+ TraceEventSyntheticDelay delays_[kMaxSyntheticDelays]; |
+ TraceEventSyntheticDelay dummy_delay_; |
+ base::subtle::Atomic32 delay_count_; |
+ |
+ ThreadLocalPointer<ThreadState> thread_states_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayRegistry); |
+}; |
+ |
+TraceEventSyntheticDelay::TraceEventSyntheticDelay() |
+ : mode_(STATIC), generation_(0), thread_state_index_(0), clock_(NULL) {} |
+ |
+TraceEventSyntheticDelay::~TraceEventSyntheticDelay() {} |
+ |
+TraceEventSyntheticDelay* TraceEventSyntheticDelay::Lookup( |
+ const std::string& name) { |
+ return TraceEventSyntheticDelayRegistry::GetInstance()->GetOrCreateDelay( |
+ name.c_str()); |
+} |
+ |
+void TraceEventSyntheticDelay::Initialize( |
+ const std::string& name, |
+ TraceEventSyntheticDelayClock* clock, |
+ int thread_state_index) { |
+ name_ = name; |
+ clock_ = clock; |
+ thread_state_index_ = thread_state_index; |
+} |
+ |
+void TraceEventSyntheticDelay::SetTargetDuration( |
+ base::TimeDelta target_duration) { |
+ AutoLock lock(lock_); |
+ target_duration_ = target_duration; |
+ generation_++; |
+} |
+ |
+void TraceEventSyntheticDelay::SetMode(Mode mode) { |
+ AutoLock lock(lock_); |
+ mode_ = mode; |
+ generation_++; |
+} |
+ |
+void TraceEventSyntheticDelay::SetClock(TraceEventSyntheticDelayClock* clock) { |
+ AutoLock lock(lock_); |
+ clock_ = clock; |
+ generation_++; |
+} |
+ |
+void TraceEventSyntheticDelay::Activate() { |
+ // Note that we check for a non-zero target duration without locking to keep |
+ // things quick for the common case when delays are disabled. Since the delay |
+ // calculation is done with a lock held, it will always be correct. The only |
+ // downside of this is that we may fail to apply some delays when the target |
+ // duration changes. |
+ ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); |
+ if (!target_duration_.ToInternalValue()) |
+ return; |
+ |
+ ThreadState* thread_state = |
+ TraceEventSyntheticDelayRegistry::GetInstance()-> |
+ GetThreadState(thread_state_index_); |
+ if (!thread_state->start_time.ToInternalValue()) |
+ thread_state->start_time = clock_->Now(); |
+} |
+ |
+void TraceEventSyntheticDelay::Apply() { |
+ ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); |
+ if (!target_duration_.ToInternalValue()) |
+ return; |
+ |
+ ThreadState* thread_state = |
+ TraceEventSyntheticDelayRegistry::GetInstance()-> |
+ GetThreadState(thread_state_index_); |
+ if (!thread_state->start_time.ToInternalValue()) |
+ return; |
+ base::TimeTicks now = clock_->Now(); |
+ base::TimeTicks start_time = thread_state->start_time; |
+ base::TimeTicks end_time; |
+ thread_state->start_time = base::TimeTicks(); |
+ |
+ { |
+ AutoLock lock(lock_); |
+ if (thread_state->generation != generation_) { |
+ thread_state->trigger_count = 0; |
+ thread_state->generation = generation_; |
+ } |
+ |
+ if (mode_ == ONE_SHOT && thread_state->trigger_count++) |
+ return; |
+ else if (mode_ == ALTERNATING && thread_state->trigger_count++ % 2) |
+ return; |
+ |
+ end_time = start_time + target_duration_; |
+ if (now >= end_time) |
+ return; |
+ } |
+ ApplyDelay(end_time); |
+} |
+ |
+void TraceEventSyntheticDelay::ApplyDelay(base::TimeTicks end_time) { |
+ TRACE_EVENT0("synthetic_delay", name_.c_str()); |
+ while (clock_->Now() < end_time) { |
+ // Busy loop. |
+ } |
+} |
+ |
+TraceEventSyntheticDelayRegistry* |
+TraceEventSyntheticDelayRegistry::GetInstance() { |
+ return Singleton< |
+ TraceEventSyntheticDelayRegistry, |
+ LeakySingletonTraits<TraceEventSyntheticDelayRegistry> >::get(); |
+} |
+ |
+TraceEventSyntheticDelayRegistry::TraceEventSyntheticDelayRegistry() |
+ : delay_count_(0) {} |
+ |
+TraceEventSyntheticDelay* TraceEventSyntheticDelayRegistry::GetOrCreateDelay( |
+ const char* name) { |
+ // Try to find an existing delay first without locking to make the common case |
+ // fast. |
+ int delay_count = base::subtle::Acquire_Load(&delay_count_); |
+ for (int i = 0; i < delay_count; ++i) { |
+ if (!strcmp(name, delays_[i].name_.c_str())) |
+ return &delays_[i]; |
+ } |
+ |
+ AutoLock lock(lock_); |
+ delay_count = base::subtle::Acquire_Load(&delay_count_); |
+ for (int i = 0; i < delay_count; ++i) { |
+ if (!strcmp(name, delays_[i].name_.c_str())) |
+ return &delays_[i]; |
+ } |
+ |
+ DCHECK(delay_count < kMaxSyntheticDelays) |
+ << "must increase kMaxSyntheticDelays"; |
+ if (delay_count >= kMaxSyntheticDelays) |
+ return &dummy_delay_; |
+ |
+ delays_[delay_count].Initialize(std::string(name), this, delay_count); |
+ base::subtle::Release_Store(&delay_count_, delay_count + 1); |
+ return &delays_[delay_count]; |
+} |
+ |
+ThreadState* TraceEventSyntheticDelayRegistry::GetThreadState(int index) { |
+ DCHECK(index >= 0 && index < kMaxSyntheticDelays); |
+ if (index < 0 || index >= kMaxSyntheticDelays) |
+ return NULL; |
+ |
+ // Note that these thread states are leaked at program exit. They will only |
+ // get allocated if synthetic delays are actually used. |
+ ThreadState* thread_states = thread_states_.Get(); |
+ if (!thread_states) |
+ thread_states_.Set((thread_states = new ThreadState[kMaxSyntheticDelays])); |
+ return &thread_states[index]; |
+} |
+ |
+base::TimeTicks TraceEventSyntheticDelayRegistry::Now() { |
+ return base::TimeTicks::HighResNow(); |
+} |
+ |
+} // namespace debug |
+} // namespace base |
+ |
+namespace trace_event_internal { |
+ |
+ScopedSyntheticDelay::ScopedSyntheticDelay(const char* name, |
+ base::subtle::AtomicWord* impl_ptr) |
+ : delay_impl_(GetOrCreateDelay(name, impl_ptr)) { |
+ delay_impl_->Activate(); |
+} |
+ |
+ScopedSyntheticDelay::~ScopedSyntheticDelay() { delay_impl_->Apply(); } |
+ |
+base::debug::TraceEventSyntheticDelay* GetOrCreateDelay( |
+ const char* name, |
+ base::subtle::AtomicWord* impl_ptr) { |
+ base::debug::TraceEventSyntheticDelay* delay_impl = |
+ reinterpret_cast<base::debug::TraceEventSyntheticDelay*>( |
+ base::subtle::NoBarrier_Load(impl_ptr)); |
+ if (!delay_impl) { |
+ delay_impl = base::debug::TraceEventSyntheticDelayRegistry::GetInstance() |
+ ->GetOrCreateDelay(name); |
+ base::subtle::NoBarrier_Store( |
+ impl_ptr, reinterpret_cast<base::subtle::AtomicWord>(delay_impl)); |
+ } |
+ return delay_impl; |
+} |
+ |
+} // namespace trace_event_internal |