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