1// Copyright (C) 2022 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 {PerfettoMetatrace, Trace, TracePacket} from '../common/protos'; 16import {perfetto} from '../gen/protos'; 17 18import {featureFlags} from './feature_flags'; 19import {toNs} from './time'; 20 21const METATRACING_BUFFER_SIZE = 100000; 22const JS_THREAD_ID = 2; 23 24import MetatraceCategories = perfetto.protos.MetatraceCategories; 25 26const AOMT_FLAG = featureFlags.register({ 27 id: 'alwaysOnMetatracing', 28 name: 'Enable always-on-metatracing', 29 description: 'Enables trace events in the UI and trace processor', 30 defaultValue: false, 31}); 32 33const AOMT_DETAILED_FLAG = featureFlags.register({ 34 id: 'alwaysOnMetatracing_detailed', 35 name: 'Detailed always-on-metatracing', 36 description: 'Enables recording additional events for trace event', 37 defaultValue: false, 38}); 39 40function getInitialCategories(): MetatraceCategories|undefined { 41 if (!AOMT_FLAG.get()) return undefined; 42 if (AOMT_DETAILED_FLAG.get()) return MetatraceCategories.ALL; 43 return MetatraceCategories.TOPLEVEL; 44} 45 46let enabledCategories: MetatraceCategories|undefined = getInitialCategories(); 47 48export function enableMetatracing(categories?: MetatraceCategories) { 49 enabledCategories = categories || MetatraceCategories.ALL; 50} 51 52export function disableMetatracingAndGetTrace(): Uint8Array { 53 enabledCategories = undefined; 54 return readMetatrace(); 55} 56 57export function isMetatracingEnabled(): boolean { 58 return enabledCategories !== undefined; 59} 60 61export function getEnabledMetatracingCategories(): MetatraceCategories| 62 undefined { 63 return enabledCategories; 64} 65 66interface TraceEvent { 67 eventName: string; 68 startNs: number; 69 durNs: number; 70} 71 72const traceEvents: TraceEvent[] = []; 73 74function readMetatrace(): Uint8Array { 75 const eventToPacket = (e: TraceEvent): TracePacket => { 76 return TracePacket.create({ 77 timestamp: e.startNs, 78 timestampClockId: 1, 79 perfettoMetatrace: PerfettoMetatrace.create({ 80 eventName: e.eventName, 81 threadId: JS_THREAD_ID, 82 eventDurationNs: e.durNs, 83 }), 84 }); 85 }; 86 const packets: TracePacket[] = []; 87 for (const event of traceEvents) { 88 packets.push(eventToPacket(event)); 89 } 90 const trace = Trace.create({ 91 packet: packets, 92 }); 93 return Trace.encode(trace).finish(); 94} 95 96export type TraceEventScope = { 97 startNs: number, eventName: string; 98}; 99 100const correctedTimeOrigin = new Date().getTime() - performance.now(); 101 102function now(): number { 103 return toNs((correctedTimeOrigin + performance.now()) / 1000); 104} 105 106export function traceEventBegin(eventName: string): TraceEventScope { 107 return { 108 eventName, 109 startNs: now(), 110 }; 111} 112 113export function traceEventEnd(traceEvent: TraceEventScope) { 114 if (!isMetatracingEnabled()) return; 115 116 traceEvents.push({ 117 eventName: traceEvent.eventName, 118 startNs: traceEvent.startNs, 119 durNs: now() - traceEvent.startNs, 120 }); 121 while (traceEvents.length > METATRACING_BUFFER_SIZE) { 122 traceEvents.shift(); 123 } 124} 125