豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit d1b4db0

Browse files
Connor ClarkDevtools-frontend LUCI CQ
authored andcommitted
[RPP] Don't make excessive copies of trace during export
- In Base64.ts `encode`, we were using `.split`. That creates copies of the very large string. Instead, use `.slice` to leverage V8's sliced strings optimization. - In TimelinePanel.ts `innerSaveToFile`, we retained large copies of the trace data as Blob/BlobPart throughout the entire lifetime of the function. Instead, clear the variables when done to free them sooner. I suspect these issues greatly raised the amount of memory needed to export a trace, and are direct causes of the referenced OOM bug. Bug: 450046606 Change-Id: I05a2ad3e30e16bd69a49501b56335b6dfde985d3 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/7453170 Auto-Submit: Connor Clark <cjamcl@chromium.org> Commit-Queue: Connor Clark <cjamcl@chromium.org> Reviewed-by: Paul Irish <paulirish@chromium.org> Commit-Queue: Paul Irish <paulirish@chromium.org>
1 parent 9066161 commit d1b4db0

File tree

2 files changed

+15
-2
lines changed

2 files changed

+15
-2
lines changed

front_end/core/common/Base64.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,15 @@ export function encode(input: BlobPart): Promise<string> {
3737
const reader = new FileReader();
3838
reader.onerror = () => reject(new Error('failed to convert to base64'));
3939
reader.onload = () => {
40+
// This string can be very large, so take care to not double memory. `split`
41+
// was used here before, which always results in new strings in V8. By using
42+
// slice instead, we leverage the sliced string optimization in V8 and avoid
43+
// doubling the memory requirement (even if temporarily: that is a potential
44+
// source of OOM crashes given large enough input, such as is common with
45+
// Performance traces).
4046
const blobAsUrl = reader.result as string;
41-
const [, base64] = blobAsUrl.split(',', 2);
47+
const index = blobAsUrl.indexOf(',');
48+
const base64 = blobAsUrl.slice(index + 1);
4249
resolve(base64);
4350
};
4451

front_end/panels/timeline/TimelinePanel.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,7 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
15401540
}
15411541

15421542
let blob = new Blob(blobParts, {type: 'application/json'});
1543+
blobParts.length = 0; // Don't retain this large object for the remaining lifetime of this function.
15431544

15441545
if (config.shouldCompress) {
15451546
this.statusDialog.updateStatus(i18nString(UIStrings.compressingTraceForDownload));
@@ -1560,6 +1561,8 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
15601561
// blobParts.join('') === (await gzBlob.arrayBuffer().then(bytes => Common.Gzip.arrayBufferToString(bytes)))
15611562
}
15621563

1564+
const blobType = blob.type; // blob may be reassigned later.
1565+
15631566
// In some cases Base64.encode() can return undefined; see crbug.com/436482118 for details.
15641567
// TODO(crbug.com/436482118): understand this edge case and fix the Base64.encode method to not just return undefined.
15651568
let bytesAsB64: string|null = null;
@@ -1569,11 +1572,14 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
15691572
this.statusDialog.updateStatus(i18nString(UIStrings.encodingTraceForDownload));
15701573
this.statusDialog.updateProgressBar(i18nString(UIStrings.encodingTraceForDownload), 100);
15711574
bytesAsB64 = await Common.Base64.encode(blob);
1575+
if (bytesAsB64.length) {
1576+
blob = new Blob(); // Don't retain this large object for the remaining lifetime of this function.
1577+
}
15721578
} catch {
15731579
}
15741580

15751581
if (bytesAsB64?.length) {
1576-
const contentData = new TextUtils.ContentData.ContentData(bytesAsB64, /* isBase64=*/ true, blob.type);
1582+
const contentData = new TextUtils.ContentData.ContentData(bytesAsB64, /* isBase64=*/ true, blobType);
15771583
await Workspace.FileManager.FileManager.instance().save(fileName, contentData, /* forceSaveAs=*/ true);
15781584
Workspace.FileManager.FileManager.instance().close(fileName);
15791585
} else {

0 commit comments

Comments
 (0)