Commit 5cbd0e8c authored by Jasper Chapman-Black's avatar Jasper Chapman-Black Committed by Commit Bot

SuperSize: Filter instead of highlight by flag

This changes both the ndjson-parsing JS library and
the experimental .size-parsing WebAssembly library.

Bug: 1019933
Change-Id: Ic050e4fb2162d29d2a4031f69bd88c43b0bfa6be
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1890021
Commit-Queue: Jasper Chapman-Black <jaspercb@chromium.org>
Reviewed-by: default avatarAndrew Grieve <agrieve@chromium.org>
Reviewed-by: default avatarSamuel Huang <huangs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#711352}
parent 75f658a1
...@@ -43,7 +43,8 @@ void LoadSizeFile(const char* compressed, size_t size) { ...@@ -43,7 +43,8 @@ void LoadSizeFile(const char* compressed, size_t size) {
void BuildTree(bool group_by_component, void BuildTree(bool group_by_component,
const char* include_regex_str, const char* include_regex_str,
const char* exclude_regex_str, const char* exclude_regex_str,
int minimum_size_bytes) { int minimum_size_bytes,
int match_flag) {
std::vector<std::function<bool(const Symbol&)>> filters; std::vector<std::function<bool(const Symbol&)>> filters;
if (minimum_size_bytes > 0) { if (minimum_size_bytes > 0) {
...@@ -52,6 +53,14 @@ void BuildTree(bool group_by_component, ...@@ -52,6 +53,14 @@ void BuildTree(bool group_by_component,
}); });
} }
// It's currently not useful to filter on more than one flag, so |match_flag|
// can be assumed to be a power of two.
if (match_flag) {
std::cout << "Filtering on flag matching " << match_flag << std::endl;
filters.push_back(
[match_flag](const Symbol& sym) { return match_flag & sym.flags; });
}
std::unique_ptr<RE2> include_regex; std::unique_ptr<RE2> include_regex;
if (include_regex_str && *include_regex_str) { if (include_regex_str && *include_regex_str) {
include_regex.reset(new RE2(include_regex_str)); include_regex.reset(new RE2(include_regex_str));
......
...@@ -98,9 +98,8 @@ NodeStats::~NodeStats() = default; ...@@ -98,9 +98,8 @@ NodeStats::~NodeStats() = default;
NodeStats::NodeStats(SectionId sectionId, NodeStats::NodeStats(SectionId sectionId,
int32_t count, int32_t count,
int32_t highlight,
float size) { float size) {
child_stats[sectionId] = {count, highlight, size}; child_stats[sectionId] = {count, size};
} }
void NodeStats::WriteIntoJson(Json::Value* out) const { void NodeStats::WriteIntoJson(Json::Value* out) const {
...@@ -111,7 +110,6 @@ void NodeStats::WriteIntoJson(Json::Value* out) const { ...@@ -111,7 +110,6 @@ void NodeStats::WriteIntoJson(Json::Value* out) const {
(*out)[sectionId] = Json::Value(Json::objectValue); (*out)[sectionId] = Json::Value(Json::objectValue);
(*out)[sectionId]["size"] = stats.size; (*out)[sectionId]["size"] = stats.size;
(*out)[sectionId]["count"] = stats.count; (*out)[sectionId]["count"] = stats.count;
(*out)[sectionId]["highlight"] = stats.highlight;
} }
} }
......
...@@ -85,12 +85,10 @@ struct SizeInfo { ...@@ -85,12 +85,10 @@ struct SizeInfo {
struct Stat { struct Stat {
int32_t count = 0; int32_t count = 0;
int32_t highlight = 0;
float size = 0.0f; float size = 0.0f;
void operator+=(const Stat& other) { void operator+=(const Stat& other) {
count += other.count; count += other.count;
highlight += other.highlight;
size += other.size; size += other.size;
} }
}; };
...@@ -98,7 +96,7 @@ struct Stat { ...@@ -98,7 +96,7 @@ struct Stat {
struct NodeStats { struct NodeStats {
NodeStats(); NodeStats();
~NodeStats(); ~NodeStats();
NodeStats(SectionId section, int32_t count, int32_t highlight, float size); NodeStats(SectionId section, int32_t count, float size);
void WriteIntoJson(Json::Value* out) const; void WriteIntoJson(Json::Value* out) const;
NodeStats& operator+=(const NodeStats& other); NodeStats& operator+=(const NodeStats& other);
SectionId ComputeBiggestSection() const; SectionId ComputeBiggestSection() const;
......
...@@ -124,9 +124,8 @@ void TreeBuilder::AddFileEntry(const std::string_view source_path, ...@@ -124,9 +124,8 @@ void TreeBuilder::AddFileEntry(const std::string_view source_path,
symbol_node->container_type = ContainerType::kSymbol; symbol_node->container_type = ContainerType::kSymbol;
symbol_node->id_path = sym->full_name; symbol_node->id_path = sym->full_name;
symbol_node->size = sym->pss(); symbol_node->size = sym->pss();
symbol_node->node_stats = symbol_node->node_stats = NodeStats(
NodeStats(size_info_->ShortSectionName(sym->section_name), 1, 0, size_info_->ShortSectionName(sym->section_name), 1, symbol_node->size);
symbol_node->size);
AttachToParent(symbol_node, file_node); AttachToParent(symbol_node, file_node);
} }
......
...@@ -323,7 +323,6 @@ const displayInfocard = (() => { ...@@ -323,7 +323,6 @@ const displayInfocard = (() => {
(a, b) => b[1].size - a[1].size (a, b) => b[1].size - a[1].size
); );
const diffMode = state.has('diff_mode'); const diffMode = state.has('diff_mode');
const highlightMode = state.has('highlight');
let totalSize = 0; let totalSize = 0;
for (const [, stats] of statsEntries) { for (const [, stats] of statsEntries) {
totalSize += Math.abs(stats.size); totalSize += Math.abs(stats.size);
...@@ -343,13 +342,6 @@ const displayInfocard = (() => { ...@@ -343,13 +342,6 @@ const displayInfocard = (() => {
const angleEnd = angleStart + arcLength; const angleEnd = angleStart + arcLength;
this._drawSlice(angleStart, angleEnd, color); this._drawSlice(angleStart, angleEnd, color);
if (highlightMode) {
const highlightPercent = stats.highlight / totalSize;
const highlightArcLength = Math.abs(highlightPercent) * 2 * Math.PI;
const highlightEnd = (angleStart + highlightArcLength);
this._drawBorder(angleStart, highlightEnd, '#feefc3', 32);
}
if (diffMode) { if (diffMode) {
const strokeColor = stats.size > 0 ? '#ea4335' : '#34a853'; const strokeColor = stats.size > 0 ? '#ea4335' : '#34a853';
this._drawBorder(angleStart, angleEnd, strokeColor, 16); this._drawBorder(angleStart, angleEnd, strokeColor, 16);
......
...@@ -188,9 +188,6 @@ ul { ...@@ -188,9 +188,6 @@ ul {
.grew { .grew {
color: #ea4335; color: #ea4335;
} }
.highlight {
background: #feefc3;
}
.diff .size-header::after { .diff .size-header::after {
content: " diff"; content: " diff";
......
...@@ -34,8 +34,6 @@ ...@@ -34,8 +34,6 @@
* a certain type. * a certain type.
* @prop {number} size Byte size * @prop {number} size Byte size
* @prop {number} count Number of symbols * @prop {number} count Number of symbols
* @prop {number} highlight Byte size of children that should be
* highlighted.
*/ */
/** /**
......
...@@ -40,22 +40,6 @@ const newTreeElement = (() => { ...@@ -40,22 +40,6 @@ const newTreeElement = (() => {
*/ */
const _uiNodeData = new WeakMap(); const _uiNodeData = new WeakMap();
/**
* Applies highlights to the tree element based on certain flags and state.
* @param {HTMLSpanElement} symbolNameElement Element that displays the
* short name of the tree item.
* @param {TreeNode} node Data about this symbol name element's tree node.
*/
function _highlightSymbolName(symbolNameElement, node) {
const dexMethodStats = node.childStats[_DEX_METHOD_SYMBOL_TYPE];
if (state.has('highlight')) {
const stats = Object.values(node.childStats);
if (stats.some(stat => stat.highlight > 0)) {
symbolNameElement.classList.add('highlight');
}
}
}
/** /**
* Replace the contents of the size element for a tree node. * Replace the contents of the size element for a tree node.
* @param {HTMLElement} sizeElement Element that should display the size * @param {HTMLElement} sizeElement Element that should display the size
...@@ -350,7 +334,6 @@ const newTreeElement = (() => { ...@@ -350,7 +334,6 @@ const newTreeElement = (() => {
_ZERO_WIDTH_SPACE _ZERO_WIDTH_SPACE
); );
symbolName.title = data.idPath; symbolName.title = data.idPath;
_highlightSymbolName(symbolName, data);
// Set the byte size and hover text // Set the byte size and hover text
_setSize(element.querySelector('.size'), data); _setSize(element.querySelector('.size'), data);
......
...@@ -144,7 +144,7 @@ const fetcher = new DataFetcher('data.size'); ...@@ -144,7 +144,7 @@ const fetcher = new DataFetcher('data.size');
let sizeFileLoaded = false; let sizeFileLoaded = false;
async function buildTree( async function buildTree(
groupBy, includeRegex, excludeRegex, minSymbolSize, highlightTest, groupBy, includeRegex, excludeRegex, minSymbolSize, flagToFilter,
methodCountMode, onProgress) { methodCountMode, onProgress) {
if (!sizeFileLoaded) { if (!sizeFileLoaded) {
let sizeBuffer = await fetcher.loadSizeBuffer(); let sizeBuffer = await fetcher.loadSizeBuffer();
...@@ -189,11 +189,13 @@ async function buildTree( ...@@ -189,11 +189,13 @@ async function buildTree(
return message; return message;
} }
let BuildTree = let BuildTree = Module.cwrap(
Module.cwrap('BuildTree', 'void', ['bool', 'string', 'string', 'number']); 'BuildTree', 'void', ['bool', 'string', 'string', 'number', 'number']);
let start_time = Date.now(); let start_time = Date.now();
const groupByComponent = groupBy === 'component'; const groupByComponent = groupBy === 'component';
BuildTree(groupByComponent, includeRegex, excludeRegex, minSymbolSize); BuildTree(
groupByComponent, includeRegex, excludeRegex, minSymbolSize,
flagToFilter);
console.log('Constructed tree in ' + console.log('Constructed tree in ' +
(Date.now() - start_time)/1000.0 + ' seconds'); (Date.now() - start_time)/1000.0 + ' seconds');
...@@ -215,8 +217,7 @@ function parseOptions(options) { ...@@ -215,8 +217,7 @@ function parseOptions(options) {
const url = params.get('load_url'); const url = params.get('load_url');
const groupBy = params.get('group_by') || 'source_path'; const groupBy = params.get('group_by') || 'source_path';
const methodCountMode = params.has('method_count'); const methodCountMode = params.has('method_count');
const filterGeneratedFiles = params.has('generated_filter'); const flagToFilter = _NAMES_TO_FLAGS[params.get('flag_filter')] || 0;
const flagToHighlight = _NAMES_TO_FLAGS[params.get('highlight')];
let minSymbolSize = Number(params.get('min_size')); let minSymbolSize = Number(params.get('min_size'));
if (Number.isNaN(minSymbolSize)) { if (Number.isNaN(minSymbolSize)) {
...@@ -226,15 +227,12 @@ function parseOptions(options) { ...@@ -226,15 +227,12 @@ function parseOptions(options) {
const includeRegex = params.get('include'); const includeRegex = params.get('include');
const excludeRegex = params.get('exclude'); const excludeRegex = params.get('exclude');
function highlightTest(symbolNode) {
return false;
}
return { return {
groupBy, groupBy,
includeRegex, includeRegex,
excludeRegex, excludeRegex,
minSymbolSize, minSymbolSize,
highlightTest, flagToFilter,
url, url,
methodCountMode methodCountMode
}; };
...@@ -248,7 +246,7 @@ const actions = { ...@@ -248,7 +246,7 @@ const actions = {
includeRegex, includeRegex,
excludeRegex, excludeRegex,
minSymbolSize, minSymbolSize,
highlightTest, flagToFilter,
url, url,
methodCountMode methodCountMode
} = parseOptions(options); } = parseOptions(options);
...@@ -262,7 +260,7 @@ const actions = { ...@@ -262,7 +260,7 @@ const actions = {
} }
return buildTree( return buildTree(
groupBy, includeRegex, excludeRegex, minSymbolSize, highlightTest, groupBy, includeRegex, excludeRegex, minSymbolSize, flagToFilter,
methodCountMode, progress => { methodCountMode, progress => {
// @ts-ignore // @ts-ignore
self.postMessage(progress); self.postMessage(progress);
......
...@@ -40,7 +40,6 @@ const _PATH_SEP = '/'; ...@@ -40,7 +40,6 @@ const _PATH_SEP = '/';
const _NAMES_TO_FLAGS = Object.freeze({ const _NAMES_TO_FLAGS = Object.freeze({
hot: _FLAGS.HOT, hot: _FLAGS.HOT,
generated: _FLAGS.GENERATED_SOURCE, generated: _FLAGS.GENERATED_SOURCE,
coverage: _FLAGS.COVERAGE,
uncompressed: _FLAGS.UNCOMPRESSED, uncompressed: _FLAGS.UNCOMPRESSED,
}); });
...@@ -137,8 +136,6 @@ class TreeBuilder { ...@@ -137,8 +136,6 @@ class TreeBuilder {
* @param {(symbolNode: TreeNode) => boolean} options.filterTest Called to see * @param {(symbolNode: TreeNode) => boolean} options.filterTest Called to see
* if a symbol should be included. If a symbol fails the test, it will not be * if a symbol should be included. If a symbol fails the test, it will not be
* attached to the tree. * attached to the tree.
* @param {(symbolNode: TreeNode) => boolean} options.highlightTest Called to
* see if a symbol should be highlighted.
* @param {boolean} options.methodCountMode Whether we're in "method count" * @param {boolean} options.methodCountMode Whether we're in "method count"
* mode. * mode.
* @param {string} options.sep Path seperator used to find parent names. * @param {string} options.sep Path seperator used to find parent names.
...@@ -147,7 +144,6 @@ class TreeBuilder { ...@@ -147,7 +144,6 @@ class TreeBuilder {
constructor(options) { constructor(options) {
this._getPath = options.getPath; this._getPath = options.getPath;
this._filterTest = options.filterTest; this._filterTest = options.filterTest;
this._highlightTest = options.highlightTest;
this._methodCountMode = options.methodCountMode; this._methodCountMode = options.methodCountMode;
this._sep = options.sep || _PATH_SEP; this._sep = options.sep || _PATH_SEP;
this._meta = options.meta; this._meta = options.meta;
...@@ -198,13 +194,12 @@ class TreeBuilder { ...@@ -198,13 +194,12 @@ class TreeBuilder {
for (const [type, stat] of additionalStats) { for (const [type, stat] of additionalStats) {
let parentStat = parent.childStats[type]; let parentStat = parent.childStats[type];
if (parentStat == null) { if (parentStat == null) {
parentStat = {size: 0, count: 0, highlight: 0}; parentStat = {size: 0, count: 0};
parent.childStats[type] = parentStat; parent.childStats[type] = parentStat;
} }
parentStat.size += stat.size; parentStat.size += stat.size;
parentStat.count += stat.count; parentStat.count += stat.count;
parentStat.highlight += stat.highlight;
const absSize = Math.abs(parentStat.size); const absSize = Math.abs(parentStat.size);
if (absSize > lastBiggestSize) { if (absSize > lastBiggestSize) {
...@@ -439,14 +434,10 @@ class TreeBuilder { ...@@ -439,14 +434,10 @@ class TreeBuilder {
[type]: { [type]: {
size, size,
count, count,
highlight: 0,
}, },
}, },
}); });
if (this._highlightTest(symbolNode)) {
symbolNode.childStats[type].highlight = size;
}
if (this._filterTest(symbolNode)) { if (this._filterTest(symbolNode)) {
this._attachToParent(symbolNode, fileNode); this._attachToParent(symbolNode, fileNode);
} }
...@@ -651,8 +642,7 @@ function parseOptions(options) { ...@@ -651,8 +642,7 @@ function parseOptions(options) {
const url = params.get('load_url'); const url = params.get('load_url');
const groupBy = params.get('group_by') || 'source_path'; const groupBy = params.get('group_by') || 'source_path';
const methodCountMode = params.has('method_count'); const methodCountMode = params.has('method_count');
const filterGeneratedFiles = params.has('generated_filter'); const flagToFilter = _NAMES_TO_FLAGS[params.get('flag_filter')];
const flagToHighlight = _NAMES_TO_FLAGS[params.get('highlight')];
let minSymbolSize = Number(params.get('min_size')); let minSymbolSize = Number(params.get('min_size'));
if (Number.isNaN(minSymbolSize)) { if (Number.isNaN(minSymbolSize)) {
...@@ -690,9 +680,9 @@ function parseOptions(options) { ...@@ -690,9 +680,9 @@ function parseOptions(options) {
filters.push(s => typeFilter.has(s.type)); filters.push(s => typeFilter.has(s.type));
} }
// Only show generated files // Only show symbols with attached flag
if (filterGeneratedFiles) { if (flagToFilter) {
filters.push(s => hasFlag(_FLAGS.GENERATED_SOURCE, s)); filters.push(s => hasFlag(flagToFilter, s));
} }
// Search symbol names using regex // Search symbol names using regex
...@@ -721,15 +711,7 @@ function parseOptions(options) { ...@@ -721,15 +711,7 @@ function parseOptions(options) {
return filters.every(fn => fn(symbolNode)); return filters.every(fn => fn(symbolNode));
} }
/** @type {(symbolNode: TreeNode) => boolean} */ return {groupBy, filterTest, url, methodCountMode};
let highlightTest;
if (flagToHighlight) {
highlightTest = symbolNode => hasFlag(flagToHighlight, symbolNode);
} else {
highlightTest = () => false;
}
return {groupBy, filterTest, highlightTest, url, methodCountMode};
} }
/** @type {TreeBuilder | null} */ /** @type {TreeBuilder | null} */
...@@ -741,14 +723,11 @@ const fetcher = new DataFetcher('data.ndjson'); ...@@ -741,14 +723,11 @@ const fetcher = new DataFetcher('data.ndjson');
* @param {string} groupBy Sets how the tree is grouped. * @param {string} groupBy Sets how the tree is grouped.
* @param {(symbolNode: TreeNode) => boolean} filterTest Filter function that * @param {(symbolNode: TreeNode) => boolean} filterTest Filter function that
* each symbol is tested against * each symbol is tested against
* @param {(symbolNode: TreeNode) => boolean} highlightTest Filter function that
* each symbol's flags are tested against
* @param {boolean} methodCountMode * @param {boolean} methodCountMode
* @param {(msg: TreeProgress) => void} onProgress * @param {(msg: TreeProgress) => void} onProgress
* @returns {Promise<TreeProgress>} * @returns {Promise<TreeProgress>}
*/ */
async function buildTree( async function buildTree(groupBy, filterTest, methodCountMode, onProgress) {
groupBy, filterTest, highlightTest, methodCountMode, onProgress) {
/** @type {Meta | null} Object from the first line of the data file */ /** @type {Meta | null} Object from the first line of the data file */
let meta = null; let meta = null;
...@@ -812,7 +791,6 @@ async function buildTree( ...@@ -812,7 +791,6 @@ async function buildTree(
builder = new TreeBuilder({ builder = new TreeBuilder({
getPath: getPathMap[groupBy], getPath: getPathMap[groupBy],
filterTest, filterTest,
highlightTest,
methodCountMode, methodCountMode,
sep: groupBy === 'component' ? '>' : _PATH_SEP, sep: groupBy === 'component' ? '>' : _PATH_SEP,
meta, meta,
...@@ -847,8 +825,7 @@ async function buildTree( ...@@ -847,8 +825,7 @@ async function buildTree(
const actions = { const actions = {
/** @param {{input:string|null,options:string}} param0 */ /** @param {{input:string|null,options:string}} param0 */
load({input, options}) { load({input, options}) {
const {groupBy, filterTest, highlightTest, url, methodCountMode} = const {groupBy, filterTest, url, methodCountMode} = parseOptions(options);
parseOptions(options);
if (input === 'from-url://' && url) { if (input === 'from-url://' && url) {
// Display the data from the `load_url` query parameter // Display the data from the `load_url` query parameter
console.info('Displaying data from', url); console.info('Displaying data from', url);
...@@ -859,7 +836,7 @@ const actions = { ...@@ -859,7 +836,7 @@ const actions = {
} }
return buildTree( return buildTree(
groupBy, filterTest, highlightTest, methodCountMode, progress => { groupBy, filterTest, methodCountMode, progress => {
// @ts-ignore // @ts-ignore
self.postMessage(progress); self.postMessage(progress);
}); });
......
...@@ -207,34 +207,25 @@ if ('serviceWorker' in navigator) { ...@@ -207,34 +207,25 @@ if ('serviceWorker' in navigator) {
<button type="button" class="text-button" id="type-none">Select none</button> <button type="button" class="text-button" id="type-none">Select none</button>
</fieldset> </fieldset>
<fieldset id="highlight-container"> <fieldset id="flag-container">
<legend class="subhead">Highlight symbols</legend> <legend class="subhead">Filter symbols</legend>
<div class="radio-wrapper"> <div class="radio-wrapper">
<input type="radio" id="clearhighlight" name="highlight" value="clear" checked> <input type="radio" id="clearflag" name="flag_filter" value="clear" checked>
<label class="radio-label" for="clearhighlight">None</label> <label class="radio-label" for="clearflag">All</label>
</div> </div>
<div class="radio-wrapper"> <div class="radio-wrapper">
<input type="radio" id="hothighlight" name="highlight" value="hot"> <input type="radio" id="hotflag" name="flag_filter" value="hot">
<label class="radio-label" for="hothighlight">Hot code</label> <label class="radio-label" for="hotflag">Hot code</label>
</div> </div>
<div class="radio-wrapper"> <div class="radio-wrapper">
<input type="radio" id="generatedhighlight" name="highlight" value="generated"> <input type="radio" id="generatedflag" name="flag_filter" value="generated">
<label class="radio-label" for="generatedhighlight">Generated files</label> <label class="radio-label" for="generatedflag">Generated files</label>
</div> </div>
<div class="radio-wrapper"> <div class="radio-wrapper">
<input type="radio" id="coveragehightlight" name="highlight" value="coverage"> <input type="radio" id="uncompressedflag" name="flag_filter" value="uncompressed">
<label class="radio-label" for="coveragehightlight">Code coverage (not implemented)</label> <label class="radio-label" for="uncompressedflag">Uncompressed .pak files</label>
</div>
<div class="radio-wrapper">
<input type="radio" id="uncompressedhighlight" name="highlight" value="uncompressed">
<label class="radio-label" for="uncompressedhighlight">Uncompressed .pak files</label>
</div> </div>
</fieldset> </fieldset>
<p class="checkbox-wrapper">
<input type="checkbox" id="generatedfilter" name="generated_filter" value="on">
<label class="checkbox-label" for="generatedfilter">Show only generated files</label>
</p>
</form> </form>
<div class="symbols"> <div class="symbols">
<!-- Icons for symbols are stored here and cloned. --> <!-- Icons for symbols are stored here and cloned. -->
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment