blob: 0282cf2e0f934153e21168257b3bd61754ec8bd7 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2017 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/math/math.html">
<link rel="import" href="/tracing/core/auditor.html">
<link rel="import" href="/tracing/model/resource_usage_series.html">
<script>
'use strict';
tr.exportTo('tr.e.audits', function() {
/**
* Auditor that analyzes the model and, if possible, adds data to it
* showing CPU usage.
*/
class CpuUsageAuditor extends tr.c.Auditor {
constructor(model) {
super();
this.model_ = model;
}
runAnnotate() {
this.model_.device.cpuUsageSeries = this.computeCpuUsageSeries_(
this.model_.bounds.min, this.model_.bounds.max,
this.computeCpuUsage_());
}
/**
* Compute the bin size given the start and end times of the trace.
*/
computeBinSize_(start, end) {
var MIN_BIN_SIZE_MS = 1.0;
var MAX_NUM_BINS = 100000;
var interval = end - start;
var binSize = MIN_BIN_SIZE_MS;
while (binSize * MAX_NUM_BINS < interval) binSize *= 2;
return binSize;
}
/**
* Returns a CPU usage series from a given set of CPU usage slices.
* Slices are in the format created by getCpuUsage below.
*/
computeCpuUsageSeries_(start, end, usageRecords) {
var series = new tr.model.ResourceUsageSeries();
if (start === undefined) return series;
var binSize = this.computeBinSize_(start, end);
var numBins = Math.ceil((end - start) / binSize);
var arr = new Array(numBins).fill(0);
for (var record of usageRecords) {
var firstIndex = Math.ceil((record.start - start) / binSize);
var lastIndex = Math.floor((record.end - start) / binSize);
for (var i = firstIndex; i <= lastIndex; i++) arr[i] += record.usage;
}
for (var i = 0; i < numBins; i++) {
series.addUsageSample(start + (i * binSize), arr[i]);
}
return series;
}
/**
* Returns a list of CPU usage slices based on tracing data. Thus, this
* effectively counts only processes that are traced (will not count
* e.g. background processes)
*/
computeCpuUsage_() {
var model = this.model_;
var result = [];
for (var pid in model.processes) {
for (var e of model.processes[pid].getDescendantEvents()) {
if (!(e instanceof tr.model.ThreadSlice) || e.duration === 0 ||
e.cpuDuration === undefined) {
continue;
}
// This slice contains the most fine-grained CPU usage information
// for the area of the trace that it covers but that is not covered
// by its subslices.
// The math goes this way:
// s.selfTime : duration of slice s not spent in its subslices.
// s.cpuSelfTime : cpuDuration over slice s but not its subslices.
//
// We're looking for
// s.cpuSelfTimeRatio: average CPU usage over the area covered by
// s but not any of its subslices.
// = s.cpuSelfTime / s.selfTime
if (e.selfTime === 0 || e.selfTime === undefined ||
e.cpuSelfTime === undefined) {
continue;
}
var usage = tr.b.math.clamp(e.cpuSelfTime / e.selfTime, 0, 1);
// Go through the area covered by this slice but not its subslices
// and add the cpuSelfTimeRatio contribution over this area.
var lastTime = e.start;
for (var subslice of e.subSlices) {
result.push({usage, start: lastTime, end: subslice.start});
lastTime = subslice.end;
}
result.push({usage, start: lastTime, end: e.end});
}
}
return result;
}
}
tr.c.Auditor.register(CpuUsageAuditor);
return {
CpuUsageAuditor: CpuUsageAuditor
};
});
</script>