Index: tracing/tracing/metrics/v8/utils.html |
diff --git a/tracing/tracing/metrics/v8/utils.html b/tracing/tracing/metrics/v8/utils.html |
index cf9fae1e34644a40d98ce8ebbf895f7a8ab13e42..0a14dd4db5aaa332b2efb2499efab2794819008f 100644 |
--- a/tracing/tracing/metrics/v8/utils.html |
+++ b/tracing/tracing/metrics/v8/utils.html |
@@ -4,7 +4,9 @@ Copyright 2016 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. |
--> |
+<link rel="import" href="/tracing/base/piecewise_linear_function.html"> |
<link rel="import" href="/tracing/base/range.html"> |
+<link rel="import" href="/tracing/base/range_utils.html"> |
<link rel="import" href="/tracing/metrics/metric_registry.html"> |
<link rel="import" href="/tracing/value/numeric.html"> |
<link rel="import" href="/tracing/value/unit.html"> |
@@ -17,6 +19,9 @@ tr.exportTo('tr.metrics.v8.utils', function() { |
// The title of the idle task event. |
var IDLE_TASK_EVENT = 'SingleThreadIdleTaskRunner::RunTask'; |
+ // V8 execution event. |
+ var V8_EXECUTE = 'V8.Execute'; |
+ |
// GC events start with this prefix. |
var GC_EVENT_PREFIX = 'V8.GC'; |
@@ -38,8 +43,8 @@ tr.exportTo('tr.metrics.v8.utils', function() { |
}; |
/** |
- * Finds the first parent of the |event| for which the |predicate| holds. |
- */ |
+ * Finds the first parent of the |event| for which the |predicate| holds. |
+ */ |
function findParent(event, predicate) { |
var parent = event.parentSlice; |
while (parent) { |
@@ -59,6 +64,14 @@ tr.exportTo('tr.metrics.v8.utils', function() { |
return event.title === LOW_MEMORY_EVENT; |
} |
+ function isV8ExecuteEvent(event) { |
+ return event.title === V8_EXECUTE; |
+ } |
+ |
+ function isTopV8ExecuteEvent(event) { |
+ return isV8ExecuteEvent(event) && findParent(isV8ExecuteEvent) === null; |
+ } |
+ |
function isGarbageCollectionEvent(event) { |
// Low memory notification is handled specially because it contains |
// several full mark compact events. |
@@ -96,15 +109,146 @@ tr.exportTo('tr.metrics.v8.utils', function() { |
return prefix + '.' + name; |
} |
+ /** |
+ * Given a list of intervals, returns a new list with all overalapping |
+ * intervals merged into a single interval. |
+ */ |
+ function unionOfIntervals(intervals) { |
+ if (intervals.length === 0) |
+ return []; |
+ return tr.b.mergeRanges( |
+ intervals.map(x => ({min: x.start, max: x.end})), 1e-6, |
+ function(ranges) { |
+ return { |
+ start: ranges.reduce((acc, x) => Math.min(acc, x.min), ranges[0].min), |
+ end: ranges.reduce((acc, x) => Math.max(acc, x.max), ranges[0].max) |
+ }; |
+ } |
+ ); |
+ } |
+ |
+ /** |
+ * An end-point of a window that is sliding from left to right |
+ * over |points| starting from time |start|. |
+ * It is intended to be used only by the |mutatorUtilization| function. |
+ * @constructor |
+ */ |
+ function WindowEndpoint(start, points) { |
+ this.points = points; |
+ // The index of the last passed point. |
+ this.lastIndex = -1; |
+ // The position of the end-point in the time line. |
+ this.position = start; |
+ // The distance until the next point. |
+ this.distanceUntilNextPoint = points[0].position - start; |
+ // The cumulative duration of GC pauses until this position. |
+ this.cummulativePause = 0; |
+ // The number of entered GC intervals. |
+ this.stackDepth = 0; |
+ } |
+ |
+ WindowEndpoint.prototype = { |
+ // Advance the end-point by the given |delta|. |
+ advance: function(delta) { |
+ var points = this.points; |
+ if (delta < this.distanceUntilNextPoint) { |
+ this.position += delta; |
+ this.cummulativePause += this.stackDepth > 0 ? delta : 0; |
+ this.distanceUntilNextPoint = |
+ points[this.lastIndex + 1].position - this.position; |
+ } else { |
+ this.position += this.distanceUntilNextPoint; |
+ this.cummulativePause += |
+ this.stackDepth > 0 ? this.distanceUntilNextPoint : 0; |
+ this.distanceUntilNextPoint = 0; |
+ this.lastIndex++; |
+ if (this.lastIndex < points.length) { |
+ this.stackDepth += points[this.lastIndex].delta; |
+ if (this.lastIndex + 1 < points.length) |
+ this.distanceUntilNextPoint = |
+ points[this.lastIndex + 1].position - this.position; |
+ } |
+ } |
+ } |
+ }; |
+ |
+ /** |
+ * Returns mutator utilization as a piecewise linear function. |
+ * Mutator utilization for a window size w is a function of time mu_w(t) |
+ * that shows how much time in [t, t+w] is left for the mutator relative |
+ * to the time window size. |
+ * More formally: |
+ * mu_w(t) = (w - total_time_spent_in_gc_in(t, t + w)) / w. |
+ * The range of mu_w(t) is [0..1]. |
+ * See "A Parallel, Real-Time Garbage Collector" by Cheng et. al. for |
+ * more info [https://www.cs.cmu.edu/~guyb/papers/gc2001.pdf]. |
+ * |
+ * All parameters must use the same time unit. |
+ * @param {number} start The start time of execution. |
+ * @param {number} end The end time of execution. |
+ * @param {number} timeWindow The size of the time window. |
+ * @param {!Array<!{start: number, end: number}>} intervals The list of |
+ * GC pauses. |
+ */ |
+ function mutatorUtilization(start, end, timeWindow, intervals) { |
+ var mu = new tr.b.PiecewiseLinearFunction(); |
+ // If the interval is smaller than the time window, then the function is |
+ // empty. |
+ if (end - start <= timeWindow) |
+ return mu; |
+ // If there are GC pauses then the mutator utilization is 1.0. |
+ if (intervals.length === 0) { |
+ mu.push(start, 1.0, end - timeWindow, 1.0); |
+ return mu; |
+ } |
+ intervals = unionOfIntervals(intervals); |
+ // Create a point for the start and the end of each interval. |
+ var points = []; |
+ intervals.forEach(function(interval) { |
+ points.push({position: interval.start, delta: 1}); |
+ points.push({position: interval.end, delta: -1}); |
+ }); |
+ points.sort((a, b) => a.position - b.position); |
+ points.push({position: end, delta: 0}); |
+ // The left and the right limit of the sliding window. |
+ var left = new WindowEndpoint(start, points); |
+ var right = new WindowEndpoint(start, points); |
+ // Advance the right end-point until we get the correct window size. |
+ while (right.position - left.position < timeWindow) |
+ right.advance(timeWindow - (right.position - left.position)); |
+ while (right.lastIndex < points.length) { |
+ // Advance the window end-points by the largest possible amount |
+ // without jumping over a point. |
+ var distanceUntilNextPoint = |
+ Math.min(left.distanceUntilNextPoint, right.distanceUntilNextPoint); |
+ var position1 = left.position; |
+ var value1 = right.cummulativePause - left.cummulativePause; |
+ left.advance(distanceUntilNextPoint); |
+ right.advance(distanceUntilNextPoint); |
+ // Add a new mutator utilization segment only if it is non-trivial. |
+ if (distanceUntilNextPoint > 0) { |
+ var position2 = left.position; |
+ var value2 = right.cummulativePause - left.cummulativePause; |
+ mu.push(position1, 1.0 - value1 / timeWindow, |
+ position2, 1.0 - value2 / timeWindow); |
+ } |
+ } |
+ return mu; |
+ } |
+ |
return { |
findParent: findParent, |
isIdleTask: isIdleTask, |
isLowMemoryEvent: isLowMemoryEvent, |
+ isV8ExecuteEvent: isV8ExecuteEvent, |
+ isTopV8ExecuteEvent: isTopV8ExecuteEvent, |
isGarbageCollectionEvent: isGarbageCollectionEvent, |
isTopGarbageCollectionEvent: isTopGarbageCollectionEvent, |
isSubGarbageCollectionEvent: isSubGarbageCollectionEvent, |
topGarbageCollectionEventName: topGarbageCollectionEventName, |
- subGarbageCollectionEventName: subGarbageCollectionEventName |
+ subGarbageCollectionEventName: subGarbageCollectionEventName, |
+ unionOfIntervals: unionOfIntervals, |
+ mutatorUtilization: mutatorUtilization |
}; |
}); |
</script> |