1/* 2 * Copyright 2017, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18import jsonProtoDefs from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto' 19import jsonProtoLogDefs from 'ProtoLogSafePath/protolog.proto' 20import jsonProtoDefsSF from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto' 21import jsonProtoDefsTrans from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto' 22import jsonProtoDefsWL from 'WaylandSafePath/waylandtrace.proto' 23import protobuf from 'protobufjs' 24import { transform_layers, transform_layers_trace } from './transform_sf.js' 25import { transform_window_service, transform_window_trace } from './transform_wm.js' 26import { transform_transaction_trace } from './transform_transaction.js' 27import { transform_wl_outputstate, transform_wayland_trace } from './transform_wl.js' 28import { transform_protolog } from './transform_protolog.js' 29import { fill_transform_data } from './matrix_utils.js' 30import { mp4Decoder } from './decodeVideo.js' 31 32var protoDefs = protobuf.Root.fromJSON(jsonProtoDefs) 33 .addJSON(jsonProtoLogDefs.nested) 34 .addJSON(jsonProtoDefsSF.nested) 35 .addJSON(jsonProtoDefsTrans.nested) 36 .addJSON(jsonProtoDefsWL.nested); 37 38var WindowTraceMessage = protoDefs.lookupType( 39 "com.android.server.wm.WindowManagerTraceFileProto"); 40var WindowMessage = protoDefs.lookupType( 41 "com.android.server.wm.WindowManagerServiceDumpProto"); 42var LayersMessage = protoDefs.lookupType("android.surfaceflinger.LayersProto"); 43var LayersTraceMessage = protoDefs.lookupType("android.surfaceflinger.LayersTraceFileProto"); 44var TransactionMessage = protoDefs.lookupType("Trace"); 45var WaylandMessage = protoDefs.lookupType("org.chromium.arc.wayland_composer.OutputStateProto"); 46var WaylandTraceMessage = protoDefs.lookupType("org.chromium.arc.wayland_composer.TraceFileProto"); 47var WindowLogMessage = protoDefs.lookupType( 48 "com.android.server.protolog.ProtoLogFileProto"); 49var LogMessage = protoDefs.lookupType( 50 "com.android.server.protolog.ProtoLogMessage"); 51 52const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45] // .LYRTRACE 53const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45] // .WINTRACE 54const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32] // ....ftypmp42 55const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x43, 0x45] // .WYLTRACE 56const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47] // .PROTOLOG 57 58const DATA_TYPES = { 59 WINDOW_MANAGER: { 60 name: "WindowManager", 61 icon: "view_compact", 62 mime: "application/octet-stream", 63 }, 64 SURFACE_FLINGER: { 65 name: "SurfaceFlinger", 66 icon: "filter_none", 67 mime: "application/octet-stream", 68 }, 69 SCREEN_RECORDING: { 70 name: "Screen recording", 71 icon: "videocam", 72 mime: "video/mp4", 73 }, 74 TRANSACTION: { 75 name: "Transaction", 76 icon: "timeline", 77 mime: "application/octet-stream", 78 }, 79 WAYLAND: { 80 name: "Wayland", 81 icon: "filter_none", 82 mime: "application/octet-stream", 83 }, 84 PROTO_LOG: { 85 name: "ProtoLog", 86 icon: "notes", 87 mime: "application/octet-stream", 88 } 89} 90 91const FILE_TYPES = { 92 'window_trace': { 93 name: "WindowManager trace", 94 dataType: DATA_TYPES.WINDOW_MANAGER, 95 decoder: protoDecoder, 96 decoderParams: { 97 protoType: WindowTraceMessage, 98 transform: transform_window_trace, 99 timeline: true, 100 }, 101 }, 102 'layers_trace': { 103 name: "SurfaceFlinger trace", 104 dataType: DATA_TYPES.SURFACE_FLINGER, 105 decoder: protoDecoder, 106 decoderParams: { 107 protoType: LayersTraceMessage, 108 transform: transform_layers_trace, 109 timeline: true, 110 }, 111 }, 112 'wl_trace': { 113 name: "Wayland trace", 114 dataType: DATA_TYPES.WAYLAND, 115 decoder: protoDecoder, 116 decoderParams: { 117 protoType: WaylandTraceMessage, 118 transform: transform_wayland_trace, 119 timeline: true, 120 }, 121 }, 122 'layers_dump': { 123 name: "SurfaceFlinger dump", 124 dataType: DATA_TYPES.SURFACE_FLINGER, 125 decoder: protoDecoder, 126 decoderParams: { 127 protoType: LayersMessage, 128 transform: transform_layers, 129 timeline: false, 130 }, 131 }, 132 'window_dump': { 133 name: "WindowManager dump", 134 dataType: DATA_TYPES.WINDOW_MANAGER, 135 decoder: protoDecoder, 136 decoderParams: { 137 protoType: WindowMessage, 138 transform: transform_window_service, 139 timeline: false, 140 }, 141 }, 142 'wl_dump': { 143 name: "Wayland dump", 144 dataType: DATA_TYPES.WAYLAND, 145 decoder: protoDecoder, 146 decoderParams: { 147 protoType: WaylandMessage, 148 transform: transform_wl_outputstate, 149 timeline: false, 150 }, 151 }, 152 'screen_recording': { 153 name: "Screen recording", 154 dataType: DATA_TYPES.SCREEN_RECORDING, 155 decoder: videoDecoder, 156 decoderParams: { 157 videoDecoder: mp4Decoder, 158 }, 159 }, 160 'transaction': { 161 name: "Transaction", 162 dataType: DATA_TYPES.TRANSACTION, 163 decoder: protoDecoder, 164 decoderParams: { 165 protoType: TransactionMessage, 166 transform: transform_transaction_trace, 167 timeline: true, 168 } 169 }, 170 'proto_log': { 171 name: "ProtoLog", 172 dataType: DATA_TYPES.PROTO_LOG, 173 decoder: protoDecoder, 174 decoderParams: { 175 protoType: WindowLogMessage, 176 transform: transform_protolog, 177 timeline: true, 178 } 179 } 180}; 181 182// Replace enum values with string representation and 183// add default values to the proto objects. This function also handles 184// a special case with TransformProtos where the matrix may be derived 185// from the transform type. 186function modifyProtoFields(protoObj, displayDefaults) { 187 if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) { 188 return; 189 } 190 for (var fieldName in protoObj.$type.fields) { 191 var fieldProperties = protoObj.$type.fields[fieldName]; 192 var field = protoObj[fieldName]; 193 194 if (Array.isArray(field)) { 195 field.forEach((item, _) => { 196 modifyProtoFields(item, displayDefaults); 197 }) 198 continue; 199 } 200 201 if (displayDefaults && !(field)) { 202 protoObj[fieldName] = fieldProperties.defaultValue; 203 } 204 205 if (fieldProperties.type === 'TransformProto') { 206 fill_transform_data(protoObj[fieldName]); 207 continue; 208 } 209 210 if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) { 211 protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]]; 212 continue; 213 } 214 modifyProtoFields(protoObj[fieldName], displayDefaults); 215 } 216} 217 218function protoDecoder(buffer, fileType, fileName, store) { 219 var decoded = fileType.decoderParams.protoType.decode(buffer); 220 modifyProtoFields(decoded, store.displayDefaults); 221 var transformed = fileType.decoderParams.transform(decoded); 222 var data 223 if (fileType.decoderParams.timeline) { 224 data = transformed.children; 225 } else { 226 data = [transformed]; 227 } 228 let blobUrl = URL.createObjectURL(new Blob([buffer], { type: fileType.dataType.mime })); 229 return dataFile(fileName, data.map(x => x.timestamp), data, blobUrl, fileType.dataType); 230} 231 232function videoDecoder(buffer, fileType, fileName, store) { 233 let [data, timeline] = fileType.decoderParams.videoDecoder(buffer); 234 let blobUrl = URL.createObjectURL(new Blob([data], { type: fileType.dataType.mime })); 235 return dataFile(fileName, timeline, blobUrl, blobUrl, fileType.dataType); 236} 237 238function dataFile(filename, timeline, data, blobUrl, type) { 239 return { 240 filename: filename, 241 timeline: timeline, 242 data: data, 243 blobUrl: blobUrl, 244 type: type, 245 selectedIndex: 0, 246 destroy() { 247 URL.revokeObjectURL(this.blobUrl); 248 }, 249 } 250} 251 252function arrayEquals(a, b) { 253 if (a.length !== b.length) { 254 return false; 255 } 256 for (var i = 0; i < a.length; i++) { 257 if (a[i] != b[i]) { 258 return false; 259 } 260 } 261 return true; 262} 263 264function arrayStartsWith(array, prefix) { 265 return arrayEquals(array.slice(0, prefix.length), prefix); 266} 267 268function decodedFile(fileType, buffer, fileName, store) { 269 return [fileType, fileType.decoder(buffer, fileType, fileName, store)]; 270} 271 272function detectAndDecode(buffer, fileName, store) { 273 if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) { 274 return decodedFile(FILE_TYPES['layers_trace'], buffer, fileName, store); 275 } 276 if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) { 277 return decodedFile(FILE_TYPES['window_trace'], buffer, fileName, store); 278 } 279 if (arrayStartsWith(buffer, MPEG4_MAGIC_NMBER)) { 280 return decodedFile(FILE_TYPES['screen_recording'], buffer, fileName, store); 281 } 282 if (arrayStartsWith(buffer, WAYLAND_TRACE_MAGIC_NUMBER)) { 283 return decodedFile(FILE_TYPES['wl_trace'], buffer, fileName, store); 284 } 285 if (arrayStartsWith(buffer, PROTO_LOG_MAGIC_NUMBER)) { 286 return decodedFile(FILE_TYPES['proto_log'], buffer, fileName, store); 287 } 288 for (var name of ['transaction', 'layers_dump', 'window_dump', 'wl_dump']) { 289 try { 290 return decodedFile(FILE_TYPES[name], buffer, fileName, store); 291 } catch (ex) { 292 // ignore exception and try next filetype 293 } 294 } 295 throw new Error('Unable to detect file'); 296} 297 298export { detectAndDecode, DATA_TYPES, FILE_TYPES }; 299