| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library server.performance; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:convert'; | |
| 9 import 'dart:io'; | |
| 10 | |
| 11 import 'package:args/args.dart'; | |
| 12 import 'package:logging/logging.dart'; | |
| 13 import 'package:path/path.dart' as path; | |
| 14 | |
| 15 import 'driver.dart'; | |
| 16 import 'input_converter.dart'; | |
| 17 import 'operation.dart'; | |
| 18 | |
| 19 /** | |
| 20 * Launch and interact with the analysis server. | |
| 21 */ | |
| 22 main(List<String> rawArgs) { | |
| 23 Logger logger = new Logger('Performance Measurement Client'); | |
| 24 logger.onRecord.listen((LogRecord rec) { | |
| 25 print(rec.message); | |
| 26 }); | |
| 27 PerfArgs args = parseArgs(rawArgs); | |
| 28 | |
| 29 Driver driver = new Driver(logger); | |
| 30 Stream<Operation> stream = openInput(args); | |
| 31 StreamSubscription<Operation> subscription; | |
| 32 subscription = stream.listen((Operation op) { | |
| 33 Future future = driver.perform(op); | |
| 34 if (future != null) { | |
| 35 logger.log(Level.FINE, 'pausing operations for ${op.runtimeType}'); | |
| 36 subscription.pause(future.then((_) { | |
| 37 logger.log(Level.FINE, 'resuming operations'); | |
| 38 })); | |
| 39 } | |
| 40 }, onDone: () { | |
| 41 subscription.cancel(); | |
| 42 driver.stopServer(SHUTDOWN_TIMEOUT); | |
| 43 }, onError: (e, s) { | |
| 44 subscription.cancel(); | |
| 45 logger.log(Level.SEVERE, '$e\n$s'); | |
| 46 driver.stopServer(SHUTDOWN_TIMEOUT); | |
| 47 }); | |
| 48 driver.runComplete.then((Results results) { | |
| 49 results.printResults(); | |
| 50 }).whenComplete(() { | |
| 51 return subscription.cancel(); | |
| 52 }); | |
| 53 } | |
| 54 | |
| 55 const DIAGNOSTIC_PORT_OPTION = 'diagnosticPort'; | |
| 56 const HELP_CMDLINE_OPTION = 'help'; | |
| 57 const INPUT_CMDLINE_OPTION = 'input'; | |
| 58 const MAP_OPTION = 'map'; | |
| 59 | |
| 60 /** | |
| 61 * The amount of time to give the server to respond to a shutdown request | |
| 62 * before forcibly terminating it. | |
| 63 */ | |
| 64 const Duration SHUTDOWN_TIMEOUT = const Duration(seconds: 25); | |
| 65 | |
| 66 const TMP_SRC_DIR_OPTION = 'tmpSrcDir'; | |
| 67 const VERBOSE_CMDLINE_OPTION = 'verbose'; | |
| 68 const VERY_VERBOSE_CMDLINE_OPTION = 'vv'; | |
| 69 | |
| 70 /** | |
| 71 * Open and return the input stream specifying how this client | |
| 72 * should interact with the analysis server. | |
| 73 */ | |
| 74 Stream<Operation> openInput(PerfArgs args) { | |
| 75 var logger = new Logger('openInput'); | |
| 76 Stream<List<int>> inputRaw; | |
| 77 if (args.inputPath == 'stdin') { | |
| 78 inputRaw = stdin; | |
| 79 } else { | |
| 80 inputRaw = new File(args.inputPath).openRead(); | |
| 81 } | |
| 82 args.srcPathMap.forEach((oldPath, newPath) { | |
| 83 logger.log( | |
| 84 Level.INFO, 'mapping source path\n from $oldPath\n to $newPath'); | |
| 85 }); | |
| 86 logger.log(Level.INFO, 'tmpSrcDir: ${args.tmpSrcDirPath}'); | |
| 87 return inputRaw | |
| 88 .transform(SYSTEM_ENCODING.decoder) | |
| 89 .transform(new LineSplitter()) | |
| 90 .transform(new InputConverter(args.tmpSrcDirPath, args.srcPathMap, | |
| 91 diagnosticPort: args.diagnosticPort)); | |
| 92 } | |
| 93 | |
| 94 /** | |
| 95 * Parse the command line arguments. | |
| 96 */ | |
| 97 PerfArgs parseArgs(List<String> rawArgs) { | |
| 98 ArgParser parser = new ArgParser(); | |
| 99 | |
| 100 parser.addOption(INPUT_CMDLINE_OPTION, abbr: 'i', help: '<filePath>\n' | |
| 101 'The input file specifying how this client should interact with the server
.\n' | |
| 102 'If the input file name is "stdin", then the instructions are read from st
andard input.'); | |
| 103 parser.addOption(MAP_OPTION, | |
| 104 abbr: 'm', | |
| 105 allowMultiple: true, | |
| 106 splitCommas: false, | |
| 107 help: '<oldSrcPath>,<newSrcPath>\n' | |
| 108 'This option defines a mapping from the original source directory <oldSrcP
ath>\n' | |
| 109 'when the instrumentation or log file was generated\n' | |
| 110 'to the target source directory <newSrcPath> used during performance testi
ng.\n' | |
| 111 'Multiple mappings can be specified.\n' | |
| 112 'WARNING: The contents of the target directory will be modified'); | |
| 113 parser.addOption(TMP_SRC_DIR_OPTION, abbr: 't', help: '<dirPath>\n' | |
| 114 'The temporary directory containing source used during performance measure
ment.\n' | |
| 115 'WARNING: The contents of the target directory will be modified'); | |
| 116 parser.addOption(DIAGNOSTIC_PORT_OPTION, | |
| 117 abbr: 'd', | |
| 118 help: 'localhost port on which server will provide diagnostic web pages'); | |
| 119 parser.addFlag(VERBOSE_CMDLINE_OPTION, | |
| 120 abbr: 'v', help: 'Verbose logging', negatable: false); | |
| 121 parser.addFlag(VERY_VERBOSE_CMDLINE_OPTION, | |
| 122 help: 'Extra verbose logging', negatable: false); | |
| 123 parser.addFlag(HELP_CMDLINE_OPTION, | |
| 124 abbr: 'h', help: 'Print this help information', negatable: false); | |
| 125 | |
| 126 ArgResults args; | |
| 127 PerfArgs perfArgs = new PerfArgs(); | |
| 128 try { | |
| 129 args = parser.parse(rawArgs); | |
| 130 } on Exception catch (e) { | |
| 131 print(e); | |
| 132 printHelp(parser); | |
| 133 exit(1); | |
| 134 } | |
| 135 | |
| 136 bool showHelp = args[HELP_CMDLINE_OPTION] || args.rest.isNotEmpty; | |
| 137 | |
| 138 bool isMissing(key) => args[key] == null || args[key].isEmpty; | |
| 139 | |
| 140 perfArgs.inputPath = args[INPUT_CMDLINE_OPTION]; | |
| 141 if (isMissing(INPUT_CMDLINE_OPTION)) { | |
| 142 print('missing $INPUT_CMDLINE_OPTION argument'); | |
| 143 showHelp = true; | |
| 144 } | |
| 145 | |
| 146 perfArgs.srcPathMap = <String, String>{}; | |
| 147 for (String pair in args[MAP_OPTION]) { | |
| 148 if (pair is String) { | |
| 149 int index = pair.indexOf(','); | |
| 150 if (index != -1 && pair.indexOf(',', index + 1) == -1) { | |
| 151 String oldSrcPath = _withTrailingSeparator(pair.substring(0, index)); | |
| 152 String newSrcPath = _withTrailingSeparator(pair.substring(index + 1)); | |
| 153 if (new Directory(newSrcPath).existsSync()) { | |
| 154 perfArgs.srcPathMap[oldSrcPath] = newSrcPath; | |
| 155 continue; | |
| 156 } | |
| 157 } | |
| 158 } | |
| 159 print('must specifiy $MAP_OPTION <oldSrcPath>,<newSrcPath>'); | |
| 160 showHelp = true; | |
| 161 } | |
| 162 | |
| 163 perfArgs.tmpSrcDirPath = _withTrailingSeparator(args[TMP_SRC_DIR_OPTION]); | |
| 164 if (isMissing(TMP_SRC_DIR_OPTION)) { | |
| 165 print('missing $TMP_SRC_DIR_OPTION argument'); | |
| 166 showHelp = true; | |
| 167 } | |
| 168 | |
| 169 String portText = args[DIAGNOSTIC_PORT_OPTION]; | |
| 170 if (portText != null) { | |
| 171 perfArgs.diagnosticPort = int.parse(portText, onError: (s) { | |
| 172 print('invalid $DIAGNOSTIC_PORT_OPTION: $s'); | |
| 173 showHelp = true; | |
| 174 }); | |
| 175 } | |
| 176 | |
| 177 if (args[VERY_VERBOSE_CMDLINE_OPTION] || rawArgs.contains('-vv')) { | |
| 178 Logger.root.level = Level.FINE; | |
| 179 } else if (args[VERBOSE_CMDLINE_OPTION]) { | |
| 180 Logger.root.level = Level.INFO; | |
| 181 } else { | |
| 182 Logger.root.level = Level.WARNING; | |
| 183 } | |
| 184 | |
| 185 if (showHelp) { | |
| 186 printHelp(parser); | |
| 187 exit(1); | |
| 188 } | |
| 189 | |
| 190 return perfArgs; | |
| 191 } | |
| 192 | |
| 193 void printHelp(ArgParser parser) { | |
| 194 print(''); | |
| 195 print('Launch and interact with the AnalysisServer'); | |
| 196 print(''); | |
| 197 print(parser.usage); | |
| 198 } | |
| 199 | |
| 200 /** | |
| 201 * Ensure that the given path has a trailing separator | |
| 202 */ | |
| 203 String _withTrailingSeparator(String dirPath) { | |
| 204 if (dirPath != null && dirPath.length > 4) { | |
| 205 if (!dirPath.endsWith(path.separator)) { | |
| 206 return '$dirPath${path.separator}'; | |
| 207 } | |
| 208 } | |
| 209 return dirPath; | |
| 210 } | |
| 211 | |
| 212 /** | |
| 213 * The performance measurement arguments specified on the command line. | |
| 214 */ | |
| 215 class PerfArgs { | |
| 216 | |
| 217 /** | |
| 218 * The file path of the instrumentation or log file | |
| 219 * used to drive performance measurement, | |
| 220 * or 'stdin' if this information should be read from standard input. | |
| 221 */ | |
| 222 String inputPath; | |
| 223 | |
| 224 /** | |
| 225 * A mapping from the original source directory | |
| 226 * when the instrumentation or log file was generated | |
| 227 * to the target source directory used during performance testing. | |
| 228 */ | |
| 229 Map<String, String> srcPathMap; | |
| 230 | |
| 231 /** | |
| 232 * The temporary directory containing source used during performance measureme
nt. | |
| 233 */ | |
| 234 String tmpSrcDirPath; | |
| 235 | |
| 236 /** | |
| 237 * The diagnostic port for Analysis Server or `null` if none. | |
| 238 */ | |
| 239 int diagnosticPort; | |
| 240 } | |
| OLD | NEW |