• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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