| // 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/trace_event_synthetic_delay.h" |
| #include "base/memory/singleton.h" |
| |
| namespace { |
| const int kMaxSyntheticDelays = 32; |
| } // namespace |
| |
| namespace base { |
| namespace debug { |
| |
| TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {} |
| TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {} |
| |
| class TraceEventSyntheticDelayRegistry : public TraceEventSyntheticDelayClock { |
| public: |
| static TraceEventSyntheticDelayRegistry* GetInstance(); |
| |
| TraceEventSyntheticDelay* GetOrCreateDelay(const char* name); |
| void ResetAllDelays(); |
| |
| // 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_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayRegistry); |
| }; |
| |
| TraceEventSyntheticDelay::TraceEventSyntheticDelay() |
| : mode_(STATIC), begin_count_(0), trigger_count_(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) { |
| name_ = name; |
| clock_ = clock; |
| } |
| |
| void TraceEventSyntheticDelay::SetTargetDuration( |
| base::TimeDelta target_duration) { |
| AutoLock lock(lock_); |
| target_duration_ = target_duration; |
| trigger_count_ = 0; |
| begin_count_ = 0; |
| } |
| |
| void TraceEventSyntheticDelay::SetMode(Mode mode) { |
| AutoLock lock(lock_); |
| mode_ = mode; |
| } |
| |
| void TraceEventSyntheticDelay::SetClock(TraceEventSyntheticDelayClock* clock) { |
| AutoLock lock(lock_); |
| clock_ = clock; |
| } |
| |
| void TraceEventSyntheticDelay::Begin() { |
| // 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; |
| |
| base::TimeTicks start_time = clock_->Now(); |
| { |
| AutoLock lock(lock_); |
| if (++begin_count_ != 1) |
| return; |
| end_time_ = CalculateEndTimeLocked(start_time); |
| } |
| } |
| |
| void TraceEventSyntheticDelay::BeginParallel(base::TimeTicks* out_end_time) { |
| // See note in Begin(). |
| ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); |
| if (!target_duration_.ToInternalValue()) { |
| *out_end_time = base::TimeTicks(); |
| return; |
| } |
| |
| base::TimeTicks start_time = clock_->Now(); |
| { |
| AutoLock lock(lock_); |
| *out_end_time = CalculateEndTimeLocked(start_time); |
| } |
| } |
| |
| void TraceEventSyntheticDelay::End() { |
| // See note in Begin(). |
| ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration"); |
| if (!target_duration_.ToInternalValue()) |
| return; |
| |
| base::TimeTicks end_time; |
| { |
| AutoLock lock(lock_); |
| if (!begin_count_ || --begin_count_ != 0) |
| return; |
| end_time = end_time_; |
| } |
| if (!end_time.is_null()) |
| ApplyDelay(end_time); |
| } |
| |
| void TraceEventSyntheticDelay::EndParallel(base::TimeTicks end_time) { |
| if (!end_time.is_null()) |
| ApplyDelay(end_time); |
| } |
| |
| base::TimeTicks TraceEventSyntheticDelay::CalculateEndTimeLocked( |
| base::TimeTicks start_time) { |
| if (mode_ == ONE_SHOT && trigger_count_++) |
| return base::TimeTicks(); |
| else if (mode_ == ALTERNATING && trigger_count_++ % 2) |
| return base::TimeTicks(); |
| return start_time + target_duration_; |
| } |
| |
| 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); |
| base::subtle::Release_Store(&delay_count_, delay_count + 1); |
| return &delays_[delay_count]; |
| } |
| |
| base::TimeTicks TraceEventSyntheticDelayRegistry::Now() { |
| return base::TimeTicks::HighResNow(); |
| } |
| |
| void TraceEventSyntheticDelayRegistry::ResetAllDelays() { |
| AutoLock lock(lock_); |
| int delay_count = base::subtle::Acquire_Load(&delay_count_); |
| for (int i = 0; i < delay_count; ++i) { |
| delays_[i].SetTargetDuration(base::TimeDelta()); |
| delays_[i].SetClock(this); |
| } |
| } |
| |
| void ResetTraceEventSyntheticDelays() { |
| TraceEventSyntheticDelayRegistry::GetInstance()->ResetAllDelays(); |
| } |
| |
| } // 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_->BeginParallel(&end_time_); |
| } |
| |
| ScopedSyntheticDelay::~ScopedSyntheticDelay() { |
| delay_impl_->EndParallel(end_time_); |
| } |
| |
| base::debug::TraceEventSyntheticDelay* GetOrCreateDelay( |
| const char* name, |
| base::subtle::AtomicWord* impl_ptr) { |
| base::debug::TraceEventSyntheticDelay* delay_impl = |
| reinterpret_cast<base::debug::TraceEventSyntheticDelay*>( |
| base::subtle::Acquire_Load(impl_ptr)); |
| if (!delay_impl) { |
| delay_impl = base::debug::TraceEventSyntheticDelayRegistry::GetInstance() |
| ->GetOrCreateDelay(name); |
| base::subtle::Release_Store( |
| impl_ptr, reinterpret_cast<base::subtle::AtomicWord>(delay_impl)); |
| } |
| return delay_impl; |
| } |
| |
| } // namespace trace_event_internal |