blob: 0548d47a15b008fa85fc24c8645953f5de835240 [file] [log] [blame]
// Copyright (c) 2023, 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:developer" as developer;
import 'package:front_end/src/api_prototype/file_system.dart' as api;
import 'package:_fe_analyzer_shared/src/util/filenames.dart';
import 'package:front_end/src/fasta/dill/dill_target.dart';
import 'package:front_end/src/fasta/kernel/kernel_target.dart';
import 'package:front_end/src/fasta/kernel/macro/macro.dart';
import 'package:front_end/src/fasta/uri_translator.dart';
import 'package:kernel/canonical_name.dart';
import 'package:vm_service/vm_service.dart' as vmService;
import "package:vm_service/vm_service_io.dart" as vmServiceIo;
import 'compiler_test_helper.dart';
import 'vm_service_helper.dart';
Future<void> main(List<String> args) async {
args = args.toList();
bool compileSdk = !args.remove('--no-sdk');
developer.ServiceProtocolInfo serviceProtocolInfo =
await developer.Service.getInfo();
bool startedServiceProtocol = false;
if (serviceProtocolInfo.serverUri == null) {
startedServiceProtocol = true;
serviceProtocolInfo = await developer.Service.controlWebServer(
enable: true, silenceOutput: true);
}
Uri? serverUri = serviceProtocolInfo.serverUri;
if (serverUri == null) {
throw "Couldn't get service protocol url.";
}
String path = serverUri.path;
if (!path.endsWith('/')) path += '/';
String wsUriString = 'ws://${serverUri.authority}${path}ws';
VmService serviceClient = await vmServiceIo.vmServiceConnectUri(wsUriString);
await compile(
inputs: args.isNotEmpty
? args.map(nativeToUri).toList()
: [
Uri.base
.resolve('pkg/front_end/test/token_leak_test_helper.dart'),
],
compileSdk: compileSdk,
kernelTargetCreator: (api.FileSystem fileSystem,
bool includeComments,
DillTarget dillTarget,
UriTranslator uriTranslator,
BodyBuilderCreator bodyBuilderCreator) {
return new KernelTargetTester(fileSystem, includeComments, dillTarget,
uriTranslator, bodyBuilderCreator, serviceClient);
});
await serviceClient.dispose();
if (startedServiceProtocol) {
await developer.Service.controlWebServer(
enable: false, silenceOutput: true);
}
}
class KernelTargetTester extends KernelTargetTest {
final VmService serviceClient;
// TODO(johnniwinther): Can we programmatically find all subclasses of [Token]
// instead?
static const String className = 'StringTokenImpl';
KernelTargetTester(
api.FileSystem fileSystem,
bool includeComments,
DillTarget dillTarget,
UriTranslator uriTranslator,
BodyBuilderCreator bodyBuilderCreator,
this.serviceClient)
: super(fileSystem, includeComments, dillTarget, uriTranslator,
bodyBuilderCreator);
@override
Future<BuildResult> buildOutlines({CanonicalName? nameRoot}) async {
BuildResult buildResult = await super.buildOutlines(nameRoot: nameRoot);
print('buildOutlines complete');
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;
String isolateId = isolateRef.id!;
int foundInstances =
await findAndPrintRetainingPaths(serviceClient, isolateId, className);
if (foundInstances > 0) {
throw 'Found $foundInstances instances of $className after '
'buildOutlines';
}
return buildResult;
}
@override
Future<BuildResult> buildComponent(
{required MacroApplications? macroApplications,
bool verify = false,
bool allowVerificationErrorForTesting = false}) async {
BuildResult buildResult = await super.buildComponent(
macroApplications: macroApplications,
verify: verify,
allowVerificationErrorForTesting: allowVerificationErrorForTesting);
print('buildComponent complete');
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;
String isolateId = isolateRef.id!;
int foundInstances =
await findAndPrintRetainingPaths(serviceClient, isolateId, className);
if (foundInstances > 0) {
throw 'Found $foundInstances instances of $className after '
'buildComponent';
}
return buildResult;
}
}
Future<int> findAndPrintRetainingPaths(
vmService.VmService serviceClient, String isolateId, String filter) async {
vmService.AllocationProfile allocationProfile =
await serviceClient.getAllocationProfile(isolateId, gc: true);
int foundInstances = 0;
for (vmService.ClassHeapStats member in allocationProfile.members!) {
if (member.classRef!.name != filter) continue;
vmService.Class c = await serviceClient.getObject(
isolateId, member.classRef!.id!) as vmService.Class;
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!, 100);
foundInstances += instances.instances!.length;
print(" => Got ${instances.instances!.length} instances");
print("");
for (vmService.ObjRef instance in instances.instances!) {
try {
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})");
String indent = '';
for (int i = retainingPath.elements!.length - 1; i >= 0; i--) {
vmService.RetainingObject retainingObject =
retainingPath.elements![i];
vmService.ObjRef? value = retainingObject.value;
String field;
if (retainingObject.parentListIndex != null) {
field = '[${retainingObject.parentListIndex}]';
} else if (retainingObject.parentMapKey != null) {
field = '[?]';
} else if (retainingObject.parentField != null) {
field = '.${retainingObject.parentField}';
} else {
field = '';
}
String className = '';
if (value is vmService.InstanceRef) {
vmService.ClassRef? classRef = value.classRef;
if (classRef != null && classRef.name != null) {
className = classRef.name!;
}
}
print("${indent}${className}$field");
indent += ' ';
}
print("");
} catch (_) {
// Suppress errors.
}
}
}
print("Done!");
return foundInstances;
}