| // Copyright (C) 2022 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 {PerfettoMetatrace, Trace, TracePacket} from '../common/protos'; |
| import {perfetto} from '../gen/protos'; |
| |
| import {featureFlags} from './feature_flags'; |
| import {toNs} from './time'; |
| |
| const METATRACING_BUFFER_SIZE = 100000; |
| const JS_THREAD_ID = 2; |
| |
| import MetatraceCategories = perfetto.protos.MetatraceCategories; |
| |
| const AOMT_FLAG = featureFlags.register({ |
| id: 'alwaysOnMetatracing', |
| name: 'Enable always-on-metatracing', |
| description: 'Enables trace events in the UI and trace processor', |
| defaultValue: false, |
| }); |
| |
| const AOMT_DETAILED_FLAG = featureFlags.register({ |
| id: 'alwaysOnMetatracing_detailed', |
| name: 'Detailed always-on-metatracing', |
| description: 'Enables recording additional events for trace event', |
| defaultValue: false, |
| }); |
| |
| function getInitialCategories(): MetatraceCategories|undefined { |
| if (!AOMT_FLAG.get()) return undefined; |
| if (AOMT_DETAILED_FLAG.get()) return MetatraceCategories.ALL; |
| return MetatraceCategories.TOPLEVEL; |
| } |
| |
| let enabledCategories: MetatraceCategories|undefined = getInitialCategories(); |
| |
| export function enableMetatracing(categories?: MetatraceCategories) { |
| enabledCategories = categories || MetatraceCategories.ALL; |
| } |
| |
| export function disableMetatracingAndGetTrace(): Uint8Array { |
| enabledCategories = undefined; |
| return readMetatrace(); |
| } |
| |
| export function isMetatracingEnabled(): boolean { |
| return enabledCategories !== undefined; |
| } |
| |
| export function getEnabledMetatracingCategories(): MetatraceCategories| |
| undefined { |
| return enabledCategories; |
| } |
| |
| interface TraceEvent { |
| eventName: string; |
| startNs: number; |
| durNs: number; |
| } |
| |
| const traceEvents: TraceEvent[] = []; |
| |
| function readMetatrace(): Uint8Array { |
| const eventToPacket = (e: TraceEvent): TracePacket => { |
| return TracePacket.create({ |
| timestamp: e.startNs, |
| timestampClockId: 1, |
| perfettoMetatrace: PerfettoMetatrace.create({ |
| eventName: e.eventName, |
| threadId: JS_THREAD_ID, |
| eventDurationNs: e.durNs, |
| }), |
| }); |
| }; |
| const packets: TracePacket[] = []; |
| for (const event of traceEvents) { |
| packets.push(eventToPacket(event)); |
| } |
| const trace = Trace.create({ |
| packet: packets, |
| }); |
| return Trace.encode(trace).finish(); |
| } |
| |
| export type TraceEventScope = { |
| startNs: number, eventName: string; |
| }; |
| |
| const correctedTimeOrigin = new Date().getTime() - performance.now(); |
| |
| function now(): number { |
| return toNs((correctedTimeOrigin + performance.now()) / 1000); |
| } |
| |
| export function traceEventBegin(eventName: string): TraceEventScope { |
| return { |
| eventName, |
| startNs: now(), |
| }; |
| } |
| |
| export function traceEventEnd(traceEvent: TraceEventScope) { |
| if (!isMetatracingEnabled()) return; |
| |
| traceEvents.push({ |
| eventName: traceEvent.eventName, |
| startNs: traceEvent.startNs, |
| durNs: now() - traceEvent.startNs, |
| }); |
| while (traceEvents.length > METATRACING_BUFFER_SIZE) { |
| traceEvents.shift(); |
| } |
| } |