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/macros.h"
6 #include "base/memory/singleton.h"
7 #include "base/trace_event/trace_event_synthetic_delay.h"
8
9 namespace {
10 const int kMaxSyntheticDelays = 32;
11 } // namespace
12
13 namespace base {
14 namespace trace_event {
15
TraceEventSyntheticDelayClock()16 TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {}
~TraceEventSyntheticDelayClock()17 TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {}
18
19 class TraceEventSyntheticDelayRegistry : public TraceEventSyntheticDelayClock {
20 public:
21 static TraceEventSyntheticDelayRegistry* GetInstance();
22
23 TraceEventSyntheticDelay* GetOrCreateDelay(const char* name);
24 void ResetAllDelays();
25
26 // TraceEventSyntheticDelayClock implementation.
27 TimeTicks Now() override;
28
29 private:
30 TraceEventSyntheticDelayRegistry();
31
32 friend struct DefaultSingletonTraits<TraceEventSyntheticDelayRegistry>;
33
34 Lock lock_;
35 TraceEventSyntheticDelay delays_[kMaxSyntheticDelays];
36 TraceEventSyntheticDelay dummy_delay_;
37 subtle::Atomic32 delay_count_;
38
39 DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayRegistry);
40 };
41
TraceEventSyntheticDelay()42 TraceEventSyntheticDelay::TraceEventSyntheticDelay()
43 : mode_(STATIC), begin_count_(0), trigger_count_(0), clock_(NULL) {}
44
~TraceEventSyntheticDelay()45 TraceEventSyntheticDelay::~TraceEventSyntheticDelay() {}
46
Lookup(const std::string & name)47 TraceEventSyntheticDelay* TraceEventSyntheticDelay::Lookup(
48 const std::string& name) {
49 return TraceEventSyntheticDelayRegistry::GetInstance()->GetOrCreateDelay(
50 name.c_str());
51 }
52
Initialize(const std::string & name,TraceEventSyntheticDelayClock * clock)53 void TraceEventSyntheticDelay::Initialize(
54 const std::string& name,
55 TraceEventSyntheticDelayClock* clock) {
56 name_ = name;
57 clock_ = clock;
58 }
59
SetTargetDuration(TimeDelta target_duration)60 void TraceEventSyntheticDelay::SetTargetDuration(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 if (!target_duration_.ToInternalValue())
84 return;
85
86 TimeTicks start_time = clock_->Now();
87 {
88 AutoLock lock(lock_);
89 if (++begin_count_ != 1)
90 return;
91 end_time_ = CalculateEndTimeLocked(start_time);
92 }
93 }
94
BeginParallel(TimeTicks * out_end_time)95 void TraceEventSyntheticDelay::BeginParallel(TimeTicks* out_end_time) {
96 // See note in Begin().
97 if (!target_duration_.ToInternalValue()) {
98 *out_end_time = TimeTicks();
99 return;
100 }
101
102 TimeTicks start_time = clock_->Now();
103 {
104 AutoLock lock(lock_);
105 *out_end_time = CalculateEndTimeLocked(start_time);
106 }
107 }
108
End()109 void TraceEventSyntheticDelay::End() {
110 // See note in Begin().
111 if (!target_duration_.ToInternalValue())
112 return;
113
114 TimeTicks end_time;
115 {
116 AutoLock lock(lock_);
117 if (!begin_count_ || --begin_count_ != 0)
118 return;
119 end_time = end_time_;
120 }
121 if (!end_time.is_null())
122 ApplyDelay(end_time);
123 }
124
EndParallel(TimeTicks end_time)125 void TraceEventSyntheticDelay::EndParallel(TimeTicks end_time) {
126 if (!end_time.is_null())
127 ApplyDelay(end_time);
128 }
129
CalculateEndTimeLocked(TimeTicks start_time)130 TimeTicks TraceEventSyntheticDelay::CalculateEndTimeLocked(
131 TimeTicks start_time) {
132 if (mode_ == ONE_SHOT && trigger_count_++)
133 return TimeTicks();
134 else if (mode_ == ALTERNATING && trigger_count_++ % 2)
135 return TimeTicks();
136 return start_time + target_duration_;
137 }
138
ApplyDelay(TimeTicks end_time)139 void TraceEventSyntheticDelay::ApplyDelay(TimeTicks end_time) {
140 TRACE_EVENT0("synthetic_delay", name_.c_str());
141 while (clock_->Now() < end_time) {
142 // Busy loop.
143 }
144 }
145
146 TraceEventSyntheticDelayRegistry*
GetInstance()147 TraceEventSyntheticDelayRegistry::GetInstance() {
148 return Singleton<
149 TraceEventSyntheticDelayRegistry,
150 LeakySingletonTraits<TraceEventSyntheticDelayRegistry> >::get();
151 }
152
TraceEventSyntheticDelayRegistry()153 TraceEventSyntheticDelayRegistry::TraceEventSyntheticDelayRegistry()
154 : delay_count_(0) {}
155
GetOrCreateDelay(const char * name)156 TraceEventSyntheticDelay* TraceEventSyntheticDelayRegistry::GetOrCreateDelay(
157 const char* name) {
158 // Try to find an existing delay first without locking to make the common case
159 // fast.
160 int delay_count = subtle::Acquire_Load(&delay_count_);
161 for (int i = 0; i < delay_count; ++i) {
162 if (!strcmp(name, delays_[i].name_.c_str()))
163 return &delays_[i];
164 }
165
166 AutoLock lock(lock_);
167 delay_count = subtle::Acquire_Load(&delay_count_);
168 for (int i = 0; i < delay_count; ++i) {
169 if (!strcmp(name, delays_[i].name_.c_str()))
170 return &delays_[i];
171 }
172
173 DCHECK(delay_count < kMaxSyntheticDelays)
174 << "must increase kMaxSyntheticDelays";
175 if (delay_count >= kMaxSyntheticDelays)
176 return &dummy_delay_;
177
178 delays_[delay_count].Initialize(std::string(name), this);
179 subtle::Release_Store(&delay_count_, delay_count + 1);
180 return &delays_[delay_count];
181 }
182
Now()183 TimeTicks TraceEventSyntheticDelayRegistry::Now() {
184 return TimeTicks::Now();
185 }
186
ResetAllDelays()187 void TraceEventSyntheticDelayRegistry::ResetAllDelays() {
188 AutoLock lock(lock_);
189 int delay_count = subtle::Acquire_Load(&delay_count_);
190 for (int i = 0; i < delay_count; ++i) {
191 delays_[i].SetTargetDuration(TimeDelta());
192 delays_[i].SetClock(this);
193 }
194 }
195
ResetTraceEventSyntheticDelays()196 void ResetTraceEventSyntheticDelays() {
197 TraceEventSyntheticDelayRegistry::GetInstance()->ResetAllDelays();
198 }
199
200 } // namespace trace_event
201 } // namespace base
202
203 namespace trace_event_internal {
204
ScopedSyntheticDelay(const char * name,base::subtle::AtomicWord * impl_ptr)205 ScopedSyntheticDelay::ScopedSyntheticDelay(const char* name,
206 base::subtle::AtomicWord* impl_ptr)
207 : delay_impl_(GetOrCreateDelay(name, impl_ptr)) {
208 delay_impl_->BeginParallel(&end_time_);
209 }
210
~ScopedSyntheticDelay()211 ScopedSyntheticDelay::~ScopedSyntheticDelay() {
212 delay_impl_->EndParallel(end_time_);
213 }
214
GetOrCreateDelay(const char * name,base::subtle::AtomicWord * impl_ptr)215 base::trace_event::TraceEventSyntheticDelay* GetOrCreateDelay(
216 const char* name,
217 base::subtle::AtomicWord* impl_ptr) {
218 base::trace_event::TraceEventSyntheticDelay* delay_impl =
219 reinterpret_cast<base::trace_event::TraceEventSyntheticDelay*>(
220 base::subtle::Acquire_Load(impl_ptr));
221 if (!delay_impl) {
222 delay_impl =
223 base::trace_event::TraceEventSyntheticDelayRegistry::GetInstance()
224 ->GetOrCreateDelay(name);
225 base::subtle::Release_Store(
226 impl_ptr, reinterpret_cast<base::subtle::AtomicWord>(delay_impl));
227 }
228 return delay_impl;
229 }
230
231 } // namespace trace_event_internal
232