[CFE] Low-level coverage tool
This/these tools can later be integrated into other tools/tests if we
want to.
For now it is "external" tools where one can for instance see that
running our strong tests leaves quite a bit to be desired in regards
to constant transformation coverage:
package:front_end/src/fasta/kernel/constant_collection_builders.dart
Misses: [950, 956, 960, 970, 1002, 1014, 1015, 1025, 1334, 1340, 1431, 1441, 1782, 1792, 2593, 2599, 2624, 2949, 2977, 2984, 3001, 3036, 3044, 3067, 3080, 3124, 3153, 3157, 3167, 3270, 3280, 3301, 3315, 3354, 3963, 3973, 4050, 4063, 4089, 4099, 4194, 4204, 4275, 4288, 4314, 4324, 4797, 4803, 4828, 5889, 5899, 5979, 6007, 6014, 6031, 6065, 6073, 6096, 6108, 6152, 6180, 6184, 6194, 6296, 6306, 6327, 6341, 6378, 7267, 7273, 7277, 7287, 7318, 7337, 7363, 7368, 7377, 7387, 7401, 7411, 7768, 7774, 7865, 7875, 8232, 8242, 8566, 8572, 8605, 8667, 8677, 8753, 8766, 8787, 8797, 8897, 8907, 8977, 8990, 9011, 9021, 9760, 9788, 9795, 9812, 9854, 9875, 9898, 9910, 9919, 9961, 9989, 9993, 10003, 10105, 10115, 10127, 10149, 10181, 10201]
Not compiled: [[2291 - 2403], [3724 - 3834]]
package:front_end/src/fasta/kernel/constant_evaluator.dart
Misses: [4301, 4340, 4371, 4408, 7793, 7822, 7841, 7848, 7910, 7918, 7958, 8102, 8937, 8983, 9000, 9017, 9030, 9105, 9119, 9165, 9424, 9459, 9492, 9600, 9622, 9670, 9705, 12639, 12657, 19070, 19088, 23629, 23928, 23953, 27554, 27562, 27610, 27623, 28624, 28681, 28698, 29046, 29103, 29120, 29471, 30091, 30138, 30210, 30243, 30284, 30738, 30747, 35250, 37816, 37842, 39028, 39708, 40583, 40660, 40859, 40949, 41212, 41248, 41291, 41340, 41386, 41396, 41658, 41687, 41708, 41736, 41761, 41778, 41803, 41839, 41862, 41872, 41883, 41935, 41948, 41975, 42248, 42274, 42636, 42659, 42761, 42791, 42809, 42825, 43014, 43032, 43048, 43452, 43481, 43563, 43572, 43577, 48621, 48670, 48681, 48704, 48745, 48769, 48773, 49496, 50581, 50623, 50713, 50831, 50843, 50859, 50875, 50879, 51141, 51168, 51199, 51207, 51240, 51259, 51286, 51302, 51310, 51318, 51334, 51424, 51486, 51738, 51771, 51793, 51826, 51842, 51850, 51858, 51874, 51917, 51981, 52047, 52096, 52127, 52159, 52247, 52260, 52269, 52305, 52337, 52389, 52402, 52449, 52465, 52475, 52522, 52530, 52569, 52632, 52660, 52710, 52723, 52764, 52780, 52790, 52833, 52841, 52876, 53178, 53414, 53457, 53474, 53498, 53518, 53550, 53559, 53569, 53857, 53882, 53920, 53958, 53995, 54035, 54072, 54236, 54287, 54313, 54354, 54434, 54447, 54483, 54491, 54512, 54689, 54696, 54741, 54790, 54817, 54859, 54895, 54919, 54925, 54933, 54975, 55063, 55076, 55153, 55169, 55179, 55226, 55234, 55275, 55883, 55971, 55984, 56057, 56073, 56083, 56127, 56135, 56176, 56429, 56509, 56522, 56586, 56602, 56612, 56652, 56660, 56697, 56897, 56977, 56990, 57054, 57070, 57080, 57123, 57131, 57168, 57720, 57800, 57813, 57877, 57893, 57903, 57943, 57951, 57988, 58046, 58085, 58092, 58140, 58163, 58223, 58249, 58285, 58307, 58322, 58348, 58374, 58410, 58432, 58447, 58473, 58499, 58535, 58557, 58563, 58572, 58635, 58667, 58726, 58797, 58810, 58838, 62482, 62498, 62505, 62511, 62531, 62536, 62544, 62569, 62585, 62591, 62631, 62698, 62727, 62767, 62795, 62823, 62849, 62854, 62859, 62887, 62900, 62910, 62935, 62941, 62966, 63002, 63034, 63046, 63070, 63081, 63124, 63156, 63214, 63280, 63293, 63312, 63317, 63333, 63416, 63520, 63548, 64132, 64233, 64246, 64256, 64374, 65680, 65776, 65789, 65797, 65802, 65986, 66082, 66095, 66103, 66108, 66139, 66197, 66227, 68515, 68884, 68958, 69039, 69083, 69113, 69153, 69197, 69253, 69495, 69606, 69645, 69655, 69755, 69765, 69867, 70039, 70054, 70057, 70067, 70165, 70182, 70272, 70378, 70442, 70540, 70615, 71144, 71187, 71227, 71278, 71571, 71576, 71581, 71942, 72011, 72049, 72189, 72240, 72283, 72334, 73668, 73720, 73800, 73834, 73907, 73912, 73933, 73956, 73984, 74017, 74032, 74078, 74093, 74107, 74134, 74175, 74357, 74400, 74413, 74432, 74436, 74456, 74477, 74521, 74540, 74818, 74861, 74874, 74898, 74918, 74936, 74961, 74974, 75117, 75677, 75693, 75711, 75727, 75812, 75828, 75840, 75856, 76609, 76621, 76713, 76729, 76747, 76763, 76852, 76868, 76880, 76896, 77022, 77038, 77050, 77066, 77781, 77839, 77860, 77886, 77938, 77984, 77999, 78834, 78856, 78866, 79352, 79395, 79409, 79447, 79461, 79495, 79892, 79921, 80247, 82403, 82446, 82456, 82459, 82475, 82485, 82573, 82589, 82618, 82632, 82648, 82658, 82700, 82788, 82798, 82905, 82921, 82950, 82964, 82980, 82990, 83029, 85628, 87178, 87195, 87236, 87253, 87294, 87311, 87352, 87369, 87590, 87614, 87631, 87673, 87693, 87712, 87729, 87750, 87769, 87787, 87808, 87827, 87845, 87865, 87884, 87908, 87972, 89297, 90734, 91335, 91363, 91615, 91643, 95325, 95341]
Not compiled: [[2884 - 3935], [4838 - 5709], [5717 - 5764], [5717 - 5764], [6120 - 6260], [6265 - 6330], [6547 - 6616], [6621 - 6690], [6695 - 7565], [8172 - 8766], [9749 - 10256], [10261 - 10332], [10337 - 10565], [10570 - 10649], [12669 - 12775], [15251 - 15850], [22509 - 22639], [22644 - 22832], [22837 - 22965], [23163 - 23291], [32272 - 32540], [32545 - 32690], [32945 - 33199], [33706 - 33737], [33801 - 33832], [34401 - 34563], [36370 - 36615], [36620 - 36744], [37854 - 37926], [40336 - 40462], [43608 - 44490], [44495 - 46198], [46743 - 46814], [47292 - 47433], [58871 - 61188], [61193 - 62190], [66274 - 68208], [70625 - 70783], [78009 - 78569], [79465 - 79493], [80342 - 80520], [80717 - 81197], [85711 - 86339], [89503 - 89864], [91027 - 91185], [91677 - 91731], [91879 - 92361], [92928 - 93577], [93773 - 93882], [94073 - 94130], [94857 - 94883], [94959 - 95053]]
package:front_end/src/fasta/kernel/constant_int_folder.dart
Misses: [801, 1452, 1462, 1516, 1529, 1541, 1551, 2244, 2268, 2280, 2289, 2327, 2337, 2385, 2659, 2673, 2769, 2783, 2824, 2841, 2883, 2897, 2939, 2953, 2994, 3008, 3049, 3063, 3104, 3118, 3217, 3231, 3279, 3294, 3300, 3306, 3313, 3319, 3344, 3396, 3406, 3425, 3463, 3473, 3492, 3531, 3541, 3560, 3663, 3673, 3722]
Not compiled: [[4076 - 4143], [4148 - 4258], [4263 - 4318], [4323 - 4420], [4425 - 4545], [4550 - 4885], [4890 - 5320], [5325 - 7041], [7046 - 7449]]
Change-Id: Ic2334d46ebadfa062f93d7c074d7a1a1ee5f9ffc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/162721
Commit-Queue: Jens Johansen <jensj@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/front_end/test/incremental_compiler_leak_test.dart b/pkg/front_end/test/incremental_compiler_leak_test.dart
index 326c2e2..e8927d1 100644
--- a/pkg/front_end/test/incremental_compiler_leak_test.dart
+++ b/pkg/front_end/test/incremental_compiler_leak_test.dart
@@ -1,11 +1,8 @@
import 'dart:async';
import 'dart:io';
-import "package:vm_service/vm_service.dart" as vmService;
-import "package:vm_service/vm_service_io.dart" as vmService;
-
-import "vm_service_heap_helper.dart" as helper;
import "simple_stats.dart";
+import "vm_service_helper.dart" as vmService;
const int limit = 10;
@@ -22,7 +19,7 @@
]);
}
-class LeakFinder extends helper.LaunchingVMServiceHeapHelper {
+class LeakFinder extends vmService.LaunchingVMServiceHelper {
@override
Future<void> run() async {
vmService.VM vm = await serviceClient.getVM();
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index f858baf..e985d0b 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -129,6 +129,8 @@
corners
costly
cov
+coverage
+coverages
cp
csi
ctrl
@@ -282,6 +284,7 @@
heuristics
hi
hints
+hits
home
hoo
hosted
@@ -403,6 +406,7 @@
mismatched
misnamed
miss
+misses
misspelled
mistake
mistakes
@@ -500,6 +504,7 @@
response
result1
result2
+resuming
retaining
retainingpath
retains
diff --git a/pkg/front_end/test/vm_service_coverage.dart b/pkg/front_end/test/vm_service_coverage.dart
new file mode 100644
index 0000000..b144e3d
--- /dev/null
+++ b/pkg/front_end/test/vm_service_coverage.dart
@@ -0,0 +1,142 @@
+import 'dart:async';
+
+import 'vm_service_helper.dart' as vmService;
+
+main(List<String> args) async {
+ CoverageHelper coverageHelper = new CoverageHelper();
+
+ List<String> allArgs = new List<String>();
+ allArgs.addAll([
+ "--disable-dart-dev",
+ "--enable-asserts",
+ "--pause_isolates_on_exit",
+ ]);
+ allArgs.addAll(args);
+
+ 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();
+ void processExited(int exitCode) {
+ cProcessExited.complete("Exit");
+ }
+
+ bool includeCoverageFor(Uri uri) {
+ if (uri.scheme == "dart") {
+ return false;
+ }
+ if (uri.scheme == "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);
+
+ String toString() => "[$startPos - $endPos]";
+
+ @override
+ int compareTo(Object other) {
+ if (other is! StartEndPair) return -1;
+ StartEndPair o = other;
+ return startPos - o.startPos;
+ }
+}
diff --git a/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart b/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart
new file mode 100644
index 0000000..4cab31b
--- /dev/null
+++ b/pkg/front_end/test/vm_service_coverage_constant_evaluator.dart
@@ -0,0 +1,27 @@
+import 'vm_service_coverage.dart' as helper;
+
+main(List<String> args) async {
+ CoverageHelper coverageHelper = new CoverageHelper();
+
+ List<String> allArgs = new List<String>();
+ allArgs.addAll([
+ "--disable-dart-dev",
+ "--enable-asserts",
+ "--pause_isolates_on_exit",
+ ]);
+ allArgs.addAll(args);
+
+ coverageHelper.start(allArgs);
+}
+
+class CoverageHelper extends helper.CoverageHelper {
+ CoverageHelper() : super(printHits: false);
+
+ bool includeCoverageFor(Uri uri) {
+ if (uri.scheme != "package") return false;
+ if (uri.path.startsWith("front_end/src/fasta/kernel/constant_")) {
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/pkg/front_end/test/vm_service_heap_finder.dart b/pkg/front_end/test/vm_service_heap_finder.dart
index ea3bcea..2e85184 100644
--- a/pkg/front_end/test/vm_service_heap_finder.dart
+++ b/pkg/front_end/test/vm_service_heap_finder.dart
@@ -3,7 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
import "dart:io";
-import "vm_service_heap_helper.dart";
+
+import "vm_service_helper.dart" as vmService;
class Foo {
final String x;
@@ -31,7 +32,7 @@
foos.add(new Foo("!", 44));
if (connectTo == null) connectTo = ask("Connect to");
- VMServiceHeapHelperBase vm = VMServiceHeapHelperBase();
+ VMServiceHeapHelperPrinter vm = VMServiceHeapHelperPrinter();
await vm.connect(Uri.parse(connectTo.trim()));
String isolateId = await vm.getIsolateId();
if (classToFind == null) classToFind = ask("Find what class");
@@ -61,3 +62,97 @@
stdout.write("$question: ");
return stdin.readLineSync();
}
+
+class VMServiceHeapHelperPrinter extends vmService.VMServiceHelper {
+ Future<void> printAllocationProfile(String isolateId, {String filter}) async {
+ await waitUntilIsolateIsRunnable(isolateId);
+ vmService.AllocationProfile allocationProfile =
+ await serviceClient.getAllocationProfile(isolateId);
+ for (vmService.ClassHeapStats member in allocationProfile.members) {
+ if (filter != null) {
+ if (member.classRef.name != filter) continue;
+ } else {
+ if (member.classRef.name == "") continue;
+ if (member.instancesCurrent == 0) continue;
+ }
+ vmService.Class c =
+ await serviceClient.getObject(isolateId, member.classRef.id);
+ if (c.location?.script?.uri == null) continue;
+ print("${member.classRef.name}: ${member.instancesCurrent}");
+ }
+ }
+
+ Future<void> filterAndPrintInstances(String isolateId, String filter,
+ String fieldName, Set<String> fieldValues) async {
+ await waitUntilIsolateIsRunnable(isolateId);
+ vmService.AllocationProfile allocationProfile =
+ await serviceClient.getAllocationProfile(isolateId);
+ for (vmService.ClassHeapStats member in allocationProfile.members) {
+ if (member.classRef.name != filter) continue;
+ vmService.Class c =
+ await serviceClient.getObject(isolateId, member.classRef.id);
+ if (c.location?.script?.uri == null) continue;
+ print("${member.classRef.name}: ${member.instancesCurrent}");
+ print(c.location.script.uri);
+
+ vmService.InstanceSet instances = await serviceClient.getInstances(
+ isolateId, member.classRef.id, 10000);
+ int instanceNum = 0;
+ for (vmService.ObjRef instance in instances.instances) {
+ instanceNum++;
+ vmService.Obj receivedObject =
+ await serviceClient.getObject(isolateId, instance.id);
+ if (receivedObject is! vmService.Instance) continue;
+ vmService.Instance object = receivedObject;
+ for (vmService.BoundField field in object.fields) {
+ if (field.decl.name == fieldName) {
+ if (field.value is vmService.Sentinel) continue;
+ vmService.Obj receivedValue =
+ await serviceClient.getObject(isolateId, field.value.id);
+ if (receivedValue is! vmService.Instance) continue;
+ String value = (receivedValue as vmService.Instance).valueAsString;
+ if (!fieldValues.contains(value)) continue;
+ print("${instanceNum}: ${field.decl.name}: "
+ "${value} --- ${instance.id}");
+ }
+ }
+ }
+ }
+ print("Done!");
+ }
+
+ Future<void> printRetainingPaths(String isolateId, String filter) async {
+ await waitUntilIsolateIsRunnable(isolateId);
+ vmService.AllocationProfile allocationProfile =
+ await serviceClient.getAllocationProfile(isolateId);
+ for (vmService.ClassHeapStats member in allocationProfile.members) {
+ if (member.classRef.name != filter) continue;
+ vmService.Class c =
+ await serviceClient.getObject(isolateId, member.classRef.id);
+ print("Found ${c.name} (location: ${c.location})");
+ print("${member.classRef.name}: "
+ "(instancesCurrent: ${member.instancesCurrent})");
+ print("");
+
+ vmService.InstanceSet instances = await serviceClient.getInstances(
+ isolateId, member.classRef.id, 10000);
+ print(" => Got ${instances.instances.length} instances");
+ print("");
+
+ for (vmService.ObjRef instance in instances.instances) {
+ vmService.Obj receivedObject =
+ await serviceClient.getObject(isolateId, instance.id);
+ print("Instance: $receivedObject");
+ vmService.RetainingPath retainingPath =
+ await serviceClient.getRetainingPath(isolateId, instance.id, 1000);
+ print("Retaining path: (length ${retainingPath.length}");
+ for (int i = 0; i < retainingPath.elements.length; i++) {
+ print(" [$i] = ${retainingPath.elements[i]}");
+ }
+
+ print("");
+ }
+ }
+ print("Done!");
+ }
+}
diff --git a/pkg/front_end/test/vm_service_heap_helper.dart b/pkg/front_end/test/vm_service_heap_helper.dart
index d801f3d..497c0b3 100644
--- a/pkg/front_end/test/vm_service_heap_helper.dart
+++ b/pkg/front_end/test/vm_service_heap_helper.dart
@@ -2,288 +2,11 @@
// 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:convert";
-import "dart:io";
-
-import "package:vm_service/vm_service.dart" as vmService;
-import "package:vm_service/vm_service_io.dart" as vmService;
-
import "dijkstras_sssp_algorithm.dart";
-
-class VMServiceHeapHelperBase {
- vmService.VmService _serviceClient;
- vmService.VmService get serviceClient => _serviceClient;
-
- VMServiceHeapHelperBase();
-
- Future connect(Uri observatoryUri) async {
- String path = observatoryUri.path;
- if (!path.endsWith("/")) path += "/";
- String wsUriString = 'ws://${observatoryUri.authority}${path}ws';
- _serviceClient = await vmService.vmServiceConnectUri(wsUriString,
- log: const StdOutLog());
- }
-
- Future disconnect() async {
- await _serviceClient.dispose();
- }
-
- Future<bool> waitUntilPaused(String isolateId) async {
- int nulls = 0;
- while (true) {
- bool result = await _isPaused(isolateId);
- if (result == null) {
- nulls++;
- if (nulls > 5) {
- // We've now asked for the isolate 5 times and in all cases gotten
- // `Sentinel`. Most likely things aren't working for whatever reason.
- return false;
- }
- } else if (result) {
- return true;
- } else {
- await Future.delayed(const Duration(milliseconds: 100));
- }
- }
- }
-
- Future<bool> _isPaused(String isolateId) async {
- dynamic tmp = await _serviceClient.getIsolate(isolateId);
- if (tmp is vmService.Isolate) {
- vmService.Isolate isolate = tmp;
- if (isolate.pauseEvent.kind != "Resume") return true;
- return false;
- }
- return null;
- }
-
- Future<bool> _isPausedAtStart(String isolateId) async {
- dynamic tmp = await _serviceClient.getIsolate(isolateId);
- if (tmp is vmService.Isolate) {
- vmService.Isolate isolate = tmp;
- return isolate.pauseEvent.kind == "PauseStart";
- }
- return false;
- }
-
- Future<vmService.AllocationProfile> forceGC(String isolateId) async {
- await waitUntilIsolateIsRunnable(isolateId);
- int expectGcAfter = new DateTime.now().millisecondsSinceEpoch;
- while (true) {
- vmService.AllocationProfile allocationProfile;
- try {
- allocationProfile =
- await _serviceClient.getAllocationProfile(isolateId, gc: true);
- } catch (e) {
- print(e.runtimeType);
- rethrow;
- }
- if (allocationProfile.dateLastServiceGC != null &&
- allocationProfile.dateLastServiceGC >= expectGcAfter) {
- return allocationProfile;
- }
- }
- }
-
- Future<bool> isIsolateRunnable(String isolateId) async {
- dynamic tmp = await _serviceClient.getIsolate(isolateId);
- if (tmp is vmService.Isolate) {
- vmService.Isolate isolate = tmp;
- return isolate.runnable;
- }
- return null;
- }
-
- Future<void> waitUntilIsolateIsRunnable(String isolateId) async {
- int nulls = 0;
- while (true) {
- bool result = await isIsolateRunnable(isolateId);
- if (result == null) {
- nulls++;
- if (nulls > 5) {
- // We've now asked for the isolate 5 times and in all cases gotten
- // `Sentinel`. Most likely things aren't working for whatever reason.
- return;
- }
- } else if (result) {
- return;
- } else {
- await Future.delayed(const Duration(milliseconds: 100));
- }
- }
- }
-
- Future<void> printAllocationProfile(String isolateId, {String filter}) async {
- await waitUntilIsolateIsRunnable(isolateId);
- vmService.AllocationProfile allocationProfile =
- await _serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (filter != null) {
- if (member.classRef.name != filter) continue;
- } else {
- if (member.classRef.name == "") continue;
- if (member.instancesCurrent == 0) continue;
- }
- vmService.Class c =
- await _serviceClient.getObject(isolateId, member.classRef.id);
- if (c.location?.script?.uri == null) continue;
- print("${member.classRef.name}: ${member.instancesCurrent}");
- }
- }
-
- Future<void> filterAndPrintInstances(String isolateId, String filter,
- String fieldName, Set<String> fieldValues) async {
- await waitUntilIsolateIsRunnable(isolateId);
- vmService.AllocationProfile allocationProfile =
- await _serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (member.classRef.name != filter) continue;
- vmService.Class c =
- await _serviceClient.getObject(isolateId, member.classRef.id);
- if (c.location?.script?.uri == null) continue;
- print("${member.classRef.name}: ${member.instancesCurrent}");
- print(c.location.script.uri);
-
- vmService.InstanceSet instances = await _serviceClient.getInstances(
- isolateId, member.classRef.id, 10000);
- int instanceNum = 0;
- for (vmService.ObjRef instance in instances.instances) {
- instanceNum++;
- var receivedObject =
- await _serviceClient.getObject(isolateId, instance.id);
- if (receivedObject is! vmService.Instance) continue;
- vmService.Instance object = receivedObject;
- for (vmService.BoundField field in object.fields) {
- if (field.decl.name == fieldName) {
- if (field.value is vmService.Sentinel) continue;
- var receivedValue =
- await _serviceClient.getObject(isolateId, field.value.id);
- if (receivedValue is! vmService.Instance) continue;
- String value = (receivedValue as vmService.Instance).valueAsString;
- if (!fieldValues.contains(value)) continue;
- print("${instanceNum}: ${field.decl.name}: "
- "${value} --- ${instance.id}");
- }
- }
- }
- }
- print("Done!");
- }
-
- Future<void> printRetainingPaths(String isolateId, String filter) async {
- await waitUntilIsolateIsRunnable(isolateId);
- vmService.AllocationProfile allocationProfile =
- await _serviceClient.getAllocationProfile(isolateId);
- for (vmService.ClassHeapStats member in allocationProfile.members) {
- if (member.classRef.name != filter) continue;
- vmService.Class c =
- await _serviceClient.getObject(isolateId, member.classRef.id);
- print("Found ${c.name} (location: ${c.location})");
- print("${member.classRef.name}: "
- "(instancesCurrent: ${member.instancesCurrent})");
- print("");
-
- vmService.InstanceSet instances = await _serviceClient.getInstances(
- isolateId, member.classRef.id, 10000);
- print(" => Got ${instances.instances.length} instances");
- print("");
-
- for (vmService.ObjRef instance in instances.instances) {
- var receivedObject =
- await _serviceClient.getObject(isolateId, instance.id);
- print("Instance: $receivedObject");
- vmService.RetainingPath retainingPath =
- await _serviceClient.getRetainingPath(isolateId, instance.id, 1000);
- print("Retaining path: (length ${retainingPath.length}");
- for (int i = 0; i < retainingPath.elements.length; i++) {
- print(" [$i] = ${retainingPath.elements[i]}");
- }
-
- print("");
- }
- }
- print("Done!");
- }
-
- Future<String> getIsolateId() 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;
- return isolateRef.id;
- }
-}
-
-abstract class LaunchingVMServiceHeapHelper extends VMServiceHeapHelperBase {
- Process _process;
- Process get process => _process;
-
- bool _started = false;
-
- void start(List<String> scriptAndArgs,
- {void stdinReceiver(String line),
- void stderrReceiver(String line)}) async {
- if (_started) throw "Already started";
- _started = true;
- _process = await Process.start(
- Platform.resolvedExecutable,
- ["--pause_isolates_on_start", "--enable-vm-service=0"]
- ..addAll(scriptAndArgs));
- _process.stdout
- .transform(utf8.decoder)
- .transform(new LineSplitter())
- .listen((line) {
- const kObservatoryListening = 'Observatory listening on ';
- if (line.startsWith(kObservatoryListening)) {
- Uri observatoryUri =
- Uri.parse(line.substring(kObservatoryListening.length));
- _setupAndRun(observatoryUri).catchError((e, st) {
- // Manually kill the process or it will leak,
- // see http://dartbug.com/42918
- killProcess();
- // This seems to rethrow.
- throw e;
- });
- }
- if (stdinReceiver != null) {
- stdinReceiver(line);
- } else {
- stdout.writeln("> $line");
- }
- });
- _process.stderr
- .transform(utf8.decoder)
- .transform(new LineSplitter())
- .listen((line) {
- if (stderrReceiver != null) {
- stderrReceiver(line);
- } else {
- stderr.writeln("> $line");
- }
- });
- // ignore: unawaited_futures
- _process.exitCode.then((value) {
- processExited(value);
- });
- }
-
- void processExited(int exitCode) {}
-
- void killProcess() {
- _process.kill();
- }
-
- Future _setupAndRun(Uri observatoryUri) async {
- await connect(observatoryUri);
- await run();
- }
-
- Future<void> run();
-}
+import "vm_service_helper.dart" as vmService;
class VMServiceHeapHelperSpecificExactLeakFinder
- extends LaunchingVMServiceHeapHelper {
+ extends vmService.LaunchingVMServiceHelper {
final Map<Uri, Map<String, List<String>>> _interests =
new Map<Uri, Map<String, List<String>>>();
final Map<Uri, Map<String, List<String>>> _prettyPrints =
@@ -326,7 +49,7 @@
}
void pause() async {
- await _serviceClient.pause(_isolateRef.id);
+ await serviceClient.pause(_isolateRef.id);
}
vmService.VM _vm;
@@ -336,7 +59,7 @@
/// Best effort check if the isolate is idle.
Future<bool> isIdle() async {
- dynamic tmp = await _serviceClient.getIsolate(_isolateRef.id);
+ dynamic tmp = await serviceClient.getIsolate(_isolateRef.id);
if (tmp is vmService.Isolate) {
vmService.Isolate isolate = tmp;
return isolate.pauseEvent.topFrame == null;
@@ -346,15 +69,15 @@
@override
Future<void> run() async {
- _vm = await _serviceClient.getVM();
+ _vm = await serviceClient.getVM();
if (_vm.isolates.length != 1) {
throw "Expected 1 isolate, got ${_vm.isolates.length}";
}
_isolateRef = _vm.isolates.single;
await forceGC(_isolateRef.id);
- assert(await _isPausedAtStart(_isolateRef.id));
- await _serviceClient.resume(_isolateRef.id);
+ assert(await isPausedAtStart(_isolateRef.id));
+ await serviceClient.resume(_isolateRef.id);
_iterationNumber = 1;
while (true) {
@@ -364,7 +87,7 @@
vmService.HeapSnapshotGraph heapSnapshotGraph =
await vmService.HeapSnapshotGraph.getSnapshot(
- _serviceClient, _isolateRef);
+ serviceClient, _isolateRef);
Set<String> duplicatePrints = {};
Map<String, List<vmService.HeapSnapshotObject>> groupedByToString = {};
@@ -396,7 +119,7 @@
}
}
- await _serviceClient.resume(_isolateRef.id);
+ await serviceClient.resume(_isolateRef.id);
_iterationNumber++;
}
}
@@ -695,20 +418,6 @@
Interest(this.uri, this.className, this.fieldNames);
}
-class StdOutLog implements vmService.Log {
- const StdOutLog();
-
- @override
- void severe(String message) {
- print("> SEVERE: $message");
- }
-
- @override
- void warning(String message) {
- print("> WARNING: $message");
- }
-}
-
HeapGraph convertHeapGraph(vmService.HeapSnapshotGraph graph) {
HeapGraphClassSentinel classSentinel = new HeapGraphClassSentinel();
List<HeapGraphClassActual> classes =
diff --git a/pkg/front_end/test/vm_service_helper.dart b/pkg/front_end/test/vm_service_helper.dart
new file mode 100644
index 0000000..a80eb5a
--- /dev/null
+++ b/pkg/front_end/test/vm_service_helper.dart
@@ -0,0 +1,215 @@
+// 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:convert";
+import "dart:io";
+
+import "package:vm_service/vm_service.dart" as vmService;
+import "package:vm_service/vm_service_io.dart" as vmService;
+
+export "package:vm_service/vm_service.dart";
+export "package:vm_service/vm_service_io.dart";
+
+class VMServiceHelper {
+ vmService.VmService _serviceClient;
+ vmService.VmService get serviceClient => _serviceClient;
+
+ VMServiceHelper();
+
+ Future connect(Uri observatoryUri) async {
+ String path = observatoryUri.path;
+ if (!path.endsWith("/")) path += "/";
+ String wsUriString = 'ws://${observatoryUri.authority}${path}ws';
+ _serviceClient = await vmService.vmServiceConnectUri(wsUriString,
+ log: const StdOutLog());
+ }
+
+ Future disconnect() async {
+ await _serviceClient.dispose();
+ }
+
+ Future<bool> waitUntilPaused(String isolateId) async {
+ int nulls = 0;
+ while (true) {
+ bool result = await isPaused(isolateId);
+ if (result == null) {
+ nulls++;
+ if (nulls > 5) {
+ // We've now asked for the isolate 5 times and in all cases gotten
+ // `Sentinel`. Most likely things aren't working for whatever reason.
+ return false;
+ }
+ } else if (result) {
+ return true;
+ } else {
+ await Future.delayed(const Duration(milliseconds: 100));
+ }
+ }
+ }
+
+ Future<bool> isPaused(String isolateId) async {
+ dynamic tmp = await _serviceClient.getIsolate(isolateId);
+ if (tmp is vmService.Isolate) {
+ vmService.Isolate isolate = tmp;
+ if (isolate.pauseEvent.kind != "Resume") return true;
+ return false;
+ }
+ return null;
+ }
+
+ Future<bool> isPausedAtStart(String isolateId) async {
+ dynamic tmp = await _serviceClient.getIsolate(isolateId);
+ if (tmp is vmService.Isolate) {
+ vmService.Isolate isolate = tmp;
+ return isolate.pauseEvent.kind == "PauseStart";
+ }
+ return false;
+ }
+
+ Future<bool> isPausedAtExit(String isolateId) async {
+ dynamic tmp = await _serviceClient.getIsolate(isolateId);
+ if (tmp is vmService.Isolate) {
+ vmService.Isolate isolate = tmp;
+ return isolate.pauseEvent.kind == "PauseExit";
+ }
+ return false;
+ }
+
+ Future<vmService.AllocationProfile> forceGC(String isolateId) async {
+ await waitUntilIsolateIsRunnable(isolateId);
+ int expectGcAfter = new DateTime.now().millisecondsSinceEpoch;
+ while (true) {
+ vmService.AllocationProfile allocationProfile;
+ try {
+ allocationProfile =
+ await _serviceClient.getAllocationProfile(isolateId, gc: true);
+ } catch (e) {
+ print(e.runtimeType);
+ rethrow;
+ }
+ if (allocationProfile.dateLastServiceGC != null &&
+ allocationProfile.dateLastServiceGC >= expectGcAfter) {
+ return allocationProfile;
+ }
+ }
+ }
+
+ Future<bool> isIsolateRunnable(String isolateId) async {
+ dynamic tmp = await _serviceClient.getIsolate(isolateId);
+ if (tmp is vmService.Isolate) {
+ vmService.Isolate isolate = tmp;
+ return isolate.runnable;
+ }
+ return null;
+ }
+
+ Future<void> waitUntilIsolateIsRunnable(String isolateId) async {
+ int nulls = 0;
+ while (true) {
+ bool result = await isIsolateRunnable(isolateId);
+ if (result == null) {
+ nulls++;
+ if (nulls > 5) {
+ // We've now asked for the isolate 5 times and in all cases gotten
+ // `Sentinel`. Most likely things aren't working for whatever reason.
+ return;
+ }
+ } else if (result) {
+ return;
+ } else {
+ await Future.delayed(const Duration(milliseconds: 100));
+ }
+ }
+ }
+
+ Future<String> getIsolateId() 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;
+ return isolateRef.id;
+ }
+}
+
+class StdOutLog implements vmService.Log {
+ const StdOutLog();
+
+ @override
+ void severe(String message) {
+ print("> SEVERE: $message");
+ }
+
+ @override
+ void warning(String message) {
+ print("> WARNING: $message");
+ }
+}
+
+abstract class LaunchingVMServiceHelper extends VMServiceHelper {
+ Process _process;
+ Process get process => _process;
+
+ bool _started = false;
+
+ void start(List<String> scriptAndArgs,
+ {void stdinReceiver(String line),
+ void stderrReceiver(String line)}) async {
+ if (_started) throw "Already started";
+ _started = true;
+ _process = await Process.start(
+ Platform.resolvedExecutable,
+ ["--pause_isolates_on_start", "--enable-vm-service=0"]
+ ..addAll(scriptAndArgs));
+ _process.stdout
+ .transform(utf8.decoder)
+ .transform(new LineSplitter())
+ .listen((line) {
+ const kObservatoryListening = 'Observatory listening on ';
+ if (line.startsWith(kObservatoryListening)) {
+ Uri observatoryUri =
+ Uri.parse(line.substring(kObservatoryListening.length));
+ _setupAndRun(observatoryUri).catchError((e, st) {
+ // Manually kill the process or it will leak,
+ // see http://dartbug.com/42918
+ killProcess();
+ // This seems to rethrow.
+ throw e;
+ });
+ }
+ if (stdinReceiver != null) {
+ stdinReceiver(line);
+ } else {
+ stdout.writeln("> $line");
+ }
+ });
+ _process.stderr
+ .transform(utf8.decoder)
+ .transform(new LineSplitter())
+ .listen((line) {
+ if (stderrReceiver != null) {
+ stderrReceiver(line);
+ } else {
+ stderr.writeln("> $line");
+ }
+ });
+ // ignore: unawaited_futures
+ _process.exitCode.then((value) {
+ processExited(value);
+ });
+ }
+
+ void processExited(int exitCode) {}
+
+ void killProcess() {
+ _process.kill();
+ }
+
+ Future _setupAndRun(Uri observatoryUri) async {
+ await connect(observatoryUri);
+ await run();
+ }
+
+ Future<void> run();
+}