[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 <devoncarew@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Devon Carew 2022-11-30 20:13:05 +00:00 committed by Commit Queue
parent 77d04c4ed0
commit 793367c498
9 changed files with 174 additions and 51 deletions

View file

@ -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<int> {
);
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(

View file

@ -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<int> 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)}';
}

View file

@ -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<ProcessInfo> _getProcessInfoLinux({bool elideFilePaths = true}) {
.toList();
}
List<ProcessInfo> _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<ProcessInfo>()
.where(_isProcessDartRelated)
.toList();
}
bool _isProcessDartRelated(ProcessInfo process) {
return process.command == 'dart';
return process.command == 'dart' || process.command == 'dart.exe';
}
String _getCommandFrom(String commandLine) {

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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();
});
}

View file

@ -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