From 793367c498da7ec7efda1a74d6663a941aeec932 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 30 Nov 2022 20:13:05 +0000 Subject: [PATCH] [pkg/dartdev] add windows support for 'dart bug' Change-Id: Id94a4d9e4a485350cbe37f78d5395e093ccf5892 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272741 Commit-Queue: Devon Carew Reviewed-by: Ben Konyi --- pkg/dartdev/lib/dartdev.dart | 8 +-- .../lib/src/commands/{bug.dart => info.dart} | 47 +++++++------ pkg/dartdev/lib/src/processes.dart | 69 +++++++++++++++++-- ...g_linux_test.dart => info_linux_test.dart} | 6 +- ...g_macos_test.dart => info_macos_test.dart} | 6 +- .../{bug_test.dart => info_test.dart} | 10 +-- .../test/commands/info_windows_test.dart | 60 ++++++++++++++++ pkg/dartdev/test/test_all.dart | 12 ++-- pkg/pkg.status | 7 +- 9 files changed, 174 insertions(+), 51 deletions(-) rename pkg/dartdev/lib/src/commands/{bug.dart => info.dart} (78%) rename pkg/dartdev/test/commands/{bug_linux_test.dart => info_linux_test.dart} (91%) rename pkg/dartdev/test/commands/{bug_macos_test.dart => info_macos_test.dart} (90%) rename pkg/dartdev/test/commands/{bug_test.dart => info_test.dart} (90%) create mode 100644 pkg/dartdev/test/commands/info_windows_test.dart diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart index 50e0b7faf54..b1bf033a2ed 100644 --- a/pkg/dartdev/lib/dartdev.dart +++ b/pkg/dartdev/lib/dartdev.dart @@ -17,7 +17,6 @@ import 'package:usage/usage.dart'; import 'src/analytics.dart'; import 'src/commands/analyze.dart'; -import 'src/commands/bug.dart'; import 'src/commands/compile.dart'; import 'src/commands/compile_server_shutdown.dart'; import 'src/commands/create.dart'; @@ -25,6 +24,7 @@ import 'src/commands/debug_adapter.dart'; import 'src/commands/devtools.dart'; import 'src/commands/doc.dart'; import 'src/commands/fix.dart'; +import 'src/commands/info.dart'; import 'src/commands/language_server.dart'; import 'src/commands/migrate.dart'; import 'src/commands/run.dart'; @@ -122,14 +122,14 @@ class DartdevRunner extends CommandRunner { ); addCommand(AnalyzeCommand(verbose: verbose)); - addCommand(BugCommand(verbose: verbose)); + addCommand(CompileCommand(verbose: verbose)); addCommand(CreateCommand(verbose: verbose)); addCommand(DebugAdapterCommand(verbose: verbose)); - addCommand(CompileCommand(verbose: verbose)); - addCommand(DocCommand(verbose: verbose)); addCommand(DevToolsCommand(verbose: verbose)); + addCommand(DocCommand(verbose: verbose)); addCommand(FixCommand(verbose: verbose)); addCommand(FormatCommand(verbose: verbose)); + addCommand(InfoCommand(verbose: verbose)); addCommand(LanguageServerCommand(verbose: verbose)); addCommand(MigrateCommand(verbose: verbose)); addCommand( diff --git a/pkg/dartdev/lib/src/commands/bug.dart b/pkg/dartdev/lib/src/commands/info.dart similarity index 78% rename from pkg/dartdev/lib/src/commands/bug.dart rename to pkg/dartdev/lib/src/commands/info.dart index 1e773e88e7d..4f28c34ce41 100644 --- a/pkg/dartdev/lib/src/commands/bug.dart +++ b/pkg/dartdev/lib/src/commands/info.dart @@ -11,26 +11,33 @@ import '../core.dart'; import '../processes.dart'; import '../utils.dart'; -// TODO(devoncarew): have a flag to elide paths (enabled by default) - -const bool _elideFilePaths = true; - /// Print output useful for diagnosing local issues. -class BugCommand extends DartdevCommand { - static const String cmdName = 'bug'; +class InfoCommand extends DartdevCommand { + static const String cmdName = 'info'; static const String cmdDescription = 'Show diagnostic information about the installed tooling.'; - BugCommand({bool verbose = false}) : super(cmdName, cmdDescription, verbose); + InfoCommand({bool verbose = false}) + : super(cmdName, cmdDescription, verbose) { + argParser.addFlag( + removeFilePathsFlag, + help: 'Remove file paths in displayed information.', + defaultsTo: true, + ); + } static const String _message = 'If providing this information as part of reporting a bug, please review ' - 'the information below carefully to ensure it only contains things ' - "you're comfortable posting publicly."; + "the information below to ensure it only contains things you're " + 'comfortable posting publicly.'; + + static const String removeFilePathsFlag = 'remove-file-paths'; @override FutureOr run() async { + final elideFilePaths = argResults![removeFilePathsFlag] as bool; + print(''); print(wrapText(_message, width: dartdevUsageLineLength)); @@ -43,7 +50,7 @@ class BugCommand extends DartdevCommand { print('- locale is ${Platform.localeName}'); // project information - var projectInfo = getProjectInfo(project, onlySimpleDeps: _elideFilePaths); + var projectInfo = getProjectInfo(project, onlySimpleDeps: elideFilePaths); if (projectInfo != null) { print(''); print('#### Project info'); @@ -58,7 +65,7 @@ class BugCommand extends DartdevCommand { // process information var processInfo = - ProcessInfo.getProcessInfo(elideFilePaths: _elideFilePaths); + ProcessInfo.getProcessInfo(elideFilePaths: elideFilePaths); if (processInfo != null) { print(''); print('#### Process info'); @@ -77,11 +84,14 @@ class BugCommand extends DartdevCommand { for (var process in processInfo) { var row = table.startRow(); row.cell('${process.memoryMb} MB', right: true); - row.cell('${process.cpuPercent.toStringAsFixed(1)}%', right: true); - row.cell(process.elapsedTime, right: true); - row.cell(_elideFilePaths - ? _noMoreThan(process.commandLine, MarkdownTable.defaultMaxWidth) - : process.commandLine); + row.cell( + process.cpuPercent == null + ? '--' + : '${process.cpuPercent!.toStringAsFixed(1)}%', + right: true, + ); + row.cell(process.elapsedTime ?? '', right: true); + row.cell(process.commandLine); } print(table.finish().trimRight()); @@ -148,8 +158,3 @@ class ProjectInfo { required this.elidedDependencies, }); } - -String _noMoreThan(String value, int length) { - if (value.length <= length) return value; - return '${value.substring(0, length - 1)}…'; -} diff --git a/pkg/dartdev/lib/src/processes.dart b/pkg/dartdev/lib/src/processes.dart index 79f3b3f6e4a..7e5bc43c643 100644 --- a/pkg/dartdev/lib/src/processes.dart +++ b/pkg/dartdev/lib/src/processes.dart @@ -4,7 +4,7 @@ import 'dart:io'; -// TODO(devoncarew): Support windows. +import 'package:meta/meta.dart'; /// A utility class to get information about the Dart related process running on /// this machine. @@ -12,8 +12,8 @@ class ProcessInfo { static final wsRegex = RegExp(r'\s+'); final int memoryMb; - final double cpuPercent; - final String elapsedTime; + final double? cpuPercent; + final String? elapsedTime; final String command; final String commandLine; @@ -91,6 +91,36 @@ class ProcessInfo { } } + @visibleForTesting + static ProcessInfo? parseWindows(String line) { + String stripQuotes(String item) { + if (item.startsWith('"')) item = item.substring(1); + if (item.endsWith('"')) item = item.substring(0, item.length - 1); + return item; + } + + // "dart.exe","12068","Console","1","233,384 K" + var items = stripQuotes(line).split('","'); + + if (items.isEmpty) { + return null; + } + + int parseMemory(String value) { + if (value.contains(' ')) value = value.substring(0, value.indexOf(' ')); + value = value.replaceAll(',', ''); + return (int.tryParse(value) ?? 0) ~/ 1024; + } + + return ProcessInfo._( + command: items.first, + memoryMb: parseMemory(items.length >= 5 ? items[4] : '0'), + cpuPercent: null, + elapsedTime: null, + commandLine: items.first, + ); + } + const ProcessInfo._({ required this.memoryMb, required this.cpuPercent, @@ -102,7 +132,7 @@ class ProcessInfo { /// Return the Dart related processes. /// /// This will try to exclude the process for the VM currently running - /// 'dart bug'. + /// 'dart info'. /// /// This will return `null` if we don't support listing the process on the /// current platform. @@ -113,12 +143,14 @@ class ProcessInfo { processInfo = _getProcessInfoMacOS(elideFilePaths: elideFilePaths); } else if (Platform.isLinux) { processInfo = _getProcessInfoLinux(elideFilePaths: elideFilePaths); + } else if (Platform.isWindows) { + processInfo = _getProcessInfoWindows(); } if (processInfo != null) { - // Remove the 'dart bug' entry. + // Remove the 'dart info' entry. processInfo = processInfo - .where((process) => process.commandLine != 'dart bug') + .where((process) => process.commandLine != 'dart info') .toList(); // Sort. @@ -209,8 +241,31 @@ List _getProcessInfoLinux({bool elideFilePaths = true}) { .toList(); } +List _getProcessInfoWindows() { + // TODO(devoncarew): Use tasklist /v to retrieve process elapsed time info. + var result = Process.runSync('tasklist', ['/nh', '/fo', 'csv']); + if (result.exitCode != 0) { + return const []; + } + + // "smss.exe","608","Services","0","288 K" + // "csrss.exe","888","Services","0","3,084 K" + // "wininit.exe","628","Services","0","1,104 K" + // "dart.exe","12068","Console","1","233,384 K" + + var lines = (result.stdout as String).split('\n'); + return lines + .skip(1) + .map((line) => line.trim()) + .where((line) => line.isNotEmpty) + .map((line) => ProcessInfo.parseWindows(line)) + .whereType() + .where(_isProcessDartRelated) + .toList(); +} + bool _isProcessDartRelated(ProcessInfo process) { - return process.command == 'dart'; + return process.command == 'dart' || process.command == 'dart.exe'; } String _getCommandFrom(String commandLine) { diff --git a/pkg/dartdev/test/commands/bug_linux_test.dart b/pkg/dartdev/test/commands/info_linux_test.dart similarity index 91% rename from pkg/dartdev/test/commands/bug_linux_test.dart rename to pkg/dartdev/test/commands/info_linux_test.dart index 7a6e3c28b6d..bf9f94c67c9 100644 --- a/pkg/dartdev/test/commands/bug_linux_test.dart +++ b/pkg/dartdev/test/commands/info_linux_test.dart @@ -31,24 +31,24 @@ void main() { }); }); - group('bug linux', () { + group('info linux', () { late TestProject p; tearDown(() async => await p.dispose()); test('shows process info', () async { p = project(mainSrc: 'void main() {}'); - final runResult = await p.run(['bug']); + final runResult = await p.run(['info']); expect(runResult.stderr, isEmpty); expect(runResult.exitCode, 0); var output = runResult.stdout as String; + expect(output, contains('providing this information')); expect(output, contains('## Process info')); expect(output, contains('Memory |')); expect(output, contains('| dart ')); - expect(output, contains(' bug')); }); }, timeout: longTimeout); } diff --git a/pkg/dartdev/test/commands/bug_macos_test.dart b/pkg/dartdev/test/commands/info_macos_test.dart similarity index 90% rename from pkg/dartdev/test/commands/bug_macos_test.dart rename to pkg/dartdev/test/commands/info_macos_test.dart index 204a2a41579..4fb53e62211 100644 --- a/pkg/dartdev/test/commands/bug_macos_test.dart +++ b/pkg/dartdev/test/commands/info_macos_test.dart @@ -26,24 +26,24 @@ void main() { }); }); - group('bug macos', () { + group('info macos', () { late TestProject p; tearDown(() async => await p.dispose()); test('shows process info', () async { p = project(mainSrc: 'void main() {}'); - final runResult = await p.run(['bug']); + final runResult = await p.run(['info']); expect(runResult.stderr, isEmpty); expect(runResult.exitCode, 0); var output = runResult.stdout as String; + expect(output, contains('providing this information')); expect(output, contains('## Process info')); expect(output, contains('| Memory')); expect(output, contains('| dart ')); - expect(output, contains(' bug')); }); }, timeout: longTimeout); } diff --git a/pkg/dartdev/test/commands/bug_test.dart b/pkg/dartdev/test/commands/info_test.dart similarity index 90% rename from pkg/dartdev/test/commands/bug_test.dart rename to pkg/dartdev/test/commands/info_test.dart index 13b364bd3e5..8d441848563 100644 --- a/pkg/dartdev/test/commands/bug_test.dart +++ b/pkg/dartdev/test/commands/info_test.dart @@ -2,21 +2,21 @@ // 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:dartdev/src/commands/bug.dart'; +import 'package:dartdev/src/commands/info.dart'; import 'package:dartdev/src/core.dart'; import 'package:test/test.dart'; import '../utils.dart'; void main() { - group('bug', () { + group('info', () { late TestProject p; tearDown(() async => await p.dispose()); test('--help', () async { p = project(); - final result = await p.run(['bug', '--help']); + final result = await p.run(['info', '--help']); expect(result.stdout, isNotEmpty); expect(result.stdout, @@ -27,7 +27,7 @@ void main() { test('shows general info', () async { p = project(mainSrc: 'void main() {}'); - final runResult = await p.run(['bug']); + final runResult = await p.run(['info']); expect(runResult.stderr, isEmpty); expect(runResult.exitCode, 0); @@ -40,7 +40,7 @@ void main() { test('shows project info', () async { p = project(mainSrc: 'void main() {}'); - final runResult = await p.run(['bug']); + final runResult = await p.run(['info']); expect(runResult.stderr, isEmpty); expect(runResult.exitCode, 0); diff --git a/pkg/dartdev/test/commands/info_windows_test.dart b/pkg/dartdev/test/commands/info_windows_test.dart new file mode 100644 index 00000000000..2370d0e76da --- /dev/null +++ b/pkg/dartdev/test/commands/info_windows_test.dart @@ -0,0 +1,60 @@ +// 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. + +@TestOn('windows') + +import 'package:dartdev/src/processes.dart'; +import 'package:test/test.dart'; + +import '../utils.dart'; + +void main() { + group('process listing', () { + test('windows', () { + var results = ProcessInfo.getProcessInfo(); + + expect(results, isNotNull); + expect(results, isNotEmpty); + + for (var process in results!) { + expect(process.memoryMb, greaterThan(0)); + expect(process.cpuPercent, null); + expect(process.elapsedTime, null); + expect(process.commandLine, startsWith('dart.exe')); + } + }); + + test('ProcessInfo.parseWindows', () { + final testLine = '"dart.exe","12068","Console","1","233,384 K"'; + + var result = ProcessInfo.parseWindows(testLine); + + expect(result, isNotNull); + expect(result!.command, 'dart.exe'); + // 233384kb == 227MB + expect(result.memoryMb, 227); + }); + }); + + group('info windows', () { + late TestProject p; + + tearDown(() async => await p.dispose()); + + test('shows process info', () async { + p = project(mainSrc: 'void main() {}'); + final runResult = await p.run(['info']); + + expect(runResult.stderr, isEmpty); + expect(runResult.exitCode, 0); + + var output = runResult.stdout as String; + + expect(output, contains('providing this information')); + expect(output, contains('## Process info')); + expect(output, contains('| Memory')); + expect(output, contains('| dart.exe ')); + }); + }, timeout: longTimeout); +} diff --git a/pkg/dartdev/test/test_all.dart b/pkg/dartdev/test/test_all.dart index 1e98dcc5e96..a096975e349 100644 --- a/pkg/dartdev/test/test_all.dart +++ b/pkg/dartdev/test/test_all.dart @@ -6,13 +6,13 @@ import 'package:test/test.dart'; import 'analytics_test.dart' as analytics; import 'commands/analyze_test.dart' as analyze; -import 'commands/bug_test.dart' as bug; import 'commands/compile_test.dart' as compile; import 'commands/create_test.dart' as create; import 'commands/fix_test.dart' as fix; import 'commands/flag_test.dart' as flag; import 'commands/format_test.dart' as format; import 'commands/help_test.dart' as help; +import 'commands/info_test.dart' as info; import 'commands/language_server_test.dart' as language_server; import 'commands/migrate_test.dart' as migrate; import 'commands/pub_test.dart' as pub; @@ -32,26 +32,26 @@ void main() { group('dart', () { analytics.main(); analyze.main(); - bug.main(); + compile.main(); + core.main(); create.main(); experiments.main(); - fix.main(); fix_driver.main(); + fix.main(); flag.main(); format.main(); help.main(); implicit_smoke.main(); + info.main(); invalid_smoke.main(); language_server.main(); migrate.main(); no_such_file.main(); pub.main(); run.main(); - compile.main(); - test.main(); - core.main(); sdk.main(); smoke.main(); + test.main(); utils.main(); }); } diff --git a/pkg/pkg.status b/pkg/pkg.status index e6404b8bbb5..17128f09bd1 100644 --- a/pkg/pkg.status +++ b/pkg/pkg.status @@ -220,10 +220,10 @@ async/test/stream_zip_test: SkipSlow # Times out. Issue 22050 collection/test/unmodifiable_collection_test: SkipSlow # Times out. Issue 22050 [ $runtime == vm && $system != linux ] -dartdev/test/commands/bug_linux_test: SkipByDesign +dartdev/test/commands/info_linux_test: SkipByDesign [ $runtime == vm && $system != macos ] -dartdev/test/commands/bug_macos_test: SkipByDesign +dartdev/test/commands/info_macos_test: SkipByDesign [ $runtime == vm && $system == windows ] analysis_server/test/analysis/get_errors_test: Skip # runtime error, Issue 22180 @@ -234,6 +234,9 @@ analyzer/tool/task_dependency_graph/check_test: Slow, Pass [ $runtime == vm && $system == windows && $checked ] front_end/tool/perf_test: Slow, Pass +[ $runtime == vm && $system != windows ] +dartdev/test/commands/info_windows_test: SkipByDesign + [ $runtime == vm && $checked ] analysis_server/test/completion_test: Slow, Pass analysis_server/test/integration/edit/sort_members_test: Slow, Pass