/* * Copyright 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. */ /* eslint-disable camelcase */ /* eslint-disable max-len */ import jsonProtoDefsAccessibility from 'frameworks/base/core/proto/android/server/accessibilitytrace.proto'; 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 jsonProtoDefsWl from 'WaylandSafePath/waylandtrace.proto'; import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto'; import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto'; import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto'; import jsonProtoDefsTags from 'platform_testing/libraries/flicker/src/com/android/server/wm/proto/tags.proto'; import jsonProtoDefsErrors from 'platform_testing/libraries/flicker/src/com/android/server/wm/proto/errors.proto'; import protobuf from 'protobufjs'; import {transform_accessibility_trace} from './transform_accessibility.js'; import {transform_transaction_trace} from './transform_transaction.js'; 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'; import {transform_launcher_trace} from './transform_launcher.js'; import {transform_ime_trace_clients, transform_ime_trace_service, transform_ime_trace_managerservice} from './transform_ime.js'; import {mp4Decoder} from './decodeVideo.js'; 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 ScreenRecordingTrace from '@/traces/ScreenRecording.ts'; import WaylandTrace from '@/traces/Wayland.ts'; import ProtoLogTrace from '@/traces/ProtoLog.ts'; import SystemUITrace from '@/traces/SystemUI.ts'; import LauncherTrace from '@/traces/Launcher.ts'; import ImeTraceClients from '@/traces/InputMethodClients.ts'; import ImeTraceService from '@/traces/InputMethodService.ts'; import ImeTraceManagerService from '@/traces/InputMethodManagerService.ts'; import SurfaceFlingerDump from '@/dumps/SurfaceFlinger.ts'; import WindowManagerDump from '@/dumps/WindowManager.ts'; import WaylandDump from '@/dumps/Wayland.ts'; import TagTrace from '@/traces/TraceTag.ts'; import ErrorTrace from '@/traces/TraceError.ts'; const AccessibilityTraceMessage = lookup_type(jsonProtoDefsAccessibility, 'com.android.server.accessibility.AccessibilityTraceFileProto'); const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto'); const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto'); const SfTraceMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersTraceFileProto'); const SfDumpMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersProto'); const SfTransactionTraceMessage = lookup_type(jsonProtoDefsTransaction, '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'); const SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, 'com.android.systemui.tracing.SystemUiTraceFileProto'); const LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, 'com.android.launcher3.tracing.LauncherTraceFileProto'); const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodClientsTraceFileProto'); const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodServiceTraceFileProto'); const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodManagerServiceTraceFileProto'); const TagTraceMessage = lookup_type(jsonProtoDefsTags, 'com.android.server.wm.flicker.FlickerTagTraceProto'); const ErrorTraceMessage = lookup_type(jsonProtoDefsErrors, 'com.android.server.wm.flicker.FlickerErrorTraceProto'); const ACCESSIBILITY_MAGIC_NUMBER = [0x09, 0x41, 0x31, 0x31, 0x59, 0x54, 0x52, 0x41, 0x43]; // .A11YTRAC const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE 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 const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47]; // .PROTOLOG const SYSTEM_UI_MAGIC_NUMBER = [0x09, 0x53, 0x59, 0x53, 0x55, 0x49, 0x54, 0x52, 0x43]; // .SYSUITRC const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0x43]; // .LNCHRTRC const IMC_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMCTRACE const IMS_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMSTRACE const IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMMTRACE const TAG_TRACE_MAGIC_NUMBER = [0x09, 0x54, 0x41, 0x47, 0x54, 0x52, 0x41, 0x43, 0x45]; //.TAGTRACE const ERROR_TRACE_MAGIC_NUMBER = [0x09, 0x45, 0x52, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; //.ERRORTRACE const FILE_TYPES = Object.freeze({ ACCESSIBILITY_TRACE: 'AccessibilityTrace', WINDOW_MANAGER_TRACE: 'WindowManagerTrace', SURFACE_FLINGER_TRACE: 'SurfaceFlingerTrace', WINDOW_MANAGER_DUMP: 'WindowManagerDump', SURFACE_FLINGER_DUMP: 'SurfaceFlingerDump', SCREEN_RECORDING: 'ScreenRecording', TRANSACTIONS_TRACE: 'TransactionsTrace', WAYLAND_TRACE: 'WaylandTrace', WAYLAND_DUMP: 'WaylandDump', PROTO_LOG: 'ProtoLog', SYSTEM_UI: 'SystemUI', LAUNCHER: 'Launcher', IME_TRACE_CLIENTS: 'ImeTraceClients', IME_TRACE_SERVICE: 'ImeTrace InputMethodService', IME_TRACE_MANAGERSERVICE: 'ImeTrace InputMethodManagerService', TAG_TRACE: 'TagTrace', ERROR_TRACE: 'ErrorTrace', }); const WINDOW_MANAGER_ICON = 'view_compact'; const SURFACE_FLINGER_ICON = 'filter_none'; const SCREEN_RECORDING_ICON = 'videocam'; const TRANSACTION_ICON = 'timeline'; const WAYLAND_ICON = 'filter_none'; const PROTO_LOG_ICON = 'notes'; const SYSTEM_UI_ICON = 'filter_none'; const LAUNCHER_ICON = 'filter_none'; const IME_ICON = 'keyboard'; const ACCESSIBILITY_ICON = 'filter_none'; const TAG_ICON = 'details'; const TRACE_ERROR_ICON = 'warning'; const FILE_ICONS = { [FILE_TYPES.ACCESSIBILITY_TRACE]: ACCESSIBILITY_ICON, [FILE_TYPES.WINDOW_MANAGER_TRACE]: WINDOW_MANAGER_ICON, [FILE_TYPES.SURFACE_FLINGER_TRACE]: SURFACE_FLINGER_ICON, [FILE_TYPES.WINDOW_MANAGER_DUMP]: WINDOW_MANAGER_ICON, [FILE_TYPES.SURFACE_FLINGER_DUMP]: SURFACE_FLINGER_ICON, [FILE_TYPES.SCREEN_RECORDING]: SCREEN_RECORDING_ICON, [FILE_TYPES.TRANSACTIONS_TRACE]: TRANSACTION_ICON, [FILE_TYPES.WAYLAND_TRACE]: WAYLAND_ICON, [FILE_TYPES.WAYLAND_DUMP]: WAYLAND_ICON, [FILE_TYPES.PROTO_LOG]: PROTO_LOG_ICON, [FILE_TYPES.SYSTEM_UI]: SYSTEM_UI_ICON, [FILE_TYPES.LAUNCHER]: LAUNCHER_ICON, [FILE_TYPES.IME_TRACE_CLIENTS]: IME_ICON, [FILE_TYPES.IME_TRACE_SERVICE]: IME_ICON, [FILE_TYPES.IME_TRACE_MANAGERSERVICE]: IME_ICON, [FILE_TYPES.TAG_TRACE]: TAG_ICON, [FILE_TYPES.ERROR_TRACE]: TRACE_ERROR_ICON, }; function oneOf(dataType) { return {oneOf: true, type: dataType}; } const TRACE_TYPES = Object.freeze({ ACCESSIBILITY: 'AccessibilityTrace', WINDOW_MANAGER: 'WindowManagerTrace', SURFACE_FLINGER: 'SurfaceFlingerTrace', SCREEN_RECORDING: 'ScreenRecording', TRANSACTION: 'Transaction', WAYLAND: 'Wayland', PROTO_LOG: 'ProtoLog', SYSTEM_UI: 'SystemUI', LAUNCHER: 'Launcher', IME_CLIENTS: 'ImeTrace Clients', IME_SERVICE: 'ImeTrace InputMethodService', IME_MANAGERSERVICE: 'ImeTrace InputMethodManagerService', TAG: 'TagTrace', ERROR: 'ErrorTrace', }); const TRACE_INFO = { [TRACE_TYPES.ACCESSIBILITY]: { name: 'Accessibility', icon: ACCESSIBILITY_ICON, files: [oneOf(FILE_TYPES.ACCESSIBILITY_TRACE)], constructor: AccessibilityTrace, }, [TRACE_TYPES.WINDOW_MANAGER]: { name: 'WindowManager', icon: WINDOW_MANAGER_ICON, files: [oneOf(FILE_TYPES.WINDOW_MANAGER_TRACE)], constructor: WindowManagerTrace, }, [TRACE_TYPES.SURFACE_FLINGER]: { name: 'SurfaceFlinger', icon: SURFACE_FLINGER_ICON, files: [oneOf(FILE_TYPES.SURFACE_FLINGER_TRACE)], constructor: SurfaceFlingerTrace, }, [TRACE_TYPES.SCREEN_RECORDING]: { name: 'Screen recording', icon: SCREEN_RECORDING_ICON, files: [oneOf(FILE_TYPES.SCREEN_RECORDING)], constructor: ScreenRecordingTrace, }, [TRACE_TYPES.TRANSACTION]: { name: 'Transaction', icon: TRANSACTION_ICON, files: [ oneOf(FILE_TYPES.TRANSACTIONS_TRACE), ], constructor: TransactionsTrace, }, [TRACE_TYPES.WAYLAND]: { name: 'Wayland', icon: WAYLAND_ICON, files: [oneOf(FILE_TYPES.WAYLAND_TRACE)], constructor: WaylandTrace, }, [TRACE_TYPES.PROTO_LOG]: { name: 'ProtoLog', icon: PROTO_LOG_ICON, files: [oneOf(FILE_TYPES.PROTO_LOG)], constructor: ProtoLogTrace, }, [TRACE_TYPES.SYSTEM_UI]: { name: 'SystemUI', icon: SYSTEM_UI_ICON, files: [oneOf(FILE_TYPES.SYSTEM_UI)], constructor: SystemUITrace, }, [TRACE_TYPES.LAUNCHER]: { name: 'Launcher', icon: LAUNCHER_ICON, files: [oneOf(FILE_TYPES.LAUNCHER)], constructor: LauncherTrace, }, [TRACE_TYPES.IME_CLIENTS]: { name: 'InputMethodClients', icon: IME_ICON, files: [oneOf(FILE_TYPES.IME_TRACE_CLIENTS)], constructor: ImeTraceClients, }, [TRACE_TYPES.IME_SERVICE]: { name: 'InputMethodService', icon: IME_ICON, files: [oneOf(FILE_TYPES.IME_TRACE_SERVICE)], constructor: ImeTraceService, }, [TRACE_TYPES.IME_MANAGERSERVICE]: { name: 'InputMethodManagerService', icon: IME_ICON, files: [oneOf(FILE_TYPES.IME_TRACE_MANAGERSERVICE)], constructor: ImeTraceManagerService, }, [TRACE_TYPES.TAG]: { name: 'Tag', icon: TAG_ICON, files: [oneOf(FILE_TYPES.TAG_TRACE)], constructor: TagTrace, }, [TRACE_TYPES.ERROR]: { name: 'Error', icon: TRACE_ERROR_ICON, files: [oneOf(FILE_TYPES.ERROR_TRACE)], constructor: ErrorTrace, }, }; const DUMP_TYPES = Object.freeze({ WINDOW_MANAGER: 'WindowManagerDump', SURFACE_FLINGER: 'SurfaceFlingerDump', WAYLAND: 'WaylandDump', }); const DUMP_INFO = { [DUMP_TYPES.WINDOW_MANAGER]: { name: 'WindowManager', icon: WINDOW_MANAGER_ICON, files: [oneOf(FILE_TYPES.WINDOW_MANAGER_DUMP)], constructor: WindowManagerDump, }, [DUMP_TYPES.SURFACE_FLINGER]: { name: 'SurfaceFlinger', icon: SURFACE_FLINGER_ICON, files: [oneOf(FILE_TYPES.SURFACE_FLINGER_DUMP)], constructor: SurfaceFlingerDump, }, [DUMP_TYPES.WAYLAND]: { name: 'Wayland', icon: WAYLAND_ICON, files: [oneOf(FILE_TYPES.WAYLAND_DUMP)], constructor: WaylandDump, }, }; export const TRACE_ICONS = { [TRACE_TYPES.WINDOW_MANAGER]: WINDOW_MANAGER_ICON, [TRACE_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON, [TRACE_TYPES.SCREEN_RECORDING]: SCREEN_RECORDING_ICON, [TRACE_TYPES.TRANSACTION]: TRANSACTION_ICON, [TRACE_TYPES.WAYLAND]: WAYLAND_ICON, [TRACE_TYPES.PROTO_LOG]: PROTO_LOG_ICON, [TRACE_TYPES.SYSTEM_UI]: SYSTEM_UI_ICON, [TRACE_TYPES.LAUNCHER]: LAUNCHER_ICON, [TRACE_TYPES.IME_CLIENTS]: IME_ICON, [TRACE_TYPES.IME_SERVICE]: IME_ICON, [TRACE_TYPES.IME_MANAGERSERVICE]: IME_ICON, [TRACE_TYPES.TAG_TRACE]: TAG_ICON, [TRACE_TYPES.ERROR_TRACE]: TRACE_ERROR_ICON, [DUMP_TYPES.WINDOW_MANAGER]: WINDOW_MANAGER_ICON, [DUMP_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON, [DUMP_TYPES.WAYLAND]: WAYLAND_ICON, }; // TODO: Rename name to defaultName const FILE_DECODERS = { [FILE_TYPES.ACCESSIBILITY_TRACE]: { name: 'Accessibility trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.ACCESSIBILITY_TRACE, objTypeProto: AccessibilityTraceMessage, transform: transform_accessibility_trace, timeline: true, }, }, [FILE_TYPES.WINDOW_MANAGER_TRACE]: { name: 'WindowManager trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.WINDOW_MANAGER_TRACE, objTypeProto: WmTraceMessage, transform: WindowManagerTrace.fromProto, timeline: true, }, }, [FILE_TYPES.SURFACE_FLINGER_TRACE]: { name: 'SurfaceFlinger trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.SURFACE_FLINGER_TRACE, mime: 'application/octet-stream', objTypeProto: SfTraceMessage, transform: SurfaceFlingerTrace.fromProto, timeline: true, }, }, [FILE_TYPES.WAYLAND_TRACE]: { name: 'Wayland trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.WAYLAND_TRACE, mime: 'application/octet-stream', objTypeProto: WaylandTraceMessage, transform: transform_wayland_trace, timeline: true, }, }, [FILE_TYPES.SURFACE_FLINGER_DUMP]: { name: 'SurfaceFlinger dump', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.SURFACE_FLINGER_DUMP, mime: 'application/octet-stream', objTypeProto: [SfDumpMessage, SfTraceMessage], transform: [SurfaceFlingerDump.fromProto, SurfaceFlingerTrace.fromProto], timeline: true, }, }, [FILE_TYPES.WINDOW_MANAGER_DUMP]: { name: 'WindowManager dump', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.WINDOW_MANAGER_DUMP, mime: 'application/octet-stream', objTypeProto: WmDumpMessage, transform: WindowManagerDump.fromProto, timeline: true, }, }, [FILE_TYPES.WAYLAND_DUMP]: { name: 'Wayland dump', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.WAYLAND_DUMP, mime: 'application/octet-stream', objTypeProto: WaylandDumpMessage, transform: transform_wl_outputstate, timeline: true, }, }, [FILE_TYPES.SCREEN_RECORDING]: { name: 'Screen recording', decoder: videoDecoder, decoderParams: { type: FILE_TYPES.SCREEN_RECORDING, mime: 'video/mp4', videoDecoder: mp4Decoder, }, }, [FILE_TYPES.TRANSACTIONS_TRACE]: { name: 'Transaction', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.TRANSACTIONS_TRACE, mime: 'application/octet-stream', objTypeProto: SfTransactionTraceMessage, transform: transform_transaction_trace, timeline: true, }, }, [FILE_TYPES.PROTO_LOG]: { name: 'ProtoLog', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.PROTO_LOG, mime: 'application/octet-stream', objTypeProto: ProtoLogMessage, transform: transformProtolog, timeline: true, }, }, [FILE_TYPES.SYSTEM_UI]: { name: 'SystemUI trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.SYSTEM_UI, mime: 'application/octet-stream', objTypeProto: SystemUiTraceMessage, transform: transform_sysui_trace, timeline: true, }, }, [FILE_TYPES.LAUNCHER]: { name: 'Launcher trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.LAUNCHER, mime: 'application/octet-stream', objTypeProto: LauncherTraceMessage, transform: transform_launcher_trace, timeline: true, }, }, [FILE_TYPES.IME_TRACE_CLIENTS]: { name: 'InputMethodClients trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.IME_TRACE_CLIENTS, mime: 'application/octet-stream', objTypeProto: InputMethodClientsTraceMessage, transform: transform_ime_trace_clients, timeline: true, }, }, [FILE_TYPES.IME_TRACE_SERVICE]: { name: 'InputMethodService trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.IME_TRACE_SERVICE, mime: 'application/octet-stream', objTypeProto: InputMethodServiceTraceMessage, transform: transform_ime_trace_service, timeline: true, }, }, [FILE_TYPES.IME_TRACE_MANAGERSERVICE]: { name: 'InputMethodManagerService trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.IME_TRACE_MANAGERSERVICE, mime: 'application/octet-stream', objTypeProto: InputMethodManagerServiceTraceMessage, transform: transform_ime_trace_managerservice, timeline: true, }, }, [FILE_TYPES.TAG_TRACE]: { name: 'Tag trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.TAG_TRACE, objTypeProto: TagTraceMessage, transform: TagTrace.fromProto, timeline: true, }, }, [FILE_TYPES.ERROR_TRACE]: { name: 'Error trace', decoder: protoDecoder, decoderParams: { type: FILE_TYPES.ERROR_TRACE, objTypeProto: ErrorTraceMessage, transform: ErrorTrace.fromProto, timeline: true, }, }, }; function lookup_type(protoPath, type) { return protobuf.Root.fromJSON(protoPath).lookupType(type); } // Replace enum values with string representation and // add default values to the proto objects. This function also handles // a special case with TransformProtos where the matrix may be derived // from the transform type. function modifyProtoFields(protoObj, displayDefaults) { if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) { return; } for (const fieldName in protoObj.$type.fields) { if (protoObj.$type.fields.hasOwnProperty(fieldName)) { const fieldProperties = protoObj.$type.fields[fieldName]; const field = protoObj[fieldName]; if (Array.isArray(field)) { field.forEach((item, _) => { modifyProtoFields(item, displayDefaults); }); continue; } if (displayDefaults && !(field)) { protoObj[fieldName] = fieldProperties.defaultValue; } if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) { protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]]; continue; } modifyProtoFields(protoObj[fieldName], displayDefaults); } } } function decodeAndTransformProto(buffer, params, displayDefaults) { var objTypesProto = []; var transforms = []; if (!Array.isArray(params.objTypeProto)) { objTypesProto = [params.objTypeProto]; transforms = [params.transform]; } else { objTypesProto = params.objTypeProto; transforms = params.transform; } // each trace or dump may have different processors, for example, until S, SF dumps // returne a list of layers and winscope built a [LayerTraceEntry] from them. // From S onwards, returns a LayerTrace object, iterating over multiple items allows // winscope to handle both the new and legacy formats // TODO Refactor the decode.js code into a set of decoders to clean up the code for (var x = 0; x < objTypesProto.length; x++) { const objType = objTypesProto[x]; const transform = transforms[x]; try { const decoded = objType.decode(buffer); modifyProtoFields(decoded, displayDefaults); const transformed = transform(decoded); return transformed; } catch (e) { // check next parser } } throw new UndetectableFileType('Unable to parse file'); } function protoDecoder(buffer, params, fileName, store) { const transformed = decodeAndTransformProto(buffer, params, store.displayDefaults); // add tagGenerationTrace to dataFile for WM/SF traces so tags can be generated var tagGenerationTrace = null; if (params.type === FILE_TYPES.WINDOW_MANAGER_TRACE || params.type === FILE_TYPES.SURFACE_FLINGER_TRACE) { tagGenerationTrace = transformed; } let data; if (params.timeline) { data = transformed.entries ?? transformed.children; } else { data = [transformed]; } const blobUrl = URL.createObjectURL(new Blob([buffer], {type: params.mime})); return dataFile( fileName, data.map((x) => x.timestamp), data, blobUrl, params.type, tagGenerationTrace ); } function videoDecoder(buffer, params, fileName, store) { const [data, timeline] = params.videoDecoder(buffer); const blobUrl = URL.createObjectURL(new Blob([data], {type: params.mime})); return dataFile(fileName, timeline, blobUrl, blobUrl, params.type); } function dataFile(filename, timeline, data, blobUrl, type, tagGenerationTrace = null) { return { filename: filename, // Object is frozen for performance reasons // It will prevent Vue from making it a reactive object which will be very slow as the timeline gets larger. timeline: Object.freeze(timeline), data: data, blobUrl: blobUrl, tagGenerationTrace: tagGenerationTrace, type: type, selectedIndex: 0, destroy() { URL.revokeObjectURL(this.blobUrl); }, }; } function arrayEquals(a, b) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (a[i] != b[i]) { return false; } } return true; } function arrayStartsWith(array, prefix) { return arrayEquals(array.slice(0, prefix.length), prefix); } function decodedFile(fileType, buffer, fileName, store) { const fileDecoder = FILE_DECODERS[fileType]; return [fileType, fileDecoder.decoder(buffer, fileDecoder.decoderParams, fileName, store)]; } function detectAndDecode(buffer, fileName, store) { if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.SURFACE_FLINGER_TRACE, buffer, fileName, store); } if (arrayStartsWith(buffer, ACCESSIBILITY_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.ACCESSIBILITY_TRACE, buffer, fileName, store); } if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.WINDOW_MANAGER_TRACE, buffer, fileName, store); } if (arrayStartsWith(buffer, MPEG4_MAGIC_NMBER)) { return decodedFile(FILE_TYPES.SCREEN_RECORDING, buffer, fileName, store); } if (arrayStartsWith(buffer, WAYLAND_TRACE_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.WAYLAND_TRACE, buffer, fileName, store); } if (arrayStartsWith(buffer, PROTO_LOG_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.PROTO_LOG, buffer, fileName, store); } if (arrayStartsWith(buffer, SYSTEM_UI_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.SYSTEM_UI, buffer, fileName, store); } if (arrayStartsWith(buffer, LAUNCHER_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.LAUNCHER, buffer, fileName, store); } if (arrayStartsWith(buffer, IMC_TRACE_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.IME_TRACE_CLIENTS, buffer, fileName, store); } if (arrayStartsWith(buffer, IMS_TRACE_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.IME_TRACE_SERVICE, buffer, fileName, store); } if (arrayStartsWith(buffer, IMM_TRACE_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.IME_TRACE_MANAGERSERVICE, buffer, fileName, store); } if (arrayStartsWith(buffer, TAG_TRACE_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.TAG_TRACE, buffer, fileName, store); } if (arrayStartsWith(buffer, ERROR_TRACE_MAGIC_NUMBER)) { return decodedFile(FILE_TYPES.ERROR_TRACE, buffer, fileName, store); } // TODO(b/169305853): Add magic number at beginning of file for better auto detection for (const [filetype, condition] of [ [FILE_TYPES.TRANSACTIONS_TRACE, (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], ]) { try { const [, fileData] = decodedFile(filetype, buffer, fileName, store); // A generic file will often wrongly be decoded as an empty wayland dump file if (condition && !condition(fileData)) { // Fall through to next filetype continue; } return [filetype, fileData]; } catch (ex) { // ignore exception and fall through to next filetype } } throw new UndetectableFileType('Unable to detect file'); } /** * Error is raised when detectAndDecode is called but the file can't be * automatically detected as being of a compatible file type. */ class UndetectableFileType extends Error { } export { dataFile, detectAndDecode, decodeAndTransformProto, TagTraceMessage, FILE_TYPES, TRACE_INFO, TRACE_TYPES, DUMP_TYPES, DUMP_INFO, FILE_DECODERS, FILE_ICONS, UndetectableFileType };