xref: /aosp_15_r20/external/perfetto/ui/src/plugins/dev.perfetto.AndroidStartup/optimizations.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker// Copyright (C) 2024 The Android Open Source Project
2*6dbdd20aSAndroid Build Coastguard Worker//
3*6dbdd20aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*6dbdd20aSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*6dbdd20aSAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*6dbdd20aSAndroid Build Coastguard Worker//
7*6dbdd20aSAndroid Build Coastguard Worker//      http://www.apache.org/licenses/LICENSE-2.0
8*6dbdd20aSAndroid Build Coastguard Worker//
9*6dbdd20aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*6dbdd20aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*6dbdd20aSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*6dbdd20aSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*6dbdd20aSAndroid Build Coastguard Worker// limitations under the License.
14*6dbdd20aSAndroid Build Coastguard Worker
15*6dbdd20aSAndroid Build Coastguard Workerimport {Trace} from '../../public/trace';
16*6dbdd20aSAndroid Build Coastguard Workerimport {STR, LONG, NUM} from '../../trace_processor/query_result';
17*6dbdd20aSAndroid Build Coastguard Workerimport {createQuerySliceTrack} from '../../components/tracks/query_slice_track';
18*6dbdd20aSAndroid Build Coastguard Workerimport {TrackNode} from '../../public/workspace';
19*6dbdd20aSAndroid Build Coastguard Worker
20*6dbdd20aSAndroid Build Coastguard Worker// The metadata container that keeps track of optimizations for packages that have startup events.
21*6dbdd20aSAndroid Build Coastguard Workerinterface Startup {
22*6dbdd20aSAndroid Build Coastguard Worker  // The startup id.
23*6dbdd20aSAndroid Build Coastguard Worker  id: number;
24*6dbdd20aSAndroid Build Coastguard Worker  // The package name.
25*6dbdd20aSAndroid Build Coastguard Worker  package: string;
26*6dbdd20aSAndroid Build Coastguard Worker  // Time start
27*6dbdd20aSAndroid Build Coastguard Worker  ts: bigint;
28*6dbdd20aSAndroid Build Coastguard Worker  // Time end
29*6dbdd20aSAndroid Build Coastguard Worker  ts_end: bigint;
30*6dbdd20aSAndroid Build Coastguard Worker  // compilation filter
31*6dbdd20aSAndroid Build Coastguard Worker  filter?: string;
32*6dbdd20aSAndroid Build Coastguard Worker  // optimization status
33*6dbdd20aSAndroid Build Coastguard Worker  optimized?: boolean;
34*6dbdd20aSAndroid Build Coastguard Worker}
35*6dbdd20aSAndroid Build Coastguard Worker
36*6dbdd20aSAndroid Build Coastguard Worker// The log tag
37*6dbdd20aSAndroid Build Coastguard Workerconst tag = 'DexOptInsights';
38*6dbdd20aSAndroid Build Coastguard Worker// The pattern for the optimization filter.
39*6dbdd20aSAndroid Build Coastguard Workerconst FILTER_PATTERN = /filter=([^\s]+)/;
40*6dbdd20aSAndroid Build Coastguard Worker
41*6dbdd20aSAndroid Build Coastguard Worker/**
42*6dbdd20aSAndroid Build Coastguard Worker * Returns a track node that contains optimization status
43*6dbdd20aSAndroid Build Coastguard Worker * for the packages that started up in a trace.
44*6dbdd20aSAndroid Build Coastguard Worker * @param trace The loaded trace.
45*6dbdd20aSAndroid Build Coastguard Worker * @returns a track node with the optimizations status.
46*6dbdd20aSAndroid Build Coastguard Worker * `undefined` if there are no app startups detected.
47*6dbdd20aSAndroid Build Coastguard Worker */
48*6dbdd20aSAndroid Build Coastguard Workerexport async function optimizationsTrack(
49*6dbdd20aSAndroid Build Coastguard Worker  trace: Trace,
50*6dbdd20aSAndroid Build Coastguard Worker): Promise<TrackNode | undefined> {
51*6dbdd20aSAndroid Build Coastguard Worker  const startups: Array<Startup> = [];
52*6dbdd20aSAndroid Build Coastguard Worker
53*6dbdd20aSAndroid Build Coastguard Worker  // Find app startups
54*6dbdd20aSAndroid Build Coastguard Worker  let result = await trace.engine.query(
55*6dbdd20aSAndroid Build Coastguard Worker    `
56*6dbdd20aSAndroid Build Coastguard Worker        INCLUDE PERFETTO MODULE android.startup.startups;
57*6dbdd20aSAndroid Build Coastguard Worker        SELECT startup_id AS id, package, ts, ts_end FROM android_startups;`,
58*6dbdd20aSAndroid Build Coastguard Worker    tag,
59*6dbdd20aSAndroid Build Coastguard Worker  );
60*6dbdd20aSAndroid Build Coastguard Worker
61*6dbdd20aSAndroid Build Coastguard Worker  const it = result.iter({id: NUM, package: STR, ts: LONG, ts_end: LONG});
62*6dbdd20aSAndroid Build Coastguard Worker  for (; it.valid(); it.next()) {
63*6dbdd20aSAndroid Build Coastguard Worker    startups.push({
64*6dbdd20aSAndroid Build Coastguard Worker      id: it.id,
65*6dbdd20aSAndroid Build Coastguard Worker      package: it.package,
66*6dbdd20aSAndroid Build Coastguard Worker      ts: it.ts,
67*6dbdd20aSAndroid Build Coastguard Worker      ts_end: it.ts_end,
68*6dbdd20aSAndroid Build Coastguard Worker    });
69*6dbdd20aSAndroid Build Coastguard Worker  }
70*6dbdd20aSAndroid Build Coastguard Worker
71*6dbdd20aSAndroid Build Coastguard Worker  if (startups.length === 0) {
72*6dbdd20aSAndroid Build Coastguard Worker    // Nothing interesting to report.
73*6dbdd20aSAndroid Build Coastguard Worker    return undefined;
74*6dbdd20aSAndroid Build Coastguard Worker  }
75*6dbdd20aSAndroid Build Coastguard Worker
76*6dbdd20aSAndroid Build Coastguard Worker  for (const startup of startups) {
77*6dbdd20aSAndroid Build Coastguard Worker    // For each startup id get the optimization status
78*6dbdd20aSAndroid Build Coastguard Worker    result = await trace.engine.query(
79*6dbdd20aSAndroid Build Coastguard Worker      `
80*6dbdd20aSAndroid Build Coastguard Worker        INCLUDE PERFETTO MODULE android.startup.startups;
81*6dbdd20aSAndroid Build Coastguard Worker        SELECT slice_name AS name FROM
82*6dbdd20aSAndroid Build Coastguard Worker          android_slices_for_startup_and_slice_name(${startup.id}, 'location=* status=* filter=* reason=*');`,
83*6dbdd20aSAndroid Build Coastguard Worker      tag,
84*6dbdd20aSAndroid Build Coastguard Worker    );
85*6dbdd20aSAndroid Build Coastguard Worker    const it = result.iter({name: STR});
86*6dbdd20aSAndroid Build Coastguard Worker    for (; it.valid(); it.next()) {
87*6dbdd20aSAndroid Build Coastguard Worker      const name = it.name;
88*6dbdd20aSAndroid Build Coastguard Worker      const relevant = name.indexOf(startup.package) >= 0;
89*6dbdd20aSAndroid Build Coastguard Worker      if (relevant) {
90*6dbdd20aSAndroid Build Coastguard Worker        const matches = name.match(FILTER_PATTERN);
91*6dbdd20aSAndroid Build Coastguard Worker        if (matches) {
92*6dbdd20aSAndroid Build Coastguard Worker          const filter = matches[1];
93*6dbdd20aSAndroid Build Coastguard Worker          startup.filter = filter;
94*6dbdd20aSAndroid Build Coastguard Worker          startup.optimized = filter === 'speed-profile';
95*6dbdd20aSAndroid Build Coastguard Worker        }
96*6dbdd20aSAndroid Build Coastguard Worker      }
97*6dbdd20aSAndroid Build Coastguard Worker    }
98*6dbdd20aSAndroid Build Coastguard Worker  }
99*6dbdd20aSAndroid Build Coastguard Worker
100*6dbdd20aSAndroid Build Coastguard Worker  // Create the optimizations track and also avoid re-querying for the data we already have.
101*6dbdd20aSAndroid Build Coastguard Worker  const sqlSource = startups
102*6dbdd20aSAndroid Build Coastguard Worker    .map((startup) => {
103*6dbdd20aSAndroid Build Coastguard Worker      return `SELECT
104*6dbdd20aSAndroid Build Coastguard Worker        ${startup.ts} AS ts,
105*6dbdd20aSAndroid Build Coastguard Worker        ${startup.ts_end - startup.ts} AS dur,
106*6dbdd20aSAndroid Build Coastguard Worker        '${buildName(startup)}' AS name,
107*6dbdd20aSAndroid Build Coastguard Worker        '${buildDetails(startup)}' AS details
108*6dbdd20aSAndroid Build Coastguard Worker      `;
109*6dbdd20aSAndroid Build Coastguard Worker    })
110*6dbdd20aSAndroid Build Coastguard Worker    .join('UNION ALL '); // The trailing space is important.
111*6dbdd20aSAndroid Build Coastguard Worker
112*6dbdd20aSAndroid Build Coastguard Worker  const uri = '/android_startups_optimization_status';
113*6dbdd20aSAndroid Build Coastguard Worker  const title = 'Optimization Status';
114*6dbdd20aSAndroid Build Coastguard Worker  const track = await createQuerySliceTrack({
115*6dbdd20aSAndroid Build Coastguard Worker    trace: trace,
116*6dbdd20aSAndroid Build Coastguard Worker    uri: uri,
117*6dbdd20aSAndroid Build Coastguard Worker    data: {
118*6dbdd20aSAndroid Build Coastguard Worker      sqlSource: sqlSource,
119*6dbdd20aSAndroid Build Coastguard Worker      columns: ['ts', 'dur', 'name', 'details'],
120*6dbdd20aSAndroid Build Coastguard Worker    },
121*6dbdd20aSAndroid Build Coastguard Worker    argColumns: ['details'],
122*6dbdd20aSAndroid Build Coastguard Worker  });
123*6dbdd20aSAndroid Build Coastguard Worker  trace.tracks.registerTrack({
124*6dbdd20aSAndroid Build Coastguard Worker    uri,
125*6dbdd20aSAndroid Build Coastguard Worker    title,
126*6dbdd20aSAndroid Build Coastguard Worker    track,
127*6dbdd20aSAndroid Build Coastguard Worker  });
128*6dbdd20aSAndroid Build Coastguard Worker  return new TrackNode({title, uri});
129*6dbdd20aSAndroid Build Coastguard Worker}
130*6dbdd20aSAndroid Build Coastguard Worker
131*6dbdd20aSAndroid Build Coastguard Workerfunction buildName(startup: Startup): string {
132*6dbdd20aSAndroid Build Coastguard Worker  if (
133*6dbdd20aSAndroid Build Coastguard Worker    !!startup.filter === false ||
134*6dbdd20aSAndroid Build Coastguard Worker    startup.filter === 'verify' ||
135*6dbdd20aSAndroid Build Coastguard Worker    startup.filter === 'speed'
136*6dbdd20aSAndroid Build Coastguard Worker  ) {
137*6dbdd20aSAndroid Build Coastguard Worker    return `Sub-optimal compilation state (${startup.filter})`;
138*6dbdd20aSAndroid Build Coastguard Worker  } else if (startup.filter === 'speed-profile') {
139*6dbdd20aSAndroid Build Coastguard Worker    return 'Ideal compilation state (speed-profile)';
140*6dbdd20aSAndroid Build Coastguard Worker  } else {
141*6dbdd20aSAndroid Build Coastguard Worker    return `Unknown compilation state (${startup.filter})`;
142*6dbdd20aSAndroid Build Coastguard Worker  }
143*6dbdd20aSAndroid Build Coastguard Worker}
144*6dbdd20aSAndroid Build Coastguard Worker
145*6dbdd20aSAndroid Build Coastguard Workerfunction buildDetails(startup: Startup): string {
146*6dbdd20aSAndroid Build Coastguard Worker  if (startup.filter === 'verify' || !!startup.filter === false) {
147*6dbdd20aSAndroid Build Coastguard Worker    return `No methods are precompiled, and class loading is unoptimized`;
148*6dbdd20aSAndroid Build Coastguard Worker  } else if (startup.filter === 'speed') {
149*6dbdd20aSAndroid Build Coastguard Worker    return 'Methods are all precompiled, and class loading is unoptimized';
150*6dbdd20aSAndroid Build Coastguard Worker  } else if (startup.filter === 'speed-profile') {
151*6dbdd20aSAndroid Build Coastguard Worker    return 'Methods and classes in the profile are optimized';
152*6dbdd20aSAndroid Build Coastguard Worker  } else {
153*6dbdd20aSAndroid Build Coastguard Worker    return `Unknown compilation state (${startup.filter})`;
154*6dbdd20aSAndroid Build Coastguard Worker  }
155*6dbdd20aSAndroid Build Coastguard Worker}
156