| <!DOCTYPE html> |
| <!-- |
| Copyright 2020 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/metrics/metric_registry.html"> |
| |
| <script> |
| 'use strict'; |
| |
| /** |
| * This metric is used for custom performance benchmark. |
| * It captures the following: |
| * - Recorded trace events with name starts with "custom_metric:" on the C++ |
| * side. |
| * - Duration between performance.mark('<YOUR_METRIC_NAME>:benchmark_begin') |
| * and performance.mark('<YOUR_METRIC_NAME>:benchmark_end') on the JS side. |
| * - performance.mark('<YOUR_METRIC_NAME>:<YOUR_METRIC_VALUE>:benchmark_value') |
| * on the JS side. |
| */ |
| tr.exportTo('tr.metrics', function() { |
| function customMetric(histograms, model, opt_options) { |
| const chromeHelper = model.getOrCreateHelper( |
| tr.model.helpers.ChromeModelHelper); |
| if (!chromeHelper) { |
| // Chrome isn't present. |
| return; |
| } |
| |
| const TITLE_PREFIX = 'custom_metric:'; |
| const traces = new Map(); |
| const benchmarkValues = new Map(); |
| // Collect trace events. |
| for (const helper of chromeHelper.browserHelpers) { |
| if (!helper.mainThread) continue; |
| for (const slice of helper.mainThread.sliceGroup.slices.concat( |
| helper.mainThread.asyncSliceGroup.slices)) { |
| if (!slice.error && slice.title.startsWith(TITLE_PREFIX)) { |
| if (!traces.has(slice.title)) { |
| traces.set(slice.title, []); |
| } |
| traces.get(slice.title).push(slice.duration); |
| } |
| } |
| } |
| |
| // Collect performance.mark(). |
| const BENCHMARK_BEGIN = 'benchmark_begin'; |
| const BENCHMARK_END = 'benchmark_end'; |
| const BENCHMARK_VALUE = 'benchmark_value'; |
| const marks = new Map(); |
| for (const helper of Object.values(chromeHelper.rendererHelpers)) { |
| for (const event of helper.mainThread.sliceGroup.childEvents()) { |
| const navId = getNavigationId(event); |
| if (!navId || !event.category.includes('blink.user_timing')) continue; |
| const {title} = event; |
| const index = title.lastIndexOf(':'); |
| if (index === -1) { |
| continue; |
| } |
| const name = title.substring(0, index); |
| const lastPart = title.substring(index + 1); |
| if (lastPart === BENCHMARK_BEGIN) { |
| marks.set(name, event); |
| } else if (lastPart === BENCHMARK_END) { |
| if (!marks.has(name)) { |
| continue; |
| } |
| const range = tr.b.math.Range.fromExplicitRange( |
| marks.get(name).start, event.start); |
| if (!traces.has(name)) { |
| traces.set(name, []); |
| } |
| traces.get(name).push(range.duration); |
| marks.delete(name); |
| } else if (lastPart === BENCHMARK_VALUE) { |
| const index2 = name.lastIndexOf(':'); |
| if (index2 === -1) { |
| continue; |
| } |
| const key = name.substring(0, index2); |
| const value = Number(name.substring(index2 + 1)); |
| if (key && !isNaN(value)) { |
| if (!benchmarkValues.has(key)) { |
| benchmarkValues.set(key, []); |
| } |
| benchmarkValues.get(key).push(value); |
| } |
| } |
| } |
| } |
| |
| // Generate histograms. |
| traces.forEach((value, key) => { |
| histograms.createHistogram(key, |
| tr.b.Unit.byName.timeDurationInMs_smallerIsBetter, |
| value); |
| }); |
| benchmarkValues.forEach((value, key) => { |
| histograms.createHistogram(key, |
| tr.b.Unit.byName.unitlessNumber_smallerIsBetter, |
| value); |
| }); |
| } |
| |
| function getNavigationId(event) { |
| return event.args.data && event.args.data.navigationId; |
| } |
| |
| tr.metrics.MetricRegistry.register(customMetric, { |
| supportsRangeOfInterest: false, |
| }); |
| |
| return { |
| customMetric, |
| }; |
| }); |
| </script> |