More trace parsers

- SurfaceFlinger dump
- WindowManager dump
- screen recording
- Minor renaming to keep code semantic consistent also with ParserScreenRecording

Fixes: 238097667
Fixes: 238097628
Fixes: 238078364
Test: npm run build:unit && npm run test:unit
Change-Id: I01aa22c1bb030da21f6fb44eb1b159a4431191ce
This commit is contained in:
Kean Mariotti
2022-07-06 12:44:42 +00:00
parent f84bc18fa1
commit 4d70c4b4a4
23 changed files with 406 additions and 30 deletions

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2022 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.
*/
class ScreenRecordingTraceEntry {
constructor(public timestamp: number,
public videoTimeSeconds: number,
public videoData: Uint8Array) {
}
};
export {ScreenRecordingTraceEntry};

View File

@@ -17,8 +17,6 @@ enum TraceTypeId {
ACCESSIBILITY,
WINDOW_MANAGER,
SURFACE_FLINGER,
WINDOW_MANAGER_DUMP,
SURFACE_FLINGER_DUMP,
SCREEN_RECORDING,
TRANSACTIONS,
TRANSACTIONS_LEGACY,

View File

@@ -38,6 +38,24 @@ describe("ArrayUtils", () => {
expect(ArrayUtils.equal(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3]))).toBeTrue();
});
it("searchSubarray", () => {
expect(ArrayUtils.searchSubarray([], [0])).toEqual(undefined);
expect(ArrayUtils.searchSubarray([], [])).toEqual(0);
expect(ArrayUtils.searchSubarray([0], [])).toEqual(0);
expect(ArrayUtils.searchSubarray([0, 1, 2], [-1])).toEqual(undefined);
expect(ArrayUtils.searchSubarray([0, 1, 2], [])).toEqual(0);
expect(ArrayUtils.searchSubarray([0, 1, 2], [0])).toEqual(0);
expect(ArrayUtils.searchSubarray([0, 1, 2], [1])).toEqual(1);
expect(ArrayUtils.searchSubarray([0, 1, 2], [2])).toEqual(2);
expect(ArrayUtils.searchSubarray([0, 1, 2], [0, 1])).toEqual(0);
expect(ArrayUtils.searchSubarray([0, 1, 2], [1, 2])).toEqual(1);
expect(ArrayUtils.searchSubarray([0, 1, 2], [2])).toEqual(2);
expect(ArrayUtils.searchSubarray([0, 1, 2], [2, 3])).toEqual(undefined);
});
it("binarySearchLowerOrEqual", () => {
// no match
expect(ArrayUtils.binarySearchLowerOrEqual([], 5)).toBeUndefined();
@@ -59,4 +77,19 @@ describe("ArrayUtils", () => {
expect(ArrayUtils.binarySearchLowerOrEqual([3, 4, 5, 6], 5)).toEqual(2);
expect(ArrayUtils.binarySearchLowerOrEqual([3, 4, 5, 6, 7], 5)).toEqual(2);
});
it("toUintLittleEndian", () => {
const buffer = new Uint8Array([0, 0, 1, 1]);
expect(ArrayUtils.toUintLittleEndian(buffer, 0, -1)).toEqual(0);
expect(ArrayUtils.toUintLittleEndian(buffer, 0, 0)).toEqual(0);
expect(ArrayUtils.toUintLittleEndian(buffer, 0, 1)).toEqual(0);
expect(ArrayUtils.toUintLittleEndian(buffer, 0, 2)).toEqual(0);
expect(ArrayUtils.toUintLittleEndian(buffer, 3, 4)).toEqual(1);
expect(ArrayUtils.toUintLittleEndian(buffer, 2, 4)).toEqual(1 + 256);
expect(ArrayUtils.toUintLittleEndian(buffer, 1, 4)).toEqual(256 + 256*256);
expect(ArrayUtils.toUintLittleEndian(buffer, 0, 3)).toEqual(256*256);
});
});

View File

