work in progress
diff --git a/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart b/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart index c8f8669..c7926f2 100644 --- a/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart +++ b/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart
@@ -17,7 +17,7 @@ import 'app_size_screen.dart'; // Temporary feature flag for deferred loading. -bool deferredLoadingSupportEnabled = false; +bool deferredLoadingSupportEnabled = true; const _artificialRootNodeName = 'ArtificialRoot'; const _entireAppNodeName = 'Entire App'; @@ -61,6 +61,20 @@ } } +class DiffTreeMap { + const DiffTreeMap({ + required this.combined, + required this.increaseOnly, + required this.decreaseOnly, + }); + + final TreemapNode? combined; + + final TreemapNode? increaseOnly; + + final TreemapNode? decreaseOnly; +} + class AppSizeController { static const unsupportedFileTypeError = 'Failed to load size analysis file: file type not supported.\n\n' @@ -252,9 +266,13 @@ ValueListenable<AppUnit> get selectedAppUnit => _selectedAppUnit; final _selectedAppUnit = ValueNotifier<AppUnit>(AppUnit.entireApp); - void changeSelectedAppUnit(AppUnit appUnit) { + void changeSelectedAppUnit(AppUnit appUnit, Key tabKey) { _selectedAppUnit.value = appUnit; - _loadApp(_dataForAppUnit!); + if (tabKey == AppSizeScreen.analysisTabKey) { + _loadApp(_dataForAppUnit!); + } else { + print('selected $appUnit in diff view'); + } } /// Notifies that the json files are currently being processed. @@ -343,7 +361,8 @@ ) { if (_hasDeferredInfo(jsonFile)) { jsonFile['children'] = _extractChildren(jsonFile) - .where((child) => child['isDeferred'] == true); + .where((child) => child['isDeferred'] == true) + .toList(); jsonFile['n'] = _deferredNodeName; } return jsonFile; @@ -385,55 +404,52 @@ await delayForBatchProcessing(micros: 10000); Map<String, dynamic> diffMap; - if (oldFile.isAnalyzeSizeFile && newFile.isAnalyzeSizeFile) { - var oldFileJson = oldFile.data as Map<String, dynamic>; - var newFileJson = newFile.data as Map<String, dynamic>; + Map<String, dynamic> mainDiffMap; + Map<String, dynamic> deferredDiffMap; - if (_hasDeferredInfo(oldFileJson) || _hasDeferredInfo(newFileJson)) { + if (oldFile.isAnalyzeSizeFile && newFile.isAnalyzeSizeFile) { + final oldFileJson = oldFile.data as Map<String, dynamic>; + final newFileJson = newFile.data as Map<String, dynamic>; + + if (!_hasDeferredInfo(oldFileJson) && !_hasDeferredInfo(newFileJson)) { + diffMap = _generateDiffMapFromAnalyzeSizeFiles( + oldFileJson: oldFileJson, + newFileJson: newFileJson, + ); + } else { _isDeferredApp.value = deferredLoadingSupportEnabled; + Map<String, dynamic> oldEntireAppFileJson = oldFileJson; + Map<String, dynamic> newEntireAppFileJson = newFileJson; if (!_hasDeferredInfo(oldFileJson)) { - oldFileJson = _wrapInArtificialRoot(oldFileJson); + oldEntireAppFileJson = _wrapInArtificialRoot(oldFileJson); } else if (!_hasDeferredInfo(newFileJson)) { - newFileJson = _wrapInArtificialRoot(newFileJson); + newEntireAppFileJson = _wrapInArtificialRoot(newFileJson); } - } - final oldApkProgramInfo = ProgramInfo(); - _apkJsonToProgramInfo( - program: oldApkProgramInfo, - parent: oldApkProgramInfo.root, - json: oldFileJson, - ); + final oldMainOnlyFileJson = _extractMainUnit(oldEntireAppFileJson); + final newMainOnlyFileJson = _extractMainUnit(newEntireAppFileJson); - // Extract the precompiler trace from the old file, if it exists, and - // generate a call graph. - final oldPrecompilerTrace = oldFileJson.remove('precompiler-trace'); - if (oldPrecompilerTrace != null) { - _oldDiffCallGraph = generateCallGraphWithDominators( - oldPrecompilerTrace, - NodeType.packageNode, + final oldDeferredOnlyFileJson = + _extractDeferredUnits(oldEntireAppFileJson); + final newDeferredOnlyFileJson = + _extractDeferredUnits(newEntireAppFileJson); + + diffMap = _generateDiffMapFromAnalyzeSizeFiles( + oldFileJson: oldEntireAppFileJson, + newFileJson: newEntireAppFileJson, + ); + + mainDiffMap = _generateDiffMapFromAnalyzeSizeFiles( + oldFileJson: oldMainOnlyFileJson, + newFileJson: newMainOnlyFileJson, + ); + + deferredDiffMap = _generateDiffMapFromAnalyzeSizeFiles( + oldFileJson: oldDeferredOnlyFileJson, + newFileJson: newDeferredOnlyFileJson, ); } - - final newApkProgramInfo = ProgramInfo(); - _apkJsonToProgramInfo( - program: newApkProgramInfo, - parent: newApkProgramInfo.root, - json: newFileJson, - ); - - // Extract the precompiler trace from the new file, if it exists, and - // generate a call graph. - final newPrecompilerTrace = newFileJson.remove('precompiler-trace'); - if (newPrecompilerTrace != null) { - _newDiffCallGraph = generateCallGraphWithDominators( - newPrecompilerTrace, - NodeType.packageNode, - ); - } - - diffMap = compareProgramInfo(oldApkProgramInfo, newApkProgramInfo); } else { try { diffMap = buildComparisonTreemap(oldFile.data, newFile.data); @@ -458,27 +474,78 @@ diffMap['n'] = isDeferredApp.value ? _entireAppNodeName : _rootNodeName; // TODO(peterdjlee): Try to move the non-active tree generation to separate isolates. - _combinedDiffTreeRoot = generateDiffTree( - diffMap, - DiffTreeType.combined, - skipNodesWithNoByteSizeChange: !isDeferredApp.value, - ); - _increasedDiffTreeRoot = generateDiffTree( - diffMap, - DiffTreeType.increaseOnly, - skipNodesWithNoByteSizeChange: !isDeferredApp.value, - ); - _decreasedDiffTreeRoot = generateDiffTree( - diffMap, - DiffTreeType.decreaseOnly, - skipNodesWithNoByteSizeChange: !isDeferredApp.value, - ); + final diffTreeMap = _generateDiffTrees(diffMap); + _combinedDiffTreeRoot = diffTreeMap.combined; + _increasedDiffTreeRoot = diffTreeMap.increaseOnly; + _decreasedDiffTreeRoot = diffTreeMap.decreaseOnly; changeDiffRoot(_activeDiffRoot); _processingNotifier.value = false; } + DiffTreeMap _generateDiffTrees(Map<String, dynamic> diffMap) { + // TODO(peterdjlee): Try to move the non-active tree generation to separate isolates. + return DiffTreeMap( + combined: generateDiffTree( + diffMap, + DiffTreeType.combined, + skipNodesWithNoByteSizeChange: !isDeferredApp.value, + ), + increaseOnly: generateDiffTree( + diffMap, + DiffTreeType.increaseOnly, + skipNodesWithNoByteSizeChange: !isDeferredApp.value, + ), + decreaseOnly: generateDiffTree( + diffMap, + DiffTreeType.decreaseOnly, + skipNodesWithNoByteSizeChange: !isDeferredApp.value, + ), + ); + } + + Map<String, dynamic> _generateDiffMapFromAnalyzeSizeFiles({ + required Map<String, dynamic> oldFileJson, + required Map<String, dynamic> newFileJson, + }) { + final oldApkProgramInfo = ProgramInfo(); + _apkJsonToProgramInfo( + program: oldApkProgramInfo, + parent: oldApkProgramInfo.root, + json: oldFileJson, + ); + + // Extract the precompiler trace from the old file, if it exists, and + // generate a call graph. + final oldPrecompilerTrace = oldFileJson.remove('precompiler-trace'); + if (oldPrecompilerTrace != null) { + _oldDiffCallGraph = generateCallGraphWithDominators( + oldPrecompilerTrace, + NodeType.packageNode, + ); + } + + final newApkProgramInfo = ProgramInfo(); + _apkJsonToProgramInfo( + program: newApkProgramInfo, + parent: newApkProgramInfo.root, + json: newFileJson, + ); + + // Extract the precompiler trace from the new file, if it exists, and + // generate a call graph. + final newPrecompilerTrace = newFileJson.remove('precompiler-trace'); + if (newPrecompilerTrace != null) { + _newDiffCallGraph = generateCallGraphWithDominators( + newPrecompilerTrace, + NodeType.packageNode, + ); + } + + return compareProgramInfo(oldApkProgramInfo, newApkProgramInfo); + } + Map<String, dynamic> _wrapInArtificialRoot(Map<String, dynamic> json) { json['n'] = _mainNodeName; return <String, dynamic>{
diff --git a/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart b/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart index 8ad5fb9..211c38a 100644 --- a/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart +++ b/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart
@@ -208,9 +208,7 @@ ), Row( children: [ - if (currentTab.key == AppSizeScreen.analysisTabKey && - isDeferredApp) - _buildAppUnitDropdown(), + if (isDeferredApp) _buildAppUnitDropdown(currentTab.key!), if (currentTab.key == AppSizeScreen.diffTabKey) _buildDiffTreeTypeDropdown(), const SizedBox(width: defaultSpacing), @@ -253,7 +251,7 @@ ); } - DropdownButtonHideUnderline _buildAppUnitDropdown() { + DropdownButtonHideUnderline _buildAppUnitDropdown(Key tabKey) { return DropdownButtonHideUnderline( key: AppSizeScreen.appUnitDropdownKey, child: DropdownButton<AppUnit>( @@ -265,7 +263,7 @@ ], onChanged: (newAppUnit) { setState(() { - controller.changeSelectedAppUnit(newAppUnit!); + controller.changeSelectedAppUnit(newAppUnit!, tabKey); }); }, ),