1*90c8c64dSAndroid Build Coastguard Worker/* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2023 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License. 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Workerimport {assertDefined} from 'common/assert_utils'; 18*90c8c64dSAndroid Build Coastguard Workerimport {FileUtils} from 'common/file_utils'; 19*90c8c64dSAndroid Build Coastguard Workerimport {OnProgressUpdateType} from 'common/function_utils'; 20*90c8c64dSAndroid Build Coastguard Workerimport {INVALID_TIME_NS, TimeRange, Timestamp} from 'common/time'; 21*90c8c64dSAndroid Build Coastguard Workerimport {TIME_UNIT_TO_NANO} from 'common/time_units'; 22*90c8c64dSAndroid Build Coastguard Workerimport {UserNotifier} from 'common/user_notifier'; 23*90c8c64dSAndroid Build Coastguard Workerimport {TraceHasOldData, TraceOverridden} from 'messaging/user_warnings'; 24*90c8c64dSAndroid Build Coastguard Workerimport {FileAndParser} from 'parsers/file_and_parser'; 25*90c8c64dSAndroid Build Coastguard Workerimport {FileAndParsers} from 'parsers/file_and_parsers'; 26*90c8c64dSAndroid Build Coastguard Workerimport {Parser} from 'trace/parser'; 27*90c8c64dSAndroid Build Coastguard Workerimport {TraceFile} from 'trace/trace_file'; 28*90c8c64dSAndroid Build Coastguard Workerimport {TRACE_INFO} from 'trace/trace_info'; 29*90c8c64dSAndroid Build Coastguard Workerimport {TraceEntryTypeMap, TraceType} from 'trace/trace_type'; 30*90c8c64dSAndroid Build Coastguard Worker 31*90c8c64dSAndroid Build Coastguard Workerexport class LoadedParsers { 32*90c8c64dSAndroid Build Coastguard Worker static readonly MAX_ALLOWED_TIME_GAP_BETWEEN_TRACES_NS = BigInt( 33*90c8c64dSAndroid Build Coastguard Worker 5 * TIME_UNIT_TO_NANO.m, 34*90c8c64dSAndroid Build Coastguard Worker ); // 5m 35*90c8c64dSAndroid Build Coastguard Worker static readonly MAX_ALLOWED_TIME_GAP_BETWEEN_RTE_OFFSET = BigInt( 36*90c8c64dSAndroid Build Coastguard Worker 5 * TIME_UNIT_TO_NANO.s, 37*90c8c64dSAndroid Build Coastguard Worker ); // 5s 38*90c8c64dSAndroid Build Coastguard Worker static readonly REAL_TIME_TRACES_WITHOUT_RTE_OFFSET = [ 39*90c8c64dSAndroid Build Coastguard Worker TraceType.CUJS, 40*90c8c64dSAndroid Build Coastguard Worker TraceType.EVENT_LOG, 41*90c8c64dSAndroid Build Coastguard Worker ]; 42*90c8c64dSAndroid Build Coastguard Worker 43*90c8c64dSAndroid Build Coastguard Worker private legacyParsers = new Array<FileAndParser>(); 44*90c8c64dSAndroid Build Coastguard Worker private perfettoParsers = new Array<FileAndParser>(); 45*90c8c64dSAndroid Build Coastguard Worker private legacyParsersKeptForDownload = new Array<FileAndParser>(); 46*90c8c64dSAndroid Build Coastguard Worker private perfettoParsersKeptForDownload = new Array<FileAndParser>(); 47*90c8c64dSAndroid Build Coastguard Worker 48*90c8c64dSAndroid Build Coastguard Worker addParsers( 49*90c8c64dSAndroid Build Coastguard Worker legacyParsers: FileAndParser[], 50*90c8c64dSAndroid Build Coastguard Worker perfettoParsers: FileAndParsers | undefined, 51*90c8c64dSAndroid Build Coastguard Worker ) { 52*90c8c64dSAndroid Build Coastguard Worker if (perfettoParsers) { 53*90c8c64dSAndroid Build Coastguard Worker this.addPerfettoParsers(perfettoParsers); 54*90c8c64dSAndroid Build Coastguard Worker } 55*90c8c64dSAndroid Build Coastguard Worker // Traces were simultaneously upgraded to contain real-to-boottime or real-to-monotonic offsets. 56*90c8c64dSAndroid Build Coastguard Worker // If we have a mix of parsers with and without offsets, the ones without must be dangling 57*90c8c64dSAndroid Build Coastguard Worker // trace files with old data, and should be filtered out. 58*90c8c64dSAndroid Build Coastguard Worker legacyParsers = this.filterOutParsersWithoutOffsetsIfRequired( 59*90c8c64dSAndroid Build Coastguard Worker legacyParsers, 60*90c8c64dSAndroid Build Coastguard Worker perfettoParsers, 61*90c8c64dSAndroid Build Coastguard Worker ); 62*90c8c64dSAndroid Build Coastguard Worker legacyParsers = this.filterOutLegacyParsersWithOldData(legacyParsers); 63*90c8c64dSAndroid Build Coastguard Worker legacyParsers = this.filterScreenshotParsersIfRequired(legacyParsers); 64*90c8c64dSAndroid Build Coastguard Worker 65*90c8c64dSAndroid Build Coastguard Worker this.addLegacyParsers(legacyParsers); 66*90c8c64dSAndroid Build Coastguard Worker } 67*90c8c64dSAndroid Build Coastguard Worker 68*90c8c64dSAndroid Build Coastguard Worker getParsers(): Array<Parser<object>> { 69*90c8c64dSAndroid Build Coastguard Worker const fileAndParsers = [ 70*90c8c64dSAndroid Build Coastguard Worker ...this.legacyParsers.values(), 71*90c8c64dSAndroid Build Coastguard Worker ...this.perfettoParsers.values(), 72*90c8c64dSAndroid Build Coastguard Worker ]; 73*90c8c64dSAndroid Build Coastguard Worker return fileAndParsers.map((fileAndParser) => fileAndParser.parser); 74*90c8c64dSAndroid Build Coastguard Worker } 75*90c8c64dSAndroid Build Coastguard Worker 76*90c8c64dSAndroid Build Coastguard Worker remove<T extends TraceType>( 77*90c8c64dSAndroid Build Coastguard Worker parser: Parser<TraceEntryTypeMap[T]>, 78*90c8c64dSAndroid Build Coastguard Worker keepForDownload = false, 79*90c8c64dSAndroid Build Coastguard Worker ) { 80*90c8c64dSAndroid Build Coastguard Worker const predicate = ( 81*90c8c64dSAndroid Build Coastguard Worker fileAndParser: FileAndParser, 82*90c8c64dSAndroid Build Coastguard Worker parsersToKeep: FileAndParser[], 83*90c8c64dSAndroid Build Coastguard Worker ) => { 84*90c8c64dSAndroid Build Coastguard Worker const shouldRemove = fileAndParser.parser === parser; 85*90c8c64dSAndroid Build Coastguard Worker if (shouldRemove && keepForDownload) { 86*90c8c64dSAndroid Build Coastguard Worker parsersToKeep.push(fileAndParser); 87*90c8c64dSAndroid Build Coastguard Worker } 88*90c8c64dSAndroid Build Coastguard Worker return !shouldRemove; 89*90c8c64dSAndroid Build Coastguard Worker }; 90*90c8c64dSAndroid Build Coastguard Worker this.legacyParsers = this.legacyParsers.filter( 91*90c8c64dSAndroid Build Coastguard Worker (fileAndParser: FileAndParser) => 92*90c8c64dSAndroid Build Coastguard Worker predicate(fileAndParser, this.legacyParsersKeptForDownload), 93*90c8c64dSAndroid Build Coastguard Worker ); 94*90c8c64dSAndroid Build Coastguard Worker this.perfettoParsers = this.perfettoParsers.filter( 95*90c8c64dSAndroid Build Coastguard Worker (fileAndParser: FileAndParser) => 96*90c8c64dSAndroid Build Coastguard Worker predicate(fileAndParser, this.perfettoParsersKeptForDownload), 97*90c8c64dSAndroid Build Coastguard Worker ); 98*90c8c64dSAndroid Build Coastguard Worker } 99*90c8c64dSAndroid Build Coastguard Worker 100*90c8c64dSAndroid Build Coastguard Worker clear() { 101*90c8c64dSAndroid Build Coastguard Worker this.legacyParsers = []; 102*90c8c64dSAndroid Build Coastguard Worker this.perfettoParsers = []; 103*90c8c64dSAndroid Build Coastguard Worker } 104*90c8c64dSAndroid Build Coastguard Worker 105*90c8c64dSAndroid Build Coastguard Worker async makeZipArchive(onProgressUpdate?: OnProgressUpdateType): Promise<Blob> { 106*90c8c64dSAndroid Build Coastguard Worker const outputFilesSoFar = new Set<File>(); 107*90c8c64dSAndroid Build Coastguard Worker const outputFilenameToFiles = new Map<string, File[]>(); 108*90c8c64dSAndroid Build Coastguard Worker 109*90c8c64dSAndroid Build Coastguard Worker if (onProgressUpdate) onProgressUpdate(0); 110*90c8c64dSAndroid Build Coastguard Worker const totalParsers = 111*90c8c64dSAndroid Build Coastguard Worker this.perfettoParsers.length + 112*90c8c64dSAndroid Build Coastguard Worker this.perfettoParsersKeptForDownload.length + 113*90c8c64dSAndroid Build Coastguard Worker this.legacyParsers.length + 114*90c8c64dSAndroid Build Coastguard Worker this.legacyParsersKeptForDownload.length; 115*90c8c64dSAndroid Build Coastguard Worker let progress = 0; 116*90c8c64dSAndroid Build Coastguard Worker 117*90c8c64dSAndroid Build Coastguard Worker const tryPushOutputFile = (file: File, filename: string) => { 118*90c8c64dSAndroid Build Coastguard Worker // Remove duplicates because some parsers (e.g. view capture) could share the same file 119*90c8c64dSAndroid Build Coastguard Worker if (outputFilesSoFar.has(file)) { 120*90c8c64dSAndroid Build Coastguard Worker return; 121*90c8c64dSAndroid Build Coastguard Worker } 122*90c8c64dSAndroid Build Coastguard Worker outputFilesSoFar.add(file); 123*90c8c64dSAndroid Build Coastguard Worker 124*90c8c64dSAndroid Build Coastguard Worker if (outputFilenameToFiles.get(filename) === undefined) { 125*90c8c64dSAndroid Build Coastguard Worker outputFilenameToFiles.set(filename, []); 126*90c8c64dSAndroid Build Coastguard Worker } 127*90c8c64dSAndroid Build Coastguard Worker assertDefined(outputFilenameToFiles.get(filename)).push(file); 128*90c8c64dSAndroid Build Coastguard Worker }; 129*90c8c64dSAndroid Build Coastguard Worker 130*90c8c64dSAndroid Build Coastguard Worker const makeArchiveFile = ( 131*90c8c64dSAndroid Build Coastguard Worker filename: string, 132*90c8c64dSAndroid Build Coastguard Worker file: File, 133*90c8c64dSAndroid Build Coastguard Worker clashCount: number, 134*90c8c64dSAndroid Build Coastguard Worker ): File => { 135*90c8c64dSAndroid Build Coastguard Worker if (clashCount === 0) { 136*90c8c64dSAndroid Build Coastguard Worker return new File([file], filename); 137*90c8c64dSAndroid Build Coastguard Worker } 138*90c8c64dSAndroid Build Coastguard Worker 139*90c8c64dSAndroid Build Coastguard Worker const filenameWithoutExt = 140*90c8c64dSAndroid Build Coastguard Worker FileUtils.removeExtensionFromFilename(filename); 141*90c8c64dSAndroid Build Coastguard Worker const extension = FileUtils.getFileExtension(filename); 142*90c8c64dSAndroid Build Coastguard Worker 143*90c8c64dSAndroid Build Coastguard Worker if (extension === undefined) { 144*90c8c64dSAndroid Build Coastguard Worker return new File([file], `${filename} (${clashCount})`); 145*90c8c64dSAndroid Build Coastguard Worker } 146*90c8c64dSAndroid Build Coastguard Worker 147*90c8c64dSAndroid Build Coastguard Worker return new File( 148*90c8c64dSAndroid Build Coastguard Worker [file], 149*90c8c64dSAndroid Build Coastguard Worker `${filenameWithoutExt} (${clashCount}).${extension}`, 150*90c8c64dSAndroid Build Coastguard Worker ); 151*90c8c64dSAndroid Build Coastguard Worker }; 152*90c8c64dSAndroid Build Coastguard Worker 153*90c8c64dSAndroid Build Coastguard Worker const tryPushOutPerfettoFile = (parsers: FileAndParser[]) => { 154*90c8c64dSAndroid Build Coastguard Worker const file: TraceFile = parsers.values().next().value.file; 155*90c8c64dSAndroid Build Coastguard Worker let outputFilename = FileUtils.removeDirFromFileName(file.file.name); 156*90c8c64dSAndroid Build Coastguard Worker if (FileUtils.getFileExtension(file.file.name) === undefined) { 157*90c8c64dSAndroid Build Coastguard Worker outputFilename += '.perfetto-trace'; 158*90c8c64dSAndroid Build Coastguard Worker } 159*90c8c64dSAndroid Build Coastguard Worker tryPushOutputFile(file.file, outputFilename); 160*90c8c64dSAndroid Build Coastguard Worker }; 161*90c8c64dSAndroid Build Coastguard Worker 162*90c8c64dSAndroid Build Coastguard Worker if (this.perfettoParsers.length > 0) { 163*90c8c64dSAndroid Build Coastguard Worker tryPushOutPerfettoFile(this.perfettoParsers); 164*90c8c64dSAndroid Build Coastguard Worker } else if (this.perfettoParsersKeptForDownload.length > 0) { 165*90c8c64dSAndroid Build Coastguard Worker tryPushOutPerfettoFile(this.perfettoParsersKeptForDownload); 166*90c8c64dSAndroid Build Coastguard Worker } 167*90c8c64dSAndroid Build Coastguard Worker if (onProgressUpdate) { 168*90c8c64dSAndroid Build Coastguard Worker progress = 169*90c8c64dSAndroid Build Coastguard Worker this.perfettoParsers.length + 170*90c8c64dSAndroid Build Coastguard Worker this.perfettoParsersKeptForDownload.length; 171*90c8c64dSAndroid Build Coastguard Worker onProgressUpdate((0.5 * progress) / totalParsers); 172*90c8c64dSAndroid Build Coastguard Worker } 173*90c8c64dSAndroid Build Coastguard Worker 174*90c8c64dSAndroid Build Coastguard Worker const tryPushOutputLegacyFile = (fileAndParser: FileAndParser) => { 175*90c8c64dSAndroid Build Coastguard Worker const {file, parser} = fileAndParser; 176*90c8c64dSAndroid Build Coastguard Worker const traceType = parser.getTraceType(); 177*90c8c64dSAndroid Build Coastguard Worker const archiveDir = 178*90c8c64dSAndroid Build Coastguard Worker TRACE_INFO[traceType].downloadArchiveDir.length > 0 179*90c8c64dSAndroid Build Coastguard Worker ? TRACE_INFO[traceType].downloadArchiveDir + '/' 180*90c8c64dSAndroid Build Coastguard Worker : ''; 181*90c8c64dSAndroid Build Coastguard Worker let outputFilename = 182*90c8c64dSAndroid Build Coastguard Worker archiveDir + FileUtils.removeDirFromFileName(file.file.name); 183*90c8c64dSAndroid Build Coastguard Worker if (FileUtils.getFileExtension(file.file.name) === undefined) { 184*90c8c64dSAndroid Build Coastguard Worker outputFilename += TRACE_INFO[traceType].legacyExt; 185*90c8c64dSAndroid Build Coastguard Worker } 186*90c8c64dSAndroid Build Coastguard Worker tryPushOutputFile(file.file, outputFilename); 187*90c8c64dSAndroid Build Coastguard Worker if (onProgressUpdate) { 188*90c8c64dSAndroid Build Coastguard Worker progress++; 189*90c8c64dSAndroid Build Coastguard Worker onProgressUpdate((0.5 * progress) / totalParsers); 190*90c8c64dSAndroid Build Coastguard Worker } 191*90c8c64dSAndroid Build Coastguard Worker }; 192*90c8c64dSAndroid Build Coastguard Worker 193*90c8c64dSAndroid Build Coastguard Worker this.legacyParsers.forEach(tryPushOutputLegacyFile); 194*90c8c64dSAndroid Build Coastguard Worker this.legacyParsersKeptForDownload.forEach(tryPushOutputLegacyFile); 195*90c8c64dSAndroid Build Coastguard Worker 196*90c8c64dSAndroid Build Coastguard Worker const archiveFiles = [...outputFilenameToFiles.entries()] 197*90c8c64dSAndroid Build Coastguard Worker .map(([filename, files]) => { 198*90c8c64dSAndroid Build Coastguard Worker return files.map((file, clashCount) => 199*90c8c64dSAndroid Build Coastguard Worker makeArchiveFile(filename, file, clashCount), 200*90c8c64dSAndroid Build Coastguard Worker ); 201*90c8c64dSAndroid Build Coastguard Worker }) 202*90c8c64dSAndroid Build Coastguard Worker .flat(); 203*90c8c64dSAndroid Build Coastguard Worker 204*90c8c64dSAndroid Build Coastguard Worker return await FileUtils.createZipArchive( 205*90c8c64dSAndroid Build Coastguard Worker archiveFiles, 206*90c8c64dSAndroid Build Coastguard Worker onProgressUpdate 207*90c8c64dSAndroid Build Coastguard Worker ? (perc: number) => onProgressUpdate(0.5 * (1 + perc)) 208*90c8c64dSAndroid Build Coastguard Worker : undefined, 209*90c8c64dSAndroid Build Coastguard Worker ); 210*90c8c64dSAndroid Build Coastguard Worker } 211*90c8c64dSAndroid Build Coastguard Worker 212*90c8c64dSAndroid Build Coastguard Worker getLatestRealToMonotonicOffset( 213*90c8c64dSAndroid Build Coastguard Worker parsers: Array<Parser<object>>, 214*90c8c64dSAndroid Build Coastguard Worker ): bigint | undefined { 215*90c8c64dSAndroid Build Coastguard Worker const p = parsers 216*90c8c64dSAndroid Build Coastguard Worker .filter((offset) => offset.getRealToMonotonicTimeOffsetNs() !== undefined) 217*90c8c64dSAndroid Build Coastguard Worker .sort((a, b) => { 218*90c8c64dSAndroid Build Coastguard Worker return Number( 219*90c8c64dSAndroid Build Coastguard Worker (a.getRealToMonotonicTimeOffsetNs() ?? 0n) - 220*90c8c64dSAndroid Build Coastguard Worker (b.getRealToMonotonicTimeOffsetNs() ?? 0n), 221*90c8c64dSAndroid Build Coastguard Worker ); 222*90c8c64dSAndroid Build Coastguard Worker }) 223*90c8c64dSAndroid Build Coastguard Worker .at(-1); 224*90c8c64dSAndroid Build Coastguard Worker return p?.getRealToMonotonicTimeOffsetNs(); 225*90c8c64dSAndroid Build Coastguard Worker } 226*90c8c64dSAndroid Build Coastguard Worker 227*90c8c64dSAndroid Build Coastguard Worker getLatestRealToBootTimeOffset( 228*90c8c64dSAndroid Build Coastguard Worker parsers: Array<Parser<object>>, 229*90c8c64dSAndroid Build Coastguard Worker ): bigint | undefined { 230*90c8c64dSAndroid Build Coastguard Worker const p = parsers 231*90c8c64dSAndroid Build Coastguard Worker .filter((offset) => offset.getRealToBootTimeOffsetNs() !== undefined) 232*90c8c64dSAndroid Build Coastguard Worker .sort((a, b) => { 233*90c8c64dSAndroid Build Coastguard Worker return Number( 234*90c8c64dSAndroid Build Coastguard Worker (a.getRealToBootTimeOffsetNs() ?? 0n) - 235*90c8c64dSAndroid Build Coastguard Worker (b.getRealToBootTimeOffsetNs() ?? 0n), 236*90c8c64dSAndroid Build Coastguard Worker ); 237*90c8c64dSAndroid Build Coastguard Worker }) 238*90c8c64dSAndroid Build Coastguard Worker .at(-1); 239*90c8c64dSAndroid Build Coastguard Worker return p?.getRealToBootTimeOffsetNs(); 240*90c8c64dSAndroid Build Coastguard Worker } 241*90c8c64dSAndroid Build Coastguard Worker 242*90c8c64dSAndroid Build Coastguard Worker private addLegacyParsers(parsers: FileAndParser[]) { 243*90c8c64dSAndroid Build Coastguard Worker const legacyParsersBeingLoaded = new Map<TraceType, Parser<object>>(); 244*90c8c64dSAndroid Build Coastguard Worker 245*90c8c64dSAndroid Build Coastguard Worker parsers.forEach((fileAndParser) => { 246*90c8c64dSAndroid Build Coastguard Worker const {parser} = fileAndParser; 247*90c8c64dSAndroid Build Coastguard Worker if (this.shouldUseLegacyParser(parser)) { 248*90c8c64dSAndroid Build Coastguard Worker legacyParsersBeingLoaded.set(parser.getTraceType(), parser); 249*90c8c64dSAndroid Build Coastguard Worker this.legacyParsers.push(fileAndParser); 250*90c8c64dSAndroid Build Coastguard Worker } 251*90c8c64dSAndroid Build Coastguard Worker }); 252*90c8c64dSAndroid Build Coastguard Worker } 253*90c8c64dSAndroid Build Coastguard Worker 254*90c8c64dSAndroid Build Coastguard Worker private addPerfettoParsers({file, parsers}: FileAndParsers) { 255*90c8c64dSAndroid Build Coastguard Worker // We currently run only one Perfetto TP WebWorker at a time, so Perfetto parsers previously 256*90c8c64dSAndroid Build Coastguard Worker // loaded are now invalid and must be removed (previous WebWorker is not running anymore). 257*90c8c64dSAndroid Build Coastguard Worker this.perfettoParsers = []; 258*90c8c64dSAndroid Build Coastguard Worker 259*90c8c64dSAndroid Build Coastguard Worker parsers.forEach((parser) => { 260*90c8c64dSAndroid Build Coastguard Worker this.perfettoParsers.push(new FileAndParser(file, parser)); 261*90c8c64dSAndroid Build Coastguard Worker 262*90c8c64dSAndroid Build Coastguard Worker // While transitioning to the Perfetto format, devices might still have old legacy trace files 263*90c8c64dSAndroid Build Coastguard Worker // dangling in the disk that get automatically included into bugreports. Hence, Perfetto 264*90c8c64dSAndroid Build Coastguard Worker // parsers must always override legacy ones so that dangling legacy files are ignored. 265*90c8c64dSAndroid Build Coastguard Worker this.legacyParsers = this.legacyParsers.filter((fileAndParser) => { 266*90c8c64dSAndroid Build Coastguard Worker const isOverriddenByPerfettoParser = 267*90c8c64dSAndroid Build Coastguard Worker fileAndParser.parser.getTraceType() === parser.getTraceType(); 268*90c8c64dSAndroid Build Coastguard Worker if (isOverriddenByPerfettoParser) { 269*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add( 270*90c8c64dSAndroid Build Coastguard Worker new TraceOverridden(fileAndParser.parser.getDescriptors().join()), 271*90c8c64dSAndroid Build Coastguard Worker ); 272*90c8c64dSAndroid Build Coastguard Worker } 273*90c8c64dSAndroid Build Coastguard Worker return !isOverriddenByPerfettoParser; 274*90c8c64dSAndroid Build Coastguard Worker }); 275*90c8c64dSAndroid Build Coastguard Worker }); 276*90c8c64dSAndroid Build Coastguard Worker } 277*90c8c64dSAndroid Build Coastguard Worker 278*90c8c64dSAndroid Build Coastguard Worker private shouldUseLegacyParser(newParser: Parser<object>): boolean { 279*90c8c64dSAndroid Build Coastguard Worker // While transitioning to the Perfetto format, devices might still have old legacy trace files 280*90c8c64dSAndroid Build Coastguard Worker // dangling in the disk that get automatically included into bugreports. Hence, Perfetto parsers 281*90c8c64dSAndroid Build Coastguard Worker // must always override legacy ones so that dangling legacy files are ignored. 282*90c8c64dSAndroid Build Coastguard Worker const isOverriddenByPerfettoParser = this.perfettoParsers.some( 283*90c8c64dSAndroid Build Coastguard Worker (fileAndParser) => 284*90c8c64dSAndroid Build Coastguard Worker fileAndParser.parser.getTraceType() === newParser.getTraceType(), 285*90c8c64dSAndroid Build Coastguard Worker ); 286*90c8c64dSAndroid Build Coastguard Worker if (isOverriddenByPerfettoParser) { 287*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new TraceOverridden(newParser.getDescriptors().join())); 288*90c8c64dSAndroid Build Coastguard Worker return false; 289*90c8c64dSAndroid Build Coastguard Worker } 290*90c8c64dSAndroid Build Coastguard Worker 291*90c8c64dSAndroid Build Coastguard Worker return true; 292*90c8c64dSAndroid Build Coastguard Worker } 293*90c8c64dSAndroid Build Coastguard Worker 294*90c8c64dSAndroid Build Coastguard Worker private filterOutLegacyParsersWithOldData( 295*90c8c64dSAndroid Build Coastguard Worker newLegacyParsers: FileAndParser[], 296*90c8c64dSAndroid Build Coastguard Worker ): FileAndParser[] { 297*90c8c64dSAndroid Build Coastguard Worker let allParsers = [ 298*90c8c64dSAndroid Build Coastguard Worker ...newLegacyParsers, 299*90c8c64dSAndroid Build Coastguard Worker ...this.legacyParsers.values(), 300*90c8c64dSAndroid Build Coastguard Worker ...this.perfettoParsers.values(), 301*90c8c64dSAndroid Build Coastguard Worker ]; 302*90c8c64dSAndroid Build Coastguard Worker 303*90c8c64dSAndroid Build Coastguard Worker const latestMonotonicOffset = this.getLatestRealToMonotonicOffset( 304*90c8c64dSAndroid Build Coastguard Worker allParsers.map(({parser, file}) => parser), 305*90c8c64dSAndroid Build Coastguard Worker ); 306*90c8c64dSAndroid Build Coastguard Worker const latestBootTimeOffset = this.getLatestRealToBootTimeOffset( 307*90c8c64dSAndroid Build Coastguard Worker allParsers.map(({parser, file}) => parser), 308*90c8c64dSAndroid Build Coastguard Worker ); 309*90c8c64dSAndroid Build Coastguard Worker 310*90c8c64dSAndroid Build Coastguard Worker newLegacyParsers = newLegacyParsers.filter(({parser, file}) => { 311*90c8c64dSAndroid Build Coastguard Worker const monotonicOffset = parser.getRealToMonotonicTimeOffsetNs(); 312*90c8c64dSAndroid Build Coastguard Worker if (monotonicOffset && latestMonotonicOffset) { 313*90c8c64dSAndroid Build Coastguard Worker const isOldData = 314*90c8c64dSAndroid Build Coastguard Worker Math.abs(Number(monotonicOffset - latestMonotonicOffset)) > 315*90c8c64dSAndroid Build Coastguard Worker LoadedParsers.MAX_ALLOWED_TIME_GAP_BETWEEN_RTE_OFFSET; 316*90c8c64dSAndroid Build Coastguard Worker if (isOldData) { 317*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new TraceHasOldData(file.getDescriptor())); 318*90c8c64dSAndroid Build Coastguard Worker return false; 319*90c8c64dSAndroid Build Coastguard Worker } 320*90c8c64dSAndroid Build Coastguard Worker } 321*90c8c64dSAndroid Build Coastguard Worker 322*90c8c64dSAndroid Build Coastguard Worker const bootTimeOffset = parser.getRealToBootTimeOffsetNs(); 323*90c8c64dSAndroid Build Coastguard Worker if (bootTimeOffset && latestBootTimeOffset) { 324*90c8c64dSAndroid Build Coastguard Worker const isOldData = 325*90c8c64dSAndroid Build Coastguard Worker Math.abs(Number(bootTimeOffset - latestBootTimeOffset)) > 326*90c8c64dSAndroid Build Coastguard Worker LoadedParsers.MAX_ALLOWED_TIME_GAP_BETWEEN_RTE_OFFSET; 327*90c8c64dSAndroid Build Coastguard Worker if (isOldData) { 328*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new TraceHasOldData(file.getDescriptor())); 329*90c8c64dSAndroid Build Coastguard Worker return false; 330*90c8c64dSAndroid Build Coastguard Worker } 331*90c8c64dSAndroid Build Coastguard Worker } 332*90c8c64dSAndroid Build Coastguard Worker 333*90c8c64dSAndroid Build Coastguard Worker return true; 334*90c8c64dSAndroid Build Coastguard Worker }); 335*90c8c64dSAndroid Build Coastguard Worker 336*90c8c64dSAndroid Build Coastguard Worker allParsers = [ 337*90c8c64dSAndroid Build Coastguard Worker ...newLegacyParsers, 338*90c8c64dSAndroid Build Coastguard Worker ...this.legacyParsers.values(), 339*90c8c64dSAndroid Build Coastguard Worker ...this.perfettoParsers.values(), 340*90c8c64dSAndroid Build Coastguard Worker ]; 341*90c8c64dSAndroid Build Coastguard Worker 342*90c8c64dSAndroid Build Coastguard Worker const timeRanges = allParsers 343*90c8c64dSAndroid Build Coastguard Worker .map(({parser}) => { 344*90c8c64dSAndroid Build Coastguard Worker const timestamps = parser.getTimestamps(); 345*90c8c64dSAndroid Build Coastguard Worker if (!timestamps || timestamps.length === 0) { 346*90c8c64dSAndroid Build Coastguard Worker return undefined; 347*90c8c64dSAndroid Build Coastguard Worker } 348*90c8c64dSAndroid Build Coastguard Worker return new TimeRange(timestamps[0], timestamps[timestamps.length - 1]); 349*90c8c64dSAndroid Build Coastguard Worker }) 350*90c8c64dSAndroid Build Coastguard Worker .filter((range) => range !== undefined) as TimeRange[]; 351*90c8c64dSAndroid Build Coastguard Worker 352*90c8c64dSAndroid Build Coastguard Worker const timeGap = this.findLastTimeGapAboveThreshold(timeRanges); 353*90c8c64dSAndroid Build Coastguard Worker if (!timeGap) { 354*90c8c64dSAndroid Build Coastguard Worker return newLegacyParsers; 355*90c8c64dSAndroid Build Coastguard Worker } 356*90c8c64dSAndroid Build Coastguard Worker 357*90c8c64dSAndroid Build Coastguard Worker return newLegacyParsers.filter(({parser, file}) => { 358*90c8c64dSAndroid Build Coastguard Worker // Only Shell Transition data used to set timestamps of merged Transition trace, 359*90c8c64dSAndroid Build Coastguard Worker // so WM Transition data should not be considered by "old data" policy 360*90c8c64dSAndroid Build Coastguard Worker if (parser.getTraceType() === TraceType.WM_TRANSITION) { 361*90c8c64dSAndroid Build Coastguard Worker return true; 362*90c8c64dSAndroid Build Coastguard Worker } 363*90c8c64dSAndroid Build Coastguard Worker 364*90c8c64dSAndroid Build Coastguard Worker let timestamps = parser.getTimestamps(); 365*90c8c64dSAndroid Build Coastguard Worker if (!this.hasValidTimestamps(timestamps)) { 366*90c8c64dSAndroid Build Coastguard Worker return true; 367*90c8c64dSAndroid Build Coastguard Worker } 368*90c8c64dSAndroid Build Coastguard Worker timestamps = assertDefined(timestamps); 369*90c8c64dSAndroid Build Coastguard Worker 370*90c8c64dSAndroid Build Coastguard Worker const endTimestamp = timestamps[timestamps.length - 1]; 371*90c8c64dSAndroid Build Coastguard Worker const isOldData = endTimestamp.getValueNs() <= timeGap.from.getValueNs(); 372*90c8c64dSAndroid Build Coastguard Worker if (isOldData) { 373*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new TraceHasOldData(file.getDescriptor(), timeGap)); 374*90c8c64dSAndroid Build Coastguard Worker return false; 375*90c8c64dSAndroid Build Coastguard Worker } 376*90c8c64dSAndroid Build Coastguard Worker 377*90c8c64dSAndroid Build Coastguard Worker return true; 378*90c8c64dSAndroid Build Coastguard Worker }); 379*90c8c64dSAndroid Build Coastguard Worker } 380*90c8c64dSAndroid Build Coastguard Worker 381*90c8c64dSAndroid Build Coastguard Worker private filterScreenshotParsersIfRequired( 382*90c8c64dSAndroid Build Coastguard Worker newLegacyParsers: FileAndParser[], 383*90c8c64dSAndroid Build Coastguard Worker ): FileAndParser[] { 384*90c8c64dSAndroid Build Coastguard Worker const hasOldScreenRecordingParsers = this.legacyParsers.some( 385*90c8c64dSAndroid Build Coastguard Worker (entry) => entry.parser.getTraceType() === TraceType.SCREEN_RECORDING, 386*90c8c64dSAndroid Build Coastguard Worker ); 387*90c8c64dSAndroid Build Coastguard Worker const hasNewScreenRecordingParsers = newLegacyParsers.some( 388*90c8c64dSAndroid Build Coastguard Worker (entry) => entry.parser.getTraceType() === TraceType.SCREEN_RECORDING, 389*90c8c64dSAndroid Build Coastguard Worker ); 390*90c8c64dSAndroid Build Coastguard Worker const hasScreenRecordingParsers = 391*90c8c64dSAndroid Build Coastguard Worker hasOldScreenRecordingParsers || hasNewScreenRecordingParsers; 392*90c8c64dSAndroid Build Coastguard Worker 393*90c8c64dSAndroid Build Coastguard Worker if (!hasScreenRecordingParsers) { 394*90c8c64dSAndroid Build Coastguard Worker return newLegacyParsers; 395*90c8c64dSAndroid Build Coastguard Worker } 396*90c8c64dSAndroid Build Coastguard Worker 397*90c8c64dSAndroid Build Coastguard Worker const oldScreenshotParsers = this.legacyParsers.filter( 398*90c8c64dSAndroid Build Coastguard Worker (fileAndParser) => 399*90c8c64dSAndroid Build Coastguard Worker fileAndParser.parser.getTraceType() === TraceType.SCREENSHOT, 400*90c8c64dSAndroid Build Coastguard Worker ); 401*90c8c64dSAndroid Build Coastguard Worker const newScreenshotParsers = newLegacyParsers.filter( 402*90c8c64dSAndroid Build Coastguard Worker (fileAndParser) => 403*90c8c64dSAndroid Build Coastguard Worker fileAndParser.parser.getTraceType() === TraceType.SCREENSHOT, 404*90c8c64dSAndroid Build Coastguard Worker ); 405*90c8c64dSAndroid Build Coastguard Worker 406*90c8c64dSAndroid Build Coastguard Worker oldScreenshotParsers.forEach((fileAndParser) => { 407*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add( 408*90c8c64dSAndroid Build Coastguard Worker new TraceOverridden( 409*90c8c64dSAndroid Build Coastguard Worker fileAndParser.parser.getDescriptors().join(), 410*90c8c64dSAndroid Build Coastguard Worker TraceType.SCREEN_RECORDING, 411*90c8c64dSAndroid Build Coastguard Worker ), 412*90c8c64dSAndroid Build Coastguard Worker ); 413*90c8c64dSAndroid Build Coastguard Worker this.remove(fileAndParser.parser); 414*90c8c64dSAndroid Build Coastguard Worker }); 415*90c8c64dSAndroid Build Coastguard Worker 416*90c8c64dSAndroid Build Coastguard Worker newScreenshotParsers.forEach((newScreenshotParser) => { 417*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add( 418*90c8c64dSAndroid Build Coastguard Worker new TraceOverridden( 419*90c8c64dSAndroid Build Coastguard Worker newScreenshotParser.parser.getDescriptors().join(), 420*90c8c64dSAndroid Build Coastguard Worker TraceType.SCREEN_RECORDING, 421*90c8c64dSAndroid Build Coastguard Worker ), 422*90c8c64dSAndroid Build Coastguard Worker ); 423*90c8c64dSAndroid Build Coastguard Worker }); 424*90c8c64dSAndroid Build Coastguard Worker 425*90c8c64dSAndroid Build Coastguard Worker return newLegacyParsers.filter( 426*90c8c64dSAndroid Build Coastguard Worker (fileAndParser) => 427*90c8c64dSAndroid Build Coastguard Worker fileAndParser.parser.getTraceType() !== TraceType.SCREENSHOT, 428*90c8c64dSAndroid Build Coastguard Worker ); 429*90c8c64dSAndroid Build Coastguard Worker } 430*90c8c64dSAndroid Build Coastguard Worker 431*90c8c64dSAndroid Build Coastguard Worker private filterOutParsersWithoutOffsetsIfRequired( 432*90c8c64dSAndroid Build Coastguard Worker newLegacyParsers: FileAndParser[], 433*90c8c64dSAndroid Build Coastguard Worker perfettoParsers: FileAndParsers | undefined, 434*90c8c64dSAndroid Build Coastguard Worker ): FileAndParser[] { 435*90c8c64dSAndroid Build Coastguard Worker const hasParserWithOffset = 436*90c8c64dSAndroid Build Coastguard Worker perfettoParsers || 437*90c8c64dSAndroid Build Coastguard Worker newLegacyParsers.find(({parser, file}) => { 438*90c8c64dSAndroid Build Coastguard Worker return ( 439*90c8c64dSAndroid Build Coastguard Worker parser.getRealToBootTimeOffsetNs() !== undefined || 440*90c8c64dSAndroid Build Coastguard Worker parser.getRealToMonotonicTimeOffsetNs() !== undefined 441*90c8c64dSAndroid Build Coastguard Worker ); 442*90c8c64dSAndroid Build Coastguard Worker }); 443*90c8c64dSAndroid Build Coastguard Worker const hasParserWithoutOffset = newLegacyParsers.find(({parser, file}) => { 444*90c8c64dSAndroid Build Coastguard Worker const timestamps = parser.getTimestamps(); 445*90c8c64dSAndroid Build Coastguard Worker return ( 446*90c8c64dSAndroid Build Coastguard Worker this.hasValidTimestamps(timestamps) && 447*90c8c64dSAndroid Build Coastguard Worker parser.getRealToBootTimeOffsetNs() === undefined && 448*90c8c64dSAndroid Build Coastguard Worker parser.getRealToMonotonicTimeOffsetNs() === undefined 449*90c8c64dSAndroid Build Coastguard Worker ); 450*90c8c64dSAndroid Build Coastguard Worker }); 451*90c8c64dSAndroid Build Coastguard Worker 452*90c8c64dSAndroid Build Coastguard Worker if (hasParserWithOffset && hasParserWithoutOffset) { 453*90c8c64dSAndroid Build Coastguard Worker return newLegacyParsers.filter(({parser, file}) => { 454*90c8c64dSAndroid Build Coastguard Worker if ( 455*90c8c64dSAndroid Build Coastguard Worker LoadedParsers.REAL_TIME_TRACES_WITHOUT_RTE_OFFSET.some( 456*90c8c64dSAndroid Build Coastguard Worker (traceType) => parser.getTraceType() === traceType, 457*90c8c64dSAndroid Build Coastguard Worker ) 458*90c8c64dSAndroid Build Coastguard Worker ) { 459*90c8c64dSAndroid Build Coastguard Worker return true; 460*90c8c64dSAndroid Build Coastguard Worker } 461*90c8c64dSAndroid Build Coastguard Worker const hasOffset = 462*90c8c64dSAndroid Build Coastguard Worker parser.getRealToMonotonicTimeOffsetNs() !== undefined || 463*90c8c64dSAndroid Build Coastguard Worker parser.getRealToBootTimeOffsetNs() !== undefined; 464*90c8c64dSAndroid Build Coastguard Worker if (!hasOffset) { 465*90c8c64dSAndroid Build Coastguard Worker UserNotifier.add(new TraceHasOldData(parser.getDescriptors().join())); 466*90c8c64dSAndroid Build Coastguard Worker } 467*90c8c64dSAndroid Build Coastguard Worker return hasOffset; 468*90c8c64dSAndroid Build Coastguard Worker }); 469*90c8c64dSAndroid Build Coastguard Worker } 470*90c8c64dSAndroid Build Coastguard Worker 471*90c8c64dSAndroid Build Coastguard Worker return newLegacyParsers; 472*90c8c64dSAndroid Build Coastguard Worker } 473*90c8c64dSAndroid Build Coastguard Worker 474*90c8c64dSAndroid Build Coastguard Worker private findLastTimeGapAboveThreshold( 475*90c8c64dSAndroid Build Coastguard Worker ranges: readonly TimeRange[], 476*90c8c64dSAndroid Build Coastguard Worker ): TimeRange | undefined { 477*90c8c64dSAndroid Build Coastguard Worker const rangesSortedByEnd = ranges 478*90c8c64dSAndroid Build Coastguard Worker .slice() 479*90c8c64dSAndroid Build Coastguard Worker .sort((a, b) => (a.to.getValueNs() < b.to.getValueNs() ? -1 : +1)); 480*90c8c64dSAndroid Build Coastguard Worker 481*90c8c64dSAndroid Build Coastguard Worker for (let i = rangesSortedByEnd.length - 2; i >= 0; --i) { 482*90c8c64dSAndroid Build Coastguard Worker const curr = rangesSortedByEnd[i]; 483*90c8c64dSAndroid Build Coastguard Worker const next = rangesSortedByEnd[i + 1]; 484*90c8c64dSAndroid Build Coastguard Worker const gap = next.from.getValueNs() - curr.to.getValueNs(); 485*90c8c64dSAndroid Build Coastguard Worker if (gap > LoadedParsers.MAX_ALLOWED_TIME_GAP_BETWEEN_TRACES_NS) { 486*90c8c64dSAndroid Build Coastguard Worker return new TimeRange(curr.to, next.from); 487*90c8c64dSAndroid Build Coastguard Worker } 488*90c8c64dSAndroid Build Coastguard Worker } 489*90c8c64dSAndroid Build Coastguard Worker 490*90c8c64dSAndroid Build Coastguard Worker return undefined; 491*90c8c64dSAndroid Build Coastguard Worker } 492*90c8c64dSAndroid Build Coastguard Worker 493*90c8c64dSAndroid Build Coastguard Worker private hasValidTimestamps(timestamps: Timestamp[] | undefined): boolean { 494*90c8c64dSAndroid Build Coastguard Worker if (!timestamps || timestamps.length === 0) { 495*90c8c64dSAndroid Build Coastguard Worker return false; 496*90c8c64dSAndroid Build Coastguard Worker } 497*90c8c64dSAndroid Build Coastguard Worker 498*90c8c64dSAndroid Build Coastguard Worker const isDump = 499*90c8c64dSAndroid Build Coastguard Worker timestamps.length === 1 && timestamps[0].getValueNs() === INVALID_TIME_NS; 500*90c8c64dSAndroid Build Coastguard Worker if (isDump) { 501*90c8c64dSAndroid Build Coastguard Worker return false; 502*90c8c64dSAndroid Build Coastguard Worker } 503*90c8c64dSAndroid Build Coastguard Worker return true; 504*90c8c64dSAndroid Build Coastguard Worker } 505*90c8c64dSAndroid Build Coastguard Worker} 506