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 { ...@@ -370,6 +370,10 @@ class VMServiceFlutterDriver extends FlutterDriver {
: const <Map<String, dynamic>>[]; : const <Map<String, dynamic>>[];
} }
Future<Map<String, Object>> _getVMTimelineMicros() async {
return await _peer.sendRequest('getVMTimelineMicros') as Map<String, dynamic>;
}
@override @override
Future<void> startTracing({ Future<void> startTracing({
List<TimelineStream> streams = const <TimelineStream>[TimelineStream.all], List<TimelineStream> streams = const <TimelineStream>[TimelineStream.all],
...@@ -397,15 +401,43 @@ class VMServiceFlutterDriver extends FlutterDriver { ...@@ -397,15 +401,43 @@ class VMServiceFlutterDriver extends FlutterDriver {
@override @override
Future<Timeline> stopTracingAndDownloadTimeline({ Future<Timeline> stopTracingAndDownloadTimeline({
Duration timeout = kUnusuallyLongTimeout, Duration timeout = kUnusuallyLongTimeout,
int startTime,
int endTime,
}) async { }) async {
assert(timeout != null); assert(timeout != null);
assert((startTime == null && endTime == null) ||
(startTime != null && endTime != null));
try { try {
await _warnIfSlow<void>( await _warnIfSlow<void>(
future: _peer.sendRequest(_setVMTimelineFlagsMethodName, <String, String>{'recordedStreams': '[]'}), future: _peer.sendRequest(_setVMTimelineFlagsMethodName, <String, String>{'recordedStreams': '[]'}),
timeout: timeout, timeout: timeout,
message: 'VM is taking an unusually long time to respond to being told to stop tracing...', 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) { } catch (error, stackTrace) {
throw DriverError( throw DriverError(
'Failed to stop tracing due to remote error', 'Failed to stop tracing due to remote error',
...@@ -434,13 +466,19 @@ class VMServiceFlutterDriver extends FlutterDriver { ...@@ -434,13 +466,19 @@ class VMServiceFlutterDriver extends FlutterDriver {
if (!retainPriorEvents) { if (!retainPriorEvents) {
await clearTimeline(); await clearTimeline();
} }
final Map<String, Object> startTimestamp = await _getVMTimelineMicros();
await startTracing(streams: streams); await startTracing(streams: streams);
await action(); await action();
final Map<String, Object> endTimestamp = await _getVMTimelineMicros();
if (!(await _isPrecompiledMode())) { if (!(await _isPrecompiledMode())) {
_log(_kDebugWarning); _log(_kDebugWarning);
} }
return stopTracingAndDownloadTimeline();
return stopTracingAndDownloadTimeline(
startTime: startTimestamp['timestamp'] as int,
endTime: endTimestamp['timestamp'] as int,
);
} }
@override @override
......
...@@ -538,6 +538,12 @@ void main() { ...@@ -538,6 +538,12 @@ void main() {
return null; 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]'})))) when(mockPeer.sendRequest('setVMTimelineFlags', argThat(equals(<String, dynamic>{'recordedStreams': '[all]'}))))
.thenAnswer((Invocation invocation) async { .thenAnswer((Invocation invocation) async {
log.add('startTracing'); log.add('startTracing');
...@@ -568,8 +574,10 @@ void main() { ...@@ -568,8 +574,10 @@ void main() {
}, retainPriorEvents: true); }, retainPriorEvents: true);
expect(log, const <String>[ expect(log, const <String>[
'getVMTimelineMicros',
'startTracing', 'startTracing',
'action', 'action',
'getVMTimelineMicros',
'stopTracing', 'stopTracing',
'download', 'download',
]); ]);
...@@ -583,13 +591,77 @@ void main() { ...@@ -583,13 +591,77 @@ void main() {
expect(log, const <String>[ expect(log, const <String>[
'clear', 'clear',
'getVMTimelineMicros',
'startTracing', 'startTracing',
'action', 'action',
'getVMTimelineMicros',
'stopTracing', 'stopTracing',
'download', 'download',
]); ]);
expect(timeline.events.single.name, 'test event'); 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', () { group('traceAction with timeline streams', () {
...@@ -598,6 +670,12 @@ void main() { ...@@ -598,6 +670,12 @@ void main() {
bool startTracingCalled = false; bool startTracingCalled = false;
bool stopTracingCalled = 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]'})))) when(mockPeer.sendRequest('setVMTimelineFlags', argThat(equals(<String, dynamic>{'recordedStreams': '[Dart, GC, Compiler]'}))))
.thenAnswer((Invocation invocation) async { .thenAnswer((Invocation invocation) async {
startTracingCalled = true; 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