[DO NOT MERGE] Sync flicker from master to sc-v2
Flicker on master diverged form sc-v2, to make it easier to debug flicker issues on sc-v2, push the current version of flicker into sc-v2 Test: atest FlickerTests WMShellFlickerTests Bug: 188792659 Change-Id: Iaacfaa75102f93351a6ccd0252ecd739784f94ff
This commit is contained in:
@@ -55,6 +55,14 @@ WINSCOPE_TOKEN_HEADER = "Winscope-Token"
|
||||
# Location to save the proxy security token
|
||||
WINSCOPE_TOKEN_LOCATION = os.path.expanduser('~/.config/winscope/.token')
|
||||
|
||||
# Winscope traces extensions
|
||||
WINSCOPE_EXT = ".winscope"
|
||||
WINSCOPE_EXT_LEGACY = ".pb"
|
||||
WINSCOPE_EXTS = [WINSCOPE_EXT, WINSCOPE_EXT_LEGACY]
|
||||
|
||||
# Winscope traces directory
|
||||
WINSCOPE_DIR = "/data/misc/wmtrace/"
|
||||
|
||||
# Max interval between the client keep-alive requests in seconds
|
||||
KEEP_ALIVE_INTERVAL_S = 5
|
||||
|
||||
@@ -85,12 +93,29 @@ class FileMatcher:
|
||||
matchingFiles = call_adb(
|
||||
f"shell su root find {self.path} -name {self.matcher}", device_id)
|
||||
|
||||
log.debug("Found file %s", matchingFiles.split('\n')[:-1])
|
||||
return matchingFiles.split('\n')[:-1]
|
||||
|
||||
def get_filetype(self):
|
||||
return self.type
|
||||
|
||||
|
||||
class WinscopeFileMatcher(FileMatcher):
|
||||
def __init__(self, path, matcher, filetype) -> None:
|
||||
self.path = path
|
||||
self.internal_matchers = list(map(lambda ext: FileMatcher(path, f'{matcher}{ext}', filetype),
|
||||
WINSCOPE_EXTS))
|
||||
self.type = filetype
|
||||
|
||||
def get_filepaths(self, device_id):
|
||||
for matcher in self.internal_matchers:
|
||||
files = matcher.get_filepaths(device_id)
|
||||
if len(files) > 0:
|
||||
return files
|
||||
log.debug("No files found")
|
||||
return []
|
||||
|
||||
|
||||
class TraceTarget:
|
||||
"""Defines a single parameter to trace.
|
||||
|
||||
@@ -111,51 +136,50 @@ class TraceTarget:
|
||||
# Order of files matters as they will be expected in that order and decoded in that order
|
||||
TRACE_TARGETS = {
|
||||
"window_trace": TraceTarget(
|
||||
File("/data/misc/wmtrace/wm_trace.pb", "window_trace"),
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "wm_trace", "window_trace"),
|
||||
'su root cmd window tracing start\necho "WM trace started."',
|
||||
'su root cmd window tracing stop >/dev/null 2>&1'
|
||||
),
|
||||
"accessibility_trace": TraceTarget(
|
||||
File("/data/misc/a11ytrace/a11y_trace.pb", "accessibility_trace"),
|
||||
WinscopeFileMatcher("/data/misc/a11ytrace", "a11y_trace", "accessibility_trace"),
|
||||
'su root cmd accessibility start-trace\necho "Accessibility trace started."',
|
||||
'su root cmd accessibility stop-trace >/dev/null 2>&1'
|
||||
),
|
||||
"layers_trace": TraceTarget(
|
||||
File("/data/misc/wmtrace/layers_trace.pb", "layers_trace"),
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "layers_trace", "layers_trace"),
|
||||
'su root service call SurfaceFlinger 1025 i32 1\necho "SF trace started."',
|
||||
'su root service call SurfaceFlinger 1025 i32 0 >/dev/null 2>&1'
|
||||
),
|
||||
"screen_recording": TraceTarget(
|
||||
File("/data/local/tmp/screen.winscope.mp4", "screen_recording"),
|
||||
'screenrecord --bit-rate 8M /data/local/tmp/screen.winscope.mp4 >/dev/null 2>&1 &\necho "ScreenRecorder started."',
|
||||
File(f'/data/local/tmp/screen.mp4', "screen_recording"),
|
||||
f'screenrecord --bit-rate 8M /data/local/tmp/screen.mp4 >/dev/null 2>&1 &\necho "ScreenRecorder started."',
|
||||
'pkill -l SIGINT screenrecord >/dev/null 2>&1'
|
||||
),
|
||||
"transaction": TraceTarget(
|
||||
[
|
||||
File("/data/misc/wmtrace/transaction_trace.pb", "transactions"),
|
||||
FileMatcher("/data/misc/wmtrace/", "transaction_merges_*.pb",
|
||||
"transaction_merges"),
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "transaction_trace", "transactions"),
|
||||
FileMatcher(WINSCOPE_DIR, f'transaction_merges_*', "transaction_merges"),
|
||||
],
|
||||
'su root service call SurfaceFlinger 1020 i32 1\necho "SF transactions recording started."',
|
||||
'su root service call SurfaceFlinger 1020 i32 0 >/dev/null 2>&1'
|
||||
),
|
||||
"proto_log": TraceTarget(
|
||||
File("/data/misc/wmtrace/wm_log.pb", "proto_log"),
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "wm_log", "proto_log"),
|
||||
'su root cmd window logging start\necho "WM logging started."',
|
||||
'su root cmd window logging stop >/dev/null 2>&1'
|
||||
),
|
||||
"ime_trace_clients": TraceTarget(
|
||||
File("/data/misc/wmtrace/ime_trace_clients.pb", "ime_trace_clients"),
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_clients", "ime_trace_clients"),
|
||||
'su root ime tracing start\necho "Clients IME trace started."',
|
||||
'su root ime tracing stop >/dev/null 2>&1'
|
||||
),
|
||||
"ime_trace_service": TraceTarget(
|
||||
File("/data/misc/wmtrace/ime_trace_service.pb", "ime_trace_service"),
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_service", "ime_trace_service"),
|
||||
'su root ime tracing start\necho "Service IME trace started."',
|
||||
'su root ime tracing stop >/dev/null 2>&1'
|
||||
),
|
||||
"ime_trace_managerservice": TraceTarget(
|
||||
File("/data/misc/wmtrace/ime_trace_managerservice.pb", "ime_trace_managerservice"),
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_managerservice", "ime_trace_managerservice"),
|
||||
'su root ime tracing start\necho "ManagerService IME trace started."',
|
||||
'su root ime tracing stop >/dev/null 2>&1'
|
||||
),
|
||||
@@ -204,12 +228,12 @@ class DumpTarget:
|
||||
|
||||
DUMP_TARGETS = {
|
||||
"window_dump": DumpTarget(
|
||||
File("/data/local/tmp/wm_dump.pb", "window_dump"),
|
||||
'su root dumpsys window --proto > /data/local/tmp/wm_dump.pb'
|
||||
File(f'/data/local/tmp/wm_dump{WINSCOPE_EXT}', "window_dump"),
|
||||
f'su root dumpsys window --proto > /data/local/tmp/wm_dump{WINSCOPE_EXT}'
|
||||
),
|
||||
"layers_dump": DumpTarget(
|
||||
File("/data/local/tmp/sf_dump.pb", "layers_dump"),
|
||||
'su root dumpsys SurfaceFlinger --proto > /data/local/tmp/sf_dump.pb'
|
||||
File(f'/data/local/tmp/sf_dump{WINSCOPE_EXT}', "layers_dump"),
|
||||
f'su root dumpsys SurfaceFlinger --proto > /data/local/tmp/sf_dump{WINSCOPE_EXT}'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -425,6 +449,9 @@ class FetchFilesEndpoint(DeviceRequestEndpoint):
|
||||
buf = base64.encodebytes(tmp.read()).decode("utf-8")
|
||||
file_buffers[file_type].append(buf)
|
||||
|
||||
if (len(file_buffers) == 0):
|
||||
log.error("Proxy didn't find any file to fetch")
|
||||
|
||||
# server.send_header('X-Content-Type-Options', 'nosniff')
|
||||
# add_standard_headers(server)
|
||||
j = json.dumps(file_buffers)
|
||||
|
||||
@@ -1,24 +1,12 @@
|
||||
import { DiffGenerator, DiffType } from "../src/utils/diff.js";
|
||||
import { Node, DiffNode, toPlainObject } from "./utils/tree.js";
|
||||
|
||||
function checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree) {
|
||||
const diffTree = new DiffGenerator(newTree)
|
||||
.compareWith(oldTree)
|
||||
.withUniqueNodeId(node => node.id)
|
||||
.generateDiffTree();
|
||||
|
||||
expect(diffTree).toEqual(expectedDiffTree);
|
||||
}
|
||||
|
||||
describe("DiffGenerator", () => {
|
||||
it("can generate a simple add diff", () => {
|
||||
const oldTree = new Node({ id: 1 }, [
|
||||
const treeOne = new Node({ id: 1 }, [
|
||||
new Node({ id: 2 }, []),
|
||||
new Node({ id: 3 }, []),
|
||||
new Node({ id: 4 }, []),
|
||||
]);
|
||||
|
||||
const newTree = new Node({ id: 1 }, [
|
||||
const treeTwo = new Node({ id: 1 }, [
|
||||
new Node({ id: 2 }, []),
|
||||
new Node({ id: 3 }, [
|
||||
new Node({ id: 5 }, []),
|
||||
@@ -26,12 +14,21 @@ describe("DiffGenerator", () => {
|
||||
new Node({ id: 4 }, []),
|
||||
]);
|
||||
|
||||
function checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree) {
|
||||
const diffTree = new DiffGenerator(newTree)
|
||||
.compareWith(oldTree)
|
||||
.withUniqueNodeId(node => node.id)
|
||||
.withModifiedCheck(() => false)
|
||||
.generateDiffTree();
|
||||
|
||||
expect(diffTree).toEqual(expectedDiffTree);
|
||||
}
|
||||
|
||||
describe("DiffGenerator", () => {
|
||||
it("can generate a simple add diff", () => {
|
||||
const oldTree = treeOne;
|
||||
const newTree = treeTwo;
|
||||
|
||||
const expectedDiffTree = toPlainObject(
|
||||
new DiffNode({ id: 1 }, DiffType.NONE, [
|
||||
new DiffNode({ id: 2 }, DiffType.NONE, []),
|
||||
@@ -46,19 +43,8 @@ describe("DiffGenerator", () => {
|
||||
});
|
||||
|
||||
it("can generate a simple delete diff", () => {
|
||||
const oldTree = new Node({ id: 1 }, [
|
||||
new Node({ id: 2 }, []),
|
||||
new Node({ id: 3 }, [
|
||||
new Node({ id: 5 }, []),
|
||||
]),
|
||||
new Node({ id: 4 }, []),
|
||||
]);
|
||||
|
||||
const newTree = new Node({ id: 1 }, [
|
||||
new Node({ id: 2 }, []),
|
||||
new Node({ id: 3 }, []),
|
||||
new Node({ id: 4 }, []),
|
||||
]);
|
||||
const oldTree = treeTwo;
|
||||
const newTree = treeOne;
|
||||
|
||||
const expectedDiffTree = toPlainObject(
|
||||
new DiffNode({ id: 1 }, DiffType.NONE, [
|
||||
@@ -74,13 +60,7 @@ describe("DiffGenerator", () => {
|
||||
});
|
||||
|
||||
it("can generate a simple move diff", () => {
|
||||
const oldTree = new Node({ id: 1 }, [
|
||||
new Node({ id: 2 }, []),
|
||||
new Node({ id: 3 }, [
|
||||
new Node({ id: 5 }, []),
|
||||
]),
|
||||
new Node({ id: 4 }, []),
|
||||
]);
|
||||
const oldTree = treeTwo;
|
||||
|
||||
const newTree = new Node({ id: 1 }, [
|
||||
new Node({ id: 2 }, []),
|
||||
|
||||
@@ -1,32 +1,6 @@
|
||||
import { DiffType } from "../src/utils/diff.js";
|
||||
import { ObjectTransformer } from "../src/transform.js";
|
||||
import { Node, DiffNode, toPlainObject } from "./utils/tree.js";
|
||||
|
||||
class ObjNode extends Node {
|
||||
constructor(name, children, combined) {
|
||||
const nodeDef = {
|
||||
kind: '',
|
||||
name: name,
|
||||
};
|
||||
if (combined) {
|
||||
nodeDef.combined = true;
|
||||
}
|
||||
super(nodeDef, children);
|
||||
}
|
||||
}
|
||||
|
||||
class ObjDiffNode extends DiffNode {
|
||||
constructor(name, diffType, children, combined) {
|
||||
const nodeDef = {
|
||||
kind: '',
|
||||
name: name,
|
||||
};
|
||||
if (combined) {
|
||||
nodeDef.combined = true;
|
||||
}
|
||||
super(nodeDef, diffType, children);
|
||||
}
|
||||
}
|
||||
import { ObjNode, ObjDiffNode, toPlainObject } from "./utils/tree.js";
|
||||
|
||||
describe("ObjectTransformer", () => {
|
||||
it("can transform a simple object", () => {
|
||||
@@ -46,19 +20,19 @@ describe("ObjectTransformer", () => {
|
||||
const expectedTransformedObj = toPlainObject(
|
||||
new ObjNode('root', [
|
||||
new ObjNode('obj', [
|
||||
new ObjNode('string: string', [], true),
|
||||
new ObjNode('number: 3', [], true),
|
||||
]),
|
||||
new ObjNode('string: string', [], true, 'root.obj.string'),
|
||||
new ObjNode('number: 3', [], true, 'root.obj.number'),
|
||||
], undefined, 'root.obj'),
|
||||
new ObjNode('array', [
|
||||
new ObjNode('0', [
|
||||
new ObjNode('nested: item', [], true),
|
||||
]),
|
||||
new ObjNode("1: two", [], true),
|
||||
]),
|
||||
])
|
||||
new ObjNode('nested: item', [], true, 'root.array.0.nested'),
|
||||
], undefined, 'root.array.0'),
|
||||
new ObjNode("1: two", [], true, 'root.array.1'),
|
||||
], undefined, 'root.array'),
|
||||
], undefined, 'root')
|
||||
);
|
||||
|
||||
const transformedObj = new ObjectTransformer(obj, 'root')
|
||||
const transformedObj = new ObjectTransformer(obj, 'root', 'root')
|
||||
.setOptions({ formatter: () => { } })
|
||||
.transform();
|
||||
|
||||
@@ -75,12 +49,12 @@ describe("ObjectTransformer", () => {
|
||||
const expectedTransformedObj = toPlainObject(
|
||||
new ObjNode('root', [
|
||||
new ObjNode('obj', [
|
||||
new ObjNode('null: null', [], true),
|
||||
]),
|
||||
])
|
||||
new ObjNode('null: null', [], true, 'root.obj.null'),
|
||||
], undefined, 'root.obj'),
|
||||
], undefined, 'root')
|
||||
);
|
||||
|
||||
const transformedObj = new ObjectTransformer(obj, 'root')
|
||||
const transformedObj = new ObjectTransformer(obj, 'root', 'root')
|
||||
.setOptions({ formatter: () => { } })
|
||||
.transform();
|
||||
|
||||
@@ -106,14 +80,14 @@ describe("ObjectTransformer", () => {
|
||||
const expectedTransformedObj = toPlainObject(
|
||||
new ObjDiffNode('root', DiffType.NONE, [
|
||||
new ObjDiffNode('a', DiffType.NONE, [
|
||||
new ObjDiffNode('b: 1', DiffType.NONE, [], true),
|
||||
new ObjDiffNode('d: 3', DiffType.ADDED, [], true),
|
||||
]),
|
||||
new ObjDiffNode('c: 2', DiffType.NONE, [], true),
|
||||
])
|
||||
new ObjDiffNode('b: 1', DiffType.NONE, [], true, 'root.a.b'),
|
||||
new ObjDiffNode('d: 3', DiffType.ADDED, [], true, 'root.a.d'),
|
||||
], false, 'root.a'),
|
||||
new ObjDiffNode('c: 2', DiffType.NONE, [], true, 'root.c'),
|
||||
], false, 'root')
|
||||
);
|
||||
|
||||
const transformedObj = new ObjectTransformer(newObj, 'root')
|
||||
const transformedObj = new ObjectTransformer(newObj, 'root', 'root')
|
||||
.setOptions({ formatter: () => { } })
|
||||
.withDiff(oldObj)
|
||||
.transform();
|
||||
@@ -133,13 +107,13 @@ describe("ObjectTransformer", () => {
|
||||
const expectedTransformedObj = toPlainObject(
|
||||
new ObjDiffNode('root', DiffType.NONE, [
|
||||
new ObjDiffNode('a', DiffType.NONE, [
|
||||
new ObjDiffNode('1', DiffType.ADDED, []),
|
||||
new ObjDiffNode('null', DiffType.DELETED, []),
|
||||
]),
|
||||
])
|
||||
new ObjDiffNode('1', DiffType.ADDED, [], false, 'root.a.1'),
|
||||
new ObjDiffNode('null', DiffType.DELETED, [], false, 'root.a.null'),
|
||||
], false, 'root.a'),
|
||||
], false, 'root')
|
||||
);
|
||||
|
||||
const transformedObj = new ObjectTransformer(newObj, 'root')
|
||||
const transformedObj = new ObjectTransformer(newObj, 'root', 'root')
|
||||
.setOptions({ formatter: () => { } })
|
||||
.withDiff(oldObj)
|
||||
.transform();
|
||||
@@ -166,15 +140,15 @@ describe("ObjectTransformer", () => {
|
||||
new ObjDiffNode('root', DiffType.NONE, [
|
||||
new ObjDiffNode('a', DiffType.NONE, [
|
||||
new ObjDiffNode('b', DiffType.NONE, [
|
||||
new ObjDiffNode('1', DiffType.ADDED, []),
|
||||
new ObjDiffNode('null', DiffType.DELETED, []),
|
||||
]),
|
||||
]),
|
||||
new ObjDiffNode('c: 2', DiffType.NONE, [], true),
|
||||
])
|
||||
new ObjDiffNode('1', DiffType.ADDED, [], false, 'root.a.b.1'),
|
||||
new ObjDiffNode('null', DiffType.DELETED, [], false, 'root.a.b.null'),
|
||||
], false, 'root.a.b'),
|
||||
], false, 'root.a'),
|
||||
new ObjDiffNode('c: 2', DiffType.NONE, [], true, 'root.c'),
|
||||
], false, 'root')
|
||||
);
|
||||
|
||||
const transformedObj = new ObjectTransformer(newObj, 'root')
|
||||
const transformedObj = new ObjectTransformer(newObj, 'root', 'root')
|
||||
.setOptions({ formatter: () => { } })
|
||||
.withDiff(oldObj)
|
||||
.transform();
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
import { detectAndDecode, decodeAndTransformProto, FILE_TYPES } from '../src/decode';
|
||||
import { decodeAndTransformProto, FILE_TYPES, FILE_DECODERS } from '../src/decode';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const layers_traces = [
|
||||
require('./traces/layers_trace/layers_trace_emptyregion.pb'),
|
||||
require('./traces/layers_trace/layers_trace_invalid_layer_visibility.pb'),
|
||||
require('./traces/layers_trace/layers_trace_orphanlayers.pb'),
|
||||
require('./traces/layers_trace/layers_trace_root.pb'),
|
||||
require('./traces/layers_trace/layers_trace_root_aosp.pb'),
|
||||
];
|
||||
import { expectedEntries, expectedLayers, layers_traces } from './traces/ExpectedTraces';
|
||||
|
||||
describe("Proto Transformations", () => {
|
||||
it("can transform surface flinger traces", () => {
|
||||
for (const trace of layers_traces) {
|
||||
fs.readFileSync(path.resolve(__dirname, trace));
|
||||
const traceBuffer = fs.readFileSync(path.resolve(__dirname, trace));
|
||||
for (var i = 0; i < layers_traces.length; i++) {
|
||||
const trace = layers_traces[i];
|
||||
const buffer = new Uint8Array(fs.readFileSync(path.resolve(__dirname, trace)));
|
||||
const data = decodeAndTransformProto(buffer, FILE_DECODERS[FILE_TYPES.SURFACE_FLINGER_TRACE].decoderParams, true);
|
||||
|
||||
const buffer = new Uint8Array(traceBuffer);
|
||||
const data = decodeAndTransformProto(buffer, FILE_TYPES.layers_trace, true);
|
||||
// use final entry as this determines if there was any error in previous entry parsing
|
||||
const transformedEntry = data.entries[data.entries.length-1];
|
||||
const expectedEntry = expectedEntries[i];
|
||||
for (const property in expectedEntry) {
|
||||
expect(transformedEntry[property]).toEqual(expectedEntry[property]);
|
||||
}
|
||||
|
||||
expect(true).toBe(true);
|
||||
// check final flattened layer
|
||||
const transformedLayer = transformedEntry.flattenedLayers[transformedEntry.flattenedLayers.length-1];
|
||||
const expectedLayer = expectedLayers[i];
|
||||
for (const property in expectedLayer) {
|
||||
expect(transformedLayer[property]).toEqual(expectedLayer[property]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
50
tools/winscope/spec/TagErrorSpec.js
Normal file
50
tools/winscope/spec/TagErrorSpec.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import { decodeAndTransformProto, FILE_TYPES, FILE_DECODERS } from '../src/decode';
|
||||
import Tag from '../src/flickerlib/tags/Tag';
|
||||
import Error from '../src/flickerlib/errors/Error';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const tagTrace = '../spec/traces/tag_trace.winscope';
|
||||
const errorTrace = '../spec/traces/error_trace.winscope';
|
||||
|
||||
describe("Tag Transformation", () => {
|
||||
it("can transform tag traces", () => {
|
||||
const buffer = new Uint8Array(fs.readFileSync(path.resolve(__dirname, tagTrace)));
|
||||
|
||||
const data = decodeAndTransformProto(buffer, FILE_DECODERS[FILE_TYPES.TAG_TRACE].decoderParams, true);
|
||||
|
||||
expect(data.entries[0].timestamp.toString()).toEqual('159979677861');
|
||||
expect(data.entries[1].timestamp.toString()).toEqual('161268519083');
|
||||
expect(data.entries[2].timestamp.toString()).toEqual('161126718496');
|
||||
expect(data.entries[3].timestamp.toString()).toEqual('161613497398');
|
||||
expect(data.entries[4].timestamp.toString()).toEqual('161227062777');
|
||||
expect(data.entries[5].timestamp.toString()).toEqual('161268519083');
|
||||
expect(data.entries[6].timestamp.toString()).toEqual('161825945076');
|
||||
expect(data.entries[7].timestamp.toString()).toEqual('162261072567');
|
||||
|
||||
expect(data.entries[0].tags).toEqual([new Tag(12345,"PIP_ENTER",true,1,"",0)]);
|
||||
expect(data.entries[1].tags).toEqual([new Tag(12345,"PIP_ENTER",false,2,"",2)]);
|
||||
expect(data.entries[2].tags).toEqual([new Tag(67890,"ROTATION",true,3,"",3)]);
|
||||
expect(data.entries[3].tags).toEqual([new Tag(67890,"ROTATION",false,4,"",4)]);
|
||||
expect(data.entries[4].tags).toEqual([new Tag(9876,"PIP_EXIT",true,5,"",5)]);
|
||||
expect(data.entries[5].tags).toEqual([new Tag(9876,"PIP_EXIT",false,6,"",6)]);
|
||||
expect(data.entries[6].tags).toEqual([new Tag(54321,"IME_APPEAR",true,7,"",7)]);
|
||||
expect(data.entries[7].tags).toEqual([new Tag(54321,"IME_APPEAR",false,8,"",8)]);
|
||||
})
|
||||
});
|
||||
|
||||
describe("Error Transformation", () => {
|
||||
it("can transform error traces", () => {
|
||||
const buffer = new Uint8Array(fs.readFileSync(path.resolve(__dirname, errorTrace)));
|
||||
|
||||
const data = decodeAndTransformProto(buffer, FILE_DECODERS[FILE_TYPES.ERROR_TRACE].decoderParams, true);
|
||||
|
||||
expect(data.entries[0].timestamp.toString()).toEqual('161401263106');
|
||||
expect(data.entries[1].timestamp.toString()).toEqual('161126718496');
|
||||
expect(data.entries[2].timestamp.toString()).toEqual('162261072567');
|
||||
|
||||
expect(data.entries[0].errors).toEqual([new Error("","",33,"",33)]);
|
||||
expect(data.entries[1].errors).toEqual([new Error("","",66,"",66)]);
|
||||
expect(data.entries[2].errors).toEqual([new Error("","",99,"",99)]);
|
||||
})
|
||||
});
|
||||
492
tools/winscope/spec/traces/ExpectedTraces.js
Normal file
492
tools/winscope/spec/traces/ExpectedTraces.js
Normal file
@@ -0,0 +1,492 @@
|
||||
import { Buffer, RectF, Transform, Matrix, Color, Rect, Region } from '../../src/flickerlib/common.js';
|
||||
import { VISIBLE_CHIP } from '../../src/flickerlib/treeview/Chips';
|
||||
|
||||
const standardTransform = new Transform(0, new Matrix(1, 0, 0, 0, 1, 0));
|
||||
const standardRect = new Rect(0, 0, 0, 0);
|
||||
const standardColor = new Color(0, 0, 0, 1);
|
||||
const standardCrop = new Rect(0, 0, -1, -1);
|
||||
|
||||
const expectedEmptyRegionLayer = {
|
||||
backgroundBlurRadius: 0,
|
||||
chips: [],
|
||||
cornerRadius: 0,
|
||||
effectiveScalingMode: 0,
|
||||
hwcCompositionType: "INVALID",
|
||||
id: 580,
|
||||
isOpaque: false,
|
||||
isRelativeOf: false,
|
||||
kind: "580",
|
||||
name: "SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
shadowRadius: 0,
|
||||
shortName: "SurfaceView - com.android.(...).Main#0",
|
||||
type: "BufferLayer",
|
||||
z: -1,
|
||||
zOrderRelativeOf: null,
|
||||
parentId: 579,
|
||||
activeBuffer: new Buffer(1440, 2614, 1472, 1),
|
||||
bufferTransform: standardTransform,
|
||||
color: new Color(0, 0, 0, 0.0069580078125),
|
||||
crop: standardCrop,
|
||||
hwcFrame: standardRect,
|
||||
screenBounds: new RectF(37, 43, 146, 152),
|
||||
transform: new Transform(0, new Matrix(1, 0, 37.37078094482422, 0, 1, -3.5995326042175293)),
|
||||
visibleRegion: new Region([new Rect(37, 43, 146, 152)]),
|
||||
};
|
||||
const emptyRegionProto = {
|
||||
2: "\nparent=0\ntype=BufferLayer\nname=Display Root#0",
|
||||
3: "\nparent=0\ntype=BufferLayer\nname=Display Overlays#0",
|
||||
4: "\nparent=2\ntype=BufferLayer\nname=mBelowAppWindowsContainers#0",
|
||||
5: "\nparent=2\ntype=BufferLayer\nname=com.android.server.wm.DisplayContent$TaskStackContainers@193aa46#0",
|
||||
6: "\nparent=5\ntype=BufferLayer\nname=animationLayer#0",
|
||||
7: "\nparent=5\ntype=BufferLayer\nname=splitScreenDividerAnchor#0",
|
||||
8: "\nparent=2\ntype=BufferLayer\nname=mAboveAppWindowsContainers#0",
|
||||
9: "\nparent=2\ntype=BufferLayer\nname=mImeWindowsContainers#0",
|
||||
10: "\nparent=5\ntype=BufferLayer\nname=Stack=0#0",
|
||||
11: "\nparent=10\ntype=ColorLayer\nname=animation background stackId=0#0",
|
||||
12: "\nparent=9\ntype=BufferLayer\nname=WindowToken{f81e7fc android.os.Binder@7c880ef}#0",
|
||||
13: "\nparent=4\ntype=BufferLayer\nname=WallpaperWindowToken{3756850 token=android.os.Binder@25b3e13}#0",
|
||||
18: "\nparent=13\ntype=BufferLayer\nname=fd46a8e com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
|
||||
19: "\nparent=18\ntype=BufferLayer\nname=com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
|
||||
20: "\nparent=8\ntype=BufferLayer\nname=WindowToken{fc1aa98 android.os.BinderProxy@3517c7b}#0",
|
||||
21: "\nparent=20\ntype=BufferLayer\nname=10022f1 DockedStackDivider#0",
|
||||
22: "\nparent=8\ntype=BufferLayer\nname=WindowToken{49a6772 android.os.BinderProxy@7ba1c7d}#0",
|
||||
23: "\nparent=22\ntype=BufferLayer\nname=56ef7c3 AssistPreviewPanel#0",
|
||||
24: "\nparent=8\ntype=BufferLayer\nname=WindowToken{35f7d5c android.os.BinderProxy@8b38fcf}#0",
|
||||
25: "\nparent=24\ntype=BufferLayer\nname=9029865 NavigationBar#0",
|
||||
26: "\nparent=8\ntype=BufferLayer\nname=WindowToken{a9a69ab android.os.BinderProxy@f64ffa}#0",
|
||||
27: "\nparent=26\ntype=BufferLayer\nname=5334808 StatusBar#0",
|
||||
28: "\nparent=8\ntype=BufferLayer\nname=WindowToken{a63ca37 android.os.BinderProxy@435eb36}#0",
|
||||
29: "\nparent=28\ntype=BufferLayer\nname=1a40ba4 ScreenDecorOverlay#0",
|
||||
30: "\nparent=8\ntype=BufferLayer\nname=WindowToken{4ed84c2 android.os.BinderProxy@33d1d0d}#0",
|
||||
31: "\nparent=30\ntype=BufferLayer\nname=7a0d2d3 ScreenDecorOverlayBottom#0",
|
||||
32: "\nparent=25\ntype=BufferLayer\nname=NavigationBar#0",
|
||||
33: "\nparent=27\ntype=BufferLayer\nname=StatusBar#0",
|
||||
34: "\nparent=29\ntype=BufferLayer\nname=ScreenDecorOverlay#0",
|
||||
35: "\nparent=31\ntype=BufferLayer\nname=ScreenDecorOverlayBottom#0",
|
||||
36: "\nparent=10\ntype=BufferLayer\nname=Task=239#0",
|
||||
37: "\nparent=632\ntype=BufferLayer\nname=AppWindowToken{188ce21 token=Token{824488 ActivityRecord{b0d882b u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t239}}}#0",
|
||||
38: "\nparent=37\ntype=BufferLayer\nname=9f6e33d com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
44: "\nparent=37\ntype=BufferLayer\nname=81a00fc com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
88: "\nparent=5\ntype=BufferLayer\nname=Stack=2#0",
|
||||
89: "\nparent=88\ntype=ColorLayer\nname=animation background stackId=2#0",
|
||||
90: "\nparent=88\ntype=BufferLayer\nname=Task=241#0",
|
||||
91: "\nparent=633\ntype=BufferLayer\nname=AppWindowToken{a9f5144 token=Token{f102257 ActivityRecord{3a0fd6 u0 com.android.chrome/com.google.android.apps.chrome.Main t241}}}#0",
|
||||
96: "\nparent=91\ntype=BufferLayer\nname=87e310e com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
574: "\nparent=8\ntype=BufferLayer\nname=WindowToken{37eed7d android.os.Binder@6e217d4}#0",
|
||||
579: "\nparent=96\ntype=BufferLayer\nname=com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
580: "\nparent=579\ntype=BufferLayer\nname=SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
581: "\nparent=579\ntype=ColorLayer\nname=Background for -SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
583: "\nparent=44\ntype=BufferLayer\nname=com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
629: "\nparent=38\ntype=BufferLayer\nname=com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#2",
|
||||
632: "\nparent=6\ntype=BufferLayer\nname=Surface(name=AppWindowToken{188ce21 token=Token{824488 ActivityRecord{b0d882b u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t239}}})/@0x90c9c46 - animation-leash#1",
|
||||
633: "\nparent=6\ntype=BufferLayer\nname=Surface(name=AppWindowToken{a9f5144 token=Token{f102257 ActivityRecord{3a0fd6 u0 com.android.chrome/com.google.android.apps.chrome.Main t241}}})/@0xd9b9374 - animation-leash#1"
|
||||
};
|
||||
const expectedEmptyRegion = {
|
||||
chips: [],
|
||||
proto: emptyRegionProto,
|
||||
hwcBlob: "",
|
||||
isVisible: true,
|
||||
kind: "entry",
|
||||
rects: [],
|
||||
shortName: "0d0h38m28s521ms",
|
||||
timestampMs: "2308521813510",
|
||||
where: "",
|
||||
name: "0d0h38m28s521ms",
|
||||
stableId: "LayerTraceEntry",
|
||||
visibleLayers: [],
|
||||
};
|
||||
|
||||
const expectedInvalidLayerVisibilityLayer = {
|
||||
backgroundBlurRadius: 0,
|
||||
chips: [],
|
||||
cornerRadius: 0,
|
||||
effectiveScalingMode: 0,
|
||||
hwcCompositionType: "INVALID",
|
||||
id: 1536,
|
||||
isOpaque: false,
|
||||
isRelativeOf: false,
|
||||
kind: "1536",
|
||||
name: "com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#2",
|
||||
shadowRadius: 0,
|
||||
shortName: "com.google.(...).NexusLauncherActivity#2",
|
||||
type: "BufferLayer",
|
||||
z: 0,
|
||||
zOrderRelativeOf: null,
|
||||
parentId: 1535,
|
||||
stableId: "BufferLayer 1536 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#2",
|
||||
activeBuffer: new Buffer(1440, 2880, 1472, 1),
|
||||
bufferTransform: standardTransform,
|
||||
color: new Color(-1, -1, -1, 0),
|
||||
hwcFrame: standardRect,
|
||||
transform: standardTransform,
|
||||
visibleRegion: new Region([standardRect]),
|
||||
};
|
||||
const invalidLayerVisibilityProto = {
|
||||
2: "\nparent=0\ntype=BufferLayer\nname=Display Root#0",
|
||||
3: "\nparent=0\ntype=BufferLayer\nname=Display Overlays#0",
|
||||
4: "\nparent=2\ntype=BufferLayer\nname=mBelowAppWindowsContainers#0",
|
||||
5: "\nparent=2\ntype=BufferLayer\nname=com.android.server.wm.DisplayContent$TaskStackContainers@4270eb4#0",
|
||||
6: "\nparent=5\ntype=BufferLayer\nname=animationLayer#0",
|
||||
7: "\nparent=5\ntype=BufferLayer\nname=boostedAnimationLayer#0",
|
||||
8: "\nparent=5\ntype=BufferLayer\nname=homeAnimationLayer#0",
|
||||
9: "\nparent=5\ntype=BufferLayer\nname=splitScreenDividerAnchor#0",
|
||||
10: "\nparent=2\ntype=BufferLayer\nname=mAboveAppWindowsContainers#0",
|
||||
11: "\nparent=2\ntype=BufferLayer\nname=mImeWindowsContainers#0",
|
||||
12: "\nparent=5\ntype=BufferLayer\nname=Stack=0#0",
|
||||
13: "\nparent=12\ntype=ColorLayer\nname=animation background stackId=0#0",
|
||||
14: "\nparent=11\ntype=BufferLayer\nname=WindowToken{268fcff android.os.Binder@6688c1e}#0",
|
||||
15: "\nparent=4\ntype=BufferLayer\nname=WallpaperWindowToken{6572e20 token=android.os.Binder@9543923}#0",
|
||||
20: "\nparent=15\ntype=BufferLayer\nname=5e2e96f com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
|
||||
21: "\nparent=20\ntype=BufferLayer\nname=com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
|
||||
26: "\nparent=10\ntype=BufferLayer\nname=WindowToken{68e3f31 android.os.BinderProxy@f018fd8}#0",
|
||||
27: "\nparent=26\ntype=BufferLayer\nname=2b80616 NavigationBar#0",
|
||||
28: "\nparent=10\ntype=BufferLayer\nname=WindowToken{4e20cae android.os.BinderProxy@9086129}#0",
|
||||
29: "\nparent=28\ntype=BufferLayer\nname=b09a4f StatusBar#0",
|
||||
30: "\nparent=3\ntype=BufferLayer\nname=WindowToken{501e3b8 android.os.BinderProxy@238661b}#0",
|
||||
31: "\nparent=30\ntype=BufferLayer\nname=d803591 ScreenDecorOverlay#0",
|
||||
32: "\nparent=3\ntype=BufferLayer\nname=WindowToken{56d48f7 android.os.BinderProxy@f0f2cf6}#0",
|
||||
33: "\nparent=32\ntype=BufferLayer\nname=1cd8364 ScreenDecorOverlayBottom#0",
|
||||
35: "\nparent=29\ntype=BufferLayer\nname=StatusBar#0",
|
||||
36: "\nparent=31\ntype=BufferLayer\nname=ScreenDecorOverlay#0",
|
||||
37: "\nparent=33\ntype=BufferLayer\nname=ScreenDecorOverlayBottom#0",
|
||||
38: "\nparent=12\ntype=BufferLayer\nname=Task=2#0",
|
||||
39: "\nparent=38\ntype=BufferLayer\nname=AppWindowToken{215b919 token=Token{104a060 ActivityRecord{7e30c63 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t2}}}#0",
|
||||
821: "\nparent=14\ntype=BufferLayer\nname=5c937c8 InputMethod#0",
|
||||
1078: "\nparent=10\ntype=BufferLayer\nname=WindowToken{7dc6283 android.os.BinderProxy@f83c532}#0",
|
||||
1079: "\nparent=1078\ntype=BufferLayer\nname=32c0c00 AssistPreviewPanel#0",
|
||||
1080: "\nparent=10\ntype=BufferLayer\nname=WindowToken{9f8a3df android.os.BinderProxy@825027e}#0",
|
||||
1081: "\nparent=1080\ntype=BufferLayer\nname=26d9efb DockedStackDivider#0",
|
||||
1403: "\nparent=10\ntype=BufferLayer\nname=WindowToken{dedcfff android.os.Binder@a80cb1e}#0",
|
||||
1447: "\nparent=39\ntype=BufferLayer\nname=39ca531 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
1454: "\nparent=27\ntype=BufferLayer\nname=NavigationBar#0",
|
||||
1502: "\nparent=10\ntype=BufferLayer\nname=WindowToken{3ea357b android.os.Binder@6d9a90a}#0",
|
||||
1505: "\nparent=1518\ntype=BufferLayer\nname=Task=623#0",
|
||||
1506: "\nparent=1505\ntype=BufferLayer\nname=AppWindowToken{6deed44 token=Token{45cae57 ActivityRecord{7f14bd6 u0 com.android.server.wm.flicker.testapp/.SimpleActivity t623}}}#0",
|
||||
1518: "\nparent=5\ntype=BufferLayer\nname=Stack=51#0",
|
||||
1519: "\nparent=1518\ntype=ColorLayer\nname=animation background stackId=51#0",
|
||||
1521: "\nparent=1506\ntype=BufferLayer\nname=496d52e com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
|
||||
1534: "\nparent=1447\ntype=BufferLayer\nname=com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
1535: "\nparent=39\ntype=BufferLayer\nname=e280197 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
1536: "\nparent=1535\ntype=BufferLayer\nname=com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#2",
|
||||
};
|
||||
const expectedInvalidLayerVisibility = {
|
||||
chips: [],
|
||||
proto: invalidLayerVisibilityProto,
|
||||
hwcBlob: "",
|
||||
isVisible: true,
|
||||
kind: "entry",
|
||||
rects: [],
|
||||
shortName: "2d22h13m17s233ms",
|
||||
timestampMs: "252797233543024",
|
||||
where: "",
|
||||
name: "2d22h13m17s233ms",
|
||||
stableId: "LayerTraceEntry",
|
||||
visibleLayers: [],
|
||||
};
|
||||
|
||||
const expectedOrphanLayersLayer = {
|
||||
backgroundBlurRadius: 0,
|
||||
chips: [],
|
||||
cornerRadius: 0,
|
||||
effectiveScalingMode: 0,
|
||||
hwcCompositionType: "INVALID",
|
||||
id: 1012,
|
||||
isOpaque: true,
|
||||
isRelativeOf: false,
|
||||
kind: "1012",
|
||||
name: "SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
shadowRadius: 0,
|
||||
shortName: "SurfaceView - com.android.(...).Main#0",
|
||||
type: "BufferLayer",
|
||||
z: -1,
|
||||
zOrderRelativeOf: null,
|
||||
parentId: 1011,
|
||||
stableId: "BufferLayer 1012 SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
activeBuffer: new Buffer(1440, 2614, 1472, 1),
|
||||
bufferTransform: standardTransform,
|
||||
color: standardColor,
|
||||
crop: standardCrop,
|
||||
hwcFrame: standardRect,
|
||||
screenBounds: new RectF(0, 98, 1440, 2712),
|
||||
transform: new Transform(0, new Matrix(1, 0, 0, 0, 1, 98)),
|
||||
visibleRegion: new Region([new Rect(0, 98, 1440, 2712)]),
|
||||
};
|
||||
const expectedOrphanLayersProto = {
|
||||
2: "\nparent=0\ntype=BufferLayer\nname=Display Root#0",
|
||||
3: "\nparent=0\ntype=BufferLayer\nname=Display Overlays#0",
|
||||
4: "\nparent=2\ntype=BufferLayer\nname=mBelowAppWindowsContainers#0",
|
||||
5: "\nparent=2\ntype=BufferLayer\nname=com.android.server.wm.DisplayContent$TaskStackContainers@e7dd520#0",
|
||||
6: "\nparent=5\ntype=BufferLayer\nname=animationLayer#0",
|
||||
7: "\nparent=5\ntype=BufferLayer\nname=splitScreenDividerAnchor#0",
|
||||
8: "\nparent=2\ntype=BufferLayer\nname=mAboveAppWindowsContainers#0",
|
||||
9: "\nparent=2\ntype=BufferLayer\nname=mImeWindowsContainers#0",
|
||||
10: "\nparent=5\ntype=BufferLayer\nname=Stack=0#0",
|
||||
11: "\nparent=10\ntype=ColorLayer\nname=animation background stackId=0#0",
|
||||
12: "\nparent=9\ntype=BufferLayer\nname=WindowToken{1350b6f android.os.Binder@d1b0e4e}#0",
|
||||
13: "\nparent=4\ntype=BufferLayer\nname=WallpaperWindowToken{4537182 token=android.os.Binder@d87c4cd}#0",
|
||||
18: "\nparent=13\ntype=BufferLayer\nname=8d26107 com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
|
||||
19: "\nparent=18\ntype=BufferLayer\nname=com.breel.wallpapers.dioramas.lagos.LagosWallpaperService#0",
|
||||
20: "\nparent=8\ntype=BufferLayer\nname=WindowToken{fba948d android.os.BinderProxy@756b124}#0",
|
||||
21: "\nparent=20\ntype=BufferLayer\nname=dc26642 DockedStackDivider#0",
|
||||
22: "\nparent=8\ntype=BufferLayer\nname=WindowToken{45663b4 android.os.BinderProxy@5273887}#0",
|
||||
23: "\nparent=22\ntype=BufferLayer\nname=c617bdd AssistPreviewPanel#0",
|
||||
24: "\nparent=8\ntype=BufferLayer\nname=WindowToken{ef90888 android.os.BinderProxy@9d4dc2b}#0",
|
||||
25: "\nparent=24\ntype=BufferLayer\nname=1d24221 NavigationBar#0",
|
||||
26: "\nparent=8\ntype=BufferLayer\nname=WindowToken{6b1dca9 android.os.BinderProxy@a53d830}#0",
|
||||
27: "\nparent=26\ntype=BufferLayer\nname=eaca22e StatusBar#0",
|
||||
28: "\nparent=8\ntype=BufferLayer\nname=WindowToken{72e584c android.os.BinderProxy@3ba407f}#0",
|
||||
29: "\nparent=28\ntype=BufferLayer\nname=46af095 ScreenDecorOverlay#0",
|
||||
30: "\nparent=8\ntype=BufferLayer\nname=WindowToken{bc659b android.os.BinderProxy@f1405aa}#0",
|
||||
31: "\nparent=30\ntype=BufferLayer\nname=80ead38 ScreenDecorOverlayBottom#0",
|
||||
33: "\nparent=27\ntype=BufferLayer\nname=StatusBar#0",
|
||||
34: "\nparent=29\ntype=BufferLayer\nname=ScreenDecorOverlay#0",
|
||||
35: "\nparent=31\ntype=BufferLayer\nname=ScreenDecorOverlayBottom#0",
|
||||
36: "\nparent=10\ntype=BufferLayer\nname=Task=2#0",
|
||||
37: "\nparent=36\ntype=BufferLayer\nname=AppWindowToken{5162f77 token=Token{ac99d76 ActivityRecord{7749795 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity t2}}}#0",
|
||||
38: "\nparent=37\ntype=BufferLayer\nname=2c19e73 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
41: "\nparent=25\ntype=BufferLayer\nname=NavigationBar#1",
|
||||
43: "\nparent=37\ntype=BufferLayer\nname=2f0c80b com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
46: "\nparent=5\ntype=BufferLayer\nname=Stack=1#0",
|
||||
47: "\nparent=46\ntype=ColorLayer\nname=animation background stackId=1#0",
|
||||
48: "\nparent=46\ntype=BufferLayer\nname=Task=89#0",
|
||||
49: "\nparent=48\ntype=BufferLayer\nname=AppWindowToken{1d514da token=Token{d36fe85 ActivityRecord{e0ec0ef u0 com.android.chrome/com.google.android.apps.chrome.Main t89}}}#0",
|
||||
54: "\nparent=49\ntype=BufferLayer\nname=8ae6e06 com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
607: "\nparent=5\ntype=BufferLayer\nname=Stack=9#0",
|
||||
608: "\nparent=607\ntype=ColorLayer\nname=animation background stackId=9#0",
|
||||
609: "\nparent=607\ntype=BufferLayer\nname=Task=97#0",
|
||||
615: "\nparent=609\ntype=BufferLayer\nname=AppWindowToken{28730c9 token=Token{4d768d0 ActivityRecord{faf093 u0 com.google.android.gm/.welcome.WelcomeTourActivity t97}}}#0",
|
||||
616: "\nparent=615\ntype=BufferLayer\nname=44e6e5c com.google.android.gm/com.google.android.gm.welcome.WelcomeTourActivity#0",
|
||||
679: "\nparent=12\ntype=BufferLayer\nname=2d0b1e4 InputMethod#0",
|
||||
993: "\nparent=8\ntype=BufferLayer\nname=WindowToken{e425e58 android.os.Binder@6d9a73b}#0",
|
||||
1011: "\nparent=54\ntype=BufferLayer\nname=com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
1012: "\nparent=1011\ntype=BufferLayer\nname=SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
1013: "\nparent=1011\ntype=ColorLayer\nname=Background for -SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0",
|
||||
};
|
||||
const expectedOrphanLayers = {
|
||||
chips: [],
|
||||
proto: expectedOrphanLayersProto,
|
||||
hwcBlob: "",
|
||||
isVisible: true,
|
||||
kind: "entry",
|
||||
rects: [],
|
||||
shortName: "3d23h30m9s820ms",
|
||||
timestampMs: "343809820196384",
|
||||
where: "",
|
||||
name: "3d23h30m9s820ms",
|
||||
stableId: "LayerTraceEntry",
|
||||
visibleLayers: [],
|
||||
};
|
||||
|
||||
const expectedRootLayer = {
|
||||
backgroundBlurRadius: 0,
|
||||
cornerRadius: 0,
|
||||
effectiveScalingMode: 0,
|
||||
hwcCompositionType: "INVALID",
|
||||
id: 12545,
|
||||
isOpaque: true,
|
||||
isRelativeOf: false,
|
||||
kind: "12545",
|
||||
name: "com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
|
||||
shadowRadius: 0,
|
||||
shortName: "com.android.(...).SimpleActivity#0",
|
||||
type: "BufferQueueLayer",
|
||||
z: 0,
|
||||
zOrderRelativeOf: null,
|
||||
parentId: 12541,
|
||||
stableId: "BufferQueueLayer 12545 com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
|
||||
activeBuffer: new Buffer(1440, 2960, 1472, 1),
|
||||
chips: [VISIBLE_CHIP],
|
||||
bufferTransform: standardTransform,
|
||||
color: standardColor,
|
||||
crop: new Rect(0, 0, 1440, 2960),
|
||||
hwcFrame: standardRect,
|
||||
screenBounds: new RectF(0, 0, 1440, 2960),
|
||||
sourceBounds: new RectF(0, 0, 1440, 2960),
|
||||
transform: standardTransform,
|
||||
visibleRegion: new Region([new Rect(0, 0, 1440, 2960)]),
|
||||
};
|
||||
const expectedRootProto = {
|
||||
2: "\nparent=-1\ntype=ContainerLayer\nname=Root#0",
|
||||
3: "\nparent=2\ntype=ContainerLayer\nname=mWindowContainers#0",
|
||||
4: "\nparent=2\ntype=ContainerLayer\nname=mOverlayContainers#0",
|
||||
5: "\nparent=3\ntype=ContainerLayer\nname=mBelowAppWindowsContainers#0",
|
||||
6: "\nparent=3\ntype=ContainerLayer\nname=com.android.server.wm.DisplayContent$TaskContainers@708b672#0",
|
||||
7: "\nparent=6\ntype=ContainerLayer\nname=animationLayer#0",
|
||||
8: "\nparent=6\ntype=ContainerLayer\nname=boostedAnimationLayer#0",
|
||||
9: "\nparent=6\ntype=ContainerLayer\nname=homeAnimationLayer#0",
|
||||
10: "\nparent=6\ntype=ContainerLayer\nname=splitScreenDividerAnchor#0",
|
||||
11: "\nparent=3\ntype=ContainerLayer\nname=mAboveAppWindowsContainers#0",
|
||||
12: "\nparent=3\ntype=ContainerLayer\nname=ImeContainer#0",
|
||||
13: "\nparent=6\ntype=ContainerLayer\nname=Task=1#0",
|
||||
18: "\nparent=5\ntype=ContainerLayer\nname=WallpaperWindowToken{4c3f8ef token=android.os.Binder@a0341ce}#0",
|
||||
19: "\nparent=18\ntype=ContainerLayer\nname=aa9ba7e com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2#0",
|
||||
20: "\nparent=19\ntype=BufferQueueLayer\nname=com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2#0",
|
||||
23: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{2e98b86 android.os.BinderProxy@6e5dbc8}#0",
|
||||
24: "\nparent=23\ntype=ContainerLayer\nname=5976c47 NavigationBar0#0",
|
||||
25: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{525aa4 android.os.BinderProxy@df1e236}#0",
|
||||
26: "\nparent=25\ntype=ContainerLayer\nname=986c00d NotificationShade#0",
|
||||
27: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{7ec5009 android.os.BinderProxy@de2add3}#0",
|
||||
28: "\nparent=27\ntype=ContainerLayer\nname=3a0542f StatusBar#0",
|
||||
31: "\nparent=-1\ntype=ContainerLayer\nname=WindowToken{eef604c android.os.BinderProxy@d3a687f}#0",
|
||||
32: "\nparent=31\ntype=ContainerLayer\nname=20b5895 ScreenDecorOverlay#0",
|
||||
33: "\nparent=-1\ntype=ContainerLayer\nname=WindowToken{4846f6f android.os.BinderProxy@39824e}#0",
|
||||
34: "\nparent=33\ntype=ContainerLayer\nname=1d714 ScreenDecorOverlayBottom#0",
|
||||
36: "\nparent=32\ntype=BufferQueueLayer\nname=ScreenDecorOverlay#0",
|
||||
38: "\nparent=34\ntype=BufferQueueLayer\nname=ScreenDecorOverlayBottom#0",
|
||||
40: "\nparent=28\ntype=BufferQueueLayer\nname=StatusBar#0",
|
||||
43: "\nparent=12\ntype=ContainerLayer\nname=WindowToken{fa12db9 android.os.Binder@4b88380}#0",
|
||||
46: "\nparent=13\ntype=ContainerLayer\nname=Task=4#0",
|
||||
47: "\nparent=46\ntype=ContainerLayer\nname=ActivityRecord{99bbfb0 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity#0",
|
||||
54: "\nparent=24\ntype=BufferQueueLayer\nname=NavigationBar0#0",
|
||||
71: "\nparent=43\ntype=ContainerLayer\nname=e8f94d2 InputMethod#0",
|
||||
11499: "\nparent=47\ntype=ContainerLayer\nname=6737b79 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
11501: "\nparent=-1\ntype=ContainerLayer\nname=Input Consumer recents_animation_input_consumer#2",
|
||||
11759: "\nparent=6\ntype=ContainerLayer\nname=Task=873#0",
|
||||
11760: "\nparent=11759\ntype=ContainerLayer\nname=Task=874#0",
|
||||
11761: "\nparent=11760\ntype=ContainerLayer\nname=ActivityRecord{7398002 u0 com.android.server.wm.flicker.testapp/.ImeActivityAutoFocus#0",
|
||||
11785: "\nparent=11761\ntype=ColorLayer\nname=Letterbox - right#0",
|
||||
12131: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{bbffcfd android.os.Binder@547b554}#0",
|
||||
12379: "\nparent=47\ntype=ContainerLayer\nname=3f8f098 com.google.android.apps.nexuslauncher/com.google.android.apps.nexuslauncher.NexusLauncherActivity#0",
|
||||
12412: "\nparent=11761\ntype=ContainerLayer\nname=edca7c6 com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.ImeActivityAutoFocus#0",
|
||||
12448: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=ActivityRecord{99bbfb0 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity)/@0x2c3972c - animation-leash#0",
|
||||
12449: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=ActivityRecord{fc16c94 u0 com.android.server.wm.flicker.testapp/.ImeActivity)/@0x7049863 - animation-leash#0",
|
||||
12485: "\nparent=6\ntype=ContainerLayer\nname=Task=908#0",
|
||||
12486: "\nparent=12485\ntype=ContainerLayer\nname=Task=909#0",
|
||||
12487: "\nparent=12486\ntype=ContainerLayer\nname=ActivityRecord{4b3c5cb u0 com.android.server.wm.flicker.testapp/.ImeActivity#0",
|
||||
12500: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=ActivityRecord{99bbfb0 u0 com.google.android.apps.nexuslauncher/.NexusLauncherActivity)/@0x2c3972c - animation-leash#0",
|
||||
12501: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=ActivityRecord{4b3c5cb u0 com.android.server.wm.flicker.testapp/.ImeActivity)/@0x4ad47a1 - animation-leash#0",
|
||||
12502: "\nparent=2147483645\ntype=ContainerLayer\nname=Surface(name=WallpaperWindowToken{4c3f8ef token=android.os.Binder@a0341ce})/@0xcde5e65 - animation-leash#0",
|
||||
12511: "\nparent=12487\ntype=ColorLayer\nname=Letterbox - right#1",
|
||||
12514: "\nparent=12487\ntype=ContainerLayer\nname=debe1ed com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.ImeActivity#0",
|
||||
12526: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{6b7d663 android.os.BinderProxy@391f21d}#0",
|
||||
12527: "\nparent=12526\ntype=ContainerLayer\nname=32aa260 AssistPreviewPanel#0",
|
||||
12529: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{31f7489 android.os.BinderProxy@67b1e53}#0",
|
||||
12530: "\nparent=12529\ntype=ContainerLayer\nname=cbb28bc DockedStackDivider#0",
|
||||
12536: "\nparent=6\ntype=ContainerLayer\nname=Task=910#0",
|
||||
12537: "\nparent=12536\ntype=ContainerLayer\nname=Task=911#0",
|
||||
12538: "\nparent=12537\ntype=ContainerLayer\nname=ActivityRecord{d3b8a44 u0 com.android.server.wm.flicker.testapp/.SimpleActivity#0",
|
||||
12541: "\nparent=12538\ntype=ContainerLayer\nname=a3583c5 com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
|
||||
12545: "\nparent=12541\ntype=BufferQueueLayer\nname=com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.SimpleActivity#0",
|
||||
2147483645: "\nparent=-1\ntype=\nname=Offscreen Root",
|
||||
};
|
||||
const expectedRoot = {
|
||||
chips: [],
|
||||
proto: expectedRootProto,
|
||||
hwcBlob: "",
|
||||
isVisible: true,
|
||||
kind: "entry",
|
||||
shortName: "0d1h46m19s146ms",
|
||||
timestampMs: "6379146308030",
|
||||
where: "",
|
||||
name: "0d1h46m19s146ms",
|
||||
stableId: "LayerTraceEntry",
|
||||
};
|
||||
|
||||
const expectedRootAospLayer = {
|
||||
backgroundBlurRadius: 0,
|
||||
cornerRadius: 0,
|
||||
effectiveScalingMode: 0,
|
||||
hwcCompositionType: "INVALID",
|
||||
id: 876,
|
||||
isOpaque: false,
|
||||
isRelativeOf: false,
|
||||
kind: "876",
|
||||
name: "com.android.launcher3/com.android.launcher3.Launcher#0",
|
||||
shadowRadius: 0,
|
||||
shortName: "com.android.(...).Launcher#0",
|
||||
type: "BufferLayer",
|
||||
z: 0,
|
||||
zOrderRelativeOf: null,
|
||||
parentId: 41,
|
||||
activeBuffer: new Buffer(1440, 2880, 1472, 1),
|
||||
bufferTransform: standardTransform,
|
||||
chips: [VISIBLE_CHIP],
|
||||
color: standardColor,
|
||||
crop: new Rect(0, 0, 1440, 2880),
|
||||
hwcFrame: standardRect,
|
||||
screenBounds: new RectF(0, 0, 1440, 2880),
|
||||
sourceBounds: new RectF(0, 0, 1440, 2880),
|
||||
transform: standardTransform,
|
||||
visibleRegion: new Region([new Rect(0, 0, 1440, 2880)]),
|
||||
};
|
||||
const expectedRootAospProto = {
|
||||
2: "\nparent=-1\ntype=ContainerLayer\nname=Display Root#0",
|
||||
3: "\nparent=-1\ntype=ContainerLayer\nname=Display Overlays#0",
|
||||
4: "\nparent=2\ntype=ContainerLayer\nname=mBelowAppWindowsContainers#0",
|
||||
5: "\nparent=2\ntype=ContainerLayer\nname=com.android.server.wm.DisplayContent$TaskStackContainers@d8077b3#0",
|
||||
6: "\nparent=5\ntype=ContainerLayer\nname=animationLayer#0",
|
||||
7: "\nparent=5\ntype=ContainerLayer\nname=boostedAnimationLayer#0",
|
||||
8: "\nparent=5\ntype=ContainerLayer\nname=homeAnimationLayer#0",
|
||||
9: "\nparent=5\ntype=ContainerLayer\nname=splitScreenDividerAnchor#0",
|
||||
10: "\nparent=2\ntype=ContainerLayer\nname=mAboveAppWindowsContainers#0",
|
||||
11: "\nparent=2\ntype=ContainerLayer\nname=mImeWindowsContainers#0",
|
||||
12: "\nparent=5\ntype=ContainerLayer\nname=Stack=0#0",
|
||||
13: "\nparent=12\ntype=ColorLayer\nname=animation background stackId=0#0",
|
||||
18: "\nparent=4\ntype=ContainerLayer\nname=WallpaperWindowToken{5a7eaca token=android.os.Binder@438b635}#0",
|
||||
23: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{d19e48 android.os.BinderProxy@560ac3a}#0",
|
||||
24: "\nparent=23\ntype=ContainerLayer\nname=b2a84e1 NavigationBar0#0",
|
||||
25: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{74d6851 android.os.BinderProxy@8b22adb}#0",
|
||||
26: "\nparent=25\ntype=ContainerLayer\nname=16448b6 StatusBar#0",
|
||||
27: "\nparent=-1\ntype=ContainerLayer\nname=WindowToken{624863c android.os.BinderProxy@975b02f}#0",
|
||||
28: "\nparent=27\ntype=ContainerLayer\nname=cdb9fc5 ScreenDecorOverlay#0",
|
||||
29: "\nparent=-1\ntype=ContainerLayer\nname=WindowToken{cb7204b android.os.BinderProxy@b8f3d1a}#0",
|
||||
30: "\nparent=29\ntype=ContainerLayer\nname=ad1ca28 ScreenDecorOverlayBottom#0",
|
||||
31: "\nparent=28\ntype=BufferLayer\nname=ScreenDecorOverlay#0",
|
||||
32: "\nparent=30\ntype=BufferLayer\nname=ScreenDecorOverlayBottom#0",
|
||||
33: "\nparent=18\ntype=ContainerLayer\nname=4f4b23b com.android.systemui.ImageWallpaper#0",
|
||||
34: "\nparent=33\ntype=BufferLayer\nname=com.android.systemui.ImageWallpaper#0",
|
||||
36: "\nparent=26\ntype=BufferLayer\nname=StatusBar#0",
|
||||
37: "\nparent=12\ntype=ContainerLayer\nname=Task=144#0",
|
||||
38: "\nparent=37\ntype=ContainerLayer\nname=AppWindowToken{54e2de0 token=Token{f4c5fe3 ActivityRecord{6a9dc12 u0 com.android.launcher3/.Launcher t144}}}#0",
|
||||
40: "\nparent=-1\ntype=ContainerLayer\nname=Input Consumer recents_animation_input_consumer#1",
|
||||
41: "\nparent=38\ntype=ContainerLayer\nname=418b5c0 com.android.launcher3/com.android.launcher3.Launcher#0",
|
||||
45: "\nparent=11\ntype=ContainerLayer\nname=WindowToken{9158878 android.os.Binder@4f4a5db}#0",
|
||||
46: "\nparent=24\ntype=BufferLayer\nname=NavigationBar0#0",
|
||||
731: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{c0ebbde android.os.BinderProxy@1af0e60}#0",
|
||||
732: "\nparent=731\ntype=ContainerLayer\nname=b37d1bf AssistPreviewPanel#0",
|
||||
733: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{dc6b7ea android.os.BinderProxy@166b08c}#0",
|
||||
734: "\nparent=733\ntype=ContainerLayer\nname=2a1cadb DockedStackDivider#0",
|
||||
862: "\nparent=10\ntype=ContainerLayer\nname=WindowToken{f63efe6 android.os.Binder@d536e41}#0",
|
||||
865: "\nparent=887\ntype=ContainerLayer\nname=Task=170#0",
|
||||
866: "\nparent=865\ntype=ContainerLayer\nname=AppWindowToken{c829d40 token=Token{59970c3 ActivityRecord{36f2472 u0 com.android.server.wm.flicker.testapp/.PipActivity t170}}}#0",
|
||||
871: "\nparent=866\ntype=ContainerLayer\nname=8153ff7 com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp.PipActivity#0",
|
||||
876: "\nparent=41\ntype=BufferLayer\nname=com.android.launcher3/com.android.launcher3.Launcher#0",
|
||||
887: "\nparent=5\ntype=ContainerLayer\nname=Stack=78#0",
|
||||
888: "\nparent=887\ntype=ColorLayer\nname=animation background stackId=78#0",
|
||||
};
|
||||
const expectedRootAosp = {
|
||||
chips: [],
|
||||
proto: expectedRootAospProto,
|
||||
hwcBlob: "",
|
||||
isVisible: true,
|
||||
kind: "entry",
|
||||
shortName: "0d1h3m1s911ms",
|
||||
timestampMs: "3781911657318",
|
||||
where: "",
|
||||
name: "0d1h3m1s911ms",
|
||||
stableId: "LayerTraceEntry",
|
||||
};
|
||||
|
||||
const expectedEntries = [
|
||||
expectedEmptyRegion,
|
||||
expectedInvalidLayerVisibility,
|
||||
expectedOrphanLayers,
|
||||
expectedRoot,
|
||||
expectedRootAosp
|
||||
];
|
||||
const expectedLayers = [
|
||||
expectedEmptyRegionLayer,
|
||||
expectedInvalidLayerVisibilityLayer,
|
||||
expectedOrphanLayersLayer,
|
||||
expectedRootLayer,
|
||||
expectedRootAospLayer
|
||||
];
|
||||
const layers_traces = [
|
||||
'../spec/traces/layers_trace/layers_trace_emptyregion.pb',
|
||||
'../spec/traces/layers_trace/layers_trace_invalid_layer_visibility.pb',
|
||||
'../spec/traces/layers_trace/layers_trace_orphanlayers.pb',
|
||||
'../spec/traces/layers_trace/layers_trace_root.pb',
|
||||
'../spec/traces/layers_trace/layers_trace_root_aosp.pb',
|
||||
];
|
||||
|
||||
export { expectedEntries, expectedLayers, layers_traces };
|
||||
BIN
tools/winscope/spec/traces/error_trace.winscope
Normal file
BIN
tools/winscope/spec/traces/error_trace.winscope
Normal file
Binary file not shown.
BIN
tools/winscope/spec/traces/tag_trace.winscope
Normal file
BIN
tools/winscope/spec/traces/tag_trace.winscope
Normal file
Binary file not shown.
@@ -9,6 +9,31 @@ class DiffNode extends Node {
|
||||
constructor(nodeDef, diffType, children) {
|
||||
super(nodeDef, children);
|
||||
this.diff = { type: diffType };
|
||||
this.name = undefined;
|
||||
this.stableId = undefined;
|
||||
this.kind = undefined;
|
||||
this.shortName = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class ObjNode extends Node {
|
||||
constructor(name, children, combined, stableId) {
|
||||
const nodeDef = {
|
||||
kind: '',
|
||||
name: name,
|
||||
stableId: stableId,
|
||||
};
|
||||
if (combined) {
|
||||
nodeDef.combined = true;
|
||||
}
|
||||
super(nodeDef, children);
|
||||
}
|
||||
}
|
||||
|
||||
class ObjDiffNode extends ObjNode {
|
||||
constructor(name, diffType, children, combined, stableId) {
|
||||
super(name, children, combined, stableId);
|
||||
this.diff = { type: diffType };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +55,4 @@ function toPlainObject(theClass) {
|
||||
}
|
||||
}
|
||||
|
||||
export { Node, DiffNode, toPlainObject };
|
||||
export { Node, DiffNode, ObjNode, ObjDiffNode, toPlainObject };
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<TraceView :store="store" :file="file" :summarizer="summarizer" />
|
||||
<TraceView
|
||||
:store="store"
|
||||
:file="file"
|
||||
:summarizer="summarizer"
|
||||
:presentTags="[]"
|
||||
:presentErrors="[]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -52,13 +52,20 @@
|
||||
:ref="file.type"
|
||||
:store="store"
|
||||
:file="file"
|
||||
:presentTags="Object.freeze(presentTags)"
|
||||
:presentErrors="Object.freeze(presentErrors)"
|
||||
:dataViewFiles="dataViewFiles"
|
||||
@click="onDataViewFocus(file)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<overlay
|
||||
:presentTags="Object.freeze(presentTags)"
|
||||
:presentErrors="Object.freeze(presentErrors)"
|
||||
:tagAndErrorTraces="tagAndErrorTraces"
|
||||
:store="store"
|
||||
:ref="overlayRef"
|
||||
:searchTypes="searchTypes"
|
||||
v-if="dataLoaded"
|
||||
v-on:bottom-nav-height-change="handleBottomNavHeightChange"
|
||||
/>
|
||||
@@ -77,7 +84,8 @@ import FileType from './mixins/FileType.js';
|
||||
import SaveAsZip from './mixins/SaveAsZip';
|
||||
import FocusedDataViewFinder from './mixins/FocusedDataViewFinder';
|
||||
import {DIRECTION} from './utils/utils';
|
||||
import {NAVIGATION_STYLE} from './utils/consts';
|
||||
import Searchbar from './Searchbar.vue';
|
||||
import {NAVIGATION_STYLE, SEARCH_TYPE} from './utils/consts';
|
||||
|
||||
const APP_NAME = 'Winscope';
|
||||
|
||||
@@ -97,11 +105,17 @@ export default {
|
||||
simplifyNames: true,
|
||||
displayDefaults: true,
|
||||
navigationStyle: NAVIGATION_STYLE.GLOBAL,
|
||||
flickerTraceView: false,
|
||||
showFileTypes: [],
|
||||
}),
|
||||
overlayRef: 'overlay',
|
||||
mainContentStyle: {
|
||||
'padding-bottom': `${CONTENT_BOTTOM_PADDING}px`,
|
||||
},
|
||||
presentTags: [],
|
||||
presentErrors: [],
|
||||
searchTypes: [SEARCH_TYPE.TIMESTAMP],
|
||||
tagAndErrorTraces: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -113,8 +127,58 @@ export default {
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
window.removeEventListener('scroll', this.onScroll);
|
||||
},
|
||||
|
||||
methods: {
|
||||
/** Get states from either tag files or error files */
|
||||
getUpdatedStates(files) {
|
||||
var states = [];
|
||||
for (const file of files) {
|
||||
states.push(...file.data);
|
||||
}
|
||||
return states;
|
||||
},
|
||||
/** Get tags from all uploaded tag files*/
|
||||
getUpdatedTags() {
|
||||
var tagStates = this.getUpdatedStates(this.tagFiles);
|
||||
var tags = [];
|
||||
tagStates.forEach(tagState => {
|
||||
tagState.tags.forEach(tag => {
|
||||
tag.timestamp = tagState.timestamp;
|
||||
tags.push(tag);
|
||||
});
|
||||
});
|
||||
return tags;
|
||||
},
|
||||
/** Get tags from all uploaded error files*/
|
||||
getUpdatedErrors() {
|
||||
var errorStates = this.getUpdatedStates(this.errorFiles);
|
||||
var errors = [];
|
||||
//TODO (b/196201487) add check if errors empty
|
||||
errorStates.forEach(errorState => {
|
||||
errorState.errors.forEach(error => {
|
||||
error.timestamp = errorState.timestamp;
|
||||
errors.push(error);
|
||||
});
|
||||
});
|
||||
return errors;
|
||||
},
|
||||
/** Set flicker mode check for if there are tag/error traces uploaded*/
|
||||
shouldUpdateTagAndErrorTraces() {
|
||||
return this.tagFiles.length > 0 || this.errorFiles.length > 0;
|
||||
},
|
||||
/** Activate flicker search tab if tags/errors uploaded*/
|
||||
updateSearchTypes() {
|
||||
this.searchTypes = [SEARCH_TYPE.TIMESTAMP];
|
||||
if (this.tagAndErrorTraces) this.searchTypes.push(SEARCH_TYPE.TAG);
|
||||
},
|
||||
/** Filter data view files by current show settings*/
|
||||
updateShowFileTypes() {
|
||||
this.store.showFileTypes = this.dataViewFiles
|
||||
.filter((file) => file.show)
|
||||
.map(file => file.type);
|
||||
},
|
||||
clear() {
|
||||
this.store.showFileTypes = [];
|
||||
this.$store.commit('clearFiles');
|
||||
},
|
||||
onDataViewFocus(file) {
|
||||
@@ -139,7 +203,12 @@ export default {
|
||||
},
|
||||
onDataReady(files) {
|
||||
this.$store.dispatch('setFiles', files);
|
||||
this.tagAndErrorTraces = this.shouldUpdateTagAndErrorTraces();
|
||||
this.presentTags = this.getUpdatedTags();
|
||||
this.presentErrors = this.getUpdatedErrors();
|
||||
this.updateSearchTypes();
|
||||
this.updateFocusedView();
|
||||
this.updateShowFileTypes();
|
||||
},
|
||||
setStatus(status) {
|
||||
if (status) {
|
||||
@@ -158,7 +227,10 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
files() {
|
||||
return this.$store.getters.sortedFiles;
|
||||
return this.$store.getters.sortedFiles.map(file => {
|
||||
if (this.hasDataView(file)) file.show = true;
|
||||
return file;
|
||||
});
|
||||
},
|
||||
prettyDump() {
|
||||
return JSON.stringify(this.dump, null, 2);
|
||||
@@ -174,7 +246,16 @@ export default {
|
||||
return this.activeDataView;
|
||||
},
|
||||
dataViewFiles() {
|
||||
return this.files.filter((f) => this.hasDataView(f));
|
||||
return this.files.filter((file) => this.hasDataView(file));
|
||||
},
|
||||
tagFiles() {
|
||||
return this.$store.getters.tagFiles;
|
||||
},
|
||||
errorFiles() {
|
||||
return this.$store.getters.errorFiles;
|
||||
},
|
||||
timelineFiles() {
|
||||
return this.$store.getters.timelineFiles;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
@@ -187,6 +268,7 @@ export default {
|
||||
dataview: DataView,
|
||||
datainput: DataInput,
|
||||
dataadb: DataAdb,
|
||||
searchbar: Searchbar,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -194,7 +276,7 @@ export default {
|
||||
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap');
|
||||
|
||||
#app .md-app-container {
|
||||
/* Get rid of tranforms which prevent fixed position from being used */
|
||||
/* Get rid of transforms which prevent fixed position from being used */
|
||||
transform: none!important;
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -240,20 +322,10 @@ export default {
|
||||
margin-top: 1em
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
.data-inputs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -158,6 +158,9 @@ const TRACES = {
|
||||
'window_trace': {
|
||||
name: 'Window Manager',
|
||||
},
|
||||
'accessibility_trace': {
|
||||
name: 'Accessibility',
|
||||
},
|
||||
'layers_trace': {
|
||||
name: 'Surface Flinger',
|
||||
},
|
||||
|
||||
@@ -16,6 +16,11 @@
|
||||
<div @click="onClick($event)">
|
||||
<flat-card v-if="hasDataView(file)">
|
||||
<md-card-header>
|
||||
<button class="toggle-view-button" @click="toggleView">
|
||||
<i aria-hidden="true" class="md-icon md-theme-default material-icons">
|
||||
{{ isShowFileType(file.type) ? "expand_more" : "chevron_right" }}
|
||||
</i>
|
||||
</button>
|
||||
<md-card-header-text>
|
||||
<div class="md-title">
|
||||
<md-icon>{{ TRACE_ICONS[file.type] }}</md-icon>
|
||||
@@ -31,41 +36,47 @@
|
||||
</md-button>
|
||||
</md-card-header>
|
||||
<AccessibilityTraceView
|
||||
v-if="showInAccessibilityTraceView(file)"
|
||||
v-if="showInAccessibilityTraceView(file) && isShowFileType(file.type)"
|
||||
:store="store"
|
||||
:file="file"
|
||||
ref="view"
|
||||
/>
|
||||
<WindowManagerTraceView
|
||||
v-if="showInWindowManagerTraceView(file)"
|
||||
v-if="showInWindowManagerTraceView(file) && isShowFileType(file.type)"
|
||||
:store="store"
|
||||
:file="file"
|
||||
:presentTags="presentTags"
|
||||
:presentErrors="presentErrors"
|
||||
ref="view"
|
||||
/>
|
||||
<SurfaceFlingerTraceView
|
||||
v-else-if="showInSurfaceFlingerTraceView(file)"
|
||||
v-else-if="showInSurfaceFlingerTraceView(file) && isShowFileType(file.type)"
|
||||
:store="store"
|
||||
:file="file"
|
||||
:presentTags="presentTags"
|
||||
:presentErrors="presentErrors"
|
||||
ref="view"
|
||||
/>
|
||||
<transactionsview
|
||||
v-else-if="isTransactions(file)"
|
||||
v-else-if="isTransactions(file) && isShowFileType(file.type)"
|
||||
:trace="file"
|
||||
ref="view"
|
||||
/>
|
||||
<logview
|
||||
v-else-if="isLog(file)"
|
||||
v-else-if="isLog(file) && isShowFileType(file.type)"
|
||||
:file="file"
|
||||
ref="view"
|
||||
/>
|
||||
<traceview
|
||||
v-else-if="showInTraceView(file)"
|
||||
v-else-if="showInTraceView(file) && isShowFileType(file.type)"
|
||||
:store="store"
|
||||
:file="file"
|
||||
:presentTags="[]"
|
||||
:presentErrors="[]"
|
||||
ref="view"
|
||||
/>
|
||||
<div v-else>
|
||||
<h1 class="bad">Unrecognized DataType</h1>
|
||||
<h1 v-if="isShowFileType(file.type)" class="bad">Unrecognized DataType</h1>
|
||||
</div>
|
||||
|
||||
</flat-card>
|
||||
@@ -150,8 +161,23 @@ export default {
|
||||
// to component.
|
||||
this.$emit('click', e);
|
||||
},
|
||||
/** Filter data view files by current show settings */
|
||||
updateShowFileTypes() {
|
||||
this.store.showFileTypes = this.dataViewFiles
|
||||
.filter((file) => file.show)
|
||||
.map(file => file.type);
|
||||
},
|
||||
props: ['store', 'file'],
|
||||
/** Expand or collapse data view */
|
||||
toggleView() {
|
||||
this.file.show = !this.file.show;
|
||||
this.updateShowFileTypes();
|
||||
},
|
||||
/** Check if data view file should be shown */
|
||||
isShowFileType(type) {
|
||||
return this.store.showFileTypes.find(fileType => fileType===type);
|
||||
},
|
||||
},
|
||||
props: ['store', 'file', 'presentTags', 'presentErrors', 'dataViewFiles'],
|
||||
mixins: [FileType],
|
||||
components: {
|
||||
'traceview': TraceView,
|
||||
@@ -170,4 +196,18 @@ export default {
|
||||
font-size: 4em;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.toggle-view-button {
|
||||
background: none;
|
||||
color: inherit;
|
||||
border: none;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
padding-right: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.md-title {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -43,13 +43,28 @@
|
||||
{{c.long}}
|
||||
</md-tooltip>
|
||||
</div>
|
||||
<div class="flicker-tags" v-for="transition in transitions" :key="transition">
|
||||
<Arrow
|
||||
class="transition-arrow"
|
||||
:style="{color: transitionArrowColor(transition)}"
|
||||
/>
|
||||
<md-tooltip md-direction="right"> {{transitionTooltip(transition)}} </md-tooltip>
|
||||
</div>
|
||||
<div class="flicker-tags" v-for="error in errors" :key="error.message">
|
||||
<Arrow class="error-arrow"/>
|
||||
<md-tooltip md-direction="right"> Error: {{error.message}} </md-tooltip>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import Arrow from './components/TagDisplay/Arrow.vue';
|
||||
import {transitionMap} from './utils/consts.js';
|
||||
|
||||
export default {
|
||||
name: 'DefaultTreeElement',
|
||||
props: ['item', 'simplify-names'],
|
||||
props: ['item', 'simplify-names', 'errors', 'transitions'],
|
||||
methods: {
|
||||
chipClassForChip(c) {
|
||||
return [
|
||||
@@ -59,6 +74,15 @@ export default {
|
||||
(c.type?.toString() || c.class?.toString() || 'default'),
|
||||
];
|
||||
},
|
||||
transitionArrowColor(transition) {
|
||||
return transitionMap.get(transition).color;
|
||||
},
|
||||
transitionTooltip(transition) {
|
||||
return transitionMap.get(transition).desc;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Arrow,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -100,4 +124,12 @@ span {
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.flicker-tags {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.error-arrow {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {VueContext} from 'vue-context';
|
||||
import VueContext from 'vue-context';
|
||||
|
||||
export default {
|
||||
name: 'NodeContextMenu',
|
||||
|
||||
@@ -52,6 +52,15 @@
|
||||
>
|
||||
<div class="nav-content">
|
||||
<div class="">
|
||||
<searchbar
|
||||
class="search-bar"
|
||||
v-if="search"
|
||||
:searchTypes="searchTypes"
|
||||
:store="store"
|
||||
:presentTags="Object.freeze(presentTags)"
|
||||
:presentErrors="Object.freeze(presentErrors)"
|
||||
:timeline="mergedTimeline.timeline"
|
||||
/>
|
||||
<md-toolbar
|
||||
md-elevation="0"
|
||||
class="md-transparent">
|
||||
@@ -66,16 +75,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="active-timeline" v-show="minimized">
|
||||
<md-field class="seek-timestamp-field">
|
||||
<label>Search for timestamp</label>
|
||||
<md-input v-model="searchTimestamp"></md-input>
|
||||
</md-field>
|
||||
|
||||
<md-button
|
||||
@click="updateSearchForTimestamp"
|
||||
>Search</md-button>
|
||||
@click="toggleSearch()"
|
||||
class="drop-search"
|
||||
>Show/hide search bar</md-button>
|
||||
|
||||
<div class="active-timeline" v-show="minimized">
|
||||
<div
|
||||
class="active-timeline-icon"
|
||||
@click="$refs.navigationTypeSelection.$el
|
||||
@@ -148,6 +153,10 @@
|
||||
{{ seekTime }}
|
||||
</label>
|
||||
<timeline
|
||||
:store="store"
|
||||
:flickerMode="flickerMode"
|
||||
:tags="Object.freeze(tags)"
|
||||
:errors="Object.freeze(errors)"
|
||||
:timeline="Object.freeze(minimizedTimeline.timeline)"
|
||||
:selected-index="minimizedTimeline.selectedIndex"
|
||||
:scale="scale"
|
||||
@@ -271,16 +280,17 @@ import TimelineSelection from './TimelineSelection.vue';
|
||||
import DraggableDiv from './DraggableDiv.vue';
|
||||
import VideoView from './VideoView.vue';
|
||||
import MdIconOption from './components/IconSelection/IconSelectOption.vue';
|
||||
import Searchbar from './Searchbar.vue';
|
||||
import FileType from './mixins/FileType.js';
|
||||
import {NAVIGATION_STYLE} from './utils/consts';
|
||||
import {TRACE_ICONS} from '@/decode.js';
|
||||
import {TRACE_ICONS, FILE_TYPES} from '@/decode.js';
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
import {nanos_to_string, string_to_nanos} from './transform.js';
|
||||
import {nanos_to_string} from './transform.js';
|
||||
|
||||
export default {
|
||||
name: 'overlay',
|
||||
props: ['store'],
|
||||
props: ['store', 'presentTags', 'presentErrors', 'tagAndErrorTraces', 'searchTypes'],
|
||||
mixins: [FileType],
|
||||
data() {
|
||||
return {
|
||||
@@ -301,7 +311,9 @@ export default {
|
||||
crop: null,
|
||||
cropIntent: null,
|
||||
TRACE_ICONS,
|
||||
searchTimestamp: '',
|
||||
search: false,
|
||||
tags: [],
|
||||
errors: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
@@ -388,7 +400,8 @@ export default {
|
||||
default:
|
||||
const split = this.navigationStyle.split('-');
|
||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
||||
throw new Error('Unexpected navigation type');
|
||||
console.warn('Unexpected navigation type; fallback to global');
|
||||
return 'All timelines';
|
||||
}
|
||||
|
||||
const fileType = split[1];
|
||||
@@ -410,7 +423,8 @@ export default {
|
||||
default:
|
||||
const split = this.navigationStyle.split('-');
|
||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
||||
throw new Error('Unexpected navigation type');
|
||||
console.warn('Unexpected navigation type; fallback to global');
|
||||
return 'public';
|
||||
}
|
||||
|
||||
const fileType = split[1];
|
||||
@@ -419,6 +433,8 @@ export default {
|
||||
}
|
||||
},
|
||||
minimizedTimeline() {
|
||||
this.updateFlickerMode(this.navigationStyle);
|
||||
|
||||
if (this.navigationStyle === NAVIGATION_STYLE.GLOBAL) {
|
||||
return this.mergedTimeline;
|
||||
}
|
||||
@@ -436,12 +452,16 @@ export default {
|
||||
return this.mergedTimeline;
|
||||
}
|
||||
|
||||
if (this.navigationStyle.split('-')[0] === NAVIGATION_STYLE.TARGETED) {
|
||||
if (
|
||||
this.navigationStyle.split('-').length >= 2
|
||||
&& this.navigationStyle.split('-')[0] === NAVIGATION_STYLE.TARGETED
|
||||
) {
|
||||
return this.$store.state
|
||||
.traces[this.navigationStyle.split('-')[1]];
|
||||
}
|
||||
|
||||
throw new Error('Unexpected Navigation Style');
|
||||
console.warn('Unexpected navigation type; fallback to global');
|
||||
return this.mergedTimeline;
|
||||
},
|
||||
isCropped() {
|
||||
return this.crop != null &&
|
||||
@@ -450,6 +470,9 @@ export default {
|
||||
multipleTraces() {
|
||||
return this.timelineFiles.length > 1;
|
||||
},
|
||||
flickerMode() {
|
||||
return this.tags.length>0 || this.errors.length>0;
|
||||
},
|
||||
},
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
@@ -461,6 +484,9 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
toggleSearch() {
|
||||
this.search = !(this.search);
|
||||
},
|
||||
emitBottomHeightUpdate() {
|
||||
if (this.$refs.bottomNav) {
|
||||
const newHeight = this.$refs.bottomNav.$el.clientHeight;
|
||||
@@ -480,9 +506,10 @@ export default {
|
||||
timelines.push(file.timeline);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var timelineToAdvance = 0;
|
||||
while (timelineToAdvance !== undefined) {
|
||||
timelineToAdvance = undefined;
|
||||
let minTime = Infinity;
|
||||
let timelineToAdvance;
|
||||
|
||||
for (let i = 0; i < timelines.length; i++) {
|
||||
const timeline = timelines[i];
|
||||
@@ -608,7 +635,9 @@ export default {
|
||||
default:
|
||||
const split = this.navigationStyle.split('-');
|
||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
||||
throw new Error('Unexpected navigation type');
|
||||
console.warn('Unexpected navigation type; fallback to global');
|
||||
navigationStyleFilter = (f) => true;
|
||||
break;
|
||||
}
|
||||
|
||||
const fileType = split[1];
|
||||
@@ -618,6 +647,43 @@ export default {
|
||||
|
||||
this.$store.commit('setNavigationFilesFilter', navigationStyleFilter);
|
||||
},
|
||||
updateFlickerMode(style) {
|
||||
if (style === NAVIGATION_STYLE.GLOBAL ||
|
||||
style === NAVIGATION_STYLE.CUSTOM) {
|
||||
this.tags = this.presentTags;
|
||||
this.errors = this.presentErrors;
|
||||
|
||||
} else if (style === NAVIGATION_STYLE.FOCUSED) {
|
||||
if (this.focusedFile.timeline) {
|
||||
this.tags = this.getTagTimelineComponents(this.presentTags, this.focusedFile);
|
||||
this.errors = this.getTagTimelineComponents(this.presentErrors, this.focusedFile);
|
||||
}
|
||||
} else if (
|
||||
style.split('-').length >= 2 &&
|
||||
style.split('-')[0] === NAVIGATION_STYLE.TARGETED
|
||||
) {
|
||||
const file = this.$store.state.traces[style.split('-')[1]];
|
||||
if (file.timeline) {
|
||||
this.tags = this.getTagTimelineComponents(this.presentTags, file);
|
||||
this.errors = this.getTagTimelineComponents(this.presentErrors, file);
|
||||
}
|
||||
//Unexpected navigation type or no timeline present in file
|
||||
} else {
|
||||
console.warn('Unexpected timeline or navigation type; no flicker mode available');
|
||||
this.tags = [];
|
||||
this.errors = [];
|
||||
}
|
||||
},
|
||||
getTagTimelineComponents(items, file) {
|
||||
if (file.type===FILE_TYPES.SURFACE_FLINGER_TRACE) {
|
||||
return items.filter(item => item.layerId !== -1);
|
||||
}
|
||||
if (file.type===FILE_TYPES.WINDOW_MANAGER_TRACE) {
|
||||
return items.filter(item => item.taskId !== -1);
|
||||
}
|
||||
// if focused file is not one supported by tags/errors
|
||||
return [];
|
||||
},
|
||||
updateVideoOverlayWidth(width) {
|
||||
this.videoOverlayExtraWidth = width;
|
||||
},
|
||||
@@ -642,17 +708,6 @@ export default {
|
||||
clearSelection() {
|
||||
this.crop = null;
|
||||
},
|
||||
updateSearchForTimestamp() {
|
||||
if (/^\d+$/.test(this.searchTimestamp)) {
|
||||
var roundedTimestamp = parseInt(this.searchTimestamp);
|
||||
} else {
|
||||
var roundedTimestamp = string_to_nanos(this.searchTimestamp);
|
||||
}
|
||||
var closestTimestamp = this.mergedTimeline.timeline.reduce(function(prev, curr) {
|
||||
return (Math.abs(curr-roundedTimestamp) < Math.abs(prev-roundedTimestamp) ? curr : prev);
|
||||
});
|
||||
this.$store.dispatch('updateTimelineTime', parseInt(closestTimestamp));
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'timeline': Timeline,
|
||||
@@ -661,6 +716,7 @@ export default {
|
||||
'videoview': VideoView,
|
||||
'draggable-div': DraggableDiv,
|
||||
'md-icon-option': MdIconOption,
|
||||
'searchbar': Searchbar,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -864,4 +920,8 @@ export default {
|
||||
margin-bottom: 15px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.drop-search:hover {
|
||||
background-color: #9af39f;
|
||||
}
|
||||
</style>
|
||||
|
||||
308
tools/winscope/src/Searchbar.vue
Normal file
308
tools/winscope/src/Searchbar.vue
Normal file
@@ -0,0 +1,308 @@
|
||||
<!-- Copyright (C) 2017 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.
|
||||
-->
|
||||
<template>
|
||||
<md-content class="searchbar">
|
||||
<div class="search-timestamp" v-if="isTimestampSearch()">
|
||||
<md-button
|
||||
class="search-timestamp-button"
|
||||
@click="updateSearchForTimestamp"
|
||||
>
|
||||
Navigate to timestamp
|
||||
</md-button>
|
||||
<md-field class="search-input">
|
||||
<label>Enter timestamp</label>
|
||||
<md-input v-model="searchInput" @keyup.enter.native="updateSearchForTimestamp" />
|
||||
</md-field>
|
||||
</div>
|
||||
|
||||
<div class="dropdown-content" v-if="isTagSearch()">
|
||||
<table>
|
||||
<tr class="header">
|
||||
<th style="width: 10%">Global Start</th>
|
||||
<th style="width: 10%">Global End</th>
|
||||
<th style="width: 80%">Description</th>
|
||||
</tr>
|
||||
|
||||
<tr v-for="item in filteredTransitionsAndErrors" :key="item">
|
||||
<td
|
||||
v-if="isTransition(item)"
|
||||
class="inline-time"
|
||||
@click="
|
||||
setCurrentTimestamp(transitionStart(transitionTags(item.id)))
|
||||
"
|
||||
>
|
||||
<span>{{ transitionTags(item.id)[0].desc }}</span>
|
||||
</td>
|
||||
<td
|
||||
v-if="isTransition(item)"
|
||||
class="inline-time"
|
||||
@click="setCurrentTimestamp(transitionEnd(transitionTags(item.id)))"
|
||||
>
|
||||
<span>{{ transitionTags(item.id)[1].desc }}</span>
|
||||
</td>
|
||||
<td
|
||||
v-if="isTransition(item)"
|
||||
class="inline-transition"
|
||||
:style="{color: transitionTextColor(item.transition)}"
|
||||
@click="
|
||||
setCurrentTimestamp(transitionStart(transitionTags(item.id)))
|
||||
"
|
||||
>
|
||||
{{ transitionDesc(item.transition) }}
|
||||
</td>
|
||||
|
||||
<td
|
||||
v-if="!isTransition(item)"
|
||||
class="inline-time"
|
||||
@click="setCurrentTimestamp(item.timestamp)"
|
||||
>
|
||||
{{ errorDesc(item.timestamp) }}
|
||||
</td>
|
||||
<td v-if="!isTransition(item)">-</td>
|
||||
<td
|
||||
v-if="!isTransition(item)"
|
||||
class="inline-error"
|
||||
@click="setCurrentTimestamp(item.timestamp)"
|
||||
>
|
||||
Error: {{item.message}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<md-field class="search-input">
|
||||
<label
|
||||
>Filter by transition or error message. Click to navigate to closest
|
||||
timestamp in active timeline.</label
|
||||
>
|
||||
<md-input v-model="searchInput"></md-input>
|
||||
</md-field>
|
||||
</div>
|
||||
|
||||
<div class="tab-container">
|
||||
<md-button
|
||||
v-for="searchType in searchTypes"
|
||||
:key="searchType"
|
||||
@click="setSearchType(searchType)"
|
||||
:class="tabClass(searchType)"
|
||||
>
|
||||
{{ searchType }}
|
||||
</md-button>
|
||||
</div>
|
||||
</md-content>
|
||||
</template>
|
||||
<script>
|
||||
import { transitionMap, SEARCH_TYPE } from "./utils/consts";
|
||||
import { nanos_to_string, string_to_nanos } from "./transform";
|
||||
|
||||
const regExpTimestampSearch = new RegExp(/^\d+$/);
|
||||
|
||||
export default {
|
||||
name: "searchbar",
|
||||
props: ["store", "presentTags", "timeline", "presentErrors", "searchTypes"],
|
||||
data() {
|
||||
return {
|
||||
searchType: SEARCH_TYPE.TIMESTAMP,
|
||||
searchInput: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
/** Set search type depending on tab selected */
|
||||
setSearchType(searchType) {
|
||||
this.searchType = searchType;
|
||||
},
|
||||
/** Set tab class to determine color highlight for active tab */
|
||||
tabClass(searchType) {
|
||||
var isActive = (this.searchType === searchType) ? 'active' : 'inactive';
|
||||
return ['tab', isActive];
|
||||
},
|
||||
|
||||
/** Filter all the tags present in the trace by the searchbar input */
|
||||
filteredTags() {
|
||||
var tags = [];
|
||||
var filter = this.searchInput.toUpperCase();
|
||||
this.presentTags.forEach((tag) => {
|
||||
if (tag.transition.includes(filter)) tags.push(tag);
|
||||
});
|
||||
return tags;
|
||||
},
|
||||
/** Add filtered errors to filtered tags to integrate both into table*/
|
||||
filteredTagsAndErrors() {
|
||||
var tagsAndErrors = [...this.filteredTags()];
|
||||
var filter = this.searchInput.toUpperCase();
|
||||
this.presentErrors.forEach((error) => {
|
||||
if (error.message.includes(filter)) tagsAndErrors.push(error);
|
||||
});
|
||||
// sort into chronological order
|
||||
tagsAndErrors.sort((a, b) => (a.timestamp > b.timestamp ? 1 : -1));
|
||||
|
||||
return tagsAndErrors;
|
||||
},
|
||||
/** Each transition has two tags present
|
||||
* Isolate the tags for the desire transition
|
||||
* Add a desc to display the timestamps as strings
|
||||
*/
|
||||
transitionTags(id) {
|
||||
var tags = this.filteredTags().filter((tag) => tag.id === id);
|
||||
tags.forEach((tag) => {
|
||||
tag.desc = nanos_to_string(tag.timestamp);
|
||||
});
|
||||
return tags;
|
||||
},
|
||||
|
||||
/** Find the start as minimum timestamp in transition tags */
|
||||
transitionStart(tags) {
|
||||
var times = tags.map((tag) => tag.timestamp);
|
||||
return times[0];
|
||||
},
|
||||
/** Find the end as maximum timestamp in transition tags */
|
||||
transitionEnd(tags) {
|
||||
var times = tags.map((tag) => tag.timestamp);
|
||||
return times[times.length - 1];
|
||||
},
|
||||
/** Upon selecting a start/end tag in the dropdown;
|
||||
* navigates to that timestamp in the timeline */
|
||||
setCurrentTimestamp(timestamp) {
|
||||
this.$store.dispatch("updateTimelineTime", timestamp);
|
||||
},
|
||||
|
||||
/** Colour codes text of transition in dropdown */
|
||||
transitionTextColor(transition) {
|
||||
return transitionMap.get(transition).color;
|
||||
},
|
||||
/** Displays transition description rather than variable name */
|
||||
transitionDesc(transition) {
|
||||
return transitionMap.get(transition).desc;
|
||||
},
|
||||
/** Add a desc to display the error timestamps as strings */
|
||||
errorDesc(timestamp) {
|
||||
return nanos_to_string(timestamp);
|
||||
},
|
||||
|
||||
/** Navigates to closest timestamp in timeline to search input*/
|
||||
updateSearchForTimestamp() {
|
||||
if (regExpTimestampSearch.test(this.searchInput)) {
|
||||
var roundedTimestamp = parseInt(this.searchInput);
|
||||
} else {
|
||||
var roundedTimestamp = string_to_nanos(this.searchInput);
|
||||
}
|
||||
var closestTimestamp = this.timeline.reduce(function (prev, curr) {
|
||||
return Math.abs(curr - roundedTimestamp) <
|
||||
Math.abs(prev - roundedTimestamp)
|
||||
? curr
|
||||
: prev;
|
||||
});
|
||||
this.setCurrentTimestamp(closestTimestamp);
|
||||
},
|
||||
|
||||
isTagSearch() {
|
||||
return this.searchType === SEARCH_TYPE.TAG;
|
||||
},
|
||||
isTimestampSearch() {
|
||||
return this.searchType === SEARCH_TYPE.TIMESTAMP;
|
||||
},
|
||||
isTransition(item) {
|
||||
return item.stacktrace === undefined;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
filteredTransitionsAndErrors() {
|
||||
var ids = [];
|
||||
return this.filteredTagsAndErrors().filter((item) => {
|
||||
if (this.isTransition(item) && !ids.includes(item.id)) {
|
||||
item.transitionStart = true;
|
||||
ids.push(item.id);
|
||||
}
|
||||
return !this.isTransition(item) || this.isTransition(item) && item.transitionStart;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.searchbar {
|
||||
background-color: rgb(250, 243, 233) !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
bottom: 1px;
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
padding: 0px 20px 0px 20px;
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background-color: rgb(236, 222, 202);
|
||||
}
|
||||
|
||||
.tab.inactive {
|
||||
background-color: rgb(250, 243, 233);
|
||||
}
|
||||
|
||||
.search-timestamp {
|
||||
padding: 5px 20px 0px 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search-timestamp-button {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.dropdown-content {
|
||||
padding: 5px 20px 0px 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-content table {
|
||||
overflow-y: scroll;
|
||||
height: 150px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-content table td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.dropdown-content table th {
|
||||
text-align: left;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.inline-time:hover {
|
||||
background: #9af39f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inline-transition {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.inline-transition:hover {
|
||||
background: #9af39f;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inline-error {
|
||||
font-weight: bold;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.inline-error:hover {
|
||||
background: #9af39f;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
@@ -14,15 +14,21 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<TraceView :store="store" :file="file" :summarizer="summarizer" />
|
||||
<TraceView
|
||||
:store="store"
|
||||
:file="file"
|
||||
:summarizer="summarizer"
|
||||
:presentTags="presentTags"
|
||||
:presentErrors="presentErrors"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TraceView from '@/TraceView.vue';
|
||||
|
||||
export default {
|
||||
name: 'WindowManagerTraceView',
|
||||
props: ['store', 'file'],
|
||||
name: 'SurfaceFlingerTraceView',
|
||||
props: ['store', 'file', 'presentTags', 'presentErrors'],
|
||||
components: {
|
||||
TraceView,
|
||||
},
|
||||
|
||||
@@ -13,6 +13,22 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<template>
|
||||
<div class="timeline-container">
|
||||
<div class="tag-timeline" v-if="flickerMode" :style="maxOverlap">
|
||||
<transition-container
|
||||
class="container"
|
||||
v-for="transition in timelineTransitions"
|
||||
:key="transition.type"
|
||||
:startPos="transition.startPos"
|
||||
:startTime="transition.startTime"
|
||||
:endTime="transition.endTime"
|
||||
:width="transition.width"
|
||||
:color="transition.color"
|
||||
:overlap="transition.overlap"
|
||||
:tooltip="transition.tooltip"
|
||||
:store="store"
|
||||
/>
|
||||
</div>
|
||||
<svg
|
||||
width="100%"
|
||||
height="20"
|
||||
@@ -39,15 +55,29 @@
|
||||
:rx="corner"
|
||||
class="point selected"
|
||||
/>
|
||||
<line
|
||||
v-for="error in errorPositions"
|
||||
:key="error"
|
||||
:x1="`${error}%`"
|
||||
:x2="`${error}%`"
|
||||
y1="0"
|
||||
y2="18px"
|
||||
class="error"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import TimelineMixin from "./mixins/Timeline.js";
|
||||
import TransitionContainer from './components/TagDisplay/TransitionContainer.vue';
|
||||
|
||||
export default {
|
||||
name: "timeline",
|
||||
// TODO: Add indication of trim, at least for collasped timeline
|
||||
props: ["selectedIndex", "crop", "disabled"],
|
||||
components: {
|
||||
'transition-container': TransitionContainer,
|
||||
},
|
||||
props: ["selectedIndex", "crop", "disabled", "store"],
|
||||
data() {
|
||||
return {
|
||||
pointHeight: 15,
|
||||
@@ -55,7 +85,6 @@ export default {
|
||||
};
|
||||
},
|
||||
mixins: [TimelineMixin],
|
||||
methods: {},
|
||||
computed: {
|
||||
timestamps() {
|
||||
if (this.timeline.length == 1) {
|
||||
@@ -66,10 +95,35 @@ export default {
|
||||
selected() {
|
||||
return this.timeline[this.selectedIndex];
|
||||
},
|
||||
maxOverlap() {
|
||||
if (!this.timelineTransitions) {
|
||||
return {
|
||||
marginTop: '0px',
|
||||
}
|
||||
}
|
||||
var overlaps = [];
|
||||
for (const transition in this.timelineTransitions) {
|
||||
overlaps.push(this.timelineTransitions[transition].overlap);
|
||||
}
|
||||
return {
|
||||
marginTop: (Math.max(...overlaps)+1)*10 + 'px',
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.timeline-container {
|
||||
width: 100%;
|
||||
}
|
||||
.container:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.tag-timeline {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
height: 10px;
|
||||
}
|
||||
.timeline-svg .point {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -78,9 +132,13 @@ export default {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.timeline-svg:not(.disabled) .point.selected {
|
||||
fill: rgb(240, 59, 59);
|
||||
fill: #b2f6faff;
|
||||
}
|
||||
.timeline-svg.disabled .point.selected {
|
||||
fill: rgba(240, 59, 59, 0.596);
|
||||
}
|
||||
.error {
|
||||
stroke: rgb(255, 0, 0);
|
||||
stroke-width: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -42,6 +42,7 @@
|
||||
</md-checkbox>
|
||||
<md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
|
||||
<md-checkbox v-model="store.flattened">Flat</md-checkbox>
|
||||
<md-checkbox v-if="hasTagsOrErrors" v-model="store.flickerTraceView">Flicker</md-checkbox>
|
||||
<md-field md-inline class="filter">
|
||||
<label>Filter...</label>
|
||||
<md-input v-model="hierarchyPropertyFilterString"></md-input>
|
||||
@@ -55,6 +56,10 @@
|
||||
:selected="hierarchySelected"
|
||||
:filter="hierarchyFilter"
|
||||
:flattened="store.flattened"
|
||||
:onlyVisible="store.onlyVisible"
|
||||
:flickerTraceView="store.flickerTraceView"
|
||||
:presentTags="presentTags"
|
||||
:presentErrors="presentErrors"
|
||||
:items-clickable="true"
|
||||
:useGlobalCollapsedState="true"
|
||||
:simplify-names="store.simplifyNames"
|
||||
@@ -165,7 +170,7 @@ function findEntryInTree(tree, id) {
|
||||
|
||||
export default {
|
||||
name: 'traceview',
|
||||
props: ['store', 'file', 'summarizer'],
|
||||
props: ['store', 'file', 'summarizer', 'presentTags', 'presentErrors'],
|
||||
data() {
|
||||
return {
|
||||
propertyFilterString: '',
|
||||
@@ -306,10 +311,30 @@ export default {
|
||||
|
||||
return prevEntry;
|
||||
},
|
||||
|
||||
/** Performs check for id match between entry and present tags/errors
|
||||
* must be carried out for every present tag/error
|
||||
*/
|
||||
matchItems(flickerItems, entryItem) {
|
||||
var match = false;
|
||||
flickerItems.forEach(flickerItem => {
|
||||
if (flickerItem.taskId===entryItem.taskId || flickerItem.layerId===entryItem.id) {
|
||||
match = true;
|
||||
}
|
||||
});
|
||||
return match;
|
||||
},
|
||||
/** Returns check for id match between entry and present tags/errors */
|
||||
isEntryTagMatch(entryItem) {
|
||||
return this.matchItems(this.presentTags, entryItem) || this.matchItems(this.presentErrors, entryItem);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
|
||||
},
|
||||
destroyed() {
|
||||
this.store.flickerTraceView = false;
|
||||
},
|
||||
watch: {
|
||||
selectedIndex() {
|
||||
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
|
||||
@@ -337,9 +362,12 @@ export default {
|
||||
hierarchyFilter() {
|
||||
const hierarchyPropertyFilter =
|
||||
getFilter(this.hierarchyPropertyFilterString);
|
||||
return this.store.onlyVisible ? (c) => {
|
||||
var fil = this.store.onlyVisible ? (c) => {
|
||||
return c.isVisible && hierarchyPropertyFilter(c);
|
||||
} : hierarchyPropertyFilter;
|
||||
return this.store.flickerTraceView ? (c) => {
|
||||
return this.isEntryTagMatch(c);
|
||||
} : fil;
|
||||
},
|
||||
propertyFilter() {
|
||||
return getFilter(this.propertyFilterString);
|
||||
@@ -363,6 +391,9 @@ export default {
|
||||
|
||||
return summary;
|
||||
},
|
||||
hasTagsOrErrors() {
|
||||
return this.presentTags.length > 0 || this.presentErrors.length > 0;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'tree-view': TreeView,
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<button
|
||||
class="toggle-tree-btn"
|
||||
@click="toggleTree"
|
||||
v-if="!isLeaf"
|
||||
v-if="!isLeaf && !flattened"
|
||||
v-on:click.stop
|
||||
>
|
||||
<i aria-hidden="true" class="md-icon md-theme-default material-icons">
|
||||
@@ -50,7 +50,12 @@
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<DefaultTreeElement :item="item" :simplify-names="simplifyNames"/>
|
||||
<DefaultTreeElement
|
||||
:item="item"
|
||||
:simplify-names="simplifyNames"
|
||||
:errors="errors"
|
||||
:transitions="transitions"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="isCollapsed">
|
||||
@@ -78,7 +83,7 @@
|
||||
v-on:collapseAllOtherNodes="collapseAllOtherNodes"
|
||||
/>
|
||||
|
||||
<div class="children" v-if="children" v-show="!isCollapsed">
|
||||
<div class="children" v-if="children" v-show="!isCollapsed" :style="childrenIndentation()">
|
||||
<tree-view
|
||||
v-for="(c,i) in children"
|
||||
:item="c"
|
||||
@@ -87,7 +92,11 @@
|
||||
:key="i"
|
||||
:filter="childFilter(c)"
|
||||
:flattened="flattened"
|
||||
:onlyVisible="onlyVisible"
|
||||
:simplify-names="simplifyNames"
|
||||
:flickerTraceView="flickerTraceView"
|
||||
:presentTags="currentTags"
|
||||
:presentErrors="currentErrors"
|
||||
:force-flattened="applyingFlattened"
|
||||
v-show="filterMatches(c)"
|
||||
:items-clickable="itemsClickable"
|
||||
@@ -112,7 +121,6 @@
|
||||
<script>
|
||||
import DefaultTreeElement from './DefaultTreeElement.vue';
|
||||
import NodeContextMenu from './NodeContextMenu.vue';
|
||||
|
||||
import {DiffType} from './utils/diff.js';
|
||||
|
||||
/* in px, must be kept in sync with css, maybe find a better solution... */
|
||||
@@ -141,6 +149,10 @@ export default {
|
||||
'useGlobalCollapsedState',
|
||||
// Custom view to use to render the elements in the tree view
|
||||
'elementView',
|
||||
'onlyVisible',
|
||||
'flickerTraceView',
|
||||
'presentTags',
|
||||
'presentErrors',
|
||||
],
|
||||
data() {
|
||||
const isCollapsedByDefault = this.collapse ?? false;
|
||||
@@ -161,6 +173,10 @@ export default {
|
||||
[DiffType.MODIFIED]: '.',
|
||||
[DiffType.MOVED]: '.',
|
||||
},
|
||||
currentTags: [],
|
||||
currentErrors: [],
|
||||
transitions: [],
|
||||
errors: [],
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
@@ -177,6 +193,10 @@ export default {
|
||||
},
|
||||
currentTimestamp() {
|
||||
// Update anything that is required to change when time changes.
|
||||
this.currentTags = this.getCurrentItems(this.presentTags);
|
||||
this.currentErrors = this.getCurrentItems(this.presentErrors);
|
||||
this.transitions = this.getCurrentTransitions();
|
||||
this.errors = this.getCurrentErrorTags();
|
||||
this.updateCollapsedDiffClass();
|
||||
},
|
||||
isSelected(isSelected) {
|
||||
@@ -277,7 +297,7 @@ export default {
|
||||
}
|
||||
|
||||
if (!this.isLeaf && e.detail % 2 === 0) {
|
||||
// Double click collaspable node
|
||||
// Double click collapsable node
|
||||
this.toggleTree();
|
||||
} else {
|
||||
this.select();
|
||||
@@ -408,6 +428,64 @@ export default {
|
||||
child.closeAllChildrenContextMenus();
|
||||
}
|
||||
},
|
||||
childrenIndentation() {
|
||||
if (this.flattened || this.forceFlattened) {
|
||||
return {
|
||||
marginLeft: '0px',
|
||||
paddingLeft: '0px',
|
||||
marginTop: '0px',
|
||||
}
|
||||
} else {
|
||||
//Aligns border with collapse arrows
|
||||
return {
|
||||
marginLeft: '12px',
|
||||
paddingLeft: '11px',
|
||||
borderLeft: '1px solid rgb(238, 238, 238)',
|
||||
marginTop: '0px',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/** Check if tag/error id matches entry id */
|
||||
isIdMatch(a, b) {
|
||||
return a.taskId===b.taskId || a.layerId===b.id;
|
||||
},
|
||||
/** Performs check for id match between entry and present tags/errors
|
||||
* exits once match has been found
|
||||
*/
|
||||
matchItems(flickerItems) {
|
||||
var match = false;
|
||||
flickerItems.every(flickerItem => {
|
||||
if (this.isIdMatch(flickerItem, this.item)) {
|
||||
match = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return match;
|
||||
},
|
||||
/** Returns check for id match between entry and present tags/errors */
|
||||
isEntryTagMatch() {
|
||||
return this.matchItems(this.currentTags) || this.matchItems(this.currentErrors);
|
||||
},
|
||||
|
||||
getCurrentItems(items) {
|
||||
if (!items) return [];
|
||||
else return items.filter(item => item.timestamp===this.currentTimestamp);
|
||||
},
|
||||
getCurrentTransitions() {
|
||||
var transitions = [];
|
||||
var ids = [];
|
||||
this.currentTags.forEach(tag => {
|
||||
if (!ids.includes(tag.id) && this.isIdMatch(tag, this.item)) {
|
||||
transitions.push(tag.transition);
|
||||
ids.push(tag.id);
|
||||
}
|
||||
});
|
||||
return transitions;
|
||||
},
|
||||
getCurrentErrorTags() {
|
||||
return this.currentErrors.filter(error => this.isIdMatch(error, this.item));
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasDiff() {
|
||||
@@ -464,11 +542,20 @@ export default {
|
||||
return this.initialDepth || 0;
|
||||
},
|
||||
nodeOffsetStyle() {
|
||||
const offest = levelOffset * (this.depth + this.isLeaf) + 'px';
|
||||
const offset = levelOffset * (this.depth + this.isLeaf) + 'px';
|
||||
|
||||
var display = "";
|
||||
if (!this.item.timestamp
|
||||
&& this.flattened
|
||||
&& (this.onlyVisible && !this.item.isVisible ||
|
||||
this.flickerTraceView && !this.isEntryTagMatch())) {
|
||||
display = 'none';
|
||||
}
|
||||
|
||||
return {
|
||||
marginLeft: '-' + offest,
|
||||
paddingLeft: offest,
|
||||
marginLeft: '-' + offset,
|
||||
paddingLeft: offset,
|
||||
display: display,
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -567,14 +654,6 @@ export default {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.children {
|
||||
/* Aligns border with collapse arrows */
|
||||
margin-left: 12px;
|
||||
padding-left: 11px;
|
||||
border-left: 1px solid rgb(238, 238, 238);
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.tree-view .node.child-selected + .children {
|
||||
border-left: 1px solid #b4b4b4;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
-->
|
||||
|
||||
<template>
|
||||
<TraceView :store="store" :file="file" :summarizer="summarizer" />
|
||||
<TraceView
|
||||
:store="store"
|
||||
:file="file"
|
||||
:summarizer="summarizer"
|
||||
:presentTags="presentTags"
|
||||
:presentErrors="presentErrors"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -22,7 +28,7 @@ import TraceView from "@/TraceView.vue"
|
||||
|
||||
export default {
|
||||
name: "WindowManagerTraceView",
|
||||
props: ["store", "file"],
|
||||
props: ["store", "file", "presentTags", "presentErrors"],
|
||||
components: {
|
||||
TraceView
|
||||
},
|
||||
|
||||
33
tools/winscope/src/components/TagDisplay/Arrow.vue
Normal file
33
tools/winscope/src/components/TagDisplay/Arrow.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="arrow"/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'arrow',
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.arrow {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-top: 10px solid;
|
||||
}
|
||||
</style>
|
||||
113
tools/winscope/src/components/TagDisplay/TransitionContainer.vue
Normal file
113
tools/winscope/src/components/TagDisplay/TransitionContainer.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
<template>
|
||||
<div class="transition-container" :style="transitionStyle" @click="handleTransitionClick()">
|
||||
<md-tooltip md-direction="left"> {{tooltip}} </md-tooltip>
|
||||
<arrow class="arrow-start" :style="transitionComponentColor"/>
|
||||
<div class="connector" :style="transitionComponentColor"/>
|
||||
<arrow class="arrow-end" :style="transitionComponentColor"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Arrow from './Arrow.vue';
|
||||
import {LocalStore} from '../../localstore.js';
|
||||
|
||||
var transitionCount = false;
|
||||
|
||||
export default {
|
||||
name: 'transition-container',
|
||||
components: {
|
||||
'arrow': Arrow,
|
||||
},
|
||||
props: {
|
||||
'width': {
|
||||
type: Number,
|
||||
},
|
||||
'startPos': {
|
||||
type: Number,
|
||||
},
|
||||
'startTime': {
|
||||
type: Number,
|
||||
},
|
||||
'endTime': {
|
||||
type: Number,
|
||||
},
|
||||
'color': {
|
||||
type: String,
|
||||
},
|
||||
'overlap': {
|
||||
type: Number,
|
||||
},
|
||||
'tooltip': {
|
||||
type: String,
|
||||
},
|
||||
'store': {
|
||||
type: LocalStore,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleTransitionClick() {
|
||||
if (transitionCount) {
|
||||
this.$store.dispatch('updateTimelineTime', this.startTime);
|
||||
transitionCount = false;
|
||||
} else {
|
||||
this.$store.dispatch('updateTimelineTime', this.endTime);
|
||||
transitionCount = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
transitionStyle() {
|
||||
return {
|
||||
width: this.width + '%',
|
||||
left: this.startPos + '%',
|
||||
bottom: this.overlap * 100 + '%',
|
||||
}
|
||||
},
|
||||
transitionComponentColor() {
|
||||
return {
|
||||
borderTopColor: this.color,
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.transition-container {
|
||||
position: absolute;
|
||||
height: 15px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.arrow-start {
|
||||
position: absolute;
|
||||
left: 0%;
|
||||
}
|
||||
|
||||
.arrow-end {
|
||||
position: absolute;
|
||||
right: 0%;
|
||||
}
|
||||
|
||||
.connector {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 9px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
border-top: 1px solid;
|
||||
}
|
||||
</style>
|
||||
@@ -26,6 +26,8 @@ import jsonProtoDefsWl from 'WaylandSafePath/waylandtrace.proto';
|
||||
import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto';
|
||||
import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto';
|
||||
import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
|
||||
import jsonProtoDefsTags from 'platform_testing/libraries/flicker/src/com/android/server/wm/proto/tags.proto';
|
||||
import jsonProtoDefsErrors from 'platform_testing/libraries/flicker/src/com/android/server/wm/proto/errors.proto';
|
||||
import protobuf from 'protobufjs';
|
||||
import {transform_accessibility_trace} from './transform_accessibility.js';
|
||||
import {transform_transaction_trace} from './transform_transaction.js';
|
||||
@@ -53,6 +55,9 @@ import SurfaceFlingerDump from '@/dumps/SurfaceFlinger.ts';
|
||||
import WindowManagerDump from '@/dumps/WindowManager.ts';
|
||||
import WaylandDump from '@/dumps/Wayland.ts';
|
||||
|
||||
import TagTrace from '@/traces/TraceTag.ts';
|
||||
import ErrorTrace from '@/traces/TraceError.ts';
|
||||
|
||||
const AccessibilityTraceMessage = lookup_type(jsonProtoDefsAccessibility, 'com.android.server.accessibility.AccessibilityTraceFileProto');
|
||||
const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto');
|
||||
const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto');
|
||||
@@ -67,6 +72,8 @@ const LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, 'com.android.lau
|
||||
const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodClientsTraceFileProto');
|
||||
const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodServiceTraceFileProto');
|
||||
const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodManagerServiceTraceFileProto');
|
||||
const TagTraceMessage = lookup_type(jsonProtoDefsTags, 'com.android.server.wm.flicker.FlickerTagTraceProto');
|
||||
const ErrorTraceMessage = lookup_type(jsonProtoDefsErrors, 'com.android.server.wm.flicker.FlickerErrorTraceProto');
|
||||
|
||||
const ACCESSIBILITY_MAGIC_NUMBER = [0x09, 0x41, 0x31, 0x31, 0x59, 0x54, 0x52, 0x41, 0x43]; // .A11YTRAC
|
||||
const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
|
||||
@@ -79,6 +86,8 @@ const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0
|
||||
const IMC_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMCTRACE
|
||||
const IMS_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMSTRACE
|
||||
const IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMMTRACE
|
||||
const TAG_TRACE_MAGIC_NUMBER = [0x09, 0x54, 0x41, 0x47, 0x54, 0x52, 0x41, 0x43, 0x45]; //.TAGTRACE
|
||||
const ERROR_TRACE_MAGIC_NUMBER = [0x09, 0x45, 0x52, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; //.ERRORTRACE
|
||||
|
||||
const FILE_TYPES = Object.freeze({
|
||||
ACCESSIBILITY_TRACE: 'AccessibilityTrace',
|
||||
@@ -96,6 +105,8 @@ const FILE_TYPES = Object.freeze({
|
||||
IME_TRACE_CLIENTS: 'ImeTraceClients',
|
||||
IME_TRACE_SERVICE: 'ImeTrace InputMethodService',
|
||||
IME_TRACE_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
|
||||
TAG_TRACE: 'TagTrace',
|
||||
ERROR_TRACE: 'ErrorTrace',
|
||||
});
|
||||
|
||||
const WINDOW_MANAGER_ICON = 'view_compact';
|
||||
@@ -108,6 +119,8 @@ const SYSTEM_UI_ICON = 'filter_none';
|
||||
const LAUNCHER_ICON = 'filter_none';
|
||||
const IME_ICON = 'keyboard';
|
||||
const ACCESSIBILITY_ICON = 'filter_none';
|
||||
const TAG_ICON = 'details';
|
||||
const TRACE_ERROR_ICON = 'warning';
|
||||
|
||||
const FILE_ICONS = {
|
||||
[FILE_TYPES.ACCESSIBILITY_TRACE]: ACCESSIBILITY_ICON,
|
||||
@@ -125,6 +138,8 @@ const FILE_ICONS = {
|
||||
[FILE_TYPES.IME_TRACE_CLIENTS]: IME_ICON,
|
||||
[FILE_TYPES.IME_TRACE_SERVICE]: IME_ICON,
|
||||
[FILE_TYPES.IME_TRACE_MANAGERSERVICE]: IME_ICON,
|
||||
[FILE_TYPES.TAG_TRACE]: TAG_ICON,
|
||||
[FILE_TYPES.ERROR_TRACE]: TRACE_ERROR_ICON,
|
||||
};
|
||||
|
||||
function oneOf(dataType) {
|
||||
@@ -144,6 +159,8 @@ const TRACE_TYPES = Object.freeze({
|
||||
IME_CLIENTS: 'ImeTrace Clients',
|
||||
IME_SERVICE: 'ImeTrace InputMethodService',
|
||||
IME_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
|
||||
TAG: 'TagTrace',
|
||||
ERROR: 'ErrorTrace',
|
||||
});
|
||||
|
||||
const TRACE_INFO = {
|
||||
@@ -221,6 +238,18 @@ const TRACE_INFO = {
|
||||
files: [oneOf(FILE_TYPES.IME_TRACE_MANAGERSERVICE)],
|
||||
constructor: ImeTraceManagerService,
|
||||
},
|
||||
[TRACE_TYPES.TAG]: {
|
||||
name: 'Tag',
|
||||
icon: TAG_ICON,
|
||||
files: [oneOf(FILE_TYPES.TAG_TRACE)],
|
||||
constructor: TagTrace,
|
||||
},
|
||||
[TRACE_TYPES.ERROR]: {
|
||||
name: 'Error',
|
||||
icon: TRACE_ERROR_ICON,
|
||||
files: [oneOf(FILE_TYPES.ERROR_TRACE)],
|
||||
constructor: ErrorTrace,
|
||||
},
|
||||
};
|
||||
|
||||
const DUMP_TYPES = Object.freeze({
|
||||
@@ -262,6 +291,8 @@ export const TRACE_ICONS = {
|
||||
[TRACE_TYPES.IME_CLIENTS]: IME_ICON,
|
||||
[TRACE_TYPES.IME_SERVICE]: IME_ICON,
|
||||
[TRACE_TYPES.IME_MANAGERSERVICE]: IME_ICON,
|
||||
[TRACE_TYPES.TAG_TRACE]: TAG_ICON,
|
||||
[TRACE_TYPES.ERROR_TRACE]: TRACE_ERROR_ICON,
|
||||
|
||||
[DUMP_TYPES.WINDOW_MANAGER]: WINDOW_MANAGER_ICON,
|
||||
[DUMP_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
|
||||
@@ -275,7 +306,7 @@ const FILE_DECODERS = {
|
||||
decoder: protoDecoder,
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.ACCESSIBILITY_TRACE,
|
||||
protoType: AccessibilityTraceMessage,
|
||||
objTypeProto: AccessibilityTraceMessage,
|
||||
transform: transform_accessibility_trace,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -285,7 +316,7 @@ const FILE_DECODERS = {
|
||||
decoder: protoDecoder,
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.WINDOW_MANAGER_TRACE,
|
||||
protoType: WmTraceMessage,
|
||||
objTypeProto: WmTraceMessage,
|
||||
transform: WindowManagerTrace.fromProto,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -296,7 +327,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.SURFACE_FLINGER_TRACE,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: SfTraceMessage,
|
||||
objTypeProto: SfTraceMessage,
|
||||
transform: SurfaceFlingerTrace.fromProto,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -307,7 +338,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.WAYLAND_TRACE,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: WaylandTraceMessage,
|
||||
objTypeProto: WaylandTraceMessage,
|
||||
transform: transform_wayland_trace,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -318,8 +349,8 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.SURFACE_FLINGER_DUMP,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: SfDumpMessage,
|
||||
transform: SurfaceFlingerDump.fromProto,
|
||||
objTypeProto: [SfDumpMessage, SfTraceMessage],
|
||||
transform: [SurfaceFlingerDump.fromProto, SurfaceFlingerTrace.fromProto],
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
@@ -329,7 +360,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.WINDOW_MANAGER_DUMP,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: WmDumpMessage,
|
||||
objTypeProto: WmDumpMessage,
|
||||
transform: WindowManagerDump.fromProto,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -340,7 +371,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.WAYLAND_DUMP,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: WaylandDumpMessage,
|
||||
objTypeProto: WaylandDumpMessage,
|
||||
transform: transform_wl_outputstate,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -360,7 +391,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.TRANSACTIONS_TRACE,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: SfTransactionTraceMessage,
|
||||
objTypeProto: SfTransactionTraceMessage,
|
||||
transform: transform_transaction_trace,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -371,7 +402,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.PROTO_LOG,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: ProtoLogMessage,
|
||||
objTypeProto: ProtoLogMessage,
|
||||
transform: transformProtolog,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -382,7 +413,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.SYSTEM_UI,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: SystemUiTraceMessage,
|
||||
objTypeProto: SystemUiTraceMessage,
|
||||
transform: transform_sysui_trace,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -393,7 +424,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.LAUNCHER,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: LauncherTraceMessage,
|
||||
objTypeProto: LauncherTraceMessage,
|
||||
transform: transform_launcher_trace,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -404,7 +435,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.IME_TRACE_CLIENTS,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: InputMethodClientsTraceMessage,
|
||||
objTypeProto: InputMethodClientsTraceMessage,
|
||||
transform: transform_ime_trace_clients,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -415,7 +446,7 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.IME_TRACE_SERVICE,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: InputMethodServiceTraceMessage,
|
||||
objTypeProto: InputMethodServiceTraceMessage,
|
||||
transform: transform_ime_trace_service,
|
||||
timeline: true,
|
||||
},
|
||||
@@ -426,11 +457,31 @@ const FILE_DECODERS = {
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.IME_TRACE_MANAGERSERVICE,
|
||||
mime: 'application/octet-stream',
|
||||
protoType: InputMethodManagerServiceTraceMessage,
|
||||
objTypeProto: InputMethodManagerServiceTraceMessage,
|
||||
transform: transform_ime_trace_managerservice,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
[FILE_TYPES.TAG_TRACE]: {
|
||||
name: 'Tag trace',
|
||||
decoder: protoDecoder,
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.TAG_TRACE,
|
||||
objTypeProto: TagTraceMessage,
|
||||
transform: TagTrace.fromProto,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
[FILE_TYPES.ERROR_TRACE]: {
|
||||
name: 'Error trace',
|
||||
decoder: protoDecoder,
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.ERROR_TRACE,
|
||||
objTypeProto: ErrorTraceMessage,
|
||||
transform: ErrorTrace.fromProto,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function lookup_type(protoPath, type) {
|
||||
@@ -472,11 +523,33 @@ function modifyProtoFields(protoObj, displayDefaults) {
|
||||
}
|
||||
|
||||
function decodeAndTransformProto(buffer, params, displayDefaults) {
|
||||
const decoded = params.protoType.decode(buffer);
|
||||
var objTypesProto = [];
|
||||
var transforms = [];
|
||||
if (!Array.isArray(params.objTypeProto)) {
|
||||
objTypesProto = [params.objTypeProto];
|
||||
transforms = [params.transform];
|
||||
} else {
|
||||
objTypesProto = params.objTypeProto;
|
||||
transforms = params.transform;
|
||||
}
|
||||
// each trace or dump may have different processors, for example, until S, SF dumps
|
||||
// returne a list of layers and winscope built a [LayerTraceEntry] from them.
|
||||
// From S onwards, returns a LayerTrace object, iterating over multiple items allows
|
||||
// winscope to handle both the new and legacy formats
|
||||
// TODO Refactor the decode.js code into a set of decoders to clean up the code
|
||||
for (var x = 0; x < objTypesProto.length; x++) {
|
||||
const objType = objTypesProto[x];
|
||||
const transform = transforms[x];
|
||||
try {
|
||||
const decoded = objType.decode(buffer);
|
||||
modifyProtoFields(decoded, displayDefaults);
|
||||
const transformed = params.transform(decoded);
|
||||
|
||||
const transformed = transform(decoded);
|
||||
return transformed;
|
||||
} catch (e) {
|
||||
// check next parser
|
||||
}
|
||||
}
|
||||
throw new UndetectableFileType('Unable to parse file');
|
||||
}
|
||||
|
||||
function protoDecoder(buffer, params, fileName, store) {
|
||||
@@ -568,6 +641,12 @@ function detectAndDecode(buffer, fileName, store) {
|
||||
if (arrayStartsWith(buffer, IMM_TRACE_MAGIC_NUMBER)) {
|
||||
return decodedFile(FILE_TYPES.IME_TRACE_MANAGERSERVICE, buffer, fileName, store);
|
||||
}
|
||||
if (arrayStartsWith(buffer, TAG_TRACE_MAGIC_NUMBER)) {
|
||||
return decodedFile(FILE_TYPES.TAG_TRACE, buffer, fileName, store);
|
||||
}
|
||||
if (arrayStartsWith(buffer, ERROR_TRACE_MAGIC_NUMBER)) {
|
||||
return decodedFile(FILE_TYPES.ERROR_TRACE, buffer, fileName, store);
|
||||
}
|
||||
|
||||
// TODO(b/169305853): Add magic number at beginning of file for better auto detection
|
||||
for (const [filetype, condition] of [
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
|
||||
import DumpBase from "./DumpBase";
|
||||
import LayersTraceEntry from '../flickerlib/layers/LayerTraceEntry'
|
||||
import LayersTraceEntry from '../flickerlib/layers/LayerTraceEntry';
|
||||
import LayersTrace from '../flickerlib/LayersTrace';
|
||||
|
||||
export default class SurfaceFlinger extends DumpBase {
|
||||
sfDumpFile: any;
|
||||
@@ -32,11 +33,14 @@ export default class SurfaceFlinger extends DumpBase {
|
||||
return DUMP_TYPES.SURFACE_FLINGER;
|
||||
}
|
||||
|
||||
static fromProto(proto: any): LayersTraceEntry {
|
||||
return LayersTraceEntry.fromProto(
|
||||
static fromProto(proto: any): LayersTrace {
|
||||
const source = null;
|
||||
const entry = LayersTraceEntry.fromProto(
|
||||
/* protos */ proto.layers,
|
||||
/* displays */ proto.displays,
|
||||
/* timestamp */ 0,
|
||||
/* hwcBlob */ ""
|
||||
);
|
||||
return new LayersTrace([entry], source);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,8 @@ export default class WindowManager extends DumpBase {
|
||||
}
|
||||
|
||||
static fromProto(proto: any): WindowManagerTrace {
|
||||
return WindowManagerTrace.fromDump(proto);
|
||||
const source = null;
|
||||
const state = WindowManagerTrace.fromDump(proto);
|
||||
return new WindowManagerTrace([state], source);
|
||||
}
|
||||
}
|
||||
33
tools/winscope/src/flickerlib/ErrorTrace.ts
Normal file
33
tools/winscope/src/flickerlib/ErrorTrace.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020, 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 { ErrorTrace } from "./common"
|
||||
import ErrorState from "./errors/ErrorState"
|
||||
|
||||
ErrorTrace.fromProto = function (proto: any) {
|
||||
const states = [];
|
||||
for (const stateProto of proto.states) {
|
||||
const transformedState = ErrorState.fromProto(
|
||||
stateProto.errors,
|
||||
stateProto.timestamp);
|
||||
|
||||
states.push(transformedState);
|
||||
}
|
||||
const source = null;
|
||||
return new ErrorTrace(states, source);
|
||||
}
|
||||
|
||||
export default ErrorTrace;
|
||||
@@ -22,6 +22,7 @@ LayersTrace.fromProto = function (proto: any): LayersTrace {
|
||||
for (const entryProto of proto.entry) {
|
||||
const transformedEntry = LayerTraceEntry.fromProto(
|
||||
/* protos */ entryProto.layers.layers,
|
||||
/* displays */ entryProto.displays,
|
||||
/* timestamp */ entryProto.elapsedRealtimeNanos,
|
||||
/* hwcBlob */ entryProto.hwcBlob);
|
||||
|
||||
|
||||
@@ -44,6 +44,13 @@ export default class ObjectFormatter {
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the true properties of an entry excluding functions, kotlin gernerated
|
||||
* variables, explicitly excluded properties, and flicker objects already in
|
||||
* the hierarchy that shouldn't be traversed when formatting the entry
|
||||
* @param entry The entry for which we want to get the properties for
|
||||
* @return The "true" properties of the entry as described above
|
||||
*/
|
||||
static getProperties(entry: any): string[] {
|
||||
var props = [];
|
||||
let obj = entry;
|
||||
@@ -58,13 +65,12 @@ export default class ObjectFormatter {
|
||||
if (it.startsWith(`_`)) return false;
|
||||
// some predefined properties used only internally (e.g., children, ref, diff)
|
||||
if (this.INVALID_ELEMENT_PROPERTIES.includes(it)) return false;
|
||||
// Flicker object properties or arrays
|
||||
if (!entry[it]) return false;
|
||||
const value = entry[it]
|
||||
|
||||
const value = entry[it];
|
||||
// only non-empty arrays of non-flicker objects (otherwise they are in hierarchy)
|
||||
if (Array.isArray(value) && value.length > 0) return !value[0].stableId
|
||||
if (Array.isArray(value) && value.length > 0) return !value[0].stableId;
|
||||
// non-flicker object
|
||||
return !value.stableId;
|
||||
return !(value?.stableId);
|
||||
});
|
||||
properties.forEach(function (prop) {
|
||||
if (typeof(entry[prop]) !== 'function' && props.indexOf(prop) === -1) {
|
||||
@@ -76,6 +82,12 @@ export default class ObjectFormatter {
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a Winscope entry to be displayed in the UI
|
||||
* Accounts for different user display settings (e.g. hiding empty/default values)
|
||||
* @param obj The raw object to format
|
||||
* @return The formatted object
|
||||
*/
|
||||
static format(obj: any): {} {
|
||||
const properties = this.getProperties(obj);
|
||||
const sortedProperties = properties.sort()
|
||||
@@ -85,7 +97,14 @@ export default class ObjectFormatter {
|
||||
const key = entry;
|
||||
const value: any = obj[key];
|
||||
|
||||
if (value || (this.displayDefaults && value !== undefined && value !== null)) {
|
||||
if (value === null || value === undefined) {
|
||||
if (this.displayDefaults) {
|
||||
result[key] = value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (value || this.displayDefaults) {
|
||||
// flicker obj
|
||||
if (value.prettyPrint) {
|
||||
const isEmpty = value.isEmpty === true;
|
||||
|
||||
33
tools/winscope/src/flickerlib/TagTrace.ts
Normal file
33
tools/winscope/src/flickerlib/TagTrace.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2020, 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 { TagTrace } from "./common"
|
||||
import TagState from "./tags/TagState"
|
||||
|
||||
TagTrace.fromProto = function (proto: any): TagTrace {
|
||||
const states = [];
|
||||
for (const stateProto of proto.states) {
|
||||
const transformedState = TagState.fromProto(
|
||||
stateProto.timestamp,
|
||||
stateProto.tags);
|
||||
|
||||
states.push(transformedState);
|
||||
}
|
||||
const source = null;
|
||||
return new TagTrace(states, source);
|
||||
}
|
||||
|
||||
export default TagTrace;
|
||||
@@ -66,6 +66,8 @@ const Matrix = require('flicker').com.android.server.wm.traces.common.layers.
|
||||
Transform.Matrix;
|
||||
const Transform = require('flicker').com.android.server.wm.traces.common.
|
||||
layers.Transform;
|
||||
const Display = require('flicker').com.android.server.wm.traces.common.
|
||||
layers.Display;
|
||||
|
||||
// Common
|
||||
const Size = require('flicker').com.android.server.wm.traces.common.Size;
|
||||
@@ -76,6 +78,16 @@ const Rect = require('flicker').com.android.server.wm.traces.common.Rect;
|
||||
const RectF = require('flicker').com.android.server.wm.traces.common.RectF;
|
||||
const Region = require('flicker').com.android.server.wm.traces.common.Region;
|
||||
|
||||
//Tags
|
||||
const Tag = require('flicker').com.android.server.wm.traces.common.tags.Tag;
|
||||
const TagState = require('flicker').com.android.server.wm.traces.common.tags.TagState;
|
||||
const TagTrace = require('flicker').com.android.server.wm.traces.common.tags.TagTrace;
|
||||
|
||||
//Errors
|
||||
const Error = require('flicker').com.android.server.wm.traces.common.errors.Error;
|
||||
const ErrorState = require('flicker').com.android.server.wm.traces.common.errors.ErrorState;
|
||||
const ErrorTrace = require('flicker').com.android.server.wm.traces.common.errors.ErrorTrace;
|
||||
|
||||
const EMPTY_BUFFER = new Buffer(0, 0, 0, 0);
|
||||
const EMPTY_COLOR = new Color(-1, -1, -1, 0);
|
||||
const EMPTY_RECT = new Rect(0, 0, 0, 0);
|
||||
@@ -226,6 +238,15 @@ export {
|
||||
LayersTrace,
|
||||
Transform,
|
||||
Matrix,
|
||||
Display,
|
||||
// Tags
|
||||
Tag,
|
||||
TagState,
|
||||
TagTrace,
|
||||
// Errors
|
||||
Error,
|
||||
ErrorState,
|
||||
ErrorTrace,
|
||||
// Common
|
||||
Size,
|
||||
Buffer,
|
||||
|
||||
31
tools/winscope/src/flickerlib/errors/Error.ts
Normal file
31
tools/winscope/src/flickerlib/errors/Error.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { Error } from "../common"
|
||||
|
||||
Error.fromProto = function (proto: any): Error {
|
||||
const error = new Error(
|
||||
proto.stacktrace,
|
||||
proto.message,
|
||||
proto.layerId,
|
||||
proto.windowToken,
|
||||
proto.taskId
|
||||
);
|
||||
return error;
|
||||
}
|
||||
|
||||
export default Error;
|
||||
26
tools/winscope/src/flickerlib/errors/ErrorState.ts
Normal file
26
tools/winscope/src/flickerlib/errors/ErrorState.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020, 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 { ErrorState } from "../common";
|
||||
import Error from './Error';
|
||||
|
||||
ErrorState.fromProto = function (protos: any[], timestamp: number): ErrorState {
|
||||
const errors = protos.map(it => Error.fromProto(it));
|
||||
const state = new ErrorState(errors, timestamp);
|
||||
return state;
|
||||
}
|
||||
|
||||
export default ErrorState;
|
||||
@@ -18,9 +18,11 @@ import LayersTrace from './LayersTrace';
|
||||
import WindowManagerState from './WindowManagerState';
|
||||
import WindowManagerTrace from './WindowManagerTrace';
|
||||
import ObjectFormatter from './ObjectFormatter';
|
||||
import TagTrace from './TagTrace';
|
||||
import ErrorTrace from './ErrorTrace';
|
||||
/**
|
||||
* Entry point into the flickerlib for Winscope.
|
||||
* Expose everything we want Winscope to have access to here.
|
||||
*/
|
||||
export {ObjectFormatter, LayersTrace, WindowManagerState, WindowManagerTrace};
|
||||
export {ObjectFormatter, LayersTrace, WindowManagerState, WindowManagerTrace, TagTrace, ErrorTrace};
|
||||
|
||||
|
||||
@@ -14,13 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { LayerTraceEntry, LayerTraceEntryBuilder } from "../common"
|
||||
import { Display, LayerTraceEntry, LayerTraceEntryBuilder, toRect, toSize, toTransform } from "../common"
|
||||
import Layer from './Layer'
|
||||
import { VISIBLE_CHIP, RELATIVE_Z_PARENT_CHIP, MISSING_LAYER } from '../treeview/Chips'
|
||||
|
||||
LayerTraceEntry.fromProto = function (protos: any[], timestamp: number, hwcBlob: string, where: string = ''): LayerTraceEntry {
|
||||
LayerTraceEntry.fromProto = function (protos: any[], displayProtos: any[],
|
||||
timestamp: number, hwcBlob: string, where: string = ''): LayerTraceEntry {
|
||||
const layers = protos.map(it => Layer.fromProto(it));
|
||||
const builder = new LayerTraceEntryBuilder(timestamp, layers, hwcBlob, where);
|
||||
const displays = (displayProtos || []).map(it => newDisplay(it));
|
||||
const builder = new LayerTraceEntryBuilder(timestamp, layers, displays, hwcBlob, where);
|
||||
const entry: LayerTraceEntry = builder.build();
|
||||
|
||||
updateChildren(entry);
|
||||
@@ -63,4 +65,15 @@ function updateChildren(entry: LayerTraceEntry) {
|
||||
});
|
||||
}
|
||||
|
||||
function newDisplay(proto: any): Display {
|
||||
return new Display(
|
||||
proto.id,
|
||||
proto.name,
|
||||
proto.layerStack,
|
||||
toSize(proto.size),
|
||||
toRect(proto.layerStackSpaceRect),
|
||||
toTransform(proto.transform)
|
||||
)
|
||||
}
|
||||
|
||||
export default LayerTraceEntry;
|
||||
|
||||
44
tools/winscope/src/flickerlib/tags/Tag.ts
Normal file
44
tools/winscope/src/flickerlib/tags/Tag.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 { Tag } from "../common";
|
||||
import TransitionType from "./TransitionType";
|
||||
|
||||
const transitionTypeMap = new Map([
|
||||
['ROTATION', TransitionType.ROTATION],
|
||||
['PIP_ENTER', TransitionType.PIP_ENTER],
|
||||
['PIP_RESIZE', TransitionType.PIP_RESIZE],
|
||||
['PIP_EXIT', TransitionType.PIP_EXIT],
|
||||
['APP_LAUNCH', TransitionType.APP_LAUNCH],
|
||||
['APP_CLOSE', TransitionType.APP_CLOSE],
|
||||
['IME_APPEAR', TransitionType.IME_APPEAR],
|
||||
['IME_DISAPPEAR', TransitionType.IME_DISAPPEAR],
|
||||
]);
|
||||
|
||||
Tag.fromProto = function (proto: any): Tag {
|
||||
const tag = new Tag(
|
||||
proto.id,
|
||||
transitionTypeMap.get(proto.transition),
|
||||
proto.isStartTag,
|
||||
proto.layerId,
|
||||
proto.windowToken,
|
||||
proto.taskId
|
||||
);
|
||||
return tag;
|
||||
};
|
||||
|
||||
export default Tag;
|
||||
26
tools/winscope/src/flickerlib/tags/TagState.ts
Normal file
26
tools/winscope/src/flickerlib/tags/TagState.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2020, 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 { TagState } from "../common";
|
||||
import Tag from './Tag';
|
||||
|
||||
TagState.fromProto = function (timestamp: number, protos: any[]): TagState {
|
||||
const tags = protos.map(it => Tag.fromProto(it));
|
||||
const state = new TagState(timestamp, tags);
|
||||
return state;
|
||||
}
|
||||
|
||||
export default TagState;
|
||||
28
tools/winscope/src/flickerlib/tags/TransitionType.ts
Normal file
28
tools/winscope/src/flickerlib/tags/TransitionType.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
enum TransitionType {
|
||||
ROTATION = 'ROTATION',
|
||||
PIP_ENTER = 'PIP_ENTER',
|
||||
PIP_RESIZE ='PIP_RESIZE',
|
||||
PIP_EXIT = 'PIP_EXIT',
|
||||
APP_LAUNCH = 'APP_LAUNCH',
|
||||
APP_CLOSE = 'APP_CLOSE',
|
||||
IME_APPEAR = 'IME_APPEAR',
|
||||
IME_DISAPPEAR = 'IME_DISAPPEAR',
|
||||
};
|
||||
|
||||
export default TransitionType;
|
||||
@@ -66,6 +66,8 @@ const store = new Vuex.Store({
|
||||
dumps: {},
|
||||
excludeFromTimeline: [
|
||||
TRACE_TYPES.PROTO_LOG,
|
||||
TRACE_TYPES.TAG,
|
||||
TRACE_TYPES.ERROR
|
||||
],
|
||||
activeFile: null,
|
||||
focusedFile: null,
|
||||
@@ -93,6 +95,14 @@ const store = new Vuex.Store({
|
||||
return Object.values(state.traces)
|
||||
.filter(file => !state.excludeFromTimeline.includes(file.type));
|
||||
},
|
||||
tagFiles(state, getters) {
|
||||
return Object.values(state.traces)
|
||||
.filter(file => file.type === TRACE_TYPES.TAG);
|
||||
},
|
||||
errorFiles(state, getters) {
|
||||
return Object.values(state.traces)
|
||||
.filter(file => file.type === TRACE_TYPES.ERROR);
|
||||
},
|
||||
sortedTimelineFiles(state, getters) {
|
||||
return sortFiles(getters.timelineFiles);
|
||||
},
|
||||
|
||||
@@ -33,14 +33,37 @@ export default {
|
||||
|
||||
document.body.removeChild(a);
|
||||
},
|
||||
/**
|
||||
* Returns the file name, if the file has an extension use its default,
|
||||
* otherwise use ".mp4" for screen recording (name from proxy script) and
|
||||
* ".winscope" for traces
|
||||
* @param {*} fileName
|
||||
*/
|
||||
getFileName(fileName) {
|
||||
var re = /(?:\.([^.]+))?$/;
|
||||
var extension = re.exec(fileName)[1];
|
||||
if (!extension) {
|
||||
extension = "";
|
||||
}
|
||||
switch (extension) {
|
||||
case "": {
|
||||
if (fileName == "Screen recording") {
|
||||
return fileName + ".mp4"
|
||||
}
|
||||
return fileName + ".winscope"
|
||||
}
|
||||
default: return fileName
|
||||
}
|
||||
},
|
||||
async downloadAsZip(traces) {
|
||||
const zip = new JSZip();
|
||||
|
||||
for (const trace of traces) {
|
||||
const traceFolder = zip.folder(trace.type);
|
||||
for (const file of trace.files) {
|
||||
var fileName = this.getFileName(file.filename);
|
||||
const blob = await fetch(file.blobUrl).then((r) => r.blob());
|
||||
traceFolder.file(file.filename, blob);
|
||||
traceFolder.file(fileName, blob);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import _ from "lodash";
|
||||
import { nanos_to_string } from "../transform";
|
||||
import { transitionMap } from "../utils/consts";
|
||||
|
||||
/**
|
||||
* Represents a continuous section of the timeline that is rendered into the
|
||||
@@ -33,10 +36,36 @@ class Block {
|
||||
}
|
||||
}
|
||||
|
||||
//Represents a continuous section of the tag display that relates to a specific transition
|
||||
class Transition {
|
||||
/**
|
||||
* Create a transition.
|
||||
* @param {number} startPos - The position of the start tag as a percentage
|
||||
* of the timeline width.
|
||||
* @param {number} startTime - The start timestamp in ms of the transition.
|
||||
* @param {number} endTime - The end timestamp in ms of the transition.
|
||||
* @param {number} width - The width of the transition as a percentage of the
|
||||
* timeline width.
|
||||
* @param {string} color - the color of transition depending on type.
|
||||
* @param {number} overlap - number of transitions with which this transition overlaps.
|
||||
* @param {string} tooltip - The tooltip of the transition, minus the type of transition.
|
||||
*/
|
||||
constructor(startPos, startTime, endTime, width, color, overlap, tooltip) {
|
||||
this.startPos = startPos;
|
||||
this.startTime = startTime;
|
||||
this.endTime = endTime;
|
||||
this.width = width;
|
||||
this.color = color;
|
||||
this.overlap = overlap;
|
||||
this.tooltip = tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This Mixin should only be injected into components which have the following:
|
||||
* - An element in the template referenced as 'timeline' (this.$refs.timeline).
|
||||
*/
|
||||
|
||||
export default {
|
||||
name: 'timeline',
|
||||
props: {
|
||||
@@ -53,6 +82,15 @@ export default {
|
||||
'scale': {
|
||||
type: Array,
|
||||
},
|
||||
'tags': {
|
||||
type: Array,
|
||||
},
|
||||
'errors': {
|
||||
type: Array,
|
||||
},
|
||||
'flickerMode': {
|
||||
type: Boolean,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -99,6 +137,65 @@ export default {
|
||||
|
||||
return Object.freeze(blocks);
|
||||
},
|
||||
|
||||
//Generates list of transitions to be displayed in flicker mode
|
||||
timelineTransitions() {
|
||||
const transitions = [];
|
||||
|
||||
//group tags by transition 'id' property
|
||||
const groupedTags = _.mapValues(
|
||||
_.groupBy(this.tags, 'id'), clist => clist.map(tag => _.omit(tag, 'id')))
|
||||
;
|
||||
|
||||
for (const transitionId in groupedTags) {
|
||||
const id = groupedTags[transitionId];
|
||||
//there are at least two tags per id, maybe more if multiple traces
|
||||
// determine which tag is the start (min of start times), which is end (max of end times)
|
||||
const startTimes = id.filter(tag => tag.isStartTag).map(tag => tag.timestamp);
|
||||
const endTimes = id.filter(tag => !tag.isStartTag).map(tag => tag.timestamp);
|
||||
|
||||
const transitionStartTime = Math.min(startTimes);
|
||||
const transitionEndTime = Math.max(endTimes);
|
||||
|
||||
//do not freeze new transition, as overlap still to be handled (defaulted to 0)
|
||||
const transition = this.generateTransition(
|
||||
transitionStartTime,
|
||||
transitionEndTime,
|
||||
id[0].transition,
|
||||
0
|
||||
);
|
||||
transitions.push(transition);
|
||||
}
|
||||
|
||||
//sort transitions in ascending start position in order to handle overlap
|
||||
transitions.sort((a, b) => (a.startPos > b.startPos) ? 1: -1);
|
||||
|
||||
//compare each transition to the ones that came before
|
||||
for (let curr=0; curr<transitions.length; curr++) {
|
||||
let overlapStore = [];
|
||||
|
||||
for (let prev=0; prev<curr; prev++) {
|
||||
overlapStore.push(transitions[prev].overlap);
|
||||
|
||||
if (transitions[prev].startPos <= transitions[curr].startPos
|
||||
&& transitions[curr].startPos <= transitions[prev].startPos+transitions[prev].width
|
||||
&& transitions[curr].overlap === transitions[prev].overlap) {
|
||||
transitions[curr].overlap++;
|
||||
}
|
||||
}
|
||||
|
||||
if (overlapStore.length>0
|
||||
&& transitions[curr].overlap === Math.max(overlapStore)
|
||||
) transitions[curr].overlap++;
|
||||
}
|
||||
|
||||
return Object.freeze(transitions);
|
||||
},
|
||||
errorPositions() {
|
||||
if (!this.flickerMode) return [];
|
||||
const errorPositions = this.errors.map(error => this.position(error.timestamp));
|
||||
return Object.freeze(errorPositions);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
position(item) {
|
||||
@@ -143,6 +240,10 @@ export default {
|
||||
return pos * (this.crop.right - this.crop.left) + this.crop.left;
|
||||
},
|
||||
|
||||
objectWidth(startTs, endTs) {
|
||||
return this.position(endTs) - this.position(startTs) + this.pointWidth;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a position as a percentage of the timeline width to a timestamp.
|
||||
* @param {number} position - target position as a percentage of the
|
||||
@@ -249,9 +350,25 @@ export default {
|
||||
* scale parameter.
|
||||
*/
|
||||
generateTimelineBlock(startTs, endTs) {
|
||||
const blockWidth = this.position(endTs) - this.position(startTs) +
|
||||
this.pointWidth;
|
||||
const blockWidth = this.objectWidth(startTs, endTs);
|
||||
return Object.freeze(new Block(this.position(startTs), blockWidth));
|
||||
},
|
||||
/**
|
||||
* Generate a transition object that can be used by the tag-timeline to render
|
||||
* a transformed transition that starts at `startTs` and ends at `endTs`.
|
||||
* @param {number} startTs - The timestamp at which the transition starts.
|
||||
* @param {number} endTs - The timestamp at which the transition ends.
|
||||
* @param {string} transitionType - The type of transition.
|
||||
* @param {number} overlap - The degree to which the transition overlaps with others.
|
||||
* @return {Transition} A transition object transformed to the timeline's crop and
|
||||
* scale parameter.
|
||||
*/
|
||||
generateTransition(startTs, endTs, transitionType, overlap) {
|
||||
const transitionWidth = this.objectWidth(startTs, endTs);
|
||||
const transitionDesc = transitionMap.get(transitionType).desc;
|
||||
const transitionColor = transitionMap.get(transitionType).color;
|
||||
const tooltip = `${transitionDesc}. Start: ${nanos_to_string(startTs)}. End: ${nanos_to_string(endTs)}.`;
|
||||
return new Transition(this.position(startTs), startTs, endTs, transitionWidth, transitionColor, overlap, tooltip);
|
||||
},
|
||||
},
|
||||
};
|
||||
37
tools/winscope/src/traces/TraceError.ts
Normal file
37
tools/winscope/src/traces/TraceError.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020, 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 { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
|
||||
import TraceBase from './TraceBase';
|
||||
import { ErrorTrace } from '@/flickerlib';
|
||||
|
||||
export default class TraceError extends TraceBase {
|
||||
errorTraceFile: Object;
|
||||
|
||||
constructor(files) {
|
||||
const errorTraceFile = files[FILE_TYPES.ERROR_TRACE];
|
||||
super(errorTraceFile.data, errorTraceFile.timeline, files);
|
||||
this.errorTraceFile = errorTraceFile;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return TRACE_TYPES.ERROR;
|
||||
}
|
||||
|
||||
static fromProto(proto: any): ErrorTrace {
|
||||
return ErrorTrace.fromProto(proto);
|
||||
}
|
||||
}
|
||||
37
tools/winscope/src/traces/TraceTag.ts
Normal file
37
tools/winscope/src/traces/TraceTag.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020, 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 { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
|
||||
import TraceBase from './TraceBase';
|
||||
import { TagTrace } from '@/flickerlib';
|
||||
|
||||
export default class TraceTag extends TraceBase {
|
||||
tagTraceFile: Object;
|
||||
|
||||
constructor(files) {
|
||||
const tagTraceFile = files[FILE_TYPES.TAG_TRACE];
|
||||
super(tagTraceFile.data, tagTraceFile.timeline, files);
|
||||
this.tagTraceFile = tagTraceFile;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return TRACE_TYPES.TAG;
|
||||
}
|
||||
|
||||
static fromProto(proto: any): TagTrace {
|
||||
return TagTrace.fromProto(proto);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import TransitionType from "../flickerlib/tags/TransitionType";
|
||||
|
||||
/**
|
||||
* Should be kept in sync with ENUM is in Google3 under:
|
||||
* google3/wireless/android/tools/android_bug_tool/extension/common/actions
|
||||
@@ -31,6 +33,11 @@ const NAVIGATION_STYLE = {
|
||||
TARGETED: 'Targeted',
|
||||
};
|
||||
|
||||
const SEARCH_TYPE = {
|
||||
TAG: 'Transitions and Errors',
|
||||
TIMESTAMP: 'Timestamp',
|
||||
};
|
||||
|
||||
const logLevel = {
|
||||
INFO: 'info',
|
||||
DEBUG: 'debug',
|
||||
@@ -38,6 +45,17 @@ const logLevel = {
|
||||
WARN: 'warn',
|
||||
ERROR: 'error',
|
||||
WTF: 'wtf',
|
||||
}
|
||||
};
|
||||
|
||||
export { WebContentScriptMessageType, NAVIGATION_STYLE, logLevel };
|
||||
const transitionMap = new Map([
|
||||
[TransitionType.ROTATION, {desc: 'Rotation', color: '#9900ffff'}],
|
||||
[TransitionType.PIP_ENTER, {desc: 'Entering PIP mode', color: '#4a86e8ff'}],
|
||||
[TransitionType.PIP_RESIZE, {desc: 'Resizing PIP mode', color: '#2b9e94ff'}],
|
||||
[TransitionType.PIP_EXIT, {desc: 'Exiting PIP mode', color: 'darkblue'}],
|
||||
[TransitionType.APP_LAUNCH, {desc: 'Launching app', color: '#ef6befff'}],
|
||||
[TransitionType.APP_CLOSE, {desc: 'Closing app', color: '#d10ddfff'}],
|
||||
[TransitionType.IME_APPEAR, {desc: 'IME appearing', color: '#ff9900ff'}],
|
||||
[TransitionType.IME_DISAPPEAR, {desc: 'IME disappearing', color: '#ad6800ff'}],
|
||||
])
|
||||
|
||||
export { WebContentScriptMessageType, NAVIGATION_STYLE, SEARCH_TYPE, logLevel, transitionMap };
|
||||
|
||||
@@ -30,8 +30,9 @@ for arg in "$@"; do
|
||||
esac
|
||||
done
|
||||
|
||||
outfileTrans=${outfile}_transactiontrace.pb
|
||||
outfileSurf=${outfile}_layerstrace.pb
|
||||
WINSCOPE_EXT=.winscope
|
||||
outfileTrans=${outfile}_transactiontrace$WINSCOPE_EXT
|
||||
outfileSurf=${outfile}_layerstrace$WINSCOPE_EXT
|
||||
|
||||
outfileTrans_abs="$(cd "$(dirname "$outfileTrans")"; pwd)/$(basename "$outfileTrans")"
|
||||
outfileSurf_abs="$(cd "$(dirname "$outfileSurf")"; pwd)/$(basename "$outfileSurf")"
|
||||
@@ -39,8 +40,8 @@ outfileSurf_abs="$(cd "$(dirname "$outfileSurf")"; pwd)/$(basename "$outfileSurf
|
||||
if [ "$help" != "" ]; then
|
||||
echo "usage: $0 [-h | --help] [OUTFILE]"
|
||||
echo
|
||||
echo "Records Transaction traces (default transactiontrace.pb)."
|
||||
echo "Records Surface traces (default layerstrace.pb)."
|
||||
echo "Records Transaction traces (default transactiontrace$WINSCOPE_EXT)."
|
||||
echo "Records Surface traces (default layerstrace$WINSCOPE_EXT)."
|
||||
echo "To view the traces, use $WINSCOPE_URL."
|
||||
exit 1
|
||||
fi
|
||||
@@ -75,8 +76,8 @@ start_tracing
|
||||
read -p "Press ENTER to stop recording" -s x
|
||||
echo
|
||||
stop_tracing
|
||||
adb exec-out su root cat /data/misc/wmtrace/transaction_trace.pb >"$outfileTrans"
|
||||
adb exec-out su root cat /data/misc/wmtrace/layers_trace.pb >"$outfileSurf"
|
||||
adb exec-out su root cat /data/misc/wmtrace/transaction_trace$WINSCOPE_EXT >"$outfileTrans"
|
||||
adb exec-out su root cat /data/misc/wmtrace/layers_trace$WINSCOPE_EXT >"$outfileSurf"
|
||||
|
||||
echo
|
||||
echo "To view the trace, go to $WINSCOPE_URL, and open"
|
||||
|
||||
@@ -64,7 +64,7 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.pb/,
|
||||
test: /\.(pb|winscope)/,
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
paths: [
|
||||
|
||||
Reference in New Issue
Block a user