1 | // Copyright 2012 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_COUNTERS_H_ |
6 | #define V8_COUNTERS_H_ |
7 | |
8 | #include "include/v8.h" |
9 | #include "src/allocation.h" |
10 | #include "src/base/atomic-utils.h" |
11 | #include "src/base/optional.h" |
12 | #include "src/base/platform/elapsed-timer.h" |
13 | #include "src/base/platform/time.h" |
14 | #include "src/counters-definitions.h" |
15 | #include "src/globals.h" |
16 | #include "src/heap-symbols.h" |
17 | #include "src/isolate.h" |
18 | #include "src/objects.h" |
19 | #include "src/runtime/runtime.h" |
20 | #include "src/tracing/trace-event.h" |
21 | #include "src/tracing/traced-value.h" |
22 | #include "src/tracing/tracing-category-observer.h" |
23 | |
24 | namespace v8 { |
25 | namespace internal { |
26 | |
27 | // This struct contains a set of flags that can be modified from multiple |
28 | // threads at runtime unlike the normal FLAG_-like flags which are not modified |
29 | // after V8 instance is initialized. |
30 | |
31 | struct TracingFlags { |
32 | static V8_EXPORT_PRIVATE std::atomic_uint runtime_stats; |
33 | static V8_EXPORT_PRIVATE std::atomic_uint gc_stats; |
34 | static V8_EXPORT_PRIVATE std::atomic_uint ic_stats; |
35 | |
36 | static bool is_runtime_stats_enabled() { |
37 | return runtime_stats.load(std::memory_order_relaxed) != 0; |
38 | } |
39 | |
40 | static bool is_gc_stats_enabled() { |
41 | return gc_stats.load(std::memory_order_relaxed) != 0; |
42 | } |
43 | |
44 | static bool is_ic_stats_enabled() { |
45 | return ic_stats.load(std::memory_order_relaxed) != 0; |
46 | } |
47 | }; |
48 | |
49 | // StatsCounters is an interface for plugging into external |
50 | // counters for monitoring. Counters can be looked up and |
51 | // manipulated by name. |
52 | |
53 | class Counters; |
54 | |
55 | class StatsTable { |
56 | public: |
57 | // Register an application-defined function for recording |
58 | // subsequent counter statistics. |
59 | void SetCounterFunction(CounterLookupCallback f); |
60 | |
61 | // Register an application-defined function to create histograms for |
62 | // recording subsequent histogram samples. |
63 | void SetCreateHistogramFunction(CreateHistogramCallback f) { |
64 | create_histogram_function_ = f; |
65 | } |
66 | |
67 | // Register an application-defined function to add a sample |
68 | // to a histogram created with CreateHistogram function. |
69 | void SetAddHistogramSampleFunction(AddHistogramSampleCallback f) { |
70 | add_histogram_sample_function_ = f; |
71 | } |
72 | |
73 | bool HasCounterFunction() const { return lookup_function_ != nullptr; } |
74 | |
75 | // Lookup the location of a counter by name. If the lookup |
76 | // is successful, returns a non-nullptr pointer for writing the |
77 | // value of the counter. Each thread calling this function |
78 | // may receive a different location to store it's counter. |
79 | // The return value must not be cached and re-used across |
80 | // threads, although a single thread is free to cache it. |
81 | int* FindLocation(const char* name) { |
82 | if (!lookup_function_) return nullptr; |
83 | return lookup_function_(name); |
84 | } |
85 | |
86 | // Create a histogram by name. If the create is successful, |
87 | // returns a non-nullptr pointer for use with AddHistogramSample |
88 | // function. min and max define the expected minimum and maximum |
89 | // sample values. buckets is the maximum number of buckets |
90 | // that the samples will be grouped into. |
91 | void* CreateHistogram(const char* name, |
92 | int min, |
93 | int max, |
94 | size_t buckets) { |
95 | if (!create_histogram_function_) return nullptr; |
96 | return create_histogram_function_(name, min, max, buckets); |
97 | } |
98 | |
99 | // Add a sample to a histogram created with the CreateHistogram |
100 | // function. |
101 | void AddHistogramSample(void* histogram, int sample) { |
102 | if (!add_histogram_sample_function_) return; |
103 | return add_histogram_sample_function_(histogram, sample); |
104 | } |
105 | |
106 | private: |
107 | friend class Counters; |
108 | |
109 | explicit StatsTable(Counters* counters); |
110 | |
111 | CounterLookupCallback lookup_function_; |
112 | CreateHistogramCallback create_histogram_function_; |
113 | AddHistogramSampleCallback add_histogram_sample_function_; |
114 | |
115 | DISALLOW_COPY_AND_ASSIGN(StatsTable); |
116 | }; |
117 | |
118 | // Base class for stats counters. |
119 | class StatsCounterBase { |
120 | protected: |
121 | Counters* counters_; |
122 | const char* name_; |
123 | int* ptr_; |
124 | |
125 | StatsCounterBase() = default; |
126 | StatsCounterBase(Counters* counters, const char* name) |
127 | : counters_(counters), name_(name), ptr_(nullptr) {} |
128 | |
129 | void SetLoc(int* loc, int value) { *loc = value; } |
130 | void IncrementLoc(int* loc) { (*loc)++; } |
131 | void IncrementLoc(int* loc, int value) { (*loc) += value; } |
132 | void DecrementLoc(int* loc) { (*loc)--; } |
133 | void DecrementLoc(int* loc, int value) { (*loc) -= value; } |
134 | |
135 | V8_EXPORT_PRIVATE int* FindLocationInStatsTable() const; |
136 | }; |
137 | |
138 | // StatsCounters are dynamically created values which can be tracked in |
139 | // the StatsTable. They are designed to be lightweight to create and |
140 | // easy to use. |
141 | // |
142 | // Internally, a counter represents a value in a row of a StatsTable. |
143 | // The row has a 32bit value for each process/thread in the table and also |
144 | // a name (stored in the table metadata). Since the storage location can be |
145 | // thread-specific, this class cannot be shared across threads. Note: This |
146 | // class is not thread safe. |
147 | class StatsCounter : public StatsCounterBase { |
148 | public: |
149 | // Sets the counter to a specific value. |
150 | void Set(int value) { |
151 | if (int* loc = GetPtr()) SetLoc(loc, value); |
152 | } |
153 | |
154 | // Increments the counter. |
155 | void Increment() { |
156 | if (int* loc = GetPtr()) IncrementLoc(loc); |
157 | } |
158 | |
159 | void Increment(int value) { |
160 | if (int* loc = GetPtr()) IncrementLoc(loc, value); |
161 | } |
162 | |
163 | // Decrements the counter. |
164 | void Decrement() { |
165 | if (int* loc = GetPtr()) DecrementLoc(loc); |
166 | } |
167 | |
168 | void Decrement(int value) { |
169 | if (int* loc = GetPtr()) DecrementLoc(loc, value); |
170 | } |
171 | |
172 | // Is this counter enabled? |
173 | // Returns false if table is full. |
174 | bool Enabled() { return GetPtr() != nullptr; } |
175 | |
176 | // Get the internal pointer to the counter. This is used |
177 | // by the code generator to emit code that manipulates a |
178 | // given counter without calling the runtime system. |
179 | int* GetInternalPointer() { |
180 | int* loc = GetPtr(); |
181 | DCHECK_NOT_NULL(loc); |
182 | return loc; |
183 | } |
184 | |
185 | private: |
186 | friend class Counters; |
187 | |
188 | StatsCounter() = default; |
189 | StatsCounter(Counters* counters, const char* name) |
190 | : StatsCounterBase(counters, name), lookup_done_(false) {} |
191 | |
192 | // Reset the cached internal pointer. |
193 | void Reset() { lookup_done_ = false; } |
194 | |
195 | // Returns the cached address of this counter location. |
196 | int* GetPtr() { |
197 | if (lookup_done_) return ptr_; |
198 | lookup_done_ = true; |
199 | ptr_ = FindLocationInStatsTable(); |
200 | return ptr_; |
201 | } |
202 | |
203 | bool lookup_done_; |
204 | }; |
205 | |
206 | // Thread safe version of StatsCounter. |
207 | class V8_EXPORT_PRIVATE StatsCounterThreadSafe : public StatsCounterBase { |
208 | public: |
209 | void Set(int Value); |
210 | void Increment(); |
211 | void Increment(int value); |
212 | void Decrement(); |
213 | void Decrement(int value); |
214 | bool Enabled() { return ptr_ != nullptr; } |
215 | int* GetInternalPointer() { |
216 | DCHECK_NOT_NULL(ptr_); |
217 | return ptr_; |
218 | } |
219 | |
220 | private: |
221 | friend class Counters; |
222 | |
223 | StatsCounterThreadSafe(Counters* counters, const char* name); |
224 | void Reset() { ptr_ = FindLocationInStatsTable(); } |
225 | |
226 | base::Mutex mutex_; |
227 | |
228 | DISALLOW_IMPLICIT_CONSTRUCTORS(StatsCounterThreadSafe); |
229 | }; |
230 | |
231 | // A Histogram represents a dynamically created histogram in the |
232 | // StatsTable. Note: This class is thread safe. |
233 | class Histogram { |
234 | public: |
235 | // Add a single sample to this histogram. |
236 | void AddSample(int sample); |
237 | |
238 | // Returns true if this histogram is enabled. |
239 | bool Enabled() { return histogram_ != nullptr; } |
240 | |
241 | const char* name() { return name_; } |
242 | |
243 | int min() const { return min_; } |
244 | int max() const { return max_; } |
245 | int num_buckets() const { return num_buckets_; } |
246 | |
247 | // Asserts that |expected_counters| are the same as the Counters this |
248 | // Histogram reports to. |
249 | void AssertReportsToCounters(Counters* expected_counters) { |
250 | DCHECK_EQ(counters_, expected_counters); |
251 | } |
252 | |
253 | protected: |
254 | Histogram() = default; |
255 | Histogram(const char* name, int min, int max, int num_buckets, |
256 | Counters* counters) |
257 | : name_(name), |
258 | min_(min), |
259 | max_(max), |
260 | num_buckets_(num_buckets), |
261 | histogram_(nullptr), |
262 | counters_(counters) { |
263 | DCHECK(counters_); |
264 | } |
265 | |
266 | Counters* counters() const { return counters_; } |
267 | |
268 | // Reset the cached internal pointer. |
269 | void Reset() { histogram_ = CreateHistogram(); } |
270 | |
271 | private: |
272 | friend class Counters; |
273 | |
274 | void* CreateHistogram() const; |
275 | |
276 | const char* name_; |
277 | int min_; |
278 | int max_; |
279 | int num_buckets_; |
280 | void* histogram_; |
281 | Counters* counters_; |
282 | }; |
283 | |
284 | enum class HistogramTimerResolution { MILLISECOND, MICROSECOND }; |
285 | |
286 | // A thread safe histogram timer. It also allows distributions of |
287 | // nested timed results. |
288 | class TimedHistogram : public Histogram { |
289 | public: |
290 | // Start the timer. Log if isolate non-null. |
291 | V8_EXPORT_PRIVATE void Start(base::ElapsedTimer* timer, Isolate* isolate); |
292 | |
293 | // Stop the timer and record the results. Log if isolate non-null. |
294 | V8_EXPORT_PRIVATE void Stop(base::ElapsedTimer* timer, Isolate* isolate); |
295 | |
296 | // Records a TimeDelta::Max() result. Useful to record percentage of tasks |
297 | // that never got to run in a given scenario. Log if isolate non-null. |
298 | void RecordAbandon(base::ElapsedTimer* timer, Isolate* isolate); |
299 | |
300 | protected: |
301 | friend class Counters; |
302 | HistogramTimerResolution resolution_; |
303 | |
304 | TimedHistogram() = default; |
305 | TimedHistogram(const char* name, int min, int max, |
306 | HistogramTimerResolution resolution, int num_buckets, |
307 | Counters* counters) |
308 | : Histogram(name, min, max, num_buckets, counters), |
309 | resolution_(resolution) {} |
310 | void AddTimeSample(); |
311 | }; |
312 | |
313 | // Helper class for scoping a TimedHistogram. |
314 | class TimedHistogramScope { |
315 | public: |
316 | explicit TimedHistogramScope(TimedHistogram* histogram, |
317 | Isolate* isolate = nullptr) |
318 | : histogram_(histogram), isolate_(isolate) { |
319 | histogram_->Start(&timer_, isolate); |
320 | } |
321 | |
322 | ~TimedHistogramScope() { histogram_->Stop(&timer_, isolate_); } |
323 | |
324 | private: |
325 | base::ElapsedTimer timer_; |
326 | TimedHistogram* histogram_; |
327 | Isolate* isolate_; |
328 | |
329 | DISALLOW_IMPLICIT_CONSTRUCTORS(TimedHistogramScope); |
330 | }; |
331 | |
332 | enum class OptionalTimedHistogramScopeMode { TAKE_TIME, DONT_TAKE_TIME }; |
333 | |
334 | // Helper class for scoping a TimedHistogram. |
335 | // It will not take time for mode = DONT_TAKE_TIME. |
336 | class OptionalTimedHistogramScope { |
337 | public: |
338 | OptionalTimedHistogramScope(TimedHistogram* histogram, Isolate* isolate, |
339 | OptionalTimedHistogramScopeMode mode) |
340 | : histogram_(histogram), isolate_(isolate), mode_(mode) { |
341 | if (mode == OptionalTimedHistogramScopeMode::TAKE_TIME) { |
342 | histogram_->Start(&timer_, isolate); |
343 | } |
344 | } |
345 | |
346 | ~OptionalTimedHistogramScope() { |
347 | if (mode_ == OptionalTimedHistogramScopeMode::TAKE_TIME) { |
348 | histogram_->Stop(&timer_, isolate_); |
349 | } |
350 | } |
351 | |
352 | private: |
353 | base::ElapsedTimer timer_; |
354 | TimedHistogram* const histogram_; |
355 | Isolate* const isolate_; |
356 | const OptionalTimedHistogramScopeMode mode_; |
357 | DISALLOW_IMPLICIT_CONSTRUCTORS(OptionalTimedHistogramScope); |
358 | }; |
359 | |
360 | // Helper class for recording a TimedHistogram asynchronously with manual |
361 | // controls (it will not generate a report if destroyed without explicitly |
362 | // triggering a report). |async_counters| should be a shared_ptr to |
363 | // |histogram->counters()|, making it is safe to report to an |
364 | // AsyncTimedHistogram after the associated isolate has been destroyed. |
365 | // AsyncTimedHistogram can be moved/copied to avoid computing Now() multiple |
366 | // times when the times of multiple tasks are identical; each copy will generate |
367 | // its own report. |
368 | class AsyncTimedHistogram { |
369 | public: |
370 | explicit AsyncTimedHistogram(TimedHistogram* histogram, |
371 | std::shared_ptr<Counters> async_counters) |
372 | : histogram_(histogram), async_counters_(std::move(async_counters)) { |
373 | histogram_->AssertReportsToCounters(async_counters_.get()); |
374 | histogram_->Start(&timer_, nullptr); |
375 | } |
376 | |
377 | // Records the time elapsed to |histogram_| and stops |timer_|. |
378 | void RecordDone() { histogram_->Stop(&timer_, nullptr); } |
379 | |
380 | // Records TimeDelta::Max() to |histogram_| and stops |timer_|. |
381 | void RecordAbandon() { histogram_->RecordAbandon(&timer_, nullptr); } |
382 | |
383 | private: |
384 | base::ElapsedTimer timer_; |
385 | TimedHistogram* histogram_; |
386 | std::shared_ptr<Counters> async_counters_; |
387 | }; |
388 | |
389 | // Helper class for scoping a TimedHistogram, where the histogram is selected at |
390 | // stop time rather than start time. |
391 | // TODO(leszeks): This is heavily reliant on TimedHistogram::Start() doing |
392 | // nothing but starting the timer, and TimedHistogram::Stop() logging the sample |
393 | // correctly even if Start() was not called. This happens to be true iff Stop() |
394 | // is passed a null isolate, but that's an implementation detail of |
395 | // TimedHistogram, and we shouldn't rely on it. |
396 | class LazyTimedHistogramScope { |
397 | public: |
398 | LazyTimedHistogramScope() : histogram_(nullptr) { timer_.Start(); } |
399 | ~LazyTimedHistogramScope() { |
400 | // We should set the histogram before this scope exits. |
401 | DCHECK_NOT_NULL(histogram_); |
402 | histogram_->Stop(&timer_, nullptr); |
403 | } |
404 | |
405 | void set_histogram(TimedHistogram* histogram) { histogram_ = histogram; } |
406 | |
407 | private: |
408 | base::ElapsedTimer timer_; |
409 | TimedHistogram* histogram_; |
410 | }; |
411 | |
412 | // A HistogramTimer allows distributions of non-nested timed results |
413 | // to be created. WARNING: This class is not thread safe and can only |
414 | // be run on the foreground thread. |
415 | class HistogramTimer : public TimedHistogram { |
416 | public: |
417 | // Note: public for testing purposes only. |
418 | HistogramTimer(const char* name, int min, int max, |
419 | HistogramTimerResolution resolution, int num_buckets, |
420 | Counters* counters) |
421 | : TimedHistogram(name, min, max, resolution, num_buckets, counters) {} |
422 | |
423 | inline void Start(); |
424 | inline void Stop(); |
425 | |
426 | // Returns true if the timer is running. |
427 | bool Running() { |
428 | return Enabled() && timer_.IsStarted(); |
429 | } |
430 | |
431 | // TODO(bmeurer): Remove this when HistogramTimerScope is fixed. |
432 | #ifdef DEBUG |
433 | base::ElapsedTimer* timer() { return &timer_; } |
434 | #endif |
435 | |
436 | private: |
437 | friend class Counters; |
438 | |
439 | base::ElapsedTimer timer_; |
440 | |
441 | HistogramTimer() = default; |
442 | }; |
443 | |
444 | // Helper class for scoping a HistogramTimer. |
445 | // TODO(bmeurer): The ifdeffery is an ugly hack around the fact that the |
446 | // Parser is currently reentrant (when it throws an error, we call back |
447 | // into JavaScript and all bets are off), but ElapsedTimer is not |
448 | // reentry-safe. Fix this properly and remove |allow_nesting|. |
449 | class HistogramTimerScope { |
450 | public: |
451 | explicit HistogramTimerScope(HistogramTimer* timer, |
452 | bool allow_nesting = false) |
453 | #ifdef DEBUG |
454 | : timer_(timer), skipped_timer_start_(false) { |
455 | if (timer_->timer()->IsStarted() && allow_nesting) { |
456 | skipped_timer_start_ = true; |
457 | } else { |
458 | timer_->Start(); |
459 | } |
460 | } |
461 | #else |
462 | : timer_(timer) { |
463 | timer_->Start(); |
464 | } |
465 | #endif |
466 | ~HistogramTimerScope() { |
467 | #ifdef DEBUG |
468 | if (!skipped_timer_start_) { |
469 | timer_->Stop(); |
470 | } |
471 | #else |
472 | timer_->Stop(); |
473 | #endif |
474 | } |
475 | |
476 | private: |
477 | HistogramTimer* timer_; |
478 | #ifdef DEBUG |
479 | bool skipped_timer_start_; |
480 | #endif |
481 | }; |
482 | |
483 | // A histogram timer that can aggregate events within a larger scope. |
484 | // |
485 | // Intended use of this timer is to have an outer (aggregating) and an inner |
486 | // (to be aggregated) scope, where the inner scope measure the time of events, |
487 | // and all those inner scope measurements will be summed up by the outer scope. |
488 | // An example use might be to aggregate the time spent in lazy compilation |
489 | // while running a script. |
490 | // |
491 | // Helpers: |
492 | // - AggregatingHistogramTimerScope, the "outer" scope within which |
493 | // times will be summed up. |
494 | // - AggregatedHistogramTimerScope, the "inner" scope which defines the |
495 | // events to be timed. |
496 | class AggregatableHistogramTimer : public Histogram { |
497 | public: |
498 | // Start/stop the "outer" scope. |
499 | void Start() { time_ = base::TimeDelta(); } |
500 | void Stop() { |
501 | if (time_ != base::TimeDelta()) { |
502 | // Only add non-zero samples, since zero samples represent situations |
503 | // where there were no aggregated samples added. |
504 | AddSample(static_cast<int>(time_.InMicroseconds())); |
505 | } |
506 | } |
507 | |
508 | // Add a time value ("inner" scope). |
509 | void Add(base::TimeDelta other) { time_ += other; } |
510 | |
511 | private: |
512 | friend class Counters; |
513 | |
514 | AggregatableHistogramTimer() = default; |
515 | AggregatableHistogramTimer(const char* name, int min, int max, |
516 | int num_buckets, Counters* counters) |
517 | : Histogram(name, min, max, num_buckets, counters) {} |
518 | |
519 | base::TimeDelta time_; |
520 | }; |
521 | |
522 | // A helper class for use with AggregatableHistogramTimer. This is the |
523 | // // outer-most timer scope used with an AggregatableHistogramTimer. It will |
524 | // // aggregate the information from the inner AggregatedHistogramTimerScope. |
525 | class AggregatingHistogramTimerScope { |
526 | public: |
527 | explicit AggregatingHistogramTimerScope(AggregatableHistogramTimer* histogram) |
528 | : histogram_(histogram) { |
529 | histogram_->Start(); |
530 | } |
531 | ~AggregatingHistogramTimerScope() { histogram_->Stop(); } |
532 | |
533 | private: |
534 | AggregatableHistogramTimer* histogram_; |
535 | }; |
536 | |
537 | // A helper class for use with AggregatableHistogramTimer, the "inner" scope |
538 | // // which defines the events to be timed. |
539 | class AggregatedHistogramTimerScope { |
540 | public: |
541 | explicit AggregatedHistogramTimerScope(AggregatableHistogramTimer* histogram) |
542 | : histogram_(histogram) { |
543 | timer_.Start(); |
544 | } |
545 | ~AggregatedHistogramTimerScope() { histogram_->Add(timer_.Elapsed()); } |
546 | |
547 | private: |
548 | base::ElapsedTimer timer_; |
549 | AggregatableHistogramTimer* histogram_; |
550 | }; |
551 | |
552 | |
553 | // AggretatedMemoryHistogram collects (time, value) sample pairs and turns |
554 | // them into time-uniform samples for the backing historgram, such that the |
555 | // backing histogram receives one sample every T ms, where the T is controlled |
556 | // by the FLAG_histogram_interval. |
557 | // |
558 | // More formally: let F be a real-valued function that maps time to sample |
559 | // values. We define F as a linear interpolation between adjacent samples. For |
560 | // each time interval [x; x + T) the backing histogram gets one sample value |
561 | // that is the average of F(t) in the interval. |
562 | template <typename Histogram> |
563 | class AggregatedMemoryHistogram { |
564 | public: |
565 | // Note: public for testing purposes only. |
566 | explicit AggregatedMemoryHistogram(Histogram* backing_histogram) |
567 | : AggregatedMemoryHistogram() { |
568 | backing_histogram_ = backing_histogram; |
569 | } |
570 | |
571 | // Invariants that hold before and after AddSample if |
572 | // is_initialized_ is true: |
573 | // |
574 | // 1) For we processed samples that came in before start_ms_ and sent the |
575 | // corresponding aggregated samples to backing histogram. |
576 | // 2) (last_ms_, last_value_) is the last received sample. |
577 | // 3) last_ms_ < start_ms_ + FLAG_histogram_interval. |
578 | // 4) aggregate_value_ is the average of the function that is constructed by |
579 | // linearly interpolating samples received between start_ms_ and last_ms_. |
580 | void AddSample(double current_ms, double current_value); |
581 | |
582 | private: |
583 | friend class Counters; |
584 | |
585 | AggregatedMemoryHistogram() |
586 | : is_initialized_(false), |
587 | start_ms_(0.0), |
588 | last_ms_(0.0), |
589 | aggregate_value_(0.0), |
590 | last_value_(0.0), |
591 | backing_histogram_(nullptr) {} |
592 | double Aggregate(double current_ms, double current_value); |
593 | |
594 | bool is_initialized_; |
595 | double start_ms_; |
596 | double last_ms_; |
597 | double aggregate_value_; |
598 | double last_value_; |
599 | Histogram* backing_histogram_; |
600 | }; |
601 | |
602 | |
603 | template <typename Histogram> |
604 | void AggregatedMemoryHistogram<Histogram>::AddSample(double current_ms, |
605 | double current_value) { |
606 | if (!is_initialized_) { |
607 | aggregate_value_ = current_value; |
608 | start_ms_ = current_ms; |
609 | last_value_ = current_value; |
610 | last_ms_ = current_ms; |
611 | is_initialized_ = true; |
612 | } else { |
613 | const double kEpsilon = 1e-6; |
614 | const int kMaxSamples = 1000; |
615 | if (current_ms < last_ms_ + kEpsilon) { |
616 | // Two samples have the same time, remember the last one. |
617 | last_value_ = current_value; |
618 | } else { |
619 | double sample_interval_ms = FLAG_histogram_interval; |
620 | double end_ms = start_ms_ + sample_interval_ms; |
621 | if (end_ms <= current_ms + kEpsilon) { |
622 | // Linearly interpolate between the last_ms_ and the current_ms. |
623 | double slope = (current_value - last_value_) / (current_ms - last_ms_); |
624 | int i; |
625 | // Send aggregated samples to the backing histogram from the start_ms |
626 | // to the current_ms. |
627 | for (i = 0; i < kMaxSamples && end_ms <= current_ms + kEpsilon; i++) { |
628 | double end_value = last_value_ + (end_ms - last_ms_) * slope; |
629 | double sample_value; |
630 | if (i == 0) { |
631 | // Take aggregate_value_ into account. |
632 | sample_value = Aggregate(end_ms, end_value); |
633 | } else { |
634 | // There is no aggregate_value_ for i > 0. |
635 | sample_value = (last_value_ + end_value) / 2; |
636 | } |
637 | backing_histogram_->AddSample(static_cast<int>(sample_value + 0.5)); |
638 | last_value_ = end_value; |
639 | last_ms_ = end_ms; |
640 | end_ms += sample_interval_ms; |
641 | } |
642 | if (i == kMaxSamples) { |
643 | // We hit the sample limit, ignore the remaining samples. |
644 | aggregate_value_ = current_value; |
645 | start_ms_ = current_ms; |
646 | } else { |
647 | aggregate_value_ = last_value_; |
648 | start_ms_ = last_ms_; |
649 | } |
650 | } |
651 | aggregate_value_ = current_ms > start_ms_ + kEpsilon |
652 | ? Aggregate(current_ms, current_value) |
653 | : aggregate_value_; |
654 | last_value_ = current_value; |
655 | last_ms_ = current_ms; |
656 | } |
657 | } |
658 | } |
659 | |
660 | |
661 | template <typename Histogram> |
662 | double AggregatedMemoryHistogram<Histogram>::Aggregate(double current_ms, |
663 | double current_value) { |
664 | double interval_ms = current_ms - start_ms_; |
665 | double value = (current_value + last_value_) / 2; |
666 | // The aggregate_value_ is the average for [start_ms_; last_ms_]. |
667 | // The value is the average for [last_ms_; current_ms]. |
668 | // Return the weighted average of the aggregate_value_ and the value. |
669 | return aggregate_value_ * ((last_ms_ - start_ms_) / interval_ms) + |
670 | value * ((current_ms - last_ms_) / interval_ms); |
671 | } |
672 | |
673 | class RuntimeCallCounter final { |
674 | public: |
675 | RuntimeCallCounter() : RuntimeCallCounter(nullptr) {} |
676 | explicit RuntimeCallCounter(const char* name) |
677 | : name_(name), count_(0), time_(0) {} |
678 | V8_NOINLINE void Reset(); |
679 | V8_NOINLINE void Dump(v8::tracing::TracedValue* value); |
680 | void Add(RuntimeCallCounter* other); |
681 | |
682 | const char* name() const { return name_; } |
683 | int64_t count() const { return count_; } |
684 | base::TimeDelta time() const { |
685 | return base::TimeDelta::FromMicroseconds(time_); |
686 | } |
687 | void Increment() { count_++; } |
688 | void Add(base::TimeDelta delta) { time_ += delta.InMicroseconds(); } |
689 | |
690 | private: |
691 | friend class RuntimeCallStats; |
692 | |
693 | const char* name_; |
694 | int64_t count_; |
695 | // Stored as int64_t so that its initialization can be deferred. |
696 | int64_t time_; |
697 | }; |
698 | |
699 | // RuntimeCallTimer is used to keep track of the stack of currently active |
700 | // timers used for properly measuring the own time of a RuntimeCallCounter. |
701 | class RuntimeCallTimer final { |
702 | public: |
703 | RuntimeCallCounter* counter() { return counter_; } |
704 | void set_counter(RuntimeCallCounter* counter) { counter_ = counter; } |
705 | RuntimeCallTimer* parent() const { return parent_.Value(); } |
706 | void set_parent(RuntimeCallTimer* timer) { parent_.SetValue(timer); } |
707 | const char* name() const { return counter_->name(); } |
708 | |
709 | inline bool IsStarted(); |
710 | |
711 | inline void Start(RuntimeCallCounter* counter, RuntimeCallTimer* parent); |
712 | void Snapshot(); |
713 | inline RuntimeCallTimer* Stop(); |
714 | |
715 | // Make the time source configurable for testing purposes. |
716 | V8_EXPORT_PRIVATE static base::TimeTicks (*Now)(); |
717 | |
718 | private: |
719 | inline void Pause(base::TimeTicks now); |
720 | inline void Resume(base::TimeTicks now); |
721 | inline void CommitTimeToCounter(); |
722 | |
723 | RuntimeCallCounter* counter_ = nullptr; |
724 | base::AtomicValue<RuntimeCallTimer*> parent_; |
725 | base::TimeTicks start_ticks_; |
726 | base::TimeDelta elapsed_; |
727 | }; |
728 | |
729 | #define FOR_EACH_GC_COUNTER(V) \ |
730 | TRACER_SCOPES(V) \ |
731 | TRACER_BACKGROUND_SCOPES(V) |
732 | |
733 | #define FOR_EACH_API_COUNTER(V) \ |
734 | V(ArrayBuffer_Cast) \ |
735 | V(ArrayBuffer_Detach) \ |
736 | V(ArrayBuffer_New) \ |
737 | V(Array_CloneElementAt) \ |
738 | V(Array_New) \ |
739 | V(BigInt64Array_New) \ |
740 | V(BigInt_NewFromWords) \ |
741 | V(BigIntObject_BigIntValue) \ |
742 | V(BigIntObject_New) \ |
743 | V(BigUint64Array_New) \ |
744 | V(BooleanObject_BooleanValue) \ |
745 | V(BooleanObject_New) \ |
746 | V(Context_New) \ |
747 | V(Context_NewRemoteContext) \ |
748 | V(DataView_New) \ |
749 | V(Date_New) \ |
750 | V(Date_NumberValue) \ |
751 | V(Debug_Call) \ |
752 | V(debug_GetPrivateFields) \ |
753 | V(Error_New) \ |
754 | V(External_New) \ |
755 | V(Float32Array_New) \ |
756 | V(Float64Array_New) \ |
757 | V(Function_Call) \ |
758 | V(Function_New) \ |
759 | V(Function_NewInstance) \ |
760 | V(FunctionTemplate_GetFunction) \ |
761 | V(FunctionTemplate_New) \ |
762 | V(FunctionTemplate_NewRemoteInstance) \ |
763 | V(FunctionTemplate_NewWithCache) \ |
764 | V(FunctionTemplate_NewWithFastHandler) \ |
765 | V(Int16Array_New) \ |
766 | V(Int32Array_New) \ |
767 | V(Int8Array_New) \ |
768 | V(Isolate_DateTimeConfigurationChangeNotification) \ |
769 | V(Isolate_LocaleConfigurationChangeNotification) \ |
770 | V(JSON_Parse) \ |
771 | V(JSON_Stringify) \ |
772 | V(Map_AsArray) \ |
773 | V(Map_Clear) \ |
774 | V(Map_Delete) \ |
775 | V(Map_Get) \ |
776 | V(Map_Has) \ |
777 | V(Map_New) \ |
778 | V(Map_Set) \ |
779 | V(Message_GetEndColumn) \ |
780 | V(Message_GetLineNumber) \ |
781 | V(Message_GetSourceLine) \ |
782 | V(Message_GetStartColumn) \ |
783 | V(Module_Evaluate) \ |
784 | V(Module_InstantiateModule) \ |
785 | V(NumberObject_New) \ |
786 | V(NumberObject_NumberValue) \ |
787 | V(Object_CallAsConstructor) \ |
788 | V(Object_CallAsFunction) \ |
789 | V(Object_CreateDataProperty) \ |
790 | V(Object_DefineOwnProperty) \ |
791 | V(Object_DefineProperty) \ |
792 | V(Object_Delete) \ |
793 | V(Object_DeleteProperty) \ |
794 | V(Object_ForceSet) \ |
795 | V(Object_Get) \ |
796 | V(Object_GetOwnPropertyDescriptor) \ |
797 | V(Object_GetOwnPropertyNames) \ |
798 | V(Object_GetPropertyAttributes) \ |
799 | V(Object_GetPropertyNames) \ |
800 | V(Object_GetRealNamedProperty) \ |
801 | V(Object_GetRealNamedPropertyAttributes) \ |
802 | V(Object_GetRealNamedPropertyAttributesInPrototypeChain) \ |
803 | V(Object_GetRealNamedPropertyInPrototypeChain) \ |
804 | V(Object_Has) \ |
805 | V(Object_HasOwnProperty) \ |
806 | V(Object_HasRealIndexedProperty) \ |
807 | V(Object_HasRealNamedCallbackProperty) \ |
808 | V(Object_HasRealNamedProperty) \ |
809 | V(Object_New) \ |
810 | V(Object_ObjectProtoToString) \ |
811 | V(Object_Set) \ |
812 | V(Object_SetAccessor) \ |
813 | V(Object_SetIntegrityLevel) \ |
814 | V(Object_SetPrivate) \ |
815 | V(Object_SetPrototype) \ |
816 | V(ObjectTemplate_New) \ |
817 | V(ObjectTemplate_NewInstance) \ |
818 | V(Object_ToArrayIndex) \ |
819 | V(Object_ToBigInt) \ |
820 | V(Object_ToDetailString) \ |
821 | V(Object_ToInt32) \ |
822 | V(Object_ToInteger) \ |
823 | V(Object_ToNumber) \ |
824 | V(Object_ToObject) \ |
825 | V(Object_ToString) \ |
826 | V(Object_ToUint32) \ |
827 | V(Persistent_New) \ |
828 | V(Private_New) \ |
829 | V(Promise_Catch) \ |
830 | V(Promise_Chain) \ |
831 | V(Promise_HasRejectHandler) \ |
832 | V(Promise_Resolver_New) \ |
833 | V(Promise_Resolver_Reject) \ |
834 | V(Promise_Resolver_Resolve) \ |
835 | V(Promise_Result) \ |
836 | V(Promise_Status) \ |
837 | V(Promise_Then) \ |
838 | V(Proxy_New) \ |
839 | V(RangeError_New) \ |
840 | V(ReferenceError_New) \ |
841 | V(RegExp_New) \ |
842 | V(ScriptCompiler_Compile) \ |
843 | V(ScriptCompiler_CompileFunctionInContext) \ |
844 | V(ScriptCompiler_CompileUnbound) \ |
845 | V(Script_Run) \ |
846 | V(Set_Add) \ |
847 | V(Set_AsArray) \ |
848 | V(Set_Clear) \ |
849 | V(Set_Delete) \ |
850 | V(Set_Has) \ |
851 | V(Set_New) \ |
852 | V(SharedArrayBuffer_New) \ |
853 | V(String_Concat) \ |
854 | V(String_NewExternalOneByte) \ |
855 | V(String_NewExternalTwoByte) \ |
856 | V(String_NewFromOneByte) \ |
857 | V(String_NewFromTwoByte) \ |
858 | V(String_NewFromUtf8) \ |
859 | V(StringObject_New) \ |
860 | V(StringObject_StringValue) \ |
861 | V(String_Write) \ |
862 | V(String_WriteUtf8) \ |
863 | V(Symbol_New) \ |
864 | V(SymbolObject_New) \ |
865 | V(SymbolObject_SymbolValue) \ |
866 | V(SyntaxError_New) \ |
867 | V(TracedGlobal_New) \ |
868 | V(TryCatch_StackTrace) \ |
869 | V(TypeError_New) \ |
870 | V(Uint16Array_New) \ |
871 | V(Uint32Array_New) \ |
872 | V(Uint8Array_New) \ |
873 | V(Uint8ClampedArray_New) \ |
874 | V(UnboundScript_GetId) \ |
875 | V(UnboundScript_GetLineNumber) \ |
876 | V(UnboundScript_GetName) \ |
877 | V(UnboundScript_GetSourceMappingURL) \ |
878 | V(UnboundScript_GetSourceURL) \ |
879 | V(ValueDeserializer_ReadHeader) \ |
880 | V(ValueDeserializer_ReadValue) \ |
881 | V(ValueSerializer_WriteValue) \ |
882 | V(Value_InstanceOf) \ |
883 | V(Value_Int32Value) \ |
884 | V(Value_IntegerValue) \ |
885 | V(Value_NumberValue) \ |
886 | V(Value_TypeOf) \ |
887 | V(Value_Uint32Value) \ |
888 | V(WeakMap_Get) \ |
889 | V(WeakMap_New) \ |
890 | V(WeakMap_Set) |
891 | |
892 | #define FOR_EACH_MANUAL_COUNTER(V) \ |
893 | V(AccessorGetterCallback) \ |
894 | V(AccessorSetterCallback) \ |
895 | V(ArrayLengthGetter) \ |
896 | V(ArrayLengthSetter) \ |
897 | V(BoundFunctionLengthGetter) \ |
898 | V(BoundFunctionNameGetter) \ |
899 | V(CompileAnalyse) \ |
900 | V(CompileBackgroundAnalyse) \ |
901 | V(CompileBackgroundCompileTask) \ |
902 | V(CompileBackgroundEval) \ |
903 | V(CompileBackgroundFunction) \ |
904 | V(CompileBackgroundIgnition) \ |
905 | V(CompileBackgroundRewriteReturnResult) \ |
906 | V(CompileBackgroundScopeAnalysis) \ |
907 | V(CompileBackgroundScript) \ |
908 | V(CompileCollectSourcePositions) \ |
909 | V(CompileDeserialize) \ |
910 | V(CompileEnqueueOnDispatcher) \ |
911 | V(CompileEval) \ |
912 | V(CompileFinalizeBackgroundCompileTask) \ |
913 | V(CompileFinishNowOnDispatcher) \ |
914 | V(CompileFunction) \ |
915 | V(CompileGetFromOptimizedCodeMap) \ |
916 | V(CompileIgnition) \ |
917 | V(CompileIgnitionFinalization) \ |
918 | V(CompileRewriteReturnResult) \ |
919 | V(CompileScopeAnalysis) \ |
920 | V(CompileScript) \ |
921 | V(CompileSerialize) \ |
922 | V(CompileWaitForDispatcher) \ |
923 | V(DeoptimizeCode) \ |
924 | V(DeserializeContext) \ |
925 | V(DeserializeIsolate) \ |
926 | V(FunctionCallback) \ |
927 | V(FunctionLengthGetter) \ |
928 | V(FunctionPrototypeGetter) \ |
929 | V(FunctionPrototypeSetter) \ |
930 | V(GC_Custom_AllAvailableGarbage) \ |
931 | V(GC_Custom_IncrementalMarkingObserver) \ |
932 | V(GC_Custom_SlowAllocateRaw) \ |
933 | V(GCEpilogueCallback) \ |
934 | V(GCPrologueCallback) \ |
935 | V(Genesis) \ |
936 | V(GetMoreDataCallback) \ |
937 | V(IndexedDefinerCallback) \ |
938 | V(IndexedDeleterCallback) \ |
939 | V(IndexedDescriptorCallback) \ |
940 | V(IndexedEnumeratorCallback) \ |
941 | V(IndexedGetterCallback) \ |
942 | V(IndexedQueryCallback) \ |
943 | V(IndexedSetterCallback) \ |
944 | V(Invoke) \ |
945 | V(InvokeApiFunction) \ |
946 | V(InvokeApiInterruptCallbacks) \ |
947 | V(InvokeFunctionCallback) \ |
948 | V(JS_Execution) \ |
949 | V(Map_SetPrototype) \ |
950 | V(Map_TransitionToAccessorProperty) \ |
951 | V(Map_TransitionToDataProperty) \ |
952 | V(MessageListenerCallback) \ |
953 | V(NamedDefinerCallback) \ |
954 | V(NamedDeleterCallback) \ |
955 | V(NamedDescriptorCallback) \ |
956 | V(NamedEnumeratorCallback) \ |
957 | V(NamedGetterCallback) \ |
958 | V(NamedQueryCallback) \ |
959 | V(NamedSetterCallback) \ |
960 | V(Object_DeleteProperty) \ |
961 | V(ObjectVerify) \ |
962 | V(OptimizeCode) \ |
963 | V(ParseArrowFunctionLiteral) \ |
964 | V(ParseBackgroundArrowFunctionLiteral) \ |
965 | V(ParseBackgroundFunctionLiteral) \ |
966 | V(ParseBackgroundProgram) \ |
967 | V(ParseEval) \ |
968 | V(ParseFunction) \ |
969 | V(ParseFunctionLiteral) \ |
970 | V(ParseProgram) \ |
971 | V(PreParseArrowFunctionLiteral) \ |
972 | V(PreParseBackgroundArrowFunctionLiteral) \ |
973 | V(PreParseBackgroundWithVariableResolution) \ |
974 | V(PreParseWithVariableResolution) \ |
975 | V(PropertyCallback) \ |
976 | V(PrototypeMap_TransitionToAccessorProperty) \ |
977 | V(PrototypeMap_TransitionToDataProperty) \ |
978 | V(PrototypeObject_DeleteProperty) \ |
979 | V(RecompileConcurrent) \ |
980 | V(RecompileSynchronous) \ |
981 | V(ReconfigureToDataProperty) \ |
982 | V(StringLengthGetter) \ |
983 | V(TestCounter1) \ |
984 | V(TestCounter2) \ |
985 | V(TestCounter3) |
986 | |
987 | #define FOR_EACH_HANDLER_COUNTER(V) \ |
988 | V(KeyedLoadIC_KeyedLoadSloppyArgumentsStub) \ |
989 | V(KeyedLoadIC_LoadElementDH) \ |
990 | V(KeyedLoadIC_LoadIndexedInterceptorStub) \ |
991 | V(KeyedLoadIC_LoadIndexedStringDH) \ |
992 | V(KeyedLoadIC_SlowStub) \ |
993 | V(KeyedStoreIC_ElementsTransitionAndStoreStub) \ |
994 | V(KeyedStoreIC_KeyedStoreSloppyArgumentsStub) \ |
995 | V(KeyedStoreIC_SlowStub) \ |
996 | V(KeyedStoreIC_StoreElementStub) \ |
997 | V(KeyedStoreIC_StoreFastElementStub) \ |
998 | V(LoadGlobalIC_LoadScriptContextField) \ |
999 | V(LoadGlobalIC_SlowStub) \ |
1000 | V(LoadIC_FunctionPrototypeStub) \ |
1001 | V(LoadIC_HandlerCacheHit_Accessor) \ |
1002 | V(LoadIC_LoadAccessorDH) \ |
1003 | V(LoadIC_LoadAccessorFromPrototypeDH) \ |
1004 | V(LoadIC_LoadApiGetterFromPrototypeDH) \ |
1005 | V(LoadIC_LoadCallback) \ |
1006 | V(LoadIC_LoadConstantDH) \ |
1007 | V(LoadIC_LoadConstantFromPrototypeDH) \ |
1008 | V(LoadIC_LoadFieldDH) \ |
1009 | V(LoadIC_LoadFieldFromPrototypeDH) \ |
1010 | V(LoadIC_LoadGlobalDH) \ |
1011 | V(LoadIC_LoadGlobalFromPrototypeDH) \ |
1012 | V(LoadIC_LoadIntegerIndexedExoticDH) \ |
1013 | V(LoadIC_LoadInterceptorDH) \ |
1014 | V(LoadIC_LoadInterceptorFromPrototypeDH) \ |
1015 | V(LoadIC_LoadNativeDataPropertyDH) \ |
1016 | V(LoadIC_LoadNativeDataPropertyFromPrototypeDH) \ |
1017 | V(LoadIC_LoadNonexistentDH) \ |
1018 | V(LoadIC_LoadNonMaskingInterceptorDH) \ |
1019 | V(LoadIC_LoadNormalDH) \ |
1020 | V(LoadIC_LoadNormalFromPrototypeDH) \ |
1021 | V(LoadIC_NonReceiver) \ |
1022 | V(LoadIC_Premonomorphic) \ |
1023 | V(LoadIC_SlowStub) \ |
1024 | V(LoadIC_StringLength) \ |
1025 | V(LoadIC_StringWrapperLength) \ |
1026 | V(StoreGlobalIC_SlowStub) \ |
1027 | V(StoreGlobalIC_StoreScriptContextField) \ |
1028 | V(StoreGlobalIC_Premonomorphic) \ |
1029 | V(StoreIC_HandlerCacheHit_Accessor) \ |
1030 | V(StoreIC_NonReceiver) \ |
1031 | V(StoreIC_Premonomorphic) \ |
1032 | V(StoreIC_SlowStub) \ |
1033 | V(StoreIC_StoreAccessorDH) \ |
1034 | V(StoreIC_StoreAccessorOnPrototypeDH) \ |
1035 | V(StoreIC_StoreApiSetterOnPrototypeDH) \ |
1036 | V(StoreIC_StoreFieldDH) \ |
1037 | V(StoreIC_StoreGlobalDH) \ |
1038 | V(StoreIC_StoreGlobalTransitionDH) \ |
1039 | V(StoreIC_StoreInterceptorStub) \ |
1040 | V(StoreIC_StoreNativeDataPropertyDH) \ |
1041 | V(StoreIC_StoreNativeDataPropertyOnPrototypeDH) \ |
1042 | V(StoreIC_StoreNormalDH) \ |
1043 | V(StoreIC_StoreTransitionDH) \ |
1044 | V(StoreInArrayLiteralIC_SlowStub) |
1045 | |
1046 | enum RuntimeCallCounterId { |
1047 | #define CALL_RUNTIME_COUNTER(name) kGC_##name, |
1048 | FOR_EACH_GC_COUNTER(CALL_RUNTIME_COUNTER) |
1049 | #undef CALL_RUNTIME_COUNTER |
1050 | #define CALL_RUNTIME_COUNTER(name) k##name, |
1051 | FOR_EACH_MANUAL_COUNTER(CALL_RUNTIME_COUNTER) |
1052 | #undef CALL_RUNTIME_COUNTER |
1053 | #define CALL_RUNTIME_COUNTER(name, nargs, ressize) kRuntime_##name, |
1054 | FOR_EACH_INTRINSIC(CALL_RUNTIME_COUNTER) |
1055 | #undef CALL_RUNTIME_COUNTER |
1056 | #define CALL_BUILTIN_COUNTER(name) kBuiltin_##name, |
1057 | BUILTIN_LIST_C(CALL_BUILTIN_COUNTER) |
1058 | #undef CALL_BUILTIN_COUNTER |
1059 | #define CALL_BUILTIN_COUNTER(name) kAPI_##name, |
1060 | FOR_EACH_API_COUNTER(CALL_BUILTIN_COUNTER) |
1061 | #undef CALL_BUILTIN_COUNTER |
1062 | #define CALL_BUILTIN_COUNTER(name) kHandler_##name, |
1063 | FOR_EACH_HANDLER_COUNTER(CALL_BUILTIN_COUNTER) |
1064 | #undef CALL_BUILTIN_COUNTER |
1065 | kNumberOfCounters |
1066 | }; |
1067 | |
1068 | class RuntimeCallStats final { |
1069 | public: |
1070 | V8_EXPORT_PRIVATE RuntimeCallStats(); |
1071 | |
1072 | // Starting measuring the time for a function. This will establish the |
1073 | // connection to the parent counter for properly calculating the own times. |
1074 | V8_EXPORT_PRIVATE void Enter(RuntimeCallTimer* timer, |
1075 | RuntimeCallCounterId counter_id); |
1076 | |
1077 | // Leave a scope for a measured runtime function. This will properly add |
1078 | // the time delta to the current_counter and subtract the delta from its |
1079 | // parent. |
1080 | V8_EXPORT_PRIVATE void Leave(RuntimeCallTimer* timer); |
1081 | |
1082 | // Set counter id for the innermost measurement. It can be used to refine |
1083 | // event kind when a runtime entry counter is too generic. |
1084 | V8_EXPORT_PRIVATE void CorrectCurrentCounterId( |
1085 | RuntimeCallCounterId counter_id); |
1086 | |
1087 | V8_EXPORT_PRIVATE void Reset(); |
1088 | // Add all entries from another stats object. |
1089 | void Add(RuntimeCallStats* other); |
1090 | V8_EXPORT_PRIVATE void Print(std::ostream& os); |
1091 | V8_EXPORT_PRIVATE void Print(); |
1092 | V8_NOINLINE void Dump(v8::tracing::TracedValue* value); |
1093 | |
1094 | ThreadId thread_id() const { return thread_id_; } |
1095 | RuntimeCallTimer* current_timer() { return current_timer_.Value(); } |
1096 | RuntimeCallCounter* current_counter() { return current_counter_.Value(); } |
1097 | bool InUse() { return in_use_; } |
1098 | bool IsCalledOnTheSameThread(); |
1099 | |
1100 | static const int kNumberOfCounters = |
1101 | static_cast<int>(RuntimeCallCounterId::kNumberOfCounters); |
1102 | RuntimeCallCounter* GetCounter(RuntimeCallCounterId counter_id) { |
1103 | return &counters_[static_cast<int>(counter_id)]; |
1104 | } |
1105 | RuntimeCallCounter* GetCounter(int counter_id) { |
1106 | return &counters_[counter_id]; |
1107 | } |
1108 | |
1109 | private: |
1110 | // Top of a stack of active timers. |
1111 | base::AtomicValue<RuntimeCallTimer*> current_timer_; |
1112 | // Active counter object associated with current timer. |
1113 | base::AtomicValue<RuntimeCallCounter*> current_counter_; |
1114 | // Used to track nested tracing scopes. |
1115 | bool in_use_; |
1116 | ThreadId thread_id_; |
1117 | RuntimeCallCounter counters_[kNumberOfCounters]; |
1118 | }; |
1119 | |
1120 | class WorkerThreadRuntimeCallStats final { |
1121 | public: |
1122 | WorkerThreadRuntimeCallStats(); |
1123 | ~WorkerThreadRuntimeCallStats(); |
1124 | |
1125 | // Returns the TLS key associated with this WorkerThreadRuntimeCallStats. |
1126 | base::Thread::LocalStorageKey GetKey(); |
1127 | |
1128 | // Returns a new worker thread runtime call stats table managed by this |
1129 | // WorkerThreadRuntimeCallStats. |
1130 | RuntimeCallStats* NewTable(); |
1131 | |
1132 | // Adds the counters from the worker thread tables to |main_call_stats|. |
1133 | void AddToMainTable(RuntimeCallStats* main_call_stats); |
1134 | |
1135 | private: |
1136 | base::Mutex mutex_; |
1137 | std::vector<std::unique_ptr<RuntimeCallStats>> tables_; |
1138 | base::Optional<base::Thread::LocalStorageKey> tls_key_; |
1139 | }; |
1140 | |
1141 | // Creating a WorkerThreadRuntimeCallStatsScope will provide a thread-local |
1142 | // runtime call stats table, and will dump the table to an immediate trace event |
1143 | // when it is destroyed. |
1144 | class WorkerThreadRuntimeCallStatsScope final { |
1145 | public: |
1146 | WorkerThreadRuntimeCallStatsScope( |
1147 | WorkerThreadRuntimeCallStats* off_thread_stats); |
1148 | ~WorkerThreadRuntimeCallStatsScope(); |
1149 | |
1150 | RuntimeCallStats* Get() const { return table_; } |
1151 | |
1152 | private: |
1153 | RuntimeCallStats* table_; |
1154 | }; |
1155 | |
1156 | #define CHANGE_CURRENT_RUNTIME_COUNTER(runtime_call_stats, counter_id) \ |
1157 | do { \ |
1158 | if (V8_UNLIKELY(TracingFlags::is_runtime_stats_enabled()) && \ |
1159 | runtime_call_stats) { \ |
1160 | runtime_call_stats->CorrectCurrentCounterId(counter_id); \ |
1161 | } \ |
1162 | } while (false) |
1163 | |
1164 | #define TRACE_HANDLER_STATS(isolate, counter_name) \ |
1165 | CHANGE_CURRENT_RUNTIME_COUNTER( \ |
1166 | isolate->counters()->runtime_call_stats(), \ |
1167 | RuntimeCallCounterId::kHandler_##counter_name) |
1168 | |
1169 | // A RuntimeCallTimerScopes wraps around a RuntimeCallTimer to measure the |
1170 | // the time of C++ scope. |
1171 | class RuntimeCallTimerScope { |
1172 | public: |
1173 | inline RuntimeCallTimerScope(Isolate* isolate, |
1174 | RuntimeCallCounterId counter_id); |
1175 | // This constructor is here just to avoid calling GetIsolate() when the |
1176 | // stats are disabled and the isolate is not directly available. |
1177 | inline RuntimeCallTimerScope(Isolate* isolate, HeapObject heap_object, |
1178 | RuntimeCallCounterId counter_id); |
1179 | inline RuntimeCallTimerScope(RuntimeCallStats* stats, |
1180 | RuntimeCallCounterId counter_id) { |
1181 | if (V8_LIKELY(!TracingFlags::is_runtime_stats_enabled() || |
1182 | stats == nullptr)) |
1183 | return; |
1184 | stats_ = stats; |
1185 | stats_->Enter(&timer_, counter_id); |
1186 | } |
1187 | |
1188 | inline ~RuntimeCallTimerScope() { |
1189 | if (V8_UNLIKELY(stats_ != nullptr)) { |
1190 | stats_->Leave(&timer_); |
1191 | } |
1192 | } |
1193 | |
1194 | private: |
1195 | RuntimeCallStats* stats_ = nullptr; |
1196 | RuntimeCallTimer timer_; |
1197 | |
1198 | DISALLOW_COPY_AND_ASSIGN(RuntimeCallTimerScope); |
1199 | }; |
1200 | |
1201 | // This file contains all the v8 counters that are in use. |
1202 | class Counters : public std::enable_shared_from_this<Counters> { |
1203 | public: |
1204 | explicit Counters(Isolate* isolate); |
1205 | |
1206 | // Register an application-defined function for recording |
1207 | // subsequent counter statistics. Note: Must be called on the main |
1208 | // thread. |
1209 | void ResetCounterFunction(CounterLookupCallback f); |
1210 | |
1211 | // Register an application-defined function to create histograms for |
1212 | // recording subsequent histogram samples. Note: Must be called on |
1213 | // the main thread. |
1214 | void ResetCreateHistogramFunction(CreateHistogramCallback f); |
1215 | |
1216 | // Register an application-defined function to add a sample |
1217 | // to a histogram. Will be used in all subsequent sample additions. |
1218 | // Note: Must be called on the main thread. |
1219 | void SetAddHistogramSampleFunction(AddHistogramSampleCallback f) { |
1220 | stats_table_.SetAddHistogramSampleFunction(f); |
1221 | } |
1222 | |
1223 | #define HR(name, caption, min, max, num_buckets) \ |
1224 | Histogram* name() { return &name##_; } |
1225 | HISTOGRAM_RANGE_LIST(HR) |
1226 | #undef HR |
1227 | |
1228 | #define HT(name, caption, max, res) \ |
1229 | HistogramTimer* name() { return &name##_; } |
1230 | HISTOGRAM_TIMER_LIST(HT) |
1231 | #undef HT |
1232 | |
1233 | #define HT(name, caption, max, res) \ |
1234 | TimedHistogram* name() { return &name##_; } |
1235 | TIMED_HISTOGRAM_LIST(HT) |
1236 | #undef HT |
1237 | |
1238 | #define AHT(name, caption) \ |
1239 | AggregatableHistogramTimer* name() { return &name##_; } |
1240 | AGGREGATABLE_HISTOGRAM_TIMER_LIST(AHT) |
1241 | #undef AHT |
1242 | |
1243 | #define HP(name, caption) \ |
1244 | Histogram* name() { return &name##_; } |
1245 | HISTOGRAM_PERCENTAGE_LIST(HP) |
1246 | #undef HP |
1247 | |
1248 | #define HM(name, caption) \ |
1249 | Histogram* name() { return &name##_; } |
1250 | HISTOGRAM_LEGACY_MEMORY_LIST(HM) |
1251 | #undef HM |
1252 | |
1253 | #define SC(name, caption) \ |
1254 | StatsCounter* name() { return &name##_; } |
1255 | STATS_COUNTER_LIST_1(SC) |
1256 | STATS_COUNTER_LIST_2(SC) |
1257 | STATS_COUNTER_NATIVE_CODE_LIST(SC) |
1258 | #undef SC |
1259 | |
1260 | #define SC(name, caption) \ |
1261 | StatsCounterThreadSafe* name() { return &name##_; } |
1262 | STATS_COUNTER_TS_LIST(SC) |
1263 | #undef SC |
1264 | |
1265 | // clang-format off |
1266 | enum Id { |
1267 | #define RATE_ID(name, caption, max, res) k_##name, |
1268 | HISTOGRAM_TIMER_LIST(RATE_ID) |
1269 | TIMED_HISTOGRAM_LIST(RATE_ID) |
1270 | #undef RATE_ID |
1271 | #define AGGREGATABLE_ID(name, caption) k_##name, |
1272 | AGGREGATABLE_HISTOGRAM_TIMER_LIST(AGGREGATABLE_ID) |
1273 | #undef AGGREGATABLE_ID |
1274 | #define PERCENTAGE_ID(name, caption) k_##name, |
1275 | HISTOGRAM_PERCENTAGE_LIST(PERCENTAGE_ID) |
1276 | #undef PERCENTAGE_ID |
1277 | #define MEMORY_ID(name, caption) k_##name, |
1278 | HISTOGRAM_LEGACY_MEMORY_LIST(MEMORY_ID) |
1279 | #undef MEMORY_ID |
1280 | #define COUNTER_ID(name, caption) k_##name, |
1281 | STATS_COUNTER_LIST_1(COUNTER_ID) |
1282 | STATS_COUNTER_LIST_2(COUNTER_ID) |
1283 | STATS_COUNTER_TS_LIST(COUNTER_ID) |
1284 | STATS_COUNTER_NATIVE_CODE_LIST(COUNTER_ID) |
1285 | #undef COUNTER_ID |
1286 | #define COUNTER_ID(name) kCountOf##name, kSizeOf##name, |
1287 | INSTANCE_TYPE_LIST(COUNTER_ID) |
1288 | #undef COUNTER_ID |
1289 | #define COUNTER_ID(name) kCountOfCODE_TYPE_##name, \ |
1290 | kSizeOfCODE_TYPE_##name, |
1291 | CODE_KIND_LIST(COUNTER_ID) |
1292 | #undef COUNTER_ID |
1293 | #define COUNTER_ID(name) kCountOfFIXED_ARRAY__##name, \ |
1294 | kSizeOfFIXED_ARRAY__##name, |
1295 | FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(COUNTER_ID) |
1296 | #undef COUNTER_ID |
1297 | stats_counter_count |
1298 | }; |
1299 | // clang-format on |
1300 | |
1301 | RuntimeCallStats* runtime_call_stats() { return &runtime_call_stats_; } |
1302 | |
1303 | WorkerThreadRuntimeCallStats* worker_thread_runtime_call_stats() { |
1304 | return &worker_thread_runtime_call_stats_; |
1305 | } |
1306 | |
1307 | private: |
1308 | friend class StatsTable; |
1309 | friend class StatsCounterBase; |
1310 | friend class Histogram; |
1311 | friend class HistogramTimer; |
1312 | |
1313 | Isolate* isolate_; |
1314 | StatsTable stats_table_; |
1315 | |
1316 | int* FindLocation(const char* name) { |
1317 | return stats_table_.FindLocation(name); |
1318 | } |
1319 | |
1320 | void* CreateHistogram(const char* name, int min, int max, size_t buckets) { |
1321 | return stats_table_.CreateHistogram(name, min, max, buckets); |
1322 | } |
1323 | |
1324 | void AddHistogramSample(void* histogram, int sample) { |
1325 | stats_table_.AddHistogramSample(histogram, sample); |
1326 | } |
1327 | |
1328 | Isolate* isolate() { return isolate_; } |
1329 | |
1330 | #define HR(name, caption, min, max, num_buckets) Histogram name##_; |
1331 | HISTOGRAM_RANGE_LIST(HR) |
1332 | #undef HR |
1333 | |
1334 | #define HT(name, caption, max, res) HistogramTimer name##_; |
1335 | HISTOGRAM_TIMER_LIST(HT) |
1336 | #undef HT |
1337 | |
1338 | #define HT(name, caption, max, res) TimedHistogram name##_; |
1339 | TIMED_HISTOGRAM_LIST(HT) |
1340 | #undef HT |
1341 | |
1342 | #define AHT(name, caption) \ |
1343 | AggregatableHistogramTimer name##_; |
1344 | AGGREGATABLE_HISTOGRAM_TIMER_LIST(AHT) |
1345 | #undef AHT |
1346 | |
1347 | #define HP(name, caption) \ |
1348 | Histogram name##_; |
1349 | HISTOGRAM_PERCENTAGE_LIST(HP) |
1350 | #undef HP |
1351 | |
1352 | #define HM(name, caption) \ |
1353 | Histogram name##_; |
1354 | HISTOGRAM_LEGACY_MEMORY_LIST(HM) |
1355 | #undef HM |
1356 | |
1357 | #define SC(name, caption) \ |
1358 | StatsCounter name##_; |
1359 | STATS_COUNTER_LIST_1(SC) |
1360 | STATS_COUNTER_LIST_2(SC) |
1361 | STATS_COUNTER_NATIVE_CODE_LIST(SC) |
1362 | #undef SC |
1363 | |
1364 | #define SC(name, caption) StatsCounterThreadSafe name##_; |
1365 | STATS_COUNTER_TS_LIST(SC) |
1366 | #undef SC |
1367 | |
1368 | #define SC(name) \ |
1369 | StatsCounter size_of_##name##_; \ |
1370 | StatsCounter count_of_##name##_; |
1371 | INSTANCE_TYPE_LIST(SC) |
1372 | #undef SC |
1373 | |
1374 | #define SC(name) \ |
1375 | StatsCounter size_of_CODE_TYPE_##name##_; \ |
1376 | StatsCounter count_of_CODE_TYPE_##name##_; |
1377 | CODE_KIND_LIST(SC) |
1378 | #undef SC |
1379 | |
1380 | #define SC(name) \ |
1381 | StatsCounter size_of_FIXED_ARRAY_##name##_; \ |
1382 | StatsCounter count_of_FIXED_ARRAY_##name##_; |
1383 | FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC) |
1384 | #undef SC |
1385 | |
1386 | RuntimeCallStats runtime_call_stats_; |
1387 | WorkerThreadRuntimeCallStats worker_thread_runtime_call_stats_; |
1388 | |
1389 | DISALLOW_IMPLICIT_CONSTRUCTORS(Counters); |
1390 | }; |
1391 | |
1392 | void HistogramTimer::Start() { |
1393 | TimedHistogram::Start(&timer_, counters()->isolate()); |
1394 | } |
1395 | |
1396 | void HistogramTimer::Stop() { |
1397 | TimedHistogram::Stop(&timer_, counters()->isolate()); |
1398 | } |
1399 | |
1400 | RuntimeCallTimerScope::RuntimeCallTimerScope(Isolate* isolate, |
1401 | RuntimeCallCounterId counter_id) { |
1402 | if (V8_LIKELY(!TracingFlags::is_runtime_stats_enabled())) return; |
1403 | stats_ = isolate->counters()->runtime_call_stats(); |
1404 | stats_->Enter(&timer_, counter_id); |
1405 | } |
1406 | |
1407 | } // namespace internal |
1408 | } // namespace v8 |
1409 | |
1410 | #endif // V8_COUNTERS_H_ |
1411 | |