blob: 6fb49d91cbb305e5b21e3c7489133fb16a7522d1 [file] [log] [blame]
// 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.
// @dart = 2.9
import "dart:io";
import "vm_service_helper.dart" as vmService;
class Foo {
final String x;
final int y;
Foo(this.x, this.y);
}
main(List<String> args) async {
String connectTo;
String classToFind;
String whatToDo;
for (String arg in args) {
if (arg.startsWith("--url=")) {
connectTo = arg.substring("--url=".length);
} else if (arg.startsWith("--find=")) {
classToFind = arg.substring("--find=".length);
} else if (arg.startsWith("--action=")) {
whatToDo = arg.substring("--action=".length);
}
}
List<Foo> foos = [];
foos.add(new Foo("hello", 42));
foos.add(new Foo("world", 43));
foos.add(new Foo("!", 44));
if (connectTo == null) connectTo = ask("Connect to");
VMServiceHeapHelperPrinter vm = VMServiceHeapHelperPrinter();
await vm.connect(Uri.parse(connectTo.trim()));
String isolateId = await vm.getIsolateId();
if (classToFind == null) classToFind = ask("Find what class");
if (whatToDo == null) whatToDo = ask("What to do? (filter/retainingpath)");
if (whatToDo == "retainingpath") {
await vm.printRetainingPaths(isolateId, classToFind);
} else {
await vm.printAllocationProfile(isolateId, filter: classToFind);
String fieldToFilter = ask("Filter on what field");
Set<String> fieldValues = {};
while (true) {
String fieldValue = ask("Look for value in field (empty to stop)");
if (fieldValue == "") break;
fieldValues.add(fieldValue);
}
await vm.filterAndPrintInstances(
isolateId, classToFind, fieldToFilter, fieldValues);
}
await vm.disconnect();
print("Disconnect done!");
}
String ask(String question) {
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!");
}
}