root/base/debug/trace_event_synthetic_delay.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. clock_
  2. Lookup
  3. Initialize
  4. SetTargetDuration
  5. SetMode
  6. SetClock
  7. Begin
  8. BeginParallel
  9. End
  10. EndParallel
  11. CalculateEndTimeLocked
  12. ApplyDelay
  13. GetInstance
  14. GetOrCreateDelay
  15. Now
  16. ResetAllDelays
  17. ResetTraceEventSyntheticDelays
  18. GetOrCreateDelay

// 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::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

/* [<][>][^][v][top][bottom][index][help] */