- Add VariableSpec class with magnitude field validation - Add Formula class supporting multiple input/output variables - Support d4rt_code as string or object with code field - Add comprehensive tests for parsing and serialization - Fix broken test import in pruebas_d4rt_test.dart Follows README.md format requirements exactly
164 lines
4.8 KiB
Dart
164 lines
4.8 KiB
Dart
import 'dart:developer' as developer;
|
|
import 'dart:io' as io;
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:analyzer/file_system/overlay_file_system.dart';
|
|
import 'package:analyzer/file_system/physical_file_system.dart';
|
|
import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart';
|
|
import 'package:heap_snapshot/analysis.dart';
|
|
import 'package:heap_snapshot/format.dart';
|
|
import 'package:vm_service/vm_service.dart';
|
|
|
|
Future<void> main() async {
|
|
timer.start();
|
|
|
|
var resourceProvider = OverlayResourceProvider(
|
|
PhysicalResourceProvider.INSTANCE,
|
|
);
|
|
|
|
var analyzerPath = '/Users/scheglov/Source/Dart/sdk.git/sdk/pkg/analyzer';
|
|
var filePath = '$analyzerPath/lib/src/dart/element/element.dart';
|
|
|
|
var collection = AnalysisContextCollectionImpl(
|
|
resourceProvider: resourceProvider,
|
|
includedPaths: [analyzerPath],
|
|
);
|
|
|
|
var analysisDriver = collection.contextFor(filePath).driver;
|
|
analysisDriver.priorityFiles = [filePath];
|
|
|
|
timer.reset();
|
|
await analysisDriver.getResolvedUnit(filePath);
|
|
print('[+${timer.elapsedMilliseconds} ms] Get resolved unit');
|
|
|
|
var bytes = _getHeapSnapshot();
|
|
|
|
timer.reset();
|
|
var graph = HeapSnapshotGraph.fromChunks([
|
|
bytes.buffer.asByteData(bytes.offsetInBytes, bytes.length),
|
|
]);
|
|
print('[+${timer.elapsedMilliseconds} ms] Create HeapSnapshotGraph');
|
|
|
|
var analysis = Analysis(graph);
|
|
|
|
// Computing reachable objects takes some time.
|
|
timer.reset();
|
|
analysis.reachableObjects;
|
|
print('[+${timer.elapsedMilliseconds} ms] Compute reachable objects');
|
|
print('');
|
|
{
|
|
var measure = analysis.measureObjects(analysis.reachableObjects);
|
|
print('reachableObjects');
|
|
print(' count: ${measure.count}');
|
|
print(' size: ${formatBytes(measure.size)}');
|
|
}
|
|
|
|
// It is interesting to see all reachable objects.
|
|
{
|
|
print('Reachable objects');
|
|
var objects = analysis.reachableObjects;
|
|
analysis.printObjectStats(objects, maxLines: 100);
|
|
}
|
|
|
|
{
|
|
print('\n\n');
|
|
print('Tokens');
|
|
var classSet = analysis.classByPredicate((e) {
|
|
return e.name.endsWith('Token') || e.name.endsWith('TokenImpl');
|
|
});
|
|
var objects = analysis.filterByClassId(analysis.reachableObjects, classSet);
|
|
analysis.printObjectStats(objects, maxLines: 100);
|
|
|
|
print('\n\n');
|
|
print('Tokens retainers');
|
|
analysis.printRetainers(objects, maxEntries: 10);
|
|
}
|
|
}
|
|
|
|
final Stopwatch timer = Stopwatch();
|
|
|
|
Future pumpEventQueue([int times = 5000]) {
|
|
if (times == 0) return Future.value();
|
|
return Future.delayed(Duration.zero, () => pumpEventQueue(times - 1));
|
|
}
|
|
|
|
Uint8List _getHeapSnapshot() {
|
|
timer.reset();
|
|
var tmpDir = io.Directory.systemTemp.createTempSync('analyzer_heap');
|
|
try {
|
|
var snapshotFile = io.File('${tmpDir.path}/0.heap_snapshot');
|
|
developer.NativeRuntime.writeHeapSnapshotToFile(snapshotFile.path);
|
|
print('[+${timer.elapsedMilliseconds} ms] Write heap snapshot');
|
|
|
|
timer.reset();
|
|
var bytes = snapshotFile.readAsBytesSync();
|
|
print(
|
|
'[+${timer.elapsedMilliseconds} ms] '
|
|
'Read heap snapshot, ${bytes.length ~/ (1024 * 1024)} MB',
|
|
);
|
|
return bytes;
|
|
} finally {
|
|
tmpDir.deleteSync(recursive: true);
|
|
}
|
|
}
|
|
|
|
class _ObjectSetMeasure {
|
|
final int count;
|
|
final int size;
|
|
|
|
_ObjectSetMeasure({required this.count, required this.size});
|
|
}
|
|
|
|
extension on Analysis {
|
|
IntSet classByPredicate(bool Function(HeapSnapshotClass) predicate) {
|
|
var allClasses = graph.classes;
|
|
var classSet = SpecializedIntSet(allClasses.length);
|
|
for (var class_ in allClasses) {
|
|
if (predicate(class_)) {
|
|
classSet.add(class_.classId);
|
|
}
|
|
}
|
|
return classSet;
|
|
}
|
|
|
|
// ignore: unused_element
|
|
IntSet filterByClass(
|
|
IntSet objectIds, {
|
|
required Uri libraryUri,
|
|
required String name,
|
|
}) {
|
|
var cid =
|
|
graph.classes.singleWhere((class_) {
|
|
return class_.libraryUri == libraryUri && class_.name == name;
|
|
}).classId;
|
|
return filter(objectIds, (object) => object.classId == cid);
|
|
}
|
|
|
|
_ObjectSetMeasure measureObjects(IntSet objectIds) {
|
|
var stats = generateObjectStats(objectIds);
|
|
var totalSize = 0;
|
|
var totalCount = 0;
|
|
for (var class_ in stats.classes) {
|
|
totalCount += stats.counts[class_.classId];
|
|
totalSize += stats.sizes[class_.classId];
|
|
}
|
|
return _ObjectSetMeasure(count: totalCount, size: totalSize);
|
|
}
|
|
|
|
void printObjectStats(IntSet objectIds, {int maxLines = 20}) {
|
|
var stats = generateObjectStats(objectIds);
|
|
print(formatHeapStats(stats, maxLines: maxLines));
|
|
print('');
|
|
}
|
|
|
|
void printRetainers(IntSet objectIds, {int maxEntries = 3}) {
|
|
var paths = retainingPathsOf(objectIds, 20);
|
|
for (int i = 0; i < paths.length; ++i) {
|
|
if (i >= maxEntries) break;
|
|
var path = paths[i];
|
|
print('There are ${path.count} retaining paths of');
|
|
print(formatRetainingPath(graph, paths[i]));
|
|
print('');
|
|
}
|
|
}
|
|
}
|