Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

viewer: use treemap for bundle visualization #10312

Closed
wants to merge 104 commits into from
Closed
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
4a62a54
initial
connorjclark Dec 9, 2019
7886f5e
do not need _ yet
connorjclark Dec 9, 2019
65cc13c
test for unused bundle
connorjclark Dec 10, 2019
2ed3b7b
lint
connorjclark Dec 10, 2019
652059e
fix null mapping bug
connorjclark Dec 10, 2019
b48a400
fix counting
connorjclark Dec 11, 2019
7bdf8b4
fix smoke test
connorjclark Dec 11, 2019
b6a4a08
comment
connorjclark Dec 11, 2019
b840b10
comment
connorjclark Dec 11, 2019
c3fd09e
lint
connorjclark Dec 11, 2019
36c7bee
better bundle size filter
connorjclark Dec 11, 2019
c5cfac2
Squashed commit of the following:
connorjclark Dec 12, 2019
912f9f1
todo
connorjclark Dec 12, 2019
ea03907
update bundle.js
connorjclark Dec 12, 2019
a862683
count new lines
connorjclark Dec 12, 2019
4ff3804
dont double count
connorjclark Dec 13, 2019
fa429e0
fix test
connorjclark Dec 13, 2019
74b642d
fix test
connorjclark Dec 13, 2019
1c9d984
destructure
connorjclark Dec 13, 2019
0d440b2
lastColumnOfMapping
connorjclark Dec 13, 2019
60ac62f
Merge remote-tracking branch 'origin/master' into unused-js-bundles
connorjclark Dec 17, 2019
c27aa4a
Merge remote-tracking branch 'origin/master' into unused-js-bundles
connorjclark Jan 21, 2020
e2d1035
Merge remote-tracking branch 'origin/master' into unused-js-bundles
connorjclark Jan 30, 2020
e317115
fix
connorjclark Jan 31, 2020
d68a555
fix one test
connorjclark Jan 31, 2020
3d9c5f4
fix default config test
connorjclark Jan 31, 2020
1bd219d
fix
connorjclark Jan 31, 2020
4b8fe87
update
connorjclark Feb 3, 2020
39a4938
Merge remote-tracking branch 'origin/master' into unused-js-bundles
connorjclark Feb 3, 2020
d338faf
rm old code
connorjclark Feb 5, 2020
315aacb
destructure
connorjclark Feb 7, 2020
c782a59
bundle viz
connorjclark Feb 9, 2020
225ff07
core: add temporary config for source map features
connorjclark Feb 9, 2020
d778ba9
Merge branch 'source-maps-config' into viewer-treemap
connorjclark Feb 9, 2020
7b8faf0
webtreemap-cdt
connorjclark Feb 9, 2020
08bfc38
rm source-maps from default
connorjclark Feb 9, 2020
79cf90b
Merge branch 'unused-js-bundles' into viewer-treemap
connorjclark Feb 11, 2020
167dee1
Merge remote-tracking branch 'origin/master' into unused-js-bundles
connorjclark Feb 13, 2020
dd23583
Merge remote-tracking branch 'origin/master' into viewer-treemap
connorjclark Feb 13, 2020
9b2e89f
config
connorjclark Feb 13, 2020
fe84391
json-size
connorjclark Feb 16, 2020
839a504
Merge remote-tracking branch 'origin/unused-js-bundles' into viewer-t…
connorjclark Feb 16, 2020
893bd20
move unused js stuff to computed artifact, consume in bundle viz data
connorjclark Feb 17, 2020
2c006a1
show all bundles; select which is shown
connorjclark Feb 19, 2020
2a53011
Merge remote-tracking branch 'origin/master' into viewer-treemap
connorjclark Feb 26, 2020
1cd31fe
update
connorjclark Feb 27, 2020
3537e45
test
connorjclark Feb 27, 2020
a938c17
blah
connorjclark Feb 27, 2020
d1aaa49
better viewer
connorjclark Feb 27, 2020
1b0bcca
colors
connorjclark Feb 27, 2020
af2a13a
treemap data
connorjclark Feb 28, 2020
7fe7a18
on hover
connorjclark Feb 28, 2020
104be3e
click recolor
connorjclark Feb 28, 2020
38c0da6
restructure treemap data
connorjclark Feb 28, 2020
d9e870e
wip
connorjclark Feb 29, 2020
438d813
add non bundles to treemap data
connorjclark Feb 29, 2020
a846cdb
modes. use cdt colors
connorjclark Feb 29, 2020
1eea14d
format bytes
connorjclark Feb 29, 2020
b206059
resource sizes
connorjclark Feb 29, 2020
2cb30bb
tags
connorjclark Feb 29, 2020
14c2d34
partition by, color by, bootup time
connorjclark Mar 2, 2020
b77f973
show label in root title
connorjclark Mar 3, 2020
f61c656
init
connorjclark Mar 3, 2020
ab3008b
lighter colors. stable hasher
connorjclark Mar 10, 2020
72c11c2
Merge branch 'viewer-treemap' of github.com:GoogleChrome/lighthouse i…
connorjclark Mar 24, 2020
ad44b81
grid
connorjclark Mar 25, 2020
7019b3c
view modes
connorjclark Mar 25, 2020
170cd1e
Merge remote-tracking branch 'origin/master' into viewer-treemap
connorjclark Mar 25, 2020
90703e3
duplicate modules
connorjclark Mar 25, 2020
c65ad26
click on view
connorjclark Mar 26, 2020
c86b592
Merge branch 'viewer-treemap' of github.com:GoogleChrome/lighthouse i…
connorjclark Mar 26, 2020
059b3f1
split
connorjclark Mar 26, 2020
3b2d093
tabulator
connorjclark Mar 27, 2020
f494736
dfs
connorjclark Mar 27, 2020
af10dec
better nodes. progress bar
connorjclark Mar 31, 2020
35a2828
test
connorjclark Mar 31, 2020
bb6d7ee
report: render sub-rows in their own tr
connorjclark Apr 7, 2020
3bf1bab
styles
connorjclark Apr 7, 2020
e91074b
Merge remote-tracking branch 'origin/master' into subrow-layout
connorjclark Apr 13, 2020
1bd9274
Apply suggestions from code review
connorjclark Apr 21, 2020
7e69d63
_renderTableRow
connorjclark Apr 21, 2020
ff6c158
delete code
connorjclark Apr 21, 2020
b3b0384
Merge remote-tracking branch 'origin/master' into subrow-layout
connorjclark Apr 21, 2020
a7eb868
zebra
connorjclark Apr 21, 2020
687b486
fontsize
connorjclark Apr 21, 2020
fe2306a
revert
connorjclark Apr 21, 2020
27d599c
Merge remote-tracking branch 'origin/master' into viewer-treemap
connorjclark Apr 30, 2020
d16ecae
remove color by
connorjclark Apr 30, 2020
f913a56
active view
connorjclark Apr 30, 2020
991c8ab
fix
connorjclark Apr 30, 2020
99be367
format bytes
connorjclark Apr 30, 2020
ed2d72f
fix views
connorjclark Apr 30, 2020
54192da
comment
connorjclark Apr 30, 2020
c26c223
resource summary
connorjclark May 4, 2020
2b9ca3e
organize treemap/index.html
connorjclark May 4, 2020
f104231
execution time
connorjclark May 4, 2020
41c906f
fix html
connorjclark May 4, 2020
454b3b6
use settings multiplier for execution time
connorjclark May 6, 2020
92f3adf
aggregate inline
connorjclark May 6, 2020
f4eb084
Merge remote-tracking branch 'origin/master' into viewer-treemap
connorjclark May 10, 2020
5944fd5
wip
connorjclark May 11, 2020
c03271c
Merge remote-tracking branch 'origin/subrow-layout' into viewer-treemap
connorjclark May 11, 2020
dfb15f5
fix transfer size
connorjclark May 11, 2020
7fbba12
update
connorjclark Aug 11, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion build/build-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ function copyAssets() {
'images/**/*',
'sw.js',
'manifest.json',
'treemap/index.html',
'treemap/treemap.js',
], distDir, {
cwd: `${sourceDir}/app/`,
parents: true,
Expand All @@ -95,7 +97,6 @@ async function css() {
async function html() {
let htmlSrc = await readFileAsync(`${sourceDir}/app/index.html`, {encoding: 'utf8'});
htmlSrc = htmlSrc.replace(/%%LIGHTHOUSE_TEMPLATES%%/, htmlReportAssets.REPORT_TEMPLATES);

await safeWriteFileAsync(`${distDir}/index.html`, htmlSrc);
}

Expand Down Expand Up @@ -140,6 +141,7 @@ async function compileJs() {
idbKeyvalJs,
versionJs,
...viewJsFiles,
fs.readFileSync(`${__dirname}/../node_modules/webtreemap-cdt/dist/webtreemap.js`, 'utf-8'),
];
const options = {
output: {preamble: license}, // Insert license at top.
Expand Down
3 changes: 3 additions & 0 deletions lighthouse-cli/test/cli/__snapshots__/index-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1277,6 +1277,9 @@ Object {
Object {
"path": "accessibility",
},
Object {
"path": "source-maps",
},
],
"loadFailureMode": "fatal",
"networkQuietThresholdMs": 1000,
Expand Down
173 changes: 32 additions & 141 deletions lighthouse-core/audits/byte-efficiency/unused-javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
'use strict';

const ByteEfficiencyAudit = require('./byte-efficiency-audit.js');
const JSBundles = require('../../computed/js-bundles.js');
const UnusedJavaScriptSummary = require('../../computed/unused-javascript-summary.js');
const JsBundles = require('../../computed/js-bundles.js');
const i18n = require('../../lib/i18n/i18n.js');

const UIStrings = {
Expand Down Expand Up @@ -71,129 +72,6 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
};
}

/**
* @param {LH.Crdp.Profiler.ScriptCoverage} scriptCoverage
* @return {WasteData}
*/
static computeWaste(scriptCoverage) {
let maximumEndOffset = 0;
for (const func of scriptCoverage.functions) {
maximumEndOffset = Math.max(maximumEndOffset, ...func.ranges.map(r => r.endOffset));
}

// We only care about unused ranges of the script, so we can ignore all the nesting and safely
// assume that if a range is unexecuted, all nested ranges within it will also be unexecuted.
const unusedByIndex = new Uint8Array(maximumEndOffset);
for (const func of scriptCoverage.functions) {
for (const range of func.ranges) {
if (range.count === 0) {
for (let i = range.startOffset; i < range.endOffset; i++) {
unusedByIndex[i] = 1;
}
}
}
}

let unused = 0;
for (const x of unusedByIndex) {
unused += x;
}

return {
unusedByIndex,
unusedLength: unused,
contentLength: maximumEndOffset,
};
}

/**
* @param {LH.Audit.ByteEfficiencyItem} item
* @param {WasteData[]} wasteData
* @param {LH.Artifacts.Bundle} bundle
* @param {ReturnType<typeof UnusedJavaScript.determineLengths>} lengths
* @param {number} bundleSourceUnusedThreshold
*/
static createBundleMultiData(item, wasteData, bundle, lengths, bundleSourceUnusedThreshold) {
if (!bundle.script.content) return;

/** @type {Record<string, number>} */
const files = {};

const lineLengths = bundle.script.content.split('\n').map(l => l.length);
let totalSoFar = 0;
const lineOffsets = lineLengths.map(len => {
const retVal = totalSoFar;
totalSoFar += len + 1;
return retVal;
});

// @ts-ignore: We will upstream computeLastGeneratedColumns to CDT eventually.
bundle.map.computeLastGeneratedColumns();
for (const mapping of bundle.map.mappings()) {
let offset = lineOffsets[mapping.lineNumber];

offset += mapping.columnNumber;
const lastColumnOfMapping =
// @ts-ignore: We will upstream lastColumnNumber to CDT eventually.
(mapping.lastColumnNumber - 1) || lineLengths[mapping.lineNumber];
for (let i = mapping.columnNumber; i <= lastColumnOfMapping; i++) {
if (wasteData.every(data => data.unusedByIndex[offset] === 1)) {
const key = mapping.sourceURL || '(unmapped)';
files[key] = (files[key] || 0) + 1;
}
offset += 1;
}
}

const transferRatio = lengths.transfer / lengths.content;
const topUnusedFilesSizes = Object.entries(files)
.filter(([_, unusedBytes]) => unusedBytes * transferRatio >= bundleSourceUnusedThreshold)
.sort(([_, unusedBytes1], [__, unusedBytes2]) => unusedBytes2 - unusedBytes1)
.slice(0, 5)
.map(([key, unusedBytes]) => {
const total = key === '(unmapped)' ? bundle.sizes.unmappedBytes : bundle.sizes.files[key];
return {
key,
unused: Math.round(unusedBytes * transferRatio),
total: Math.round(total * transferRatio),
};
});

const commonSourcePrefix = commonPrefix([...bundle.map._sourceInfos.keys()]);
Object.assign(item, {
sources: trimCommonPrefix(topUnusedFilesSizes.map(d => d.key), commonSourcePrefix),
sourceBytes: topUnusedFilesSizes.map(d => d.total),
sourceWastedBytes: topUnusedFilesSizes.map(d => d.unused),
});
}

/**
* @param {WasteData[]} wasteData
* @param {string} url
* @param {ReturnType<typeof UnusedJavaScript.determineLengths>} lengths
* @return {LH.Audit.ByteEfficiencyItem}
*/
static mergeWaste(wasteData, url, lengths) {
let unused = 0;
let content = 0;
// TODO: this is right for multiple script tags in an HTML document,
// but may be wrong for multiple frames using the same script resource.
for (const usage of wasteData) {
unused += usage.unusedLength;
content += usage.contentLength;
}

const wastedRatio = (unused / content) || 0;
const wastedBytes = Math.round(lengths.transfer * wastedRatio);

return {
url: url,
totalBytes: lengths.transfer,
wastedBytes,
wastedPercent: 100 * wastedRatio,
};
}

/**
* @param {WasteData[]} wasteData
* @param {LH.Artifacts.NetworkRequest} networkRecord
Expand Down Expand Up @@ -223,31 +101,44 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
* @return {Promise<ByteEfficiencyAudit.ByteEfficiencyProduct>}
*/
static async audit_(artifacts, networkRecords, context) {
const bundles = artifacts.SourceMaps ? await JSBundles.request(artifacts, context) : [];
const bundles = await JsBundles.request(artifacts, context);
const {bundleSourceUnusedThreshold = IGNORE_BUNDLE_SOURCE_THRESHOLD_IN_BYTES} =
context.options || {};

/** @type {Map<string, Array<LH.Crdp.Profiler.ScriptCoverage>>} */
const scriptsByUrl = new Map();
for (const script of artifacts.JsUsage) {
const scripts = scriptsByUrl.get(script.url) || [];
scripts.push(script);
scriptsByUrl.set(script.url, scripts);
}

const items = [];
for (const [url, scriptCoverage] of scriptsByUrl.entries()) {
for (const [url, scriptCoverages] of Object.entries(artifacts.JsUsage)) {
const networkRecord = networkRecords.find(record => record.url === url);
if (!networkRecord) continue;
const wasteData = scriptCoverage.map(UnusedJavaScript.computeWaste);
const lengths = UnusedJavaScript.determineLengths(wasteData, networkRecord);
const bundle = bundles.find(b => b.script.src === url);
const item = UnusedJavaScript.mergeWaste(wasteData, networkRecord.url, lengths);
if (item.wastedBytes <= IGNORE_THRESHOLD_IN_BYTES) continue;
if (bundle) {
UnusedJavaScript.createBundleMultiData(
item, wasteData, bundle, lengths, bundleSourceUnusedThreshold);
const unusedJsSummary =
await UnusedJavaScriptSummary.request({networkRecord, scriptCoverages, bundle}, context);
if (unusedJsSummary.wastedBytes <= IGNORE_THRESHOLD_IN_BYTES) continue;

const item = {
url: unusedJsSummary.url,
totalBytes: unusedJsSummary.totalBytes,
wastedBytes: unusedJsSummary.wastedBytes,
wastedPercent: unusedJsSummary.wastedPercent,
};

// Augment with bundle data.
if (bundle && unusedJsSummary.sourcesWastedBytes) {
const topUnusedSourceSizes = Object.entries(unusedJsSummary.sourcesWastedBytes)
.slice(0, 5)
.map(([source, unused]) => {
const total = source === '(unmapped)' ? bundle.sizes.unmappedBytes : bundle.sizes.files[source];
return {source, unused, total};
})
.filter(d => d.unused >= bundleSourceUnusedThreshold);

const commonSourcePrefix = commonPrefix([...bundle.map._sourceInfos.keys()]);
Object.assign(item, {
sources: trimCommonPrefix(topUnusedSourceSizes.map(d => d.source), commonSourcePrefix),
sourceBytes: topUnusedSourceSizes.map(d => d.total),
sourceWastedBytes: topUnusedSourceSizes.map(d => d.unused),
});
}

items.push(item);
}

Expand Down
Loading