mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 14:32:49 +00:00
9394b30b77
Bug: b/286184681 Change-Id: I903528c4adfbc576644aec7541903df6b9633e26 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/325523 Reviewed-by: Jonas Termansen <sortie@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com> Commit-Queue: Alexander Thomas <athom@google.com>
149 lines
4.6 KiB
Dart
149 lines
4.6 KiB
Dart
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
|
|
import 'vm_service_helper.dart' as vmService;
|
|
|
|
Future<void> main(List<String> args) async {
|
|
CoverageHelper coverageHelper = new CoverageHelper();
|
|
|
|
List<String> allArgs = <String>[];
|
|
allArgs.addAll([
|
|
"--disable-dart-dev",
|
|
"--enable-asserts",
|
|
"--pause_isolates_on_exit",
|
|
]);
|
|
allArgs.addAll(args);
|
|
|
|
await coverageHelper.start(allArgs);
|
|
}
|
|
|
|
class CoverageHelper extends vmService.LaunchingVMServiceHelper {
|
|
final bool forceCompilation;
|
|
final bool printHits;
|
|
|
|
CoverageHelper({this.forceCompilation = false, this.printHits = true});
|
|
|
|
@override
|
|
Future<void> run() async {
|
|
vmService.VM vm = await serviceClient.getVM();
|
|
if (vm.isolates!.length != 1) {
|
|
throw "Expected 1 isolate, got ${vm.isolates!.length}";
|
|
}
|
|
vmService.IsolateRef isolateRef = vm.isolates!.single;
|
|
await waitUntilIsolateIsRunnable(isolateRef.id!);
|
|
await serviceClient.resume(isolateRef.id!);
|
|
Completer<String> cTimeout = new Completer();
|
|
Timer timer = new Timer(new Duration(minutes: 20), () {
|
|
cTimeout.complete("Timeout");
|
|
killProcess();
|
|
});
|
|
|
|
Completer<String> cRunDone = new Completer();
|
|
// ignore: unawaited_futures
|
|
waitUntilPaused(isolateRef.id!).then((value) => cRunDone.complete("Done"));
|
|
|
|
await Future.any([cRunDone.future, cTimeout.future, cProcessExited.future]);
|
|
|
|
timer.cancel();
|
|
|
|
if (!await isPausedAtExit(isolateRef.id!)) {
|
|
killProcess();
|
|
throw "Expected to be paused at exit, but is just paused!";
|
|
}
|
|
|
|
// Get and process coverage information.
|
|
Stopwatch stopwatch = new Stopwatch()..start();
|
|
vmService.SourceReport sourceReport = await serviceClient.getSourceReport(
|
|
isolateRef.id!, [vmService.SourceReportKind.kCoverage],
|
|
forceCompile: forceCompilation);
|
|
print("Got source report from VM in ${stopwatch.elapsedMilliseconds} ms");
|
|
stopwatch.reset();
|
|
Map<Uri, Coverage> coverages = {};
|
|
for (vmService.SourceReportRange range in sourceReport.ranges!) {
|
|
vmService.ScriptRef script = sourceReport.scripts![range.scriptIndex!];
|
|
Uri scriptUri = Uri.parse(script.uri!);
|
|
if (!includeCoverageFor(scriptUri)) continue;
|
|
Coverage coverage = coverages[scriptUri] ??= new Coverage();
|
|
|
|
vmService.SourceReportCoverage? sourceReportCoverage = range.coverage;
|
|
if (sourceReportCoverage == null) {
|
|
// Range not compiled. Record the range if provided.
|
|
assert(!range.compiled!);
|
|
if (range.startPos! >= 0 || range.endPos! >= 0) {
|
|
coverage.notCompiled
|
|
.add(new StartEndPair(range.startPos!, range.endPos!));
|
|
}
|
|
continue;
|
|
}
|
|
coverage.hits.addAll(sourceReportCoverage.hits!);
|
|
coverage.misses.addAll(sourceReportCoverage.misses!);
|
|
}
|
|
print("Processed source report from VM in "
|
|
"${stopwatch.elapsedMilliseconds} ms");
|
|
stopwatch.reset();
|
|
|
|
// It's paused at exit, so resuming should allow us to exit.
|
|
await serviceClient.resume(isolateRef.id!);
|
|
|
|
for (MapEntry<Uri, Coverage> entry in coverages.entries) {
|
|
assert(entry.value.hits.intersection(entry.value.misses).isEmpty);
|
|
if (entry.value.hits.isEmpty &&
|
|
entry.value.misses.isEmpty &&
|
|
entry.value.notCompiled.isEmpty) {
|
|
continue;
|
|
}
|
|
print(entry.key);
|
|
if (printHits) {
|
|
print("Hits: ${entry.value.hits.toList()..sort()}");
|
|
}
|
|
print("Misses: ${entry.value.misses.toList()..sort()}");
|
|
print("Not compiled: ${entry.value.notCompiled.toList()..sort()}");
|
|
print("");
|
|
}
|
|
}
|
|
|
|
Completer<String> cProcessExited = new Completer();
|
|
@override
|
|
void processExited(int exitCode) {
|
|
cProcessExited.complete("Exit");
|
|
}
|
|
|
|
bool includeCoverageFor(Uri uri) {
|
|
if (uri.isScheme("dart")) {
|
|
return false;
|
|
}
|
|
if (uri.isScheme("package")) {
|
|
return uri.pathSegments.first == "front_end" ||
|
|
uri.pathSegments.first == "_fe_analyzer_shared" ||
|
|
uri.pathSegments.first == "kernel";
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
class Coverage {
|
|
final Set<int> hits = {};
|
|
final Set<int> misses = {};
|
|
final Set<StartEndPair> notCompiled = {};
|
|
}
|
|
|
|
class StartEndPair implements Comparable {
|
|
final int startPos;
|
|
final int endPos;
|
|
|
|
StartEndPair(this.startPos, this.endPos);
|
|
|
|
@override
|
|
String toString() => "[$startPos - $endPos]";
|
|
|
|
@override
|
|
int compareTo(dynamic other) {
|
|
if (other is! StartEndPair) return -1;
|
|
StartEndPair o = other;
|
|
return startPos - o.startPos;
|
|
}
|
|
}
|