blob: 4fd00a7dfa22462f0dad8dfe1a19f0d5a0500674 [file] [edit]
// Copyright (C) 2026 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 {assetSrc} from './assets';
import {defer} from './deferred';
import type {ErrorDetails} from './logging';
import type {time} from './time';
import {utf8Decode} from './string_utils';
type Args =
| UpdateStatusArgs
| JobCompletedArgs
| DownloadFileArgs
| OpenTraceInLegacyArgs
| ErrorArgs;
export type PprofProfileType = 'alloc' | 'perf' | 'java-heap';
export type WorkerRequest =
| ConvertTraceAndDownloadRequest
| ConvertTraceAndOpenInLegacyRequest
| ConvertTraceToPprofRequest;
export interface ConvertTraceAndDownloadRequest {
kind: 'ConvertTraceAndDownload';
trace: Blob;
format: 'json' | 'systrace';
truncate?: 'start' | 'end';
}
export interface ConvertTraceAndOpenInLegacyRequest {
kind: 'ConvertTraceAndOpenInLegacy';
trace: Blob;
truncate?: 'start' | 'end';
}
export interface ConvertTraceToPprofRequest {
kind: 'ConvertTraceToPprof';
trace: Blob;
profileType: PprofProfileType;
pid: number;
ts: time;
}
interface UpdateStatusArgs {
kind: 'updateStatus';
status: string;
}
interface JobCompletedArgs {
kind: 'jobCompleted';
}
interface DownloadFileArgs {
kind: 'downloadFile';
buffer: Uint8Array<ArrayBuffer>;
name: string;
}
interface OpenTraceInLegacyArgs {
kind: 'openTraceInLegacy';
buffer: Uint8Array;
}
interface ErrorArgs {
kind: 'error';
error: ErrorDetails;
}
type OpenTraceInLegacyCallback = (
name: string,
data: ArrayBuffer | string,
size: number,
) => void;
export interface WorkerCallbacks {
readonly onStatus?: (status: string) => void;
readonly onError?: (error: ErrorDetails) => void;
readonly onDownloadFile?: (
buffer: Uint8Array<ArrayBuffer>,
name: string,
) => void;
readonly onOpenTraceInLegacy?: OpenTraceInLegacyCallback;
}
// A thin wrapper around the traceconv bundle. Simply runs the bundle in a
// worker, sends the request, and calls the various callbacks when the worker
// sends the relevant messages back.
export async function runTraceconv(
msg: WorkerRequest,
callbacks?: WorkerCallbacks,
) {
const promise = defer<void>();
function handleOnMessage(msg: MessageEvent): void {
const args: Args = msg.data;
if (args.kind === 'updateStatus') {
callbacks?.onStatus?.(args.status);
} else if (args.kind === 'jobCompleted') {
promise.resolve();
} else if (args.kind === 'downloadFile') {
callbacks?.onDownloadFile?.(args.buffer, args.name);
} else if (args.kind === 'openTraceInLegacy') {
const str = utf8Decode(args.buffer);
callbacks?.onOpenTraceInLegacy?.('trace.json', str, 0);
} else if (args.kind === 'error') {
callbacks?.onError?.(args.error);
} else {
throw new Error(`Unhandled message ${JSON.stringify(args)}`);
}
}
const worker = new Worker(assetSrc('traceconv_bundle.js'));
worker.onmessage = handleOnMessage;
worker.postMessage(msg);
return promise;
}