Index: tracing/tracing/metrics/system_health/cpu_time_metric.html |
diff --git a/tracing/tracing/metrics/system_health/cpu_time_metric.html b/tracing/tracing/metrics/system_health/cpu_time_metric.html |
index abfc457fb58f8e6a795688a8e3ffb317100d9bcd..ba0487938b9ead75959df8540d71b63c78622c45 100644 |
--- a/tracing/tracing/metrics/system_health/cpu_time_metric.html |
+++ b/tracing/tracing/metrics/system_health/cpu_time_metric.html |
@@ -9,21 +9,124 @@ found in the LICENSE file. |
<link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> |
<link rel="import" href="/tracing/model/helpers/chrome_renderer_helper.html"> |
<link rel="import" href="/tracing/value/histogram.html"> |
+<link rel="import" href="/tracing/extras/chrome/cpu_time.html"> |
+ |
+<!-- Experimental --> |
+<link rel="import" href="/tracing/base/multi_dimensional_view.html"> |
<script> |
+// TODO: (Do this before landing) Cleanup imports once you're done |
'use strict'; |
tr.exportTo('tr.metrics.sh', function() { |
- // Use a lower bound of 0.01 for the metric boundaries (when no CPU time |
- // is consumed) and an upper bound of 50 (fifty cores are all active |
- // for the entire time). We can't use zero exactly for the lower bound with an |
- // exponential histogram. |
- const CPU_TIME_PERCENTAGE_BOUNDARIES = |
- tr.v.HistogramBinBoundaries.createExponential(0.01, 50, 200); |
+ const CPU_TIME_UNIT = tr.b.Unit.byName.normalizedPercentage_smallerIsBetter; |
+ const CPU_TIME_TOTAL_UNIT = tr.b.Unit.byName.timeDurationInMs_smallerIsBetter; |
+ |
+ const cpuTime = tr.e.chrome.cpuTime; |
+ |
+ /** |
+ * Translates a multidimensional tree view path to process type, thread type, |
+ * rail stage, and initiator type. |
+ */ |
+ function translatePath_(path) { |
+ return { |
+ processType: path[0][0], |
+ threadType: path[1][0], |
+ railStage: path[2][0], |
+ initiatorType: path[2][1] |
+ }; |
+ } |
+ |
+ function createNewHistogramAndAddSample_( |
+ histograms, histogramName, value, unit) { |
+ const histogram = new tr.v.Histogram(histogramName, unit); |
+ histogram.customizeSummaryOptions({ |
+ avg: false, |
+ count: false, |
+ max: false, |
+ min: false, |
+ std: false, |
+ sum: true |
+ }); |
+ histogram.addSample(value); |
+ histograms.addHistogram(histogram); |
+ } |
+ |
+ function getCanonicalHistogramName_( |
+ histogramPrefix, processType, threadType, railStage, initiatorType) { |
+ return `${histogramPrefix}:${processType}:${threadType}:` + |
+ `${railStage}:${initiatorType}`; |
+ } |
+ |
+ function maybeAddDataToHistograms_(histograms, path, node) { |
+ const translatedPath = translatePath_(path); |
+ const processType = translatedPath.processType || 'all_processes'; |
+ const threadType = translatedPath.threadType || 'all_threads'; |
+ |
+ // We need some rail stage and some initiator type to process a node |
+ // All rail stages and all initiator types are handled by the special |
+ // 'all_stages' and 'all_initiators' nodes respectively. |
+ if (!translatedPath.railStage || !translatedPath.initiatorType) return; |
+ const {railStage, initiatorType} = translatedPath; |
+ |
+ const cpuUsageValue = node.values[0].total; |
+ const cpuTotalValue = node.values[1].total; |
+ |
+ createNewHistogramAndAddSample_( |
+ histograms, |
+ getCanonicalHistogramName_( |
+ 'cpuUsage', processType, threadType, railStage, initiatorType), |
+ cpuUsageValue, CPU_TIME_UNIT); |
+ |
+ createNewHistogramAndAddSample_( |
+ histograms, |
+ getCanonicalHistogramName_( |
+ 'cpuTotal', processType, threadType, railStage, initiatorType), |
+ cpuTotalValue, CPU_TIME_TOTAL_UNIT); |
+ } |
/** |
- * This metric measures total CPU time for Chrome processes, per second of |
- * clock time. |
+ * Returns a string representing which cpu time metric this node represents. |
+ * This is not quite the histogram name, but a unique key for the node to |
+ * keep track of whether it has been visited. |
+ */ |
+ function getCanonicalTitleFromPath_(path) { |
+ const translatedPath = translatePath_(path); |
+ const titleComponents = |
+ ['processType', 'threadType', 'railStage', 'initiatorType']; |
+ return titleComponents.map(key => translatedPath[key]).join(':'); |
+ } |
+ |
+ function clonePath_(previousPath) { |
+ return previousPath.map(subPath => subPath.map(x => x)); |
+ } |
+ |
+ /** |
+ * Traverses all the path of a multidimensional tree view starting from |
+ * |node|, creates necessary histograms and adds samples to them. |
+ * |
+ * TODO: DO BEFORE LANDING: Add param descriptions to this jsdoc. |
+ */ |
+ function reportDataFromTree_(histograms, node, currentPath, visitedSet) { |
+ const canonicalTitle = getCanonicalTitleFromPath_(currentPath); |
+ if (visitedSet.has(canonicalTitle)) return; |
+ visitedSet.add(canonicalTitle); |
+ maybeAddDataToHistograms_(histograms, currentPath, node); |
+ |
+ for (let dimension = 0; dimension < node.children.length; dimension++) { |
+ const children = node.children[dimension]; |
+ for (const [name, node] of children) { |
+ const newPath = clonePath_(currentPath); |
+ newPath[dimension].push(name); |
+ reportDataFromTree_(histograms, node, newPath, visitedSet); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * This metric measures total cpuTime utilization per second of wall clock |
+ * time. |
+ * |
* This metric requires only the 'toplevel' tracing category. |
* |
* @param {!tr.v.HistogramSet} histograms |
@@ -31,79 +134,16 @@ tr.exportTo('tr.metrics.sh', function() { |
* @param {!Object=} opt_options |
*/ |
function cpuTimeMetric(histograms, model, opt_options) { |
- let rangeOfInterest = model.bounds; |
- |
+ var rangeOfInterest = model.bounds; |
if (opt_options && opt_options.rangeOfInterest) { |
rangeOfInterest = opt_options.rangeOfInterest; |
- } else { |
- // If no range of interest is provided, limit the relevant range to |
- // Chrome processes. This prevents us from normalizing against non-Chrome |
- // related slices in the trace. |
- const chromeHelper = model.getOrCreateHelper( |
- tr.model.helpers.ChromeModelHelper); |
- if (chromeHelper) { |
- const chromeBounds = chromeHelper.chromeBounds; |
- if (chromeBounds) { |
- rangeOfInterest = chromeBounds; |
- } |
- } |
} |
- let allProcessCpuTime = 0; |
- |
- for (const pid in model.processes) { |
- const process = model.processes[pid]; |
- if (tr.model.helpers.ChromeRendererHelper.isTracingProcess(process)) { |
- continue; |
- } |
- |
- let processCpuTime = 0; |
- for (const tid in process.threads) { |
- const thread = process.threads[tid]; |
- let threadCpuTime = 0; |
- thread.sliceGroup.topLevelSlices.forEach(function(slice) { |
- if (slice.duration === 0) return; |
- if (!slice.cpuDuration) return; |
- const sliceRange = tr.b.math.Range.fromExplicitRange( |
- slice.start, slice.end); |
- const intersection = rangeOfInterest.findIntersection(sliceRange); |
- const fractionOfSliceInsideRangeOfInterest = |
- intersection.duration / slice.duration; |
- |
- // We assume that if a slice doesn't lie entirely inside the range of |
- // interest, then the CPU time is evenly distributed inside of the |
- // slice. |
- threadCpuTime += |
- slice.cpuDuration * fractionOfSliceInsideRangeOfInterest; |
- }); |
- processCpuTime += threadCpuTime; |
- } |
- allProcessCpuTime += processCpuTime; |
- } |
- |
- // Normalize cpu time by clock time. |
- let normalizedAllProcessCpuTime = 0; |
- if (rangeOfInterest.duration > 0) { |
- normalizedAllProcessCpuTime = |
- allProcessCpuTime / rangeOfInterest.duration; |
- } |
- |
- const unit = tr.b.Unit.byName.normalizedPercentage_smallerIsBetter; |
- const cpuTimeHist = new tr.v.Histogram( |
- 'cpu_time_percentage', unit, CPU_TIME_PERCENTAGE_BOUNDARIES); |
- cpuTimeHist.description = |
- 'Percent CPU utilization, normalized against a single core. Can be ' + |
- 'greater than 100% if machine has multiple cores.'; |
- cpuTimeHist.customizeSummaryOptions({ |
- avg: true, |
- count: false, |
- max: false, |
- min: false, |
- std: false, |
- sum: false |
- }); |
- cpuTimeHist.addSample(normalizedAllProcessCpuTime); |
- histograms.addHistogram(cpuTimeHist); |
+ const treeRoot = |
+ cpuTime.constructMultiDimensionalView(model, rangeOfInterest); |
+ const rootPath = [[], [], []]; |
+ const visitedSet = new Set(); |
+ reportDataFromTree_(histograms, treeRoot, rootPath, visitedSet); |
} |
tr.metrics.MetricRegistry.register(cpuTimeMetric, { |