Support new transactions trace
Keep previous implementation as "transactions (legacy)" The proxy is not yet able to generate traces because no adb command is available at the moment. Known issues: - Enum fields are not translated into values in Winscope (only IntDef support) Prepare the codebase for the new SF transaction tracing Bug: 167521734 Test: run winscope and collect a transactions trace Change-Id: Ie489ee1a63056377c1699e14df8de975c2978085
This commit is contained in:
@@ -132,7 +132,6 @@ class TraceTarget:
|
||||
self.trace_start = trace_start
|
||||
self.trace_stop = trace_stop
|
||||
|
||||
|
||||
# Order of files matters as they will be expected in that order and decoded in that order
|
||||
TRACE_TARGETS = {
|
||||
"window_trace": TraceTarget(
|
||||
@@ -155,9 +154,14 @@ TRACE_TARGETS = {
|
||||
f'screenrecord --bit-rate 8M /data/local/tmp/screen.mp4 >/dev/null 2>&1 &\necho "ScreenRecorder started."',
|
||||
'pkill -l SIGINT screenrecord >/dev/null 2>&1'
|
||||
),
|
||||
"transaction": TraceTarget(
|
||||
"transactions": TraceTarget(
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "transactions_trace", "transactions"),
|
||||
'su root service call SurfaceFlinger 1041 i32 1\necho "SF transactions recording started."',
|
||||
'su root service call SurfaceFlinger 1041 i32 0 >/dev/null 2>&1'
|
||||
),
|
||||
"transactions_legacy": TraceTarget(
|
||||
[
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "transaction_trace", "transactions"),
|
||||
WinscopeFileMatcher(WINSCOPE_DIR, "transaction_trace", "transactions_legacy"),
|
||||
FileMatcher(WINSCOPE_DIR, f'transaction_merges_*', "transaction_merges"),
|
||||
],
|
||||
'su root service call SurfaceFlinger 1020 i32 1\necho "SF transactions recording started."',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Buffer, RectF, Transform, Matrix, Color, Rect, Region } from '../../src/flickerlib/common.js';
|
||||
import { ActiveBuffer, RectF, Transform, Matrix33, 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 standardTransform = new Transform(0, new Matrix33(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);
|
||||
@@ -23,13 +23,13 @@ const expectedEmptyRegionLayer = {
|
||||
z: -1,
|
||||
zOrderRelativeOf: null,
|
||||
parentId: 579,
|
||||
activeBuffer: new Buffer(1440, 2614, 1472, 1),
|
||||
activeBuffer: new ActiveBuffer(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)),
|
||||
transform: new Transform(0, new Matrix33(1, 0, 37.37078094482422, 0, 1, -3.5995326042175293)),
|
||||
visibleRegion: new Region([new Rect(37, 43, 146, 152)]),
|
||||
};
|
||||
const emptyRegionProto = {
|
||||
@@ -114,7 +114,7 @@ const expectedInvalidLayerVisibilityLayer = {
|
||||
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),
|
||||
activeBuffer: new ActiveBuffer(1440, 2880, 1472, 1),
|
||||
bufferTransform: standardTransform,
|
||||
color: new Color(-1, -1, -1, 0),
|
||||
hwcFrame: standardRect,
|
||||
@@ -202,13 +202,13 @@ const expectedOrphanLayersLayer = {
|
||||
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),
|
||||
activeBuffer: new ActiveBuffer(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)),
|
||||
transform: new Transform(0, new Matrix33(1, 0, 0, 0, 1, 98)),
|
||||
visibleRegion: new Region([new Rect(0, 98, 1440, 2712)]),
|
||||
};
|
||||
const expectedOrphanLayersProto = {
|
||||
@@ -294,7 +294,7 @@ const expectedRootLayer = {
|
||||
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),
|
||||
activeBuffer: new ActiveBuffer(1440, 2960, 1472, 1),
|
||||
chips: [VISIBLE_CHIP],
|
||||
bufferTransform: standardTransform,
|
||||
color: standardColor,
|
||||
@@ -398,7 +398,7 @@ const expectedRootAospLayer = {
|
||||
z: 0,
|
||||
zOrderRelativeOf: null,
|
||||
parentId: 41,
|
||||
activeBuffer: new Buffer(1440, 2880, 1472, 1),
|
||||
activeBuffer: new ActiveBuffer(1440, 2880, 1472, 1),
|
||||
bufferTransform: standardTransform,
|
||||
chips: [VISIBLE_CHIP],
|
||||
color: standardColor,
|
||||
|
||||
@@ -188,8 +188,8 @@ const TRACES = {
|
||||
'layers_trace': {
|
||||
name: 'Surface Flinger',
|
||||
},
|
||||
'transaction': {
|
||||
name: 'Transactions',
|
||||
'transactions': {
|
||||
name: 'Transaction',
|
||||
},
|
||||
'proto_log': {
|
||||
name: 'ProtoLog',
|
||||
@@ -269,6 +269,7 @@ const proxyFileTypeAdapter = {
|
||||
'wl_dump': FILE_TYPES.WAYLAND_DUMP,
|
||||
'screen_recording': FILE_TYPES.SCREEN_RECORDING,
|
||||
'transactions': FILE_TYPES.TRANSACTIONS_TRACE,
|
||||
'transactions_legacy': FILE_TYPES.TRANSACTIONS_TRACE_LEGACY,
|
||||
'proto_log': FILE_TYPES.PROTO_LOG,
|
||||
'system_ui_trace': FILE_TYPES.SYSTEM_UI,
|
||||
'launcher_trace': FILE_TYPES.LAUNCHER,
|
||||
|
||||
@@ -57,8 +57,8 @@
|
||||
:presentErrors="presentErrors"
|
||||
ref="view"
|
||||
/>
|
||||
<transactionsview
|
||||
v-else-if="isTransactions(file) && isShowFileType(file.type)"
|
||||
<transactionsviewlegacy
|
||||
v-else-if="isTransactionsLegacy(file) && isShowFileType(file.type)"
|
||||
:trace="file"
|
||||
ref="view"
|
||||
/>
|
||||
@@ -87,7 +87,7 @@ import TraceView from '@/TraceView.vue';
|
||||
import AccessibilityTraceView from '@/AccessibilityTraceView.vue';
|
||||
import WindowManagerTraceView from '@/WindowManagerTraceView.vue';
|
||||
import SurfaceFlingerTraceView from '@/SurfaceFlingerTraceView.vue';
|
||||
import TransactionsView from '@/TransactionsView.vue';
|
||||
import TransactionsViewLegacy from '@/TransactionsViewLegacy.vue';
|
||||
import LogView from '@/LogView.vue';
|
||||
import FileType from '@/mixins/FileType.js';
|
||||
import FlatCard from '@/components/FlatCard.vue';
|
||||
@@ -181,7 +181,7 @@ export default {
|
||||
mixins: [FileType],
|
||||
components: {
|
||||
'traceview': TraceView,
|
||||
'transactionsview': TransactionsView,
|
||||
'transactionsviewlegacy': TransactionsViewLegacy,
|
||||
'logview': LogView,
|
||||
'flat-card': FlatCard,
|
||||
AccessibilityTraceView,
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
import { shortenName } from './flickerlib/mixin'
|
||||
|
||||
export default {
|
||||
name: 'transaction-entry',
|
||||
name: 'transaction-entry-legacy',
|
||||
props: {
|
||||
index: {
|
||||
type: Number,
|
||||
@@ -152,14 +152,17 @@
|
||||
<script>
|
||||
import TreeView from './TreeView.vue';
|
||||
import VirtualList from '../libs/virtualList/VirtualList';
|
||||
import TransactionEntry from './TransactionEntry.vue';
|
||||
import TransactionEntryLegacy from './TransactionEntryLegacy.vue';
|
||||
import FlatCard from './components/FlatCard.vue';
|
||||
|
||||
import {ObjectTransformer} from './transform.js';
|
||||
import {expandTransactionId} from '@/traces/Transactions.ts';
|
||||
import {expandTransactionId} from '@/traces/TransactionsLegacy.ts';
|
||||
|
||||
/**
|
||||
* @deprecated This trace has been replaced by the new transactions trace
|
||||
*/
|
||||
export default {
|
||||
name: 'transactionsview',
|
||||
name: 'transactionsviewlegacy',
|
||||
props: ['trace'],
|
||||
data() {
|
||||
const transactionTypes = new Set();
|
||||
@@ -201,7 +204,7 @@ export default {
|
||||
filters: [],
|
||||
selectedProperties: [],
|
||||
selectedTransaction: null,
|
||||
transactionEntryComponent: TransactionEntry,
|
||||
transactionEntryComponent: TransactionEntryLegacy,
|
||||
transactionsTrace,
|
||||
expandTransactionId,
|
||||
};
|
||||
@@ -21,7 +21,8 @@ import jsonProtoDefsAccessibility from 'frameworks/base/core/proto/android/serve
|
||||
import jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
|
||||
import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/internal/protolog.proto';
|
||||
import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto';
|
||||
import jsonProtoDefsTransaction from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto';
|
||||
import jsonProtoDefsTransaction from 'frameworks/native/services/surfaceflinger/layerproto/transactions.proto';
|
||||
import jsonProtoDefsTransactionLegacy from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto';
|
||||
import jsonProtoDefsWl from 'WaylandSafePath/waylandtrace.proto';
|
||||
import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto';
|
||||
import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto';
|
||||
@@ -31,6 +32,7 @@ import jsonProtoDefsErrors from 'platform_testing/libraries/flicker/src/com/andr
|
||||
import protobuf from 'protobufjs';
|
||||
import {transform_accessibility_trace} from './transform_accessibility.js';
|
||||
import {transform_transaction_trace} from './transform_transaction.js';
|
||||
import {transform_transaction_trace_legacy} from './transform_transaction_legacy.js';
|
||||
import {transform_wl_outputstate, transform_wayland_trace} from './transform_wl.js';
|
||||
import {transformProtolog} from './transform_protolog.js';
|
||||
import {transform_sysui_trace} from './transform_sys_ui.js';
|
||||
@@ -42,6 +44,7 @@ import AccessibilityTrace from '@/traces/Accessibility.ts';
|
||||
import SurfaceFlingerTrace from '@/traces/SurfaceFlinger.ts';
|
||||
import WindowManagerTrace from '@/traces/WindowManager.ts';
|
||||
import TransactionsTrace from '@/traces/Transactions.ts';
|
||||
import TransactionsTraceLegacy from '@/traces/TransactionsLegacy.ts';
|
||||
import ScreenRecordingTrace from '@/traces/ScreenRecording.ts';
|
||||
import WaylandTrace from '@/traces/Wayland.ts';
|
||||
import ProtoLogTrace from '@/traces/ProtoLog.ts';
|
||||
@@ -63,7 +66,8 @@ const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.Windo
|
||||
const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto');
|
||||
const SfTraceMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersTraceFileProto');
|
||||
const SfDumpMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersProto');
|
||||
const SfTransactionTraceMessage = lookup_type(jsonProtoDefsTransaction, 'Trace');
|
||||
const SfTransactionTraceMessage = lookup_type(jsonProtoDefsTransaction, 'TransactionTraceFile');
|
||||
const SfTransactionTraceMessageLegacy = lookup_type(jsonProtoDefsTransactionLegacy, 'Trace');
|
||||
const WaylandTraceMessage = lookup_type(jsonProtoDefsWl, 'org.chromium.arc.wayland_composer.TraceFileProto');
|
||||
const WaylandDumpMessage = lookup_type(jsonProtoDefsWl, 'org.chromium.arc.wayland_composer.OutputStateProto');
|
||||
const ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, 'com.android.internal.protolog.ProtoLogFileProto');
|
||||
@@ -77,6 +81,7 @@ const ErrorTraceMessage = lookup_type(jsonProtoDefsErrors, 'com.android.server.w
|
||||
|
||||
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 TRANSACTIONS_TRACE_MAGIC_NUMBER = [0x09, 0x54, 0x4e, 0x58, 0x54, 0x52, 0x41, 0x43, 0x45]; // .TNXTRACE
|
||||
const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WINTRACE
|
||||
const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42
|
||||
const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WYLTRACE
|
||||
@@ -97,6 +102,7 @@ const FILE_TYPES = Object.freeze({
|
||||
SURFACE_FLINGER_DUMP: 'SurfaceFlingerDump',
|
||||
SCREEN_RECORDING: 'ScreenRecording',
|
||||
TRANSACTIONS_TRACE: 'TransactionsTrace',
|
||||
TRANSACTIONS_TRACE_LEGACY: 'TransactionsTraceLegacy',
|
||||
WAYLAND_TRACE: 'WaylandTrace',
|
||||
WAYLAND_DUMP: 'WaylandDump',
|
||||
PROTO_LOG: 'ProtoLog',
|
||||
@@ -130,6 +136,7 @@ const FILE_ICONS = {
|
||||
[FILE_TYPES.SURFACE_FLINGER_DUMP]: SURFACE_FLINGER_ICON,
|
||||
[FILE_TYPES.SCREEN_RECORDING]: SCREEN_RECORDING_ICON,
|
||||
[FILE_TYPES.TRANSACTIONS_TRACE]: TRANSACTION_ICON,
|
||||
[FILE_TYPES.TRANSACTIONS_TRACE_LEGACY]: TRANSACTION_ICON,
|
||||
[FILE_TYPES.WAYLAND_TRACE]: WAYLAND_ICON,
|
||||
[FILE_TYPES.WAYLAND_DUMP]: WAYLAND_ICON,
|
||||
[FILE_TYPES.PROTO_LOG]: PROTO_LOG_ICON,
|
||||
@@ -152,6 +159,7 @@ const TRACE_TYPES = Object.freeze({
|
||||
SURFACE_FLINGER: 'SurfaceFlingerTrace',
|
||||
SCREEN_RECORDING: 'ScreenRecording',
|
||||
TRANSACTION: 'Transaction',
|
||||
TRANSACTION_LEGACY: 'Transaction (Legacy)',
|
||||
WAYLAND: 'Wayland',
|
||||
PROTO_LOG: 'ProtoLog',
|
||||
SYSTEM_UI: 'SystemUI',
|
||||
@@ -196,6 +204,14 @@ const TRACE_INFO = {
|
||||
],
|
||||
constructor: TransactionsTrace,
|
||||
},
|
||||
[TRACE_TYPES.TRANSACTION_LEGACY]: {
|
||||
name: 'Transactions (Legacy)',
|
||||
icon: TRANSACTION_ICON,
|
||||
files: [
|
||||
oneOf(FILE_TYPES.TRANSACTIONS_TRACE_LEGACY),
|
||||
],
|
||||
constructor: TransactionsTraceLegacy,
|
||||
},
|
||||
[TRACE_TYPES.WAYLAND]: {
|
||||
name: 'Wayland',
|
||||
icon: WAYLAND_ICON,
|
||||
@@ -284,6 +300,7 @@ export const TRACE_ICONS = {
|
||||
[TRACE_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
|
||||
[TRACE_TYPES.SCREEN_RECORDING]: SCREEN_RECORDING_ICON,
|
||||
[TRACE_TYPES.TRANSACTION]: TRANSACTION_ICON,
|
||||
[TRACE_TYPES.TRANSACTION_LEGACY]: TRANSACTION_ICON,
|
||||
[TRACE_TYPES.WAYLAND]: WAYLAND_ICON,
|
||||
[TRACE_TYPES.PROTO_LOG]: PROTO_LOG_ICON,
|
||||
[TRACE_TYPES.SYSTEM_UI]: SYSTEM_UI_ICON,
|
||||
@@ -396,6 +413,17 @@ const FILE_DECODERS = {
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
[FILE_TYPES.TRANSACTIONS_TRACE_LEGACY]: {
|
||||
name: 'Transactions (Legacy)',
|
||||
decoder: protoDecoder,
|
||||
decoderParams: {
|
||||
type: FILE_TYPES.TRANSACTIONS_TRACE_LEGACY,
|
||||
mime: 'application/octet-stream',
|
||||
objTypeProto: SfTransactionTraceMessageLegacy,
|
||||
transform: transform_transaction_trace_legacy,
|
||||
timeline: true,
|
||||
},
|
||||
},
|
||||
[FILE_TYPES.PROTO_LOG]: {
|
||||
name: 'ProtoLog',
|
||||
decoder: protoDecoder,
|
||||
@@ -643,6 +671,9 @@ function detectAndDecode(buffer, fileName, store) {
|
||||
if (arrayStartsWith(buffer, MPEG4_MAGIC_NMBER)) {
|
||||
return decodedFile(FILE_TYPES.SCREEN_RECORDING, buffer, fileName, store);
|
||||
}
|
||||
if (arrayStartsWith(buffer, TRANSACTIONS_TRACE_MAGIC_NUMBER)) {
|
||||
return decodedFile(FILE_TYPES.TRANSACTIONS_TRACE, buffer, fileName, store);
|
||||
}
|
||||
if (arrayStartsWith(buffer, WAYLAND_TRACE_MAGIC_NUMBER)) {
|
||||
return decodedFile(FILE_TYPES.WAYLAND_TRACE, buffer, fileName, store);
|
||||
}
|
||||
@@ -673,10 +704,10 @@ function detectAndDecode(buffer, fileName, store) {
|
||||
|
||||
// TODO(b/169305853): Add magic number at beginning of file for better auto detection
|
||||
for (const [filetype, condition] of [
|
||||
[FILE_TYPES.TRANSACTIONS_TRACE, (file) => file.data.length > 0],
|
||||
[FILE_TYPES.TRANSACTIONS_TRACE_LEGACY, (file) => file.data.length > 0],
|
||||
[FILE_TYPES.WAYLAND_DUMP, (file) => (file.data.length > 0 && file.data.children[0] > 0) || file.data.length > 1],
|
||||
[FILE_TYPES.WINDOW_MANAGER_DUMP],
|
||||
[FILE_TYPES.SURFACE_FLINGER_DUMP],
|
||||
[FILE_TYPES.SURFACE_FLINGER_DUMP]
|
||||
]) {
|
||||
try {
|
||||
const [, fileData] = decodedFile(filetype, buffer, fileName, store);
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {toSize, toBuffer, toColor, toPoint, toRect,
|
||||
toRectF, toRegion, toTransform} from './common';
|
||||
import {toSize, toActiveBuffer, toColor, toColor3, toPoint, toRect,
|
||||
toRectF, toRegion, toMatrix22, toTransform} from './common';
|
||||
import intDefMapping from
|
||||
'../../../../../prebuilts/misc/common/winscope/intDefMapping.json';
|
||||
import config from '../config/Configuration.json'
|
||||
@@ -99,7 +99,7 @@ export default class ObjectFormatter {
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
if (this.displayDefaults) {
|
||||
result[key] = value
|
||||
result[key] = value;
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -112,31 +112,35 @@ export default class ObjectFormatter {
|
||||
} else if (value.prettyPrint) {
|
||||
const isEmpty = value.isEmpty === true;
|
||||
if (!isEmpty || this.displayDefaults) {
|
||||
result[key] = value.prettyPrint()
|
||||
result[key] = value.prettyPrint();
|
||||
}
|
||||
} else {
|
||||
// converted proto to flicker
|
||||
const translatedObject = this.translateObject(value)
|
||||
const translatedObject = this.translateObject(value);
|
||||
if (translatedObject) {
|
||||
result[key] = translatedObject.prettyPrint()
|
||||
if (translatedObject.prettyPrint) {
|
||||
result[key] = translatedObject.prettyPrint();
|
||||
}
|
||||
else {
|
||||
result[key] = translatedObject;
|
||||
}
|
||||
// objects - recursive call
|
||||
} else if (value && typeof(value) == `object`) {
|
||||
const childObj = this.format(value) as any
|
||||
const isEmpty = Object.entries(childObj).length == 0 || childObj.isEmpty
|
||||
const childObj = this.format(value) as any;
|
||||
const isEmpty = Object.entries(childObj).length == 0 || childObj.isEmpty;
|
||||
if (!isEmpty || this.displayDefaults) {
|
||||
result[key] = childObj
|
||||
result[key] = childObj;
|
||||
}
|
||||
} else {
|
||||
// values
|
||||
result[key] = this.translateIntDef(obj, key, value)
|
||||
result[key] = this.translateIntDef(obj, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
// return Object.freeze(result)
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,23 +151,25 @@ export default class ObjectFormatter {
|
||||
* @param obj Object to translate
|
||||
*/
|
||||
private static translateObject(obj) {
|
||||
const type = obj?.$type?.name
|
||||
const type = obj?.$type?.name;
|
||||
switch(type) {
|
||||
case `SizeProto`: return toSize(obj)
|
||||
case `ActiveBufferProto`: return toBuffer(obj)
|
||||
case `ColorProto`: return toColor(obj)
|
||||
case `PointProto`: return toPoint(obj)
|
||||
case `RectProto`: return toRect(obj)
|
||||
case `FloatRectProto`: return toRectF(obj)
|
||||
case `RegionProto`: return toRegion(obj)
|
||||
case `TransformProto`: return toTransform(obj)
|
||||
case `SizeProto`: return toSize(obj);
|
||||
case `ActiveBufferProto`: return toActiveBuffer(obj);
|
||||
case `Color3`: return toColor3(obj);
|
||||
case `ColorProto`: return toColor(obj);
|
||||
case `PointProto`: return toPoint(obj);
|
||||
case `RectProto`: return toRect(obj);
|
||||
case `Matrix22`: return toMatrix22(obj);
|
||||
case `FloatRectProto`: return toRectF(obj);
|
||||
case `RegionProto`: return toRegion(obj);
|
||||
case `TransformProto`: return toTransform(obj);
|
||||
case 'ColorTransformProto': {
|
||||
const formatted = this.formatColorTransform(obj.val);
|
||||
return `${formatted}`;
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
private static formatColorTransform(vals) {
|
||||
@@ -184,17 +190,17 @@ export default class ObjectFormatter {
|
||||
* @param propertyName Property to search
|
||||
*/
|
||||
private static getTypeDefSpec(obj: any, propertyName: string): string {
|
||||
const fields = obj?.$type?.fields
|
||||
const fields = obj?.$type?.fields;
|
||||
if (!fields) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
const options = fields[propertyName]?.options
|
||||
const options = fields[propertyName]?.options;
|
||||
if (!options) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
return options["(.android.typedef)"]
|
||||
return options["(.android.typedef)"];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,23 +213,23 @@ export default class ObjectFormatter {
|
||||
* @param value Property value
|
||||
*/
|
||||
private static translateIntDef(parentObj: any, propertyName: string, value: any): string {
|
||||
const parentClassName = parentObj.constructor.name
|
||||
const propertyPath = `${parentClassName}.${propertyName}`
|
||||
const parentClassName = parentObj.constructor.name;
|
||||
const propertyPath = `${parentClassName}.${propertyName}`;
|
||||
|
||||
let translatedValue = value
|
||||
let translatedValue = value;
|
||||
// Parse Flicker objects (no intdef annotation supported)
|
||||
if (this.FLICKER_INTDEF_MAP.has(propertyPath)) {
|
||||
translatedValue = this.getIntFlagsAsStrings(value,
|
||||
this.FLICKER_INTDEF_MAP.get(propertyPath))
|
||||
this.FLICKER_INTDEF_MAP.get(propertyPath));
|
||||
} else {
|
||||
// If it's a proto, search on the proto definition for the intdef type
|
||||
const typeDefSpec = this.getTypeDefSpec(parentObj, propertyName)
|
||||
const typeDefSpec = this.getTypeDefSpec(parentObj, propertyName);
|
||||
if (typeDefSpec) {
|
||||
translatedValue = this.getIntFlagsAsStrings(value, typeDefSpec)
|
||||
translatedValue = this.getIntFlagsAsStrings(value, typeDefSpec);
|
||||
}
|
||||
}
|
||||
|
||||
return translatedValue
|
||||
return translatedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -62,8 +62,10 @@ const LayerTraceEntryBuilder = require('flicker').com.android.server.wm.traces.
|
||||
common.layers.LayerTraceEntryBuilder;
|
||||
const LayersTrace = require('flicker').com.android.server.wm.traces.common.
|
||||
layers.LayersTrace;
|
||||
const Matrix = require('flicker').com.android.server.wm.traces.common.layers.
|
||||
Transform.Matrix;
|
||||
const Matrix22 = require('flicker').com.android.server.wm.traces.common
|
||||
.Matrix22;
|
||||
const Matrix33 = require('flicker').com.android.server.wm.traces.common
|
||||
.Matrix33;
|
||||
const Transform = require('flicker').com.android.server.wm.traces.common.
|
||||
layers.Transform;
|
||||
const Display = require('flicker').com.android.server.wm.traces.common.
|
||||
@@ -71,7 +73,9 @@ const Display = require('flicker').com.android.server.wm.traces.common.
|
||||
|
||||
// Common
|
||||
const Size = require('flicker').com.android.server.wm.traces.common.Size;
|
||||
const Buffer = require('flicker').com.android.server.wm.traces.common.Buffer;
|
||||
const ActiveBuffer = require('flicker').com.android.server.wm.traces.common
|
||||
.ActiveBuffer;
|
||||
const Color3 = require('flicker').com.android.server.wm.traces.common.Color3;
|
||||
const Color = require('flicker').com.android.server.wm.traces.common.Color;
|
||||
const Point = require('flicker').com.android.server.wm.traces.common.Point;
|
||||
const Rect = require('flicker').com.android.server.wm.traces.common.Rect;
|
||||
@@ -91,13 +95,15 @@ const ErrorTrace = require('flicker').com.android.server.wm.traces.common.errors
|
||||
// Service
|
||||
const TaggingEngine = require('flicker').com.android.server.wm.traces.common.service.TaggingEngine;
|
||||
|
||||
const EMPTY_BUFFER = new Buffer(0, 0, 0, 0);
|
||||
const EMPTY_BUFFER = new ActiveBuffer(0, 0, 0, 0);
|
||||
const EMPTY_COLOR3 = new Color3(-1, -1, -1);
|
||||
const EMPTY_COLOR = new Color(-1, -1, -1, 0);
|
||||
const EMPTY_RECT = new Rect(0, 0, 0, 0);
|
||||
const EMPTY_RECTF = new RectF(0, 0, 0, 0);
|
||||
const EMPTY_POINT = new Point(0, 0);
|
||||
const EMPTY_MATRIX = new Matrix(0, 0, 0, 0, 0, 0);
|
||||
const EMPTY_TRANSFORM = new Transform(0, EMPTY_MATRIX);
|
||||
const EMPTY_MATRIX22 = new Matrix22(0, 0, 0, 0, 0, 0);
|
||||
const EMPTY_MATRIX33 = new Matrix33(0, 0, 0, 0, 0, 0);
|
||||
const EMPTY_TRANSFORM = new Transform(0, EMPTY_MATRIX33);
|
||||
|
||||
function toSize(proto) {
|
||||
if (proto == null) {
|
||||
@@ -111,18 +117,31 @@ function toSize(proto) {
|
||||
return EMPTY_BOUNDS;
|
||||
}
|
||||
|
||||
function toBuffer(proto) {
|
||||
function toActiveBuffer(proto) {
|
||||
const width = proto?.width ?? 0;
|
||||
const height = proto?.height ?? 0;
|
||||
const stride = proto?.stride ?? 0;
|
||||
const format = proto?.format ?? 0;
|
||||
|
||||
if (width || height || stride || format) {
|
||||
return new Buffer(width, height, stride, format);
|
||||
return new ActiveBuffer(width, height, stride, format);
|
||||
}
|
||||
return EMPTY_BUFFER;
|
||||
}
|
||||
|
||||
function toColor3(proto) {
|
||||
if (proto == null) {
|
||||
return EMPTY_COLOR;
|
||||
}
|
||||
const r = proto.r ?? 0;
|
||||
const g = proto.g ?? 0;
|
||||
const b = proto.b ?? 0;
|
||||
if (r || g || b) {
|
||||
return new Color3(r, g, b);
|
||||
}
|
||||
return EMPTY_COLOR3;
|
||||
}
|
||||
|
||||
function toColor(proto) {
|
||||
if (proto == null) {
|
||||
return EMPTY_COLOR;
|
||||
@@ -206,16 +225,32 @@ function toTransform(proto) {
|
||||
const ty = proto.ty ?? 0;
|
||||
|
||||
if (dsdx || dtdx || tx || dsdy || dtdy || ty) {
|
||||
const matrix = new Matrix(dsdx, dtdx, tx, dsdy, dtdy, ty);
|
||||
const matrix = new Matrix33(dsdx, dtdx, tx, dsdy, dtdy, ty);
|
||||
return new Transform(proto.type ?? 0, matrix);
|
||||
}
|
||||
|
||||
if (proto.type) {
|
||||
return new Transform(proto.type ?? 0, EMPTY_MATRIX);
|
||||
return new Transform(proto.type ?? 0, EMPTY_MATRIX33);
|
||||
}
|
||||
return EMPTY_TRANSFORM;
|
||||
}
|
||||
|
||||
function toMatrix22(proto) {
|
||||
if (proto == null) {
|
||||
return EMPTY_MATRIX22;
|
||||
}
|
||||
const dsdx = proto.dsdx ?? 0;
|
||||
const dtdx = proto.dtdx ?? 0;
|
||||
const dsdy = proto.dsdy ?? 0;
|
||||
const dtdy = proto.dtdy ?? 0;
|
||||
|
||||
if (dsdx || dtdx || dsdy || dtdy) {
|
||||
return new Matrix22(dsdx, dtdx, dsdy, dtdy);
|
||||
}
|
||||
|
||||
return EMPTY_MATRIX22;
|
||||
}
|
||||
|
||||
export {
|
||||
Activity,
|
||||
Configuration,
|
||||
@@ -240,7 +275,8 @@ export {
|
||||
LayerTraceEntryBuilder,
|
||||
LayersTrace,
|
||||
Transform,
|
||||
Matrix,
|
||||
Matrix22,
|
||||
Matrix33,
|
||||
Display,
|
||||
// Tags
|
||||
Tag,
|
||||
@@ -252,8 +288,9 @@ export {
|
||||
ErrorTrace,
|
||||
// Common
|
||||
Size,
|
||||
Buffer,
|
||||
ActiveBuffer,
|
||||
Color,
|
||||
Color3,
|
||||
Point,
|
||||
Rect,
|
||||
RectF,
|
||||
@@ -261,11 +298,13 @@ export {
|
||||
// Service
|
||||
TaggingEngine,
|
||||
toSize,
|
||||
toBuffer,
|
||||
toActiveBuffer,
|
||||
toColor,
|
||||
toColor3,
|
||||
toPoint,
|
||||
toRect,
|
||||
toRectF,
|
||||
toRegion,
|
||||
toMatrix22,
|
||||
toTransform,
|
||||
};
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
|
||||
|
||||
import { Layer, Rect, toBuffer, toColor, toRect, toRectF, toRegion } from "../common"
|
||||
import { Layer, Rect, toActiveBuffer, toColor, toRect, toRectF, toRegion } from "../common"
|
||||
import { shortenName } from '../mixin'
|
||||
import { RELATIVE_Z_CHIP, GPU_CHIP, HWC_CHIP } from '../treeview/Chips'
|
||||
import Transform from './Transform'
|
||||
|
||||
Layer.fromProto = function (proto: any): Layer {
|
||||
const visibleRegion = toRegion(proto.visibleRegion)
|
||||
const activeBuffer = toBuffer(proto.activeBuffer)
|
||||
const activeBuffer = toActiveBuffer(proto.activeBuffer)
|
||||
const bounds = toRectF(proto.bounds)
|
||||
const color = toColor(proto.color)
|
||||
const screenBounds = toRectF(proto.screenBounds)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Transform, Matrix } from "../common"
|
||||
import { Transform, Matrix33 } from "../common"
|
||||
|
||||
Transform.fromProto = function (transformProto, positionProto): Transform {
|
||||
const entry = new Transform(
|
||||
@@ -24,7 +24,7 @@ Transform.fromProto = function (transformProto, positionProto): Transform {
|
||||
return entry
|
||||
}
|
||||
|
||||
function getMatrix(transform, position): Matrix {
|
||||
function getMatrix(transform, position): Matrix33 {
|
||||
const x = position?.x ?? 0
|
||||
const y = position?.y ?? 0
|
||||
|
||||
@@ -32,33 +32,33 @@ function getMatrix(transform, position): Matrix {
|
||||
return getDefaultTransform(transform?.type, x, y)
|
||||
}
|
||||
|
||||
return new Matrix(transform.dsdx, transform.dtdx, x, transform.dsdy, transform.dtdy, y)
|
||||
return new Matrix33(transform.dsdx, transform.dtdx, x, transform.dsdy, transform.dtdy, y)
|
||||
}
|
||||
|
||||
function getDefaultTransform(type, x, y): Matrix {
|
||||
function getDefaultTransform(type, x, y): Matrix33 {
|
||||
// IDENTITY
|
||||
if (!type) {
|
||||
return new Matrix(1, 0, x, 0, 1, y)
|
||||
return new Matrix33(1, 0, x, 0, 1, y)
|
||||
}
|
||||
|
||||
// ROT_270 = ROT_90|FLIP_H|FLIP_V
|
||||
if (isFlagSet(type, ROT_90_VAL | FLIP_V_VAL | FLIP_H_VAL)) {
|
||||
return new Matrix(0, -1, x, 1, 0, y)
|
||||
return new Matrix33(0, -1, x, 1, 0, y)
|
||||
}
|
||||
|
||||
// ROT_180 = FLIP_H|FLIP_V
|
||||
if (isFlagSet(type, FLIP_V_VAL | FLIP_H_VAL)) {
|
||||
return new Matrix(-1, 0, x, 0, -1, y)
|
||||
return new Matrix33(-1, 0, x, 0, -1, y)
|
||||
}
|
||||
|
||||
// ROT_90
|
||||
if (isFlagSet(type, ROT_90_VAL)) {
|
||||
return new Matrix(0, 1, x, -1, 0, y)
|
||||
return new Matrix33(0, 1, x, -1, 0, y)
|
||||
}
|
||||
|
||||
// IDENTITY
|
||||
if (isFlagClear(type, SCALE_VAL | ROTATE_VAL)) {
|
||||
return new Matrix(1, 0, x, 0, 1, y)
|
||||
return new Matrix33(1, 0, x, 0, 1, y)
|
||||
}
|
||||
|
||||
throw new Error(`Unknown transform type ${type}`)
|
||||
@@ -87,4 +87,4 @@ const FLIP_V_VAL = 0x0200 // (1 << 1 << 8)
|
||||
const ROT_90_VAL = 0x0400 // (1 << 2 << 8)
|
||||
const ROT_INVALID_VAL = 0x8000 // (0x80 << 8)
|
||||
|
||||
export default Transform
|
||||
export default Transform
|
||||
|
||||
@@ -33,7 +33,7 @@ Vue.use(VueMaterial)
|
||||
const fileOrder = {
|
||||
[TRACE_TYPES.WINDOW_MANAGER]: 1,
|
||||
[TRACE_TYPES.SURFACE_FLINGER]: 2,
|
||||
[TRACE_TYPES.TRANSACTION]: 3,
|
||||
[TRACE_TYPES.TRANSACTION_LEGACY]: 3,
|
||||
[TRACE_TYPES.PROTO_LOG]: 4,
|
||||
[TRACE_TYPES.IME_CLIENTS]: 5,
|
||||
[TRACE_TYPES.IME_SERVICE]: 6,
|
||||
@@ -416,7 +416,7 @@ Vue.mixin({
|
||||
this.$gtag.event(string, {
|
||||
'event_category': "Opened trace",
|
||||
'event_label': "Winscope Interactions",
|
||||
'value': field,
|
||||
'value': entryType,
|
||||
});
|
||||
},
|
||||
recordChangedNavigationStyleEvent(field) {
|
||||
|
||||
@@ -21,6 +21,7 @@ const mixin = {
|
||||
return file.type == TRACE_TYPES.WINDOW_MANAGER ||
|
||||
file.type == TRACE_TYPES.ACCESSIBILITY ||
|
||||
file.type == TRACE_TYPES.SURFACE_FLINGER ||
|
||||
file.type == TRACE_TYPES.TRANSACTION ||
|
||||
file.type == TRACE_TYPES.WAYLAND ||
|
||||
file.type == TRACE_TYPES.SYSTEM_UI ||
|
||||
file.type == TRACE_TYPES.LAUNCHER ||
|
||||
@@ -48,12 +49,15 @@ const mixin = {
|
||||
isTransactions(file) {
|
||||
return file.type == TRACE_TYPES.TRANSACTION;
|
||||
},
|
||||
isTransactionsLegacy(file) {
|
||||
return file.type == TRACE_TYPES.TRANSACTION_LEGACY;
|
||||
},
|
||||
isLog(file) {
|
||||
return file.type == TRACE_TYPES.PROTO_LOG;
|
||||
},
|
||||
hasDataView(file) {
|
||||
return this.isLog(file) || this.showInTraceView(file) ||
|
||||
this.isTransactions(file);
|
||||
this.isTransactionsLegacy(file);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020, The Android Open Source Project
|
||||
* 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.
|
||||
@@ -17,9 +17,8 @@
|
||||
import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
|
||||
import TraceBase from './TraceBase';
|
||||
|
||||
export default class Transactions extends TraceBase {
|
||||
export default class TransactionsTrace extends TraceBase {
|
||||
transactionsFile: Object;
|
||||
transactionHistory: TransactionHistory;
|
||||
|
||||
constructor(files: any[]) {
|
||||
const transactionsFile = files[FILE_TYPES.TRANSACTIONS_TRACE];
|
||||
@@ -27,197 +26,9 @@ export default class Transactions extends TraceBase {
|
||||
super(transactionsFile.data, transactionsFile.timeline, files);
|
||||
|
||||
this.transactionsFile = transactionsFile;
|
||||
|
||||
// Create new transaction history
|
||||
this.transactionHistory = new TransactionHistory(transactionsFile);
|
||||
}
|
||||
|
||||
get type() {
|
||||
return TRACE_TYPES.TRANSACTION;
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionHistory {
|
||||
history: Object;
|
||||
applied: Object;
|
||||
mergeTrees: any;
|
||||
|
||||
constructor(transactionsEventsFiles) {
|
||||
this.history = {};
|
||||
this.applied = {};
|
||||
|
||||
if (!transactionsEventsFiles) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const eventsFile of transactionsEventsFiles) {
|
||||
for (const event of eventsFile.data) {
|
||||
if (event.merge) {
|
||||
const merge = event.merge;
|
||||
const originalId = merge.originalTransaction.id;
|
||||
const mergedId = merge.mergedTransaction.id;
|
||||
|
||||
this.addMerge(originalId, mergedId);
|
||||
} else if (event.apply) {
|
||||
this.addApply(event.apply.tx_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addMerge(originalId, mergedId) {
|
||||
const merge = new Merge(originalId, mergedId, this.history);
|
||||
this.addToHistoryOf(originalId, merge);
|
||||
}
|
||||
|
||||
addApply(transactionId) {
|
||||
this.applied[transactionId] = true;
|
||||
this.addToHistoryOf(transactionId, new Apply(transactionId));
|
||||
}
|
||||
|
||||
addToHistoryOf(transactionId, event) {
|
||||
if (!this.history[transactionId]) {
|
||||
this.history[transactionId] = [];
|
||||
}
|
||||
this.history[transactionId].push(event);
|
||||
}
|
||||
|
||||
generateHistoryTreesOf(transactionId) {
|
||||
return this._generateHistoryTree(transactionId);
|
||||
}
|
||||
|
||||
_generateHistoryTree(transactionId, upTo = null) {
|
||||
if (!this.history[transactionId]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const children = [];
|
||||
const events = this.history[transactionId];
|
||||
for (let i = 0; i < (upTo ?? events.length); i++) {
|
||||
const event = events[i];
|
||||
|
||||
if (event instanceof Merge) {
|
||||
const historyTree = this.
|
||||
_generateHistoryTree(event.mergedId, event.mergedAt);
|
||||
const mergeTreeNode = new MergeTreeNode(event.mergedId, historyTree);
|
||||
children.push(mergeTreeNode);
|
||||
} else if (event instanceof Apply) {
|
||||
children.push(new ApplyTreeNode());
|
||||
} else {
|
||||
throw new Error('Unhandled event type');
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the list of all the transactions that have ever been merged into
|
||||
* the target transaction directly or indirectly through the merges of
|
||||
* transactions that ended up being merged into the transaction.
|
||||
* This includes both merges that occur before and after the transaction is
|
||||
* applied.
|
||||
* @param {Number} transactionId - The id of the transaction we want the list
|
||||
* of transactions merged in for
|
||||
* @return {Set<Number>} a set of all the transaction ids that are in the
|
||||
* history of merges of the transaction
|
||||
*/
|
||||
allTransactionsMergedInto(transactionId) {
|
||||
const allTransactionsMergedIn = new Set();
|
||||
|
||||
let event;
|
||||
const toVisit = this.generateHistoryTreesOf(transactionId);
|
||||
while (event = toVisit.pop()) {
|
||||
if (event instanceof MergeTreeNode) {
|
||||
allTransactionsMergedIn.add(event.mergedId);
|
||||
for (const child of event.children) {
|
||||
toVisit.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allTransactionsMergedIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generated the list of transactions that have been directly merged into the
|
||||
* target transaction those are transactions that have explicitly been merged
|
||||
* in the code with a call to merge.
|
||||
* @param {Number} transactionId - The id of the target transaction.
|
||||
* @return {Array<Number>} an array of the transaction ids of the transactions
|
||||
* directly merged into the target transaction
|
||||
*/
|
||||
allDirectMergesInto(transactionId) {
|
||||
return (this.history[transactionId] ?? [])
|
||||
.filter((event) => event instanceof Merge)
|
||||
.map((merge) => merge.mergedId);
|
||||
}
|
||||
}
|
||||
|
||||
class MergeTreeNode {
|
||||
mergedId: Number;
|
||||
mergedTransactionHistory: TransactionHistory;
|
||||
children: TransactionHistory[];
|
||||
|
||||
constructor(mergedId, mergedTransactionHistory) {
|
||||
this.mergedId = mergedId;
|
||||
this.mergedTransactionHistory = mergedTransactionHistory;
|
||||
this.children = mergedTransactionHistory;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'merge';
|
||||
}
|
||||
}
|
||||
|
||||
class ApplyTreeNode {
|
||||
children: any[];
|
||||
|
||||
constructor() {
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'apply';
|
||||
}
|
||||
}
|
||||
|
||||
class Merge {
|
||||
originalId: Number;
|
||||
mergedId: Number;
|
||||
mergedAt: Number;
|
||||
|
||||
constructor(originalId, mergedId, history) {
|
||||
this.originalId = originalId;
|
||||
this.mergedId = mergedId;
|
||||
// Specifies how long the merge chain of the merged transaction was at the
|
||||
// time is was merged.
|
||||
this.mergedAt = history[mergedId]?.length ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Apply {
|
||||
transactionId: Number;
|
||||
|
||||
constructor(transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the transactionId to the values that compose the identifier.
|
||||
* The top 32 bits is the PID of the process that created the transaction
|
||||
* and the bottom 32 bits is the ID of the transaction unique within that
|
||||
* process.
|
||||
* @param {Number} transactionId
|
||||
* @return {Object} An object containing the id and pid of the transaction.
|
||||
*/
|
||||
export function expandTransactionId(transactionId) {
|
||||
// Can't use bit shift operation because it isn't a 32 bit integer...
|
||||
// Because js uses floating point numbers for everything, maths isn't 100%
|
||||
// accurate so we need to round...
|
||||
return Object.freeze({
|
||||
id: Math.round(transactionId % Math.pow(2, 32)),
|
||||
pid: Math.round(transactionId / Math.pow(2, 32)),
|
||||
});
|
||||
}
|
||||
|
||||
226
tools/winscope/src/traces/TransactionsLegacy.ts
Normal file
226
tools/winscope/src/traces/TransactionsLegacy.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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';
|
||||
|
||||
/**
|
||||
* @deprecated This trace has been replaced by the new transactions trace
|
||||
*/
|
||||
export default class TransactionsTraceLegacy extends TraceBase {
|
||||
transactionsFile: Object;
|
||||
transactionHistory: TransactionHistory;
|
||||
|
||||
constructor(files: any[]) {
|
||||
const transactionsFile = files[FILE_TYPES.TRANSACTIONS_TRACE_LEGACY];
|
||||
|
||||
super(transactionsFile.data, transactionsFile.timeline, files);
|
||||
|
||||
this.transactionsFile = transactionsFile;
|
||||
|
||||
// Create new transaction history
|
||||
this.transactionHistory = new TransactionHistory(transactionsFile);
|
||||
}
|
||||
|
||||
get type() {
|
||||
return TRACE_TYPES.TRANSACTION_LEGACY;
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionHistory {
|
||||
history: Object;
|
||||
applied: Object;
|
||||
mergeTrees: any;
|
||||
|
||||
constructor(transactionsEventsFiles) {
|
||||
this.history = {};
|
||||
this.applied = {};
|
||||
|
||||
if (!transactionsEventsFiles) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const eventsFile of transactionsEventsFiles) {
|
||||
for (const event of eventsFile.data) {
|
||||
if (event.merge) {
|
||||
const merge = event.merge;
|
||||
const originalId = merge.originalTransaction.id;
|
||||
const mergedId = merge.mergedTransaction.id;
|
||||
|
||||
this.addMerge(originalId, mergedId);
|
||||
} else if (event.apply) {
|
||||
this.addApply(event.apply.tx_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addMerge(originalId, mergedId) {
|
||||
const merge = new Merge(originalId, mergedId, this.history);
|
||||
this.addToHistoryOf(originalId, merge);
|
||||
}
|
||||
|
||||
addApply(transactionId) {
|
||||
this.applied[transactionId] = true;
|
||||
this.addToHistoryOf(transactionId, new Apply(transactionId));
|
||||
}
|
||||
|
||||
addToHistoryOf(transactionId, event) {
|
||||
if (!this.history[transactionId]) {
|
||||
this.history[transactionId] = [];
|
||||
}
|
||||
this.history[transactionId].push(event);
|
||||
}
|
||||
|
||||
generateHistoryTreesOf(transactionId) {
|
||||
return this._generateHistoryTree(transactionId);
|
||||
}
|
||||
|
||||
_generateHistoryTree(transactionId, upTo = null) {
|
||||
if (!this.history[transactionId]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const children = [];
|
||||
const events = this.history[transactionId];
|
||||
for (let i = 0; i < (upTo ?? events.length); i++) {
|
||||
const event = events[i];
|
||||
|
||||
if (event instanceof Merge) {
|
||||
const historyTree = this.
|
||||
_generateHistoryTree(event.mergedId, event.mergedAt);
|
||||
const mergeTreeNode = new MergeTreeNode(event.mergedId, historyTree);
|
||||
children.push(mergeTreeNode);
|
||||
} else if (event instanceof Apply) {
|
||||
children.push(new ApplyTreeNode());
|
||||
} else {
|
||||
throw new Error('Unhandled event type');
|
||||
}
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the list of all the transactions that have ever been merged into
|
||||
* the target transaction directly or indirectly through the merges of
|
||||
* transactions that ended up being merged into the transaction.
|
||||
* This includes both merges that occur before and after the transaction is
|
||||
* applied.
|
||||
* @param {Number} transactionId - The id of the transaction we want the list
|
||||
* of transactions merged in for
|
||||
* @return {Set<Number>} a set of all the transaction ids that are in the
|
||||
* history of merges of the transaction
|
||||
*/
|
||||
allTransactionsMergedInto(transactionId) {
|
||||
const allTransactionsMergedIn = new Set();
|
||||
|
||||
let event;
|
||||
const toVisit = this.generateHistoryTreesOf(transactionId);
|
||||
while (event = toVisit.pop()) {
|
||||
if (event instanceof MergeTreeNode) {
|
||||
allTransactionsMergedIn.add(event.mergedId);
|
||||
for (const child of event.children) {
|
||||
toVisit.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allTransactionsMergedIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generated the list of transactions that have been directly merged into the
|
||||
* target transaction those are transactions that have explicitly been merged
|
||||
* in the code with a call to merge.
|
||||
* @param {Number} transactionId - The id of the target transaction.
|
||||
* @return {Array<Number>} an array of the transaction ids of the transactions
|
||||
* directly merged into the target transaction
|
||||
*/
|
||||
allDirectMergesInto(transactionId) {
|
||||
return (this.history[transactionId] ?? [])
|
||||
.filter((event) => event instanceof Merge)
|
||||
.map((merge) => merge.mergedId);
|
||||
}
|
||||
}
|
||||
|
||||
class MergeTreeNode {
|
||||
mergedId: Number;
|
||||
mergedTransactionHistory: TransactionHistory;
|
||||
children: TransactionHistory[];
|
||||
|
||||
constructor(mergedId, mergedTransactionHistory) {
|
||||
this.mergedId = mergedId;
|
||||
this.mergedTransactionHistory = mergedTransactionHistory;
|
||||
this.children = mergedTransactionHistory;
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'merge';
|
||||
}
|
||||
}
|
||||
|
||||
class ApplyTreeNode {
|
||||
children: any[];
|
||||
|
||||
constructor() {
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'apply';
|
||||
}
|
||||
}
|
||||
|
||||
class Merge {
|
||||
originalId: Number;
|
||||
mergedId: Number;
|
||||
mergedAt: Number;
|
||||
|
||||
constructor(originalId, mergedId, history) {
|
||||
this.originalId = originalId;
|
||||
this.mergedId = mergedId;
|
||||
// Specifies how long the merge chain of the merged transaction was at the
|
||||
// time is was merged.
|
||||
this.mergedAt = history[mergedId]?.length ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
class Apply {
|
||||
transactionId: Number;
|
||||
|
||||
constructor(transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the transactionId to the values that compose the identifier.
|
||||
* The top 32 bits is the PID of the process that created the transaction
|
||||
* and the bottom 32 bits is the ID of the transaction unique within that
|
||||
* process.
|
||||
* @param {Number} transactionId
|
||||
* @return {Object} An object containing the id and pid of the transaction.
|
||||
*/
|
||||
export function expandTransactionId(transactionId) {
|
||||
// Can't use bit shift operation because it isn't a 32 bit integer...
|
||||
// Because js uses floating point numbers for everything, maths isn't 100%
|
||||
// accurate so we need to round...
|
||||
return Object.freeze({
|
||||
id: Math.round(transactionId % Math.pow(2, 32)),
|
||||
pid: Math.round(transactionId / Math.pow(2, 32)),
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019, The Android Open Source Project
|
||||
* 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.
|
||||
@@ -14,69 +14,59 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {nanos_to_string} from './transform.js';
|
||||
import {nanos_to_string, transform} from './transform.js'
|
||||
|
||||
function transform_transaction(transaction, layerIdToName) {
|
||||
const transactions = [];
|
||||
|
||||
for (const surfaceChange of transaction.surfaceChange) {
|
||||
transactions.push(Object.freeze({
|
||||
type: 'surfaceChange',
|
||||
obj: surfaceChange,
|
||||
layerName: layerIdToName[surfaceChange.id],
|
||||
}));
|
||||
}
|
||||
|
||||
for (const displayChange of transaction.displayChange) {
|
||||
transactions.push(Object.freeze({
|
||||
type: 'displayChange',
|
||||
obj: displayChange,
|
||||
layerName: layerIdToName[displayChange.id],
|
||||
}));
|
||||
}
|
||||
|
||||
return transactions;
|
||||
function transform_change(change_data) {
|
||||
const kind = change_data.__proto__.$type.name;
|
||||
const name = change_data.layerId || change_data.id;
|
||||
return transform({
|
||||
kind: kind,
|
||||
name: name,
|
||||
stableId: kind + name,
|
||||
obj: change_data,
|
||||
children: [],
|
||||
isVisible: true,
|
||||
});
|
||||
}
|
||||
|
||||
function transform_entry(entry, layerIdToName) {
|
||||
const type = entry.increment;
|
||||
const timestamp = entry.timeStamp;
|
||||
const time = nanos_to_string(timestamp);
|
||||
|
||||
switch (type) {
|
||||
case 'transaction':
|
||||
|
||||
return Object.freeze({
|
||||
type,
|
||||
// TODO: Rename to changes
|
||||
transactions: transform_transaction(entry.transaction, layerIdToName),
|
||||
synchronous: entry.transaction.synchronous,
|
||||
animation: entry.transaction.animation,
|
||||
identifier: entry.transaction.id,
|
||||
time,
|
||||
origin: entry.transaction.origin,
|
||||
timestamp,
|
||||
});
|
||||
|
||||
case 'surfaceCreation':
|
||||
// NOTE: There is no break on purpose — we want to fall through to default
|
||||
layerIdToName[entry[type].id] = entry[type].name;
|
||||
|
||||
default:
|
||||
return Object.freeze({
|
||||
type,
|
||||
obj: entry[type],
|
||||
layerName: entry[type].name ?? layerIdToName[entry[type].id],
|
||||
time,
|
||||
timestamp,
|
||||
});
|
||||
}
|
||||
function transform_transaction_state(transaction_state) {
|
||||
const obj = Object.assign({}, transaction_state)
|
||||
if (obj.displayChanges) delete obj.displayChanges;
|
||||
if (obj.layerChanges) delete obj.layerChanges;
|
||||
const stableId = 'pid=' + transaction_state.pid +
|
||||
' uid=' + transaction_state.uid +
|
||||
' postTime=' + transaction_state.postTime;
|
||||
return transform({
|
||||
kind: 'TransactionState',
|
||||
name: stableId,
|
||||
stableId: stableId,
|
||||
obj: obj,
|
||||
children: [
|
||||
[
|
||||
[...transaction_state.layerChanges, ...transaction_state.displayChanges],
|
||||
transform_change]
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
function transform_transaction_trace(entries) {
|
||||
const layerIdToName = {};
|
||||
const data = entries.increment.map((entry) => transform_entry(entry, layerIdToName));
|
||||
function transform_transaction_trace_entry(entry) {
|
||||
const obj = Object.assign({}, entry)
|
||||
if (obj.transactions) delete obj.transactions;
|
||||
|
||||
return transform({
|
||||
obj: obj,
|
||||
kind: 'entry',
|
||||
stableId: 'entry',
|
||||
timestamp: entry.elapsedRealtimeNanos,
|
||||
name: nanos_to_string(entry.elapsedRealtimeNanos),
|
||||
children: [
|
||||
[entry.transactions, transform_transaction_state]
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function transform_transaction_trace(trace) {
|
||||
const data = trace.entry.map((entry) => transform_transaction_trace_entry(entry));
|
||||
return {children: data};
|
||||
}
|
||||
|
||||
|
||||
86
tools/winscope/src/transform_transaction_legacy.js
Normal file
86
tools/winscope/src/transform_transaction_legacy.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2019, 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 {nanos_to_string} from './transform.js';
|
||||
|
||||
function transform_transaction(transaction, layerIdToName) {
|
||||
const transactions = [];
|
||||
|
||||
for (const surfaceChange of transaction.surfaceChange) {
|
||||
transactions.push(Object.freeze({
|
||||
type: 'surfaceChange',
|
||||
obj: surfaceChange,
|
||||
layerName: layerIdToName[surfaceChange.id],
|
||||
}));
|
||||
}
|
||||
|
||||
for (const displayChange of transaction.displayChange) {
|
||||
transactions.push(Object.freeze({
|
||||
type: 'displayChange',
|
||||
obj: displayChange,
|
||||
layerName: layerIdToName[displayChange.id],
|
||||
}));
|
||||
}
|
||||
|
||||
return transactions;
|
||||
}
|
||||
|
||||
function transform_entry(entry, layerIdToName) {
|
||||
const type = entry.increment;
|
||||
const timestamp = entry.timeStamp;
|
||||
const time = nanos_to_string(timestamp);
|
||||
|
||||
switch (type) {
|
||||
case 'transaction':
|
||||
|
||||
return Object.freeze({
|
||||
type,
|
||||
// TODO: Rename to changes
|
||||
transactions: transform_transaction(entry.transaction, layerIdToName),
|
||||
synchronous: entry.transaction.synchronous,
|
||||
animation: entry.transaction.animation,
|
||||
identifier: entry.transaction.id,
|
||||
time,
|
||||
origin: entry.transaction.origin,
|
||||
timestamp,
|
||||
});
|
||||
|
||||
case 'surfaceCreation':
|
||||
// NOTE: There is no break on purpose — we want to fall through to default
|
||||
layerIdToName[entry[type].id] = entry[type].name;
|
||||
|
||||
default:
|
||||
return Object.freeze({
|
||||
type,
|
||||
obj: entry[type],
|
||||
layerName: entry[type].name ?? layerIdToName[entry[type].id],
|
||||
time,
|
||||
timestamp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This trace has been replaced by the new transactions trace
|
||||
*/
|
||||
function transform_transaction_trace_legacy(entries) {
|
||||
const layerIdToName = {};
|
||||
const data = entries.increment.map((entry) => transform_entry(entry, layerIdToName));
|
||||
|
||||
return {children: data};
|
||||
}
|
||||
|
||||
export {transform_transaction_trace_legacy};
|
||||
Reference in New Issue
Block a user