blob: 58a4742b4077b352ac5fa2611812f255cdd9a439 [file]
/*
* 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.
*/
import jsonProtoDefs from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto'
import jsonProtoLogDefs from 'frameworks/base/core/proto/android/server/windowmanagerlog.proto'
import jsonProtoDefsSF from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto'
import jsonProtoDefsTrans from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto'
import jsonProtoDefsWL from 'vendor/google_arc/libs/wayland_service/waylandtrace.proto'
import protobuf from 'protobufjs'
import { transform_layers, transform_layers_trace } from './transform_sf.js'
import { transform_window_service, transform_window_trace } from './transform_wm.js'
import { transform_transaction_trace } from './transform_transaction.js'
import { transform_wl_outputstate, transform_wayland_trace } from './transform_wl.js'
import { transform_protolog } from './transform_protolog.js'
import { fill_transform_data } from './matrix_utils.js'
import { mp4Decoder } from './decodeVideo.js'
var protoDefs = protobuf.Root.fromJSON(jsonProtoDefs)
.addJSON(jsonProtoLogDefs.nested)
.addJSON(jsonProtoDefsSF.nested)
.addJSON(jsonProtoDefsTrans.nested)
.addJSON(jsonProtoDefsWL.nested);
var WindowTraceMessage = protoDefs.lookupType(
"com.android.server.wm.WindowManagerTraceFileProto");
var WindowMessage = protoDefs.lookupType(
"com.android.server.wm.WindowManagerServiceDumpProto");
var LayersMessage = protoDefs.lookupType("android.surfaceflinger.LayersProto");
var LayersTraceMessage = protoDefs.lookupType("android.surfaceflinger.LayersTraceFileProto");
var TransactionMessage = protoDefs.lookupType("Trace");
var WaylandMessage = protoDefs.lookupType("org.chromium.arc.wayland_composer.OutputStateProto");
var WaylandTraceMessage = protoDefs.lookupType("org.chromium.arc.wayland_composer.TraceFileProto");
var WindowLogMessage = protoDefs.lookupType(
"com.android.server.wm.WindowManagerLogFileProto");
var LogMessage = protoDefs.lookupType(
"com.android.server.wm.ProtoLogMessage");
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 WINDOW_LOG_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x44, 0x4f, 0x4c, 0x4f, 0x47] // .WINDOLOG
const DATA_TYPES = {
WINDOW_MANAGER: {
name: "WindowManager",
icon: "view_compact",
},
SURFACE_FLINGER: {
name: "SurfaceFlinger",
icon: "filter_none",
},
SCREEN_RECORDING: {
name: "Screen recording",
icon: "videocam",
},
TRANSACTION: {
name: "Transaction",
icon: "timeline",
},
WAYLAND: {
name: "Wayland",
icon: "filter_none",
},
WINDOW_LOG: {
name: "WindowManager log",
icon: "notes",
}
}
const FILE_TYPES = {
'window_trace': {
name: "WindowManager trace",
dataType: DATA_TYPES.WINDOW_MANAGER,
decoder: protoDecoder,
decoderParams: {
protoType: WindowTraceMessage,
transform: transform_window_trace,
timeline: true,
},
},
'layers_trace': {
name: "SurfaceFlinger trace",
dataType: DATA_TYPES.SURFACE_FLINGER,
decoder: protoDecoder,
decoderParams: {
protoType: LayersTraceMessage,
transform: transform_layers_trace,
timeline: true,
},
},
'wl_trace': {
name: "Wayland trace",
dataType: DATA_TYPES.WAYLAND,
decoder: protoDecoder,
decoderParams: {
protoType: WaylandTraceMessage,
transform: transform_wayland_trace,
timeline: true,
},
},
'layers_dump': {
name: "SurfaceFlinger dump",
dataType: DATA_TYPES.SURFACE_FLINGER,
decoder: protoDecoder,
decoderParams: {
protoType: LayersMessage,
transform: transform_layers,
timeline: false,
},
},
'window_dump': {
name: "WindowManager dump",
dataType: DATA_TYPES.WINDOW_MANAGER,
decoder: protoDecoder,
decoderParams: {
protoType: WindowMessage,
transform: transform_window_service,
timeline: false,
},
},
'wl_dump': {
name: "Wayland dump",
dataType: DATA_TYPES.WAYLAND,
decoder: protoDecoder,
decoderParams: {
protoType: WaylandMessage,
transform: transform_wl_outputstate,
timeline: false,
},
},
'screen_recording': {
name: "Screen recording",
dataType: DATA_TYPES.SCREEN_RECORDING,
decoder: videoDecoder,
decoderParams: {
videoDecoder: mp4Decoder,
},
},
'transaction': {
name: "Transaction",
dataType: DATA_TYPES.TRANSACTION,
decoder: protoDecoder,
decoderParams: {
protoType: TransactionMessage,
transform: transform_transaction_trace,
timeline: true,
}
},
'window_log': {
name: "WindowManager log",
dataType: DATA_TYPES.WINDOW_LOG,
decoder: protoDecoder,
decoderParams: {
protoType: WindowLogMessage,
transform: transform_protolog,
timeline: true,
}
}
};
// 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 (var fieldName in protoObj.$type.fields) {
var fieldProperties = protoObj.$type.fields[fieldName];
var field = protoObj[fieldName];
if (Array.isArray(field)) {
field.forEach((item, _) => {
modifyProtoFields(item, displayDefaults);
})
continue;
}
if (displayDefaults && !(field)) {
protoObj[fieldName] = fieldProperties.defaultValue;
}
if (fieldProperties.type === 'TransformProto') {
fill_transform_data(protoObj[fieldName]);
continue;
}
if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
continue;
}
modifyProtoFields(protoObj[fieldName], displayDefaults);
}
}
function protoDecoder(buffer, fileType, fileName, store) {
var decoded = fileType.decoderParams.protoType.decode(buffer);
modifyProtoFields(decoded, store.displayDefaults);
var transformed = fileType.decoderParams.transform(decoded);
var data
if (fileType.decoderParams.timeline) {
data = transformed.children;
} else {
data = [transformed];
}
return dataFile(fileName, data.map(x => x.timestamp), data, fileType.dataType);
}
function videoDecoder(buffer, fileType, fileName, store) {
var [data, timeline] = fileType.decoderParams.videoDecoder(buffer)
return dataFile(fileName, timeline, data, fileType.dataType);
}
function dataFile(filename, timeline, data, type) {
return {
filename: filename,
timeline: timeline,
data: data,
type: type,
selectedIndex: 0,
}
}
function arrayEquals(a, b) {
if (a.length !== b.length) {
return false;
}
for (var 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) {
return [fileType, fileType.decoder(buffer, fileType, fileName, store)];
}
function detectAndDecode(buffer, fileName, store) {
if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) {
return decodedFile(FILE_TYPES['layers_trace'], buffer, fileName, store);
}
if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) {
return decodedFile(FILE_TYPES['window_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['wl_trace'], buffer, fileName, store);
}
if (arrayStartsWith(buffer, WINDOW_LOG_MAGIC_NUMBER)) {
return decodedFile(FILE_TYPES['window_log'], buffer, fileName, store);
}
for (var name of ['transaction', 'layers_dump', 'window_dump', 'wl_dump']) {
try {
return decodedFile(FILE_TYPES[name], buffer, fileName, store);
} catch (ex) {
// ignore exception and try next filetype
}
}
throw new Error('Unable to detect file');
}
export { detectAndDecode, dataFile, DATA_TYPES, FILE_TYPES };