@@ -39,6 +39,25 @@ class ArrayUtils {
return true;
}
static searchSubarray<T>(array: T[] | TypedArray, subarray: T[] | TypedArray): number|undefined {
for (let i = 0; i + subarray.length <= array.length; ++i) {
let match = true;
for (let j = 0; j < subarray.length; ++j) {
if (array[i + j] != subarray[j]) {
match = false;
break;
}
}
if (match) {
return i;
}
}
return undefined;
}
static binarySearchLowerOrEqual<T>(values: T[] | TypedArray, target: T): number|undefined {
if (values.length == 0) {
return undefined;
@@ -66,6 +85,15 @@ class ArrayUtils {
return result;
}
static toUintLittleEndian(buffer: Uint8Array, start: number, end: number) {
let result = 0;
for (let i = end-1; i>=start; --i) {
result *= 256;
result += buffer[i];
}
return result;
}
}
export {ArrayUtils};

View File

@@ -19,13 +19,16 @@ import {TraceTypeId} from "common/trace/type_id";
abstract class Parser {
constructor(buffer: Uint8Array) {
const magicNumber = this.getMagicNumber();
const bufferContainsMagicNumber = ArrayUtils.equal(magicNumber, buffer.slice(0, magicNumber.length));
if (!bufferContainsMagicNumber) {
throw TypeError("buffer doesn't contain expected magic number");
if (magicNumber !== undefined)
{
const bufferContainsMagicNumber = ArrayUtils.equal(magicNumber, buffer.slice(0, magicNumber.length));
if (!bufferContainsMagicNumber) {
throw TypeError("buffer doesn't contain expected magic number");
}
}
this.traceEntriesProto = this.decodeProto(buffer);
this.timestamps = this.traceEntriesProto.map((entryProto: any) => this.getTimestamp(entryProto));
this.decodedEntries = this.decodeTrace(buffer);
this.timestamps = this.decodedEntries.map((entry: any) => this.getTimestamp(entry));
}
public abstract getTraceTypeId(): TraceTypeId;
@@ -34,20 +37,20 @@ abstract class Parser {
return this.timestamps;
}
public getTraceEntry(timestamp: number): any|undefined {
public getTraceEntry(timestamp: number): undefined|any {
const index = ArrayUtils.binarySearchLowerOrEqual(this.getTimestamps(), timestamp);
if (index === undefined) {
return undefined;
}
return this.processTraceEntryProto(this.traceEntriesProto[index]);
return this.processDecodedEntry(this.decodedEntries[index]);
}
protected abstract getMagicNumber(): number[];
protected abstract decodeProto(buffer: Uint8Array): any[];
protected abstract getTimestamp(entryProto: any): number;
protected abstract processTraceEntryProto(entryProto: any): any;
protected abstract getMagicNumber(): undefined|number[];
protected abstract decodeTrace(buffer: Uint8Array): any[];
protected abstract getTimestamp(decodedEntry: any): number;
protected abstract processDecodedEntry(decodedEntry: any): any;
private traceEntriesProto: any[];
private decodedEntries: any[];
private timestamps: number[];
}

View File

@@ -30,7 +30,7 @@ class ParserAccessibility extends Parser {
return ParserAccessibility.MAGIC_NUMBER;
}
override decodeProto(buffer: Uint8Array): any[] {
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>AccessibilityTraceFileProto.decode(buffer)).entry;
}
@@ -38,7 +38,7 @@ class ParserAccessibility extends Parser {
return Number(entryProto.elapsedRealtimeNanos);
}
override processTraceEntryProto(entryProto: any): any {
override processDecodedEntry(entryProto: any): any {
return entryProto;
}

View File

@@ -19,9 +19,11 @@ import {ParserInputMethodClients} from "./parser_input_method_clients";
import {ParserInputMethodManagerService} from "./parser_input_method_manager_service";
import {ParserInputMethodService} from "./parser_input_method_service";
import {ParserProtoLog} from "./parser_protolog"
import {ParserScreenRecording} from "./parser_screen_recording"
import {ParserSurfaceFlinger} from "./parser_surface_flinger"
import {ParserTransactions} from "./parser_transactions";
import {ParserWindowManager} from "./parser_window_manager"
import {ParserWindowManagerDump} from "./parser_window_manager_dump"
class ParserFactory {
static readonly PARSERS = [
@@ -30,9 +32,11 @@ class ParserFactory {
ParserInputMethodManagerService,
ParserInputMethodService,
ParserProtoLog,
ParserScreenRecording,
ParserSurfaceFlinger,
ParserTransactions,
ParserWindowManager,
ParserWindowManagerDump,
]
createParsers(buffers: Uint8Array[]): Parser[] {

View File

@@ -30,7 +30,7 @@ class ParserInputMethodClients extends Parser {
return ParserInputMethodClients.MAGIC_NUMBER;
}
override decodeProto(buffer: Uint8Array): any[] {
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>InputMethodClientsTraceFileProto.decode(buffer)).entry;
}
@@ -38,7 +38,7 @@ class ParserInputMethodClients extends Parser {
return Number(entryProto.elapsedRealtimeNanos);
}
override processTraceEntryProto(entryProto: any): any {
override processDecodedEntry(entryProto: any): any {
return entryProto;
}

View File

@@ -30,7 +30,7 @@ class ParserInputMethodManagerService extends Parser {
return ParserInputMethodManagerService.MAGIC_NUMBER;
}
override decodeProto(buffer: Uint8Array): any[] {
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>InputMethodManagerServiceTraceFileProto.decode(buffer)).entry;
}
@@ -38,7 +38,7 @@ class ParserInputMethodManagerService extends Parser {
return Number(entryProto.elapsedRealtimeNanos);
}
protected override processTraceEntryProto(entryProto: any): any {
protected override processDecodedEntry(entryProto: any): any {
return entryProto;
}

View File

@@ -30,7 +30,7 @@ class ParserInputMethodService extends Parser {
return ParserInputMethodService.MAGIC_NUMBER;
}
override decodeProto(buffer: Uint8Array): any[] {
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>InputMethodServiceTraceFileProto.decode(buffer)).entry;
}
@@ -38,7 +38,7 @@ class ParserInputMethodService extends Parser {
return Number(entryProto.elapsedRealtimeNanos);
}
override processTraceEntryProto(entryProto: any): any {
override processDecodedEntry(entryProto: any): any {
return entryProto;
}

View File

@@ -32,7 +32,7 @@ class ParserProtoLog extends Parser {
return ParserProtoLog.MAGIC_NUMBER;
}
override decodeProto(buffer: Uint8Array): any[] {
override decodeTrace(buffer: Uint8Array): any[] {
const fileProto: any = ProtoLogFileProto.decode(buffer);
if (fileProto.version !== ParserProtoLog.PROTOLOG_VERSION) {
@@ -58,7 +58,7 @@ class ParserProtoLog extends Parser {
return Number(entryProto.elapsedRealtimeNanos);
}
override processTraceEntryProto(entryProto: any): LogMessage {
override processDecodedEntry(entryProto: any): LogMessage {
const message = (<any>configJson).messages[entryProto.messageHash];
if (!message) {
return new FormattedLogMessage(entryProto);

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2022 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 {ScreenRecordingTraceEntry} from "common/trace/screen_recording";
import {TraceTypeId} from "common/trace/type_id";
import {TestUtils} from 'test/test_utils';
import {Parser} from './parser';
import {ParserFactory} from './parser_factory';
describe("ParserScreenRecording", () => {
let parser: Parser;
beforeAll(() => {
const buffer = TestUtils.loadFixture("screen_recording.mp4");
const parsers = new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
});
it("has expected trace type", () => {
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.SCREEN_RECORDING);
});
it("provides timestamps", () => {
const timestamps = parser.getTimestamps();
expect(timestamps.length)
.toEqual(85);
expect(timestamps.slice(0, 3))
.toEqual([19446131807000, 19446158500000, 19446167117000]);
expect(timestamps.slice(timestamps.length-3, timestamps.length))
.toEqual([19448470076000, 19448487525000, 19448501007000]);
});
it("retrieves trace entry", () => {
{
const entry = parser.getTraceEntry(19446131807000)!
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0);
}
{
const entry = parser.getTraceEntry(19448501007000)!
expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry);
expect(Number(entry.videoTimeSeconds)).toBeCloseTo(2.37, 0.001);
}
});
});

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2022 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 {TraceTypeId} from "common/trace/type_id";
import {ArrayUtils} from "common/utils/array_utils";
import {Parser} from "./parser"
import {ScreenRecordingTraceEntry} from "common/trace/screen_recording";
class ParserScreenRecording extends Parser {
constructor(private videoData: Uint8Array) {
super(videoData);
}
override getTraceTypeId(): TraceTypeId {
return TraceTypeId.SCREEN_RECORDING;
}
override getMagicNumber(): number[] {
return ParserScreenRecording.MPEG4_MAGIC_NMBER;
}
override decodeTrace(videoData: Uint8Array): number[] {
let posCount = this.searchMagicString(videoData);
let [posTimestamps, count] = this.parseTimestampsCount(videoData, posCount);
return this.parseTimestamps(videoData, posTimestamps, count);
}
override getTimestamp(decodedEntry: number): number {
return decodedEntry;
}
override processDecodedEntry(timestamp: number): ScreenRecordingTraceEntry {
const videoTimeSeconds = (timestamp - this.timestamps[0]) / 1000000000 + ParserScreenRecording.EPSILON;
return new ScreenRecordingTraceEntry(timestamp, videoTimeSeconds, this.videoData);
}
private searchMagicString(videoData: Uint8Array): number {
let pos = ArrayUtils.searchSubarray(videoData, ParserScreenRecording.WINSCOPE_META_MAGIC_STRING);
if (pos === undefined) {
throw new TypeError("video data doesn't contain winscope magic string");
}
pos += ParserScreenRecording.WINSCOPE_META_MAGIC_STRING.length;
return pos;
}
private parseTimestampsCount(videoData: Uint8Array, pos: number) : [number, number] {
if (pos + 4 >= videoData.length) {
throw new TypeError("video data is too short. Expected timestamps count doesn't fit");
}
const timestampsCount = ArrayUtils.toUintLittleEndian(videoData, pos, pos+4);
pos += 4;
return [pos, timestampsCount];
}
private parseTimestamps(videoData: Uint8Array, pos: number, count: number): number[] {
if (pos + count * 8 >= videoData.length) {
throw new TypeError("video data is too short. Expected timestamps do not fit");
}
const timestamps: number[] = [];
for (let i = 0; i < count; ++i) {
const timestamp = ArrayUtils.toUintLittleEndian(videoData, pos, pos+8) * 1000;
pos += 8;
timestamps.push(timestamp);
}
return timestamps;
}
private static readonly MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42
private static readonly WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23]; // #VV1NSC0PET1ME!#
private static readonly EPSILON = 0.00001;
}
export {ParserScreenRecording};

View File

@@ -31,7 +31,7 @@ class ParserSurfaceFlinger extends Parser {
return ParserSurfaceFlinger.MAGIC_NUMBER;
}
override decodeProto(buffer: Uint8Array): any[] {
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>LayersTraceFileProto.decode(buffer)).entry;
}
@@ -39,7 +39,7 @@ class ParserSurfaceFlinger extends Parser {
return Number(entryProto.elapsedRealtimeNanos);
}
override processTraceEntryProto(entryProto: any): any {
override processDecodedEntry(entryProto: any): any {
return LayerTraceEntry.fromProto(entryProto.layers.layers, entryProto.displays, entryProto.elapsedRealtimeNanos, entryProto.hwcBlob);
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2022 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 {TraceTypeId} from "common/trace/type_id";
import {LayerTraceEntry} from 'common/trace/flickerlib/layers/LayerTraceEntry';
import {TestUtils} from 'test/test_utils';
import {Parser} from './parser';
import {ParserFactory} from './parser_factory';
describe("ParserSurfaceFlingerDump", () => {
let parser: Parser;
beforeAll(() => {
const buffer = TestUtils.loadFixture("dump_SurfaceFlinger.pb");
const parsers = new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
});
it("has expected trace type", () => {
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.SURFACE_FLINGER);
});
it("provides timestamp", () => {
expect(parser.getTimestamps()).toEqual([0]);
});
it("retrieves trace entry", () => {
const entry = parser.getTraceEntry(0)!
expect(entry).toBeInstanceOf(LayerTraceEntry);
expect(Number(entry.timestampMs)).toEqual(0);
});
});

View File

@@ -30,7 +30,7 @@ class ParserTransactions extends Parser {
return ParserTransactions.MAGIC_NUMBER;
}
override decodeProto(buffer: Uint8Array): any[] {
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>TransactionsTraceFileProto.decode(buffer)).entry;
}
@@ -38,7 +38,7 @@ class ParserTransactions extends Parser {
return Number(entryProto.elapsedRealtimeNanos);
}
override processTraceEntryProto(entryProto: any): any {
override processDecodedEntry(entryProto: any): any {
return entryProto;
}

View File

@@ -31,7 +31,7 @@ class ParserWindowManager extends Parser {
return ParserWindowManager.MAGIC_NUMBER;
}
override decodeProto(buffer: Uint8Array): any {
override decodeTrace(buffer: Uint8Array): any[] {
return (<any>WindowManagerTraceFileProto.decode(buffer)).entry;
}
@@ -39,7 +39,7 @@ class ParserWindowManager extends Parser {
return Number(entryProto.elapsedRealtimeNanos);
}
override processTraceEntryProto(entryProto: any): WindowManagerState {
override processDecodedEntry(entryProto: any): WindowManagerState {
return WindowManagerState.fromProto(entryProto.windowManagerService, entryProto.elapsedRealtimeNanos, entryProto.where);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2022 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 {WindowManagerState} from 'common/trace/flickerlib/windows/WindowManagerState';
import {TraceTypeId} from "common/trace/type_id";
import {ParserFactory} from "./parser_factory";
import {Parser} from "./parser";
import {TestUtils} from "test/test_utils";
describe("ParserWindowManagerDump", () => {
let parser: Parser;
beforeAll(() => {
const buffer = TestUtils.loadFixture("dump_WindowManager.pb");
const parsers = new ParserFactory().createParsers([buffer]);
expect(parsers.length).toEqual(1);
parser = parsers[0];
});
it("has expected trace type", () => {
expect(parser.getTraceTypeId()).toEqual(TraceTypeId.WINDOW_MANAGER);
});
it("provides timestamps", () => {
expect(parser.getTimestamps())
.toEqual([0]);
});
it("retrieves trace entry", () => {
const entry = parser.getTraceEntry(0)!;
expect(entry).toBeInstanceOf(WindowManagerState);
expect(Number(entry.timestampMs)).toEqual(0);
});
});

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2022 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 {TraceTypeId} from "common/trace/type_id";
import {Parser} from './parser'
import {WindowManagerServiceDumpProto} from './proto_types';
import {WindowManagerState} from 'common/trace/flickerlib/windows/WindowManagerState';
class ParserWindowManagerDump extends Parser {
constructor(buffer: Uint8Array) {
super(buffer);
}
override getTraceTypeId(): TraceTypeId {
return TraceTypeId.WINDOW_MANAGER;
}
override getMagicNumber(): undefined {
return undefined;
}
override decodeTrace(buffer: Uint8Array): any[] {
return [WindowManagerServiceDumpProto.decode(buffer)];
}
override getTimestamp(entryProto: any): number {
return 0;
}
override processDecodedEntry(entryProto: any): WindowManagerState {
return WindowManagerState.fromProto(entryProto);
}
}
export {ParserWindowManagerDump};

View File

@@ -14,6 +14,7 @@ const InputMethodServiceTraceFileProto = protobuf.Root.fromJSON(inputMethodClien
const LayersTraceFileProto = protobuf.Root.fromJSON(layersJson).lookupType("android.surfaceflinger.LayersTraceFileProto");
const ProtoLogFileProto = protobuf.Root.fromJSON(protoLogJson).lookupType("com.android.internal.protolog.ProtoLogFileProto");
const TransactionsTraceFileProto = protobuf.Root.fromJSON(transactionsJson).lookupType("android.surfaceflinger.proto.TransactionTraceFile");
const WindowManagerServiceDumpProto = protobuf.Root.fromJSON(windowManagerJson).lookupType("com.android.server.wm.WindowManagerServiceDumpProto");
const WindowManagerTraceFileProto = protobuf.Root.fromJSON(windowManagerJson).lookupType("com.android.server.wm.WindowManagerTraceFileProto");
export {
@@ -24,5 +25,6 @@ export {
LayersTraceFileProto,
ProtoLogFileProto,
TransactionsTraceFileProto,
WindowManagerServiceDumpProto,
WindowManagerTraceFileProto
};

Binary file not shown.

Binary file not shown.

Binary file not shown.