trace.dart 3.57 KB
Newer Older
1 2 3 4 5
// 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.

import 'dart:async';
6
import 'dart:convert';
7

8
import '../base/common.dart';
9
import '../base/file_system.dart';
10
import '../base/utils.dart';
11
import '../cache.dart';
12
import '../globals.dart';
13
import '../runner/flutter_command.dart';
14
import '../tracing.dart';
15

16
class TraceCommand extends FlutterCommand {
17
  TraceCommand() {
18
    requiresPubspecYaml();
19
    argParser.addOption('debug-port',
20 21 22 23 24 25
      help: 'Local port where the observatory is listening. Required.',
    );
    argParser.addFlag('start', negatable: false, help: 'Start tracing. Implied if --stop is also omitted.');
    argParser.addFlag('stop', negatable: false, help: 'Stop tracing. Implied if --start is also omitted.');
    argParser.addOption('duration',
      abbr: 'd',
26
      help: 'Time to wait after starting (if --start is specified or implied) and before '
27 28 29 30
            'stopping (if --stop is specified or implied).\n'
            'Defaults to ten seconds if --stop is specified or implied, zero otherwise.',
    );
    argParser.addOption('out', help: 'Specify the path of the saved trace file.');
31 32
  }

33
  @override
34
  final String name = 'trace';
35 36

  @override
37
  final String description = 'Start and stop tracing for a running Flutter app.';
38 39

  @override
Devon Carew's avatar
Devon Carew committed
40
  final String usageFooter =
41 42
    '\`trace\` called without the --start or --stop flags will automatically start tracing, '
    'delay a set amount of time (controlled by --duration), and stop tracing. To explicitly '
43 44
    'control tracing, call trace with --start and later with --stop.\n'
    'The --debug-port argument is required.';
45 46

  @override
47
  Future<FlutterCommandResult> runCommand() async {
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    int observatoryPort;
    if (argResults.wasParsed('debug-port')) {
      observatoryPort = int.tryParse(argResults['debug-port']);
    }
    if (observatoryPort == null) {
      throwToolExit('The --debug-port argument must be specified.');
    }

    bool start = argResults['start'];
    bool stop = argResults['stop'];
    if (!start && !stop) {
      start = true;
      stop = true;
    }
    assert(start || stop);

    Duration duration;
    if (argResults.wasParsed('duration')) {
      try {
67
        duration = Duration(seconds: int.parse(argResults['duration']));
68 69 70 71 72 73
      } on FormatException {
        throwToolExit('Invalid duration passed to --duration; it should be a positive number of seconds.');
      }
    } else {
      duration = stop ? const Duration(seconds: 10) : Duration.zero;
    }
74

75 76
    // TODO(danrubel): this will break if we move to the new observatory URL
    // See https://github.com/flutter/flutter/issues/7038
77
    final Uri observatoryUri = Uri.parse('http://127.0.0.1:$observatoryPort');
78

79 80 81
    Tracing tracing;

    try {
82
      tracing = await Tracing.connect(observatoryUri);
83
    } catch (error) {
84
      throwToolExit('Error connecting to observatory: $error');
85 86
    }

87 88
    Cache.releaseLockEarly();

89
    if (start)
90
      await tracing.startTracing();
91
    await Future<void>.delayed(duration);
92
    if (stop)
93
      await _stopTracing(tracing);
94 95

    return null;
96 97
  }

98
  Future<void> _stopTracing(Tracing tracing) async {
99
    final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline();
100
    File localFile;
101 102

    if (argResults['out'] != null) {
103
      localFile = fs.file(argResults['out']);
104
    } else {
105
      localFile = getUniqueFile(fs.currentDirectory, 'trace', 'json');
106
    }
107

108
    await localFile.writeAsString(json.encode(timeline));
109

110
    printStatus('Trace file saved to ${localFile.path}');
111 112
  }
}