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