From 07f9bcccf425561a2c4efcac919c67d568d152d0 Mon Sep 17 00:00:00 2001 From: Nataniel Borges Date: Tue, 8 Mar 2022 15:31:58 +0100 Subject: [PATCH 1/2] Use lazy file for LayerTraceEntry This speeds up winscope by parsing each state only when necessary Bug: 211049519 Test: build winscope and open a trace Change-Id: I35e362ebcb7927887b2793697037984fa26de61c --- tools/winscope/src/flickerlib/LayersTrace.ts | 4 +- tools/winscope/src/flickerlib/common.js | 3 + .../flickerlib/layers/LayerTraceEntryLazy.ts | 114 ++++++++++++++++++ 3 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 tools/winscope/src/flickerlib/layers/LayerTraceEntryLazy.ts diff --git a/tools/winscope/src/flickerlib/LayersTrace.ts b/tools/winscope/src/flickerlib/LayersTrace.ts index 7160d604d..01720114a 100644 --- a/tools/winscope/src/flickerlib/LayersTrace.ts +++ b/tools/winscope/src/flickerlib/LayersTrace.ts @@ -15,12 +15,12 @@ */ import { LayersTrace } from "./common" -import LayerTraceEntry from './layers/LayerTraceEntry' +import LayerTraceEntryLazy from './layers/LayerTraceEntryLazy' LayersTrace.fromProto = function (proto: any): LayersTrace { const entries = [] for (const entryProto of proto.entry) { - const transformedEntry = LayerTraceEntry.fromProto( + const transformedEntry = new LayerTraceEntryLazy( /* protos */ entryProto.layers.layers, /* displays */ entryProto.displays, /* timestamp */ entryProto.elapsedRealtimeNanos, diff --git a/tools/winscope/src/flickerlib/common.js b/tools/winscope/src/flickerlib/common.js index a20b909bc..7538d233d 100644 --- a/tools/winscope/src/flickerlib/common.js +++ b/tools/winscope/src/flickerlib/common.js @@ -56,6 +56,8 @@ const WindowToken = require('flicker').com.android.server.wm.traces.common. // SF const Layer = require('flicker').com.android.server.wm.traces.common. layers.Layer; +const AbstractLayerTraceEntry = require('flicker').com.android.server.wm.traces.common. + layers.AbstractLayerTraceEntry; const LayerTraceEntry = require('flicker').com.android.server.wm.traces.common. layers.LayerTraceEntry; const LayerTraceEntryBuilder = require('flicker').com.android.server.wm.traces. @@ -270,6 +272,7 @@ export { WindowManagerTrace, WindowManagerState, // SF + AbstractLayerTraceEntry, Layer, LayerTraceEntry, LayerTraceEntryBuilder, diff --git a/tools/winscope/src/flickerlib/layers/LayerTraceEntryLazy.ts b/tools/winscope/src/flickerlib/layers/LayerTraceEntryLazy.ts new file mode 100644 index 000000000..035cee7e1 --- /dev/null +++ b/tools/winscope/src/flickerlib/layers/LayerTraceEntryLazy.ts @@ -0,0 +1,114 @@ +/* + * Copyright 2021, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +import { AbstractLayerTraceEntry } from "../common"; +import LayerTraceEntry from "./LayerTraceEntry"; + +class LayerTraceEntryLazy extends AbstractLayerTraceEntry { + private _isInitialized: boolean = false; + private _layersProto: any[]; + private _displayProtos: any[]; + timestamp: number; + timestampMs: string; + hwcBlob: string; + where: string; + private _lazyLayerTraceEntry: LayerTraceEntry; + + constructor (layersProto: any[], displayProtos: any[], + timestamp: number, hwcBlob: string, where: string = '') { + super(); + this._layersProto = layersProto; + this._displayProtos = displayProtos; + this.timestamp = timestamp; + this.timestampMs = timestamp.toString(); + this.hwcBlob = hwcBlob; + this.where = where; + + this.declareLazyProperties(); + } + + private initialize() { + if (this._isInitialized) return; + + this._isInitialized = true; + this._lazyLayerTraceEntry = LayerTraceEntry.fromProto( + this._layersProto, this._displayProtos, this.timestamp, + this.hwcBlob, this.where); + this._layersProto = []; + this._displayProtos = []; + } + + + private declareLazyProperties() { + Object.defineProperty(this, 'kind', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.kind; + }}); + + Object.defineProperty(this, 'timestampMs', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.timestampMs; + }}); + + Object.defineProperty(this, 'rects', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.rects; + }}); + + Object.defineProperty(this, 'proto', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.proto; + }}); + + Object.defineProperty(this, 'shortName', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.shortName; + }}); + + Object.defineProperty(this, 'isVisible', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.isVisible; + }}); + + Object.defineProperty(this, 'flattenedLayers', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.flattenedLayers; + }}); + + Object.defineProperty(this, 'stableId', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.stableId; + }}); + + Object.defineProperty(this, 'visibleLayers', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.visibleLayers; + }}); + + Object.defineProperty(this, 'children', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.children; + }}); + + Object.defineProperty(this, 'displays', {configurable: true, enumerable: true, get: function () { + this.initialize(); + return this._lazyLayerTraceEntry.displays; + }}); + } +} + +export default LayerTraceEntryLazy; From 08815fa938ba070b0756f37e1efda1b1f8abb0d7 Mon Sep 17 00:00:00 2001 From: Nataniel Borges Date: Wed, 9 Mar 2022 17:32:55 +0100 Subject: [PATCH 2/2] Freeze objects for performance Frozen objects don't trigger Vue's reactive Observers and are significantly faster. With this changes, winscope can now load traces with over 100mb Bug: 211049519 Test: build winscope and open a large trace Change-Id: Iec9f91e408db734ce884cf846d5c2826ff346484 --- tools/winscope/src/DataInput.vue | 12 ++++++++++-- tools/winscope/src/dumps/DumpBase.ts | 4 ++-- tools/winscope/src/main.js | 8 ++++++-- tools/winscope/src/traces/SurfaceFlinger.ts | 8 ++++---- tools/winscope/src/traces/TraceBase.ts | 12 ++++++------ 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/tools/winscope/src/DataInput.vue b/tools/winscope/src/DataInput.vue index 8cfa29b29..57fcb9810 100644 --- a/tools/winscope/src/DataInput.vue +++ b/tools/winscope/src/DataInput.vue @@ -356,8 +356,12 @@ export default { } decodedFileTypes.add(dataType); + const frozenData = Object.freeze(decodedFile.data.data); + delete decodedFile.data.data; + decodedFile.data.data = frozenData; + this.$set(this.dataFiles, - dataType, decodedFile.data); + dataType, Object.freeze(decodedFile.data)); } // TODO(b/169305853): Remove this once we have magic numbers or another @@ -369,7 +373,11 @@ export default { const selectedFile = this.getMostLikelyCandidateFile(dataType, files); - this.$set(this.dataFiles, dataType, selectedFile); + const frozenData = Object.freeze(selectedFile.data); + delete selectedFile.data; + selectedFile.data = frozenData; + + this.$set(this.dataFiles, dataType, Object.freeze(selectedFile)); // Remove selected file from overriden list const index = files.indexOf(selectedFile); diff --git a/tools/winscope/src/dumps/DumpBase.ts b/tools/winscope/src/dumps/DumpBase.ts index 9e2bd141e..38c86c4d7 100644 --- a/tools/winscope/src/dumps/DumpBase.ts +++ b/tools/winscope/src/dumps/DumpBase.ts @@ -23,7 +23,7 @@ export default abstract class DumpBase implements IDump { this._files = files; } - get files(): any[] { + get files(): readonly any[] { return Object.values(this._files).flat(); } @@ -31,6 +31,6 @@ export default abstract class DumpBase implements IDump { } interface IDump { - files: Array; + files: readonly Object[]; type: String, } \ No newline at end of file diff --git a/tools/winscope/src/main.js b/tools/winscope/src/main.js index 88aa5f5a2..587f42519 100644 --- a/tools/winscope/src/main.js +++ b/tools/winscope/src/main.js @@ -165,7 +165,9 @@ const store = new Vuex.Store({ } if (Object.keys(traceFiles).length > 0 && typeInfo.constructor) { - traces[traceType] = new typeInfo.constructor(traceFiles); + const newObj = new typeInfo.constructor(traceFiles); + newObj.data = Object.freeze(newObj.data); + traces[traceType] = newObj; } } @@ -198,7 +200,9 @@ const store = new Vuex.Store({ } if (Object.keys(dumpFiles).length > 0 && typeInfo.constructor) { - dumps[dumpType] = new typeInfo.constructor(dumpFiles); + const newObj = new typeInfo.constructor(dumpFiles); + newObj.data = Object.freeze(newObj.data); + dumps[dumpType] = newObj; } } diff --git a/tools/winscope/src/traces/SurfaceFlinger.ts b/tools/winscope/src/traces/SurfaceFlinger.ts index 049dc684a..b8ec6df93 100644 --- a/tools/winscope/src/traces/SurfaceFlinger.ts +++ b/tools/winscope/src/traces/SurfaceFlinger.ts @@ -19,15 +19,15 @@ import TraceBase from './TraceBase'; import { LayersTrace } from '@/flickerlib'; export default class SurfaceFlinger extends TraceBase { - sfTraceFile: Object; - tagGenerationTrace: Object; + readonly sfTraceFile: Object; + readonly tagGenerationTrace: Object; constructor(files) { - const sfTraceFile = files[FILE_TYPES.SURFACE_FLINGER_TRACE]; + const sfTraceFile = Object.freeze(files[FILE_TYPES.SURFACE_FLINGER_TRACE]); const tagGenerationTrace = files[FILE_TYPES.SURFACE_FLINGER_TRACE].tagGenerationTrace; super(sfTraceFile.data, sfTraceFile.timeline, files); - this.tagGenerationTrace = tagGenerationTrace; + this.tagGenerationTrace = Object.freeze(tagGenerationTrace); this.sfTraceFile = sfTraceFile; } diff --git a/tools/winscope/src/traces/TraceBase.ts b/tools/winscope/src/traces/TraceBase.ts index 652dc7ee0..ab9666c6d 100644 --- a/tools/winscope/src/traces/TraceBase.ts +++ b/tools/winscope/src/traces/TraceBase.ts @@ -23,9 +23,9 @@ import JSZip from 'jszip'; export default abstract class Trace implements ITrace { selectedIndex: Number; - data: Object; - timeline: Array; - _files: File[]; + readonly data: Object; + readonly timeline: Array; + readonly _files: File[]; constructor(data: any, timeline: Number[], files: any[]) { this.selectedIndex = 0; @@ -34,7 +34,7 @@ export default abstract class Trace implements ITrace { this._files = files; } - get files(): File[] { + get files(): readonly File[] { return Object.values(this._files).flat(); } @@ -64,6 +64,6 @@ export default abstract class Trace implements ITrace { } interface ITrace { - files: Array; + files: readonly Object[]; type: String, -} \ No newline at end of file +}