Skip to content

Commit 27cdef7

Browse files
committed
FI
1 parent 51c6d65 commit 27cdef7

File tree

9 files changed

+273
-80
lines changed

9 files changed

+273
-80
lines changed

ui/src/plugins/dev.perfetto.RecordTraceV2/config/config_interfaces.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
import m from 'mithril';
1616
import {TargetPlatformId} from '../interfaces/target_platform';
1717
import {TraceConfigBuilder} from './trace_config_builder';
18-
import {RecordPluginSchema, RecordSessionSchema} from '../serialization_schema';
18+
import {
19+
ProbesSessionSchema,
20+
RecordPluginSchema,
21+
} from '../serialization_schema';
1922

2023
/**
2124
* A sub-page of the Record page.
@@ -59,8 +62,8 @@ export type RecordSubpage = {
5962

6063
// Save-restore the page state into the JSON object that is saved in
6164
// localstorage and shared when sharing a config.
62-
serialize(state: RecordSessionSchema): void;
63-
deserialize(state: RecordSessionSchema): void;
65+
serialize(state: ProbesSessionSchema): void;
66+
deserialize(state: ProbesSessionSchema): void;
6467
}
6568
| {
6669
kind: 'GLOBAL_PAGE';

ui/src/plugins/dev.perfetto.RecordTraceV2/pages/buffer_config_page.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {Slider} from './widgets/slider';
1919
import {RecordMode, TraceConfigBuilder} from '../config/trace_config_builder';
2020
import {ConfigManager} from '../config/config_manager';
2121
import {RecordSubpage} from '../config/config_interfaces';
22-
import {RecordSessionSchema} from '../serialization_schema';
22+
import {ProbesSessionSchema} from '../serialization_schema';
2323
import {Toggle} from './widgets/toggle';
2424

2525
type RecMgrAttrs = {recMgr: RecordingManager};
@@ -34,7 +34,7 @@ export function bufferConfigPage(recMgr: RecordingManager): RecordSubpage {
3434
render() {
3535
return m(BufferConfigPage, {recMgr});
3636
},
37-
serialize(state: RecordSessionSchema) {
37+
serialize(state: ProbesSessionSchema) {
3838
const tc: TraceConfigBuilder = recMgr.recordConfig.traceConfig;
3939
state.mode = tc.mode;
4040
state.bufSizeKb = tc.defaultBuffer.sizeKb;
@@ -43,7 +43,7 @@ export function bufferConfigPage(recMgr: RecordingManager): RecordSubpage {
4343
state.fileWritePeriodMs = tc.fileWritePeriodMs;
4444
state.compression = tc.compression;
4545
},
46-
async deserialize(state: RecordSessionSchema) {
46+
async deserialize(state: ProbesSessionSchema) {
4747
const tc: TraceConfigBuilder = recMgr.recordConfig.traceConfig;
4848
tc.mode = state.mode;
4949
tc.defaultBuffer.sizeKb = state.bufSizeKb;

ui/src/plugins/dev.perfetto.RecordTraceV2/pages/record_page.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,20 @@ export class RecordPageV2 implements m.ClassComponent<RecordPageAttrs> {
128128
private renderSubpage(page: RecordSubpage): m.Children {
129129
switch (page.kind) {
130130
case 'PROBES_PAGE':
131-
return page.probes
132-
.filter((p) => supportsPlatform(p, this.recMgr.currentPlatform))
133-
.map((probe) => m(Probe, {cfgMgr: this.recMgr.recordConfig, probe}));
131+
return [
132+
this.recMgr.hasCustomTraceConfig &&
133+
m(
134+
Callout,
135+
{intent: Intent.Primary, icon: 'upload_file'},
136+
'Using imported custom config. Changes to probe settings ' +
137+
'below will not take effect.',
138+
),
139+
...page.probes
140+
.filter((p) => supportsPlatform(p, this.recMgr.currentPlatform))
141+
.map((probe) =>
142+
m(Probe, {cfgMgr: this.recMgr.recordConfig, probe}),
143+
),
144+
];
134145
case 'GLOBAL_PAGE':
135146
case 'SESSION_PAGE':
136147
return page.render();
@@ -201,8 +212,19 @@ export class RecordPageV2 implements m.ClassComponent<RecordPageAttrs> {
201212
},
202213
}),
203214
),
215+
this.recMgr.hasCustomTraceConfig &&
216+
m(
217+
'.pf-custom-config-notice',
218+
m(Icon, {icon: 'upload_file'}),
219+
m('span', 'Imported config overrides probe settings'),
220+
),
204221
m(
205222
'ul',
223+
{
224+
className: this.recMgr.hasCustomTraceConfig
225+
? 'pf-probes-disabled'
226+
: '',
227+
},
206228
this.getSortedProbes(Array.from(pages.values())).map((rc) =>
207229
this.renderMenuEntry(rc),
208230
),
@@ -252,7 +274,7 @@ export class RecordPageV2 implements m.ClassComponent<RecordPageAttrs> {
252274
showModal({title: 'Restore error', content: res.error});
253275
return;
254276
}
255-
this.recMgr.app.navigate('#!/record/cmdline');
277+
this.recMgr.app.navigate('#!/record');
256278
}
257279
}
258280

ui/src/plugins/dev.perfetto.RecordTraceV2/pages/target_selection_page.ts

Lines changed: 94 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@ import {DisposableStack} from '../../../base/disposable_stack';
2727
import {CurrentTracingSession, RecordingManager} from '../recording_manager';
2828
import {download} from '../../../base/download_utils';
2929
import {RecordSubpage} from '../config/config_interfaces';
30-
import {RecordPluginSchema} from '../serialization_schema';
30+
import {RecordPluginSchema, SavedSessionSchema} from '../serialization_schema';
3131
import {Checkbox} from '../../../widgets/checkbox';
3232
import {linkify} from '../../../widgets/anchor';
3333
import {getPresetsForPlatform} from '../presets';
3434
import {Icons} from '../../../base/semantic_icons';
3535
import {shareRecordConfig} from '../config/config_sharing';
3636
import {Card} from '../../../widgets/card';
37+
import {showModal} from '../../../widgets/modal';
38+
import {traceConfigToPb} from '../../../base/proto_utils_wasm';
39+
import protos from '../../../protos';
3740

3841
type RecMgrAttrs = {recMgr: RecordingManager};
3942

@@ -68,7 +71,7 @@ export function targetSelectionPage(recMgr: RecordingManager): RecordSubpage {
6871
// Restore config
6972
const hasSavedProbes =
7073
state.lastSession !== undefined &&
71-
state.lastSession.probes !== undefined &&
74+
state.lastSession.kind === 'probes' &&
7275
Object.keys(state.lastSession.probes).length > 0;
7376

7477
if (state.selectedConfigId || hasSavedProbes) {
@@ -185,6 +188,7 @@ class RecordConfigSelector implements m.ClassComponent<RecMgrAttrs> {
185188
const isEmptySelected =
186189
recMgr.selectedConfigId === undefined &&
187190
recMgr.isConfigModified === false &&
191+
!recMgr.hasCustomTraceConfig &&
188192
!recMgr.recordConfig.hasActiveProbes();
189193

190194
return [
@@ -225,62 +229,63 @@ class RecordConfigSelector implements m.ClassComponent<RecMgrAttrs> {
225229

226230
private renderSavedConfigsSection(recMgr: RecordingManager) {
227231
const hasActiveProbes = recMgr.recordConfig.hasActiveProbes();
232+
const hasUnsavedCustomConfig =
233+
recMgr.hasCustomTraceConfig && recMgr.selectedConfigId === undefined;
228234
const shouldHighlightSave =
235+
hasUnsavedCustomConfig ||
229236
(hasActiveProbes && recMgr.selectedConfigId === undefined) ||
230237
recMgr.isConfigModified === true;
231-
const hasSavedConfigs = recMgr.savedConfigs.length > 0;
232-
const showSection = hasSavedConfigs || shouldHighlightSave;
233-
if (!showSection) {
234-
return null;
235-
}
236238
return [
237239
m('h3', 'User configs'),
238240
m('.pf-config-selector__grid', [
239241
// Saved configs
240-
...recMgr.savedConfigs.map((config) => {
242+
...recMgr.savedConfigs.map((saved) => {
241243
const isSelected =
242-
recMgr.selectedConfigId === `saved:${config.name}` &&
244+
recMgr.selectedConfigId === `saved:${saved.name}` &&
243245
recMgr.isConfigModified === false;
246+
const config = saved.config;
247+
const isCustom = config.kind === 'custom';
244248
return m(
245249
Card,
246250
{
247251
className:
248252
'pf-preset-card' +
249253
(isSelected ? ' pf-preset-card--selected' : ''),
250-
onclick: () =>
251-
recMgr.loadConfig({
252-
config: config.config,
253-
configId: `saved:${config.name}`,
254-
configName: config.name,
255-
}),
254+
onclick: () => this.loadSavedConfig(recMgr, saved),
256255
tabindex: 0,
257256
},
258-
m(Icon, {icon: 'bookmark'}),
259-
m('.pf-preset-card__title', config.name),
257+
m(Icon, {icon: isCustom ? 'description' : 'bookmark'}),
258+
m('.pf-preset-card__title', saved.name),
259+
isCustom &&
260+
m(
261+
'.pf-preset-card__subtitle',
262+
`Imported from ${config.customConfigFileName ?? 'textproto'}`,
263+
),
260264
m('.pf-preset-card__actions', [
261-
m(Button, {
262-
icon: 'save',
263-
compact: true,
264-
title: 'Overwrite with current settings',
265-
onclick: (e: Event) => {
266-
e.stopPropagation();
267-
if (
268-
confirm(
269-
`Overwrite config "${config.name}" with current settings?`,
270-
)
271-
) {
272-
recMgr.saveConfig(config.name, recMgr.serializeSession());
273-
recMgr.app.raf.scheduleFullRedraw();
274-
}
275-
},
276-
}),
265+
!isCustom &&
266+
m(Button, {
267+
icon: 'save',
268+
compact: true,
269+
title: 'Overwrite with current settings',
270+
onclick: (e: Event) => {
271+
e.stopPropagation();
272+
if (
273+
confirm(
274+
`Overwrite config "${saved.name}" with current settings?`,
275+
)
276+
) {
277+
recMgr.saveConfig(saved.name);
278+
recMgr.app.raf.scheduleFullRedraw();
279+
}
280+
},
281+
}),
277282
m(Button, {
278283
icon: 'share',
279284
compact: true,
280285
title: 'Share configuration',
281286
onclick: (e: Event) => {
282287
e.stopPropagation();
283-
shareRecordConfig(config.config);
288+
shareRecordConfig(saved.config);
284289
},
285290
}),
286291
m(Button, {
@@ -289,8 +294,8 @@ class RecordConfigSelector implements m.ClassComponent<RecMgrAttrs> {
289294
title: 'Delete configuration',
290295
onclick: (e: Event) => {
291296
e.stopPropagation();
292-
if (confirm(`Delete "${config.name}"?`)) {
293-
recMgr.deleteConfig(config.name);
297+
if (confirm(`Delete "${saved.name}"?`)) {
298+
recMgr.deleteConfig(saved.name);
294299
recMgr.app.raf.scheduleFullRedraw();
295300
}
296301
},
@@ -315,26 +320,70 @@ class RecordConfigSelector implements m.ClassComponent<RecMgrAttrs> {
315320
);
316321
return;
317322
}
318-
const savedConfig = recMgr.serializeSession();
319-
recMgr.saveConfig(trimmedName, savedConfig);
320-
recMgr.loadConfig({
321-
config: savedConfig,
322-
configId: `saved:${trimmedName}`,
323-
configName: trimmedName,
324-
});
323+
const saved = recMgr.saveConfig(trimmedName);
324+
this.loadSavedConfig(recMgr, saved);
325325
recMgr.app.raf.scheduleFullRedraw();
326326
}
327327
},
328328
tabindex: 0,
329329
},
330-
m(Icon, {icon: 'tune'}),
331-
m('.pf-preset-card__title', 'Custom'),
330+
m(Icon, {icon: hasUnsavedCustomConfig ? 'description' : 'tune'}),
331+
m(
332+
'.pf-preset-card__title',
333+
hasUnsavedCustomConfig
334+
? recMgr.customConfigFileName ?? 'Imported config'
335+
: 'Custom',
336+
),
332337
m('.pf-preset-card__subtitle', 'Click to save'),
333338
),
339+
this.renderImportCard(recMgr),
334340
]),
335341
];
336342
}
337343

344+
private loadSavedConfig(recMgr: RecordingManager, saved: SavedSessionSchema) {
345+
recMgr.loadConfig({
346+
config: saved.config,
347+
configId: `saved:${saved.name}`,
348+
configName: saved.name,
349+
});
350+
}
351+
352+
private renderImportCard(recMgr: RecordingManager) {
353+
return m(
354+
Card,
355+
{
356+
className: 'pf-preset-card pf-preset-card--dashed',
357+
onclick: () => this.openImportDialog(recMgr),
358+
tabindex: 0,
359+
},
360+
m(Icon, {icon: 'upload_file'}),
361+
m('.pf-preset-card__title', 'Import'),
362+
m('.pf-preset-card__subtitle', 'Load textproto'),
363+
);
364+
}
365+
366+
private openImportDialog(recMgr: RecordingManager) {
367+
const input = document.createElement('input');
368+
input.type = 'file';
369+
input.onchange = async () => {
370+
const file = input.files?.[0];
371+
if (!file) return;
372+
const text = await file.text();
373+
const res = await traceConfigToPb(text);
374+
if (!res.ok) {
375+
showModal({
376+
title: 'Import error',
377+
content: `Failed to parse config: ${res.error}`,
378+
});
379+
return;
380+
}
381+
const config = protos.TraceConfig.decode(res.value);
382+
recMgr.setCustomTraceConfig(config, file.name);
383+
};
384+
input.click();
385+
}
386+
338387
private renderCard(
339388
icon: string,
340389
title: string,

ui/src/plugins/dev.perfetto.RecordTraceV2/presets.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ const CHROME_DEFAULT_PRESET: Preset = {
107107
subtitle: 'Common Chrome trace events',
108108
icon: 'public',
109109
session: {
110+
kind: 'probes',
110111
mode: 'STOP_WHEN_FULL',
111112
bufSizeKb: 256 * 1024,
112113
durationMs: 30_000,
@@ -133,6 +134,7 @@ const CHROME_V8_PRESET: Preset = {
133134
subtitle: 'JavaScript, wasm & GC',
134135
icon: 'mode_fan',
135136
session: {
137+
kind: 'probes',
136138
mode: 'STOP_WHEN_FULL',
137139
bufSizeKb: 256 * 1024,
138140
durationMs: 30_000,
@@ -174,6 +176,7 @@ export const ANDROID_PRESETS: Preset[] = [
174176
subtitle: 'The default config for general purpose tracing',
175177
icon: 'auto_awesome',
176178
session: {
179+
kind: 'probes',
177180
mode: 'STOP_WHEN_FULL',
178181
bufSizeKb: 64 * 1024,
179182
durationMs: 10_000,
@@ -206,6 +209,7 @@ export const ANDROID_PRESETS: Preset[] = [
206209
subtitle: 'Battery usage and power consumption',
207210
icon: 'battery_profile',
208211
session: {
212+
kind: 'probes',
209213
mode: 'STOP_WHEN_FULL',
210214
bufSizeKb: 64 * 1024,
211215
durationMs: 30_000,
@@ -231,6 +235,7 @@ export const ANDROID_PRESETS: Preset[] = [
231235
subtitle: 'Thermal throttling and mitigation',
232236
icon: 'thermostat',
233237
session: {
238+
kind: 'probes',
234239
mode: 'STOP_WHEN_FULL',
235240
bufSizeKb: 64 * 1024,
236241
durationMs: 30_000,
@@ -259,6 +264,7 @@ export const ANDROID_PRESETS: Preset[] = [
259264
subtitle: 'Graphics pipeline and system compositor',
260265
icon: 'layers',
261266
session: {
267+
kind: 'probes',
262268
mode: 'STOP_WHEN_FULL',
263269
bufSizeKb: 64 * 1024,
264270
durationMs: 30000,
@@ -297,6 +303,7 @@ export const LINUX_PRESETS: Preset[] = [
297303
subtitle: 'General purpose CPU and system tracing',
298304
icon: 'auto_awesome',
299305
session: {
306+
kind: 'probes',
300307
mode: 'STOP_WHEN_FULL',
301308
bufSizeKb: 64 * 1024,
302309
durationMs: 10_000,
@@ -318,6 +325,7 @@ export const LINUX_PRESETS: Preset[] = [
318325
subtitle: 'CPU scheduling and process activity',
319326
icon: 'schedule',
320327
session: {
328+
kind: 'probes',
321329
mode: 'STOP_WHEN_FULL',
322330
bufSizeKb: 64 * 1024,
323331
durationMs: 10_000,

0 commit comments

Comments
 (0)