| 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()); |
| 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(' ')}"]; |
| 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 |
| 222 class CommandList { |
| 223 List _commands = new List<Command>(); |
| 224 |
| 225 void register(Command cmd) { |
| 226 _commands.add(cmd); |
| 227 } |
| 228 |
| 229 List<Command> match(String commandName, bool exactMatchWins) { |
| 230 var matches = []; |
| 231 for (var command in _commands) { |
| 232 if (command.name.startsWith(commandName)) { |
| 233 if (exactMatchWins && command.name == commandName) { |
| 234 // Exact match |
| 235 return [command]; |
| 236 } else { |
| 237 matches.add(command); |
| 238 } |
| 239 } |
| 240 } |
| 241 return matches; |
| 242 } |
| 243 |
| 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) { |
| 256 var cmdo = debugger.cmdo; |
| 257 if (args.length <= 1) { |
| 258 cmdo.print("\nDebugger commands:\n"); |
| 259 for (var command in _commands) { |
| 260 cmdo.print(' ${command.name.padRight(11)} ${command.helpShort}'); |
| 261 } |
| 262 cmdo.print("For more information about a particular command, type:\n\n" |
| 263 " help <command>\n"); |
| 264 |
| 265 cmdo.print("Commands may be abbreviated: e.g. type 'h' for 'help.\n"); |
| 266 } else { |
| 267 var commandName = args[1]; |
| 268 var matches =match(commandName, true); |
| 269 if (matches.length == 0) { |
| 270 cmdo.print("Command '$commandName' not recognized. " |
| 271 "Try 'help' for a list of commands."); |
| 272 } else { |
| 273 for (var command in matches) { |
| 274 command.printHelp(debugger, args); |
| 275 } |
| 276 } |
| 277 } |
| 278 } |
| 279 } |
| 280 |
| 206 class DetachCommand extends Command { | 281 class DetachCommand extends Command { |
| 207 final name = 'detach'; | 282 final name = 'detach'; |
| 208 final helpShort = 'Detach from a running Dart VM'; | 283 final helpShort = 'Detach from a running Dart VM'; |
| 209 void printHelp(Debugger debugger, List<String> args) { | 284 void printHelp(Debugger debugger, List<String> args) { |
| 210 cmdo.print(''' | 285 debugger.cmdo.print(''' |
| 211 ----- detach ----- | 286 ----- detach ----- |
| 212 | 287 |
| 213 Detach from the Dart VM. | 288 Detach from the Dart VM. |
| 214 | 289 |
| 215 Usage: | 290 Usage: |
| 216 detach | 291 detach |
| 217 '''); | 292 '''); |
| 218 } | 293 } |
| 219 | 294 |
| 220 Future run(Debugger debugger, List<String> args) { | 295 Future run(Debugger debugger, List<String> args) { |
| 221 var cmdo = debugger.cmdo; | 296 var cmdo = debugger.cmdo; |
| 222 if (args.length > 1) { | 297 if (args.length > 1) { |
| 223 cmdo.print('$name expects no arguments'); | 298 cmdo.print('$name expects no arguments'); |
| 224 return new Future.value(); | 299 return new Future.value(); |
| 225 } | 300 } |
| 226 if (debugger.vm == null) { | 301 if (debugger.vm == null) { |
| 227 cmdo.print('No VM is attached'); | 302 cmdo.print('No VM is attached'); |
| 228 } else { | 303 } else { |
| 229 debugger.vm = null; | 304 debugger.vm = null; |
| 230 } | 305 } |
| 231 return new Future.value(); | 306 return new Future.value(); |
| 232 } | 307 } |
| 233 } | 308 } |
| 234 | 309 |
| 235 class HelpCommand extends Command { | 310 class HelpCommand extends Command { |
| 311 HelpCommand(this._commands); |
| 312 final CommandList _commands; |
| 313 |
| 236 final name = 'help'; | 314 final name = 'help'; |
| 237 final helpShort = 'Show a list of debugger commands'; | 315 final helpShort = 'Show a list of debugger commands'; |
| 238 void printHelp(Debugger debugger, List<String> args) { | 316 void printHelp(Debugger debugger, List<String> args) { |
| 239 debugger.cmdo.print(''' | 317 debugger.cmdo.print(''' |
| 240 ----- help ----- | 318 ----- help ----- |
| 241 | 319 |
| 242 Show a list of debugger commands or get more information about a | 320 Show a list of debugger commands or get more information about a |
| 243 particular command. | 321 particular command. |
| 244 | 322 |
| 245 Usage: | 323 Usage: |
| 246 help | 324 help |
| 247 help <command> | 325 help <command> |
| 248 '''); | 326 '''); |
| 249 } | 327 } |
| 250 | 328 |
| 251 Future run(Debugger debugger, List<String> args) { | 329 Future run(Debugger debugger, List<String> args) { |
| 252 var cmdo = debugger.cmdo; | 330 _commands.printHelp(debugger, args); |
| 253 if (args.length <= 1) { | 331 return new Future.value(); |
| 254 cmdo.print("\nDebugger commands:\n"); | 332 } |
| 255 for (var command in commandList) { | |
| 256 cmdo.print(' ${command.name.padRight(11)} ${command.helpShort}'); | |
| 257 } | |
| 258 cmdo.print("For more information about a particular command, type:\n\n" | |
| 259 " help <command>\n"); | |
| 260 | 333 |
| 261 cmdo.print("Commands may be abbreviated: e.g. type 'h' for 'help.\n"); | 334 List<String> complete(List<String> commandParts) { |
| 262 } else { | 335 if (commandParts.isEmpty) { |
| 263 var commandName = args[1]; | 336 return ['$name ']; |
| 264 var matches = matchCommand(commandName, true); | |
| 265 if (matches.length == 0) { | |
| 266 cmdo.print("Command '$commandName' not recognized. " | |
| 267 "Try 'help' for a list of commands."); | |
| 268 } else { | |
| 269 for (var command in matches) { | |
| 270 command.printHelp(debugger, args); | |
| 271 } | |
| 272 } | |
| 273 } | 337 } |
| 274 | 338 return _commands.complete(commandParts).map((value) { |
| 275 return new Future.value(); | 339 return '$name $value'; |
| 340 }); |
| 276 } | 341 } |
| 277 } | 342 } |
| 278 | 343 |
| 279 class IsolateCommand extends Command { | 344 class IsolateCommand extends Command { |
| 280 final name = 'isolate'; | 345 final name = 'isolate'; |
| 281 final helpShort = 'Isolate control'; | 346 final helpShort = 'Isolate control'; |
| 282 void printHelp(Debugger debugger, List<String> args) { | 347 void printHelp(Debugger debugger, List<String> args) { |
| 283 debugger.cmdo.print(''' | 348 debugger.cmdo.print(''' |
| 284 ----- isolate ----- | 349 ----- isolate ----- |
| 285 | 350 |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 368 } else if (isolate.loading) { | 433 } else if (isolate.loading) { |
| 369 // TODO(turnidge): This is weird in a command line debugger. | 434 // TODO(turnidge): This is weird in a command line debugger. |
| 370 sb.write('(not available)'); | 435 sb.write('(not available)'); |
| 371 } | 436 } |
| 372 sb.write('\n'); | 437 sb.write('\n'); |
| 373 } | 438 } |
| 374 cmdo.print(sb); | 439 cmdo.print(sb); |
| 375 }); | 440 }); |
| 376 return new Future.value(); | 441 return new Future.value(); |
| 377 } | 442 } |
| 443 |
| 444 List<String> complete(List<String> commandParts) { |
| 445 if (commandParts.isEmpty) { |
| 446 return ['$name ${commandParts.join(" ")}']; |
| 447 } else { |
| 448 var completions = _commands.complete(commandParts); |
| 449 return completions.map((completion) { |
| 450 return '$name $completion'; |
| 451 }); |
| 452 } |
| 453 } |
| 378 } | 454 } |
| 379 | 455 |
| 380 class QuitCommand extends Command { | 456 class QuitCommand extends Command { |
| 381 final name = 'quit'; | 457 final name = 'quit'; |
| 382 final helpShort = 'Quit the debugger.'; | 458 final helpShort = 'Quit the debugger.'; |
| 383 void printHelp(Debugger debugger, List<String> args) { | 459 void printHelp(Debugger debugger, List<String> args) { |
| 384 debugger.cmdo.print(''' | 460 debugger.cmdo.print(''' |
| 385 ----- quit ----- | 461 ----- quit ----- |
| 386 | 462 |
| 387 Quit the debugger. | 463 Quit the debugger. |
| 388 | 464 |
| 389 Usage: | 465 Usage: |
| 390 quit | 466 quit |
| 391 '''); | 467 '''); |
| 392 } | 468 } |
| 393 | 469 |
| 394 Future run(Debugger debugger, List<String> args) { | 470 Future run(Debugger debugger, List<String> args) { |
| 395 var cmdo = debugger.cmdo; | 471 var cmdo = debugger.cmdo; |
| 396 if (args.length > 1) { | 472 if (args.length > 1) { |
| 397 cmdo.print("Unexpected arguments to $name command."); | 473 cmdo.print("Unexpected arguments to $name command."); |
| 398 return new Future.value(); | 474 return new Future.value(); |
| 399 } | 475 } |
| 400 return debugger.quit(); | 476 return debugger.quit(); |
| 401 } | 477 } |
| 402 } | 478 } |
| 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 |