@@ -10,14 +10,21 @@ import 'package:front_end/src/fasta/dill/dill_target.dart';
10
10
import 'package:front_end/src/fasta/kernel/kernel_target.dart' ;
11
11
import 'package:front_end/src/fasta/kernel/macro/macro.dart' ;
12
12
import 'package:front_end/src/fasta/uri_translator.dart' ;
13
- import 'package:kernel/canonical_name .dart' ;
13
+ import 'package:kernel/ast .dart' show CanonicalName, Class ;
14
14
import 'package:vm_service/vm_service.dart' as vmService;
15
15
import "package:vm_service/vm_service_io.dart" as vmServiceIo;
16
16
17
17
import 'compiler_test_helper.dart' ;
18
- import 'vm_service_helper.dart' ;
18
+
19
+ import 'find_all_subclasses_tool.dart' ;
19
20
20
21
Future <void > main (List <String > args) async {
22
+ Set <Class > allTokenClasses = await getAllTokens ();
23
+ Map <String , List <Uri >> classesInUris = {};
24
+ for (Class c in allTokenClasses) {
25
+ (classesInUris[c.name] ?? = []).add (c.fileUri);
26
+ }
27
+
21
28
args = args.toList ();
22
29
bool compileSdk = ! args.remove ('--no-sdk' );
23
30
developer.ServiceProtocolInfo serviceProtocolInfo =
@@ -36,7 +43,8 @@ Future<void> main(List<String> args) async {
36
43
String path = serverUri.path;
37
44
if (! path.endsWith ('/' )) path += '/' ;
38
45
String wsUriString = 'ws://${serverUri .authority }${path }ws' ;
39
- VmService serviceClient = await vmServiceIo.vmServiceConnectUri (wsUriString);
46
+ vmService.VmService serviceClient =
47
+ await vmServiceIo.vmServiceConnectUri (wsUriString);
40
48
41
49
await compile (
42
50
inputs: args.isNotEmpty
@@ -52,7 +60,7 @@ Future<void> main(List<String> args) async {
52
60
UriTranslator uriTranslator,
53
61
BodyBuilderCreator bodyBuilderCreator) {
54
62
return new KernelTargetTester (fileSystem, includeComments, dillTarget,
55
- uriTranslator, bodyBuilderCreator, serviceClient);
63
+ uriTranslator, bodyBuilderCreator, serviceClient, classesInUris );
56
64
});
57
65
58
66
await serviceClient.dispose ();
@@ -64,20 +72,18 @@ Future<void> main(List<String> args) async {
64
72
}
65
73
66
74
class KernelTargetTester extends KernelTargetTest {
67
- final VmService serviceClient;
68
-
69
- // TODO(johnniwinther): Can we programmatically find all subclasses of [Token]
70
- // instead?
71
- static const String className = 'StringTokenImpl' ;
75
+ final vmService.VmService serviceClient;
76
+ final Map <String , List <Uri >> classesInUris;
72
77
73
78
KernelTargetTester (
74
- api.FileSystem fileSystem,
75
- bool includeComments,
76
- DillTarget dillTarget,
77
- UriTranslator uriTranslator,
78
- BodyBuilderCreator bodyBuilderCreator,
79
- this .serviceClient)
80
- : super (fileSystem, includeComments, dillTarget, uriTranslator,
79
+ api.FileSystem fileSystem,
80
+ bool includeComments,
81
+ DillTarget dillTarget,
82
+ UriTranslator uriTranslator,
83
+ BodyBuilderCreator bodyBuilderCreator,
84
+ this .serviceClient,
85
+ this .classesInUris,
86
+ ) : super (fileSystem, includeComments, dillTarget, uriTranslator,
81
87
bodyBuilderCreator);
82
88
83
89
@override
@@ -92,12 +98,11 @@ class KernelTargetTester extends KernelTargetTest {
92
98
93
99
String isolateId = isolateRef.id! ;
94
100
95
- int foundInstances =
96
- await findAndPrintRetainingPaths (serviceClient, isolateId, className);
97
- if (foundInstances > 0 ) {
98
- throw 'Found $foundInstances instances of $className after '
99
- 'buildOutlines' ;
100
- }
101
+ throwOnLeaksOrNoFinds (
102
+ await findAndPrintRetainingPaths (
103
+ serviceClient, isolateId, classesInUris),
104
+ "buildOutlines" ,
105
+ classesInUris);
101
106
return buildResult;
102
107
}
103
108
@@ -119,35 +124,96 @@ class KernelTargetTester extends KernelTargetTest {
119
124
120
125
String isolateId = isolateRef.id! ;
121
126
122
- int foundInstances =
123
- await findAndPrintRetainingPaths (serviceClient, isolateId, className);
124
- if (foundInstances > 0 ) {
125
- throw 'Found $foundInstances instances of $className after '
126
- 'buildComponent' ;
127
- }
127
+ throwOnLeaksOrNoFinds (
128
+ await findAndPrintRetainingPaths (
129
+ serviceClient, isolateId, classesInUris),
130
+ "buildComponent" ,
131
+ classesInUris);
128
132
return buildResult;
129
133
}
130
134
}
131
135
132
- Future <int > findAndPrintRetainingPaths (
133
- vmService.VmService serviceClient, String isolateId, String filter) async {
136
+ void throwOnLeaksOrNoFinds (Map <vmService.Class , int > foundInstances,
137
+ String afterWhat, Map <String , List <Uri >> classesInUris) {
138
+ Map <String , List <Uri >> notFound = {};
139
+ for (MapEntry <String , List <Uri >> entry in classesInUris.entries) {
140
+ notFound[entry.key] = new List .of (entry.value);
141
+ }
142
+ StringBuffer ? sb;
143
+ for (MapEntry <vmService.Class , int > entry in foundInstances.entries) {
144
+ List <Uri > notFoundUrisForName = notFound[entry.key.name! ]! ;
145
+ Uri uri = Uri .parse (entry.key.location! .script! .uri! );
146
+ for (int i = 0 ; i < notFoundUrisForName.length; i++ ) {
147
+ if (notFoundUrisForName[i].pathSegments.last == uri.pathSegments.last) {
148
+ notFoundUrisForName.removeAt (i);
149
+ break ;
150
+ }
151
+ }
152
+ if (entry.value > 0 ) {
153
+ // 'SyntheticToken' will have 1 alive because of dummyToken in
154
+ // front_end/lib/src/fasta/kernel/utils.dart. Hack around that.
155
+ if (entry.key.name == "SyntheticToken" && entry.value == 1 ) {
156
+ continue ;
157
+ }
158
+ sb ?? = new StringBuffer ();
159
+ sb.writeln ('Found ${entry .value } instances of ${entry .key } '
160
+ 'after $afterWhat ' );
161
+ }
162
+ }
163
+ if (sb != null ) {
164
+ throw sb.toString ();
165
+ }
166
+ if (foundInstances.isEmpty) {
167
+ throw "Didn't find anything." ;
168
+ }
169
+ for (MapEntry <String , List <Uri >> notFoundData in notFound.entries) {
170
+ if (notFoundData.value.isNotEmpty) {
171
+ print ("WARNING: Didn't find ${notFoundData .key }' in "
172
+ "${notFoundData .value .join (" and " )}" );
173
+ }
174
+ }
175
+ }
176
+
177
+ Future <Map <vmService.Class , int >> findAndPrintRetainingPaths (
178
+ vmService.VmService serviceClient,
179
+ String isolateId,
180
+ Map <String , List <Uri >> classesInUrisFilter) async {
134
181
vmService.AllocationProfile allocationProfile =
135
182
await serviceClient.getAllocationProfile (isolateId, gc: true );
136
183
137
- int foundInstances = 0 ;
184
+ Map <vmService. Class , int > foundInstances = {} ;
138
185
139
186
for (vmService.ClassHeapStats member in allocationProfile.members! ) {
140
- if (member.classRef! .name != filter) continue ;
187
+ String ? className = member.classRef! .name;
188
+ if (className == null ) continue ;
189
+ List <Uri >? uris = classesInUrisFilter[className];
190
+ if (uris == null ) continue ;
191
+ // File uris vs package uris --- for now just compare the filename.
192
+ String ? classUriString = member.classRef? .location? .script? .uri;
193
+ if (classUriString == null ) continue ;
194
+ Uri classUri = Uri .parse (classUriString);
195
+ bool foundMatch = false ;
196
+ for (Uri uri in uris) {
197
+ if (classUri.pathSegments.last == uri.pathSegments.last) {
198
+ foundMatch = true ;
199
+ break ;
200
+ }
201
+ }
202
+ if (! foundMatch) continue ;
141
203
vmService.Class c = await serviceClient.getObject (
142
204
isolateId, member.classRef! .id! ) as vmService.Class ;
205
+ int ? instancesCurrent = member.instancesCurrent;
206
+ if (instancesCurrent == null ) continue ;
207
+ foundInstances[c] = instancesCurrent;
208
+ if (instancesCurrent == 0 ) continue ;
209
+
143
210
print ("Found ${c .name } (location: ${c .location })" );
144
211
print ("${member .classRef !.name }: "
145
212
"(instancesCurrent: ${member .instancesCurrent })" );
146
213
print ("" );
147
214
148
215
vmService.InstanceSet instances =
149
216
await serviceClient.getInstances (isolateId, member.classRef! .id! , 100 );
150
- foundInstances += instances.instances! .length;
151
217
print (" => Got ${instances .instances !.length } instances" );
152
218
print ("" );
153
219
@@ -160,29 +226,34 @@ Future<int> findAndPrintRetainingPaths(
160
226
await serviceClient.getRetainingPath (isolateId, instance.id! , 1000 );
161
227
162
228
print ("Retaining path: (length ${retainingPath .length })" );
163
- String indent = '' ;
229
+ print (retainingPath.gcRootType);
230
+ String indent = ' ' ;
164
231
for (int i = retainingPath.elements! .length - 1 ; i >= 0 ; i-- ) {
165
232
vmService.RetainingObject retainingObject =
166
233
retainingPath.elements! [i];
167
234
vmService.ObjRef ? value = retainingObject.value;
168
- String field;
169
- if (retainingObject.parentListIndex != null ) {
170
- field = '[${retainingObject .parentListIndex }]' ;
171
- } else if (retainingObject.parentMapKey != null ) {
172
- field = '[?]' ;
173
- } else if (retainingObject.parentField != null ) {
174
- field = '.${retainingObject .parentField }' ;
235
+ if (value is vmService.FieldRef ) {
236
+ print ("${indent }field '${value .name }'" );
175
237
} else {
176
- field = '' ;
177
- }
178
- String className = '' ;
179
- if (value is vmService.InstanceRef ) {
180
- vmService.ClassRef ? classRef = value.classRef;
181
- if (classRef != null && classRef.name != null ) {
182
- className = classRef.name! ;
238
+ String field;
239
+ if (retainingObject.parentListIndex != null ) {
240
+ field = '[${retainingObject .parentListIndex }]' ;
241
+ } else if (retainingObject.parentMapKey != null ) {
242
+ field = '[?]' ;
243
+ } else if (retainingObject.parentField != null ) {
244
+ field = '.${retainingObject .parentField }' ;
245
+ } else {
246
+ field = '' ;
247
+ }
248
+ String className = '' ;
249
+ if (value is vmService.InstanceRef ) {
250
+ vmService.ClassRef ? classRef = value.classRef;
251
+ if (classRef != null && classRef.name != null ) {
252
+ className = 'class ${classRef .name }' ;
253
+ }
183
254
}
255
+ print ("${indent }${className }$field " );
184
256
}
185
- print ("${indent }${className }$field " );
186
257
indent += ' ' ;
187
258
}
188
259
0 commit comments