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 jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto' 19import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/server/protolog.proto' 20import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto' 21import jsonProtoDefsTransaction from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto' 22import jsonProtoDefsWl from 'WaylandSafePath/waylandtrace.proto' 23import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto' 24import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto' 25import protobuf from 'protobufjs' 26import { transform_layers, transform_layers_trace } from './transform_sf.js' 27import { transform_window_service, transform_window_trace } from './transform_wm.js' 28import { transform_transaction_trace } from './transform_transaction.js' 29import { transform_wl_outputstate, transform_wayland_trace } from './transform_wl.js' 30import { transform_protolog } from './transform_protolog.js' 31import { transform_sysui_trace } from './transform_sys_ui.js' 32import { transform_launcher_trace } from './transform_launcher.js' 33import { fill_transform_data } from './matrix_utils.js' 34import { mp4Decoder } from './decodeVideo.js' 35 36var WmTraceMessage = lookup_type(jsonProtoDefsWm, "com.android.server.wm.WindowManagerTraceFileProto"); 37var WmDumpMessage = lookup_type(jsonProtoDefsWm, "com.android.server.wm.WindowManagerServiceDumpProto"); 38var SfTraceMessage = lookup_type(jsonProtoDefsSf, "android.surfaceflinger.LayersTraceFileProto"); 39var SfDumpMessage = lookup_type(jsonProtoDefsSf, "android.surfaceflinger.LayersProto"); 40var SfTransactionTraceMessage = lookup_type(jsonProtoDefsTransaction, "Trace"); 41var WaylandTraceMessage = lookup_type(jsonProtoDefsWl, "org.chromium.arc.wayland_composer.TraceFileProto"); 42var WaylandDumpMessage = lookup_type(jsonProtoDefsWl, "org.chromium.arc.wayland_composer.OutputStateProto"); 43var ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, "com.android.server.protolog.ProtoLogFileProto"); 44var SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, "com.android.systemui.tracing.SystemUiTraceFileProto"); 45var LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, "com.android.launcher3.tracing.LauncherTraceFileProto"); 46 47const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45] // .LYRTRACE 48const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45] // .WINTRACE 49const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32] // ....ftypmp42 50const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x43, 0x45] // .WYLTRACE 51const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47] // .PROTOLOG 52const SYSTEM_UI_MAGIC_NUMBER = [0x09, 0x53, 0x59, 0x53, 0x55, 0x49, 0x54, 0x52, 0x43] // .SYSUITRC 53const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0x43] // .LNCHRTRC 54 55const DATA_TYPES = { 56 WINDOW_MANAGER: { 57 name: "WindowManager", 58 icon: "view_compact", 59 mime: "application/octet-stream", 60 }, 61 SURFACE_FLINGER: { 62 name: "SurfaceFlinger", 63 icon: "filter_none", 64 mime: "application/octet-stream", 65 }, 66 SCREEN_RECORDING: { 67 name: "Screen recording", 68 icon: "videocam", 69 mime: "video/mp4", 70 }, 71 TRANSACTION: { 72 name: "Transaction", 73 icon: "timeline", 74 mime: "application/octet-stream", 75 }, 76 WAYLAND: { 77 name: "Wayland", 78 icon: "filter_none", 79 mime: "application/octet-stream", 80 }, 81 PROTO_LOG: { 82 name: "ProtoLog", 83 icon: "notes", 84 mime: "application/octet-stream", 85 }, 86 SYSTEM_UI: { 87 name: "SystemUI", 88 icon: "filter_none", 89 mime: "application/octet-stream", 90 }, 91 LAUNCHER: { 92 name: "Launcher", 93 icon: "filter_none", 94 mime: "application/octet-stream", 95 }, 96} 97 98const FILE_TYPES = { 99 'window_trace': { 100 name: "WindowManager trace", 101 dataType: DATA_TYPES.WINDOW_MANAGER, 102 decoder: protoDecoder, 103 decoderParams: { 104 protoType: WmTraceMessage, 105 transform: transform_window_trace, 106 timeline: true, 107 }, 108 }, 109 'layers_trace': { 110 name: "SurfaceFlinger trace", 111 dataType: DATA_TYPES.SURFACE_FLINGER, 112 decoder: protoDecoder, 113 decoderParams: { 114 protoType: SfTraceMessage, 115 transform: transform_layers_trace, 116 timeline: true, 117 }, 118 }, 119 'wl_trace': { 120 name: "Wayland trace", 121 dataType: DATA_TYPES.WAYLAND, 122 decoder: protoDecoder, 123 decoderParams: { 124 protoType: WaylandTraceMessage, 125 transform: transform_wayland_trace, 126 timeline: true, 127 }, 128 }, 129 'layers_dump': { 130 name: "SurfaceFlinger dump", 131 dataType: DATA_TYPES.SURFACE_FLINGER, 132 decoder: protoDecoder, 133 decoderParams: { 134 protoType: SfDumpMessage, 135 transform: (decoded) => transform_layers(true /*includesCompositionState*/, decoded), 136 timeline: false, 137 }, 138 }, 139 'window_dump': { 140 name: "WindowManager dump", 141 dataType: DATA_TYPES.WINDOW_MANAGER, 142 decoder: protoDecoder, 143 decoderParams: { 144 protoType: WmDumpMessage, 145 transform: transform_window_service, 146 timeline: false, 147 }, 148 }, 149 'wl_dump': { 150 name: "Wayland dump", 151 dataType: DATA_TYPES.WAYLAND, 152 decoder: protoDecoder, 153 decoderParams: { 154 protoType: WaylandDumpMessage, 155 transform: transform_wl_outputstate, 156 timeline: false, 157 }, 158 }, 159 'screen_recording': { 160 name: "Screen recording", 161 dataType: DATA_TYPES.SCREEN_RECORDING, 162 decoder: videoDecoder, 163 decoderParams: { 164 videoDecoder: mp4Decoder, 165 }, 166 }, 167 'transaction': { 168 name: "Transaction", 169 dataType: DATA_TYPES.TRANSACTION, 170 decoder: protoDecoder, 171 decoderParams: { 172 protoType: SfTransactionTraceMessage, 173 transform: transform_transaction_trace, 174 timeline: true, 175 } 176 }, 177 'proto_log': { 178 name: "ProtoLog", 179 dataType: DATA_TYPES.PROTO_LOG, 180 decoder: protoDecoder, 181 decoderParams: { 182 protoType: ProtoLogMessage, 183 transform: transform_protolog, 184 timeline: true, 185 } 186 }, 187 'system_ui_trace': { 188 name: "SystemUI trace", 189 dataType: DATA_TYPES.SYSTEM_UI, 190 decoder: protoDecoder, 191 decoderParams: { 192 protoType: SystemUiTraceMessage, 193 transform: transform_sysui_trace, 194 timeline: true, 195 } 196 }, 197 'launcher_trace': { 198 name: "Launcher trace", 199 dataType: DATA_TYPES.LAUNCHER, 200 decoder: protoDecoder, 201 decoderParams: { 202 protoType: LauncherTraceMessage, 203 transform: transform_launcher_trace, 204 timeline: true, 205 } 206 }, 207}; 208 209function lookup_type(protoPath, type) { 210 return protobuf.Root.fromJSON(protoPath).lookupType(type); 211} 212 213// Replace enum values with string representation and 214// add default values to the proto objects. This function also handles 215// a special case with TransformProtos where the matrix may be derived 216// from the transform type. 217function modifyProtoFields(protoObj, displayDefaults) { 218 if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) { 219 return; 220 } 221 for (var fieldName in protoObj.$type.fields) { 222 var fieldProperties = protoObj.$type.fields[fieldName]; 223 var field = protoObj[fieldName]; 224 225 if (Array.isArray(field)) { 226 field.forEach((item, _) => { 227 modifyProtoFields(item, displayDefaults); 228 }) 229 continue; 230 } 231 232 if (displayDefaults && !(field)) { 233 protoObj[fieldName] = fieldProperties.defaultValue; 234 } 235 236 if (fieldProperties.type === 'TransformProto') { 237 fill_transform_data(protoObj[fieldName]); 238 continue; 239 } 240 241 if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) { 242 protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]]; 243 continue; 244 } 245 modifyProtoFields(protoObj[fieldName], displayDefaults); 246 } 247} 248 249function protoDecoder(buffer, fileType, fileName, store) { 250 var decoded = fileType.decoderParams.protoType.decode(buffer); 251 modifyProtoFields(decoded, store.displayDefaults); 252 var transformed = fileType.decoderParams.transform(decoded); 253 var data 254 if (fileType.decoderParams.timeline) { 255 data = transformed.children; 256 } else { 257 data = [transformed]; 258 } 259 let blobUrl = URL.createObjectURL(new Blob([buffer], { type: fileType.dataType.mime })); 260 return dataFile(fileName, data.map(x => x.timestamp), data, blobUrl, fileType.dataType); 261} 262 263function videoDecoder(buffer, fileType, fileName, store) { 264 let [data, timeline] = fileType.decoderParams.videoDecoder(buffer); 265 let blobUrl = URL.createObjectURL(new Blob([data], { type: fileType.dataType.mime })); 266 return dataFile(fileName, timeline, blobUrl, blobUrl, fileType.dataType); 267} 268 269function dataFile(filename, timeline, data, blobUrl, type) { 270 return { 271 filename: filename, 272 timeline: timeline, 273 data: data, 274 blobUrl: blobUrl, 275 type: type, 276 selectedIndex: 0, 277 destroy() { 278 URL.revokeObjectURL(this.blobUrl); 279 }, 280 } 281} 282 283function arrayEquals(a, b) { 284 if (a.length !== b.length) { 285 return false; 286 } 287 for (var i = 0; i < a.length; i++) { 288 if (a[i] != b[i]) { 289 return false; 290 } 291 } 292 return true; 293} 294 295function arrayStartsWith(array, prefix) { 296 return arrayEquals(array.slice(0, prefix.length), prefix); 297} 298 299function decodedFile(fileType, buffer, fileName, store) { 300 return [fileType, fileType.decoder(buffer, fileType, fileName, store)]; 301} 302 303function detectAndDecode(buffer, fileName, store) { 304 if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) { 305 return decodedFile(FILE_TYPES['layers_trace'], buffer, fileName, store); 306 } 307 if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) { 308 return decodedFile(FILE_TYPES['window_trace'], buffer, fileName, store); 309 } 310 if (arrayStartsWith(buffer, MPEG4_MAGIC_NMBER)) { 311 return decodedFile(FILE_TYPES['screen_recording'], buffer, fileName, store); 312 } 313 if (arrayStartsWith(buffer, WAYLAND_TRACE_MAGIC_NUMBER)) { 314 return decodedFile(FILE_TYPES['wl_trace'], buffer, fileName, store); 315 } 316 if (arrayStartsWith(buffer, PROTO_LOG_MAGIC_NUMBER)) { 317 return decodedFile(FILE_TYPES['proto_log'], buffer, fileName, store); 318 } 319 if (arrayStartsWith(buffer, SYSTEM_UI_MAGIC_NUMBER)) { 320 return decodedFile(FILE_TYPES['system_ui_trace'], buffer, fileName, store); 321 } 322 if (arrayStartsWith(buffer, LAUNCHER_MAGIC_NUMBER)) { 323 return decodedFile(FILE_TYPES['launcher_trace'], buffer, fileName, store); 324 } 325 for (var name of ['transaction', 'layers_dump', 'window_dump', 'wl_dump']) { 326 try { 327 return decodedFile(FILE_TYPES[name], buffer, fileName, store); 328 } catch (ex) { 329 // ignore exception and try next filetype 330 } 331 } 332 throw new Error('Unable to detect file'); 333} 334 335export { detectAndDecode, DATA_TYPES, FILE_TYPES }; 336