diff --git a/packages/devtools_app/lib/src/screens/memory/memory_controller.dart b/packages/devtools_app/lib/src/screens/memory/memory_controller.dart index 7885269a1c1..7dcc327faa8 100644 --- a/packages/devtools_app/lib/src/screens/memory/memory_controller.dart +++ b/packages/devtools_app/lib/src/screens/memory/memory_controller.dart @@ -298,6 +298,9 @@ class MemoryController extends DisposableController ValueListenable get selectedSnapshotNotifier => _selectedSnapshotNotifier; + final _shouldShowLeaksTab = ValueNotifier(false); + ValueListenable get shouldShowLeaksTab => _shouldShowLeaksTab; + static String formattedTimestamp(DateTime? timestamp) => timestamp != null ? DateFormat('MMM dd HH:mm:ss').format(timestamp) : ''; @@ -815,7 +818,15 @@ class MemoryController extends DisposableController // TODO(terry): Need an event on the controller for this too? } - void _handleConnectionStart(ServiceConnectionManager serviceManager) { + void _refreshShouldShowLeaksTab() { + _shouldShowLeaksTab.value = serviceManager.serviceExtensionManager + .hasServiceExtension(memoryLeakTrackingExtensionName) + .value; + } + + void _handleConnectionStart(ServiceConnectionManager serviceManager) async { + _refreshShouldShowLeaksTab(); + _memoryTracker = MemoryTracker(this); _memoryTracker!.start(); diff --git a/packages/devtools_app/lib/src/screens/memory/memory_heap_tree_view.dart b/packages/devtools_app/lib/src/screens/memory/memory_heap_tree_view.dart index dd663b7da24..1230d1f22ec 100644 --- a/packages/devtools_app/lib/src/screens/memory/memory_heap_tree_view.dart +++ b/packages/devtools_app/lib/src/screens/memory/memory_heap_tree_view.dart @@ -32,6 +32,7 @@ import 'memory_graph_model.dart'; import 'memory_heap_treemap.dart'; import 'memory_instance_tree_view.dart'; import 'memory_snapshot_models.dart'; +import 'panes/leaks/leaks_pane.dart'; const memorySearchFieldKeyName = 'MemorySearchFieldKey'; @@ -143,6 +144,8 @@ class HeapTreeViewState extends State static const dartHeapAnalysisTabKey = Key('Dart Heap Analysis Tab'); @visibleForTesting static const dartHeapAllocationsTabKey = Key('Dart Heap Allocations Tab'); + @visibleForTesting + static const leaksTabKey = Key('Leaks Tab'); /// Below constants should match index for Tab index in DartHeapTabs. static const int analysisTabIndex = 0; @@ -150,19 +153,7 @@ class HeapTreeViewState extends State static const _gaPrefix = 'memoryTab'; - static final List dartHeapTabs = [ - DevToolsTab.create( - key: dartHeapAnalysisTabKey, - gaPrefix: _gaPrefix, - tabName: 'Analysis', - ), - DevToolsTab.create( - key: dartHeapAllocationsTabKey, - gaPrefix: _gaPrefix, - tabName: 'Allocations', - ), - ]; - + late List _tabs; late TabController _tabController; Widget? snapshotDisplay; @@ -192,12 +183,32 @@ class HeapTreeViewState extends State void initState() { super.initState(); - _tabController = TabController(length: dartHeapTabs.length, vsync: this); - addAutoDisposeListener(_tabController); - _animation = _setupBubbleAnimationController(); } + void _initTabs() { + _tabs = [ + DevToolsTab.create( + key: dartHeapAnalysisTabKey, + gaPrefix: _gaPrefix, + tabName: 'Analysis', + ), + DevToolsTab.create( + key: dartHeapAllocationsTabKey, + gaPrefix: _gaPrefix, + tabName: 'Allocations', + ), + if (widget.controller.shouldShowLeaksTab.value) + DevToolsTab.create( + key: leaksTabKey, + gaPrefix: _gaPrefix, + tabName: 'Leaks', + ), + ]; + + _tabController = TabController(length: _tabs.length, vsync: this); + } + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -205,6 +216,14 @@ class HeapTreeViewState extends State cancelListeners(); + _initTabs(); + + addAutoDisposeListener(controller.shouldShowLeaksTab, () { + setState(() { + _initTabs(); + }); + }); + addAutoDisposeListener(controller.selectedSnapshotNotifier, () { setState(() { controller.computeAllLibraries(rebuild: true); @@ -418,7 +437,7 @@ class HeapTreeViewState extends State labelColor: themeData.textTheme.bodyText1!.color, isScrollable: true, controller: _tabController, - tabs: HeapTreeViewState.dartHeapTabs, + tabs: _tabs, ), _buildSearchFilterControls(), ], @@ -454,6 +473,10 @@ class HeapTreeViewState extends State ], ), ), + + // Leaks tab. + if (controller.shouldShowLeaksTab.value) + const KeepAliveWrapper(child: LeaksPane()), ], ), ), diff --git a/packages/devtools_app/lib/src/screens/memory/panes/leaks/LEAK_TRACKING.md b/packages/devtools_app/lib/src/screens/memory/panes/leaks/LEAK_TRACKING.md new file mode 100644 index 00000000000..83c352a1965 --- /dev/null +++ b/packages/devtools_app/lib/src/screens/memory/panes/leaks/LEAK_TRACKING.md @@ -0,0 +1,3 @@ +# Memory Leaks Tracking with DevTools + +This page and functionality are under construction. See https://github.com/flutter/devtools/issues/3951. diff --git a/packages/devtools_app/lib/src/screens/memory/panes/leaks/leaks_pane.dart b/packages/devtools_app/lib/src/screens/memory/panes/leaks/leaks_pane.dart new file mode 100644 index 00000000000..fa6a4c6abeb --- /dev/null +++ b/packages/devtools_app/lib/src/screens/memory/panes/leaks/leaks_pane.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +class LeaksPane extends StatefulWidget { + const LeaksPane({Key? key}) : super(key: key); + + @override + State createState() => _LeaksPaneState(); +} + +class _LeaksPaneState extends State { + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text('Memory leak tracking functionality will be here.'), + ], + ); + } +} diff --git a/packages/devtools_app/lib/src/service/service_extensions.dart b/packages/devtools_app/lib/src/service/service_extensions.dart index b386c62a87f..9138124ac96 100644 --- a/packages/devtools_app/lib/src/service/service_extensions.dart +++ b/packages/devtools_app/lib/src/service/service_extensions.dart @@ -4,6 +4,7 @@ library service_extensions; +import 'package:devtools_shared/devtools_shared.dart'; import 'package:flutter/material.dart'; import '../analytics/constants.dart' as analytics_constants; @@ -526,9 +527,10 @@ final trackRebuildWidgets = ToggleableServiceExtensionDescription._( gaItem: analytics_constants.trackRebuildWidgets, ); -// This extension should never be displayed as a button so does not need a -// ServiceExtensionDescription object. +// This extensions below should never be displayed as a button so does not need +// a ServiceExtensionDescription object. const String didSendFirstFrameEvent = 'ext.flutter.didSendFirstFrameEvent'; +const String memoryLeakTracking = memoryLeakTrackingExtensionName; final List _extensionDescriptions = [ debugPaint, diff --git a/packages/devtools_shared/lib/devtools_shared.dart b/packages/devtools_shared/lib/devtools_shared.dart index 7274b71087b..185d3430721 100644 --- a/packages/devtools_shared/lib/devtools_shared.dart +++ b/packages/devtools_shared/lib/devtools_shared.dart @@ -8,5 +8,6 @@ export 'src/memory/class_heap_detail_stats.dart'; export 'src/memory/event_sample.dart'; export 'src/memory/heap_sample.dart'; export 'src/memory/heap_space.dart'; +export 'src/memory/leaks/constants.dart'; export 'src/memory/memory_json.dart'; export 'src/service_utils.dart'; diff --git a/packages/devtools_shared/lib/src/memory/leaks/README.md b/packages/devtools_shared/lib/src/memory/leaks/README.md new file mode 100644 index 00000000000..6eea8318dc7 --- /dev/null +++ b/packages/devtools_shared/lib/src/memory/leaks/README.md @@ -0,0 +1,2 @@ +Code in this folder will be moved to Dart SDK and/or Flutter framework after piloting the feature. +See https://github.com/flutter/devtools/issues/3951. diff --git a/packages/devtools_shared/lib/src/memory/leaks/constants.dart b/packages/devtools_shared/lib/src/memory/leaks/constants.dart new file mode 100644 index 00000000000..12789262af8 --- /dev/null +++ b/packages/devtools_shared/lib/src/memory/leaks/constants.dart @@ -0,0 +1,6 @@ +// Copyright 2021 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Name of extension that enables leak tracking for applications. +const memoryLeakTrackingExtensionName = 'ext.dart.memoryLeakTracking';