mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
Add sky_tools trace command, Android implementation, and basic test.
This commit is contained in:
parent
00bed774ce
commit
b8085cd033
|
@ -18,6 +18,7 @@ import 'package:sky_tools/src/logs.dart';
|
||||||
import 'package:sky_tools/src/run_mojo.dart';
|
import 'package:sky_tools/src/run_mojo.dart';
|
||||||
import 'package:sky_tools/src/start.dart';
|
import 'package:sky_tools/src/start.dart';
|
||||||
import 'package:sky_tools/src/stop.dart';
|
import 'package:sky_tools/src/stop.dart';
|
||||||
|
import 'package:sky_tools/src/trace.dart';
|
||||||
|
|
||||||
class FlutterCommandRunner extends CommandRunner {
|
class FlutterCommandRunner extends CommandRunner {
|
||||||
FlutterCommandRunner()
|
FlutterCommandRunner()
|
||||||
|
@ -155,5 +156,6 @@ void main(List<String> args) {
|
||||||
..addCommand(new RunMojoCommand())
|
..addCommand(new RunMojoCommand())
|
||||||
..addCommand(new StartCommand())
|
..addCommand(new StartCommand())
|
||||||
..addCommand(new StopCommand())
|
..addCommand(new StopCommand())
|
||||||
|
..addCommand(new TraceCommand())
|
||||||
..run(args);
|
..run(args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -339,6 +339,62 @@ class AndroidDevice extends _Device {
|
||||||
], prefix: 'ANDROID: ');
|
], prefix: 'ANDROID: ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void startTracing(AndroidApk apk) {
|
||||||
|
runCheckedSync([
|
||||||
|
adbPath,
|
||||||
|
'shell',
|
||||||
|
'am',
|
||||||
|
'broadcast',
|
||||||
|
'-a',
|
||||||
|
'${apk.appPackageID}.TRACING_START'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
String stopTracing(AndroidApk apk) {
|
||||||
|
clearLogs();
|
||||||
|
runCheckedSync([
|
||||||
|
adbPath,
|
||||||
|
'shell',
|
||||||
|
'am',
|
||||||
|
'broadcast',
|
||||||
|
'-a',
|
||||||
|
'${apk.appPackageID}.TRACING_STOP'
|
||||||
|
]);
|
||||||
|
|
||||||
|
RegExp traceRegExp = new RegExp(r'Saving trace to (\S+)', multiLine: true);
|
||||||
|
RegExp completeRegExp = new RegExp(r'Trace complete', multiLine: true);
|
||||||
|
|
||||||
|
String tracePath = null;
|
||||||
|
bool isComplete = false;
|
||||||
|
while (!isComplete) {
|
||||||
|
String logs = runSync([adbPath, 'logcat', '-d']);
|
||||||
|
Match fileMatch = traceRegExp.firstMatch(logs);
|
||||||
|
if (fileMatch[1] != null) {
|
||||||
|
tracePath = fileMatch[1];
|
||||||
|
}
|
||||||
|
isComplete = completeRegExp.hasMatch(logs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tracePath != null) {
|
||||||
|
// adb root exits with 0 even if the command fails,
|
||||||
|
// so check the output string
|
||||||
|
String output = runSync([adbPath, 'root']);
|
||||||
|
if (new RegExp(r'.*cannot run as root.*').hasMatch(output)) {
|
||||||
|
_logging
|
||||||
|
.severe('Unable to download trace "${path.basename(tracePath)}"\n'
|
||||||
|
'You need to be able to run adb as root '
|
||||||
|
'on your android device');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
runSync([adbPath, 'pull', tracePath]);
|
||||||
|
runSync([adbPath, 'shell', 'rm', tracePath]);
|
||||||
|
return path.basename(tracePath);
|
||||||
|
}
|
||||||
|
_logging.warning('No trace file detected. '
|
||||||
|
'Did you remember to start the trace before stopping it?');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool isConnected() => _hasValidAndroid;
|
bool isConnected() => _hasValidAndroid;
|
||||||
}
|
}
|
||||||
|
|
69
packages/flutter_tools/lib/src/trace.dart
Normal file
69
packages/flutter_tools/lib/src/trace.dart
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
library sky_tools.trace;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:sky_tools/src/application_package.dart';
|
||||||
|
import 'package:sky_tools/src/device.dart';
|
||||||
|
|
||||||
|
final Logger _logging = new Logger('sky_tools.trace');
|
||||||
|
|
||||||
|
class TraceCommand extends Command {
|
||||||
|
final name = 'trace';
|
||||||
|
final description = 'Start and stop tracing a running Flutter app '
|
||||||
|
'(Android only, requires root).\n'
|
||||||
|
'To start a trace, wait, and then stop the trace, don\'t set any flags '
|
||||||
|
'except (optionally) duration.\n'
|
||||||
|
'Otherwise, specify either start or stop to manually control the trace.';
|
||||||
|
AndroidDevice android = null;
|
||||||
|
|
||||||
|
TraceCommand([this.android]) {
|
||||||
|
argParser.addFlag('start', negatable: false, help: 'Start tracing.');
|
||||||
|
argParser.addFlag('stop', negatable: false, help: 'Stop tracing.');
|
||||||
|
argParser.addOption('duration',
|
||||||
|
defaultsTo: '10', abbr: 'd', help: 'Duration in seconds to trace.');
|
||||||
|
if (android == null) {
|
||||||
|
android = new AndroidDevice();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<int> run() async {
|
||||||
|
if (!android.isConnected()) {
|
||||||
|
_logging.warning('No device connected, so no trace was completed.');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Map<BuildPlatform, ApplicationPackage> packages =
|
||||||
|
ApplicationPackageFactory.getAvailableApplicationPackages();
|
||||||
|
ApplicationPackage androidApp = packages[BuildPlatform.android];
|
||||||
|
|
||||||
|
if ((!argResults['start'] && !argResults['stop']) ||
|
||||||
|
(argResults['start'] && argResults['stop'])) {
|
||||||
|
// Setting neither flags or both flags means do both commands and wait
|
||||||
|
// duration seconds in between.
|
||||||
|
android.startTracing(androidApp);
|
||||||
|
await new Future.delayed(
|
||||||
|
new Duration(seconds: int.parse(argResults['duration'])),
|
||||||
|
() => _stopTracing(androidApp));
|
||||||
|
} else if (argResults['stop']) {
|
||||||
|
_stopTracing(androidApp);
|
||||||
|
} else {
|
||||||
|
android.startTracing(androidApp);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _stopTracing(AndroidApk androidApp) {
|
||||||
|
String tracePath = android.stopTracing(androidApp);
|
||||||
|
if (tracePath == null) {
|
||||||
|
_logging.warning('No trace file saved.');
|
||||||
|
} else {
|
||||||
|
_logging.warning('Trace file saved to $tracePath');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
packages/flutter_tools/test/trace_test.dart
Normal file
33
packages/flutter_tools/test/trace_test.dart
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright 2015 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.
|
||||||
|
|
||||||
|
library trace_test;
|
||||||
|
|
||||||
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:sky_tools/src/application_package.dart';
|
||||||
|
import 'package:sky_tools/src/trace.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import 'src/common.dart';
|
||||||
|
|
||||||
|
main() => defineTests();
|
||||||
|
|
||||||
|
defineTests() {
|
||||||
|
group('trace', () {
|
||||||
|
test('returns 1 when no Android device is connected', () {
|
||||||
|
ApplicationPackageFactory.srcPath = './';
|
||||||
|
ApplicationPackageFactory.setBuildPath(
|
||||||
|
BuildType.prebuilt, BuildPlatform.android, './');
|
||||||
|
|
||||||
|
MockAndroidDevice android = new MockAndroidDevice();
|
||||||
|
when(android.isConnected()).thenReturn(false);
|
||||||
|
TraceCommand command = new TraceCommand(android);
|
||||||
|
|
||||||
|
CommandRunner runner = new CommandRunner('test_flutter', '')
|
||||||
|
..addCommand(command);
|
||||||
|
runner.run(['trace']).then((int code) => expect(code, equals(1)));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue