1// Copyright (C) 2024 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 {assertExists, assertFalse} from '../../../base/logging'; 16import {getOrCreate} from '../../../base/utils'; 17import protos from '../../../protos'; 18 19export const FTRACE_DS = 'linux.ftrace'; 20export type RecordMode = 'STOP_WHEN_FULL' | 'RING_BUFFER' | 'LONG_TRACE'; 21export type BufferMode = 'DISCARD' | 'RING_BUFFER'; 22 23export const DEFAULT_BUFFER_ID = 'default'; 24 25export class TraceConfigBuilder { 26 readonly buffers = new Map<string, BufferConfig>(); 27 readonly dataSources = new Map<string, DataSource>(); 28 29 // The default values here don't matter, they exist only to make the TS 30 // compiler happy. The actual defaults are defined by serialization_schema.ts. 31 mode: RecordMode = 'STOP_WHEN_FULL'; 32 durationMs = 10_000; 33 maxFileSizeMb = 0; 34 fileWritePeriodMs = 0; 35 compression = false; 36 37 constructor() { 38 this.buffers.set(DEFAULT_BUFFER_ID, {sizeKb: 64 * 1024}); 39 } 40 41 get defaultBuffer(): BufferConfig { 42 return assertExists(this.buffers.get(DEFAULT_BUFFER_ID)); 43 } 44 45 // It has get-or-create semantics. 46 addDataSource(name: string, targetBufId?: string): protos.IDataSourceConfig { 47 return getOrCreate(this.dataSources, name, () => ({ 48 targetBufId, 49 config: {name}, 50 })).config; 51 } 52 53 addBuffer(id: string, sizeKb: number, mode?: BufferMode) { 54 assertFalse(this.buffers.has(id)); 55 this.buffers.set(id, {sizeKb, mode}); 56 } 57 58 addFtraceEvents(...ftraceEvents: string[]) { 59 const cfg = this.addDataSource('linux.ftrace'); 60 cfg.ftraceConfig ??= {}; 61 cfg.ftraceConfig.ftraceEvents ??= []; 62 cfg.ftraceConfig.ftraceEvents.push(...ftraceEvents); 63 } 64 65 addAtraceApps(...apps: string[]) { 66 const cfg = this.addDataSource('linux.ftrace'); 67 cfg.ftraceConfig ??= {}; 68 cfg.ftraceConfig.atraceApps ??= []; 69 cfg.ftraceConfig.atraceApps.push(...apps); 70 } 71 72 addAtraceCategories(...cats: string[]) { 73 const cfg = this.addDataSource('linux.ftrace'); 74 cfg.ftraceConfig ??= {}; 75 cfg.ftraceConfig.atraceCategories ??= []; 76 cfg.ftraceConfig.atraceCategories.push(...cats); 77 } 78 79 toTraceConfig(): protos.TraceConfig { 80 const traceCfg = new protos.TraceConfig(); 81 traceCfg.durationMs = this.durationMs; 82 if (this.mode === 'LONG_TRACE') { 83 traceCfg.writeIntoFile = true; 84 traceCfg.fileWritePeriodMs = this.fileWritePeriodMs; 85 traceCfg.maxFileSizeBytes = this.maxFileSizeMb * 1_000_000; 86 } 87 88 if (this.compression) { 89 traceCfg.compressionType = 90 protos.TraceConfig.CompressionType.COMPRESSION_TYPE_DEFLATE; 91 } 92 93 const orderedBufIds = []; 94 for (const [id, buf] of this.buffers.entries()) { 95 const fillPolicy = 96 buf.mode === 'DISCARD' || 97 (buf.mode === undefined && this.mode === 'STOP_WHEN_FULL') 98 ? protos.TraceConfig.BufferConfig.FillPolicy.DISCARD 99 : protos.TraceConfig.BufferConfig.FillPolicy.RING_BUFFER; 100 traceCfg.buffers.push({sizeKb: buf.sizeKb, fillPolicy}); 101 orderedBufIds.push(id); 102 } 103 for (const ds of this.dataSources.values()) { 104 let targetBuffer: number | undefined = undefined; 105 if (ds.targetBufId !== undefined) { 106 targetBuffer = orderedBufIds.indexOf(ds.targetBufId); 107 if (targetBuffer < 0) { 108 throw new Error( 109 `DataSource ${ds.config.name} specified buffer id ` + 110 `${ds.targetBufId} but it doesn't exist. ` + 111 `Buffers: [${orderedBufIds.join(',')}]`, 112 ); 113 } 114 } 115 traceCfg.dataSources.push({config: {...ds.config, targetBuffer}}); 116 } 117 return traceCfg; 118 } 119} 120 121export interface DataSource { 122 config: protos.IDataSourceConfig; 123 targetBufId?: string; 124} 125 126export interface BufferConfig { 127 sizeKb: number; 128 // If omitted infers from the config-wide mode. 129 mode?: BufferMode; 130} 131