Skip to content

Commit 9929446

Browse files
authored
Extra timing on hot reload (#104242)
This PR adds extra timings for a hot reload. As an example, before a user might see > Performing hot reload... > Reloaded 1 of 788 libraries in 554ms. With this PR it would instead be something like > Performing hot reload... > Reloaded 1 of 788 libraries in 554ms (compile: 33 ms, reload: 153 ms, reassemble: 310 ms).
1 parent a8fd63a commit 9929446

File tree

4 files changed

+119
-3
lines changed

4 files changed

+119
-3
lines changed

packages/flutter_tools/lib/src/resident_runner.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1486,14 +1486,17 @@ abstract class ResidentRunner extends ResidentHandlers {
14861486
}
14871487

14881488
class OperationResult {
1489-
OperationResult(this.code, this.message, { this.fatal = false, this.updateFSReport });
1489+
OperationResult(this.code, this.message, { this.fatal = false, this.updateFSReport, this.extraTimings = const <OperationResultExtraTiming>[] });
14901490

14911491
/// The result of the operation; a non-zero code indicates a failure.
14921492
final int code;
14931493

14941494
/// A user facing message about the results of the operation.
14951495
final String message;
14961496

1497+
/// User facing extra timing information about the operation.
1498+
final List<OperationResultExtraTiming> extraTimings;
1499+
14971500
/// Whether this error should cause the runner to exit.
14981501
final bool fatal;
14991502

@@ -1504,6 +1507,16 @@ class OperationResult {
15041507
static final OperationResult ok = OperationResult(0, '');
15051508
}
15061509

1510+
class OperationResultExtraTiming {
1511+
const OperationResultExtraTiming(this.description, this.timeInMs);
1512+
1513+
/// A user facing short description of this timing.
1514+
final String description;
1515+
1516+
/// The time this operation took in milliseconds.
1517+
final int timeInMs;
1518+
}
1519+
15071520
Future<String> getMissingPackageHintForPlatform(TargetPlatform platform) async {
15081521
switch (platform) {
15091522
case TargetPlatform.android_arm:

packages/flutter_tools/lib/src/run_hot.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,14 @@ class HotRunner extends ResidentRunner {
726726
if (result.isOk) {
727727
final String elapsed = getElapsedAsMilliseconds(timer.elapsed);
728728
if (!silent) {
729-
globals.printStatus('${result.message} in $elapsed.');
729+
if (result.extraTimings.isNotEmpty) {
730+
final String extraTimingsString = result.extraTimings
731+
.map((OperationResultExtraTiming e) => '${e.description}: ${e.timeInMs} ms')
732+
.join(', ');
733+
globals.printStatus('${result.message} in $elapsed ($extraTimingsString).');
734+
} else {
735+
globals.printStatus('${result.message} in $elapsed.');
736+
}
730737
}
731738
}
732739
return result;
@@ -898,6 +905,10 @@ class HotRunner extends ResidentRunner {
898905
if (!updatedDevFS.success) {
899906
return OperationResult(1, 'DevFS synchronization failed');
900907
}
908+
909+
final List<OperationResultExtraTiming> extraTimings = <OperationResultExtraTiming>[];
910+
extraTimings.add(OperationResultExtraTiming('compile', updatedDevFS.compileDuration.inMilliseconds));
911+
901912
String reloadMessage = 'Reloaded 0 libraries';
902913
final Stopwatch reloadVMTimer = _stopwatchFactory.createStopwatch('reloadSources:vm')..start();
903914
final Map<String, Object> firstReloadDetails = <String, Object>{};
@@ -920,6 +931,7 @@ class HotRunner extends ResidentRunner {
920931
_addBenchmarkData('hotReloadVMReloadMilliseconds', 0);
921932
}
922933
reloadVMTimer.stop();
934+
extraTimings.add(OperationResultExtraTiming('reload', reloadVMTimer.elapsedMilliseconds));
923935

924936
await evictDirtyAssets();
925937

@@ -939,6 +951,7 @@ class HotRunner extends ResidentRunner {
939951
// Record time it took for Flutter to reassemble the application.
940952
reassembleTimer.stop();
941953
_addBenchmarkData('hotReloadFlutterReassembleMilliseconds', reassembleTimer.elapsed.inMilliseconds);
954+
extraTimings.add(OperationResultExtraTiming('reassemble', reassembleTimer.elapsedMilliseconds));
942955

943956
reloadTimer.stop();
944957
final Duration reloadDuration = reloadTimer.elapsed;
@@ -985,6 +998,7 @@ class HotRunner extends ResidentRunner {
985998
return OperationResult(
986999
reassembleResult.failedReassemble ? 1 : OperationResult.ok.code,
9871000
reloadMessage,
1001+
extraTimings: extraTimings
9881002
);
9891003
}
9901004

packages/flutter_tools/test/general.shard/resident_runner_test.dart

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,95 @@ void main() {
846846
FeatureFlags: () => TestFeatureFlags(isSingleWidgetReloadEnabled: true),
847847
}));
848848

849+
testUsingContext('ResidentRunner reports hot reload time details', () => testbed.run(() async {
850+
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
851+
listViews,
852+
FakeVmServiceRequest(
853+
method: 'getVM',
854+
jsonResponse: fakeVM.toJson(),
855+
),
856+
listViews,
857+
listViews,
858+
FakeVmServiceRequest(
859+
method: 'getVM',
860+
jsonResponse: fakeVM.toJson(),
861+
),
862+
const FakeVmServiceRequest(
863+
method: 'reloadSources',
864+
args: <String, Object>{
865+
'isolateId': '1',
866+
'pause': false,
867+
'rootLibUri': 'main.dart.incremental.dill',
868+
},
869+
jsonResponse: <String, Object>{
870+
'type': 'ReloadReport',
871+
'success': true,
872+
'details': <String, Object>{
873+
'loadedLibraryCount': 1,
874+
'finalLibraryCount': 42,
875+
},
876+
},
877+
),
878+
FakeVmServiceRequest(
879+
method: 'getIsolate',
880+
args: <String, Object>{
881+
'isolateId': '1',
882+
},
883+
jsonResponse: fakeUnpausedIsolate.toJson(),
884+
),
885+
FakeVmServiceRequest(
886+
method: 'ext.flutter.fastReassemble',
887+
args: <String, Object>{
888+
'isolateId': fakeUnpausedIsolate.id,
889+
'className': 'FOO',
890+
},
891+
),
892+
]);
893+
final FakeDelegateFlutterDevice flutterDevice = FakeDelegateFlutterDevice(
894+
device,
895+
BuildInfo.debug,
896+
FakeResidentCompiler(),
897+
devFS,
898+
)..vmService = fakeVmServiceHost.vmService;
899+
residentRunner = HotRunner(
900+
<FlutterDevice>[
901+
flutterDevice,
902+
],
903+
stayResident: false,
904+
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
905+
target: 'main.dart',
906+
devtoolsHandler: createNoOpHandler,
907+
);
908+
devFS.nextUpdateReport = UpdateFSReport(
909+
success: true,
910+
fastReassembleClassName: 'FOO',
911+
invalidatedSourcesCount: 1,
912+
);
913+
914+
final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
915+
final Completer<void> futureAppStart = Completer<void>.sync();
916+
unawaited(residentRunner.attach(
917+
appStartedCompleter: futureAppStart,
918+
connectionInfoCompleter: futureConnectionInfo,
919+
enableDevTools: true,
920+
));
921+
922+
await futureAppStart.future;
923+
await residentRunner.restart();
924+
925+
// The actual test: Expect to have compile, reload and reassemble times.
926+
expect(
927+
testLogger.statusText,
928+
contains(RegExp(r'Reloaded 1 of 42 libraries in \d+ms '
929+
r'\(compile: \d+ ms, reload: \d+ ms, reassemble: \d+ ms\)\.')));
930+
}, overrides: <Type, Generator>{
931+
FileSystem: () => MemoryFileSystem.test(),
932+
Platform: () => FakePlatform(),
933+
ProjectFileInvalidator: () => FakeProjectFileInvalidator(),
934+
Usage: () => TestUsage(),
935+
FeatureFlags: () => TestFeatureFlags(isSingleWidgetReloadEnabled: true),
936+
}));
937+
849938
testUsingContext('ResidentRunner can send target platform to analytics from full restart', () => testbed.run(() async {
850939
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
851940
listViews,

packages/flutter_tools/test/integration.shard/overall_experience_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ void main() {
375375
return null;
376376
}),
377377
Barrier('Performing hot reload...'.padRight(progressMessageWidth), logging: true),
378-
Multiple(<Pattern>[RegExp(r'^Reloaded 0 libraries in [0-9]+ms\.$'), 'called reassemble', 'called paint'], handler: (String line) {
378+
Multiple(<Pattern>[RegExp(r'^Reloaded 0 libraries in [0-9]+ms \(compile: \d+ ms, reload: \d+ ms, reassemble: \d+ ms\)\.$'), 'called reassemble', 'called paint'], handler: (String line) {
379379
processManager.killPid(pid, ProcessSignal.sigusr2);
380380
return null;
381381
}),

0 commit comments

Comments
 (0)