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
9namespace {
10const int kMaxSyntheticDelays = 32;
11}  // namespace
12
13namespace base {
14namespace trace_event {
15
16TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {}
17TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {}
18
19class 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
42TraceEventSyntheticDelay::TraceEventSyntheticDelay()
43    : mode_(STATIC), begin_count_(0), trigger_count_(0), clock_(NULL) {}
44
45TraceEventSyntheticDelay::~TraceEventSyntheticDelay() {}
46
47TraceEventSyntheticDelay* TraceEventSyntheticDelay::Lookup(
48    const std::string& name) {
49  return TraceEventSyntheticDelayRegistry::GetInstance()->GetOrCreateDelay(
50      name.c_str());
51}
52
53void TraceEventSyntheticDelay::Initialize(
54    const std::string& name,
55    TraceEventSyntheticDelayClock* clock) {
56  name_ = name;
57  clock_ = clock;
58}
59
60void TraceEventSyntheticDelay::SetTargetDuration(TimeDelta target_duration) {
61  AutoLock lock(lock_);
62  target_duration_ = target_duration;
63  trigger_count_ = 0;
64  begin_count_ = 0;
65}
66
67void TraceEventSyntheticDelay::SetMode(Mode mode) {
68  AutoLock lock(lock_);
69  mode_ = mode;
70}
71
72void TraceEventSyntheticDelay::SetClock(TraceEventSyntheticDelayClock* clock) {
73  AutoLock lock(lock_);
74  clock_ = clock;
75}
76
77void 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
95void 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
109void 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
125void TraceEventSyntheticDelay::EndParallel(TimeTicks end_time) {
126  if (!end_time.is_null())
127    ApplyDelay(end_time);
128}
129
130TimeTicks 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
139void 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
146TraceEventSyntheticDelayRegistry*
147TraceEventSyntheticDelayRegistry::GetInstance() {
148  return Singleton<
149      TraceEventSyntheticDelayRegistry,
150      LeakySingletonTraits<TraceEventSyntheticDelayRegistry> >::get();
151}
152
153TraceEventSyntheticDelayRegistry::TraceEventSyntheticDelayRegistry()
154    : delay_count_(0) {}
155
156TraceEventSyntheticDelay* 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
183TimeTicks TraceEventSyntheticDelayRegistry::Now() {
184  return TimeTicks::Now();
185}
186
187void 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
196void ResetTraceEventSyntheticDelays() {
197  TraceEventSyntheticDelayRegistry::GetInstance()->ResetAllDelays();
198}
199
200}  // namespace trace_event
201}  // namespace base
202
203namespace trace_event_internal {
204
205ScopedSyntheticDelay::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
211ScopedSyntheticDelay::~ScopedSyntheticDelay() {
212  delay_impl_->EndParallel(end_time_);
213}
214
215base::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