1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/debug/trace_event_synthetic_delay.h"
6 #include "base/memory/singleton.h"
7
8 namespace {
9 const int kMaxSyntheticDelays = 32;
10 } // namespace
11
12 namespace base {
13 namespace debug {
14
TraceEventSyntheticDelayClock()15 TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {}
~TraceEventSyntheticDelayClock()16 TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {}
17
18 class TraceEventSyntheticDelayRegistry : public TraceEventSyntheticDelayClock {
19 public:
20 static TraceEventSyntheticDelayRegistry* GetInstance();
21
22 TraceEventSyntheticDelay* GetOrCreateDelay(const char* name);
23 void ResetAllDelays();
24
25 // TraceEventSyntheticDelayClock implementation.
26 virtual base::TimeTicks Now() OVERRIDE;
27
28 private:
29 TraceEventSyntheticDelayRegistry();
30
31 friend struct DefaultSingletonTraits<TraceEventSyntheticDelayRegistry>;
32
33 Lock lock_;
34 TraceEventSyntheticDelay delays_[kMaxSyntheticDelays];
35 TraceEventSyntheticDelay dummy_delay_;
36 base::subtle::Atomic32 delay_count_;
37
38 DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayRegistry);
39 };
40
TraceEventSyntheticDelay()41 TraceEventSyntheticDelay::TraceEventSyntheticDelay()
42 : mode_(STATIC), begin_count_(0), trigger_count_(0), clock_(NULL) {}
43
~TraceEventSyntheticDelay()44 TraceEventSyntheticDelay::~TraceEventSyntheticDelay() {}
45
Lookup(const std::string & name)46 TraceEventSyntheticDelay* TraceEventSyntheticDelay::Lookup(
47 const std::string& name) {
48 return TraceEventSyntheticDelayRegistry::GetInstance()->GetOrCreateDelay(
49 name.c_str());
50 }
51
Initialize(const std::string & name,TraceEventSyntheticDelayClock * clock)52 void TraceEventSyntheticDelay::Initialize(
53 const std::string& name,
54 TraceEventSyntheticDelayClock* clock) {
55 name_ = name;
56 clock_ = clock;
57 }
58
SetTargetDuration(base::TimeDelta target_duration)59 void TraceEventSyntheticDelay::SetTargetDuration(
60 base::TimeDelta target_duration) {
61 AutoLock lock(lock_);
62 target_duration_ = target_duration;
63 trigger_count_ = 0;
64 begin_count_ = 0;
65 }
66
SetMode(Mode mode)67 void TraceEventSyntheticDelay::SetMode(Mode mode) {
68 AutoLock lock(lock_);
69 mode_ = mode;
70 }
71
SetClock(TraceEventSyntheticDelayClock * clock)72 void TraceEventSyntheticDelay::SetClock(TraceEventSyntheticDelayClock* clock) {
73 AutoLock lock(lock_);
74 clock_ = clock;
75 }
76
Begin()77 void TraceEventSyntheticDelay::Begin() {
78 // Note that we check for a non-zero target duration without locking to keep
79 // things quick for the common case when delays are disabled. Since the delay
80 // calculation is done with a lock held, it will always be correct. The only
81 // downside of this is that we may fail to apply some delays when the target
82 // duration changes.
83 ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration");
84 if (!target_duration_.ToInternalValue())
85 return;
86
87 base::TimeTicks start_time = clock_->Now();
88 {
89 AutoLock lock(lock_);
90 if (++begin_count_ != 1)
91 return;
92 end_time_ = CalculateEndTimeLocked(start_time);
93 }
94 }
95
BeginParallel(base::TimeTicks * out_end_time)96 void TraceEventSyntheticDelay::BeginParallel(base::TimeTicks* out_end_time) {
97 // See note in Begin().
98 ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration");
99 if (!target_duration_.ToInternalValue()) {
100 *out_end_time = base::TimeTicks();
101 return;
102 }
103
104 base::TimeTicks start_time = clock_->Now();
105 {
106 AutoLock lock(lock_);
107 *out_end_time = CalculateEndTimeLocked(start_time);
108 }
109 }
110
End()111 void TraceEventSyntheticDelay::End() {
112 // See note in Begin().
113 ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration");
114 if (!target_duration_.ToInternalValue())
115 return;
116
117 base::TimeTicks end_time;
118 {
119 AutoLock lock(lock_);
120 if (!begin_count_ || --begin_count_ != 0)
121 return;
122 end_time = end_time_;
123 }
124 if (!end_time.is_null())
125 ApplyDelay(end_time);
126 }
127
EndParallel(base::TimeTicks end_time)128 void TraceEventSyntheticDelay::EndParallel(base::TimeTicks end_time) {
129 if (!end_time.is_null())
130 ApplyDelay(end_time);
131 }
132
CalculateEndTimeLocked(base::TimeTicks start_time)133 base::TimeTicks TraceEventSyntheticDelay::CalculateEndTimeLocked(
134 base::TimeTicks start_time) {
135 if (mode_ == ONE_SHOT && trigger_count_++)
136 return base::TimeTicks();
137 else if (mode_ == ALTERNATING && trigger_count_++ % 2)
138 return base::TimeTicks();
139 return start_time + target_duration_;
140 }
141
ApplyDelay(base::TimeTicks end_time)142 void TraceEventSyntheticDelay::ApplyDelay(base::TimeTicks end_time) {
143 TRACE_EVENT0("synthetic_delay", name_.c_str());
144 while (clock_->Now() < end_time) {
145 // Busy loop.
146 }
147 }
148
149 TraceEventSyntheticDelayRegistry*
GetInstance()150 TraceEventSyntheticDelayRegistry::GetInstance() {
151 return Singleton<
152 TraceEventSyntheticDelayRegistry,
153 LeakySingletonTraits<TraceEventSyntheticDelayRegistry> >::get();
154 }
155
TraceEventSyntheticDelayRegistry()156 TraceEventSyntheticDelayRegistry::TraceEventSyntheticDelayRegistry()
157 : delay_count_(0) {}
158
GetOrCreateDelay(const char * name)159 TraceEventSyntheticDelay* TraceEventSyntheticDelayRegistry::GetOrCreateDelay(
160 const char* name) {
161 // Try to find an existing delay first without locking to make the common case
162 // fast.
163 int delay_count = base::subtle::Acquire_Load(&delay_count_);
164 for (int i = 0; i < delay_count; ++i) {
165 if (!strcmp(name, delays_[i].name_.c_str()))
166 return &delays_[i];
167 }
168
169 AutoLock lock(lock_);
170 delay_count = base::subtle::Acquire_Load(&delay_count_);
171 for (int i = 0; i < delay_count; ++i) {
172 if (!strcmp(name, delays_[i].name_.c_str()))
173 return &delays_[i];
174 }
175
176 DCHECK(delay_count < kMaxSyntheticDelays)
177 << "must increase kMaxSyntheticDelays";
178 if (delay_count >= kMaxSyntheticDelays)
179 return &dummy_delay_;
180
181 delays_[delay_count].Initialize(std::string(name), this);
182 base::subtle::Release_Store(&delay_count_, delay_count + 1);
183 return &delays_[delay_count];
184 }
185
Now()186 base::TimeTicks TraceEventSyntheticDelayRegistry::Now() {
187 return base::TimeTicks::HighResNow();
188 }
189
ResetAllDelays()190 void TraceEventSyntheticDelayRegistry::ResetAllDelays() {
191 AutoLock lock(lock_);
192 int delay_count = base::subtle::Acquire_Load(&delay_count_);
193 for (int i = 0; i < delay_count; ++i) {
194 delays_[i].SetTargetDuration(base::TimeDelta());
195 delays_[i].SetClock(this);
196 }
197 }
198
ResetTraceEventSyntheticDelays()199 void ResetTraceEventSyntheticDelays() {
200 TraceEventSyntheticDelayRegistry::GetInstance()->ResetAllDelays();
201 }
202
203 } // namespace debug
204 } // namespace base
205
206 namespace trace_event_internal {
207
ScopedSyntheticDelay(const char * name,base::subtle::AtomicWord * impl_ptr)208 ScopedSyntheticDelay::ScopedSyntheticDelay(const char* name,
209 base::subtle::AtomicWord* impl_ptr)
210 : delay_impl_(GetOrCreateDelay(name, impl_ptr)) {
211 delay_impl_->BeginParallel(&end_time_);
212 }
213
~ScopedSyntheticDelay()214 ScopedSyntheticDelay::~ScopedSyntheticDelay() {
215 delay_impl_->EndParallel(end_time_);
216 }
217
GetOrCreateDelay(const char * name,base::subtle::AtomicWord * impl_ptr)218 base::debug::TraceEventSyntheticDelay* GetOrCreateDelay(
219 const char* name,
220 base::subtle::AtomicWord* impl_ptr) {
221 base::debug::TraceEventSyntheticDelay* delay_impl =
222 reinterpret_cast<base::debug::TraceEventSyntheticDelay*>(
223 base::subtle::Acquire_Load(impl_ptr));
224 if (!delay_impl) {
225 delay_impl = base::debug::TraceEventSyntheticDelayRegistry::GetInstance()
226 ->GetOrCreateDelay(name);
227 base::subtle::Release_Store(
228 impl_ptr, reinterpret_cast<base::subtle::AtomicWord>(delay_impl));
229 }
230 return delay_impl;
231 }
232
233 } // namespace trace_event_internal
234