[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
|
# Location to save the proxy security token
|
||||||
WINSCOPE_TOKEN_LOCATION = os.path.expanduser('~/.config/winscope/.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
|
# Max interval between the client keep-alive requests in seconds
|
||||||
KEEP_ALIVE_INTERVAL_S = 5
|
KEEP_ALIVE_INTERVAL_S = 5
|
||||||
|
|
||||||
@@ -85,12 +93,29 @@ class FileMatcher:
|
|||||||
matchingFiles = call_adb(
|
matchingFiles = call_adb(
|
||||||
f"shell su root find {self.path} -name {self.matcher}", device_id)
|
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]
|
return matchingFiles.split('\n')[:-1]
|
||||||
|
|
||||||
def get_filetype(self):
|
def get_filetype(self):
|
||||||
return self.type
|
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:
|
class TraceTarget:
|
||||||
"""Defines a single parameter to trace.
|
"""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
|
# Order of files matters as they will be expected in that order and decoded in that order
|
||||||
TRACE_TARGETS = {
|
TRACE_TARGETS = {
|
||||||
"window_trace": TraceTarget(
|
"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 start\necho "WM trace started."',
|
||||||
'su root cmd window tracing stop >/dev/null 2>&1'
|
'su root cmd window tracing stop >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
"accessibility_trace": TraceTarget(
|
"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 start-trace\necho "Accessibility trace started."',
|
||||||
'su root cmd accessibility stop-trace >/dev/null 2>&1'
|
'su root cmd accessibility stop-trace >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
"layers_trace": TraceTarget(
|
"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 1\necho "SF trace started."',
|
||||||
'su root service call SurfaceFlinger 1025 i32 0 >/dev/null 2>&1'
|
'su root service call SurfaceFlinger 1025 i32 0 >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
"screen_recording": TraceTarget(
|
"screen_recording": TraceTarget(
|
||||||
File("/data/local/tmp/screen.winscope.mp4", "screen_recording"),
|
File(f'/data/local/tmp/screen.mp4', "screen_recording"),
|
||||||
'screenrecord --bit-rate 8M /data/local/tmp/screen.winscope.mp4 >/dev/null 2>&1 &\necho "ScreenRecorder started."',
|
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'
|
'pkill -l SIGINT screenrecord >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
"transaction": TraceTarget(
|
"transaction": TraceTarget(
|
||||||
[
|
[
|
||||||
File("/data/misc/wmtrace/transaction_trace.pb", "transactions"),
|
WinscopeFileMatcher(WINSCOPE_DIR, "transaction_trace", "transactions"),
|
||||||
FileMatcher("/data/misc/wmtrace/", "transaction_merges_*.pb",
|
FileMatcher(WINSCOPE_DIR, f'transaction_merges_*', "transaction_merges"),
|
||||||
"transaction_merges"),
|
|
||||||
],
|
],
|
||||||
'su root service call SurfaceFlinger 1020 i32 1\necho "SF transactions recording started."',
|
'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'
|
'su root service call SurfaceFlinger 1020 i32 0 >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
"proto_log": TraceTarget(
|
"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 start\necho "WM logging started."',
|
||||||
'su root cmd window logging stop >/dev/null 2>&1'
|
'su root cmd window logging stop >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
"ime_trace_clients": TraceTarget(
|
"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 start\necho "Clients IME trace started."',
|
||||||
'su root ime tracing stop >/dev/null 2>&1'
|
'su root ime tracing stop >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
"ime_trace_service": TraceTarget(
|
"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 start\necho "Service IME trace started."',
|
||||||
'su root ime tracing stop >/dev/null 2>&1'
|
'su root ime tracing stop >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
"ime_trace_managerservice": TraceTarget(
|
"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 start\necho "ManagerService IME trace started."',
|
||||||
'su root ime tracing stop >/dev/null 2>&1'
|
'su root ime tracing stop >/dev/null 2>&1'
|
||||||
),
|
),
|
||||||
@@ -204,12 +228,12 @@ class DumpTarget:
|
|||||||
|
|
||||||
DUMP_TARGETS = {
|
DUMP_TARGETS = {
|
||||||
"window_dump": DumpTarget(
|
"window_dump": DumpTarget(
|
||||||
File("/data/local/tmp/wm_dump.pb", "window_dump"),
|
File(f'/data/local/tmp/wm_dump{WINSCOPE_EXT}', "window_dump"),
|
||||||
'su root dumpsys window --proto > /data/local/tmp/wm_dump.pb'
|
f'su root dumpsys window --proto > /data/local/tmp/wm_dump{WINSCOPE_EXT}'
|
||||||
),
|
),
|
||||||
"layers_dump": DumpTarget(
|
"layers_dump": DumpTarget(
|
||||||
File("/data/local/tmp/sf_dump.pb", "layers_dump"),
|
File(f'/data/local/tmp/sf_dump{WINSCOPE_EXT}', "layers_dump"),
|
||||||
'su root dumpsys SurfaceFlinger --proto > /data/local/tmp/sf_dump.pb'
|
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")
|
buf = base64.encodebytes(tmp.read()).decode("utf-8")
|
||||||
file_buffers[file_type].append(buf)
|
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')
|
# server.send_header('X-Content-Type-Options', 'nosniff')
|
||||||
# add_standard_headers(server)
|
# add_standard_headers(server)
|
||||||
j = json.dumps(file_buffers)
|
j = json.dumps(file_buffers)
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
import { DiffGenerator, DiffType } from "../src/utils/diff.js";
|
import { DiffGenerator, DiffType } from "../src/utils/diff.js";
|
||||||
import { Node, DiffNode, toPlainObject } from "./utils/tree.js";
|
import { Node, DiffNode, toPlainObject } from "./utils/tree.js";
|
||||||
|
|
||||||
|
const treeOne = new Node({ id: 1 }, [
|
||||||
|
new Node({ id: 2 }, []),
|
||||||
|
new Node({ id: 3 }, []),
|
||||||
|
new Node({ id: 4 }, []),
|
||||||
|
]);
|
||||||
|
const treeTwo = new Node({ id: 1 }, [
|
||||||
|
new Node({ id: 2 }, []),
|
||||||
|
new Node({ id: 3 }, [
|
||||||
|
new Node({ id: 5 }, []),
|
||||||
|
]),
|
||||||
|
new Node({ id: 4 }, []),
|
||||||
|
]);
|
||||||
|
|
||||||
function checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree) {
|
function checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree) {
|
||||||
const diffTree = new DiffGenerator(newTree)
|
const diffTree = new DiffGenerator(newTree)
|
||||||
.compareWith(oldTree)
|
.compareWith(oldTree)
|
||||||
.withUniqueNodeId(node => node.id)
|
.withUniqueNodeId(node => node.id)
|
||||||
|
.withModifiedCheck(() => false)
|
||||||
.generateDiffTree();
|
.generateDiffTree();
|
||||||
|
|
||||||
expect(diffTree).toEqual(expectedDiffTree);
|
expect(diffTree).toEqual(expectedDiffTree);
|
||||||
@@ -12,25 +26,8 @@ function checkDiffTreeWithNoModifiedCheck(oldTree, newTree, expectedDiffTree) {
|
|||||||
|
|
||||||
describe("DiffGenerator", () => {
|
describe("DiffGenerator", () => {
|
||||||
it("can generate a simple add diff", () => {
|
it("can generate a simple add diff", () => {
|
||||||
const oldTree = new Node({ id: 1 }, [
|
const oldTree = treeOne;
|
||||||
new Node({ id: 2 }, []),
|
const newTree = treeTwo;
|
||||||
new Node({ id: 3 }, []),
|
|
||||||
new Node({ id: 4 }, []),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const newTree = new Node({ id: 1 }, [
|
|
||||||
new Node({ id: 2 }, []),
|
|
||||||
new Node({ id: 3 }, [
|
|
||||||
new Node({ id: 5 }, []),
|
|
||||||
]),
|
|
||||||
new Node({ id: 4 }, []),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const diffTree = new DiffGenerator(newTree)
|
|
||||||
.compareWith(oldTree)
|
|
||||||
.withUniqueNodeId(node => node.id)
|
|
||||||
.withModifiedCheck(() => false)
|
|
||||||
.generateDiffTree();
|
|
||||||
|
|
||||||
const expectedDiffTree = toPlainObject(
|
const expectedDiffTree = toPlainObject(
|
||||||
new DiffNode({ id: 1 }, DiffType.NONE, [
|
new DiffNode({ id: 1 }, DiffType.NONE, [
|
||||||
@@ -46,19 +43,8 @@ describe("DiffGenerator", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("can generate a simple delete diff", () => {
|
it("can generate a simple delete diff", () => {
|
||||||
const oldTree = new Node({ id: 1 }, [
|
const oldTree = treeTwo;
|
||||||
new Node({ id: 2 }, []),
|
const newTree = treeOne;
|
||||||
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 expectedDiffTree = toPlainObject(
|
const expectedDiffTree = toPlainObject(
|
||||||
new DiffNode({ id: 1 }, DiffType.NONE, [
|
new DiffNode({ id: 1 }, DiffType.NONE, [
|
||||||
@@ -74,13 +60,7 @@ describe("DiffGenerator", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("can generate a simple move diff", () => {
|
it("can generate a simple move diff", () => {
|
||||||
const oldTree = new Node({ id: 1 }, [
|
const oldTree = treeTwo;
|
||||||
new Node({ id: 2 }, []),
|
|
||||||
new Node({ id: 3 }, [
|
|
||||||
new Node({ id: 5 }, []),
|
|
||||||
]),
|
|
||||||
new Node({ id: 4 }, []),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const newTree = new Node({ id: 1 }, [
|
const newTree = new Node({ id: 1 }, [
|
||||||
new Node({ id: 2 }, []),
|
new Node({ id: 2 }, []),
|
||||||
|
|||||||
@@ -1,32 +1,6 @@
|
|||||||
import { DiffType } from "../src/utils/diff.js";
|
import { DiffType } from "../src/utils/diff.js";
|
||||||
import { ObjectTransformer } from "../src/transform.js";
|
import { ObjectTransformer } from "../src/transform.js";
|
||||||
import { Node, DiffNode, toPlainObject } from "./utils/tree.js";
|
import { ObjNode, ObjDiffNode, 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("ObjectTransformer", () => {
|
describe("ObjectTransformer", () => {
|
||||||
it("can transform a simple object", () => {
|
it("can transform a simple object", () => {
|
||||||
@@ -46,19 +20,19 @@ describe("ObjectTransformer", () => {
|
|||||||
const expectedTransformedObj = toPlainObject(
|
const expectedTransformedObj = toPlainObject(
|
||||||
new ObjNode('root', [
|
new ObjNode('root', [
|
||||||
new ObjNode('obj', [
|
new ObjNode('obj', [
|
||||||
new ObjNode('string: string', [], true),
|
new ObjNode('string: string', [], true, 'root.obj.string'),
|
||||||
new ObjNode('number: 3', [], true),
|
new ObjNode('number: 3', [], true, 'root.obj.number'),
|
||||||
]),
|
], undefined, 'root.obj'),
|
||||||
new ObjNode('array', [
|
new ObjNode('array', [
|
||||||
new ObjNode('0', [
|
new ObjNode('0', [
|
||||||
new ObjNode('nested: item', [], true),
|
new ObjNode('nested: item', [], true, 'root.array.0.nested'),
|
||||||
]),
|
], undefined, 'root.array.0'),
|
||||||
new ObjNode("1: two", [], true),
|
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: () => { } })
|
.setOptions({ formatter: () => { } })
|
||||||
.transform();
|
.transform();
|
||||||
|
|
||||||
@@ -75,12 +49,12 @@ describe("ObjectTransformer", () => {
|
|||||||
const expectedTransformedObj = toPlainObject(
|
const expectedTransformedObj = toPlainObject(
|
||||||
new ObjNode('root', [
|
new ObjNode('root', [
|
||||||
new ObjNode('obj', [
|
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: () => { } })
|
.setOptions({ formatter: () => { } })
|
||||||
.transform();
|
.transform();
|
||||||
|
|
||||||
@@ -106,14 +80,14 @@ describe("ObjectTransformer", () => {
|
|||||||
const expectedTransformedObj = toPlainObject(
|
const expectedTransformedObj = toPlainObject(
|
||||||
new ObjDiffNode('root', DiffType.NONE, [
|
new ObjDiffNode('root', DiffType.NONE, [
|
||||||
new ObjDiffNode('a', DiffType.NONE, [
|
new ObjDiffNode('a', DiffType.NONE, [
|
||||||
new ObjDiffNode('b: 1', DiffType.NONE, [], true),
|
new ObjDiffNode('b: 1', DiffType.NONE, [], true, 'root.a.b'),
|
||||||
new ObjDiffNode('d: 3', DiffType.ADDED, [], true),
|
new ObjDiffNode('d: 3', DiffType.ADDED, [], true, 'root.a.d'),
|
||||||
]),
|
], false, 'root.a'),
|
||||||
new ObjDiffNode('c: 2', DiffType.NONE, [], true),
|
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: () => { } })
|
.setOptions({ formatter: () => { } })
|
||||||
.withDiff(oldObj)
|
.withDiff(oldObj)
|
||||||
.transform();
|
.transform();
|
||||||
@@ -133,13 +107,13 @@ describe("ObjectTransformer", () => {
|
|||||||
const expectedTransformedObj = toPlainObject(
|
const expectedTransformedObj = toPlainObject(
|
||||||
new ObjDiffNode('root', DiffType.NONE, [
|
new ObjDiffNode('root', DiffType.NONE, [
|
||||||
new ObjDiffNode('a', DiffType.NONE, [
|
new ObjDiffNode('a', DiffType.NONE, [
|
||||||
new ObjDiffNode('1', DiffType.ADDED, []),
|
new ObjDiffNode('1', DiffType.ADDED, [], false, 'root.a.1'),
|
||||||
new ObjDiffNode('null', DiffType.DELETED, []),
|
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: () => { } })
|
.setOptions({ formatter: () => { } })
|
||||||
.withDiff(oldObj)
|
.withDiff(oldObj)
|
||||||
.transform();
|
.transform();
|
||||||
@@ -166,15 +140,15 @@ describe("ObjectTransformer", () => {
|
|||||||
new ObjDiffNode('root', DiffType.NONE, [
|
new ObjDiffNode('root', DiffType.NONE, [
|
||||||
new ObjDiffNode('a', DiffType.NONE, [
|
new ObjDiffNode('a', DiffType.NONE, [
|
||||||
new ObjDiffNode('b', DiffType.NONE, [
|
new ObjDiffNode('b', DiffType.NONE, [
|
||||||
new ObjDiffNode('1', DiffType.ADDED, []),
|
new ObjDiffNode('1', DiffType.ADDED, [], false, 'root.a.b.1'),
|
||||||
new ObjDiffNode('null', DiffType.DELETED, []),
|
new ObjDiffNode('null', DiffType.DELETED, [], false, 'root.a.b.null'),
|
||||||
]),
|
], false, 'root.a.b'),
|
||||||
]),
|
], false, 'root.a'),
|
||||||
new ObjDiffNode('c: 2', DiffType.NONE, [], true),
|
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: () => { } })
|
.setOptions({ formatter: () => { } })
|
||||||
.withDiff(oldObj)
|
.withDiff(oldObj)
|
||||||
.transform();
|
.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 fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { expectedEntries, expectedLayers, layers_traces } from './traces/ExpectedTraces';
|
||||||
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'),
|
|
||||||
];
|
|
||||||
|
|
||||||
describe("Proto Transformations", () => {
|
describe("Proto Transformations", () => {
|
||||||
it("can transform surface flinger traces", () => {
|
it("can transform surface flinger traces", () => {
|
||||||
for (const trace of layers_traces) {
|
for (var i = 0; i < layers_traces.length; i++) {
|
||||||
fs.readFileSync(path.resolve(__dirname, trace));
|
const trace = layers_traces[i];
|
||||||
const traceBuffer = fs.readFileSync(path.resolve(__dirname, trace));
|
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);
|
// use final entry as this determines if there was any error in previous entry parsing
|
||||||
const data = decodeAndTransformProto(buffer, FILE_TYPES.layers_trace, true);
|
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) {
|
constructor(nodeDef, diffType, children) {
|
||||||
super(nodeDef, children);
|
super(nodeDef, children);
|
||||||
this.diff = { type: diffType };
|
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>
|
<template>
|
||||||
<TraceView :store="store" :file="file" :summarizer="summarizer" />
|
<TraceView
|
||||||
|
:store="store"
|
||||||
|
:file="file"
|
||||||
|
:summarizer="summarizer"
|
||||||
|
:presentTags="[]"
|
||||||
|
:presentErrors="[]"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -52,13 +52,20 @@
|
|||||||
:ref="file.type"
|
:ref="file.type"
|
||||||
:store="store"
|
:store="store"
|
||||||
:file="file"
|
:file="file"
|
||||||
|
:presentTags="Object.freeze(presentTags)"
|
||||||
|
:presentErrors="Object.freeze(presentErrors)"
|
||||||
|
:dataViewFiles="dataViewFiles"
|
||||||
@click="onDataViewFocus(file)"
|
@click="onDataViewFocus(file)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<overlay
|
<overlay
|
||||||
|
:presentTags="Object.freeze(presentTags)"
|
||||||
|
:presentErrors="Object.freeze(presentErrors)"
|
||||||
|
:tagAndErrorTraces="tagAndErrorTraces"
|
||||||
:store="store"
|
:store="store"
|
||||||
:ref="overlayRef"
|
:ref="overlayRef"
|
||||||
|
:searchTypes="searchTypes"
|
||||||
v-if="dataLoaded"
|
v-if="dataLoaded"
|
||||||
v-on:bottom-nav-height-change="handleBottomNavHeightChange"
|
v-on:bottom-nav-height-change="handleBottomNavHeightChange"
|
||||||
/>
|
/>
|
||||||
@@ -77,7 +84,8 @@ import FileType from './mixins/FileType.js';
|
|||||||
import SaveAsZip from './mixins/SaveAsZip';
|
import SaveAsZip from './mixins/SaveAsZip';
|
||||||
import FocusedDataViewFinder from './mixins/FocusedDataViewFinder';
|
import FocusedDataViewFinder from './mixins/FocusedDataViewFinder';
|
||||||
import {DIRECTION} from './utils/utils';
|
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';
|
const APP_NAME = 'Winscope';
|
||||||
|
|
||||||
@@ -97,11 +105,17 @@ export default {
|
|||||||
simplifyNames: true,
|
simplifyNames: true,
|
||||||
displayDefaults: true,
|
displayDefaults: true,
|
||||||
navigationStyle: NAVIGATION_STYLE.GLOBAL,
|
navigationStyle: NAVIGATION_STYLE.GLOBAL,
|
||||||
|
flickerTraceView: false,
|
||||||
|
showFileTypes: [],
|
||||||
}),
|
}),
|
||||||
overlayRef: 'overlay',
|
overlayRef: 'overlay',
|
||||||
mainContentStyle: {
|
mainContentStyle: {
|
||||||
'padding-bottom': `${CONTENT_BOTTOM_PADDING}px`,
|
'padding-bottom': `${CONTENT_BOTTOM_PADDING}px`,
|
||||||
},
|
},
|
||||||
|
presentTags: [],
|
||||||
|
presentErrors: [],
|
||||||
|
searchTypes: [SEARCH_TYPE.TIMESTAMP],
|
||||||
|
tagAndErrorTraces: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -113,8 +127,58 @@ export default {
|
|||||||
window.removeEventListener('keydown', this.onKeyDown);
|
window.removeEventListener('keydown', this.onKeyDown);
|
||||||
window.removeEventListener('scroll', this.onScroll);
|
window.removeEventListener('scroll', this.onScroll);
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
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() {
|
clear() {
|
||||||
|
this.store.showFileTypes = [];
|
||||||
this.$store.commit('clearFiles');
|
this.$store.commit('clearFiles');
|
||||||
},
|
},
|
||||||
onDataViewFocus(file) {
|
onDataViewFocus(file) {
|
||||||
@@ -139,7 +203,12 @@ export default {
|
|||||||
},
|
},
|
||||||
onDataReady(files) {
|
onDataReady(files) {
|
||||||
this.$store.dispatch('setFiles', files);
|
this.$store.dispatch('setFiles', files);
|
||||||
|
this.tagAndErrorTraces = this.shouldUpdateTagAndErrorTraces();
|
||||||
|
this.presentTags = this.getUpdatedTags();
|
||||||
|
this.presentErrors = this.getUpdatedErrors();
|
||||||
|
this.updateSearchTypes();
|
||||||
this.updateFocusedView();
|
this.updateFocusedView();
|
||||||
|
this.updateShowFileTypes();
|
||||||
},
|
},
|
||||||
setStatus(status) {
|
setStatus(status) {
|
||||||
if (status) {
|
if (status) {
|
||||||
@@ -158,7 +227,10 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
files() {
|
files() {
|
||||||
return this.$store.getters.sortedFiles;
|
return this.$store.getters.sortedFiles.map(file => {
|
||||||
|
if (this.hasDataView(file)) file.show = true;
|
||||||
|
return file;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
prettyDump() {
|
prettyDump() {
|
||||||
return JSON.stringify(this.dump, null, 2);
|
return JSON.stringify(this.dump, null, 2);
|
||||||
@@ -174,7 +246,16 @@ export default {
|
|||||||
return this.activeDataView;
|
return this.activeDataView;
|
||||||
},
|
},
|
||||||
dataViewFiles() {
|
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: {
|
watch: {
|
||||||
@@ -187,6 +268,7 @@ export default {
|
|||||||
dataview: DataView,
|
dataview: DataView,
|
||||||
datainput: DataInput,
|
datainput: DataInput,
|
||||||
dataadb: DataAdb,
|
dataadb: DataAdb,
|
||||||
|
searchbar: Searchbar,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -194,7 +276,7 @@ export default {
|
|||||||
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@600&display=swap');
|
||||||
|
|
||||||
#app .md-app-container {
|
#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;
|
transform: none!important;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
@@ -240,20 +322,10 @@ export default {
|
|||||||
margin-top: 1em
|
margin-top: 1em
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1 {
|
||||||
h2 {
|
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
|
|
||||||
.data-inputs {
|
.data-inputs {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -292,5 +364,5 @@ a {
|
|||||||
-webkit-hyphens: auto;
|
-webkit-hyphens: auto;
|
||||||
hyphens: auto;
|
hyphens: auto;
|
||||||
padding: 10px 10px 10px 10px;
|
padding: 10px 10px 10px 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -158,6 +158,9 @@ const TRACES = {
|
|||||||
'window_trace': {
|
'window_trace': {
|
||||||
name: 'Window Manager',
|
name: 'Window Manager',
|
||||||
},
|
},
|
||||||
|
'accessibility_trace': {
|
||||||
|
name: 'Accessibility',
|
||||||
|
},
|
||||||
'layers_trace': {
|
'layers_trace': {
|
||||||
name: 'Surface Flinger',
|
name: 'Surface Flinger',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,10 +16,15 @@
|
|||||||
<div @click="onClick($event)">
|
<div @click="onClick($event)">
|
||||||
<flat-card v-if="hasDataView(file)">
|
<flat-card v-if="hasDataView(file)">
|
||||||
<md-card-header>
|
<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>
|
<md-card-header-text>
|
||||||
<div class="md-title">
|
<div class="md-title">
|
||||||
<md-icon>{{ TRACE_ICONS[file.type] }}</md-icon>
|
<md-icon>{{ TRACE_ICONS[file.type] }}</md-icon>
|
||||||
{{file.type}}
|
{{ file.type }}
|
||||||
</div>
|
</div>
|
||||||
</md-card-header-text>
|
</md-card-header-text>
|
||||||
<md-button
|
<md-button
|
||||||
@@ -31,41 +36,47 @@
|
|||||||
</md-button>
|
</md-button>
|
||||||
</md-card-header>
|
</md-card-header>
|
||||||
<AccessibilityTraceView
|
<AccessibilityTraceView
|
||||||
v-if="showInAccessibilityTraceView(file)"
|
v-if="showInAccessibilityTraceView(file) && isShowFileType(file.type)"
|
||||||
:store="store"
|
:store="store"
|
||||||
:file="file"
|
:file="file"
|
||||||
ref="view"
|
ref="view"
|
||||||
/>
|
/>
|
||||||
<WindowManagerTraceView
|
<WindowManagerTraceView
|
||||||
v-if="showInWindowManagerTraceView(file)"
|
v-if="showInWindowManagerTraceView(file) && isShowFileType(file.type)"
|
||||||
:store="store"
|
:store="store"
|
||||||
:file="file"
|
:file="file"
|
||||||
|
:presentTags="presentTags"
|
||||||
|
:presentErrors="presentErrors"
|
||||||
ref="view"
|
ref="view"
|
||||||
/>
|
/>
|
||||||
<SurfaceFlingerTraceView
|
<SurfaceFlingerTraceView
|
||||||
v-else-if="showInSurfaceFlingerTraceView(file)"
|
v-else-if="showInSurfaceFlingerTraceView(file) && isShowFileType(file.type)"
|
||||||
:store="store"
|
:store="store"
|
||||||
:file="file"
|
:file="file"
|
||||||
|
:presentTags="presentTags"
|
||||||
|
:presentErrors="presentErrors"
|
||||||
ref="view"
|
ref="view"
|
||||||
/>
|
/>
|
||||||
<transactionsview
|
<transactionsview
|
||||||
v-else-if="isTransactions(file)"
|
v-else-if="isTransactions(file) && isShowFileType(file.type)"
|
||||||
:trace="file"
|
:trace="file"
|
||||||
ref="view"
|
ref="view"
|
||||||
/>
|
/>
|
||||||
<logview
|
<logview
|
||||||
v-else-if="isLog(file)"
|
v-else-if="isLog(file) && isShowFileType(file.type)"
|
||||||
:file="file"
|
:file="file"
|
||||||
ref="view"
|
ref="view"
|
||||||
/>
|
/>
|
||||||
<traceview
|
<traceview
|
||||||
v-else-if="showInTraceView(file)"
|
v-else-if="showInTraceView(file) && isShowFileType(file.type)"
|
||||||
:store="store"
|
:store="store"
|
||||||
:file="file"
|
:file="file"
|
||||||
|
:presentTags="[]"
|
||||||
|
:presentErrors="[]"
|
||||||
ref="view"
|
ref="view"
|
||||||
/>
|
/>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<h1 class="bad">Unrecognized DataType</h1>
|
<h1 v-if="isShowFileType(file.type)" class="bad">Unrecognized DataType</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</flat-card>
|
</flat-card>
|
||||||
@@ -150,8 +161,23 @@ export default {
|
|||||||
// to component.
|
// to component.
|
||||||
this.$emit('click', e);
|
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],
|
mixins: [FileType],
|
||||||
components: {
|
components: {
|
||||||
'traceview': TraceView,
|
'traceview': TraceView,
|
||||||
@@ -170,4 +196,18 @@ export default {
|
|||||||
font-size: 4em;
|
font-size: 4em;
|
||||||
color: red;
|
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>
|
</style>
|
||||||
|
|||||||
@@ -43,13 +43,28 @@
|
|||||||
{{c.long}}
|
{{c.long}}
|
||||||
</md-tooltip>
|
</md-tooltip>
|
||||||
</div>
|
</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>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import Arrow from './components/TagDisplay/Arrow.vue';
|
||||||
|
import {transitionMap} from './utils/consts.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DefaultTreeElement',
|
name: 'DefaultTreeElement',
|
||||||
props: ['item', 'simplify-names'],
|
props: ['item', 'simplify-names', 'errors', 'transitions'],
|
||||||
methods: {
|
methods: {
|
||||||
chipClassForChip(c) {
|
chipClassForChip(c) {
|
||||||
return [
|
return [
|
||||||
@@ -59,6 +74,15 @@ export default {
|
|||||||
(c.type?.toString() || c.class?.toString() || '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>
|
</script>
|
||||||
@@ -100,4 +124,12 @@ span {
|
|||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flicker-tags {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-arrow {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {VueContext} from 'vue-context';
|
import VueContext from 'vue-context';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'NodeContextMenu',
|
name: 'NodeContextMenu',
|
||||||
|
|||||||
@@ -52,6 +52,15 @@
|
|||||||
>
|
>
|
||||||
<div class="nav-content">
|
<div class="nav-content">
|
||||||
<div class="">
|
<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-toolbar
|
||||||
md-elevation="0"
|
md-elevation="0"
|
||||||
class="md-transparent">
|
class="md-transparent">
|
||||||
@@ -66,16 +75,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</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
|
<md-button
|
||||||
@click="updateSearchForTimestamp"
|
@click="toggleSearch()"
|
||||||
>Search</md-button>
|
class="drop-search"
|
||||||
|
>Show/hide search bar</md-button>
|
||||||
|
|
||||||
|
<div class="active-timeline" v-show="minimized">
|
||||||
<div
|
<div
|
||||||
class="active-timeline-icon"
|
class="active-timeline-icon"
|
||||||
@click="$refs.navigationTypeSelection.$el
|
@click="$refs.navigationTypeSelection.$el
|
||||||
@@ -148,6 +153,10 @@
|
|||||||
{{ seekTime }}
|
{{ seekTime }}
|
||||||
</label>
|
</label>
|
||||||
<timeline
|
<timeline
|
||||||
|
:store="store"
|
||||||
|
:flickerMode="flickerMode"
|
||||||
|
:tags="Object.freeze(tags)"
|
||||||
|
:errors="Object.freeze(errors)"
|
||||||
:timeline="Object.freeze(minimizedTimeline.timeline)"
|
:timeline="Object.freeze(minimizedTimeline.timeline)"
|
||||||
:selected-index="minimizedTimeline.selectedIndex"
|
:selected-index="minimizedTimeline.selectedIndex"
|
||||||
:scale="scale"
|
:scale="scale"
|
||||||
@@ -271,16 +280,17 @@ import TimelineSelection from './TimelineSelection.vue';
|
|||||||
import DraggableDiv from './DraggableDiv.vue';
|
import DraggableDiv from './DraggableDiv.vue';
|
||||||
import VideoView from './VideoView.vue';
|
import VideoView from './VideoView.vue';
|
||||||
import MdIconOption from './components/IconSelection/IconSelectOption.vue';
|
import MdIconOption from './components/IconSelection/IconSelectOption.vue';
|
||||||
|
import Searchbar from './Searchbar.vue';
|
||||||
import FileType from './mixins/FileType.js';
|
import FileType from './mixins/FileType.js';
|
||||||
import {NAVIGATION_STYLE} from './utils/consts';
|
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
|
// eslint-disable-next-line camelcase
|
||||||
import {nanos_to_string, string_to_nanos} from './transform.js';
|
import {nanos_to_string} from './transform.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'overlay',
|
name: 'overlay',
|
||||||
props: ['store'],
|
props: ['store', 'presentTags', 'presentErrors', 'tagAndErrorTraces', 'searchTypes'],
|
||||||
mixins: [FileType],
|
mixins: [FileType],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -301,7 +311,9 @@ export default {
|
|||||||
crop: null,
|
crop: null,
|
||||||
cropIntent: null,
|
cropIntent: null,
|
||||||
TRACE_ICONS,
|
TRACE_ICONS,
|
||||||
searchTimestamp: '',
|
search: false,
|
||||||
|
tags: [],
|
||||||
|
errors: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -388,7 +400,8 @@ export default {
|
|||||||
default:
|
default:
|
||||||
const split = this.navigationStyle.split('-');
|
const split = this.navigationStyle.split('-');
|
||||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
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];
|
const fileType = split[1];
|
||||||
@@ -410,7 +423,8 @@ export default {
|
|||||||
default:
|
default:
|
||||||
const split = this.navigationStyle.split('-');
|
const split = this.navigationStyle.split('-');
|
||||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
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];
|
const fileType = split[1];
|
||||||
@@ -419,6 +433,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
minimizedTimeline() {
|
minimizedTimeline() {
|
||||||
|
this.updateFlickerMode(this.navigationStyle);
|
||||||
|
|
||||||
if (this.navigationStyle === NAVIGATION_STYLE.GLOBAL) {
|
if (this.navigationStyle === NAVIGATION_STYLE.GLOBAL) {
|
||||||
return this.mergedTimeline;
|
return this.mergedTimeline;
|
||||||
}
|
}
|
||||||
@@ -436,12 +452,16 @@ export default {
|
|||||||
return this.mergedTimeline;
|
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
|
return this.$store.state
|
||||||
.traces[this.navigationStyle.split('-')[1]];
|
.traces[this.navigationStyle.split('-')[1]];
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Unexpected Navigation Style');
|
console.warn('Unexpected navigation type; fallback to global');
|
||||||
|
return this.mergedTimeline;
|
||||||
},
|
},
|
||||||
isCropped() {
|
isCropped() {
|
||||||
return this.crop != null &&
|
return this.crop != null &&
|
||||||
@@ -450,6 +470,9 @@ export default {
|
|||||||
multipleTraces() {
|
multipleTraces() {
|
||||||
return this.timelineFiles.length > 1;
|
return this.timelineFiles.length > 1;
|
||||||
},
|
},
|
||||||
|
flickerMode() {
|
||||||
|
return this.tags.length>0 || this.errors.length>0;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
updated() {
|
updated() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@@ -461,6 +484,9 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
toggleSearch() {
|
||||||
|
this.search = !(this.search);
|
||||||
|
},
|
||||||
emitBottomHeightUpdate() {
|
emitBottomHeightUpdate() {
|
||||||
if (this.$refs.bottomNav) {
|
if (this.$refs.bottomNav) {
|
||||||
const newHeight = this.$refs.bottomNav.$el.clientHeight;
|
const newHeight = this.$refs.bottomNav.$el.clientHeight;
|
||||||
@@ -480,9 +506,10 @@ export default {
|
|||||||
timelines.push(file.timeline);
|
timelines.push(file.timeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
var timelineToAdvance = 0;
|
||||||
|
while (timelineToAdvance !== undefined) {
|
||||||
|
timelineToAdvance = undefined;
|
||||||
let minTime = Infinity;
|
let minTime = Infinity;
|
||||||
let timelineToAdvance;
|
|
||||||
|
|
||||||
for (let i = 0; i < timelines.length; i++) {
|
for (let i = 0; i < timelines.length; i++) {
|
||||||
const timeline = timelines[i];
|
const timeline = timelines[i];
|
||||||
@@ -608,7 +635,9 @@ export default {
|
|||||||
default:
|
default:
|
||||||
const split = this.navigationStyle.split('-');
|
const split = this.navigationStyle.split('-');
|
||||||
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
|
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];
|
const fileType = split[1];
|
||||||
@@ -618,6 +647,43 @@ export default {
|
|||||||
|
|
||||||
this.$store.commit('setNavigationFilesFilter', navigationStyleFilter);
|
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) {
|
updateVideoOverlayWidth(width) {
|
||||||
this.videoOverlayExtraWidth = width;
|
this.videoOverlayExtraWidth = width;
|
||||||
},
|
},
|
||||||
@@ -642,17 +708,6 @@ export default {
|
|||||||
clearSelection() {
|
clearSelection() {
|
||||||
this.crop = null;
|
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: {
|
components: {
|
||||||
'timeline': Timeline,
|
'timeline': Timeline,
|
||||||
@@ -661,6 +716,7 @@ export default {
|
|||||||
'videoview': VideoView,
|
'videoview': VideoView,
|
||||||
'draggable-div': DraggableDiv,
|
'draggable-div': DraggableDiv,
|
||||||
'md-icon-option': MdIconOption,
|
'md-icon-option': MdIconOption,
|
||||||
|
'searchbar': Searchbar,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -864,4 +920,8 @@ export default {
|
|||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drop-search:hover {
|
||||||
|
background-color: #9af39f;
|
||||||
|
}
|
||||||
</style>
|
</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>
|
<template>
|
||||||
<TraceView :store="store" :file="file" :summarizer="summarizer" />
|
<TraceView
|
||||||
|
:store="store"
|
||||||
|
:file="file"
|
||||||
|
:summarizer="summarizer"
|
||||||
|
:presentTags="presentTags"
|
||||||
|
:presentErrors="presentErrors"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import TraceView from '@/TraceView.vue';
|
import TraceView from '@/TraceView.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WindowManagerTraceView',
|
name: 'SurfaceFlingerTraceView',
|
||||||
props: ['store', 'file'],
|
props: ['store', 'file', 'presentTags', 'presentErrors'],
|
||||||
components: {
|
components: {
|
||||||
TraceView,
|
TraceView,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,6 +13,22 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<template>
|
<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
|
<svg
|
||||||
width="100%"
|
width="100%"
|
||||||
height="20"
|
height="20"
|
||||||
@@ -39,15 +55,29 @@
|
|||||||
:rx="corner"
|
:rx="corner"
|
||||||
class="point selected"
|
class="point selected"
|
||||||
/>
|
/>
|
||||||
|
<line
|
||||||
|
v-for="error in errorPositions"
|
||||||
|
:key="error"
|
||||||
|
:x1="`${error}%`"
|
||||||
|
:x2="`${error}%`"
|
||||||
|
y1="0"
|
||||||
|
y2="18px"
|
||||||
|
class="error"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import TimelineMixin from "./mixins/Timeline.js";
|
import TimelineMixin from "./mixins/Timeline.js";
|
||||||
|
import TransitionContainer from './components/TagDisplay/TransitionContainer.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "timeline",
|
name: "timeline",
|
||||||
// TODO: Add indication of trim, at least for collasped 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() {
|
data() {
|
||||||
return {
|
return {
|
||||||
pointHeight: 15,
|
pointHeight: 15,
|
||||||
@@ -55,7 +85,6 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
mixins: [TimelineMixin],
|
mixins: [TimelineMixin],
|
||||||
methods: {},
|
|
||||||
computed: {
|
computed: {
|
||||||
timestamps() {
|
timestamps() {
|
||||||
if (this.timeline.length == 1) {
|
if (this.timeline.length == 1) {
|
||||||
@@ -66,10 +95,35 @@ export default {
|
|||||||
selected() {
|
selected() {
|
||||||
return this.timeline[this.selectedIndex];
|
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>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.timeline-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.container:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.tag-timeline {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
.timeline-svg .point {
|
.timeline-svg .point {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@@ -78,9 +132,13 @@ export default {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
.timeline-svg:not(.disabled) .point.selected {
|
.timeline-svg:not(.disabled) .point.selected {
|
||||||
fill: rgb(240, 59, 59);
|
fill: #b2f6faff;
|
||||||
}
|
}
|
||||||
.timeline-svg.disabled .point.selected {
|
.timeline-svg.disabled .point.selected {
|
||||||
fill: rgba(240, 59, 59, 0.596);
|
fill: rgba(240, 59, 59, 0.596);
|
||||||
}
|
}
|
||||||
|
.error {
|
||||||
|
stroke: rgb(255, 0, 0);
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
</md-checkbox>
|
</md-checkbox>
|
||||||
<md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
|
<md-checkbox v-model="store.onlyVisible">Only visible</md-checkbox>
|
||||||
<md-checkbox v-model="store.flattened">Flat</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">
|
<md-field md-inline class="filter">
|
||||||
<label>Filter...</label>
|
<label>Filter...</label>
|
||||||
<md-input v-model="hierarchyPropertyFilterString"></md-input>
|
<md-input v-model="hierarchyPropertyFilterString"></md-input>
|
||||||
@@ -55,6 +56,10 @@
|
|||||||
:selected="hierarchySelected"
|
:selected="hierarchySelected"
|
||||||
:filter="hierarchyFilter"
|
:filter="hierarchyFilter"
|
||||||
:flattened="store.flattened"
|
:flattened="store.flattened"
|
||||||
|
:onlyVisible="store.onlyVisible"
|
||||||
|
:flickerTraceView="store.flickerTraceView"
|
||||||
|
:presentTags="presentTags"
|
||||||
|
:presentErrors="presentErrors"
|
||||||
:items-clickable="true"
|
:items-clickable="true"
|
||||||
:useGlobalCollapsedState="true"
|
:useGlobalCollapsedState="true"
|
||||||
:simplify-names="store.simplifyNames"
|
:simplify-names="store.simplifyNames"
|
||||||
@@ -165,7 +170,7 @@ function findEntryInTree(tree, id) {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'traceview',
|
name: 'traceview',
|
||||||
props: ['store', 'file', 'summarizer'],
|
props: ['store', 'file', 'summarizer', 'presentTags', 'presentErrors'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
propertyFilterString: '',
|
propertyFilterString: '',
|
||||||
@@ -306,10 +311,30 @@ export default {
|
|||||||
|
|
||||||
return prevEntry;
|
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() {
|
created() {
|
||||||
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
|
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
|
||||||
},
|
},
|
||||||
|
destroyed() {
|
||||||
|
this.store.flickerTraceView = false;
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
selectedIndex() {
|
selectedIndex() {
|
||||||
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
|
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
|
||||||
@@ -337,9 +362,12 @@ export default {
|
|||||||
hierarchyFilter() {
|
hierarchyFilter() {
|
||||||
const hierarchyPropertyFilter =
|
const hierarchyPropertyFilter =
|
||||||
getFilter(this.hierarchyPropertyFilterString);
|
getFilter(this.hierarchyPropertyFilterString);
|
||||||
return this.store.onlyVisible ? (c) => {
|
var fil = this.store.onlyVisible ? (c) => {
|
||||||
return c.isVisible && hierarchyPropertyFilter(c);
|
return c.isVisible && hierarchyPropertyFilter(c);
|
||||||
} : hierarchyPropertyFilter;
|
} : hierarchyPropertyFilter;
|
||||||
|
return this.store.flickerTraceView ? (c) => {
|
||||||
|
return this.isEntryTagMatch(c);
|
||||||
|
} : fil;
|
||||||
},
|
},
|
||||||
propertyFilter() {
|
propertyFilter() {
|
||||||
return getFilter(this.propertyFilterString);
|
return getFilter(this.propertyFilterString);
|
||||||
@@ -363,6 +391,9 @@ export default {
|
|||||||
|
|
||||||
return summary;
|
return summary;
|
||||||
},
|
},
|
||||||
|
hasTagsOrErrors() {
|
||||||
|
return this.presentTags.length > 0 || this.presentErrors.length > 0;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
'tree-view': TreeView,
|
'tree-view': TreeView,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<button
|
<button
|
||||||
class="toggle-tree-btn"
|
class="toggle-tree-btn"
|
||||||
@click="toggleTree"
|
@click="toggleTree"
|
||||||
v-if="!isLeaf"
|
v-if="!isLeaf && !flattened"
|
||||||
v-on:click.stop
|
v-on:click.stop
|
||||||
>
|
>
|
||||||
<i aria-hidden="true" class="md-icon md-theme-default material-icons">
|
<i aria-hidden="true" class="md-icon md-theme-default material-icons">
|
||||||
@@ -50,7 +50,12 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<DefaultTreeElement :item="item" :simplify-names="simplifyNames"/>
|
<DefaultTreeElement
|
||||||
|
:item="item"
|
||||||
|
:simplify-names="simplifyNames"
|
||||||
|
:errors="errors"
|
||||||
|
:transitions="transitions"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-show="isCollapsed">
|
<div v-show="isCollapsed">
|
||||||
@@ -78,7 +83,7 @@
|
|||||||
v-on:collapseAllOtherNodes="collapseAllOtherNodes"
|
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
|
<tree-view
|
||||||
v-for="(c,i) in children"
|
v-for="(c,i) in children"
|
||||||
:item="c"
|
:item="c"
|
||||||
@@ -87,7 +92,11 @@
|
|||||||
:key="i"
|
:key="i"
|
||||||
:filter="childFilter(c)"
|
:filter="childFilter(c)"
|
||||||
:flattened="flattened"
|
:flattened="flattened"
|
||||||
|
:onlyVisible="onlyVisible"
|
||||||
:simplify-names="simplifyNames"
|
:simplify-names="simplifyNames"
|
||||||
|
:flickerTraceView="flickerTraceView"
|
||||||
|
:presentTags="currentTags"
|
||||||
|
:presentErrors="currentErrors"
|
||||||
:force-flattened="applyingFlattened"
|
:force-flattened="applyingFlattened"
|
||||||
v-show="filterMatches(c)"
|
v-show="filterMatches(c)"
|
||||||
:items-clickable="itemsClickable"
|
:items-clickable="itemsClickable"
|
||||||
@@ -112,7 +121,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import DefaultTreeElement from './DefaultTreeElement.vue';
|
import DefaultTreeElement from './DefaultTreeElement.vue';
|
||||||
import NodeContextMenu from './NodeContextMenu.vue';
|
import NodeContextMenu from './NodeContextMenu.vue';
|
||||||
|
|
||||||
import {DiffType} from './utils/diff.js';
|
import {DiffType} from './utils/diff.js';
|
||||||
|
|
||||||
/* in px, must be kept in sync with css, maybe find a better solution... */
|
/* in px, must be kept in sync with css, maybe find a better solution... */
|
||||||
@@ -141,6 +149,10 @@ export default {
|
|||||||
'useGlobalCollapsedState',
|
'useGlobalCollapsedState',
|
||||||
// Custom view to use to render the elements in the tree view
|
// Custom view to use to render the elements in the tree view
|
||||||
'elementView',
|
'elementView',
|
||||||
|
'onlyVisible',
|
||||||
|
'flickerTraceView',
|
||||||
|
'presentTags',
|
||||||
|
'presentErrors',
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
const isCollapsedByDefault = this.collapse ?? false;
|
const isCollapsedByDefault = this.collapse ?? false;
|
||||||
@@ -161,6 +173,10 @@ export default {
|
|||||||
[DiffType.MODIFIED]: '.',
|
[DiffType.MODIFIED]: '.',
|
||||||
[DiffType.MOVED]: '.',
|
[DiffType.MOVED]: '.',
|
||||||
},
|
},
|
||||||
|
currentTags: [],
|
||||||
|
currentErrors: [],
|
||||||
|
transitions: [],
|
||||||
|
errors: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -177,6 +193,10 @@ export default {
|
|||||||
},
|
},
|
||||||
currentTimestamp() {
|
currentTimestamp() {
|
||||||
// Update anything that is required to change when time changes.
|
// 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();
|
this.updateCollapsedDiffClass();
|
||||||
},
|
},
|
||||||
isSelected(isSelected) {
|
isSelected(isSelected) {
|
||||||
@@ -277,7 +297,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isLeaf && e.detail % 2 === 0) {
|
if (!this.isLeaf && e.detail % 2 === 0) {
|
||||||
// Double click collaspable node
|
// Double click collapsable node
|
||||||
this.toggleTree();
|
this.toggleTree();
|
||||||
} else {
|
} else {
|
||||||
this.select();
|
this.select();
|
||||||
@@ -408,6 +428,64 @@ export default {
|
|||||||
child.closeAllChildrenContextMenus();
|
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: {
|
computed: {
|
||||||
hasDiff() {
|
hasDiff() {
|
||||||
@@ -464,11 +542,20 @@ export default {
|
|||||||
return this.initialDepth || 0;
|
return this.initialDepth || 0;
|
||||||
},
|
},
|
||||||
nodeOffsetStyle() {
|
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 {
|
return {
|
||||||
marginLeft: '-' + offest,
|
marginLeft: '-' + offset,
|
||||||
paddingLeft: offest,
|
paddingLeft: offset,
|
||||||
|
display: display,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -567,14 +654,6 @@ export default {
|
|||||||
color: white;
|
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 {
|
.tree-view .node.child-selected + .children {
|
||||||
border-left: 1px solid #b4b4b4;
|
border-left: 1px solid #b4b4b4;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,13 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<TraceView :store="store" :file="file" :summarizer="summarizer" />
|
<TraceView
|
||||||
|
:store="store"
|
||||||
|
:file="file"
|
||||||
|
:summarizer="summarizer"
|
||||||
|
:presentTags="presentTags"
|
||||||
|
:presentErrors="presentErrors"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -22,7 +28,7 @@ import TraceView from "@/TraceView.vue"
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "WindowManagerTraceView",
|
name: "WindowManagerTraceView",
|
||||||
props: ["store", "file"],
|
props: ["store", "file", "presentTags", "presentErrors"],
|
||||||
components: {
|
components: {
|
||||||
TraceView
|
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 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 jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto';
|
||||||
import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.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 protobuf from 'protobufjs';
|
||||||
import {transform_accessibility_trace} from './transform_accessibility.js';
|
import {transform_accessibility_trace} from './transform_accessibility.js';
|
||||||
import {transform_transaction_trace} from './transform_transaction.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 WindowManagerDump from '@/dumps/WindowManager.ts';
|
||||||
import WaylandDump from '@/dumps/Wayland.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 AccessibilityTraceMessage = lookup_type(jsonProtoDefsAccessibility, 'com.android.server.accessibility.AccessibilityTraceFileProto');
|
||||||
const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto');
|
const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto');
|
||||||
const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto');
|
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 InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodClientsTraceFileProto');
|
||||||
const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodServiceTraceFileProto');
|
const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodServiceTraceFileProto');
|
||||||
const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodManagerServiceTraceFileProto');
|
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 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
|
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 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 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 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({
|
const FILE_TYPES = Object.freeze({
|
||||||
ACCESSIBILITY_TRACE: 'AccessibilityTrace',
|
ACCESSIBILITY_TRACE: 'AccessibilityTrace',
|
||||||
@@ -96,6 +105,8 @@ const FILE_TYPES = Object.freeze({
|
|||||||
IME_TRACE_CLIENTS: 'ImeTraceClients',
|
IME_TRACE_CLIENTS: 'ImeTraceClients',
|
||||||
IME_TRACE_SERVICE: 'ImeTrace InputMethodService',
|
IME_TRACE_SERVICE: 'ImeTrace InputMethodService',
|
||||||
IME_TRACE_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
|
IME_TRACE_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
|
||||||
|
TAG_TRACE: 'TagTrace',
|
||||||
|
ERROR_TRACE: 'ErrorTrace',
|
||||||
});
|
});
|
||||||
|
|
||||||
const WINDOW_MANAGER_ICON = 'view_compact';
|
const WINDOW_MANAGER_ICON = 'view_compact';
|
||||||
@@ -108,6 +119,8 @@ const SYSTEM_UI_ICON = 'filter_none';
|
|||||||
const LAUNCHER_ICON = 'filter_none';
|
const LAUNCHER_ICON = 'filter_none';
|
||||||
const IME_ICON = 'keyboard';
|
const IME_ICON = 'keyboard';
|
||||||
const ACCESSIBILITY_ICON = 'filter_none';
|
const ACCESSIBILITY_ICON = 'filter_none';
|
||||||
|
const TAG_ICON = 'details';
|
||||||
|
const TRACE_ERROR_ICON = 'warning';
|
||||||
|
|
||||||
const FILE_ICONS = {
|
const FILE_ICONS = {
|
||||||
[FILE_TYPES.ACCESSIBILITY_TRACE]: ACCESSIBILITY_ICON,
|
[FILE_TYPES.ACCESSIBILITY_TRACE]: ACCESSIBILITY_ICON,
|
||||||
@@ -125,6 +138,8 @@ const FILE_ICONS = {
|
|||||||
[FILE_TYPES.IME_TRACE_CLIENTS]: IME_ICON,
|
[FILE_TYPES.IME_TRACE_CLIENTS]: IME_ICON,
|
||||||
[FILE_TYPES.IME_TRACE_SERVICE]: IME_ICON,
|
[FILE_TYPES.IME_TRACE_SERVICE]: IME_ICON,
|
||||||
[FILE_TYPES.IME_TRACE_MANAGERSERVICE]: 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) {
|
function oneOf(dataType) {
|
||||||
@@ -144,6 +159,8 @@ const TRACE_TYPES = Object.freeze({
|
|||||||
IME_CLIENTS: 'ImeTrace Clients',
|
IME_CLIENTS: 'ImeTrace Clients',
|
||||||
IME_SERVICE: 'ImeTrace InputMethodService',
|
IME_SERVICE: 'ImeTrace InputMethodService',
|
||||||
IME_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
|
IME_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
|
||||||
|
TAG: 'TagTrace',
|
||||||
|
ERROR: 'ErrorTrace',
|
||||||
});
|
});
|
||||||
|
|
||||||
const TRACE_INFO = {
|
const TRACE_INFO = {
|
||||||
@@ -221,6 +238,18 @@ const TRACE_INFO = {
|
|||||||
files: [oneOf(FILE_TYPES.IME_TRACE_MANAGERSERVICE)],
|
files: [oneOf(FILE_TYPES.IME_TRACE_MANAGERSERVICE)],
|
||||||
constructor: ImeTraceManagerService,
|
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({
|
const DUMP_TYPES = Object.freeze({
|
||||||
@@ -262,6 +291,8 @@ export const TRACE_ICONS = {
|
|||||||
[TRACE_TYPES.IME_CLIENTS]: IME_ICON,
|
[TRACE_TYPES.IME_CLIENTS]: IME_ICON,
|
||||||
[TRACE_TYPES.IME_SERVICE]: IME_ICON,
|
[TRACE_TYPES.IME_SERVICE]: IME_ICON,
|
||||||
[TRACE_TYPES.IME_MANAGERSERVICE]: 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.WINDOW_MANAGER]: WINDOW_MANAGER_ICON,
|
||||||
[DUMP_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
|
[DUMP_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
|
||||||
@@ -275,7 +306,7 @@ const FILE_DECODERS = {
|
|||||||
decoder: protoDecoder,
|
decoder: protoDecoder,
|
||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.ACCESSIBILITY_TRACE,
|
type: FILE_TYPES.ACCESSIBILITY_TRACE,
|
||||||
protoType: AccessibilityTraceMessage,
|
objTypeProto: AccessibilityTraceMessage,
|
||||||
transform: transform_accessibility_trace,
|
transform: transform_accessibility_trace,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -285,7 +316,7 @@ const FILE_DECODERS = {
|
|||||||
decoder: protoDecoder,
|
decoder: protoDecoder,
|
||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.WINDOW_MANAGER_TRACE,
|
type: FILE_TYPES.WINDOW_MANAGER_TRACE,
|
||||||
protoType: WmTraceMessage,
|
objTypeProto: WmTraceMessage,
|
||||||
transform: WindowManagerTrace.fromProto,
|
transform: WindowManagerTrace.fromProto,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -296,7 +327,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.SURFACE_FLINGER_TRACE,
|
type: FILE_TYPES.SURFACE_FLINGER_TRACE,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: SfTraceMessage,
|
objTypeProto: SfTraceMessage,
|
||||||
transform: SurfaceFlingerTrace.fromProto,
|
transform: SurfaceFlingerTrace.fromProto,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -307,7 +338,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.WAYLAND_TRACE,
|
type: FILE_TYPES.WAYLAND_TRACE,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: WaylandTraceMessage,
|
objTypeProto: WaylandTraceMessage,
|
||||||
transform: transform_wayland_trace,
|
transform: transform_wayland_trace,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -318,8 +349,8 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.SURFACE_FLINGER_DUMP,
|
type: FILE_TYPES.SURFACE_FLINGER_DUMP,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: SfDumpMessage,
|
objTypeProto: [SfDumpMessage, SfTraceMessage],
|
||||||
transform: SurfaceFlingerDump.fromProto,
|
transform: [SurfaceFlingerDump.fromProto, SurfaceFlingerTrace.fromProto],
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -329,7 +360,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.WINDOW_MANAGER_DUMP,
|
type: FILE_TYPES.WINDOW_MANAGER_DUMP,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: WmDumpMessage,
|
objTypeProto: WmDumpMessage,
|
||||||
transform: WindowManagerDump.fromProto,
|
transform: WindowManagerDump.fromProto,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -340,7 +371,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.WAYLAND_DUMP,
|
type: FILE_TYPES.WAYLAND_DUMP,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: WaylandDumpMessage,
|
objTypeProto: WaylandDumpMessage,
|
||||||
transform: transform_wl_outputstate,
|
transform: transform_wl_outputstate,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -360,7 +391,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.TRANSACTIONS_TRACE,
|
type: FILE_TYPES.TRANSACTIONS_TRACE,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: SfTransactionTraceMessage,
|
objTypeProto: SfTransactionTraceMessage,
|
||||||
transform: transform_transaction_trace,
|
transform: transform_transaction_trace,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -371,7 +402,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.PROTO_LOG,
|
type: FILE_TYPES.PROTO_LOG,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: ProtoLogMessage,
|
objTypeProto: ProtoLogMessage,
|
||||||
transform: transformProtolog,
|
transform: transformProtolog,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -382,7 +413,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.SYSTEM_UI,
|
type: FILE_TYPES.SYSTEM_UI,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: SystemUiTraceMessage,
|
objTypeProto: SystemUiTraceMessage,
|
||||||
transform: transform_sysui_trace,
|
transform: transform_sysui_trace,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -393,7 +424,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.LAUNCHER,
|
type: FILE_TYPES.LAUNCHER,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: LauncherTraceMessage,
|
objTypeProto: LauncherTraceMessage,
|
||||||
transform: transform_launcher_trace,
|
transform: transform_launcher_trace,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -404,7 +435,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.IME_TRACE_CLIENTS,
|
type: FILE_TYPES.IME_TRACE_CLIENTS,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: InputMethodClientsTraceMessage,
|
objTypeProto: InputMethodClientsTraceMessage,
|
||||||
transform: transform_ime_trace_clients,
|
transform: transform_ime_trace_clients,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -415,7 +446,7 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.IME_TRACE_SERVICE,
|
type: FILE_TYPES.IME_TRACE_SERVICE,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: InputMethodServiceTraceMessage,
|
objTypeProto: InputMethodServiceTraceMessage,
|
||||||
transform: transform_ime_trace_service,
|
transform: transform_ime_trace_service,
|
||||||
timeline: true,
|
timeline: true,
|
||||||
},
|
},
|
||||||
@@ -426,11 +457,31 @@ const FILE_DECODERS = {
|
|||||||
decoderParams: {
|
decoderParams: {
|
||||||
type: FILE_TYPES.IME_TRACE_MANAGERSERVICE,
|
type: FILE_TYPES.IME_TRACE_MANAGERSERVICE,
|
||||||
mime: 'application/octet-stream',
|
mime: 'application/octet-stream',
|
||||||
protoType: InputMethodManagerServiceTraceMessage,
|
objTypeProto: InputMethodManagerServiceTraceMessage,
|
||||||
transform: transform_ime_trace_managerservice,
|
transform: transform_ime_trace_managerservice,
|
||||||
timeline: true,
|
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) {
|
function lookup_type(protoPath, type) {
|
||||||
@@ -472,11 +523,33 @@ function modifyProtoFields(protoObj, displayDefaults) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function decodeAndTransformProto(buffer, params, 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);
|
modifyProtoFields(decoded, displayDefaults);
|
||||||
const transformed = params.transform(decoded);
|
const transformed = transform(decoded);
|
||||||
|
|
||||||
return transformed;
|
return transformed;
|
||||||
|
} catch (e) {
|
||||||
|
// check next parser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new UndetectableFileType('Unable to parse file');
|
||||||
}
|
}
|
||||||
|
|
||||||
function protoDecoder(buffer, params, fileName, store) {
|
function protoDecoder(buffer, params, fileName, store) {
|
||||||
@@ -568,6 +641,12 @@ function detectAndDecode(buffer, fileName, store) {
|
|||||||
if (arrayStartsWith(buffer, IMM_TRACE_MAGIC_NUMBER)) {
|
if (arrayStartsWith(buffer, IMM_TRACE_MAGIC_NUMBER)) {
|
||||||
return decodedFile(FILE_TYPES.IME_TRACE_MANAGERSERVICE, buffer, fileName, store);
|
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
|
// TODO(b/169305853): Add magic number at beginning of file for better auto detection
|
||||||
for (const [filetype, condition] of [
|
for (const [filetype, condition] of [
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
|
import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
|
||||||
import DumpBase from "./DumpBase";
|
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 {
|
export default class SurfaceFlinger extends DumpBase {
|
||||||
sfDumpFile: any;
|
sfDumpFile: any;
|
||||||
@@ -32,11 +33,14 @@ export default class SurfaceFlinger extends DumpBase {
|
|||||||
return DUMP_TYPES.SURFACE_FLINGER;
|
return DUMP_TYPES.SURFACE_FLINGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromProto(proto: any): LayersTraceEntry {
|
static fromProto(proto: any): LayersTrace {
|
||||||
return LayersTraceEntry.fromProto(
|
const source = null;
|
||||||
/*protos */ proto.layers,
|
const entry = LayersTraceEntry.fromProto(
|
||||||
|
/* protos */ proto.layers,
|
||||||
|
/* displays */ proto.displays,
|
||||||
/* timestamp */ 0,
|
/* timestamp */ 0,
|
||||||
/* hwcBlob */ ""
|
/* hwcBlob */ ""
|
||||||
);
|
);
|
||||||
|
return new LayersTrace([entry], source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,8 @@ export default class WindowManager extends DumpBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static fromProto(proto: any): WindowManagerTrace {
|
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) {
|
for (const entryProto of proto.entry) {
|
||||||
const transformedEntry = LayerTraceEntry.fromProto(
|
const transformedEntry = LayerTraceEntry.fromProto(
|
||||||
/* protos */ entryProto.layers.layers,
|
/* protos */ entryProto.layers.layers,
|
||||||
|
/* displays */ entryProto.displays,
|
||||||
/* timestamp */ entryProto.elapsedRealtimeNanos,
|
/* timestamp */ entryProto.elapsedRealtimeNanos,
|
||||||
/* hwcBlob */ entryProto.hwcBlob);
|
/* hwcBlob */ entryProto.hwcBlob);
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,13 @@ export default class ObjectFormatter {
|
|||||||
return obj;
|
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[] {
|
static getProperties(entry: any): string[] {
|
||||||
var props = [];
|
var props = [];
|
||||||
let obj = entry;
|
let obj = entry;
|
||||||
@@ -58,13 +65,12 @@ export default class ObjectFormatter {
|
|||||||
if (it.startsWith(`_`)) return false;
|
if (it.startsWith(`_`)) return false;
|
||||||
// some predefined properties used only internally (e.g., children, ref, diff)
|
// some predefined properties used only internally (e.g., children, ref, diff)
|
||||||
if (this.INVALID_ELEMENT_PROPERTIES.includes(it)) return false;
|
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)
|
// 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
|
// non-flicker object
|
||||||
return !value.stableId;
|
return !(value?.stableId);
|
||||||
});
|
});
|
||||||
properties.forEach(function (prop) {
|
properties.forEach(function (prop) {
|
||||||
if (typeof(entry[prop]) !== 'function' && props.indexOf(prop) === -1) {
|
if (typeof(entry[prop]) !== 'function' && props.indexOf(prop) === -1) {
|
||||||
@@ -76,6 +82,12 @@ export default class ObjectFormatter {
|
|||||||
return props;
|
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): {} {
|
static format(obj: any): {} {
|
||||||
const properties = this.getProperties(obj);
|
const properties = this.getProperties(obj);
|
||||||
const sortedProperties = properties.sort()
|
const sortedProperties = properties.sort()
|
||||||
@@ -85,7 +97,14 @@ export default class ObjectFormatter {
|
|||||||
const key = entry;
|
const key = entry;
|
||||||
const value: any = obj[key];
|
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
|
// flicker obj
|
||||||
if (value.prettyPrint) {
|
if (value.prettyPrint) {
|
||||||
const isEmpty = value.isEmpty === true;
|
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;
|
Transform.Matrix;
|
||||||
const Transform = require('flicker').com.android.server.wm.traces.common.
|
const Transform = require('flicker').com.android.server.wm.traces.common.
|
||||||
layers.Transform;
|
layers.Transform;
|
||||||
|
const Display = require('flicker').com.android.server.wm.traces.common.
|
||||||
|
layers.Display;
|
||||||
|
|
||||||
// Common
|
// Common
|
||||||
const Size = require('flicker').com.android.server.wm.traces.common.Size;
|
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 RectF = require('flicker').com.android.server.wm.traces.common.RectF;
|
||||||
const Region = require('flicker').com.android.server.wm.traces.common.Region;
|
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_BUFFER = new Buffer(0, 0, 0, 0);
|
||||||
const EMPTY_COLOR = new Color(-1, -1, -1, 0);
|
const EMPTY_COLOR = new Color(-1, -1, -1, 0);
|
||||||
const EMPTY_RECT = new Rect(0, 0, 0, 0);
|
const EMPTY_RECT = new Rect(0, 0, 0, 0);
|
||||||
@@ -226,6 +238,15 @@ export {
|
|||||||
LayersTrace,
|
LayersTrace,
|
||||||
Transform,
|
Transform,
|
||||||
Matrix,
|
Matrix,
|
||||||
|
Display,
|
||||||
|
// Tags
|
||||||
|
Tag,
|
||||||
|
TagState,
|
||||||
|
TagTrace,
|
||||||
|
// Errors
|
||||||
|
Error,
|
||||||
|
ErrorState,
|
||||||
|
ErrorTrace,
|
||||||
// Common
|
// Common
|
||||||
Size,
|
Size,
|
||||||
Buffer,
|
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 WindowManagerState from './WindowManagerState';
|
||||||
import WindowManagerTrace from './WindowManagerTrace';
|
import WindowManagerTrace from './WindowManagerTrace';
|
||||||
import ObjectFormatter from './ObjectFormatter';
|
import ObjectFormatter from './ObjectFormatter';
|
||||||
|
import TagTrace from './TagTrace';
|
||||||
|
import ErrorTrace from './ErrorTrace';
|
||||||
/**
|
/**
|
||||||
* Entry point into the flickerlib for Winscope.
|
* Entry point into the flickerlib for Winscope.
|
||||||
* Expose everything we want Winscope to have access to here.
|
* 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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { LayerTraceEntry, LayerTraceEntryBuilder } from "../common"
|
import { Display, LayerTraceEntry, LayerTraceEntryBuilder, toRect, toSize, toTransform } from "../common"
|
||||||
import Layer from './Layer'
|
import Layer from './Layer'
|
||||||
import { VISIBLE_CHIP, RELATIVE_Z_PARENT_CHIP, MISSING_LAYER } from '../treeview/Chips'
|
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 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();
|
const entry: LayerTraceEntry = builder.build();
|
||||||
|
|
||||||
updateChildren(entry);
|
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;
|
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: {},
|
dumps: {},
|
||||||
excludeFromTimeline: [
|
excludeFromTimeline: [
|
||||||
TRACE_TYPES.PROTO_LOG,
|
TRACE_TYPES.PROTO_LOG,
|
||||||
|
TRACE_TYPES.TAG,
|
||||||
|
TRACE_TYPES.ERROR
|
||||||
],
|
],
|
||||||
activeFile: null,
|
activeFile: null,
|
||||||
focusedFile: null,
|
focusedFile: null,
|
||||||
@@ -93,6 +95,14 @@ const store = new Vuex.Store({
|
|||||||
return Object.values(state.traces)
|
return Object.values(state.traces)
|
||||||
.filter(file => !state.excludeFromTimeline.includes(file.type));
|
.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) {
|
sortedTimelineFiles(state, getters) {
|
||||||
return sortFiles(getters.timelineFiles);
|
return sortFiles(getters.timelineFiles);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,14 +33,37 @@ export default {
|
|||||||
|
|
||||||
document.body.removeChild(a);
|
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) {
|
async downloadAsZip(traces) {
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
|
|
||||||
for (const trace of traces) {
|
for (const trace of traces) {
|
||||||
const traceFolder = zip.folder(trace.type);
|
const traceFolder = zip.folder(trace.type);
|
||||||
for (const file of trace.files) {
|
for (const file of trace.files) {
|
||||||
|
var fileName = this.getFileName(file.filename);
|
||||||
const blob = await fetch(file.blobUrl).then((r) => r.blob());
|
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.
|
* 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
|
* 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:
|
* This Mixin should only be injected into components which have the following:
|
||||||
* - An element in the template referenced as 'timeline' (this.$refs.timeline).
|
* - An element in the template referenced as 'timeline' (this.$refs.timeline).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'timeline',
|
name: 'timeline',
|
||||||
props: {
|
props: {
|
||||||
@@ -53,6 +82,15 @@ export default {
|
|||||||
'scale': {
|
'scale': {
|
||||||
type: Array,
|
type: Array,
|
||||||
},
|
},
|
||||||
|
'tags': {
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
|
'errors': {
|
||||||
|
type: Array,
|
||||||
|
},
|
||||||
|
'flickerMode': {
|
||||||
|
type: Boolean,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@@ -99,6 +137,65 @@ export default {
|
|||||||
|
|
||||||
return Object.freeze(blocks);
|
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: {
|
methods: {
|
||||||
position(item) {
|
position(item) {
|
||||||
@@ -143,6 +240,10 @@ export default {
|
|||||||
return pos * (this.crop.right - this.crop.left) + this.crop.left;
|
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.
|
* Converts a position as a percentage of the timeline width to a timestamp.
|
||||||
* @param {number} position - target position as a percentage of the
|
* @param {number} position - target position as a percentage of the
|
||||||
@@ -249,9 +350,25 @@ export default {
|
|||||||
* scale parameter.
|
* scale parameter.
|
||||||
*/
|
*/
|
||||||
generateTimelineBlock(startTs, endTs) {
|
generateTimelineBlock(startTs, endTs) {
|
||||||
const blockWidth = this.position(endTs) - this.position(startTs) +
|
const blockWidth = this.objectWidth(startTs, endTs);
|
||||||
this.pointWidth;
|
|
||||||
return Object.freeze(new Block(this.position(startTs), blockWidth));
|
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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import TransitionType from "../flickerlib/tags/TransitionType";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should be kept in sync with ENUM is in Google3 under:
|
* Should be kept in sync with ENUM is in Google3 under:
|
||||||
* google3/wireless/android/tools/android_bug_tool/extension/common/actions
|
* google3/wireless/android/tools/android_bug_tool/extension/common/actions
|
||||||
@@ -31,6 +33,11 @@ const NAVIGATION_STYLE = {
|
|||||||
TARGETED: 'Targeted',
|
TARGETED: 'Targeted',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SEARCH_TYPE = {
|
||||||
|
TAG: 'Transitions and Errors',
|
||||||
|
TIMESTAMP: 'Timestamp',
|
||||||
|
};
|
||||||
|
|
||||||
const logLevel = {
|
const logLevel = {
|
||||||
INFO: 'info',
|
INFO: 'info',
|
||||||
DEBUG: 'debug',
|
DEBUG: 'debug',
|
||||||
@@ -38,6 +45,17 @@ const logLevel = {
|
|||||||
WARN: 'warn',
|
WARN: 'warn',
|
||||||
ERROR: 'error',
|
ERROR: 'error',
|
||||||
WTF: 'wtf',
|
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
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
outfileTrans=${outfile}_transactiontrace.pb
|
WINSCOPE_EXT=.winscope
|
||||||
outfileSurf=${outfile}_layerstrace.pb
|
outfileTrans=${outfile}_transactiontrace$WINSCOPE_EXT
|
||||||
|
outfileSurf=${outfile}_layerstrace$WINSCOPE_EXT
|
||||||
|
|
||||||
outfileTrans_abs="$(cd "$(dirname "$outfileTrans")"; pwd)/$(basename "$outfileTrans")"
|
outfileTrans_abs="$(cd "$(dirname "$outfileTrans")"; pwd)/$(basename "$outfileTrans")"
|
||||||
outfileSurf_abs="$(cd "$(dirname "$outfileSurf")"; pwd)/$(basename "$outfileSurf")"
|
outfileSurf_abs="$(cd "$(dirname "$outfileSurf")"; pwd)/$(basename "$outfileSurf")"
|
||||||
@@ -39,8 +40,8 @@ outfileSurf_abs="$(cd "$(dirname "$outfileSurf")"; pwd)/$(basename "$outfileSurf
|
|||||||
if [ "$help" != "" ]; then
|
if [ "$help" != "" ]; then
|
||||||
echo "usage: $0 [-h | --help] [OUTFILE]"
|
echo "usage: $0 [-h | --help] [OUTFILE]"
|
||||||
echo
|
echo
|
||||||
echo "Records Transaction traces (default transactiontrace.pb)."
|
echo "Records Transaction traces (default transactiontrace$WINSCOPE_EXT)."
|
||||||
echo "Records Surface traces (default layerstrace.pb)."
|
echo "Records Surface traces (default layerstrace$WINSCOPE_EXT)."
|
||||||
echo "To view the traces, use $WINSCOPE_URL."
|
echo "To view the traces, use $WINSCOPE_URL."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
@@ -75,8 +76,8 @@ start_tracing
|
|||||||
read -p "Press ENTER to stop recording" -s x
|
read -p "Press ENTER to stop recording" -s x
|
||||||
echo
|
echo
|
||||||
stop_tracing
|
stop_tracing
|
||||||
adb exec-out su root cat /data/misc/wmtrace/transaction_trace.pb >"$outfileTrans"
|
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.pb >"$outfileSurf"
|
adb exec-out su root cat /data/misc/wmtrace/layers_trace$WINSCOPE_EXT >"$outfileSurf"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "To view the trace, go to $WINSCOPE_URL, and open"
|
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',
|
loader: 'file-loader',
|
||||||
options: {
|
options: {
|
||||||
paths: [
|
paths: [
|
||||||
|
|||||||
Reference in New Issue
Block a user