Chromium Code Reviews| Index: pkg/analysis_server/lib/src/status/memory_use.dart |
| diff --git a/pkg/analysis_server/lib/src/status/memory_use.dart b/pkg/analysis_server/lib/src/status/memory_use.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ed49ed599c166b84f5936ffeefb4e16c2b07826e |
| --- /dev/null |
| +++ b/pkg/analysis_server/lib/src/status/memory_use.dart |
| @@ -0,0 +1,293 @@ |
| +// Copyright (c) 2016, 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. |
| + |
| +library analysis_server.src.status.memory_use; |
| + |
| +import 'dart:collection'; |
| + |
| +import 'package:analysis_server/src/analysis_server.dart'; |
| +import 'package:analyzer/dart/ast/ast.dart'; |
| +import 'package:analyzer/dart/ast/visitor.dart'; |
| +import 'package:analyzer/dart/element/element.dart'; |
| +import 'package:analyzer/dart/element/visitor.dart'; |
| +import 'package:analyzer/src/context/cache.dart'; |
| +import 'package:analyzer/src/context/context.dart' show AnalysisContextImpl; |
| +import 'package:analyzer/src/dart/element/element.dart'; |
| +import 'package:analyzer/src/generated/engine.dart'; |
| +import 'package:analyzer/src/generated/sdk.dart'; |
| +import 'package:analyzer/task/dart.dart'; |
| +import 'package:analyzer/task/model.dart'; |
| + |
| +/** |
| + * A visitor that will count the number of instances of each type of AST node. |
| + */ |
| +class AstNodeCounter extends UnifyingAstVisitor<Null> { |
| + /** |
| + * A table mapping the types of the AST nodes to the number of instances |
| + * visited. |
| + */ |
| + final Map<Type, int> nodeCounts; |
| + |
| + /** |
| + * Initialize a newly created counter to increment the counts in the given map |
| + * of [nodeCounts]. |
| + */ |
| + AstNodeCounter(this.nodeCounts); |
| + |
| + @override |
| + visitNode(AstNode node) { |
| + Type type = node.runtimeType; |
| + int count = nodeCounts[type] ?? 0; |
| + nodeCounts[type] = count + 1; |
| + super.visitNode(node); |
| + } |
| +} |
| + |
| +/** |
| + * A visitor that will count the number of instances of each type of element. |
| + */ |
| +class ElementCounter extends GeneralizingElementVisitor<Null> { |
| + /** |
| + * A table mapping the types of the elements to the number of instances |
| + * visited. |
| + */ |
| + final Map<Type, int> elementCounts; |
| + |
| + /** |
| + * A table mapping the types of the AST nodes to the number of instances |
| + * visited. |
| + */ |
| + final Map<Type, int> nodeCounts; |
| + |
| + /** |
| + * Initialize a newly created counter to increment the counts in the given map |
| + * of [elementCounts]. |
| + */ |
| + ElementCounter(this.elementCounts, this.nodeCounts); |
| + |
| + @override |
| + visitConstructorElement(ConstructorElement element) { |
| + if (element is ConstructorElementImpl) { |
| + List<ConstructorInitializer> initializers = element.constantInitializers; |
| + if (initializers != null) { |
| + initializers.forEach((ConstructorInitializer initializer) { |
| + _countNodes(initializer); |
| + }); |
| + } |
| + } |
| + visitElement(element); |
| + } |
| + |
| + @override |
| + visitElement(Element element) { |
| + Type type = element.runtimeType; |
| + int count = elementCounts[type] ?? 0; |
| + elementCounts[type] = count + 1; |
| + element.metadata.forEach((ElementAnnotation annotation) { |
| + if (annotation is ElementAnnotationImpl) { |
| + _countNodes(annotation.annotationAst); |
| + } |
| + }); |
| + super.visitElement(element); |
| + } |
| + |
| + visitFieldElement(FieldElement element) { |
| + if (element is ConstVariableElement) { |
| + _countInitializer(element as ConstVariableElement); |
| + } |
| + visitElement(element); |
| + } |
| + |
| + visitLocalVariableElement(LocalVariableElement element) { |
| + if (element is ConstVariableElement) { |
| + _countInitializer(element as ConstVariableElement); |
| + } |
| + visitElement(element); |
| + } |
| + |
| + visitParameterElement(ParameterElement element) { |
| + if (element is ConstVariableElement) { |
| + _countInitializer(element as ConstVariableElement); |
| + } |
| + visitElement(element); |
| + } |
| + |
| + visitTopLevelVariableElement(TopLevelVariableElement element) { |
| + if (element is ConstVariableElement) { |
| + _countInitializer(element as ConstVariableElement); |
| + } |
| + visitElement(element); |
| + } |
| + |
| + void _countInitializer(ConstVariableElement element) { |
| + _countNodes(element.constantInitializer); |
| + } |
| + |
| + void _countNodes(AstNode node) { |
| + if (node != null) { |
| + node.accept(new AstNodeCounter(nodeCounts)); |
| + } |
| + } |
| +} |
| + |
| +/** |
| + * A set used when the number of instances of some type is too large to be kept. |
| + */ |
| +class InfiniteSet implements Set { |
| + /** |
| + * The unique instance of this class. |
| + */ |
| + static final InfiniteSet instance = new InfiniteSet(); |
| + |
| + @override |
| + int get length => -1; |
| + |
| + @override |
| + dynamic noSuchMethod(Invocation invocation) { |
| + throw new UnsupportedError('Do not use instances of InfiniteSet'); |
| + } |
| +} |
| + |
| +/** |
| + * Computes memory usage data by traversing the data structures reachable from |
| + * an analysis server. |
| + */ |
| +class MemoryUseData { |
| + /** |
| + * The maximum size of an instance set. |
| + */ |
| + static const int maxInstanceSetSize = 1000000; |
| + |
| + /** |
| + * A table mapping classes to instances of the class. |
| + */ |
| + Map<Type, Set> instances = new HashMap<Type, Set>(); |
| + |
| + /** |
| + * A set of all the library specific units, using equality rather than |
| + * identity in order to determine whether re-using equal instances would save |
| + * significant space. |
| + */ |
| + Set<LibrarySpecificUnit> uniqueLSUs = new HashSet<LibrarySpecificUnit>(); |
| + |
| + /** |
| + * A set of all the targeted results, using equality rather than identity in |
| + * order to determine whether re-using equal instances would save significant |
| + * space. |
| + */ |
| + Set<TargetedResult> uniqueTargetedResults = new HashSet<TargetedResult>(); |
| + |
| + /** |
| + * A table mapping the types of AST nodes to the number of instances being |
| + * held directly (as values in the cache). |
| + */ |
| + Map<Type, int> directNodeCounts = new HashMap<Type, int>(); |
| + |
| + /** |
| + * A table mapping the types of AST nodes to the number of instances being |
| + * held indirectly (such as nodes reachable from element models). |
| + */ |
| + Map<Type, int> indirectNodeCounts = new HashMap<Type, int>(); |
| + |
| + /** |
| + * A table mapping the types of the elements to the number of instances being |
| + * held directly (as values in the cache). |
| + */ |
| + final Map<Type, int> elementCounts = new HashMap<Type, int>(); |
| + |
| + /** |
| + * Initialize a newly created instance. |
| + */ |
| + MemoryUseData(); |
| + |
| + /** |
| + * Traverse an analysis [server] to compute memory usage data. |
| + */ |
| + void processAnalysisServer(AnalysisServer server) { |
| + _recordInstance(server); |
| + Iterable<AnalysisContext> contexts = server.analysisContexts; |
| + for (AnalysisContextImpl context in contexts) { |
| + _processAnalysisContext(context); |
| + } |
| + DartSdkManager manager = server.sdkManager; |
| + List<SdkDescription> descriptors = manager.sdkDescriptors; |
| + for (SdkDescription descriptor in descriptors) { |
| + _processAnalysisContext(manager.getSdk(descriptor, () => null).context); |
| + } |
| + } |
| + |
| + void _processAnalysisContext(AnalysisContextImpl context) { |
| + _recordInstance(context); |
| + _recordInstance(context.analysisCache); |
| + Map<AnalysisTarget, CacheEntry> map = |
| + context.privateAnalysisCachePartition.entryMap; |
| + map.forEach((AnalysisTarget target, CacheEntry entry) { |
| + _processAnalysisTarget(target); |
| + _processCacheEntry(entry); |
| + }); |
| + } |
| + |
| + void _processAnalysisTarget(AnalysisTarget target) { |
| + _recordInstance(target); |
| + } |
| + |
| + void _processCacheEntry(CacheEntry entry) { |
| + _recordInstance(entry); |
| + List<ResultDescriptor> descriptors = entry.nonInvalidResults; |
| + for (ResultDescriptor descriptor in descriptors) { |
| + _recordInstance(descriptor); |
| + _processResultData(entry.getResultDataOrNull(descriptor)); |
| + } |
| + } |
| + |
| + void _processResultData(ResultData resultData) { |
| + _recordInstance(resultData); |
| + 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
|
| + _recordInstance(resultData.state); |
| + _recordInstance(resultData.value, onFirstOccurrence: (Object object) { |
| + if (object is AstNode) { |
| + object.accept(new AstNodeCounter(directNodeCounts)); |
| + } else if (object is Element) { |
| + object.accept(new ElementCounter(elementCounts, indirectNodeCounts)); |
| + } |
| + }); |
| + resultData.dependedOnResults.forEach(_processTargetedResult); |
| + resultData.dependentResults.forEach(_processTargetedResult); |
| + } |
| + } |
| + |
| + void _processTargetedResult(TargetedResult result) { |
| + _recordInstance(result); |
| + uniqueTargetedResults.add(result); |
| + _recordInstance(result.target); |
| + _recordInstance(result.result); |
| + } |
| + |
| + /** |
| + * Record the given [instance] that was found. If this is the first time that |
| + * the instance has been found, execute the [onFirstOccurrence] function. |
| + * |
| + * Note that instances will not be recorded if there are more than |
| + * [maxInstanceSetSize] instances of the same type, and that the |
| + * [onFirstOccurrence] function will not be executed if the instance is not |
| + * recorded. |
| + */ |
| + void _recordInstance(Object instance, |
| + {void onFirstOccurrence(Object object)}) { |
| + Type type = instance.runtimeType; |
| + Set instanceSet = |
| + 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.
|
| + if (instanceSet != InfiniteSet.instance) { |
| + if (instanceSet.add(instance) && onFirstOccurrence != null) { |
| + onFirstOccurrence(instance); |
| + } |
| + if (instanceSet.length >= maxInstanceSetSize) { |
| + instances[type] = InfiniteSet.instance; |
| + } |
| + } |
| + if (instance is LibrarySpecificUnit) { |
| + uniqueLSUs.add(instance); |
| + } |
| + } |
| +} |