| <!DOCTYPE html> |
| <!-- |
| 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/range.html"> |
| <link rel="import" href="/tracing/base/unit.html"> |
| <link rel="import" href="/tracing/metrics/metric_registry.html"> |
| <link rel="import" href="/tracing/metrics/v8/utils.html"> |
| <link rel="import" href="/tracing/value/histogram.html"> |
| |
| <script> |
| 'use strict'; |
| |
| tr.exportTo('tr.metrics.v8', function() { |
| // The time window size for mutator utilization computation. |
| // It is equal to the duration of one frame corresponding to 60 FPS rendering. |
| var TARGET_FPS = 60; |
| var MS_PER_SECOND = 1000; |
| var WINDOW_SIZE_MS = MS_PER_SECOND / TARGET_FPS; |
| |
| function gcMetric(values, model) { |
| addDurationOfTopEvents(values, model); |
| addTotalDurationOfTopEvents(values, model); |
| addDurationOfSubEvents(values, model); |
| addIdleTimesOfTopEvents(values, model); |
| addTotalIdleTimesOfTopEvents(values, model); |
| addPercentageInV8ExecuteOfTopEvents(values, model); |
| addTotalPercentageInV8Execute(values, model); |
| addV8ExecuteMutatorUtilization(values, model); |
| } |
| |
| tr.metrics.MetricRegistry.register(gcMetric); |
| |
| var timeDurationInMs_smallerIsBetter = |
| tr.b.Unit.byName.timeDurationInMs_smallerIsBetter; |
| var percentage_biggerIsBetter = |
| tr.b.Unit.byName.normalizedPercentage_biggerIsBetter; |
| var percentage_smallerIsBetter = |
| tr.b.Unit.byName.normalizedPercentage_smallerIsBetter; |
| |
| // 0.1 steps from 0 to 20 since it is the most common range. |
| // Exponentially increasing steps from 20 to 200. |
| var CUSTOM_BOUNDARIES = tr.v.HistogramBinBoundaries.createLinear(0, 20, 200) |
| .addExponentialBins(200, 100); |
| |
| function createNumericForTopEventTime(name) { |
| var n = new tr.v.Histogram(name, |
| timeDurationInMs_smallerIsBetter, CUSTOM_BOUNDARIES); |
| n.customizeSummaryOptions({ |
| avg: true, |
| count: true, |
| max: true, |
| min: false, |
| std: true, |
| sum: true, |
| percentile: [0.90]}); |
| return n; |
| } |
| |
| function createNumericForSubEventTime(name) { |
| var n = new tr.v.Histogram(name, |
| timeDurationInMs_smallerIsBetter, CUSTOM_BOUNDARIES); |
| n.customizeSummaryOptions({ |
| avg: true, |
| count: false, |
| max: true, |
| min: false, |
| std: false, |
| sum: false, |
| percentile: [0.90] |
| }); |
| return n; |
| } |
| |
| function createNumericForIdleTime(name) { |
| var n = new tr.v.Histogram(name, |
| timeDurationInMs_smallerIsBetter, CUSTOM_BOUNDARIES); |
| n.customizeSummaryOptions({ |
| avg: true, |
| count: false, |
| max: true, |
| min: false, |
| std: false, |
| sum: true, |
| percentile: [] |
| }); |
| return n; |
| } |
| |
| function createPercentage(name, numerator, denominator, unit) { |
| var hist = new tr.v.Histogram(name, unit); |
| if (denominator === 0) |
| hist.addSample(0); |
| else |
| hist.addSample(numerator / denominator); |
| hist.customizeSummaryOptions({ |
| avg: true, |
| count: false, |
| max: false, |
| min: false, |
| std: false, |
| sum: false, |
| percentile: [] |
| }); |
| return hist; |
| } |
| |
| function isNotForcedTopGarbageCollectionEvent(event) { |
| // We exclude garbage collection events forced by benchmark runner, |
| // because they cannot happen in real world. |
| return tr.metrics.v8.utils.isTopGarbageCollectionEvent(event) && |
| !tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event); |
| } |
| |
| function isNotForcedSubGarbageCollectionEvent(event) { |
| // We exclude garbage collection events forced by benchmark runner, |
| // because they cannot happen in real world. |
| return tr.metrics.v8.utils.isSubGarbageCollectionEvent(event) && |
| !tr.metrics.v8.utils.isForcedGarbageCollectionEvent(event); |
| } |
| |
| /** |
| * Example output: |
| * - v8-gc-full-mark-compactor. |
| */ |
| function addDurationOfTopEvents(values, model) { |
| tr.metrics.v8.utils.groupAndProcessEvents(model, |
| isNotForcedTopGarbageCollectionEvent, |
| tr.metrics.v8.utils.topGarbageCollectionEventName, |
| function(name, events) { |
| var cpuDuration = createNumericForTopEventTime(name); |
| events.forEach(function(event) { |
| cpuDuration.addSample(event.cpuDuration); |
| }); |
| values.addHistogram(cpuDuration); |
| } |
| ); |
| } |
| |
| /** |
| * Example output: |
| * - v8-gc-total |
| */ |
| function addTotalDurationOfTopEvents(values, model) { |
| tr.metrics.v8.utils.groupAndProcessEvents(model, |
| isNotForcedTopGarbageCollectionEvent, |
| event => 'v8-gc-total', |
| function(name, events) { |
| var cpuDuration = createNumericForTopEventTime(name); |
| events.forEach(function(event) { |
| cpuDuration.addSample(event.cpuDuration); |
| }); |
| values.addHistogram(cpuDuration); |
| } |
| ); |
| } |
| |
| /** |
| * Example output: |
| * - v8-gc-full-mark-compactor-evacuate. |
| */ |
| function addDurationOfSubEvents(values, model) { |
| tr.metrics.v8.utils.groupAndProcessEvents(model, |
| isNotForcedSubGarbageCollectionEvent, |
| tr.metrics.v8.utils.subGarbageCollectionEventName, |
| function(name, events) { |
| var cpuDuration = createNumericForSubEventTime(name); |
| events.forEach(function(event) { |
| cpuDuration.addSample(event.cpuDuration); |
| }); |
| values.addHistogram(cpuDuration); |
| } |
| ); |
| } |
| |
| /** |
| * Example output: |
| * - v8-gc-full-mark-compactor_idle_deadline_overrun, |
| * - v8-gc-full-mark-compactor_outside_idle, |
| * - v8-gc-full-mark-compactor_percentage_idle. |
| */ |
| function addIdleTimesOfTopEvents(values, model) { |
| tr.metrics.v8.utils.groupAndProcessEvents(model, |
| isNotForcedTopGarbageCollectionEvent, |
| tr.metrics.v8.utils.topGarbageCollectionEventName, |
| function(name, events) { |
| addIdleTimes(values, model, name, events); |
| } |
| ); |
| } |
| |
| /** |
| * Example output: |
| * - v8-gc-total_idle_deadline_overrun, |
| * - v8-gc-total_outside_idle, |
| * - v8-gc-total_percentage_idle. |
| */ |
| function addTotalIdleTimesOfTopEvents(values, model) { |
| tr.metrics.v8.utils.groupAndProcessEvents(model, |
| isNotForcedTopGarbageCollectionEvent, |
| event => 'v8-gc-total', |
| function(name, events) { |
| addIdleTimes(values, model, name, events); |
| } |
| ); |
| } |
| |
| function addIdleTimes(values, model, name, events) { |
| var cpuDuration = createNumericForIdleTime(); |
| var insideIdle = createNumericForIdleTime(); |
| var outsideIdle = createNumericForIdleTime(name + '_outside_idle'); |
| var idleDeadlineOverrun = createNumericForIdleTime( |
| name + '_idle_deadline_overrun'); |
| events.forEach(function(event) { |
| var idleTask = tr.metrics.v8.utils.findParent( |
| event, tr.metrics.v8.utils.isIdleTask); |
| var inside = 0; |
| var overrun = 0; |
| if (idleTask) { |
| var allottedTime = idleTask['args']['allotted_time_ms']; |
| if (event.duration > allottedTime) { |
| overrun = event.duration - allottedTime; |
| // Don't count time over the deadline as being inside idle time. |
| // Since the deadline should be relative to wall clock we |
| // compare allotted_time_ms with wall duration instead of thread |
| // duration, and then assume the thread duration was inside idle |
| // for the same percentage of time. |
| inside = event.cpuDuration * allottedTime / event.duration; |
| } else { |
| inside = event.cpuDuration; |
| } |
| } |
| cpuDuration.addSample(event.cpuDuration); |
| insideIdle.addSample(inside); |
| outsideIdle.addSample(event.cpuDuration - inside); |
| idleDeadlineOverrun.addSample(overrun); |
| }); |
| values.addHistogram(idleDeadlineOverrun); |
| values.addHistogram(outsideIdle); |
| var percentage = createPercentage(name + '_percentage_idle', insideIdle.sum, |
| cpuDuration.sum, |
| percentage_biggerIsBetter); |
| values.addHistogram(percentage); |
| } |
| |
| |
| /** |
| * Example output: |
| * - v8-gc-full-mark-compactor_percentage_in_v8_execute. |
| */ |
| function addPercentageInV8ExecuteOfTopEvents(values, model) { |
| tr.metrics.v8.utils.groupAndProcessEvents(model, |
| isNotForcedTopGarbageCollectionEvent, |
| tr.metrics.v8.utils.topGarbageCollectionEventName, |
| function(name, events) { |
| addPercentageInV8Execute(values, model, name, events); |
| } |
| ); |
| } |
| |
| /** |
| * Example output: |
| * - v8-gc-total_percentage_in_v8_execute. |
| */ |
| function addTotalPercentageInV8Execute(values, model) { |
| tr.metrics.v8.utils.groupAndProcessEvents(model, |
| isNotForcedTopGarbageCollectionEvent, |
| event => 'v8-gc-total', |
| function(name, events) { |
| addPercentageInV8Execute(values, model, name, events); |
| } |
| ); |
| } |
| |
| function addPercentageInV8Execute(values, model, name, events) { |
| var cpuDurationInV8Execute = 0; |
| var cpuDurationTotal = 0; |
| events.forEach(function(event) { |
| var v8Execute = tr.metrics.v8.utils.findParent( |
| event, tr.metrics.v8.utils.isV8ExecuteEvent); |
| if (v8Execute) { |
| cpuDurationInV8Execute += event.cpuDuration; |
| } |
| cpuDurationTotal += event.cpuDuration; |
| }); |
| var percentage = createPercentage( |
| name + '_percentage_in_v8_execute', cpuDurationInV8Execute, |
| cpuDurationTotal, percentage_smallerIsBetter); |
| values.addHistogram(percentage); |
| } |
| |
| function addV8ExecuteMutatorUtilization(values, model) { |
| tr.metrics.v8.utils.groupAndProcessEvents(model, |
| tr.metrics.v8.utils.isTopV8ExecuteEvent, |
| event => 'v8-execute', |
| function(name, events) { |
| events.sort((a, b) => a.start - b.start); |
| var time = 0; |
| var pauses = []; |
| // Glue together the v8.execute events and adjust the GC pause |
| // times accordingly. |
| for (var topEvent of events) { |
| for (var e of topEvent.enumerateAllDescendents()) { |
| if (isNotForcedTopGarbageCollectionEvent(e)) { |
| pauses.push({ start: e.start - topEvent.start + time, |
| end: e.end - topEvent.start + time }); |
| } |
| } |
| time += topEvent.duration; |
| } |
| // Now we have one big v8.execute interval from 0 to |time| and |
| // a list of GC pauses. |
| var mutatorUtilization = tr.metrics.v8.utils.mutatorUtilization( |
| 0, time, WINDOW_SIZE_MS, pauses); |
| [0.90, 0.95, 0.99].forEach(function(percent) { |
| var hist = new tr.v.Histogram( |
| 'v8-execute-mutator-utilization_pct_0' + percent * 100, |
| percentage_biggerIsBetter); |
| hist.addSample(mutatorUtilization.percentile(1.0 - percent)); |
| values.addHistogram(hist); |
| }); |
| var hist = new tr.v.Histogram( |
| 'v8-execute-mutator-utilization_min', percentage_biggerIsBetter); |
| hist.addSample(mutatorUtilization.min); |
| values.addHistogram(hist); |
| } |
| ); |
| } |
| |
| return { |
| gcMetric: gcMetric, |
| WINDOW_SIZE_MS: WINDOW_SIZE_MS // For testing purposes only. |
| }; |
| }); |
| </script> |