OLD | NEW |
---|---|
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library debugger; | 5 library debugger; |
6 | 6 |
7 import "dart:async"; | 7 import "dart:async"; |
8 import "dart:io"; | 8 import "dart:io"; |
9 | 9 |
10 import "package:ddbg/commando.dart"; | 10 import "package:ddbg/commando.dart"; |
11 import "package:observatory/service_io.dart"; | 11 import "package:observatory/service_io.dart"; |
12 | 12 |
13 class Debugger { | 13 class Debugger { |
14 Commando cmdo; | 14 Commando cmdo; |
15 var _cmdoSubscription; | 15 var _cmdoSubscription; |
16 | 16 |
17 CommandList _commands; | |
18 | |
17 VM _vm; | 19 VM _vm; |
18 VM get vm => _vm; | 20 VM get vm => _vm; |
19 set vm(VM vm) { | 21 set vm(VM vm) { |
20 if (_vm == vm) { | 22 if (_vm == vm) { |
21 // Do nothing. | 23 // Do nothing. |
22 return; | 24 return; |
23 } | 25 } |
24 if (_vm != null) { | 26 if (_vm != null) { |
25 _vm.disconnect(); | 27 _vm.disconnect(); |
26 } | 28 } |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
84 error is ServiceError) { | 86 error is ServiceError) { |
85 // These are handled elsewhere. Ignore. | 87 // These are handled elsewhere. Ignore. |
86 return; | 88 return; |
87 } | 89 } |
88 cmdo.print('\n--------\nExiting due to unexpected error:\n' | 90 cmdo.print('\n--------\nExiting due to unexpected error:\n' |
89 ' $error\n$trace\n'); | 91 ' $error\n$trace\n'); |
90 quit(); | 92 quit(); |
91 } | 93 } |
92 | 94 |
93 Debugger() { | 95 Debugger() { |
94 cmdo = new Commando(completer: completeCommand); | 96 cmdo = new Commando(completer: _completeCommand); |
95 _cmdoSubscription = cmdo.commands.listen(_processCommand, | 97 _cmdoSubscription = cmdo.commands.listen(_processCommand, |
96 onError: _cmdoError, | 98 onError: _cmdoError, |
97 onDone: _cmdoDone); | 99 onDone: _cmdoDone); |
100 _commands = new CommandList(); | |
101 _commands.register(new AttachCommand()); | |
102 _commands.register(new DetachCommand()); | |
103 _commands.register(new HelpCommand(_commands)); | |
104 _commands.register(new IsolateCommand()); | |
105 _commands.register(new QuitCommand()); | |
turnidge
2014/09/17 16:06:24
CommandList is now an object and owned by the Debu
| |
98 } | 106 } |
99 | 107 |
100 Future _closeCmdo() { | 108 Future _closeCmdo() { |
101 var sub = _cmdoSubscription; | 109 var sub = _cmdoSubscription; |
102 _cmdoSubscription = null; | 110 _cmdoSubscription = null; |
103 cmdo = null; | 111 cmdo = null; |
104 | 112 |
105 var future = sub.cancel(); | 113 var future = sub.cancel(); |
106 if (future != null) { | 114 if (future != null) { |
107 return future; | 115 return future; |
108 } else { | 116 } else { |
109 return new Future.value(); | 117 return new Future.value(); |
110 } | 118 } |
111 } | 119 } |
112 | 120 |
113 Future quit() { | 121 Future quit() { |
114 return Future.wait([_closeCmdo()]).then((_) { | 122 return Future.wait([_closeCmdo()]).then((_) { |
115 exit(0); | 123 exit(0); |
116 }); | 124 }); |
117 } | 125 } |
118 | 126 |
119 void _cmdoError(self, parent, zone, error, StackTrace trace) { | 127 void _cmdoError(error, StackTrace trace) { |
120 cmdo.print('\n--------\nExiting due to unexpected error:\n' | 128 cmdo.print('\n--------\nExiting due to unexpected error:\n' |
121 ' $error\n$trace\n'); | 129 ' $error\n$trace\n'); |
122 quit(); | 130 quit(); |
123 } | 131 } |
124 | 132 |
125 void _cmdoDone() { | 133 void _cmdoDone() { |
126 quit(); | 134 quit(); |
127 } | 135 } |
128 | 136 |
137 List<String> _completeCommand(List<String> commandParts) { | |
138 return _commands.complete(commandParts); | |
139 } | |
140 | |
129 void _processCommand(String cmdLine) { | 141 void _processCommand(String cmdLine) { |
130 void huh() { | 142 void huh() { |
131 cmdo.print("'$cmdLine' not understood, try 'help' for help."); | 143 cmdo.print("'$cmdLine' not understood, try 'help' for help."); |
132 } | 144 } |
133 | 145 |
134 cmdo.hide(); | 146 cmdo.hide(); |
135 cmdLine = cmdLine.trim(); | 147 cmdLine = cmdLine.trim(); |
136 var args = cmdLine.split(' '); | 148 var args = cmdLine.split(' '); |
137 if (args.length == 0) { | 149 if (args.length == 0) { |
138 return; | 150 return; |
139 } | 151 } |
140 var command = args[0]; | 152 var command = args[0]; |
141 var matches = matchCommand(command, true); | 153 var matches = _commands.match(command, true); |
142 if (matches.length == 0) { | 154 if (matches.length == 0) { |
143 huh(); | 155 huh(); |
144 cmdo.show(); | 156 cmdo.show(); |
145 } else if (matches.length == 1) { | 157 } else if (matches.length == 1) { |
146 matches[0].run(this, args).then((_) { | 158 matches[0].run(this, args).then((_) { |
147 cmdo.show(); | 159 cmdo.show(); |
148 }); | 160 }); |
149 } else { | 161 } else { |
150 var matchNames = matches.map((handler) => handler.name); | 162 var matchNames = matches.map((handler) => handler.name); |
151 cmdo.print("Ambigous command '$command' : ${matchNames.toList()}"); | 163 cmdo.print("Ambigous command '$command' : ${matchNames.toList()}"); |
152 cmdo.show(); | 164 cmdo.show(); |
153 } | 165 } |
154 } | 166 } |
155 | 167 |
156 } | 168 } |
157 | 169 |
158 // Every debugger command extends this base class. | 170 // Every debugger command extends this base class. |
159 abstract class Command { | 171 abstract class Command { |
160 String get name; | 172 String get name; |
161 String get helpShort; | 173 String get helpShort; |
162 void printHelp(Debugger debugger, List<String> args); | 174 void printHelp(Debugger debugger, List<String> args); |
163 Future run(Debugger debugger, List<String> args); | 175 Future run(Debugger debugger, List<String> args); |
176 List<String> complete(List<String> commandParts) { | |
177 return ["$name ${commandParts.join(' ')}"]; | |
turnidge
2014/09/17 16:06:24
Commands can now implement command-specific comple
Cutch
2014/09/17 20:07:26
Nice!
| |
178 | |
179 } | |
164 } | 180 } |
165 | 181 |
166 class AttachCommand extends Command { | 182 class AttachCommand extends Command { |
167 final name = 'attach'; | 183 final name = 'attach'; |
168 final helpShort = 'Attach to a running Dart VM'; | 184 final helpShort = 'Attach to a running Dart VM'; |
169 void printHelp(Debugger debugger, List<String> args) { | 185 void printHelp(Debugger debugger, List<String> args) { |
170 debugger.cmdo.print(''' | 186 debugger.cmdo.print(''' |
171 ----- attach ----- | 187 ----- attach ----- |
172 | 188 |
173 Attach to the Dart VM running at the indicated host:port. If no | 189 Attach to the Dart VM running at the indicated host:port. If no |
(...skipping 22 matching lines...) Expand all Loading... | |
196 for (var isolate in vm.isolates) { | 212 for (var isolate in vm.isolates) { |
197 if (isolate.name == 'root') { | 213 if (isolate.name == 'root') { |
198 debugger.isolate = isolate; | 214 debugger.isolate = isolate; |
199 } | 215 } |
200 } | 216 } |
201 } | 217 } |
202 }); | 218 }); |
203 } | 219 } |
204 } | 220 } |
205 | 221 |
206 class DetachCommand extends Command { | 222 class CommandList { |
207 final name = 'detach'; | 223 List _commands = new List<Command>(); |
208 final helpShort = 'Detach from a running Dart VM'; | |
209 void printHelp(Debugger debugger, List<String> args) { | |
210 cmdo.print(''' | |
211 ----- detach ----- | |
212 | 224 |
213 Detach from the Dart VM. | 225 void register(Command cmd) { |
214 | 226 _commands.add(cmd); |
215 Usage: | |
216 detach | |
217 '''); | |
218 } | 227 } |
219 | 228 |
220 Future run(Debugger debugger, List<String> args) { | 229 List<Command> match(String commandName, bool exactMatchWins) { |
221 var cmdo = debugger.cmdo; | 230 var matches = []; |
222 if (args.length > 1) { | 231 for (var command in _commands) { |
223 cmdo.print('$name expects no arguments'); | 232 if (command.name.startsWith(commandName)) { |
224 return new Future.value(); | 233 if (exactMatchWins && command.name == commandName) { |
234 // Exact match | |
235 return [command]; | |
236 } else { | |
237 matches.add(command); | |
238 } | |
239 } | |
225 } | 240 } |
226 if (debugger.vm == null) { | 241 return matches; |
227 cmdo.print('No VM is attached'); | |
228 } else { | |
229 debugger.vm = null; | |
230 } | |
231 return new Future.value(); | |
232 } | |
233 } | |
234 | |
235 class HelpCommand extends Command { | |
236 final name = 'help'; | |
237 final helpShort = 'Show a list of debugger commands'; | |
238 void printHelp(Debugger debugger, List<String> args) { | |
239 debugger.cmdo.print(''' | |
240 ----- help ----- | |
241 | |
242 Show a list of debugger commands or get more information about a | |
243 particular command. | |
244 | |
245 Usage: | |
246 help | |
247 help <command> | |
248 '''); | |
249 } | 242 } |
250 | 243 |
251 Future run(Debugger debugger, List<String> args) { | 244 List<String> complete(List<String> commandParts) { |
245 var completions = new List<String>(); | |
246 String prefix = commandParts[0]; | |
247 for (var command in _commands) { | |
248 if (command.name.startsWith(prefix)) { | |
249 completions.addAll(command.complete(commandParts.sublist(1))); | |
250 } | |
251 } | |
252 return completions; | |
253 } | |
254 | |
255 void printHelp(Debugger debugger, List<String> args) { | |
252 var cmdo = debugger.cmdo; | 256 var cmdo = debugger.cmdo; |
253 if (args.length <= 1) { | 257 if (args.length <= 1) { |
254 cmdo.print("\nDebugger commands:\n"); | 258 cmdo.print("\nDebugger commands:\n"); |
255 for (var command in commandList) { | 259 for (var command in _commands) { |
256 cmdo.print(' ${command.name.padRight(11)} ${command.helpShort}'); | 260 cmdo.print(' ${command.name.padRight(11)} ${command.helpShort}'); |
257 } | 261 } |
258 cmdo.print("For more information about a particular command, type:\n\n" | 262 cmdo.print("For more information about a particular command, type:\n\n" |
259 " help <command>\n"); | 263 " help <command>\n"); |
260 | 264 |
261 cmdo.print("Commands may be abbreviated: e.g. type 'h' for 'help.\n"); | 265 cmdo.print("Commands may be abbreviated: e.g. type 'h' for 'help.\n"); |
262 } else { | 266 } else { |
263 var commandName = args[1]; | 267 var commandName = args[1]; |
264 var matches = matchCommand(commandName, true); | 268 var matches =match(commandName, true); |
265 if (matches.length == 0) { | 269 if (matches.length == 0) { |
266 cmdo.print("Command '$commandName' not recognized. " | 270 cmdo.print("Command '$commandName' not recognized. " |
267 "Try 'help' for a list of commands."); | 271 "Try 'help' for a list of commands."); |
268 } else { | 272 } else { |
269 for (var command in matches) { | 273 for (var command in matches) { |
270 command.printHelp(debugger, args); | 274 command.printHelp(debugger, args); |
271 } | 275 } |
272 } | 276 } |
273 } | 277 } |
278 } | |
279 } | |
274 | 280 |
281 class DetachCommand extends Command { | |
282 final name = 'detach'; | |
283 final helpShort = 'Detach from a running Dart VM'; | |
284 void printHelp(Debugger debugger, List<String> args) { | |
285 cmdo.print(''' | |
286 ----- detach ----- | |
287 | |
288 Detach from the Dart VM. | |
289 | |
290 Usage: | |
291 detach | |
292 '''); | |
293 } | |
294 | |
295 Future run(Debugger debugger, List<String> args) { | |
296 var cmdo = debugger.cmdo; | |
297 if (args.length > 1) { | |
298 cmdo.print('$name expects no arguments'); | |
299 return new Future.value(); | |
300 } | |
301 if (debugger.vm == null) { | |
302 cmdo.print('No VM is attached'); | |
303 } else { | |
304 debugger.vm = null; | |
305 } | |
275 return new Future.value(); | 306 return new Future.value(); |
276 } | 307 } |
277 } | 308 } |
278 | 309 |
279 class IsolateCommand extends Command { | 310 class IsolateCommand extends Command { |
280 final name = 'isolate'; | 311 final name = 'isolate'; |
281 final helpShort = 'Isolate control'; | 312 final helpShort = 'Isolate control'; |
282 void printHelp(Debugger debugger, List<String> args) { | 313 void printHelp(Debugger debugger, List<String> args) { |
283 debugger.cmdo.print(''' | 314 debugger.cmdo.print(''' |
284 ----- isolate ----- | 315 ----- isolate ----- |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
368 } else if (isolate.loading) { | 399 } else if (isolate.loading) { |
369 // TODO(turnidge): This is weird in a command line debugger. | 400 // TODO(turnidge): This is weird in a command line debugger. |
370 sb.write('(not available)'); | 401 sb.write('(not available)'); |
371 } | 402 } |
372 sb.write('\n'); | 403 sb.write('\n'); |
373 } | 404 } |
374 cmdo.print(sb); | 405 cmdo.print(sb); |
375 }); | 406 }); |
376 return new Future.value(); | 407 return new Future.value(); |
377 } | 408 } |
409 | |
410 List<String> complete(List<String> commandParts) { | |
411 if (commandParts.isEmpty) { | |
412 return ['$name ${commandParts.join(" ")}']; | |
413 } else { | |
414 var completions = _commands.complete(commandParts); | |
415 return completions.map((completion) { | |
416 return '$name $completion'; | |
417 }); | |
418 } | |
419 } | |
378 } | 420 } |
379 | 421 |
380 class QuitCommand extends Command { | 422 class QuitCommand extends Command { |
381 final name = 'quit'; | 423 final name = 'quit'; |
382 final helpShort = 'Quit the debugger.'; | 424 final helpShort = 'Quit the debugger.'; |
383 void printHelp(Debugger debugger, List<String> args) { | 425 void printHelp(Debugger debugger, List<String> args) { |
384 debugger.cmdo.print(''' | 426 debugger.cmdo.print(''' |
385 ----- quit ----- | 427 ----- quit ----- |
386 | 428 |
387 Quit the debugger. | 429 Quit the debugger. |
388 | 430 |
389 Usage: | 431 Usage: |
390 quit | 432 quit |
391 '''); | 433 '''); |
392 } | 434 } |
393 | 435 |
394 Future run(Debugger debugger, List<String> args) { | 436 Future run(Debugger debugger, List<String> args) { |
395 var cmdo = debugger.cmdo; | 437 var cmdo = debugger.cmdo; |
396 if (args.length > 1) { | 438 if (args.length > 1) { |
397 cmdo.print("Unexpected arguments to $name command."); | 439 cmdo.print("Unexpected arguments to $name command."); |
398 return new Future.value(); | 440 return new Future.value(); |
399 } | 441 } |
400 return debugger.quit(); | 442 return debugger.quit(); |
401 } | 443 } |
402 } | 444 } |
403 | |
404 List<Command> commandList = | |
405 [ new AttachCommand(), | |
406 new DetachCommand(), | |
407 new HelpCommand(), | |
408 new IsolateCommand(), | |
409 new QuitCommand() ]; | |
410 | |
411 List<Command> matchCommand(String commandName, bool exactMatchWins) { | |
412 var matches = []; | |
413 for (var command in commandList) { | |
414 if (command.name.startsWith(commandName)) { | |
415 if (exactMatchWins && command.name == commandName) { | |
416 // Exact match | |
417 return [command]; | |
418 } else { | |
419 matches.add(command); | |
420 } | |
421 } | |
422 } | |
423 return matches; | |
424 } | |
425 | |
426 List<String> completeCommand(List<String> commandParts) { | |
427 var completions = new List<String>(); | |
428 if (commandParts.length == 1) { | |
429 String prefix = commandParts[0]; | |
430 for (var command in commandList) { | |
431 if (command.name.startsWith(prefix)) { | |
432 completions.add(command.name); | |
433 } | |
434 } | |
435 } | |
436 return completions; | |
437 } | |
OLD | NEW |