Merge "ui: fix recording on P"
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index c7053d1..028fde9 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -315,6 +315,10 @@
// 'Q','P','O' for Android, 'L' for Linux, 'C' for Chrome.
export declare type TargetOs = 'Q' | 'P' | 'O' | 'C' | 'L';
+export function isAndroidP(target: RecordingTarget) {
+ return target.os === 'P';
+}
+
export function isAndroidTarget(target: RecordingTarget) {
return ['Q', 'P', 'O'].includes(target.os);
}
diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts
index 1913817..5b70619 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/controller/record_controller.ts
@@ -39,6 +39,7 @@
import {
AdbRecordingTarget,
isAdbTarget,
+ isAndroidP,
isChromeTarget,
MAX_TIME,
RecordConfig,
@@ -62,11 +63,13 @@
type RPCImplMethod = (Method|rpc.ServiceMethod<Message<{}>, Message<{}>>);
-export function genConfigProto(uiCfg: RecordConfig): Uint8Array {
- return TraceConfig.encode(genConfig(uiCfg)).finish();
+export function genConfigProto(
+ uiCfg: RecordConfig, target: RecordingTarget): Uint8Array {
+ return TraceConfig.encode(genConfig(uiCfg, target)).finish();
}
-export function genConfig(uiCfg: RecordConfig): TraceConfig {
+export function genConfig(
+ uiCfg: RecordConfig, target: RecordingTarget): TraceConfig {
const protoCfg = new TraceConfig();
protoCfg.durationMs = uiCfg.durationMs;
@@ -441,7 +444,28 @@
ftraceEvents.add('ftrace/print');
}
- ds.config.ftraceConfig.ftraceEvents = Array.from(ftraceEvents);
+ var ftraceEventsArray = Array<string>();
+ if (isAndroidP(target)) {
+ for (const ftraceEvent of ftraceEvents) {
+ // On P, we don't support groups so strip all group names from ftrace
+ // events.
+ const groupAndName = ftraceEvent.split('/');
+ if (groupAndName.length != 2) {
+ ftraceEventsArray.push(ftraceEvent);
+ continue;
+ }
+ // Filter out any wildcard event groups which was not supported
+ // before Q.
+ if (groupAndName[1] === '*') {
+ continue;
+ }
+ ftraceEventsArray.push(groupAndName[1]);
+ }
+ } else {
+ ftraceEventsArray = Array.from(ftraceEvents)
+ }
+
+ ds.config.ftraceConfig.ftraceEvents = ftraceEventsArray;
ds.config.ftraceConfig.atraceCategories = Array.from(atraceCats);
ds.config.ftraceConfig.atraceApps = Array.from(atraceApps);
protoCfg.dataSources.push(ds);
@@ -536,19 +560,26 @@
}
this.config = this.app.state.recordConfig;
- const configProto = genConfigProto(this.config);
+ const configProto =
+ genConfigProto(this.config, this.app.state.recordingTarget);
const configProtoText = toPbtxt(configProto);
+ const configProtoBase64 = uint8ArrayToBase64(configProto);
const commandline = `
- echo '${uint8ArrayToBase64(configProto)}' |
+ echo '${configProtoBase64}' |
base64 --decode |
adb shell "perfetto -c - -o /data/misc/perfetto-traces/trace" &&
adb pull /data/misc/perfetto-traces/trace /tmp/trace
`;
- const traceConfig = genConfig(this.config);
+ const traceConfig = genConfig(this.config, this.app.state.recordingTarget);
// TODO(hjd): This should not be TrackData after we unify the stores.
this.app.publish('TrackData', {
id: 'config',
- data: {commandline, pbtxt: configProtoText, traceConfig}
+ data: {
+ commandline,
+ pbBase64: configProtoBase64,
+ pbtxt: configProtoText,
+ traceConfig
+ }
});
// If the recordingInProgress boolean state is different, it means that we
diff --git a/ui/src/controller/record_controller_jsdomtest.ts b/ui/src/controller/record_controller_jsdomtest.ts
index 77da1ff..07f1487 100644
--- a/ui/src/controller/record_controller_jsdomtest.ts
+++ b/ui/src/controller/record_controller_jsdomtest.ts
@@ -24,14 +24,16 @@
test('encodeConfig', () => {
const config = createEmptyRecordConfig();
config.durationSeconds = 10;
- const result = TraceConfig.decode(genConfigProto(config));
+ const result =
+ TraceConfig.decode(genConfigProto(config, {os: 'Q', name: 'Android Q'}));
expect(result.durationMs).toBe(10000);
});
test('SysConfig', () => {
const config = createEmptyRecordConfig();
config.cpuSyscall = true;
- const result = TraceConfig.decode(genConfigProto(config));
+ const result =
+ TraceConfig.decode(genConfigProto(config, {os: 'Q', name: 'Android Q'}));
const sources = assertExists(result.dataSources);
const srcConfig = assertExists(sources[0].config);
const ftraceConfig = assertExists(srcConfig.ftraceConfig);
@@ -108,7 +110,8 @@
config.ipcFlows = true;
config.jsExecution = true;
config.mode = 'STOP_WHEN_FULL';
- const result = TraceConfig.decode(genConfigProto(config));
+ const result =
+ TraceConfig.decode(genConfigProto(config, {os: 'C', name: 'Chrome'}));
const sources = assertExists(result.dataSources);
const traceConfigSource = assertExists(sources[0].config);
@@ -133,7 +136,8 @@
config.ipcFlows = true;
config.jsExecution = true;
config.mode = 'RING_BUFFER';
- const result = TraceConfig.decode(genConfigProto(config));
+ const result =
+ TraceConfig.decode(genConfigProto(config, {os: 'C', name: 'Chrome'}));
const sources = assertExists(result.dataSources);
const traceConfigSource = assertExists(sources[0].config);
@@ -159,7 +163,8 @@
config.ipcFlows = true;
config.jsExecution = true;
config.mode = 'RING_BUFFER';
- const result = TraceConfig.decode(genConfigProto(config));
+ const result =
+ TraceConfig.decode(genConfigProto(config, {os: 'C', name: 'Chrome'}));
const sources = assertExists(result.dataSources);
const traceConfigSource = assertExists(sources[0].config);
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 3890e70..a1a4440 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -23,6 +23,7 @@
getBuiltinChromeCategoryList,
getDefaultRecordingTargets,
isAdbTarget,
+ isAndroidP,
isAndroidTarget,
isChromeTarget,
RecordingTarget
@@ -140,7 +141,7 @@
m('.record-mode',
recButton('STOP_WHEN_FULL', 'Stop when full', 'rec_one_shot.png'),
recButton('RING_BUFFER', 'Ring buffer', 'rec_ring_buf.png'),
- recButton('LONG_TRACE', 'Long trace', 'rec_long_trace.png'), ),
+ recButton('LONG_TRACE', 'Long trace', 'rec_long_trace.png')),
m(Slider, {
title: 'In-memory buffer size',
@@ -210,15 +211,13 @@
}
function GpuSettings(cssClass: string) {
- return m(
- `.record-section${cssClass}`,
- m(Probe, {
- title: 'GPU frequency',
- img: 'rec_cpu_freq.png',
- descr: 'Records gpu frequency via ftrace',
- setEnabled: (cfg, val) => cfg.gpuFreq = val,
- isEnabled: (cfg) => cfg.gpuFreq
- } as ProbeAttrs));
+ return m(`.record-section${cssClass}`, m(Probe, {
+ title: 'GPU frequency',
+ img: 'rec_cpu_freq.png',
+ descr: 'Records gpu frequency via ftrace',
+ setEnabled: (cfg, val) => cfg.gpuFreq = val,
+ isEnabled: (cfg) => cfg.gpuFreq
+ } as ProbeAttrs));
}
function CpuSettings(cssClass: string) {
@@ -741,18 +740,19 @@
img: null,
descr: `Records the screen along with running a trace. Max
time of recording is 3 minutes (180 seconds).`,
- setEnabled: (cfg, val) => cfg.screenRecord = val,
- isEnabled: (cfg) => cfg.screenRecord,
- } as ProbeAttrs,
- m(Slider, {
- title: 'Max duration',
- icon: 'timer',
- values: [S(10), S(15), S(30), S(60), M(2), M(3)],
- isTime: true,
- unit: 'm:s',
- set: (cfg, val) => cfg.durationMs = val,
- get: (cfg) => cfg.durationMs,
- } as SliderAttrs),) : null);
+ setEnabled: (cfg, val) => cfg.screenRecord = val,
+ isEnabled: (cfg) => cfg.screenRecord,
+ } as ProbeAttrs,
+ m(Slider, {
+ title: 'Max duration',
+ icon: 'timer',
+ values: [S(10), S(15), S(30), S(60), M(2), M(3)],
+ isTime: true,
+ unit: 'm:s',
+ set: (cfg, val) => cfg.durationMs = val,
+ get: (cfg) => cfg.durationMs,
+ } as SliderAttrs)) :
+ null);
}
function RecordHeader() {
@@ -863,6 +863,11 @@
const msgPerfettoNotSupported =
m('div', `Perfetto is not supported natively before Android P.`);
+ const msgRecordingNotSupported =
+ m('div', `Recording Perfetto traces from the UI is not supported natively
+ before Android Q. If you are using a P device, please select 'Android P'
+ as the 'Target Platform' and collect the trace using ADB`);
+
const msgSideload =
m('div',
`If you have a rooted device you can sideload the latest version of
@@ -882,6 +887,9 @@
output directory. `,
doc);
+ if (isAdbTarget(globals.state.recordingTarget)) {
+ notes.push(msgRecordingNotSupported);
+ }
switch (globals.state.recordingTarget.os) {
case 'Q':
break;
@@ -922,7 +930,7 @@
function getRecordCommand(target: RecordingTarget) {
const data = globals.trackDataStore.get('config') as
- {commandline: string, pbtxt: string} |
+ {commandline: string, pbtxt: string, pbBase64: string} |
null;
const cfg = globals.state.recordConfig;
@@ -932,6 +940,7 @@
time = MAX_TIME;
}
+ const pbBase64 = data ? data.pbBase64 : '';
const pbtx = data ? data.pbtxt : '';
let cmd = '';
if (cfg.screenRecord) {
@@ -940,13 +949,19 @@
cmd += `(sleep 0.5 && adb shell screenrecord --time-limit ${time}`;
cmd += ' "/sdcard/tracescr.mp4") &\\\n';
}
- cmd += isAndroidTarget(target) ? 'adb shell perfetto \\\n' : 'perfetto \\\n';
- cmd += ' -c - --txt \\\n';
- cmd += ' -o /data/misc/perfetto-traces/trace \\\n';
- cmd += '<<EOF\n\n';
- cmd += pbtx;
- cmd += '\nEOF\n';
-
+ if (isAndroidP(target)) {
+ cmd += `echo '${pbBase64}' | \n`;
+ cmd += 'base64 --decode | \n';
+ cmd += 'adb shell "perfetto -c - -o /data/misc/perfetto-traces/trace"\n';
+ } else {
+ cmd +=
+ isAndroidTarget(target) ? 'adb shell perfetto \\\n' : 'perfetto \\\n';
+ cmd += ' -c - --txt \\\n';
+ cmd += ' -o /data/misc/perfetto-traces/trace \\\n';
+ cmd += '<<EOF\n\n';
+ cmd += pbtx;
+ cmd += '\nEOF\n';
+ }
return cmd;
}