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

Unified Diff: base/debug/trace_event_synthetic_delay.cc

Issue 53923005: Add synthetic delay testing framework (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Happy new year Created 6 years, 12 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/debug/trace_event_synthetic_delay.h ('k') | base/debug/trace_event_synthetic_delay_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « base/debug/trace_event_synthetic_delay.h ('k') | base/debug/trace_event_synthetic_delay_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698