blob: 332a2b1cd04ac432b278c60f8ad42345ad99fbdd [file] [log] [blame]
<!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/utils.html">
<link rel="import" href="/tracing/metrics/metric_registry.html">
<link rel="import" href="/tracing/value/diagnostics/diagnostic_map.html">
<link rel="import" href="/tracing/value/histogram.html">
<script>
'use strict';
tr.exportTo('tr.metrics', function() {
const MEMORY_INFRA_TRACING_CATEGORY = 'disabled-by-default-memory-infra';
const TIME_BOUNDARIES = tr.v.HistogramBinBoundaries.createExponential(
1e-3, 1e5, 30);
const BYTE_BOUNDARIES = tr.v.HistogramBinBoundaries.createExponential(
1, 1e9, 30);
const COUNT_BOUNDARIES = tr.v.HistogramBinBoundaries.createExponential(
1, 1e5, 30);
// By default, we store a single value, so we only need one of the
// statistics to keep track. We choose the average for that.
const SUMMARY_OPTIONS = tr.v.Histogram.AVERAGE_ONLY_SUMMARY_OPTIONS;
// Adds histograms specific to memory-infra dumps.
function addMemoryInfraHistograms(
histograms, model, categoryNamesToTotalEventSizes) {
const memoryDumpCount = model.globalMemoryDumps.length;
if (memoryDumpCount === 0) return;
let totalOverhead = 0;
let nonMemoryInfraThreadOverhead = 0;
const overheadByProvider = {};
for (const process of Object.values(model.processes)) {
for (const thread of Object.values(process.threads)) {
for (const slice of Object.values(thread.sliceGroup.slices)) {
if (slice.category !== MEMORY_INFRA_TRACING_CATEGORY) continue;
totalOverhead += slice.duration;
if (thread.name !== 'MemoryInfra') {
nonMemoryInfraThreadOverhead += slice.duration;
}
if (slice.args && slice.args['dump_provider.name']) {
const providerName = slice.args['dump_provider.name'];
let durationAndCount = overheadByProvider[providerName];
if (durationAndCount === undefined) {
overheadByProvider[providerName] = durationAndCount =
{duration: 0, count: 0};
}
durationAndCount.duration += slice.duration;
durationAndCount.count++;
}
}
}
}
histograms.createHistogram('memory_dump_cpu_overhead',
tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,
totalOverhead / memoryDumpCount, {
binBoundaries: TIME_BOUNDARIES,
description:
'Average CPU overhead on all threads per memory-infra dump',
summaryOptions: SUMMARY_OPTIONS,
});
histograms.createHistogram('nonmemory_thread_memory_dump_cpu_overhead',
tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,
nonMemoryInfraThreadOverhead / memoryDumpCount, {
binBoundaries: TIME_BOUNDARIES,
description: 'Average CPU overhead on non-memory-infra threads ' +
'per memory-infra dump',
summaryOptions: SUMMARY_OPTIONS,
});
for (const [providerName, overhead] of Object.entries(overheadByProvider)) {
histograms.createHistogram(`${providerName}_memory_dump_cpu_overhead`,
tr.b.Unit.byName.timeDurationInMs_smallerIsBetter,
overhead.duration / overhead.count, {
binBoundaries: TIME_BOUNDARIES,
description:
`Average CPU overhead of ${providerName} per OnMemoryDump call`,
summaryOptions: SUMMARY_OPTIONS,
});
}
const memoryInfraEventsSize =
categoryNamesToTotalEventSizes.get(MEMORY_INFRA_TRACING_CATEGORY);
const memoryInfraTraceBytesValue = new tr.v.Histogram(
'total_memory_dump_size',
tr.b.Unit.byName.sizeInBytes_smallerIsBetter, BYTE_BOUNDARIES);
memoryInfraTraceBytesValue.description =
'Total trace size of memory-infra dumps in bytes';
memoryInfraTraceBytesValue.customizeSummaryOptions(SUMMARY_OPTIONS);
memoryInfraTraceBytesValue.addSample(memoryInfraEventsSize);
histograms.addHistogram(memoryInfraTraceBytesValue);
const traceBytesPerDumpValue = new tr.v.Histogram(
'memory_dump_size',
tr.b.Unit.byName.sizeInBytes_smallerIsBetter, BYTE_BOUNDARIES);
traceBytesPerDumpValue.description =
'Average trace size of memory-infra dumps in bytes';
traceBytesPerDumpValue.customizeSummaryOptions(SUMMARY_OPTIONS);
traceBytesPerDumpValue.addSample(memoryInfraEventsSize / memoryDumpCount);
histograms.addHistogram(traceBytesPerDumpValue);
}
// TODO(charliea): The metrics in this file should be renamed to have names
// more consistent with those in the rest of the codebase
// (e.g. 'trace_size_growth_per_second', not 'Max event size in bytes per
// second').
// https://github.com/catapult-project/catapult/issues/3233
function tracingMetric(histograms, model) {
if (!model.stats.hasEventSizesinBytes) return;
const eventStats = model.stats.allTraceEventStatsInTimeIntervals;
eventStats.sort((a, b) => a.timeInterval - b.timeInterval);
const totalTraceBytes = eventStats.reduce(
(a, b) => a + b.totalEventSizeinBytes, 0);
// We maintain a sliding window of records [start ... end-1] where end
// increments each time through the loop, and we move start just far enough
// to keep the window less than 1 second wide. Note that we need to compute
// the number of time intervals (i.e. units that timeInterval is given in)
// in one second to know how wide the sliding window should be.
let maxEventCountPerSec = 0;
let maxEventBytesPerSec = 0;
const INTERVALS_PER_SEC = Math.floor(
1000 / model.stats.TIME_INTERVAL_SIZE_IN_MS);
let runningEventNumPerSec = 0;
let runningEventBytesPerSec = 0;
let start = 0;
let end = 0;
while (end < eventStats.length) {
// Slide the end marker forward. Moving the end marker from N
// to N+1 adds eventStats[N] to the sliding window.
runningEventNumPerSec += eventStats[end].numEvents;
runningEventBytesPerSec += eventStats[end].totalEventSizeinBytes;
end++;
// Slide the start marker forward so that the time interval covered
// by the window is less than 1 second wide.
while ((eventStats[end - 1].timeInterval -
eventStats[start].timeInterval) >= INTERVALS_PER_SEC) {
runningEventNumPerSec -= eventStats[start].numEvents;
runningEventBytesPerSec -= eventStats[start].totalEventSizeinBytes;
start++;
}
// Update maximum values.
maxEventCountPerSec = Math.max(maxEventCountPerSec,
runningEventNumPerSec);
maxEventBytesPerSec = Math.max(maxEventBytesPerSec,
runningEventBytesPerSec);
}
const stats = model.stats.allTraceEventStats;
const categoryNamesToTotalEventSizes = (
stats.reduce((map, stat) => (
map.set(stat.category,
((map.get(stat.category) || 0) +
stat.totalEventSizeinBytes))), new Map()));
// Determine the category with the highest total event size.
const maxCatNameAndBytes = Array.from(
categoryNamesToTotalEventSizes.entries()).reduce(
(a, b) => ((b[1] >= a[1]) ? b : a));
const maxEventBytesPerCategory = maxCatNameAndBytes[1];
const categoryWithMaxEventBytes = maxCatNameAndBytes[0];
const maxEventCountPerSecValue = new tr.v.Histogram(
'peak_event_rate', tr.b.Unit.byName.count_smallerIsBetter,
COUNT_BOUNDARIES);
maxEventCountPerSecValue.description = 'Max number of events per second';
maxEventCountPerSecValue.customizeSummaryOptions(SUMMARY_OPTIONS);
maxEventCountPerSecValue.addSample(maxEventCountPerSec);
const maxEventBytesPerSecValue = new tr.v.Histogram(
'peak_event_size_rate', tr.b.Unit.byName.sizeInBytes_smallerIsBetter,
BYTE_BOUNDARIES);
maxEventBytesPerSecValue.description = 'Max event size in bytes per second';
maxEventBytesPerSecValue.customizeSummaryOptions(SUMMARY_OPTIONS);
maxEventBytesPerSecValue.addSample(maxEventBytesPerSec);
const totalTraceBytesValue = new tr.v.Histogram('trace_size',
tr.b.Unit.byName.sizeInBytes_smallerIsBetter, BYTE_BOUNDARIES);
totalTraceBytesValue.customizeSummaryOptions(SUMMARY_OPTIONS);
totalTraceBytesValue.addSample(totalTraceBytes);
const biggestCategory = {
name: categoryWithMaxEventBytes,
size_in_bytes: maxEventBytesPerCategory
};
totalTraceBytesValue.diagnostics.set(
'category_with_max_event_size',
new tr.v.d.GenericSet([biggestCategory]));
histograms.addHistogram(totalTraceBytesValue);
maxEventCountPerSecValue.diagnostics.set(
'category_with_max_event_size',
new tr.v.d.GenericSet([biggestCategory]));
histograms.addHistogram(maxEventCountPerSecValue);
maxEventBytesPerSecValue.diagnostics.set(
'category_with_max_event_size',
new tr.v.d.GenericSet([biggestCategory]));
histograms.addHistogram(maxEventBytesPerSecValue);
addMemoryInfraHistograms(histograms, model, categoryNamesToTotalEventSizes);
}
tr.metrics.MetricRegistry.register(tracingMetric);
return {
tracingMetric,
// For testing only:
MEMORY_INFRA_TRACING_CATEGORY,
};
});
</script>