OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 library analysis_server.src.status.memory_use; | |
6 | |
7 import 'dart:collection'; | |
8 | |
9 import 'package:analysis_server/src/analysis_server.dart'; | |
10 import 'package:analyzer/dart/ast/ast.dart'; | |
11 import 'package:analyzer/dart/ast/visitor.dart'; | |
12 import 'package:analyzer/dart/element/element.dart'; | |
13 import 'package:analyzer/dart/element/visitor.dart'; | |
14 import 'package:analyzer/src/context/cache.dart'; | |
15 import 'package:analyzer/src/context/context.dart' show AnalysisContextImpl; | |
16 import 'package:analyzer/src/dart/element/element.dart'; | |
17 import 'package:analyzer/src/generated/engine.dart'; | |
18 import 'package:analyzer/src/generated/sdk.dart'; | |
19 import 'package:analyzer/task/dart.dart'; | |
20 import 'package:analyzer/task/model.dart'; | |
21 | |
22 /** | |
23 * A visitor that will count the number of instances of each type of AST node. | |
24 */ | |
25 class AstNodeCounter extends UnifyingAstVisitor<Null> { | |
26 /** | |
27 * A table mapping the types of the AST nodes to the number of instances | |
28 * visited. | |
29 */ | |
30 final Map<Type, int> nodeCounts; | |
31 | |
32 /** | |
33 * Initialize a newly created counter to increment the counts in the given map | |
34 * of [nodeCounts]. | |
35 */ | |
36 AstNodeCounter(this.nodeCounts); | |
37 | |
38 @override | |
39 visitNode(AstNode node) { | |
40 Type type = node.runtimeType; | |
41 int count = nodeCounts[type] ?? 0; | |
42 nodeCounts[type] = count + 1; | |
43 super.visitNode(node); | |
44 } | |
45 } | |
46 | |
47 /** | |
48 * A visitor that will count the number of instances of each type of element. | |
49 */ | |
50 class ElementCounter extends GeneralizingElementVisitor<Null> { | |
51 /** | |
52 * A table mapping the types of the elements to the number of instances | |
53 * visited. | |
54 */ | |
55 final Map<Type, int> elementCounts; | |
56 | |
57 /** | |
58 * A table mapping the types of the AST nodes to the number of instances | |
59 * visited. | |
60 */ | |
61 final Map<Type, int> nodeCounts; | |
62 | |
63 /** | |
64 * Initialize a newly created counter to increment the counts in the given map | |
65 * of [elementCounts]. | |
66 */ | |
67 ElementCounter(this.elementCounts, this.nodeCounts); | |
68 | |
69 @override | |
70 visitConstructorElement(ConstructorElement element) { | |
71 if (element is ConstructorElementImpl) { | |
72 List<ConstructorInitializer> initializers = element.constantInitializers; | |
73 if (initializers != null) { | |
74 initializers.forEach((ConstructorInitializer initializer) { | |
75 _countNodes(initializer); | |
76 }); | |
77 } | |
78 } | |
79 visitElement(element); | |
80 } | |
81 | |
82 @override | |
83 visitElement(Element element) { | |
84 Type type = element.runtimeType; | |
85 int count = elementCounts[type] ?? 0; | |
86 elementCounts[type] = count + 1; | |
87 element.metadata.forEach((ElementAnnotation annotation) { | |
88 if (annotation is ElementAnnotationImpl) { | |
89 _countNodes(annotation.annotationAst); | |
90 } | |
91 }); | |
92 super.visitElement(element); | |
93 } | |
94 | |
95 visitFieldElement(FieldElement element) { | |
96 if (element is ConstVariableElement) { | |
97 _countInitializer(element as ConstVariableElement); | |
98 } | |
99 visitElement(element); | |
100 } | |
101 | |
102 visitLocalVariableElement(LocalVariableElement element) { | |
103 if (element is ConstVariableElement) { | |
104 _countInitializer(element as ConstVariableElement); | |
105 } | |
106 visitElement(element); | |
107 } | |
108 | |
109 visitParameterElement(ParameterElement element) { | |
110 if (element is ConstVariableElement) { | |
111 _countInitializer(element as ConstVariableElement); | |
112 } | |
113 visitElement(element); | |
114 } | |
115 | |
116 visitTopLevelVariableElement(TopLevelVariableElement element) { | |
117 if (element is ConstVariableElement) { | |
118 _countInitializer(element as ConstVariableElement); | |
119 } | |
120 visitElement(element); | |
121 } | |
122 | |
123 void _countInitializer(ConstVariableElement element) { | |
124 _countNodes(element.constantInitializer); | |
125 } | |
126 | |
127 void _countNodes(AstNode node) { | |
128 if (node != null) { | |
129 node.accept(new AstNodeCounter(nodeCounts)); | |
130 } | |
131 } | |
132 } | |
133 | |
134 /** | |
135 * A set used when the number of instances of some type is too large to be kept. | |
136 */ | |
137 class InfiniteSet implements Set { | |
138 /** | |
139 * The unique instance of this class. | |
140 */ | |
141 static final InfiniteSet instance = new InfiniteSet(); | |
142 | |
143 @override | |
144 int get length => -1; | |
145 | |
146 @override | |
147 dynamic noSuchMethod(Invocation invocation) { | |
148 throw new UnsupportedError('Do not use instances of InfiniteSet'); | |
149 } | |
150 } | |
151 | |
152 /** | |
153 * Computes memory usage data by traversing the data structures reachable from | |
154 * an analysis server. | |
155 */ | |
156 class MemoryUseData { | |
157 /** | |
158 * The maximum size of an instance set. | |
159 */ | |
160 static const int maxInstanceSetSize = 1000000; | |
161 | |
162 /** | |
163 * A table mapping classes to instances of the class. | |
164 */ | |
165 Map<Type, Set> instances = new HashMap<Type, Set>(); | |
166 | |
167 /** | |
168 * A set of all the library specific units, using equality rather than | |
169 * identity in order to determine whether re-using equal instances would save | |
170 * significant space. | |
171 */ | |
172 Set<LibrarySpecificUnit> uniqueLSUs = new HashSet<LibrarySpecificUnit>(); | |
173 | |
174 /** | |
175 * A set of all the targeted results, using equality rather than identity in | |
176 * order to determine whether re-using equal instances would save significant | |
177 * space. | |
178 */ | |
179 Set<TargetedResult> uniqueTargetedResults = new HashSet<TargetedResult>(); | |
180 | |
181 /** | |
182 * A table mapping the types of AST nodes to the number of instances being | |
183 * held directly (as values in the cache). | |
184 */ | |
185 Map<Type, int> directNodeCounts = new HashMap<Type, int>(); | |
186 | |
187 /** | |
188 * A table mapping the types of AST nodes to the number of instances being | |
189 * held indirectly (such as nodes reachable from element models). | |
190 */ | |
191 Map<Type, int> indirectNodeCounts = new HashMap<Type, int>(); | |
192 | |
193 /** | |
194 * A table mapping the types of the elements to the number of instances being | |
195 * held directly (as values in the cache). | |
196 */ | |
197 final Map<Type, int> elementCounts = new HashMap<Type, int>(); | |
198 | |
199 /** | |
200 * Initialize a newly created instance. | |
201 */ | |
202 MemoryUseData(); | |
203 | |
204 /** | |
205 * Traverse an analysis [server] to compute memory usage data. | |
206 */ | |
207 void processAnalysisServer(AnalysisServer server) { | |
208 _recordInstance(server); | |
209 Iterable<AnalysisContext> contexts = server.analysisContexts; | |
210 for (AnalysisContextImpl context in contexts) { | |
211 _processAnalysisContext(context); | |
212 } | |
213 DartSdkManager manager = server.sdkManager; | |
214 List<SdkDescription> descriptors = manager.sdkDescriptors; | |
215 for (SdkDescription descriptor in descriptors) { | |
216 _processAnalysisContext(manager.getSdk(descriptor, () => null).context); | |
217 } | |
218 } | |
219 | |
220 void _processAnalysisContext(AnalysisContextImpl context) { | |
221 _recordInstance(context); | |
222 _recordInstance(context.analysisCache); | |
223 Map<AnalysisTarget, CacheEntry> map = | |
224 context.privateAnalysisCachePartition.entryMap; | |
225 map.forEach((AnalysisTarget target, CacheEntry entry) { | |
226 _processAnalysisTarget(target); | |
227 _processCacheEntry(entry); | |
228 }); | |
229 } | |
230 | |
231 void _processAnalysisTarget(AnalysisTarget target) { | |
232 _recordInstance(target); | |
233 } | |
234 | |
235 void _processCacheEntry(CacheEntry entry) { | |
236 _recordInstance(entry); | |
237 List<ResultDescriptor> descriptors = entry.nonInvalidResults; | |
238 for (ResultDescriptor descriptor in descriptors) { | |
239 _recordInstance(descriptor); | |
240 _processResultData(entry.getResultDataOrNull(descriptor)); | |
241 } | |
242 } | |
243 | |
244 void _processResultData(ResultData resultData) { | |
245 _recordInstance(resultData); | |
246 if (resultData != null) { | |
scheglov
2016/07/11 15:27:49
_recordInstance throws if resultData is null.
Brian Wilkerson
2016/07/11 17:26:36
I don't think that's true. It will invoke runtimeT
| |
247 _recordInstance(resultData.state); | |
248 _recordInstance(resultData.value, onFirstOccurrence: (Object object) { | |
249 if (object is AstNode) { | |
250 object.accept(new AstNodeCounter(directNodeCounts)); | |
251 } else if (object is Element) { | |
252 object.accept(new ElementCounter(elementCounts, indirectNodeCounts)); | |
253 } | |
254 }); | |
255 resultData.dependedOnResults.forEach(_processTargetedResult); | |
256 resultData.dependentResults.forEach(_processTargetedResult); | |
257 } | |
258 } | |
259 | |
260 void _processTargetedResult(TargetedResult result) { | |
261 _recordInstance(result); | |
262 uniqueTargetedResults.add(result); | |
263 _recordInstance(result.target); | |
264 _recordInstance(result.result); | |
265 } | |
266 | |
267 /** | |
268 * Record the given [instance] that was found. If this is the first time that | |
269 * the instance has been found, execute the [onFirstOccurrence] function. | |
270 * | |
271 * Note that instances will not be recorded if there are more than | |
272 * [maxInstanceSetSize] instances of the same type, and that the | |
273 * [onFirstOccurrence] function will not be executed if the instance is not | |
274 * recorded. | |
275 */ | |
276 void _recordInstance(Object instance, | |
277 {void onFirstOccurrence(Object object)}) { | |
278 Type type = instance.runtimeType; | |
279 Set instanceSet = | |
280 instances.putIfAbsent(type, () => new HashSet(equals: identical)); | |
scheglov
2016/07/11 15:27:49
Or just new HashSet.identity()
Brian Wilkerson
2016/07/11 17:26:36
Done.
| |
281 if (instanceSet != InfiniteSet.instance) { | |
282 if (instanceSet.add(instance) && onFirstOccurrence != null) { | |
283 onFirstOccurrence(instance); | |
284 } | |
285 if (instanceSet.length >= maxInstanceSetSize) { | |
286 instances[type] = InfiniteSet.instance; | |
287 } | |
288 } | |
289 if (instance is LibrarySpecificUnit) { | |
290 uniqueLSUs.add(instance); | |
291 } | |
292 } | |
293 } | |
OLD | NEW |