1// Copyright (C) 2018 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 {dingus} from 'dingusjs';
16
17import {assertExists} from '../base/logging';
18import {TraceConfig} from '../common/protos';
19import {createEmptyRecordConfig, RecordConfig} from '../common/state';
20
21import {App} from './globals';
22import {genConfigProto, RecordController, toPbtxt} from './record_controller';
23
24test('encodeConfig', () => {
25  const config = createEmptyRecordConfig();
26  config.durationSeconds = 10;
27  const result =
28      TraceConfig.decode(genConfigProto(config, {os: 'Q', name: 'Android Q'}));
29  expect(result.durationMs).toBe(10000);
30});
31
32test('SysConfig', () => {
33  const config = createEmptyRecordConfig();
34  config.cpuSyscall = true;
35  const result =
36      TraceConfig.decode(genConfigProto(config, {os: 'Q', name: 'Android Q'}));
37  const sources = assertExists(result.dataSources);
38  const srcConfig = assertExists(sources[0].config);
39  const ftraceConfig = assertExists(srcConfig.ftraceConfig);
40  const ftraceEvents = assertExists(ftraceConfig.ftraceEvents);
41  expect(ftraceEvents.includes('raw_syscalls/sys_enter')).toBe(true);
42  expect(ftraceEvents.includes('raw_syscalls/sys_exit')).toBe(true);
43});
44
45test('toPbtxt', () => {
46  const config = {
47    durationMs: 1000,
48    maxFileSizeBytes: 43,
49    buffers: [
50      {
51        sizeKb: 42,
52      },
53    ],
54    dataSources: [{
55      config: {
56        name: 'linux.ftrace',
57        targetBuffer: 1,
58        ftraceConfig: {
59          ftraceEvents: ['sched_switch', 'print'],
60        },
61      },
62    }],
63    producers: [
64      {
65        producerName: 'perfetto.traced_probes',
66      },
67    ],
68  };
69
70  const text = toPbtxt(TraceConfig.encode(config).finish());
71
72  expect(text).toEqual(`buffers: {
73    size_kb: 42
74}
75data_sources: {
76    config {
77        name: "linux.ftrace"
78        target_buffer: 1
79        ftrace_config {
80            ftrace_events: "sched_switch"
81            ftrace_events: "print"
82        }
83    }
84}
85duration_ms: 1000
86producers: {
87    producer_name: "perfetto.traced_probes"
88}
89max_file_size_bytes: 43
90`);
91});
92
93test('RecordController', () => {
94  const app = dingus<App>('globals');
95  (app.state.recordConfig as RecordConfig) = createEmptyRecordConfig();
96  const m: MessageChannel = dingus<MessageChannel>('extensionPort');
97  const controller = new RecordController({app, extensionPort: m.port1});
98  controller.run();
99  controller.run();
100  controller.run();
101  // tslint:disable-next-line no-any
102  const calls = app.calls.filter((call: any) => call[0] === 'publish()');
103  expect(calls.length).toBe(1);
104  // TODO(hjd): Fix up dingus to have a more sensible API.
105  expect(calls[0][1][0]).toEqual('TrackData');
106});
107
108test('ChromeConfig', () => {
109  const config = createEmptyRecordConfig();
110  config.ipcFlows = true;
111  config.jsExecution = true;
112  config.mode = 'STOP_WHEN_FULL';
113  const result =
114      TraceConfig.decode(genConfigProto(config, {os: 'C', name: 'Chrome'}));
115  const sources = assertExists(result.dataSources);
116
117  const traceConfigSource = assertExists(sources[0].config);
118  expect(traceConfigSource.name).toBe('org.chromium.trace_event');
119  const chromeConfig = assertExists(traceConfigSource.chromeConfig);
120  const traceConfig = assertExists(chromeConfig.traceConfig);
121
122  const metadataConfigSource = assertExists(sources[1].config);
123  expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
124  const chromeConfigM = assertExists(metadataConfigSource.chromeConfig);
125  const traceConfigM = assertExists(chromeConfigM.traceConfig);
126
127  const expectedTraceConfig = '{"record_mode":"record-until-full",' +
128      '"included_categories":' +
129      '["toplevel","disabled-by-default-ipc.flow","mojom","v8"],' +
130      '"memory_dump_config":{}}';
131  expect(traceConfigM).toEqual(expectedTraceConfig);
132  expect(traceConfig).toEqual(expectedTraceConfig);
133});
134
135test('ChromeMemoryConfig', () => {
136  const config = createEmptyRecordConfig();
137  config.chromeCategoriesSelected = ['disabled-by-default-memory-infra'];
138  const result =
139      TraceConfig.decode(genConfigProto(config, {os: 'C', name: 'Chrome'}));
140  const sources = assertExists(result.dataSources);
141
142  const traceConfigSource = assertExists(sources[0].config);
143  expect(traceConfigSource.name).toBe('org.chromium.trace_event');
144  const chromeConfig = assertExists(traceConfigSource.chromeConfig);
145  const traceConfig = assertExists(chromeConfig.traceConfig);
146
147  const metadataConfigSource = assertExists(sources[1].config);
148  expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
149  const chromeConfigM = assertExists(metadataConfigSource.chromeConfig);
150  const traceConfigM = assertExists(chromeConfigM.traceConfig);
151
152  const expectedTraceConfig = '{"record_mode":"record-until-full",' +
153      '"included_categories":["disabled-by-default-memory-infra"],' +
154      '"memory_dump_config":{"triggers":' +
155      '[{"mode":"detailed","periodic_interval_ms":10000}]}}';
156  expect(traceConfigM).toEqual(expectedTraceConfig);
157  expect(traceConfig).toEqual(expectedTraceConfig);
158});
159
160test('ChromeConfigRingBuffer', () => {
161  const config = createEmptyRecordConfig();
162  config.ipcFlows = true;
163  config.jsExecution = true;
164  config.mode = 'RING_BUFFER';
165  const result =
166      TraceConfig.decode(genConfigProto(config, {os: 'C', name: 'Chrome'}));
167  const sources = assertExists(result.dataSources);
168
169  const traceConfigSource = assertExists(sources[0].config);
170  expect(traceConfigSource.name).toBe('org.chromium.trace_event');
171  const chromeConfig = assertExists(traceConfigSource.chromeConfig);
172  const traceConfig = assertExists(chromeConfig.traceConfig);
173
174  const metadataConfigSource = assertExists(sources[1].config);
175  expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
176  const chromeConfigM = assertExists(metadataConfigSource.chromeConfig);
177  const traceConfigM = assertExists(chromeConfigM.traceConfig);
178
179  const expectedTraceConfig = '{"record_mode":"record-continuously",' +
180      '"included_categories":' +
181      '["toplevel","disabled-by-default-ipc.flow","mojom","v8"],' +
182      '"memory_dump_config":{}}';
183  expect(traceConfigM).toEqual(expectedTraceConfig);
184  expect(traceConfig).toEqual(expectedTraceConfig);
185});
186
187
188test('ChromeConfigLongTrace', () => {
189  const config = createEmptyRecordConfig();
190  config.ipcFlows = true;
191  config.jsExecution = true;
192  config.mode = 'RING_BUFFER';
193  const result =
194      TraceConfig.decode(genConfigProto(config, {os: 'C', name: 'Chrome'}));
195  const sources = assertExists(result.dataSources);
196
197  const traceConfigSource = assertExists(sources[0].config);
198  expect(traceConfigSource.name).toBe('org.chromium.trace_event');
199  const chromeConfig = assertExists(traceConfigSource.chromeConfig);
200  const traceConfig = assertExists(chromeConfig.traceConfig);
201
202  const metadataConfigSource = assertExists(sources[1].config);
203  expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
204  const chromeConfigM = assertExists(metadataConfigSource.chromeConfig);
205  const traceConfigM = assertExists(chromeConfigM.traceConfig);
206
207  const expectedTraceConfig = '{"record_mode":"record-continuously",' +
208      '"included_categories":' +
209      '["toplevel","disabled-by-default-ipc.flow","mojom","v8"],' +
210      '"memory_dump_config":{}}';
211  expect(traceConfigM).toEqual(expectedTraceConfig);
212  expect(traceConfig).toEqual(expectedTraceConfig);
213});
214
215test('ChromeConfigToPbtxt', () => {
216  const config = {
217    dataSources: [{
218      config: {
219        name: 'org.chromium.trace_event',
220        chromeConfig:
221            {traceConfig: JSON.stringify({included_categories: ['v8']})},
222      },
223    }],
224  };
225  const text = toPbtxt(TraceConfig.encode(config).finish());
226
227  expect(text).toEqual(`data_sources: {
228    config {
229        name: "org.chromium.trace_event"
230        chrome_config {
231            trace_config: "{\\"included_categories\\":[\\"v8\\"]}"
232        }
233    }
234}
235`);
236});
237