1// Copyright (C) 2024 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import protos from '../../../protos'; 16import {assetSrc} from '../../../base/assets'; 17import {defer} from '../../../base/deferred'; 18import {assertTrue} from '../../../base/logging'; 19import {errResult, okResult, Result} from '../../../base/result'; 20import {utf8Decode, utf8Encode} from '../../../base/string_utils'; 21import WasmModuleGen from '../../../gen/trace_config_utils'; 22 23/** 24 * This file is the TS-equivalent of src/trace_config_utils. 25 * It exposes two functions to conver the TraceConfig proto from txt<>protobuf. 26 * It guarrantees to have the same behaviour of perfetto_cmd and trace_processor 27 * by using precisely the same code via WebAssembly. 28 */ 29interface WasmModule { 30 module: WasmModuleGen.Module; 31 buf: Uint8Array; 32} 33 34let moduleInstance: WasmModule | undefined = undefined; 35 36/** 37 * Convert a binary-encoded protos.TracConfig to pbtxt (i.e. the text format 38 * that can be passed to perfetto --txt). 39 */ 40export async function traceConfigToTxt( 41 config: Uint8Array | protos.ITraceConfig, 42): Promise<string> { 43 const wasm = await initWasmOnce(); 44 45 const configU8: Uint8Array = 46 config instanceof Uint8Array 47 ? config 48 : protos.TraceConfig.encode(config).finish(); 49 assertTrue(configU8.length <= wasm.buf.length); 50 wasm.buf.set(configU8); 51 52 const txtSize = 53 wasm.module.ccall( 54 'trace_config_pb_to_txt', 55 'number', 56 ['number'], 57 [configU8.length], 58 ) >>> 0; 59 60 const txt = utf8Decode(wasm.buf.subarray(0, txtSize)); 61 return txt; 62} 63 64/** Convert a pbtxt (text-proto) text to a proto-encoded TraceConfig. */ 65export async function traceConfigToPb( 66 configTxt: string, 67): Promise<Result<Uint8Array>> { 68 const wasm = await initWasmOnce(); 69 70 const configUtf8 = utf8Encode(configTxt); 71 assertTrue(configUtf8.length <= wasm.buf.length); 72 wasm.buf.set(configUtf8); 73 74 const resSize = 75 wasm.module.ccall( 76 'trace_config_txt_to_pb', 77 'number', 78 ['number'], 79 [configUtf8.length], 80 ) >>> 0; 81 82 const success = wasm.buf.at(0) === 1; 83 const payload = wasm.buf.slice(1, 1 + resSize); 84 return success ? okResult(payload) : errResult(utf8Decode(payload)); 85} 86 87async function initWasmOnce(): Promise<WasmModule> { 88 if (moduleInstance === undefined) { 89 // We have to fetch the .wasm file manually because the stub generated by 90 // emscripten uses sync-loading, which works only in Workers. 91 const resp = await fetch(assetSrc('trace_config_utils.wasm')); 92 const wasmBinary = await resp.arrayBuffer(); 93 const deferredRuntimeInitialized = defer<void>(); 94 const instance = WasmModuleGen({ 95 noInitialRun: true, 96 locateFile: (s: string) => s, 97 print: (s: string) => console.log(s), 98 printErr: (s: string) => console.error(s), 99 onRuntimeInitialized: () => deferredRuntimeInitialized.resolve(), 100 wasmBinary, 101 } as WasmModuleGen.ModuleArgs); 102 await deferredRuntimeInitialized; 103 const bufAddr = 104 instance.ccall('trace_config_utils_buf', 'number', [], []) >>> 0; 105 const bufSize = 106 instance.ccall('trace_config_utils_buf_size', 'number', [], []) >>> 0; 107 moduleInstance = { 108 module: instance, 109 buf: instance.HEAPU8.subarray(bufAddr, bufAddr + bufSize), 110 }; 111 } 112 return moduleInstance; 113} 114