| <!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/value/histogram_set.html"> |
| |
| <script> |
| 'use strict'; |
| tr.exportTo('tr.v', function() { |
| /* |
| * See also HistogramSet.groupHistogramsRecursively(). |
| * See also tr.v.ui.HistogramSetTableRow. |
| */ |
| class HistogramSetHierarchy { |
| /** |
| * @param {string} name |
| */ |
| constructor(name) { |
| this.name = name; |
| this.description = ''; |
| this.depth = 0; |
| this.subRows = []; |
| this.columns = new Map(); |
| } |
| |
| * walk() { |
| yield this; |
| for (const row of this.subRows) yield* row.walk(); |
| } |
| |
| static* walkAll(rootRows) { |
| for (const rootRow of rootRows) yield* rootRow.walk(); |
| } |
| |
| /** |
| * Build table rows recursively from grouped Histograms. |
| * |
| * @param {!(HistogramArray|HistogramArrayMap)} |
| * @returns {!Array.<!HistogramSetHierarchy>} |
| */ |
| static build(histogramArrayMap) { |
| const rootRows = []; |
| HistogramSetHierarchy.buildInternal_(histogramArrayMap, [], rootRows); |
| |
| const histograms = new tr.v.HistogramSet(); |
| |
| for (const row of HistogramSetHierarchy.walkAll(rootRows)) { |
| for (const hist of row.columns.values()) { |
| if (!(hist instanceof tr.v.Histogram)) continue; |
| histograms.addHistogram(hist); |
| } |
| } |
| |
| histograms.deduplicateDiagnostics(); |
| |
| for (const row of HistogramSetHierarchy.walkAll(rootRows)) { |
| row.maybeRebin_(); |
| } |
| |
| return rootRows; |
| } |
| |
| maybeRebin_() { |
| // if all of |this| row's columns are single-bin, then re-bin all of them. |
| const dataRange = new tr.b.math.Range(); |
| for (const hist of this.columns.values()) { |
| if (!(hist instanceof tr.v.Histogram)) continue; |
| if (hist.allBins.length > 1) return; // don't re-bin |
| if (hist.numValues === 0) continue; // ignore hist |
| dataRange.addValue(hist.min); |
| dataRange.addValue(hist.max); |
| } |
| |
| dataRange.addValue(tr.b.math.lesserWholeNumber(dataRange.min)); |
| dataRange.addValue(tr.b.math.greaterWholeNumber(dataRange.max)); |
| |
| if (dataRange.min === dataRange.max) return; // don't rebin |
| |
| const boundaries = tr.v.HistogramBinBoundaries.createLinear( |
| dataRange.min, dataRange.max, tr.v.DEFAULT_REBINNED_COUNT); |
| |
| for (const [name, hist] of this.columns) { |
| if (!(hist instanceof tr.v.Histogram)) continue; |
| this.columns.set(name, hist.rebin(boundaries)); |
| } |
| } |
| |
| static mergeHistogramDownHierarchy_(histogram, hierarchy, columnName) { |
| // Track the path down the grouping tree to each Histogram, |
| // but only start tracking the path at the grouping level that |
| // corresponds to the Histogram NAME Grouping. |
| for (const row of hierarchy) { |
| if (!row.description) { |
| row.description = histogram.description; |
| } |
| |
| const existing = row.columns.get(columnName); |
| |
| if (existing === undefined) { |
| row.columns.set(columnName, histogram.clone()); |
| continue; |
| } |
| |
| if (existing instanceof tr.v.HistogramSet) { |
| // There have already been unmergeable histograms. |
| existing.addHistogram(histogram); |
| continue; |
| } |
| |
| if (!existing.canAddHistogram(histogram)) { |
| // TODO(benjhayden) Remove? |
| const unmergeableHistograms = new tr.v.HistogramSet([histogram]); |
| row.columns.set(columnName, unmergeableHistograms); |
| continue; |
| } |
| |
| existing.addHistogram(histogram); |
| } |
| } |
| |
| static buildInternal_( |
| histogramArrayMap, hierarchy, rootRows) { |
| for (const [name, histograms] of histogramArrayMap) { |
| if (histograms instanceof Array) { |
| // This recursion base case corresponds to the recursion base case of |
| // groupHistogramsRecursively(). The last groupingCallback is always |
| // getDisplayLabel, which defines the columns of the table and is |
| // unskippable. |
| for (const histogram of histograms) { |
| HistogramSetHierarchy.mergeHistogramDownHierarchy_( |
| histogram, hierarchy, name); |
| } |
| } else if (histograms instanceof Map) { |
| // |histograms| is actually a nested histogramArrayMap. |
| const row = new HistogramSetHierarchy(name); |
| row.depth = hierarchy.length; |
| hierarchy.push(row); |
| HistogramSetHierarchy.buildInternal_(histograms, hierarchy, rootRows); |
| hierarchy.pop(); |
| |
| if (hierarchy.length === 0) { |
| rootRows.push(row); |
| } else { |
| const parentRow = hierarchy[hierarchy.length - 1]; |
| parentRow.subRows.push(row); |
| } |
| } |
| } |
| } |
| } |
| |
| return {HistogramSetHierarchy}; |
| }); |
| </script> |