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 |