1// Copyright 2021 the V8 project 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#ifndef V8_LOGGING_COUNTERS_SCOPES_H_
6#define V8_LOGGING_COUNTERS_SCOPES_H_
7
8#include "src/execution/isolate.h"
9#include "src/logging/counters.h"
10#include "src/logging/log.h"
11
12namespace v8 {
13namespace internal {
14
15class BaseTimedHistogramScope {
16 protected:
17  explicit BaseTimedHistogramScope(TimedHistogram* histogram)
18      : histogram_(histogram) {}
19
20  void StartInternal() {
21    DCHECK(histogram_->ToggleRunningState(true));
22    timer_.Start();
23  }
24
25  void StopInternal() {
26    DCHECK(histogram_->ToggleRunningState(false));
27    histogram_->AddTimedSample(timer_.Elapsed());
28    timer_.Stop();
29  }
30
31  V8_INLINE void Start() {
32    if (histogram_->Enabled()) StartInternal();
33  }
34
35  V8_INLINE void Stop() {
36    if (histogram_->Enabled()) StopInternal();
37  }
38
39  V8_INLINE void LogStart(Isolate* isolate) {
40    Logger::CallEventLogger(isolate, histogram_->name(),
41                            v8::LogEventStatus::kStart, true);
42  }
43
44  V8_INLINE void LogEnd(Isolate* isolate) {
45    Logger::CallEventLogger(isolate, histogram_->name(),
46                            v8::LogEventStatus::kEnd, true);
47  }
48
49  base::ElapsedTimer timer_;
50  TimedHistogram* histogram_;
51};
52
53// Helper class for scoping a TimedHistogram.
54class V8_NODISCARD TimedHistogramScope : public BaseTimedHistogramScope {
55 public:
56  explicit TimedHistogramScope(TimedHistogram* histogram,
57                               Isolate* isolate = nullptr)
58      : BaseTimedHistogramScope(histogram), isolate_(isolate) {
59    Start();
60    if (isolate_) LogStart(isolate_);
61  }
62
63  ~TimedHistogramScope() {
64    Stop();
65    if (isolate_) LogEnd(isolate_);
66  }
67
68 private:
69  Isolate* const isolate_;
70
71  DISALLOW_IMPLICIT_CONSTRUCTORS(TimedHistogramScope);
72};
73
74enum class OptionalTimedHistogramScopeMode { TAKE_TIME, DONT_TAKE_TIME };
75
76// Helper class for scoping a TimedHistogram.
77// It will not take time for mode = DONT_TAKE_TIME.
78class V8_NODISCARD OptionalTimedHistogramScope
79    : public BaseTimedHistogramScope {
80 public:
81  OptionalTimedHistogramScope(TimedHistogram* histogram, Isolate* isolate,
82                              OptionalTimedHistogramScopeMode mode)
83      : BaseTimedHistogramScope(histogram), isolate_(isolate), mode_(mode) {
84    if (mode != OptionalTimedHistogramScopeMode::TAKE_TIME) return;
85    Start();
86    LogStart(isolate_);
87  }
88
89  ~OptionalTimedHistogramScope() {
90    if (mode_ != OptionalTimedHistogramScopeMode::TAKE_TIME) return;
91    Stop();
92    LogEnd(isolate_);
93  }
94
95 private:
96  Isolate* const isolate_;
97  const OptionalTimedHistogramScopeMode mode_;
98  DISALLOW_IMPLICIT_CONSTRUCTORS(OptionalTimedHistogramScope);
99};
100
101// Helper class for scoping a TimedHistogram, where the histogram is selected at
102// stop time rather than start time.
103class V8_NODISCARD LazyTimedHistogramScope : public BaseTimedHistogramScope {
104 public:
105  LazyTimedHistogramScope() : BaseTimedHistogramScope(nullptr) {
106    timer_.Start();
107  }
108  ~LazyTimedHistogramScope() {
109    // We should set the histogram before this scope exits.
110    Stop();
111  }
112
113  void set_histogram(TimedHistogram* histogram) {
114    DCHECK_IMPLIES(histogram->Enabled(), histogram->ToggleRunningState(true));
115    histogram_ = histogram;
116  }
117};
118
119// Helper class for scoping a NestedHistogramTimer.
120class V8_NODISCARD NestedTimedHistogramScope : public BaseTimedHistogramScope {
121 public:
122  explicit NestedTimedHistogramScope(NestedTimedHistogram* histogram,
123                                     Isolate* isolate = nullptr)
124      : BaseTimedHistogramScope(histogram), isolate_(isolate) {
125    Start();
126  }
127  ~NestedTimedHistogramScope() { Stop(); }
128
129 private:
130  friend NestedTimedHistogram;
131  friend PauseNestedTimedHistogramScope;
132
133  void StartInteral() {
134    previous_scope_ = timed_histogram()->Enter(this);
135    base::TimeTicks now = base::TimeTicks::Now();
136    if (previous_scope_) previous_scope_->Pause(now);
137    timer_.Start(now);
138  }
139
140  void StopInternal() {
141    timed_histogram()->Leave(previous_scope_);
142    base::TimeTicks now = base::TimeTicks::Now();
143    base::TimeDelta elapsed = timer_.Elapsed(now);
144    histogram_->AddTimedSample(elapsed);
145    if (isolate_) RecordLongTaskTime(elapsed);
146#ifdef DEBUG
147    // StopInternal() is called in the destructor and don't access timer_
148    // after that.
149    timer_.Stop();
150#endif
151    if (previous_scope_) previous_scope_->Resume(now);
152  }
153
154  V8_INLINE void Start() {
155    if (histogram_->Enabled()) StartInteral();
156    LogStart(timed_histogram()->counters()->isolate());
157  }
158
159  V8_INLINE void Stop() {
160    if (histogram_->Enabled()) StopInternal();
161    LogEnd(timed_histogram()->counters()->isolate());
162  }
163
164  void Pause(base::TimeTicks now) {
165    DCHECK(histogram_->Enabled());
166    timer_.Pause(now);
167  }
168
169  void Resume(base::TimeTicks now) {
170    DCHECK(histogram_->Enabled());
171    timer_.Resume(now);
172  }
173
174  void RecordLongTaskTime(base::TimeDelta elapsed) const {
175    if (histogram_ == isolate_->counters()->execute()) {
176      isolate_->GetCurrentLongTaskStats()->v8_execute_us +=
177          elapsed.InMicroseconds();
178    }
179  }
180
181  NestedTimedHistogram* timed_histogram() {
182    return static_cast<NestedTimedHistogram*>(histogram_);
183  }
184
185  NestedTimedHistogramScope* previous_scope_;
186  Isolate* isolate_;
187};
188
189// Temporarily pause a NestedTimedHistogram when for instance leaving V8 for
190// external callbacks.
191class V8_NODISCARD PauseNestedTimedHistogramScope {
192 public:
193  explicit PauseNestedTimedHistogramScope(NestedTimedHistogram* histogram)
194      : histogram_(histogram) {
195    previous_scope_ = histogram_->Enter(nullptr);
196    if (isEnabled()) {
197      previous_scope_->Pause(base::TimeTicks::Now());
198    }
199  }
200  ~PauseNestedTimedHistogramScope() {
201    histogram_->Leave(previous_scope_);
202    if (isEnabled()) {
203      previous_scope_->Resume(base::TimeTicks::Now());
204    }
205  }
206
207 private:
208  bool isEnabled() const { return previous_scope_ && histogram_->Enabled(); }
209  NestedTimedHistogram* histogram_;
210  NestedTimedHistogramScope* previous_scope_;
211};
212
213}  // namespace internal
214}  // namespace v8
215
216#endif  // V8_LOGGING_COUNTERS_SCOPES_H_
217