Chromium Code Reviews| 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 |