summaryrefslogtreecommitdiffstats
path: root/chromium/base/debug/trace_event_synthetic_delay.h
blob: 06d6cdea1f98b7125ba9f64248a5fe5bfd28a9d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// 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.

// The synthetic delay framework makes it possible to dynamically inject
// arbitrary delays into into different parts of the codebase. This can be used,
// for instance, for testing various task scheduling algorithms.
//
// The delays are specified in terms of a target duration for a given block of
// code. If the code executes faster than the duration, the thread is made to
// sleep until the deadline is met.
//
// Code can be instrumented for delays with two sets of macros. First, for
// delays that should apply within a scope, use the following macro:
//
//   TRACE_EVENT_SYNTHETIC_DELAY("cc.LayerTreeHost.DrawAndSwap");
//
// For delaying operations that span multiple scopes, use:
//
//   TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("cc.Scheduler.BeginMainFrame");
//   ...
//   TRACE_EVENT_SYNTHETIC_DELAY_END("cc.Scheduler.BeginMainFrame");
//
// Here BEGIN establishes the start time for the delay and END executes the
// delay based on the remaining time. If BEGIN is called multiple times in a
// row, END should be called a corresponding number of times. Only the last
// call to END will have an effect.
//
// Note that a single delay may begin on one thread and end on another. This
// implies that a single delay cannot not be applied in several threads at once.

#ifndef BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_
#define BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_

#include "base/atomicops.h"
#include "base/debug/trace_event.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"

// Apply a named delay in the current scope.
#define TRACE_EVENT_SYNTHETIC_DELAY(name)                                     \
  static base::subtle::AtomicWord INTERNAL_TRACE_EVENT_UID(impl_ptr) = 0;     \
  trace_event_internal::ScopedSyntheticDelay INTERNAL_TRACE_EVENT_UID(delay)( \
      name, &INTERNAL_TRACE_EVENT_UID(impl_ptr));

// Begin a named delay, establishing its timing start point. May be called
// multiple times as long as the calls to TRACE_EVENT_SYNTHETIC_DELAY_END are
// balanced. Only the first call records the timing start point.
#define TRACE_EVENT_SYNTHETIC_DELAY_BEGIN(name)                          \
  do {                                                                   \
    static base::subtle::AtomicWord impl_ptr = 0;                        \
    trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->Begin();    \
  } while (false)

// End a named delay. The delay is applied only if this call matches the
// first corresponding call to TRACE_EVENT_SYNTHETIC_DELAY_BEGIN with the
// same delay.
#define TRACE_EVENT_SYNTHETIC_DELAY_END(name)                         \
  do {                                                                \
    static base::subtle::AtomicWord impl_ptr = 0;                     \
    trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->End();   \
  } while (false)

template <typename Type>
struct DefaultSingletonTraits;

namespace base {
namespace debug {

// Time source for computing delay durations. Used for testing.
class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelayClock {
 public:
  TraceEventSyntheticDelayClock();
  virtual ~TraceEventSyntheticDelayClock();
  virtual base::TimeTicks Now() = 0;

 private:
  DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayClock);
};

// Single delay point instance.
class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelay {
 public:
  enum Mode {
    STATIC,      // Apply the configured delay every time.
    ONE_SHOT,    // Apply the configured delay just once.
    ALTERNATING  // Apply the configured delay every other time.
  };

  // Returns an existing named delay instance or creates a new one with |name|.
  static TraceEventSyntheticDelay* Lookup(const std::string& name);

  void SetTargetDuration(TimeDelta target_duration);
  void SetMode(Mode mode);
  void SetClock(TraceEventSyntheticDelayClock* clock);

  // Begin the delay, establishing its timing start point. May be called
  // multiple times as long as the calls to End() are balanced. Only the first
  // call records the timing start point.
  void Begin();

  // End the delay. The delay is applied only if this call matches the first
  // corresponding call to Begin() with the same delay.
  void End();

  // Begin a parallel instance of the delay. Several parallel instances may be
  // active simultaneously and will complete independently. The computed end
  // time for the delay is stored in |out_end_time|, which should later be
  // passed to EndParallel().
  void BeginParallel(base::TimeTicks* out_end_time);

  // End a previously started parallel delay. |end_time| is the delay end point
  // computed by BeginParallel().
  void EndParallel(base::TimeTicks end_time);

 private:
  TraceEventSyntheticDelay();
  ~TraceEventSyntheticDelay();
  friend class TraceEventSyntheticDelayRegistry;

  void Initialize(const std::string& name,
                  TraceEventSyntheticDelayClock* clock);
  base::TimeTicks CalculateEndTimeLocked(base::TimeTicks start_time);
  void ApplyDelay(base::TimeTicks end_time);

  Lock lock_;
  Mode mode_;
  std::string name_;
  int begin_count_;
  int trigger_count_;
  base::TimeTicks end_time_;
  base::TimeDelta target_duration_;
  TraceEventSyntheticDelayClock* clock_;

  DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelay);
};

// Set the target durations of all registered synthetic delay points to zero.
TRACE_EVENT_API_CLASS_EXPORT void ResetTraceEventSyntheticDelays();

}  // namespace debug
}  // namespace base

namespace trace_event_internal {

// Helper class for scoped delays. Do not use directly.
class TRACE_EVENT_API_CLASS_EXPORT ScopedSyntheticDelay {
 public:
  explicit ScopedSyntheticDelay(const char* name,
                                base::subtle::AtomicWord* impl_ptr);
  ~ScopedSyntheticDelay();

 private:
  base::debug::TraceEventSyntheticDelay* delay_impl_;
  base::TimeTicks end_time_;

  DISALLOW_COPY_AND_ASSIGN(ScopedSyntheticDelay);
};

// Helper for registering delays. Do not use directly.
TRACE_EVENT_API_CLASS_EXPORT base::debug::TraceEventSyntheticDelay*
    GetOrCreateDelay(const char* name, base::subtle::AtomicWord* impl_ptr);

}  // namespace trace_event_internal

#endif /* BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_ */