Unverified Commit cd7dfd0a authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_driver] make timeline request in chunks (#58430)

Work-around large timeline data killing devicelab machines by requesting data in 1 second intervals and combining at the end. Non-breaking change to the driver API.
parent f4d26a3b
......@@ -370,6 +370,10 @@ class VMServiceFlutterDriver extends FlutterDriver {
: const <Map<String, dynamic>>[];
}
Future<Map<String, Object>> _getVMTimelineMicros() async {
return await _peer.sendRequest('getVMTimelineMicros') as Map<String, dynamic>;
}
@override
Future<void> startTracing({
List<TimelineStream> streams = const <TimelineStream>[TimelineStream.all],
......@@ -397,15 +401,43 @@ class VMServiceFlutterDriver extends FlutterDriver {
@override
Future<Timeline> stopTracingAndDownloadTimeline({
Duration timeout = kUnusuallyLongTimeout,
int startTime,
int endTime,
}) async {
assert(timeout != null);
assert((startTime == null && endTime == null) ||
(startTime != null && endTime != null));
try {
await _warnIfSlow<void>(
future: _peer.sendRequest(_setVMTimelineFlagsMethodName, <String, String>{'recordedStreams': '[]'}),
timeout: timeout,
message: 'VM is taking an unusually long time to respond to being told to stop tracing...',
);
return Timeline.fromJson(await _peer.sendRequest(_getVMTimelineMethodName) as Map<String, dynamic>);
if (startTime == null) {
return Timeline.fromJson(await _peer.sendRequest(_getVMTimelineMethodName) as Map<String, dynamic>);
}
const int kSecondInMicros = 1000000;
int currentStart = startTime;
int currentEnd = startTime + kSecondInMicros; // 1 second of timeline
final List<Map<String, Object>> chunks = <Map<String, Object>>[];
do {
final Map<String, Object> chunk = await _peer.sendRequest(_getVMTimelineMethodName, <String, Object>{
'timeOriginMicros': currentStart,
// The range is inclusive, avoid double counting on the chance something
// aligns on the boundary.
'timeExtentMicros': kSecondInMicros - 1,
}) as Map<String, dynamic>;
chunks.add(chunk);
currentStart = currentEnd;
currentEnd += kSecondInMicros;
} while (currentStart < endTime);
return Timeline.fromJson(<String, Object>{
'traceEvents': <Object> [
for (Map<String, Object> chunk in chunks)
...chunk['traceEvents'] as List<Object>,
],
});
} catch (error, stackTrace) {
throw DriverError(
'Failed to stop tracing due to remote error',
......@@ -434,13 +466,19 @@ class VMServiceFlutterDriver extends FlutterDriver {
if (!retainPriorEvents) {
await clearTimeline();
}
final Map<String, Object> startTimestamp = await _getVMTimelineMicros();
await startTracing(streams: streams);
await action();
final Map<String, Object> endTimestamp = await _getVMTimelineMicros();
if (!(await _isPrecompiledMode())) {
_log(_kDebugWarning);
}
return stopTracingAndDownloadTimeline();
return stopTracingAndDownloadTimeline(
startTime: startTimestamp['timestamp'] as int,
endTime: endTimestamp['timestamp'] as int,
);
}
@override
......
......@@ -538,6 +538,12 @@ void main() {
return null;
});
when(mockPeer.sendRequest('getVMTimelineMicros'))
.thenAnswer((Invocation invocation) async {
log.add('getVMTimelineMicros');
return <String, Object>{};
});
when(mockPeer.sendRequest('setVMTimelineFlags', argThat(equals(<String, dynamic>{'recordedStreams': '[all]'}))))
.thenAnswer((Invocation invocation) async {
log.add('startTracing');
......@@ -568,8 +574,10 @@ void main() {
}, retainPriorEvents: true);
expect(log, const <String>[
'getVMTimelineMicros',
'startTracing',
'action',
'getVMTimelineMicros',
'stopTracing',
'download',
]);
......@@ -583,13 +591,77 @@ void main() {
expect(log, const <String>[
'clear',
'getVMTimelineMicros',
'startTracing',
'action',
'getVMTimelineMicros',
'stopTracing',
'download',
]);
expect(timeline.events.single.name, 'test event');
});
test('with time interval', () async {
int count = 0;
when(mockPeer.sendRequest('getVMTimelineMicros'))
.thenAnswer((Invocation invocation) async {
log.add('getVMTimelineMicros');
return <String, Object>{
if (count++ == 0)
'timestamp': 0
else
'timestamp': 1000001,
};
});
when(mockPeer.sendRequest('getVMTimeline', argThat(equals(<String, dynamic>{
'timeOriginMicros': 0,
'timeExtentMicros': 999999
}))))
.thenAnswer((Invocation invocation) async {
log.add('download 1');
return <String, dynamic>{
'traceEvents': <dynamic>[
<String, String>{
'name': 'test event 1',
},
],
};
});
when(mockPeer.sendRequest('getVMTimeline', argThat(equals(<String, dynamic>{
'timeOriginMicros': 1000000,
'timeExtentMicros': 999999,
}))))
.thenAnswer((Invocation invocation) async {
log.add('download 2');
return <String, dynamic>{
'traceEvents': <dynamic>[
<String, String>{
'name': 'test event 2',
},
],
};
});
final Timeline timeline = await driver.traceAction(() async {
log.add('action');
});
expect(log, const <String>[
'clear',
'getVMTimelineMicros',
'startTracing',
'action',
'getVMTimelineMicros',
'stopTracing',
'download 1',
'download 2',
]);
expect(timeline.events.map((TimelineEvent event) => event.name), <String>[
'test event 1',
'test event 2',
]);
});
});
group('traceAction with timeline streams', () {
......@@ -598,6 +670,12 @@ void main() {
bool startTracingCalled = false;
bool stopTracingCalled = false;
when(mockPeer.sendRequest('getVMTimelineMicros'))
.thenAnswer((Invocation invocation) async {
log.add('getVMTimelineMicros');
return <String, Object>{};
});
when(mockPeer.sendRequest('setVMTimelineFlags', argThat(equals(<String, dynamic>{'recordedStreams': '[Dart, GC, Compiler]'}))))
.thenAnswer((Invocation invocation) async {
startTracingCalled = true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment