From a21e90eb61f8010fe669d43f625532be007f6025 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 11 Jul 2023 12:29:17 -0700 Subject: [PATCH] Protect from identityHashCode equal to 0. (#90) --- doc/TROUBLESHOOT.md | 5 +++-- pkgs/leak_tracker/CHANGELOG.md | 4 ++++ .../src/leak_tracking/_object_tracker.dart | 1 + .../lib/src/leak_tracking/orchestration.dart | 3 +++ .../retaining_path/_retaining_path.dart | 20 ++++++++++++++++++- pkgs/leak_tracker/pubspec.yaml | 2 +- 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/doc/TROUBLESHOOT.md b/doc/TROUBLESHOOT.md index e7eb008f..268a11cd 100644 --- a/doc/TROUBLESHOOT.md +++ b/doc/TROUBLESHOOT.md @@ -82,8 +82,9 @@ if (ref.target == null) { ``` IMPORTANT: this code will not work in release mode, so -you need to run it with flag `--debug` or `--profile`, or, -if it is test, by clicking `Debug` near your test name in IDE. +you need to run it with flag `--debug` or `--profile` +([not available](https://github.com/flutter/flutter/issues/127331) for Flutter tests), +or, if it is a test, by clicking `Debug` near the test name in IDE. ## Known complicated cases diff --git a/pkgs/leak_tracker/CHANGELOG.md b/pkgs/leak_tracker/CHANGELOG.md index aa4d7e8f..429d66cf 100644 --- a/pkgs/leak_tracker/CHANGELOG.md +++ b/pkgs/leak_tracker/CHANGELOG.md @@ -1,3 +1,7 @@ +# 7.0.7 + +* Protect from identityHashCode equal to 0. + # 7.0.6 * Add helpers for troubleshooting. diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart index 89b36487..7a2242f8 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart @@ -54,6 +54,7 @@ class ObjectTracker implements LeakProvider { }) { throwIfDisposed(); final code = _coder(object); + assert(code > 0); if (_checkForDuplicate(code)) return; _finalizer.attach(object, code); diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart index 37783ba1..2ee24017 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart @@ -168,6 +168,9 @@ Future forceGC({ /// If the object is garbage collected or not retained, returns null. /// /// Does not work in web and in release mode. +/// +/// Also does not work for objects that are not returned by getInstances. +/// https://github.com/dart-lang/sdk/blob/3e80d29fd6fec56187d651ce22ea81f1e8732214/runtime/vm/object_graph.cc#L1803 Future formattedRetainingPath(WeakReference ref) async { if (ref.target == null) return null; final path = await obtainRetainingPath( diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/_retaining_path.dart b/pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/_retaining_path.dart index 48777478..a649a7f5 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/_retaining_path.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/_retaining_path.dart @@ -9,7 +9,13 @@ import 'package:vm_service/vm_service.dart'; import '_connection.dart'; +/// Obtains retainig path for an object. +/// +/// Does not work for objects that have [identityHashCode] equal to 0. +/// https://github.com/dart-lang/sdk/blob/3e80d29fd6fec56187d651ce22ea81f1e8732214/runtime/vm/object_graph.cc#L1803 Future obtainRetainingPath(Type type, int code) async { + assert(code > 0); + final connection = await connect(); final fp = _ObjectFingerprint(type, code); @@ -26,7 +32,7 @@ Future obtainRetainingPath(Type type, int code) async { } class _ObjectFingerprint { - _ObjectFingerprint(this.type, this.code); + _ObjectFingerprint(this.type, this.code) : assert(code > 0); final Type type; final int code; @@ -39,6 +45,11 @@ class _ObjectFingerprint { } } +/// Finds and object in an isolate. +/// +/// This method will NOT find objects, that have [identityHashCode] equal to 0 +/// in result of `getInstances`. +/// https://github.com/dart-lang/sdk/blob/3e80d29fd6fec56187d651ce22ea81f1e8732214/runtime/vm/object_graph.cc#L1803 Future<_ItemInIsolate?> _objectInIsolate( Connection connection, _ObjectFingerprint object, @@ -58,6 +69,9 @@ Future<_ItemInIsolate?> _objectInIsolate( .instances ?? []; + if (instances.isEmpty) continue; + assert(_refIsIdentifiable(instances.first)); + final result = instances.firstWhereOrNull( (objRef) => objRef is InstanceRef && objRef.identityHashCode == object.code, @@ -73,6 +87,10 @@ Future<_ItemInIsolate?> _objectInIsolate( return null; } +bool _refIsIdentifiable(ObjRef ref) { + return ref is InstanceRef && (ref.identityHashCode ?? 0) > 0; +} + /// Represents an item in an isolate. /// /// It can be class or object. diff --git a/pkgs/leak_tracker/pubspec.yaml b/pkgs/leak_tracker/pubspec.yaml index 15f975f3..cd71ad5e 100644 --- a/pkgs/leak_tracker/pubspec.yaml +++ b/pkgs/leak_tracker/pubspec.yaml @@ -1,5 +1,5 @@ name: leak_tracker -version: 7.0.6 +version: 7.0.7 description: A framework for memory leak tracking for Dart and Flutter applications. repository: https://github.com/dart-lang/leak_tracker/tree/main/pkgs/leak_tracker