From a6d6d0d5bb340286dfadc460b79888b004e4b516 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Mon, 24 Jul 2023 07:23:03 -0700 Subject: [PATCH 01/18] Update pubspec.yaml --- pkgs/leak_tracker/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/leak_tracker/pubspec.yaml b/pkgs/leak_tracker/pubspec.yaml index 1de0306f..bf8b4095 100644 --- a/pkgs/leak_tracker/pubspec.yaml +++ b/pkgs/leak_tracker/pubspec.yaml @@ -1,5 +1,5 @@ name: leak_tracker -version: 8.0.3 +version: 9.0.0 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 From fd743e9d380f976d92bea0ab0703ce733bcf7950 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Mon, 24 Jul 2023 07:49:24 -0700 Subject: [PATCH 02/18] Update pubspec.yaml --- pkgs/leak_tracker_testing/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/leak_tracker_testing/pubspec.yaml b/pkgs/leak_tracker_testing/pubspec.yaml index 0c4f5351..5cbd80ac 100644 --- a/pkgs/leak_tracker_testing/pubspec.yaml +++ b/pkgs/leak_tracker_testing/pubspec.yaml @@ -7,7 +7,7 @@ environment: sdk: '>=3.0.0 <4.0.0' dependencies: - leak_tracker: ^8.0.0 + leak_tracker: '>=8.0.0 <10.0.0' test: ^1.16.0 dev_dependencies: From efc40b661269247e32c920d28c2ff4304bbc0f5e Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Mon, 24 Jul 2023 10:08:23 -0700 Subject: [PATCH 03/18] - --- doc/TROUBLESHOOT.md | 10 +------ pkgs/leak_tracker/CHANGELOG.md | 5 ++++ .../src/leak_tracking/leak_tracker_model.dart | 9 +++--- pkgs/leak_tracker/pubspec.yaml | 2 +- .../test/end_to_end_test.dart | 30 ------------------- .../test/test_infra/helpers.dart | 10 ++----- pkgs/leak_tracker_testing/pubspec.yaml | 2 +- 7 files changed, 16 insertions(+), 52 deletions(-) diff --git a/doc/TROUBLESHOOT.md b/doc/TROUBLESHOOT.md index 52ff760b..7d189821 100644 --- a/doc/TROUBLESHOOT.md +++ b/doc/TROUBLESHOOT.md @@ -47,14 +47,6 @@ For collecting debugging information in tests, temporarily pass an instance of ` }, leakTrackingTestConfig: LeakTrackingTestConfig.debug()); ``` -Or, you can temporarily set global flag, to make all tests collecting debug information: - -``` -setUpAll(() { - LeakTrackerGlobalFlags.collectDebugInformationForLeaks = true; -}); -``` - **Applications** For collecting debugging information in your running application, the options are: @@ -111,7 +103,7 @@ also become unreachable, and thus available for garbage collection. One of signs that some leaks still exist is fixing a leak by releasing link to child in the parent's `dispose`, because link to not needed parent should be released itself, together with its disposal. If -your fix for a leak is like this, you are defenitely hiding leaks of non-tracked objects: +your fix for a leak is like this, you are defenitely hiding leaks of non-tracked objects: ``` void dispose() { diff --git a/pkgs/leak_tracker/CHANGELOG.md b/pkgs/leak_tracker/CHANGELOG.md index 32691e71..49904438 100644 --- a/pkgs/leak_tracker/CHANGELOG.md +++ b/pkgs/leak_tracker/CHANGELOG.md @@ -1,3 +1,8 @@ +# 10.0.0 + +* Enable bulk leak tracking for regression tests. +* Remove global flag [collectDebugInformationForLeaks]. + # 9.0.0 * Rename `gcCountBuffer` to `numberOfGcCycles` and `disposalTimeBuffer` to `disposalTime`. diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart index d8b678e9..66fe440a 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart @@ -7,9 +7,6 @@ import '../shared/shared_model.dart'; // ignore: avoid_classes_with_only_static_members, as it is ok for enum-like classes. /// Global settings for leak tracker. class LeakTrackerGlobalSettings { - /// If true, the leak tracker will collect debug information for leaks. - static bool collectDebugInformationForLeaks = false; - /// If true, a warning will be printed when leak tracking is /// requested for a non-supported platform. static bool warnForNonSupportedPlatforms = true; @@ -18,8 +15,12 @@ class LeakTrackerGlobalSettings { /// of validation for leaks. /// /// If the number is too big, the performance may be seriously impacted. - /// If null, path will be requested without limit. + /// If null, the path will be requested without limit. static int? maxRequestsForRetainingPath = 10; + + static bool get isLeakTrackingInProcess => throw UnimplementedError(); + + static bool get isLeakTrackingPaused => throw UnimplementedError(); } /// Handler to collect leak summary. diff --git a/pkgs/leak_tracker/pubspec.yaml b/pkgs/leak_tracker/pubspec.yaml index bf8b4095..5d7459e2 100644 --- a/pkgs/leak_tracker/pubspec.yaml +++ b/pkgs/leak_tracker/pubspec.yaml @@ -1,5 +1,5 @@ name: leak_tracker -version: 9.0.0 +version: 10.0.0 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 diff --git a/pkgs/leak_tracker_flutter_test/test/end_to_end_test.dart b/pkgs/leak_tracker_flutter_test/test/end_to_end_test.dart index fd6346c3..914e5a19 100644 --- a/pkgs/leak_tracker_flutter_test/test/end_to_end_test.dart +++ b/pkgs/leak_tracker_flutter_test/test/end_to_end_test.dart @@ -45,36 +45,6 @@ void main() { ); }); - group('Leak tracker respects flag collectDebugInformationForLeaks', () { - late Leaks leaks; - - setUp( - () => LeakTrackerGlobalSettings.collectDebugInformationForLeaks = true, - ); - - testWidgetsWithLeakTracking( - 'for $StatelessLeakingWidget', - (WidgetTester tester) async { - await tester.pumpWidget(StatelessLeakingWidget()); - }, - leakTrackingTestConfig: LeakTrackingTestConfig( - onLeaks: (Leaks theLeaks) { - leaks = theLeaks; - }, - failTestOnLeaks: false, - ), - ); - - tearDown( - () => _verifyLeaks( - leaks, - expectedNotDisposed: 1, - expectedNotGCed: 1, - shouldContainDebugInfo: false, - ), - ); - }); - group('Stack trace does not start with leak tracker calls.', () { late Leaks leaks; diff --git a/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart b/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart index 8dfdd68e..eeee9db6 100644 --- a/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart +++ b/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart @@ -32,18 +32,14 @@ void testWidgetsWithLeakTracking( bool semanticsEnabled = true, TestVariant variant = const DefaultTestVariant(), dynamic tags, - LeakTrackingTestConfig? leakTrackingTestConfig, + LeakTrackingTestConfig leakTrackingTestConfig = + const LeakTrackingTestConfig(), }) { - final config = leakTrackingTestConfig ?? - (LeakTrackerGlobalSettings.collectDebugInformationForLeaks - ? LeakTrackingTestConfig.debug() - : const LeakTrackingTestConfig()); - Future wrappedCallback(WidgetTester tester) async { await withFlutterLeakTracking( () async => callback(tester), tester, - config, + leakTrackingTestConfig, ); } diff --git a/pkgs/leak_tracker_testing/pubspec.yaml b/pkgs/leak_tracker_testing/pubspec.yaml index 5cbd80ac..3eaf961b 100644 --- a/pkgs/leak_tracker_testing/pubspec.yaml +++ b/pkgs/leak_tracker_testing/pubspec.yaml @@ -7,7 +7,7 @@ environment: sdk: '>=3.0.0 <4.0.0' dependencies: - leak_tracker: '>=8.0.0 <10.0.0' + leak_tracker: '>=8.0.0 <11.0.0' test: ^1.16.0 dev_dependencies: From 3a03bb04fc93f9237449eb030e87c31fca37b0ff Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Mon, 24 Jul 2023 10:34:01 -0700 Subject: [PATCH 04/18] - --- .../leak_tracker/lib/src/leak_tracking/_object_tracker.dart | 4 ++-- .../{retaining_path => _retaining_path}/DEPENDENCIES.md | 0 .../{retaining_path => _retaining_path}/_connection.dart | 0 .../_retaining_path.dart | 0 .../lib/src/leak_tracking/leak_tracker_model.dart | 6 ++++-- pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart | 4 ++-- .../leak_tracking/retaining_path/_retaining_path_test.dart | 4 ++-- 7 files changed, 10 insertions(+), 8 deletions(-) rename pkgs/leak_tracker/lib/src/leak_tracking/{retaining_path => _retaining_path}/DEPENDENCIES.md (100%) rename pkgs/leak_tracker/lib/src/leak_tracking/{retaining_path => _retaining_path}/_connection.dart (100%) rename pkgs/leak_tracker/lib/src/leak_tracking/{retaining_path => _retaining_path}/_retaining_path.dart (100%) 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 42035cf9..47b399d5 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart @@ -13,8 +13,8 @@ import '_finalizer.dart'; import '_gc_counter.dart'; import '_object_record.dart'; import 'leak_tracker_model.dart'; -import 'retaining_path/_connection.dart'; -import 'retaining_path/_retaining_path.dart'; +import '_retaining_path/_connection.dart'; +import '_retaining_path/_retaining_path.dart'; /// Keeps collection of object records until /// disposal and garbage gollection. diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/DEPENDENCIES.md similarity index 100% rename from pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/DEPENDENCIES.md rename to pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/DEPENDENCIES.md diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/_connection.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/_connection.dart similarity index 100% rename from pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/_connection.dart rename to pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/_connection.dart 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 similarity index 100% rename from pkgs/leak_tracker/lib/src/leak_tracking/retaining_path/_retaining_path.dart rename to pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/_retaining_path.dart diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart index 66fe440a..0a3ca3dc 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart @@ -18,9 +18,11 @@ class LeakTrackerGlobalSettings { /// If null, the path will be requested without limit. static int? maxRequestsForRetainingPath = 10; - static bool get isLeakTrackingInProcess => throw UnimplementedError(); + static bool get isTrackingInProcess => throw UnimplementedError(); - static bool get isLeakTrackingPaused => throw UnimplementedError(); + static bool get isTrackingPaused => throw UnimplementedError(); + + static String get phase => throw UnimplementedError(); } /// Handler to collect leak summary. diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart index 8a87334b..936cbb47 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart @@ -11,8 +11,8 @@ import '../shared/shared_model.dart'; import '_formatting.dart'; import 'leak_tracker.dart'; import 'leak_tracker_model.dart'; -import 'retaining_path/_connection.dart'; -import 'retaining_path/_retaining_path.dart'; +import '_retaining_path/_connection.dart'; +import '_retaining_path/_retaining_path.dart'; final _log = Logger('orchestration.dart'); diff --git a/pkgs/leak_tracker/test/debug/leak_tracking/retaining_path/_retaining_path_test.dart b/pkgs/leak_tracker/test/debug/leak_tracking/retaining_path/_retaining_path_test.dart index ac8f2458..d0059c61 100644 --- a/pkgs/leak_tracker/test/debug/leak_tracking/retaining_path/_retaining_path_test.dart +++ b/pkgs/leak_tracker/test/debug/leak_tracking/retaining_path/_retaining_path_test.dart @@ -4,8 +4,8 @@ import 'dart:async'; -import 'package:leak_tracker/src/leak_tracking/retaining_path/_connection.dart'; -import 'package:leak_tracker/src/leak_tracking/retaining_path/_retaining_path.dart'; +import 'package:leak_tracker/src/leak_tracking/_retaining_path/_connection.dart'; +import 'package:leak_tracker/src/leak_tracking/_retaining_path/_retaining_path.dart'; import 'package:logging/logging.dart'; import 'package:test/test.dart'; From 091067d40b0d7cd438a69d54934b2a735383f9cc Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Mon, 24 Jul 2023 12:14:45 -0700 Subject: [PATCH 05/18] - --- pkgs/leak_tracker/lib/DEPENDENCIES.md | 1 - pkgs/leak_tracker/lib/leak_tracker.dart | 3 +- pkgs/leak_tracker/lib/src/DEPENDENCIES.md | 5 ++-- .../lib/src/leak_tracking/DEPENDENCIES.md | 16 +++++++---- .../lib/src/leak_tracking/_global_state.dart | 13 +++++++++ .../lib/src/leak_tracking/_leak_checker.dart | 2 +- .../src/leak_tracking/_object_tracker.dart | 5 ++-- .../_retaining_path/DEPENDENCIES.md | 2 +- .../lib/src/leak_tracking/global_state.dart | 28 +++++++++++++++++++ .../lib/src/leak_tracking/leak_tracker.dart | 2 +- .../{leak_tracker_model.dart => model.dart} | 21 -------------- .../lib/src/leak_tracking/orchestration.dart | 4 +-- .../debug/leak_tracking/end_to_end_test.dart | 4 +-- .../test/test_infra/helpers.dart | 2 +- 14 files changed, 67 insertions(+), 41 deletions(-) create mode 100644 pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart create mode 100644 pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart rename pkgs/leak_tracker/lib/src/leak_tracking/{leak_tracker_model.dart => model.dart} (90%) diff --git a/pkgs/leak_tracker/lib/DEPENDENCIES.md b/pkgs/leak_tracker/lib/DEPENDENCIES.md index cc8d4e49..8d2c05d5 100644 --- a/pkgs/leak_tracker/lib/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/DEPENDENCIES.md @@ -7,6 +7,5 @@ Dependencies that create loop are markes with `!`. flowchart TD; devtools_integration.dart-->src; leak_tracker.dart-->src; -testing.dart-->src; ``` diff --git a/pkgs/leak_tracker/lib/leak_tracker.dart b/pkgs/leak_tracker/lib/leak_tracker.dart index 84609155..080b3aa3 100644 --- a/pkgs/leak_tracker/lib/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/leak_tracker.dart @@ -2,8 +2,9 @@ // 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. +export 'src/leak_tracking/global_state.dart'; export 'src/leak_tracking/leak_tracker.dart'; -export 'src/leak_tracking/leak_tracker_model.dart'; +export 'src/leak_tracking/model.dart'; export 'src/leak_tracking/orchestration.dart'; export 'src/shared/shared_model.dart'; export 'src/usage_tracking/model.dart'; diff --git a/pkgs/leak_tracker/lib/src/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/DEPENDENCIES.md index b4c5149a..0c45afa2 100644 --- a/pkgs/leak_tracker/lib/src/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/src/DEPENDENCIES.md @@ -5,10 +5,11 @@ Dependencies that create loop are markes with `!`. ```mermaid flowchart TD; +devtools_integration-->leak_tracking; devtools_integration-->shared; -leak_tracking-->devtools_integration; +leak_tracking--!-->devtools_integration; leak_tracking-->shared; -testing-->shared; +shared--!-->leak_tracking; usage_tracking-->shared; ``` diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md index 1fa58ae9..9cf29710 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md @@ -6,18 +6,22 @@ Dependencies that create loop are markes with `!`. ```mermaid flowchart TD; _dispatcher.dart-->_object_tracker.dart; -_leak_checker.dart-->leak_tracker_model.dart; +_leak_checker.dart-->model.dart; _object_record.dart-->_gc_counter.dart; +_object_tracker.dart-->_finalizer.dart; _object_tracker.dart-->_gc_counter.dart; _object_tracker.dart-->_object_record.dart; -_object_tracker.dart-->leak_tracker_model.dart; -_object_tracker.dart-->retaining_path; +_object_tracker.dart-->_retaining_path; +_object_tracker.dart-->global_state.dart; +_object_tracker.dart-->model.dart; +global_state.dart-->_global_state.dart; leak_tracker.dart-->_dispatcher.dart; leak_tracker.dart-->_leak_checker.dart; leak_tracker.dart-->_object_tracker.dart; -leak_tracker.dart-->leak_tracker_model.dart; -orchestration.dart-->_gc_counter.dart; +leak_tracker.dart-->model.dart; +orchestration.dart-->_formatting.dart; +orchestration.dart-->_retaining_path; orchestration.dart-->leak_tracker.dart; -orchestration.dart-->leak_tracker_model.dart; +orchestration.dart-->model.dart; ``` diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart new file mode 100644 index 00000000..abc58699 --- /dev/null +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart @@ -0,0 +1,13 @@ +// Copyright (c) 2022, 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. + +// ignore: avoid_classes_with_only_static_members +/// Writable global settings for leak tracker, as it is ok for enum-like classes. +class InternalGlobalState { + static bool isTrackingInProcess = false; + + static bool isTrackingPaused = false; + + static String? phase; +} diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_checker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_checker.dart index d8156ebf..e5363b42 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_checker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_checker.dart @@ -7,7 +7,7 @@ import 'dart:async'; import '../devtools_integration/delivery.dart'; import '../shared/_util.dart'; import '../shared/shared_model.dart'; -import 'leak_tracker_model.dart'; +import 'model.dart'; /// Checks [leakProvider] either by schedule or by request. /// 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 47b399d5..8792bb90 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart @@ -12,9 +12,10 @@ import '../shared/shared_model.dart'; import '_finalizer.dart'; import '_gc_counter.dart'; import '_object_record.dart'; -import 'leak_tracker_model.dart'; import '_retaining_path/_connection.dart'; import '_retaining_path/_retaining_path.dart'; +import 'global_state.dart'; +import 'model.dart'; /// Keeps collection of object records until /// disposal and garbage gollection. @@ -192,7 +193,7 @@ class ObjectTracker implements LeakProvider { await processIfNeeded( items: objectsToGetPath, - limit: LeakTrackerGlobalSettings.maxRequestsForRetainingPath, + limit: LeakTrackerGlobalState.maxRequestsForRetainingPath, processor: _addRetainingPath, ); diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/DEPENDENCIES.md index 4ca77646..30acac45 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_retaining_path/DEPENDENCIES.md @@ -5,6 +5,6 @@ Dependencies that create loop are markes with `!`. ```mermaid flowchart TD; -_retaining_path.dart-->_service.dart; +_retaining_path.dart-->_connection.dart; ``` diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart b/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart new file mode 100644 index 00000000..6157f93a --- /dev/null +++ b/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart @@ -0,0 +1,28 @@ +// ignore: avoid_classes_with_only_static_members, as it is ok for enum-like classes. +import 'dart:developer'; + +import 'package:vm_service/vm_service.dart'; + +import '_global_state.dart'; + +// ignore: avoid_classes_with_only_static_members, as it is ok for enum-like classes. +/// Global settings for leak tracker. +class LeakTrackerGlobalState { + /// If true, a warning will be printed when leak tracking is + /// requested for a non-supported platform. + static bool warnForNonSupportedPlatforms = true; + + /// Limit for number of requests for retaining path per one round + /// of validation for leaks. + /// + /// If the number is too big, the performance may be seriously impacted. + /// If null, the path will be requested without limit. + static int? maxRequestsForRetainingPath = 10; + + static bool get isTrackingInProcess => + InternalGlobalState.isTrackingInProcess; + + static bool get isTrackingPaused => throw UnimplementedError(); + + static String get phase => throw UnimplementedError(); +} diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart index 59b9521a..4c0ba62b 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart @@ -8,7 +8,7 @@ import '../shared/shared_model.dart'; import '_dispatcher.dart' as dispatcher; import '_leak_checker.dart'; import '_object_tracker.dart'; -import 'leak_tracker_model.dart'; +import 'model.dart'; final _objectTracker = ObjectRef(null); LeakChecker? _leakChecker; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart similarity index 90% rename from pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart rename to pkgs/leak_tracker/lib/src/leak_tracking/model.dart index 0a3ca3dc..d958a897 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker_model.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart @@ -4,27 +4,6 @@ import '../shared/shared_model.dart'; -// ignore: avoid_classes_with_only_static_members, as it is ok for enum-like classes. -/// Global settings for leak tracker. -class LeakTrackerGlobalSettings { - /// If true, a warning will be printed when leak tracking is - /// requested for a non-supported platform. - static bool warnForNonSupportedPlatforms = true; - - /// Limit for number of requests for retaining path per one round - /// of validation for leaks. - /// - /// If the number is too big, the performance may be seriously impacted. - /// If null, the path will be requested without limit. - static int? maxRequestsForRetainingPath = 10; - - static bool get isTrackingInProcess => throw UnimplementedError(); - - static bool get isTrackingPaused => throw UnimplementedError(); - - static String get phase => throw UnimplementedError(); -} - /// Handler to collect leak summary. typedef LeakSummaryCallback = void Function(LeakSummary); diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart index 936cbb47..f74520a0 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart @@ -9,10 +9,10 @@ import 'package:logging/logging.dart'; import '../shared/shared_model.dart'; import '_formatting.dart'; -import 'leak_tracker.dart'; -import 'leak_tracker_model.dart'; import '_retaining_path/_connection.dart'; import '_retaining_path/_retaining_path.dart'; +import 'leak_tracker.dart'; +import 'model.dart'; final _log = Logger('orchestration.dart'); diff --git a/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart b/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart index 57687116..518c41e7 100644 --- a/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart +++ b/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart @@ -11,7 +11,7 @@ import '../../test_infra/data/dart_classes.dart'; /// Tests for non-mocked public API of leak tracker. void main() { setUp(() { - LeakTrackerGlobalSettings.maxRequestsForRetainingPath = null; + LeakTrackerGlobalState.maxRequestsForRetainingPath = null; }); tearDown(() => disableLeakTracking()); @@ -20,7 +20,7 @@ void main() { test( 'Leak tracker respects maxRequestsForRetainingPath, $numberOfGcCycles.', () async { - LeakTrackerGlobalSettings.maxRequestsForRetainingPath = 2; + LeakTrackerGlobalState.maxRequestsForRetainingPath = 2; final leaks = await withLeakTracking( () async { LeakingClass(); diff --git a/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart b/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart index eeee9db6..1f55356d 100644 --- a/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart +++ b/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart @@ -79,7 +79,7 @@ Future withFlutterLeakTracking( // Leak tracker does not work for web platform. if (kIsWeb) { final bool shouldPrintWarning = !_webWarningPrinted && - LeakTrackerGlobalSettings.warnForNonSupportedPlatforms; + LeakTrackerGlobalState.warnForNonSupportedPlatforms; if (shouldPrintWarning) { _webWarningPrinted = true; debugPrint( From 359a525efb045bea6c971eb2fc4df1d48b814fa4 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Mon, 24 Jul 2023 12:18:43 -0700 Subject: [PATCH 06/18] - --- pkgs/leak_tracker/lib/src/DEPENDENCIES.md | 4 +--- pkgs/leak_tracker/lib/src/devtools_integration/delivery.dart | 2 +- pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md | 1 - pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart | 3 --- pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart | 2 +- pkgs/leak_tracker/lib/src/shared/DEPENDENCIES.md | 3 +++ .../lib/src/{leak_tracking => shared}/_formatting.dart | 4 ++-- pkgs/leak_tracker/lib/src/shared/shared_model.dart | 2 +- pkgs/leak_tracker_flutter_test/test/_formatting_test.dart | 2 +- 9 files changed, 10 insertions(+), 13 deletions(-) rename pkgs/leak_tracker/lib/src/{leak_tracking => shared}/_formatting.dart (98%) diff --git a/pkgs/leak_tracker/lib/src/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/DEPENDENCIES.md index 0c45afa2..9ea699d7 100644 --- a/pkgs/leak_tracker/lib/src/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/src/DEPENDENCIES.md @@ -5,11 +5,9 @@ Dependencies that create loop are markes with `!`. ```mermaid flowchart TD; -devtools_integration-->leak_tracking; devtools_integration-->shared; -leak_tracking--!-->devtools_integration; +leak_tracking-->devtools_integration; leak_tracking-->shared; -shared--!-->leak_tracking; usage_tracking-->shared; ``` diff --git a/pkgs/leak_tracker/lib/src/devtools_integration/delivery.dart b/pkgs/leak_tracker/lib/src/devtools_integration/delivery.dart index de19af88..73306a90 100644 --- a/pkgs/leak_tracker/lib/src/devtools_integration/delivery.dart +++ b/pkgs/leak_tracker/lib/src/devtools_integration/delivery.dart @@ -7,7 +7,7 @@ import 'dart:developer'; import 'package:vm_service/vm_service.dart'; -import '../leak_tracking/_formatting.dart'; +import '../shared/_formatting.dart'; import '_protocol.dart'; import 'primitives.dart'; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md index 9cf29710..69cf14a2 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md @@ -19,7 +19,6 @@ leak_tracker.dart-->_dispatcher.dart; leak_tracker.dart-->_leak_checker.dart; leak_tracker.dart-->_object_tracker.dart; leak_tracker.dart-->model.dart; -orchestration.dart-->_formatting.dart; orchestration.dart-->_retaining_path; orchestration.dart-->leak_tracker.dart; orchestration.dart-->model.dart; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart b/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart index 6157f93a..aaa0be29 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart @@ -1,7 +1,4 @@ // ignore: avoid_classes_with_only_static_members, as it is ok for enum-like classes. -import 'dart:developer'; - -import 'package:vm_service/vm_service.dart'; import '_global_state.dart'; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart index f74520a0..5134091e 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart @@ -7,8 +7,8 @@ import 'dart:developer'; import 'package:logging/logging.dart'; +import '../shared/_formatting.dart'; import '../shared/shared_model.dart'; -import '_formatting.dart'; import '_retaining_path/_connection.dart'; import '_retaining_path/_retaining_path.dart'; import 'leak_tracker.dart'; diff --git a/pkgs/leak_tracker/lib/src/shared/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/shared/DEPENDENCIES.md index c1d00302..747750ef 100644 --- a/pkgs/leak_tracker/lib/src/shared/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/src/shared/DEPENDENCIES.md @@ -5,6 +5,9 @@ Dependencies that create loop are markes with `!`. ```mermaid flowchart TD; +_formatting.dart-->_primitives.dart; +_formatting.dart-->_util.dart; +shared_model.dart-->_formatting.dart; shared_model.dart-->_primitives.dart; shared_model.dart-->_util.dart; ``` diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_formatting.dart b/pkgs/leak_tracker/lib/src/shared/_formatting.dart similarity index 98% rename from pkgs/leak_tracker/lib/src/leak_tracking/_formatting.dart rename to pkgs/leak_tracker/lib/src/shared/_formatting.dart index efb6e4ca..cc6dd5e0 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_formatting.dart +++ b/pkgs/leak_tracker/lib/src/shared/_formatting.dart @@ -4,8 +4,8 @@ import 'package:vm_service/vm_service.dart'; -import '../shared/_primitives.dart'; -import '../shared/_util.dart'; +import '_primitives.dart'; +import '_util.dart'; /// Converts item in leak tracking context to string. String contextToString(Object? object) { diff --git a/pkgs/leak_tracker/lib/src/shared/shared_model.dart b/pkgs/leak_tracker/lib/src/shared/shared_model.dart index 62725f7d..adad755d 100644 --- a/pkgs/leak_tracker/lib/src/shared/shared_model.dart +++ b/pkgs/leak_tracker/lib/src/shared/shared_model.dart @@ -4,7 +4,7 @@ import 'package:collection/collection.dart'; -import '../leak_tracking/_formatting.dart'; +import '_formatting.dart'; import '_primitives.dart'; import '_util.dart'; diff --git a/pkgs/leak_tracker_flutter_test/test/_formatting_test.dart b/pkgs/leak_tracker_flutter_test/test/_formatting_test.dart index 2c82ae1f..3e7d3ad9 100644 --- a/pkgs/leak_tracker_flutter_test/test/_formatting_test.dart +++ b/pkgs/leak_tracker_flutter_test/test/_formatting_test.dart @@ -3,7 +3,7 @@ // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker/src/leak_tracking/_formatting.dart'; +import 'package:leak_tracker/src/shared/_formatting.dart'; const _jsonEmpty = {}; From 59dc886e5c5bab22924154bd785d44b5fb102eeb Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Mon, 24 Jul 2023 13:54:25 -0700 Subject: [PATCH 07/18] - --- pkgs/leak_tracker/CHANGELOG.md | 2 ++ pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart | 2 +- pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/leak_tracker/CHANGELOG.md b/pkgs/leak_tracker/CHANGELOG.md index 49904438..b8ffc85a 100644 --- a/pkgs/leak_tracker/CHANGELOG.md +++ b/pkgs/leak_tracker/CHANGELOG.md @@ -2,6 +2,8 @@ * Enable bulk leak tracking for regression tests. * Remove global flag [collectDebugInformationForLeaks]. +* Rename `checkNonGCed` to `checkNotGCed`. +* Group items related to object LeakTracker, in class LeakTracker with single instance. # 9.0.0 diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart index 4c0ba62b..9b6351a1 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart @@ -183,7 +183,7 @@ Future collectLeaks() async { /// Invoke this method to detect the leaks earlier, when /// the leaked objects are not GCed yet, /// to obtain retaining path. -Future checkNonGCed() async { +Future checkNotGCed() async { Future? result; assert(() { diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart index 5134091e..3586a385 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart @@ -119,7 +119,7 @@ Future withLeakTracking( if (leakDiagnosticConfig.collectRetainingPathForNonGCed) { // This early check is needed to collect retaing paths before forced GC, // because paths are unavailable for GCed objects. - await checkNonGCed(); + await checkNotGCed(); } await forceGC( From 21c6b564a1110dbf453a65020cdfb72c645c432d Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 07:59:50 -0700 Subject: [PATCH 08/18] Update CHANGELOG.md --- pkgs/leak_tracker/CHANGELOG.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pkgs/leak_tracker/CHANGELOG.md b/pkgs/leak_tracker/CHANGELOG.md index b8ffc85a..cea06b94 100644 --- a/pkgs/leak_tracker/CHANGELOG.md +++ b/pkgs/leak_tracker/CHANGELOG.md @@ -1,12 +1,9 @@ -# 10.0.0 +# 9.0.0 * Enable bulk leak tracking for regression tests. * Remove global flag [collectDebugInformationForLeaks]. * Rename `checkNonGCed` to `checkNotGCed`. * Group items related to object LeakTracker, in class LeakTracker with single instance. - -# 9.0.0 - * Rename `gcCountBuffer` to `numberOfGcCycles` and `disposalTimeBuffer` to `disposalTime`. # 8.0.3 From b3499cdd5b67b0696a604b550d7a7dfa5ffbc20d Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 09:06:25 -0700 Subject: [PATCH 09/18] - --- pkgs/leak_tracker/lib/leak_tracker.dart | 2 +- .../lib/src/leak_tracking/leak_tracker.dart | 184 +--------------- .../src/leak_tracking/leak_tracker___.dart | 198 ++++++++++++++++++ .../lib/src/leak_tracking/leak_tracking.dart | 22 ++ .../lib/src/leak_tracking/orchestration.dart | 2 +- pkgs/leak_tracker/pubspec.yaml | 2 +- .../test/test_infra/data/dart_classes.dart | 2 +- .../test/test_infra/dart_classes.dart | 1 + 8 files changed, 228 insertions(+), 185 deletions(-) create mode 100644 pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart create mode 100644 pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart diff --git a/pkgs/leak_tracker/lib/leak_tracker.dart b/pkgs/leak_tracker/lib/leak_tracker.dart index 080b3aa3..5a8472e2 100644 --- a/pkgs/leak_tracker/lib/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/leak_tracker.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. export 'src/leak_tracking/global_state.dart'; -export 'src/leak_tracking/leak_tracker.dart'; +export 'src/leak_tracking/leak_tracker___.dart'; export 'src/leak_tracking/model.dart'; export 'src/leak_tracking/orchestration.dart'; export 'src/shared/shared_model.dart'; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart index 9b6351a1..06de769d 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart @@ -10,186 +10,8 @@ import '_leak_checker.dart'; import '_object_tracker.dart'; import 'model.dart'; -final _objectTracker = ObjectRef(null); -LeakChecker? _leakChecker; +class LeakTracker { + // static LeakTracker? _leakTracker; -ObjectTracker _theObjectTracker() { - // TODO(polina-c): return both tracker and checker when tuples get released. - final result = _objectTracker.value; - assert((result == null) == (_leakChecker == null)); - if (result == null) throw StateError('Leak tracking should be enabled.'); - return result; -} - -/// Enables leak tracking for the application. -/// -/// The leak tracking will function only for debug/profile/developer mode. -/// See usage guidance at https://github.com/dart-lang/leak_tracker. -/// -/// If [resetIfAlreadyEnabled] is true and leak tracking is already on, -/// the tracking will be reset with new configuration. -/// -/// If [resetIfAlreadyEnabled] is true and leak tracking is already on, -/// [StateError] will be thrown. -void enableLeakTracking({ - LeakTrackingConfiguration? config, - bool resetIfAlreadyEnabled = false, -}) { - assert(() { - final theConfig = config ??= const LeakTrackingConfiguration(); - if (_objectTracker.value != null) { - if (!resetIfAlreadyEnabled) { - throw StateError('Leak tracking is already enabled.'); - } - disableLeakTracking(); - } - - final newTracker = ObjectTracker( - leakDiagnosticConfig: theConfig.leakDiagnosticConfig, - disposalTime: theConfig.disposalTime, - numberOfGcCycles: theConfig.numberOfGcCycles, - ); - - _objectTracker.value = newTracker; - - _leakChecker = LeakChecker( - leakProvider: newTracker, - checkPeriod: theConfig.checkPeriod, - onLeaks: theConfig.onLeaks, - stdoutSink: theConfig.stdoutLeaks ? StdoutSummarySink() : null, - devToolsSink: theConfig.notifyDevTools ? DevToolsSummarySink() : null, - ); - - if (theConfig.notifyDevTools) { - setupDevToolsIntegration(_objectTracker); - } else { - registerLeakTrackingServiceExtension(); - } - return true; - }()); -} - -/// Disables leak tracking for the application. -/// -/// See usage guidance at https://github.com/dart-lang/leak_tracker. -void disableLeakTracking() { - assert(() { - _leakChecker?.dispose(); - _leakChecker = null; - _objectTracker.value?.dispose(); - _objectTracker.value = null; - - return true; - }()); -} - -/// Dispatches an object event to the leak tracker. -/// -/// Consumes the MemoryAllocations event format: -/// https://github.com/flutter/flutter/blob/a479718b02a818fb4ac8d4900bf08ca389cd8e7d/packages/flutter/lib/src/foundation/memory_allocations.dart#L51 -void dispatchObjectEvent(Map> event) { - assert(() { - dispatcher.dispatchObjectEvent(event, _objectTracker.value); - - return true; - }()); -} - -/// Dispatches object creation to the leak tracker. -/// -/// Use [context] to provide additional information, that may help in leek troubleshooting. -/// The value must be serializable. -void dispatchObjectCreated({ - required String library, - required String className, - required Object object, - Map? context, -}) { - assert(() { - final tracker = _objectTracker.value; - if (tracker == null) return true; - - tracker.startTracking( - object, - context: context, - trackedClass: fullClassName(library: library, shortClassName: className), - ); - - return true; - }()); -} - -/// Dispatches object disposal to the leak tracker. -/// -/// See [dispatchObjectCreated] for parameters documentation. -void dispatchObjectDisposed({ - required Object object, - Map? context, -}) { - assert(() { - final tracker = _objectTracker.value; - if (tracker == null) return true; - - tracker.dispatchDisposal(object, context: context); - return true; - }()); -} - -/// Dispatches additional context information to the leak tracker. -/// -/// See [dispatchObjectCreated] for parameters documentation. -void dispatchObjectTrace({ - required Object object, - Map? context, -}) { - assert(() { - _theObjectTracker().addContext(object, context: context); - return true; - }()); -} - -/// Checks for leaks and outputs [LeakSummary] as configured. -Future checkLeaks() async { - Future? result; - - assert(() { - // TODO(polina-c): get checker as result when tuples are released. - _theObjectTracker(); - result = _leakChecker!.checkLeaks(); - - return true; - }()); - - return await (result ?? Future.value(LeakSummary({}))); -} - -/// Returns details of the leaks collected since last invocation. -/// -/// The same object may be reported as leaked twice: first -/// as non GCed, and then as GCed late. -Future collectLeaks() async { - Future? result; - - assert(() { - result = _theObjectTracker().collectLeaks(); - return true; - }()); - - return await (result ?? Future.value(Leaks({}))); -} - -/// Checks for new not GCed leaks. -/// -/// Invoke this method to detect the leaks earlier, when -/// the leaked objects are not GCed yet, -/// to obtain retaining path. -Future checkNotGCed() async { - Future? result; - - assert(() { - result = _theObjectTracker().checkNonGCed(); - return true; - }()); - - await (result ?? Future.value()); + // LeakProvider? } diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart new file mode 100644 index 00000000..980e3324 --- /dev/null +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart @@ -0,0 +1,198 @@ +// Copyright (c) 2022, 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. + +import '../devtools_integration/_registration.dart'; +import '../shared/_primitives.dart'; +import '../shared/shared_model.dart'; +import '_dispatcher.dart' as dispatcher; +import '_leak_checker.dart'; +import '_object_tracker.dart'; +import 'model.dart'; + +class LeakTracker {} + +final _objectTracker = ObjectRef(null); + +LeakChecker? _leakChecker; + +ObjectTracker _theObjectTracker() { + // TODO(polina-c): return both tracker and checker when tuples get released. + final result = _objectTracker.value; + assert((result == null) == (_leakChecker == null)); + if (result == null) throw StateError('Leak tracking should be enabled.'); + return result; +} + +/// Enables leak tracking for the application. +/// +/// The leak tracking will function only for debug/profile/developer mode. +/// See usage guidance at https://github.com/dart-lang/leak_tracker. +/// +/// If [resetIfAlreadyEnabled] is true and leak tracking is already on, +/// the tracking will be reset with new configuration. +/// +/// If [resetIfAlreadyEnabled] is true and leak tracking is already on, +/// [StateError] will be thrown. +void enableLeakTracking({ + LeakTrackingConfiguration? config, + bool resetIfAlreadyEnabled = false, +}) { + assert(() { + final theConfig = config ??= const LeakTrackingConfiguration(); + if (_objectTracker.value != null) { + if (!resetIfAlreadyEnabled) { + throw StateError('Leak tracking is already enabled.'); + } + disableLeakTracking(); + } + + final newTracker = ObjectTracker( + leakDiagnosticConfig: theConfig.leakDiagnosticConfig, + disposalTime: theConfig.disposalTime, + numberOfGcCycles: theConfig.numberOfGcCycles, + ); + + _objectTracker.value = newTracker; + + _leakChecker = LeakChecker( + leakProvider: newTracker, + checkPeriod: theConfig.checkPeriod, + onLeaks: theConfig.onLeaks, + stdoutSink: theConfig.stdoutLeaks ? StdoutSummarySink() : null, + devToolsSink: theConfig.notifyDevTools ? DevToolsSummarySink() : null, + ); + + if (theConfig.notifyDevTools) { + setupDevToolsIntegration(_objectTracker); + } else { + registerLeakTrackingServiceExtension(); + } + return true; + }()); +} + +/// Disables leak tracking for the application. +/// +/// See usage guidance at https://github.com/dart-lang/leak_tracker. +void disableLeakTracking() { + assert(() { + _leakChecker?.dispose(); + _leakChecker = null; + _objectTracker.value?.dispose(); + _objectTracker.value = null; + + return true; + }()); +} + +/// Dispatches an object event to the leak tracker. +/// +/// Consumes the MemoryAllocations event format: +/// https://github.com/flutter/flutter/blob/a479718b02a818fb4ac8d4900bf08ca389cd8e7d/packages/flutter/lib/src/foundation/memory_allocations.dart#L51 +void dispatchObjectEvent(Map> event) { + assert(() { + dispatcher.dispatchObjectEvent(event, _objectTracker.value); + + return true; + }()); +} + +/// Dispatches object creation to the leak tracker. +/// +/// Use [context] to provide additional information, that may help in leek troubleshooting. +/// The value must be serializable. +void dispatchObjectCreated({ + required String library, + required String className, + required Object object, + Map? context, +}) { + assert(() { + final tracker = _objectTracker.value; + if (tracker == null) return true; + + tracker.startTracking( + object, + context: context, + trackedClass: fullClassName(library: library, shortClassName: className), + ); + + return true; + }()); +} + +/// Dispatches object disposal to the leak tracker. +/// +/// See [dispatchObjectCreated] for parameters documentation. +void dispatchObjectDisposed({ + required Object object, + Map? context, +}) { + assert(() { + final tracker = _objectTracker.value; + if (tracker == null) return true; + + tracker.dispatchDisposal(object, context: context); + return true; + }()); +} + +/// Dispatches additional context information to the leak tracker. +/// +/// See [dispatchObjectCreated] for parameters documentation. +void dispatchObjectTrace({ + required Object object, + Map? context, +}) { + assert(() { + _theObjectTracker().addContext(object, context: context); + return true; + }()); +} + +/// Checks for leaks and outputs [LeakSummary] as configured. +Future checkLeaks() async { + Future? result; + + assert(() { + // TODO(polina-c): get checker as result when tuples are released. + _theObjectTracker(); + result = _leakChecker!.checkLeaks(); + + return true; + }()); + + return await (result ?? Future.value(LeakSummary({}))); +} + +/// Returns details of the leaks collected since last invocation. +/// +/// The same object may be reported as leaked twice: first +/// as non GCed, and then as GCed late. +Future collectLeaks() async { + Future? result; + + assert(() { + result = _theObjectTracker().collectLeaks(); + return true; + }()); + + return await (result ?? Future.value(Leaks({}))); +} + +/// Checks for new not GCed leaks. +/// +/// Invoke this method to detect the leaks earlier, when +/// the leaked objects are not GCed yet, +/// to obtain retaining path. +Future checkNotGCed() async { + Future? result; + + assert(() { + result = _theObjectTracker().checkNonGCed(); + return true; + }()); + + await (result ?? Future.value()); +} diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart new file mode 100644 index 00000000..053ca575 --- /dev/null +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart @@ -0,0 +1,22 @@ +// Copyright (c) 2022, 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. + +import '../../leak_tracker.dart'; +import '../shared/_primitives.dart'; +import '../devtools_integration/_registration.dart'; +import '../shared/_primitives.dart'; +import '../shared/shared_model.dart'; +import '_dispatcher.dart' as dispatcher; +import '_leak_checker.dart'; +import '_object_tracker.dart'; +import 'model.dart'; + +abstract class LeakTracking { + static LeakTracker? _leakTracker; + + /// Leak provider, used in integration with DevTools. + /// + /// It should be updated every time leak tracking is reconfigured. + static final _leakProvider = ObjectRef(null); +} diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart index 3586a385..4cd8bfe2 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart @@ -11,7 +11,7 @@ import '../shared/_formatting.dart'; import '../shared/shared_model.dart'; import '_retaining_path/_connection.dart'; import '_retaining_path/_retaining_path.dart'; -import 'leak_tracker.dart'; +import 'leak_tracker___.dart'; import 'model.dart'; final _log = Logger('orchestration.dart'); diff --git a/pkgs/leak_tracker/pubspec.yaml b/pkgs/leak_tracker/pubspec.yaml index 5d7459e2..44270b5a 100644 --- a/pkgs/leak_tracker/pubspec.yaml +++ b/pkgs/leak_tracker/pubspec.yaml @@ -1,5 +1,5 @@ name: leak_tracker -version: 10.0.0 +version: 9.0.0-dev.1 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 diff --git a/pkgs/leak_tracker/test/test_infra/data/dart_classes.dart b/pkgs/leak_tracker/test/test_infra/data/dart_classes.dart index ddd54a9f..aa172abe 100644 --- a/pkgs/leak_tracker/test/test_infra/data/dart_classes.dart +++ b/pkgs/leak_tracker/test/test_infra/data/dart_classes.dart @@ -2,7 +2,7 @@ // 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. -import 'package:leak_tracker/src/leak_tracking/leak_tracker.dart'; +import 'package:leak_tracker/src/leak_tracking/leak_tracker___.dart'; class LeakTrackedClass { LeakTrackedClass() { diff --git a/pkgs/leak_tracker_flutter_test/test/test_infra/dart_classes.dart b/pkgs/leak_tracker_flutter_test/test/test_infra/dart_classes.dart index 7e97b0c7..b1f1d331 100644 --- a/pkgs/leak_tracker_flutter_test/test/test_infra/dart_classes.dart +++ b/pkgs/leak_tracker_flutter_test/test/test_infra/dart_classes.dart @@ -2,6 +2,7 @@ // 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. +import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker/src/leak_tracking/leak_tracker.dart'; class LeakTrackedClass { From ab158a7afb160722836d6b39b805a21836f50b49 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 11:50:44 -0700 Subject: [PATCH 10/18] - --- ..._leak_checker.dart => _leak_reporter.dart} | 4 +- .../{leak_tracker.dart => _leak_tracker.dart} | 7 +- .../src/leak_tracking/leak_tracker___.dart | 6 +- .../lib/src/leak_tracking/leak_tracking.dart | 69 ++++++++++++++++++- .../leak_tracking/_leak_checker_test.dart | 8 +-- 5 files changed, 78 insertions(+), 16 deletions(-) rename pkgs/leak_tracker/lib/src/leak_tracking/{_leak_checker.dart => _leak_reporter.dart} (98%) rename pkgs/leak_tracker/lib/src/leak_tracking/{leak_tracker.dart => _leak_tracker.dart} (81%) diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_checker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_reporter.dart similarity index 98% rename from pkgs/leak_tracker/lib/src/leak_tracking/_leak_checker.dart rename to pkgs/leak_tracker/lib/src/leak_tracking/_leak_reporter.dart index e5363b42..c24e58f1 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_checker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_reporter.dart @@ -13,8 +13,8 @@ import 'model.dart'; /// /// If there are leaks, reports them to the enabled outputs: /// listener, console and DevTools. -class LeakChecker { - LeakChecker({ +class LeakReporter { + LeakReporter({ required this.leakProvider, required this.checkPeriod, required this.onLeaks, diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart similarity index 81% rename from pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart rename to pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart index 06de769d..d7e7b969 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart @@ -6,12 +6,11 @@ import '../devtools_integration/_registration.dart'; import '../shared/_primitives.dart'; import '../shared/shared_model.dart'; import '_dispatcher.dart' as dispatcher; -import '_leak_checker.dart'; +import '_leak_reporter.dart'; import '_object_tracker.dart'; import 'model.dart'; class LeakTracker { - // static LeakTracker? _leakTracker; - - // LeakProvider? + final ObjectTracker _objectTracker; + final LeakReporter _leakChecker; } diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart index 980e3324..5e710a86 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart @@ -6,7 +6,7 @@ import '../devtools_integration/_registration.dart'; import '../shared/_primitives.dart'; import '../shared/shared_model.dart'; import '_dispatcher.dart' as dispatcher; -import '_leak_checker.dart'; +import '_leak_reporter.dart'; import '_object_tracker.dart'; import 'model.dart'; @@ -14,7 +14,7 @@ class LeakTracker {} final _objectTracker = ObjectRef(null); -LeakChecker? _leakChecker; +LeakReporter? _leakChecker; ObjectTracker _theObjectTracker() { // TODO(polina-c): return both tracker and checker when tuples get released. @@ -55,7 +55,7 @@ void enableLeakTracking({ _objectTracker.value = newTracker; - _leakChecker = LeakChecker( + _leakChecker = LeakReporter( leakProvider: newTracker, checkPeriod: theConfig.checkPeriod, onLeaks: theConfig.onLeaks, diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart index 053ca575..7e523eb4 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart @@ -8,15 +8,78 @@ import '../devtools_integration/_registration.dart'; import '../shared/_primitives.dart'; import '../shared/shared_model.dart'; import '_dispatcher.dart' as dispatcher; -import '_leak_checker.dart'; +import '_leak_reporter.dart'; import '_object_tracker.dart'; import 'model.dart'; abstract class LeakTracking { static LeakTracker? _leakTracker; - /// Leak provider, used in integration with DevTools. + /// Leak provider, used for integration with DevTools. /// /// It should be updated every time leak tracking is reconfigured. - static final _leakProvider = ObjectRef(null); + static final _leakProvider = ObjectRef?>(null); + + /// Enables leak tracking for the application. + /// + /// The leak tracking will function only for debug/profile/developer mode. + /// See usage guidance at https://github.com/dart-lang/leak_tracker. + /// + /// If [resetIfAlreadyEnabled] is true and leak tracking is already on, + /// the tracking will be reset with new configuration. + /// + /// If [resetIfAlreadyEnabled] is true and leak tracking is already on, + /// [StateError] will be thrown. + void enableLeakTracking({ + LeakTrackingConfiguration? config, + bool resetIfAlreadyEnabled = false, + }) { + assert(() { + final theConfig = config ??= const LeakTrackingConfiguration(); + if (_leakTracker != null) { + if (!resetIfAlreadyEnabled) { + throw StateError('Leak tracking is already enabled.'); + } + disableLeakTracking(); + } + + final objectTracker = ObjectTracker( + leakDiagnosticConfig: theConfig.leakDiagnosticConfig, + disposalTime: theConfig.disposalTime, + numberOfGcCycles: theConfig.numberOfGcCycles, + ); + + final leakChecker = LeakReporter( + leakProvider: objectTracker, + checkPeriod: theConfig.checkPeriod, + onLeaks: theConfig.onLeaks, + stdoutSink: theConfig.stdoutLeaks ? StdoutSummarySink() : null, + devToolsSink: theConfig.notifyDevTools ? DevToolsSummarySink() : null, + ); + + _leakProvider.value = WeakReference(objectTracker); + + if (theConfig.notifyDevTools) { + setupDevToolsIntegration(_leakProvider); + } else { + registerLeakTrackingServiceExtension(); + } + return true; + }()); + } + + /// Disables leak tracking for the application. + /// + /// See usage guidance at https://github.com/dart-lang/leak_tracker. + void disableLeakTracking() { + assert(() { + _leakProvider.value?.dispose(); + _leakChecker?.dispose(); + _leakChecker = null; + _objectTracker.value?.dispose(); + _objectTracker.value = null; + + return true; + }()); + } } diff --git a/pkgs/leak_tracker/test/release/leak_tracking/_leak_checker_test.dart b/pkgs/leak_tracker/test/release/leak_tracking/_leak_checker_test.dart index 7c1074fd..52b1d9dd 100644 --- a/pkgs/leak_tracker/test/release/leak_tracking/_leak_checker_test.dart +++ b/pkgs/leak_tracker/test/release/leak_tracking/_leak_checker_test.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'package:leak_tracker/leak_tracker.dart'; -import 'package:leak_tracker/src/leak_tracking/_leak_checker.dart'; +import 'package:leak_tracker/src/leak_tracking/_leak_reporter.dart'; import 'package:test/test.dart'; // Enum-like static classes are ok. @@ -31,13 +31,13 @@ void main() { const period = Duration(milliseconds: 5); late final doublePeriod = Duration(microseconds: period.inMicroseconds); - LeakChecker leakChecker({ + LeakReporter leakChecker({ required bool checkPeriodically, required bool hasListener, required bool hasStdout, required bool hasDevtools, }) => - LeakChecker( + LeakReporter( leakProvider: leakProvider, checkPeriod: checkPeriodically ? period : null, onLeaks: hasListener ? (summary) => listened.store.add(summary) : null, @@ -45,7 +45,7 @@ void main() { devToolsSink: hasDevtools ? devtools : null, ); - LeakChecker defaultLeakChecker() => leakChecker( + LeakReporter defaultLeakChecker() => leakChecker( checkPeriodically: true, hasListener: false, hasStdout: true, From a22971842861041ac8c5c759a510ea46daac277a Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 12:55:20 -0700 Subject: [PATCH 11/18] - --- pkgs/leak_tracker/analysis_options.yaml | 2 +- .../devtools_integration/_registration.dart | 6 +-- .../lib/src/leak_tracking/_global_state.dart | 1 - .../lib/src/leak_tracking/_leak_tracker.dart | 26 ++++++++++- .../lib/src/leak_tracking/global_state.dart | 3 -- .../src/leak_tracking/leak_tracker___.dart | 4 +- .../lib/src/leak_tracking/leak_tracking.dart | 44 +++++++------------ .../leak_tracking/_leak_checker_test.dart | 2 - 8 files changed, 45 insertions(+), 43 deletions(-) diff --git a/pkgs/leak_tracker/analysis_options.yaml b/pkgs/leak_tracker/analysis_options.yaml index bdf4f4f2..b838293c 100644 --- a/pkgs/leak_tracker/analysis_options.yaml +++ b/pkgs/leak_tracker/analysis_options.yaml @@ -34,7 +34,7 @@ linter: # - avoid_bool_literals_in_conditional_expressions # not yet tested # - avoid_catches_without_on_clauses # we do this commonly # - avoid_catching_errors # we do this commonly - - avoid_classes_with_only_static_members + # - avoid_classes_with_only_static_members # - avoid_double_and_int_checks # only useful when targeting JS runtime - avoid_empty_else - avoid_field_initializers_in_const_classes diff --git a/pkgs/leak_tracker/lib/src/devtools_integration/_registration.dart b/pkgs/leak_tracker/lib/src/devtools_integration/_registration.dart index 7f984ead..dae305ee 100644 --- a/pkgs/leak_tracker/lib/src/devtools_integration/_registration.dart +++ b/pkgs/leak_tracker/lib/src/devtools_integration/_registration.dart @@ -27,8 +27,8 @@ bool registerLeakTrackingServiceExtension() => _registerServiceExtension( /// Registers service extension for DevTools integration. /// /// If the extension is already registered, returns false. -bool setupDevToolsIntegration( - ObjectRef leakProvider, +bool initializeDevToolsIntegration( + ObjectRef?> leakProvider, ) { Future handler( String method, @@ -37,7 +37,7 @@ bool setupDevToolsIntegration( try { assert(method == memoryLeakTrackingExtensionName); - final theLeakProvider = leakProvider.value; + final theLeakProvider = leakProvider.value?.target; if (theLeakProvider == null) { return ResponseFromApp(LeakTrackingTurnedOffError()) diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart index abc58699..e2b8ac2a 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart @@ -2,7 +2,6 @@ // 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. -// ignore: avoid_classes_with_only_static_members /// Writable global settings for leak tracker, as it is ok for enum-like classes. class InternalGlobalState { static bool isTrackingInProcess = false; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart index d7e7b969..5f33a55f 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart @@ -11,6 +11,28 @@ import '_object_tracker.dart'; import 'model.dart'; class LeakTracker { - final ObjectTracker _objectTracker; - final LeakReporter _leakChecker; + LeakTracker(LeakTrackingConfiguration config) { + objectTracker = ObjectTracker( + leakDiagnosticConfig: config.leakDiagnosticConfig, + disposalTime: config.disposalTime, + numberOfGcCycles: config.numberOfGcCycles, + ); + + leakReporter = LeakReporter( + leakProvider: objectTracker, + checkPeriod: config.checkPeriod, + onLeaks: config.onLeaks, + stdoutSink: config.stdoutLeaks ? StdoutSummarySink() : null, + devToolsSink: config.notifyDevTools ? DevToolsSummarySink() : null, + ); + } + + late final ObjectTracker objectTracker; + + late final LeakReporter leakReporter; + + void dispose() { + objectTracker.dispose(); + leakReporter.dispose(); + } } diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart b/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart index aaa0be29..424ce2f1 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart @@ -1,8 +1,5 @@ -// ignore: avoid_classes_with_only_static_members, as it is ok for enum-like classes. - import '_global_state.dart'; -// ignore: avoid_classes_with_only_static_members, as it is ok for enum-like classes. /// Global settings for leak tracker. class LeakTrackerGlobalState { /// If true, a warning will be printed when leak tracking is diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart index 5e710a86..8e55c1cd 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart @@ -10,8 +10,6 @@ import '_leak_reporter.dart'; import '_object_tracker.dart'; import 'model.dart'; -class LeakTracker {} - final _objectTracker = ObjectRef(null); LeakReporter? _leakChecker; @@ -64,7 +62,7 @@ void enableLeakTracking({ ); if (theConfig.notifyDevTools) { - setupDevToolsIntegration(_objectTracker); + initializeDevToolsIntegration(_objectTracker); } else { registerLeakTrackingServiceExtension(); } diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart index 7e523eb4..2a7a0be1 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart @@ -9,6 +9,7 @@ import '../shared/_primitives.dart'; import '../shared/shared_model.dart'; import '_dispatcher.dart' as dispatcher; import '_leak_reporter.dart'; +import '_leak_tracker.dart'; import '_object_tracker.dart'; import 'model.dart'; @@ -17,9 +18,11 @@ abstract class LeakTracking { /// Leak provider, used for integration with DevTools. /// - /// It should be updated every time leak tracking is reconfigured. + /// It's value should be updated every time leak tracking is reconfigured. static final _leakProvider = ObjectRef?>(null); + static bool get isEnabled => _leakTracker != null; + /// Enables leak tracking for the application. /// /// The leak tracking will function only for debug/profile/developer mode. @@ -30,12 +33,11 @@ abstract class LeakTracking { /// /// If [resetIfAlreadyEnabled] is true and leak tracking is already on, /// [StateError] will be thrown. - void enableLeakTracking({ - LeakTrackingConfiguration? config, + static void enableLeakTracking({ + LeakTrackingConfiguration config = const LeakTrackingConfiguration(), bool resetIfAlreadyEnabled = false, }) { assert(() { - final theConfig = config ??= const LeakTrackingConfiguration(); if (_leakTracker != null) { if (!resetIfAlreadyEnabled) { throw StateError('Leak tracking is already enabled.'); @@ -43,24 +45,14 @@ abstract class LeakTracking { disableLeakTracking(); } - final objectTracker = ObjectTracker( - leakDiagnosticConfig: theConfig.leakDiagnosticConfig, - disposalTime: theConfig.disposalTime, - numberOfGcCycles: theConfig.numberOfGcCycles, - ); - - final leakChecker = LeakReporter( - leakProvider: objectTracker, - checkPeriod: theConfig.checkPeriod, - onLeaks: theConfig.onLeaks, - stdoutSink: theConfig.stdoutLeaks ? StdoutSummarySink() : null, - devToolsSink: theConfig.notifyDevTools ? DevToolsSummarySink() : null, - ); + final leakTracker = _leakTracker = LeakTracker(config); + _leakProvider.value = WeakReference(leakTracker.objectTracker); - _leakProvider.value = WeakReference(objectTracker); - - if (theConfig.notifyDevTools) { - setupDevToolsIntegration(_leakProvider); + if (config.notifyDevTools) { + // While [leakTracker] will push summary leak notifications to DevTools, + // DevTools may request leak details from the application via integration. + // That's why it needs [_leakProvider]. + initializeDevToolsIntegration(_leakProvider); } else { registerLeakTrackingServiceExtension(); } @@ -71,14 +63,10 @@ abstract class LeakTracking { /// Disables leak tracking for the application. /// /// See usage guidance at https://github.com/dart-lang/leak_tracker. - void disableLeakTracking() { + static void disableLeakTracking() { assert(() { - _leakProvider.value?.dispose(); - _leakChecker?.dispose(); - _leakChecker = null; - _objectTracker.value?.dispose(); - _objectTracker.value = null; - + _leakTracker?.dispose(); + _leakTracker = null; return true; }()); } diff --git a/pkgs/leak_tracker/test/release/leak_tracking/_leak_checker_test.dart b/pkgs/leak_tracker/test/release/leak_tracking/_leak_checker_test.dart index 52b1d9dd..943eb253 100644 --- a/pkgs/leak_tracker/test/release/leak_tracking/_leak_checker_test.dart +++ b/pkgs/leak_tracker/test/release/leak_tracking/_leak_checker_test.dart @@ -6,8 +6,6 @@ import 'package:leak_tracker/leak_tracker.dart'; import 'package:leak_tracker/src/leak_tracking/_leak_reporter.dart'; import 'package:test/test.dart'; -// Enum-like static classes are ok. -// ignore: avoid_classes_with_only_static_members class _SummaryValues { static final zero = LeakSummary({}); From d22097a7be49c8e8c2c3d25d4ba8154517e030b4 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 14:24:18 -0700 Subject: [PATCH 12/18] - --- .github/workflows/ci.yaml | 3 + examples/minimal_flutter/lib/main.dart | 7 +- pkgs/leak_tracker/example/main.dart | 4 +- pkgs/leak_tracker/lib/leak_tracker.dart | 2 +- .../lib/src/leak_tracking/_leak_tracker.dart | 4 - .../src/leak_tracking/leak_tracker___.dart | 196 ------------------ .../lib/src/leak_tracking/leak_tracking.dart | 129 ++++++++++-- .../lib/src/leak_tracking/model.dart | 14 ++ .../lib/src/leak_tracking/orchestration.dart | 13 +- .../debug/leak_tracking/end_to_end_test.dart | 2 +- .../leak_tracking/end_to_end_test.dart | 2 +- .../test/test_infra/data/dart_classes.dart | 6 +- .../test/test_infra/dart_classes.dart | 5 +- .../test/test_infra/helpers.dart | 2 +- 14 files changed, 153 insertions(+), 236 deletions(-) delete mode 100644 pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5a4cc7b8..7902471a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,5 +1,8 @@ name: CI +# TODO(polina-c): configure auto-update for diagrams +# https://github.com/dart-lang/leak_tracker/issues/104 + on: schedule: # “At 00:00 (UTC) on Sunday.” diff --git a/examples/minimal_flutter/lib/main.dart b/examples/minimal_flutter/lib/main.dart index a4aa27f2..343a201e 100644 --- a/examples/minimal_flutter/lib/main.dart +++ b/examples/minimal_flutter/lib/main.dart @@ -3,9 +3,10 @@ import 'package:flutter/material.dart'; import 'package:leak_tracker/leak_tracker.dart'; void main() { - enableLeakTracking(); - MemoryAllocations.instance - .addListener((ObjectEvent event) => dispatchObjectEvent(event.toMap())); + LeakTracking.start(); + MemoryAllocations.instance.addListener( + (ObjectEvent event) => LeakTracking.dispatchObjectEvent(event.toMap()), + ); runApp(const MyApp()); } diff --git a/pkgs/leak_tracker/example/main.dart b/pkgs/leak_tracker/example/main.dart index 9c95c7c7..fb4765e9 100644 --- a/pkgs/leak_tracker/example/main.dart +++ b/pkgs/leak_tracker/example/main.dart @@ -1,8 +1,8 @@ import 'package:leak_tracker/leak_tracker.dart'; void main(List arguments) { - enableLeakTracking(); + LeakTracking.start(); // ignore: avoid_print print('Hello, world!'); - disableLeakTracking(); + LeakTracking.stop(); } diff --git a/pkgs/leak_tracker/lib/leak_tracker.dart b/pkgs/leak_tracker/lib/leak_tracker.dart index 5a8472e2..6801b323 100644 --- a/pkgs/leak_tracker/lib/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/leak_tracker.dart @@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. export 'src/leak_tracking/global_state.dart'; -export 'src/leak_tracking/leak_tracker___.dart'; +export 'src/leak_tracking/leak_tracking.dart'; export 'src/leak_tracking/model.dart'; export 'src/leak_tracking/orchestration.dart'; export 'src/shared/shared_model.dart'; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart index 5f33a55f..5ec528df 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart @@ -2,10 +2,6 @@ // 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. -import '../devtools_integration/_registration.dart'; -import '../shared/_primitives.dart'; -import '../shared/shared_model.dart'; -import '_dispatcher.dart' as dispatcher; import '_leak_reporter.dart'; import '_object_tracker.dart'; import 'model.dart'; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart deleted file mode 100644 index 8e55c1cd..00000000 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracker___.dart +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (c) 2022, 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. - -import '../devtools_integration/_registration.dart'; -import '../shared/_primitives.dart'; -import '../shared/shared_model.dart'; -import '_dispatcher.dart' as dispatcher; -import '_leak_reporter.dart'; -import '_object_tracker.dart'; -import 'model.dart'; - -final _objectTracker = ObjectRef(null); - -LeakReporter? _leakChecker; - -ObjectTracker _theObjectTracker() { - // TODO(polina-c): return both tracker and checker when tuples get released. - final result = _objectTracker.value; - assert((result == null) == (_leakChecker == null)); - if (result == null) throw StateError('Leak tracking should be enabled.'); - return result; -} - -/// Enables leak tracking for the application. -/// -/// The leak tracking will function only for debug/profile/developer mode. -/// See usage guidance at https://github.com/dart-lang/leak_tracker. -/// -/// If [resetIfAlreadyEnabled] is true and leak tracking is already on, -/// the tracking will be reset with new configuration. -/// -/// If [resetIfAlreadyEnabled] is true and leak tracking is already on, -/// [StateError] will be thrown. -void enableLeakTracking({ - LeakTrackingConfiguration? config, - bool resetIfAlreadyEnabled = false, -}) { - assert(() { - final theConfig = config ??= const LeakTrackingConfiguration(); - if (_objectTracker.value != null) { - if (!resetIfAlreadyEnabled) { - throw StateError('Leak tracking is already enabled.'); - } - disableLeakTracking(); - } - - final newTracker = ObjectTracker( - leakDiagnosticConfig: theConfig.leakDiagnosticConfig, - disposalTime: theConfig.disposalTime, - numberOfGcCycles: theConfig.numberOfGcCycles, - ); - - _objectTracker.value = newTracker; - - _leakChecker = LeakReporter( - leakProvider: newTracker, - checkPeriod: theConfig.checkPeriod, - onLeaks: theConfig.onLeaks, - stdoutSink: theConfig.stdoutLeaks ? StdoutSummarySink() : null, - devToolsSink: theConfig.notifyDevTools ? DevToolsSummarySink() : null, - ); - - if (theConfig.notifyDevTools) { - initializeDevToolsIntegration(_objectTracker); - } else { - registerLeakTrackingServiceExtension(); - } - return true; - }()); -} - -/// Disables leak tracking for the application. -/// -/// See usage guidance at https://github.com/dart-lang/leak_tracker. -void disableLeakTracking() { - assert(() { - _leakChecker?.dispose(); - _leakChecker = null; - _objectTracker.value?.dispose(); - _objectTracker.value = null; - - return true; - }()); -} - -/// Dispatches an object event to the leak tracker. -/// -/// Consumes the MemoryAllocations event format: -/// https://github.com/flutter/flutter/blob/a479718b02a818fb4ac8d4900bf08ca389cd8e7d/packages/flutter/lib/src/foundation/memory_allocations.dart#L51 -void dispatchObjectEvent(Map> event) { - assert(() { - dispatcher.dispatchObjectEvent(event, _objectTracker.value); - - return true; - }()); -} - -/// Dispatches object creation to the leak tracker. -/// -/// Use [context] to provide additional information, that may help in leek troubleshooting. -/// The value must be serializable. -void dispatchObjectCreated({ - required String library, - required String className, - required Object object, - Map? context, -}) { - assert(() { - final tracker = _objectTracker.value; - if (tracker == null) return true; - - tracker.startTracking( - object, - context: context, - trackedClass: fullClassName(library: library, shortClassName: className), - ); - - return true; - }()); -} - -/// Dispatches object disposal to the leak tracker. -/// -/// See [dispatchObjectCreated] for parameters documentation. -void dispatchObjectDisposed({ - required Object object, - Map? context, -}) { - assert(() { - final tracker = _objectTracker.value; - if (tracker == null) return true; - - tracker.dispatchDisposal(object, context: context); - return true; - }()); -} - -/// Dispatches additional context information to the leak tracker. -/// -/// See [dispatchObjectCreated] for parameters documentation. -void dispatchObjectTrace({ - required Object object, - Map? context, -}) { - assert(() { - _theObjectTracker().addContext(object, context: context); - return true; - }()); -} - -/// Checks for leaks and outputs [LeakSummary] as configured. -Future checkLeaks() async { - Future? result; - - assert(() { - // TODO(polina-c): get checker as result when tuples are released. - _theObjectTracker(); - result = _leakChecker!.checkLeaks(); - - return true; - }()); - - return await (result ?? Future.value(LeakSummary({}))); -} - -/// Returns details of the leaks collected since last invocation. -/// -/// The same object may be reported as leaked twice: first -/// as non GCed, and then as GCed late. -Future collectLeaks() async { - Future? result; - - assert(() { - result = _theObjectTracker().collectLeaks(); - return true; - }()); - - return await (result ?? Future.value(Leaks({}))); -} - -/// Checks for new not GCed leaks. -/// -/// Invoke this method to detect the leaks earlier, when -/// the leaked objects are not GCed yet, -/// to obtain retaining path. -Future checkNotGCed() async { - Future? result; - - assert(() { - result = _theObjectTracker().checkNonGCed(); - return true; - }()); - - await (result ?? Future.value()); -} diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart index 2a7a0be1..185d4082 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart @@ -3,15 +3,10 @@ // BSD-style license that can be found in the LICENSE file. import '../../leak_tracker.dart'; -import '../shared/_primitives.dart'; import '../devtools_integration/_registration.dart'; import '../shared/_primitives.dart'; -import '../shared/shared_model.dart'; import '_dispatcher.dart' as dispatcher; -import '_leak_reporter.dart'; import '_leak_tracker.dart'; -import '_object_tracker.dart'; -import 'model.dart'; abstract class LeakTracking { static LeakTracker? _leakTracker; @@ -21,28 +16,29 @@ abstract class LeakTracking { /// It's value should be updated every time leak tracking is reconfigured. static final _leakProvider = ObjectRef?>(null); - static bool get isEnabled => _leakTracker != null; + /// Returns true if leak tracking is configured. + static bool get isStarted => _leakTracker != null; - /// Enables leak tracking for the application. + /// Configures leak tracking for the application. /// /// The leak tracking will function only for debug/profile/developer mode. /// See usage guidance at https://github.com/dart-lang/leak_tracker. /// - /// If [resetIfAlreadyEnabled] is true and leak tracking is already on, + /// If [resetIfAlreadyStarted] is true and leak tracking is already on, /// the tracking will be reset with new configuration. /// - /// If [resetIfAlreadyEnabled] is true and leak tracking is already on, + /// If [resetIfAlreadyStarted] is true and leak tracking is already on, /// [StateError] will be thrown. - static void enableLeakTracking({ + static void start({ LeakTrackingConfiguration config = const LeakTrackingConfiguration(), - bool resetIfAlreadyEnabled = false, + bool resetIfAlreadyStarted = false, }) { assert(() { if (_leakTracker != null) { - if (!resetIfAlreadyEnabled) { + if (!resetIfAlreadyStarted) { throw StateError('Leak tracking is already enabled.'); } - disableLeakTracking(); + stop(); } final leakTracker = _leakTracker = LeakTracker(config); @@ -60,14 +56,117 @@ abstract class LeakTracking { }()); } - /// Disables leak tracking for the application. + /// Stops leak tracking for the application. /// /// See usage guidance at https://github.com/dart-lang/leak_tracker. - static void disableLeakTracking() { + static void stop() { assert(() { _leakTracker?.dispose(); _leakTracker = null; return true; }()); } + + /// Dispatches an object event to the leak tracker. + /// + /// Consumes the MemoryAllocations event format: + /// https://github.com/flutter/flutter/blob/a479718b02a818fb4ac8d4900bf08ca389cd8e7d/packages/flutter/lib/src/foundation/memory_allocations.dart#L51 + static void dispatchObjectEvent(Map> event) { + assert(() { + dispatcher.dispatchObjectEvent(event, _leakTracker?.objectTracker); + + return true; + }()); + } + + /// Dispatches object creation to the leak tracker. + /// + /// Use [context] to provide additional information, that may help in leek troubleshooting. + /// The value must be serializable. + static void dispatchObjectCreated({ + required String library, + required String className, + required Object object, + Map? context, + }) { + assert(() { + _leakTracker?.objectTracker.startTracking( + object, + context: context, + trackedClass: + fullClassName(library: library, shortClassName: className), + ); + + return true; + }()); + } + + /// Dispatches object disposal to the leak tracker. + /// + /// See [dispatchObjectCreated] for parameters documentation. + static void dispatchObjectDisposed({ + required Object object, + Map? context, + }) { + assert(() { + _leakTracker?.objectTracker.dispatchDisposal(object, context: context); + return true; + }()); + } + + /// Dispatches additional context information to the leak tracker. + /// + /// See [dispatchObjectCreated] for parameters documentation. + static void dispatchObjectTrace({ + required Object object, + Map? context, + }) { + assert(() { + _leakTracker?.objectTracker.addContext(object, context: context); + return true; + }()); + } + + /// Checks for leaks and outputs [LeakSummary] as configured. + static Future checkLeaks() async { + Future? result; + + assert(() { + result = _leakTracker?.leakReporter.checkLeaks(); + return true; + }()); + + return await (result ?? Future.value(LeakSummary({}))); + } + + /// Returns details of the leaks collected since last invocation. + /// + /// The same object may be reported as leaked twice: first + /// as non GCed, and then as GCed late. + static Future collectLeaks() async { + Future? result; + + assert(() { + result = _leakTracker?.objectTracker.collectLeaks(); + return true; + }()); + + return await (result ?? Future.value(Leaks({}))); + } + + /// Checks for new not-GCed leaks. + /// + /// Invoke this method to detect the leaks earlier, when + /// the leaked objects are not GCed yet, + /// to obtain retaining path. + static Future checkNotGCed() async { + Future? result; + + assert(() { + result = _leakTracker?.objectTracker.checkNonGCed(); + return true; + }()); + + await (result ?? Future.value()); + } } diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/model.dart b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart index d958a897..b8275821 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/model.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart @@ -69,6 +69,7 @@ class LeakDiagnosticConfig { /// if there is no activity in the application for ~5 minutes. const defaultNumberOfGcCycles = 3; +/// Leak tracking configuration, that cannot be changed after leak tracking is started. class LeakTrackingConfiguration { const LeakTrackingConfiguration({ this.stdoutLeaks = true, @@ -78,6 +79,8 @@ class LeakTrackingConfiguration { this.disposalTime = const Duration(milliseconds: 100), this.leakDiagnosticConfig = const LeakDiagnosticConfig(), this.numberOfGcCycles = defaultNumberOfGcCycles, + this.warnForNonSupportedPlatforms = true, + this.maxRequestsForRetainingPath = 10, }); /// The leak tracker: @@ -118,6 +121,17 @@ class LeakTrackingConfiguration { /// Time to allow the disposal invoker to release the reference to the object. final Duration disposalTime; + + /// If true, a warning will be printed when leak tracking is + /// requested for a non-supported platform. + final bool warnForNonSupportedPlatforms; + + /// Limit for number of requests for retaining path per one round + /// of validation for leaks. + /// + /// If the number is too big, the performance may be seriously impacted. + /// If null, the path will be srequested without limit. + final int? maxRequestsForRetainingPath; } /// Configuration for leak tracking in unit tests. diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart index 4cd8bfe2..d61e6ac8 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart @@ -11,7 +11,8 @@ import '../shared/_formatting.dart'; import '../shared/shared_model.dart'; import '_retaining_path/_connection.dart'; import '_retaining_path/_retaining_path.dart'; -import 'leak_tracker___.dart'; + +import 'leak_tracking.dart'; import 'model.dart'; final _log = Logger('orchestration.dart'); @@ -99,8 +100,8 @@ Future withLeakTracking( if (callback == null) return Leaks({}); - enableLeakTracking( - resetIfAlreadyEnabled: true, + LeakTracking.start( + resetIfAlreadyStarted: true, config: LeakTrackingConfiguration.passive( leakDiagnosticConfig: leakDiagnosticConfig, numberOfGcCycles: numberOfGcCycles, @@ -119,14 +120,14 @@ Future withLeakTracking( if (leakDiagnosticConfig.collectRetainingPathForNonGCed) { // This early check is needed to collect retaing paths before forced GC, // because paths are unavailable for GCed objects. - await checkNotGCed(); + await LeakTracking.checkNotGCed(); } await forceGC( fullGcCycles: numberOfGcCycles, timeout: timeoutForFinalGarbageCollection, ); - leaks = await collectLeaks(); + leaks = await LeakTracking.collectLeaks(); if ((leaks?.total ?? 0) > 0 && shouldThrowOnLeaks) { // `expect` should not be used here, because, when the method is used // from Flutter, the packages `test` and `flutter_test` conflict. @@ -139,7 +140,7 @@ Future withLeakTracking( if (leaks == null) throw StateError('Leaks collection failed.'); return leaks!; } finally { - disableLeakTracking(); + LeakTracking.stop(); } } diff --git a/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart b/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart index 518c41e7..ed2f1659 100644 --- a/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart +++ b/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart @@ -14,7 +14,7 @@ void main() { LeakTrackerGlobalState.maxRequestsForRetainingPath = null; }); - tearDown(() => disableLeakTracking()); + tearDown(() => LeakTracking.stop()); for (var numberOfGcCycles in [1, defaultNumberOfGcCycles]) { test( diff --git a/pkgs/leak_tracker/test/release/leak_tracking/end_to_end_test.dart b/pkgs/leak_tracker/test/release/leak_tracking/end_to_end_test.dart index f13e1e54..6be08310 100644 --- a/pkgs/leak_tracker/test/release/leak_tracking/end_to_end_test.dart +++ b/pkgs/leak_tracker/test/release/leak_tracking/end_to_end_test.dart @@ -13,7 +13,7 @@ import '../../test_infra/data/dart_classes.dart'; /// Tests for non-mocked public API of leak tracker. void main() { tearDown(() { - disableLeakTracking(); + LeakTracking.stop(); }); for (var numberOfGcCycles in [1, defaultNumberOfGcCycles]) { diff --git a/pkgs/leak_tracker/test/test_infra/data/dart_classes.dart b/pkgs/leak_tracker/test/test_infra/data/dart_classes.dart index aa172abe..8dd3d750 100644 --- a/pkgs/leak_tracker/test/test_infra/data/dart_classes.dart +++ b/pkgs/leak_tracker/test/test_infra/data/dart_classes.dart @@ -2,11 +2,11 @@ // 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. -import 'package:leak_tracker/src/leak_tracking/leak_tracker___.dart'; +import 'package:leak_tracker/leak_tracker.dart'; class LeakTrackedClass { LeakTrackedClass() { - dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: library, className: '$LeakTrackedClass', object: this, @@ -16,7 +16,7 @@ class LeakTrackedClass { static const library = 'package:my_package/lib/src/my_lib.dart'; void dispose() { - dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } } diff --git a/pkgs/leak_tracker_flutter_test/test/test_infra/dart_classes.dart b/pkgs/leak_tracker_flutter_test/test/test_infra/dart_classes.dart index b1f1d331..b182a5fb 100644 --- a/pkgs/leak_tracker_flutter_test/test/test_infra/dart_classes.dart +++ b/pkgs/leak_tracker_flutter_test/test/test_infra/dart_classes.dart @@ -3,11 +3,10 @@ // BSD-style license that can be found in the LICENSE file. import 'package:leak_tracker/leak_tracker.dart'; -import 'package:leak_tracker/src/leak_tracking/leak_tracker.dart'; class LeakTrackedClass { LeakTrackedClass() { - dispatchObjectCreated( + LeakTracking.dispatchObjectCreated( library: library, className: '$LeakTrackedClass', object: this, @@ -17,7 +16,7 @@ class LeakTrackedClass { static const library = 'package:my_package/lib/src/my_lib.dart'; void dispose() { - dispatchObjectDisposed(object: this); + LeakTracking.dispatchObjectDisposed(object: this); } } diff --git a/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart b/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart index 1f55356d..52ac548e 100644 --- a/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart +++ b/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart @@ -91,7 +91,7 @@ Future withFlutterLeakTracking( } void flutterEventToLeakTracker(ObjectEvent event) { - return dispatchObjectEvent(event.toMap()); + return LeakTracking.dispatchObjectEvent(event.toMap()); } return TestAsyncUtils.guard(() async { From 57646097996c82b4fef4b8e0aaeec823033fd189 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 14:30:39 -0700 Subject: [PATCH 13/18] - --- .../lib/src/leak_tracking/DEPENDENCIES.md | 14 ++++++++------ .../lib/src/leak_tracking/leak_tracking.dart | 3 ++- pkgs/leak_tracker/lib/src/leak_tracking/model.dart | 4 +++- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md index 69cf14a2..3311fafe 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md @@ -6,7 +6,10 @@ Dependencies that create loop are markes with `!`. ```mermaid flowchart TD; _dispatcher.dart-->_object_tracker.dart; -_leak_checker.dart-->model.dart; +_leak_reporter.dart-->model.dart; +_leak_tracker.dart-->_leak_reporter.dart; +_leak_tracker.dart-->_object_tracker.dart; +_leak_tracker.dart-->model.dart; _object_record.dart-->_gc_counter.dart; _object_tracker.dart-->_finalizer.dart; _object_tracker.dart-->_gc_counter.dart; @@ -15,12 +18,11 @@ _object_tracker.dart-->_retaining_path; _object_tracker.dart-->global_state.dart; _object_tracker.dart-->model.dart; global_state.dart-->_global_state.dart; -leak_tracker.dart-->_dispatcher.dart; -leak_tracker.dart-->_leak_checker.dart; -leak_tracker.dart-->_object_tracker.dart; -leak_tracker.dart-->model.dart; +leak_tracking.dart-->_dispatcher.dart; +leak_tracking.dart-->_leak_tracker.dart; +leak_tracking.dart-->model.dart; orchestration.dart-->_retaining_path; -orchestration.dart-->leak_tracker.dart; +orchestration.dart-->leak_tracking.dart; orchestration.dart-->model.dart; ``` diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart index 185d4082..e1f6c1fe 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart @@ -2,11 +2,12 @@ // 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. -import '../../leak_tracker.dart'; import '../devtools_integration/_registration.dart'; import '../shared/_primitives.dart'; +import '../shared/shared_model.dart'; import '_dispatcher.dart' as dispatcher; import '_leak_tracker.dart'; +import 'model.dart'; abstract class LeakTracking { static LeakTracker? _leakTracker; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/model.dart b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart index b8275821..b61663b9 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/model.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart @@ -69,7 +69,9 @@ class LeakDiagnosticConfig { /// if there is no activity in the application for ~5 minutes. const defaultNumberOfGcCycles = 3; -/// Leak tracking configuration, that cannot be changed after leak tracking is started. +/// Leak tracking configuration. +/// +/// Contains settings that cannot be changed after leak tracking is started. class LeakTrackingConfiguration { const LeakTrackingConfiguration({ this.stdoutLeaks = true, From acfbf9252f37f794890a54783fa42a71d08ec3b7 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 14:43:30 -0700 Subject: [PATCH 14/18] - --- pkgs/leak_tracker/lib/leak_tracker.dart | 1 - .../lib/src/leak_tracking/_global_state.dart | 12 ---------- .../lib/src/leak_tracking/_leak_tracker.dart | 1 + .../src/leak_tracking/_object_tracker.dart | 6 +++-- .../lib/src/leak_tracking/global_state.dart | 22 ------------------- .../lib/src/leak_tracking/leak_tracking.dart | 4 ++++ .../lib/src/leak_tracking/model.dart | 5 ----- .../test/test_infra/helpers.dart | 6 ++--- 8 files changed, 12 insertions(+), 45 deletions(-) delete mode 100644 pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart delete mode 100644 pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart diff --git a/pkgs/leak_tracker/lib/leak_tracker.dart b/pkgs/leak_tracker/lib/leak_tracker.dart index 6801b323..cba7a79f 100644 --- a/pkgs/leak_tracker/lib/leak_tracker.dart +++ b/pkgs/leak_tracker/lib/leak_tracker.dart @@ -2,7 +2,6 @@ // 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. -export 'src/leak_tracking/global_state.dart'; export 'src/leak_tracking/leak_tracking.dart'; export 'src/leak_tracking/model.dart'; export 'src/leak_tracking/orchestration.dart'; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart deleted file mode 100644 index e2b8ac2a..00000000 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_global_state.dart +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) 2022, 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. - -/// Writable global settings for leak tracker, as it is ok for enum-like classes. -class InternalGlobalState { - static bool isTrackingInProcess = false; - - static bool isTrackingPaused = false; - - static String? phase; -} diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart index 5ec528df..5d6909d0 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_leak_tracker.dart @@ -12,6 +12,7 @@ class LeakTracker { leakDiagnosticConfig: config.leakDiagnosticConfig, disposalTime: config.disposalTime, numberOfGcCycles: config.numberOfGcCycles, + maxRequestsForRetainingPath: config.maxRequestsForRetainingPath, ); leakReporter = LeakReporter( 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 8792bb90..06fda7df 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/_object_tracker.dart @@ -14,7 +14,6 @@ import '_gc_counter.dart'; import '_object_record.dart'; import '_retaining_path/_connection.dart'; import '_retaining_path/_retaining_path.dart'; -import 'global_state.dart'; import 'model.dart'; /// Keeps collection of object records until @@ -28,6 +27,7 @@ class ObjectTracker implements LeakProvider { this.leakDiagnosticConfig = const LeakDiagnosticConfig(), required this.disposalTime, required this.numberOfGcCycles, + required this.maxRequestsForRetainingPath, FinalizerBuilder? finalizerBuilder, GcCounter? gcCounter, IdentityHashCoder? coder, @@ -55,6 +55,8 @@ class ObjectTracker implements LeakProvider { final int numberOfGcCycles; + final int? maxRequestsForRetainingPath; + void startTracking( Object object, { required Map? context, @@ -193,7 +195,7 @@ class ObjectTracker implements LeakProvider { await processIfNeeded( items: objectsToGetPath, - limit: LeakTrackerGlobalState.maxRequestsForRetainingPath, + limit: maxRequestsForRetainingPath, processor: _addRetainingPath, ); diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart b/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart deleted file mode 100644 index 424ce2f1..00000000 --- a/pkgs/leak_tracker/lib/src/leak_tracking/global_state.dart +++ /dev/null @@ -1,22 +0,0 @@ -import '_global_state.dart'; - -/// Global settings for leak tracker. -class LeakTrackerGlobalState { - /// If true, a warning will be printed when leak tracking is - /// requested for a non-supported platform. - static bool warnForNonSupportedPlatforms = true; - - /// Limit for number of requests for retaining path per one round - /// of validation for leaks. - /// - /// If the number is too big, the performance may be seriously impacted. - /// If null, the path will be requested without limit. - static int? maxRequestsForRetainingPath = 10; - - static bool get isTrackingInProcess => - InternalGlobalState.isTrackingInProcess; - - static bool get isTrackingPaused => throw UnimplementedError(); - - static String get phase => throw UnimplementedError(); -} diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart index e1f6c1fe..02aa8754 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart @@ -20,6 +20,10 @@ abstract class LeakTracking { /// Returns true if leak tracking is configured. static bool get isStarted => _leakTracker != null; + /// If true, a warning will be printed when leak tracking is + /// requested for a non-supported platform. + static bool warnForNonSupportedPlatforms = true; + /// Configures leak tracking for the application. /// /// The leak tracking will function only for debug/profile/developer mode. diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/model.dart b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart index b61663b9..ebc91379 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/model.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart @@ -81,7 +81,6 @@ class LeakTrackingConfiguration { this.disposalTime = const Duration(milliseconds: 100), this.leakDiagnosticConfig = const LeakDiagnosticConfig(), this.numberOfGcCycles = defaultNumberOfGcCycles, - this.warnForNonSupportedPlatforms = true, this.maxRequestsForRetainingPath = 10, }); @@ -124,10 +123,6 @@ class LeakTrackingConfiguration { /// Time to allow the disposal invoker to release the reference to the object. final Duration disposalTime; - /// If true, a warning will be printed when leak tracking is - /// requested for a non-supported platform. - final bool warnForNonSupportedPlatforms; - /// Limit for number of requests for retaining path per one round /// of validation for leaks. /// diff --git a/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart b/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart index 52ac548e..67d9a078 100644 --- a/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart +++ b/pkgs/leak_tracker_flutter_test/test/test_infra/helpers.dart @@ -78,12 +78,12 @@ Future withFlutterLeakTracking( ) async { // Leak tracker does not work for web platform. if (kIsWeb) { - final bool shouldPrintWarning = !_webWarningPrinted && - LeakTrackerGlobalState.warnForNonSupportedPlatforms; + final bool shouldPrintWarning = + !_webWarningPrinted && LeakTracking.warnForNonSupportedPlatforms; if (shouldPrintWarning) { _webWarningPrinted = true; debugPrint( - 'Leak tracking is not supported on web platform.\nTo turn off this message, set `LeakTrackerGlobalFlags.warnForNonSupportedPlatforms` to false.', + 'Leak tracking is not supported on web platform.\nTo turn off this message, set `LeakTracking.warnForNonSupportedPlatforms` to false.', ); } await callback(); From 50e7d1ef91dd0d95bd28bcb2f36a3c004172ca7c Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 14:47:19 -0700 Subject: [PATCH 15/18] - --- pkgs/leak_tracker/lib/src/leak_tracking/model.dart | 2 ++ pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart | 2 ++ .../test/debug/leak_tracking/end_to_end_test.dart | 5 ----- .../test/release/leak_tracking/_object_tracker_test.dart | 3 +++ .../test/test_infra/mocks/mock_object_tracker.dart | 1 + .../test/test_infra/mock_object_tracker.dart | 1 + 6 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/model.dart b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart index ebc91379..4fb509a8 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/model.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/model.dart @@ -92,6 +92,7 @@ class LeakTrackingConfiguration { LeakTrackingConfiguration.passive({ LeakDiagnosticConfig leakDiagnosticConfig = const LeakDiagnosticConfig(), int numberOfGcCycles = defaultNumberOfGcCycles, + int? maxRequestsForRetainingPath = 10, }) : this( stdoutLeaks: false, notifyDevTools: false, @@ -99,6 +100,7 @@ class LeakTrackingConfiguration { disposalTime: const Duration(), leakDiagnosticConfig: leakDiagnosticConfig, numberOfGcCycles: numberOfGcCycles, + maxRequestsForRetainingPath: maxRequestsForRetainingPath, ); /// Number of full GC cycles, enough for a non reachable object to be GCed. diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart index d61e6ac8..9238304f 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/orchestration.dart @@ -89,6 +89,7 @@ Future withLeakTracking( LeakDiagnosticConfig leakDiagnosticConfig = const LeakDiagnosticConfig(), AsyncCodeRunner? asyncCodeRunner, int numberOfGcCycles = defaultNumberOfGcCycles, + int? maxRequestsForRetainingPath = 10, }) async { if (numberOfGcCycles <= 0) { throw ArgumentError.value( @@ -105,6 +106,7 @@ Future withLeakTracking( config: LeakTrackingConfiguration.passive( leakDiagnosticConfig: leakDiagnosticConfig, numberOfGcCycles: numberOfGcCycles, + maxRequestsForRetainingPath: maxRequestsForRetainingPath, ), ); diff --git a/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart b/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart index ed2f1659..1007fe23 100644 --- a/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart +++ b/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart @@ -10,17 +10,12 @@ import '../../test_infra/data/dart_classes.dart'; /// Tests for non-mocked public API of leak tracker. void main() { - setUp(() { - LeakTrackerGlobalState.maxRequestsForRetainingPath = null; - }); - tearDown(() => LeakTracking.stop()); for (var numberOfGcCycles in [1, defaultNumberOfGcCycles]) { test( 'Leak tracker respects maxRequestsForRetainingPath, $numberOfGcCycles.', () async { - LeakTrackerGlobalState.maxRequestsForRetainingPath = 2; final leaks = await withLeakTracking( () async { LeakingClass(); diff --git a/pkgs/leak_tracker/test/release/leak_tracking/_object_tracker_test.dart b/pkgs/leak_tracker/test/release/leak_tracking/_object_tracker_test.dart index dc8541e8..b39884aa 100644 --- a/pkgs/leak_tracker/test/release/leak_tracking/_object_tracker_test.dart +++ b/pkgs/leak_tracker/test/release/leak_tracking/_object_tracker_test.dart @@ -81,6 +81,7 @@ void main() { disposalTime: _disposalTime, coder: mockCoder, numberOfGcCycles: defaultNumberOfGcCycles, + maxRequestsForRetainingPath: 0, ); }); @@ -147,6 +148,7 @@ void main() { gcCounter: gcCounter, disposalTime: _disposalTime, numberOfGcCycles: defaultNumberOfGcCycles, + maxRequestsForRetainingPath: 0, ); }); @@ -323,6 +325,7 @@ void main() { ), disposalTime: _disposalTime, numberOfGcCycles: defaultNumberOfGcCycles, + maxRequestsForRetainingPath: 0, ); }); diff --git a/pkgs/leak_tracker/test/test_infra/mocks/mock_object_tracker.dart b/pkgs/leak_tracker/test/test_infra/mocks/mock_object_tracker.dart index 797a8ac9..7738c162 100644 --- a/pkgs/leak_tracker/test/test_infra/mocks/mock_object_tracker.dart +++ b/pkgs/leak_tracker/test/test_infra/mocks/mock_object_tracker.dart @@ -25,6 +25,7 @@ class MockObjectTracker extends ObjectTracker { disposalTime: const Duration(milliseconds: 100), leakDiagnosticConfig: const LeakDiagnosticConfig(), numberOfGcCycles: defaultNumberOfGcCycles, + maxRequestsForRetainingPath: 0, ); final events = []; diff --git a/pkgs/leak_tracker_flutter_test/test/test_infra/mock_object_tracker.dart b/pkgs/leak_tracker_flutter_test/test/test_infra/mock_object_tracker.dart index 1924667a..c37590a9 100644 --- a/pkgs/leak_tracker_flutter_test/test/test_infra/mock_object_tracker.dart +++ b/pkgs/leak_tracker_flutter_test/test/test_infra/mock_object_tracker.dart @@ -25,6 +25,7 @@ class MockObjectTracker extends ObjectTracker { disposalTime: const Duration(milliseconds: 100), leakDiagnosticConfig: const LeakDiagnosticConfig(), numberOfGcCycles: 3, + maxRequestsForRetainingPath: 10, ); final events = []; From 7184edd15b1870b5d9bb4e2e23a5c94ff8b22e0e Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 14:52:46 -0700 Subject: [PATCH 16/18] - --- README.md | 8 ++++++++ pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md | 2 -- .../leak_tracker/lib/src/leak_tracking/leak_tracking.dart | 1 + .../test/debug/leak_tracking/end_to_end_test.dart | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fdeb7e7b..94751cf2 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,11 @@ To temporary enable logs, add this line to `main`: ``` Logger.root.onRecord.listen((LogRecord record) => print(record.message)); ``` + +## How to regenerate diagrams + +To regenerate diagrams, run: + +``` +dart run layerlens +``` diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md b/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md index 3311fafe..6cc2eab0 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md +++ b/pkgs/leak_tracker/lib/src/leak_tracking/DEPENDENCIES.md @@ -15,9 +15,7 @@ _object_tracker.dart-->_finalizer.dart; _object_tracker.dart-->_gc_counter.dart; _object_tracker.dart-->_object_record.dart; _object_tracker.dart-->_retaining_path; -_object_tracker.dart-->global_state.dart; _object_tracker.dart-->model.dart; -global_state.dart-->_global_state.dart; leak_tracking.dart-->_dispatcher.dart; leak_tracking.dart-->_leak_tracker.dart; leak_tracking.dart-->model.dart; diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart index 02aa8754..8ea1e1ed 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart @@ -9,6 +9,7 @@ import '_dispatcher.dart' as dispatcher; import '_leak_tracker.dart'; import 'model.dart'; +/// Provides leak tracking functionality. abstract class LeakTracking { static LeakTracker? _leakTracker; diff --git a/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart b/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart index 1007fe23..81601252 100644 --- a/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart +++ b/pkgs/leak_tracker/test/debug/leak_tracking/end_to_end_test.dart @@ -27,6 +27,7 @@ void main() { collectRetainingPathForNonGCed: true, ), numberOfGcCycles: numberOfGcCycles, + maxRequestsForRetainingPath: 2, ); const pathHeader = ' path: >'; From db49a892961df88748fde78677556aa20b0b26a1 Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 14:58:54 -0700 Subject: [PATCH 17/18] Update CHANGELOG.md --- pkgs/leak_tracker/CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/leak_tracker/CHANGELOG.md b/pkgs/leak_tracker/CHANGELOG.md index cea06b94..5dd29368 100644 --- a/pkgs/leak_tracker/CHANGELOG.md +++ b/pkgs/leak_tracker/CHANGELOG.md @@ -1,9 +1,8 @@ # 9.0.0 -* Enable bulk leak tracking for regression tests. * Remove global flag [collectDebugInformationForLeaks]. * Rename `checkNonGCed` to `checkNotGCed`. -* Group items related to object LeakTracker, in class LeakTracker with single instance. +* Group global items related to leak tracking, in abstract class LeakTracking. * Rename `gcCountBuffer` to `numberOfGcCycles` and `disposalTimeBuffer` to `disposalTime`. # 8.0.3 From 8b9dfb8bedb538b96462c841e8cc5d63d2c1ba7b Mon Sep 17 00:00:00 2001 From: Polina Cherkasova Date: Tue, 25 Jul 2023 17:23:14 -0700 Subject: [PATCH 18/18] Update leak_tracking.dart --- pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart index 8ea1e1ed..ee451d0b 100644 --- a/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart +++ b/pkgs/leak_tracker/lib/src/leak_tracking/leak_tracking.dart @@ -33,7 +33,7 @@ abstract class LeakTracking { /// If [resetIfAlreadyStarted] is true and leak tracking is already on, /// the tracking will be reset with new configuration. /// - /// If [resetIfAlreadyStarted] is true and leak tracking is already on, + /// If [resetIfAlreadyStarted] is false and leak tracking is already on, /// [StateError] will be thrown. static void start({ LeakTrackingConfiguration config = const LeakTrackingConfiguration(),