| 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_page_element; | 5 library debugger_page_element; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:html'; | 8 import 'dart:html'; |
| 9 import 'observatory_element.dart'; | 9 import 'observatory_element.dart'; |
| 10 import 'package:observatory/cli.dart'; | 10 import 'package:observatory/cli.dart'; |
| 11 import 'package:observatory/debugger.dart'; | 11 import 'package:observatory/debugger.dart'; |
| 12 import 'package:observatory/service.dart'; | 12 import 'package:observatory/service.dart'; |
| 13 import 'package:polymer/polymer.dart'; | 13 import 'package:polymer/polymer.dart'; |
| 14 | 14 |
| 15 // TODO(turnidge): Move Debugger, DebuggerCommand to debugger library. | 15 // TODO(turnidge): Move Debugger, DebuggerCommand to debugger library. |
| 16 abstract class DebuggerCommand extends Command { | 16 abstract class DebuggerCommand extends Command { |
| 17 ObservatoryDebugger debugger; | 17 ObservatoryDebugger debugger; |
| 18 | 18 |
| 19 DebuggerCommand(this.debugger, name, children) | 19 DebuggerCommand(this.debugger, name, children) |
| 20 : super(name, children); | 20 : super(name, children); |
| 21 |
| 22 String get helpShort; |
| 23 String get helpLong; |
| 21 } | 24 } |
| 22 | 25 |
| 26 // TODO(turnidge): Rewrite HelpCommand so that it is a general utility |
| 27 // provided by the cli library. |
| 23 class HelpCommand extends DebuggerCommand { | 28 class HelpCommand extends DebuggerCommand { |
| 24 HelpCommand(Debugger debugger) : super(debugger, 'help', []); | 29 HelpCommand(Debugger debugger) : super(debugger, 'help', []); |
| 25 | 30 |
| 26 Future run(List<String> args) { | 31 Future run(List<String> args) { |
| 27 var con = debugger.console; | 32 var con = debugger.console; |
| 28 con.printLine('List of commands:'); | 33 if (args.length == 0) { |
| 29 con.newline(); | 34 // Print list of all top-level commands. |
| 35 var commands = debugger.cmd.matchCommand([], false); |
| 36 commands.sort((a, b) => a.name.compareTo(b.name)); |
| 37 con.print('List of commands:\n'); |
| 38 for (var command in commands) { |
| 39 con.print('${command.name.padRight(12)} - ${command.helpShort}'); |
| 40 } |
| 41 con.print( |
| 42 "\nFor more information on a specific command type 'help <command>'\n" |
| 43 "\n" |
| 44 "Command prefixes are accepted (e.g. 'h' for 'help')\n" |
| 45 "Hit [TAB] to complete a command (try 'i[TAB][TAB]')\n" |
| 46 "Hit [ENTER] to repeat the last command\n"); |
| 47 return new Future.value(null); |
| 48 } else { |
| 49 // Print any matching commands. |
| 50 var commands = debugger.cmd.matchCommand(args, true); |
| 51 commands.sort((a, b) => a.name.compareTo(b.name)); |
| 52 if (commands.isEmpty) { |
| 53 var line = args.join(' '); |
| 54 con.print("No command matches '${line}'"); |
| 55 return new Future.value(null); |
| 56 } |
| 57 con.print(''); |
| 58 for (var command in commands) { |
| 59 con.printBold(command.fullName); |
| 60 con.print(command.helpLong); |
| 30 | 61 |
| 31 // TODO(turnidge): Build a real help system. | 62 var newArgs = []; |
| 32 return debugger.cmd.completeCommand('').then((completions) { | 63 newArgs.addAll(args.take(args.length - 1)); |
| 33 completions = completions.map((s) => s.trimRight()).toList(); | 64 newArgs.add(command.name); |
| 34 completions.sort(); | 65 newArgs.add(''); |
| 35 con.printLine(completions.toString()); | 66 var subCommands = debugger.cmd.matchCommand(newArgs, false); |
| 36 con.newline(); | 67 subCommands.remove(command); |
| 37 con.printLine("Command prefixes are accepted (e.g. 'h' for 'help')"); | 68 if (subCommands.isNotEmpty) { |
| 38 con.printLine("Hit [TAB] to complete a command (try 'i[TAB][TAB]')"); | 69 subCommands.sort((a, b) => a.name.compareTo(b.name)); |
| 39 con.printLine("Hit [ENTER] to repeat the last command"); | 70 con.print('Subcommands:\n'); |
| 40 }); | 71 for (var subCommand in subCommands) { |
| 72 con.print(' ${subCommand.fullName.padRight(16)} ' |
| 73 '- ${subCommand.helpShort}'); |
| 74 } |
| 75 con.print(''); |
| 76 } |
| 77 } |
| 78 return new Future.value(null); |
| 79 } |
| 41 } | 80 } |
| 81 |
| 82 Future<List<String>> complete(List<String> args) { |
| 83 var commands = debugger.cmd.matchCommand(args, false); |
| 84 var result = commands.map((command) => '${command.fullName} '); |
| 85 return new Future.value(result); |
| 86 } |
| 87 |
| 88 String helpShort = 'List commands or provide details about a specific command'
; |
| 89 |
| 90 String helpLong = |
| 91 'List commands or provide details about a specific command.\n' |
| 92 '\n' |
| 93 'Syntax: help - Show a list of all commands\n' |
| 94 ' help <command> - Help for a specific command\n'; |
| 42 } | 95 } |
| 43 | 96 |
| 44 class PauseCommand extends DebuggerCommand { | 97 class PauseCommand extends DebuggerCommand { |
| 45 PauseCommand(Debugger debugger) : super(debugger, 'pause', []); | 98 PauseCommand(Debugger debugger) : super(debugger, 'pause', []); |
| 46 | 99 |
| 47 Future run(List<String> args) { | 100 Future run(List<String> args) { |
| 48 if (!debugger.isolatePaused()) { | 101 if (!debugger.isolatePaused()) { |
| 49 return debugger.isolate.pause(); | 102 return debugger.isolate.pause(); |
| 50 } else { | 103 } else { |
| 51 debugger.console.printLine('The program is already paused'); | 104 debugger.console.print('The program is already paused'); |
| 52 return new Future.value(null); | 105 return new Future.value(null); |
| 53 } | 106 } |
| 54 } | 107 } |
| 108 |
| 109 String helpShort = 'Pause the isolate'; |
| 110 |
| 111 String helpLong = |
| 112 'Pause the isolate.\n' |
| 113 '\n' |
| 114 'Syntax: pause\n'; |
| 55 } | 115 } |
| 56 | 116 |
| 57 class ContinueCommand extends DebuggerCommand { | 117 class ContinueCommand extends DebuggerCommand { |
| 58 ContinueCommand(Debugger debugger) : super(debugger, 'continue', []); | 118 ContinueCommand(Debugger debugger) : super(debugger, 'continue', []); |
| 59 | 119 |
| 60 Future run(List<String> args) { | 120 Future run(List<String> args) { |
| 61 if (debugger.isolatePaused()) { | 121 if (debugger.isolatePaused()) { |
| 62 return debugger.isolate.resume().then((_) { | 122 return debugger.isolate.resume().then((_) { |
| 63 debugger.warnOutOfDate(); | 123 debugger.warnOutOfDate(); |
| 64 }); | 124 }); |
| 65 } else { | 125 } else { |
| 66 debugger.console.printLine('The program must be paused'); | 126 debugger.console.print('The program must be paused'); |
| 67 return new Future.value(null); | 127 return new Future.value(null); |
| 68 } | 128 } |
| 69 } | 129 } |
| 130 |
| 131 String helpShort = 'Resume execution of the isolate'; |
| 132 |
| 133 String helpLong = |
| 134 'Continue running the isolate.\n' |
| 135 '\n' |
| 136 'Syntax: continue\n'; |
| 70 } | 137 } |
| 71 | 138 |
| 72 class NextCommand extends DebuggerCommand { | 139 class NextCommand extends DebuggerCommand { |
| 73 NextCommand(Debugger debugger) : super(debugger, 'next', []); | 140 NextCommand(Debugger debugger) : super(debugger, 'next', []); |
| 74 | 141 |
| 75 Future run(List<String> args) { | 142 Future run(List<String> args) { |
| 76 if (debugger.isolatePaused()) { | 143 if (debugger.isolatePaused()) { |
| 77 var event = debugger.isolate.pauseEvent; | 144 var event = debugger.isolate.pauseEvent; |
| 78 if (event.eventType == 'IsolateCreated') { | 145 if (event.eventType == 'IsolateCreated') { |
| 79 debugger.console.printLine("Type 'continue' to start the isolate"); | 146 debugger.console.print("Type 'continue' to start the isolate"); |
| 80 return new Future.value(null); | 147 return new Future.value(null); |
| 81 } | 148 } |
| 82 if (event.eventType == 'IsolateShutdown') { | 149 if (event.eventType == 'IsolateShutdown') { |
| 83 debugger.console.printLine("Type 'continue' to exit the isolate"); | 150 debugger.console.print("Type 'continue' to exit the isolate"); |
| 84 return new Future.value(null); | 151 return new Future.value(null); |
| 85 } | 152 } |
| 86 return debugger.isolate.stepOver(); | 153 return debugger.isolate.stepOver(); |
| 87 } else { | 154 } else { |
| 88 debugger.console.printLine('The program is already running'); | 155 debugger.console.print('The program is already running'); |
| 89 return new Future.value(null); | 156 return new Future.value(null); |
| 90 } | 157 } |
| 91 } | 158 } |
| 159 |
| 160 String helpShort = |
| 161 'Continue running the isolate until it reaches the next source location ' |
| 162 'in the current function'; |
| 163 |
| 164 String helpLong = |
| 165 'Continue running the isolate until it reaches the next source location ' |
| 166 'in the current function.\n' |
| 167 '\n' |
| 168 'Syntax: next\n'; |
| 92 } | 169 } |
| 93 | 170 |
| 94 class StepCommand extends DebuggerCommand { | 171 class StepCommand extends DebuggerCommand { |
| 95 StepCommand(Debugger debugger) : super(debugger, 'step', []); | 172 StepCommand(Debugger debugger) : super(debugger, 'step', []); |
| 96 | 173 |
| 97 Future run(List<String> args) { | 174 Future run(List<String> args) { |
| 98 if (debugger.isolatePaused()) { | 175 if (debugger.isolatePaused()) { |
| 99 var event = debugger.isolate.pauseEvent; | 176 var event = debugger.isolate.pauseEvent; |
| 100 if (event.eventType == 'IsolateCreated') { | 177 if (event.eventType == 'IsolateCreated') { |
| 101 debugger.console.printLine("Type 'continue' to start the isolate"); | 178 debugger.console.print("Type 'continue' to start the isolate"); |
| 102 return new Future.value(null); | 179 return new Future.value(null); |
| 103 } | 180 } |
| 104 if (event.eventType == 'IsolateShutdown') { | 181 if (event.eventType == 'IsolateShutdown') { |
| 105 debugger.console.printLine("Type 'continue' to exit the isolate"); | 182 debugger.console.print("Type 'continue' to exit the isolate"); |
| 106 return new Future.value(null); | 183 return new Future.value(null); |
| 107 } | 184 } |
| 108 return debugger.isolate.stepInto(); | 185 return debugger.isolate.stepInto(); |
| 109 } else { | 186 } else { |
| 110 debugger.console.printLine('The program is already running'); | 187 debugger.console.print('The program is already running'); |
| 111 return new Future.value(null); | 188 return new Future.value(null); |
| 112 } | 189 } |
| 113 } | 190 } |
| 191 |
| 192 String helpShort = |
| 193 'Continue running the isolate until it reaches the next source location'; |
| 194 |
| 195 String helpLong = |
| 196 'Continue running the isolate until it reaches the next source ' |
| 197 'location.\n' |
| 198 '\n' |
| 199 'Syntax: step\n'; |
| 114 } | 200 } |
| 115 | 201 |
| 116 class FinishCommand extends DebuggerCommand { | 202 class FinishCommand extends DebuggerCommand { |
| 117 FinishCommand(Debugger debugger) : super(debugger, 'finish', []); | 203 FinishCommand(Debugger debugger) : super(debugger, 'finish', []); |
| 118 | 204 |
| 119 Future run(List<String> args) { | 205 Future run(List<String> args) { |
| 120 if (debugger.isolatePaused()) { | 206 if (debugger.isolatePaused()) { |
| 121 return debugger.isolate.stepOut(); | 207 return debugger.isolate.stepOut(); |
| 122 } else { | 208 } else { |
| 123 debugger.console.printLine('The program is already running'); | 209 debugger.console.print('The program is already running'); |
| 124 return new Future.value(null); | 210 return new Future.value(null); |
| 125 } | 211 } |
| 126 } | 212 } |
| 213 |
| 214 String helpShort = |
| 215 'Continue running the isolate until the current function exits'; |
| 216 |
| 217 String helpLong = |
| 218 'Continue running the isolate until the current function exits.\n' |
| 219 '\n' |
| 220 'Syntax: finish\n'; |
| 127 } | 221 } |
| 128 | 222 |
| 129 class BreakCommand extends DebuggerCommand { | 223 class BreakCommand extends DebuggerCommand { |
| 130 BreakCommand(Debugger debugger) : super(debugger, 'break', []); | 224 BreakCommand(Debugger debugger) : super(debugger, 'break', []); |
| 131 | 225 |
| 132 Future run(List<String> args) { | 226 Future run(List<String> args) { |
| 133 if (args.length > 1) { | 227 if (args.length > 1) { |
| 134 debugger.console.printLine('not implemented'); | 228 debugger.console.print('not implemented'); |
| 135 return new Future.value(null); | 229 return new Future.value(null); |
| 136 } | 230 } |
| 137 var arg = (args.length == 0 ? '' : args[0]); | 231 var arg = (args.length == 0 ? '' : args[0]); |
| 138 return SourceLocation.parse(debugger, arg).then((loc) { | 232 return SourceLocation.parse(debugger, arg).then((loc) { |
| 139 if (loc.valid) { | 233 if (loc.valid) { |
| 140 if (loc.function != null) { | 234 if (loc.function != null) { |
| 141 debugger.console.printLine( | 235 debugger.console.print( |
| 142 'Ignoring breakpoint at $loc: ' | 236 'Ignoring breakpoint at $loc: ' |
| 143 'Function entry breakpoints not yet implemented'); | 237 'Function entry breakpoints not yet implemented'); |
| 144 return null; | 238 return null; |
| 145 } | 239 } |
| 146 if (loc.col != null) { | 240 if (loc.col != null) { |
| 147 // TODO(turnidge): Add tokenPos breakpoint support. | 241 // TODO(turnidge): Add tokenPos breakpoint support. |
| 148 debugger.console.printLine( | 242 debugger.console.print( |
| 149 'Ignoring column: ' | 243 'Ignoring column: ' |
| 150 'adding breakpoint at a specific column not yet implemented'); | 244 'adding breakpoint at a specific column not yet implemented'); |
| 151 } | 245 } |
| 152 return debugger.isolate.addBreakpoint(loc.script, loc.line).then((result
) { | 246 return debugger.isolate.addBreakpoint(loc.script, loc.line).then((result
) { |
| 153 if (result is DartError) { | 247 if (result is DartError) { |
| 154 debugger.console.printLine('Unable to set breakpoint at ${loc}'); | 248 debugger.console.print('Unable to set breakpoint at ${loc}'); |
| 155 } else { | 249 } else { |
| 156 // TODO(turnidge): Adding a duplicate breakpoint is | 250 // TODO(turnidge): Adding a duplicate breakpoint is |
| 157 // currently ignored. May want to change the protocol to | 251 // currently ignored. May want to change the protocol to |
| 158 // inform us when this happens. | 252 // inform us when this happens. |
| 159 | 253 |
| 160 // The BreakpointResolved event prints resolved | 254 // The BreakpointResolved event prints resolved |
| 161 // breakpoints already. Just print the unresolved ones here. | 255 // breakpoints already. Just print the unresolved ones here. |
| 162 ServiceMap bpt = result; | 256 ServiceMap bpt = result; |
| 163 if (!bpt['resolved']) { | 257 if (!bpt['resolved']) { |
| 164 var script = bpt['location']['script']; | 258 var script = bpt['location']['script']; |
| 165 var bpId = bpt['breakpointNumber']; | 259 var bpId = bpt['breakpointNumber']; |
| 166 var tokenPos = bpt['location']['tokenPos']; | 260 var tokenPos = bpt['location']['tokenPos']; |
| 167 return script.load().then((_) { | 261 return script.load().then((_) { |
| 168 var line = script.tokenToLine(tokenPos); | 262 var line = script.tokenToLine(tokenPos); |
| 169 var col = script.tokenToCol(tokenPos); | 263 var col = script.tokenToCol(tokenPos); |
| 170 debugger.console.printLine( | 264 debugger.console.print( |
| 171 'Future breakpoint ${bpId} added at ' | 265 'Future breakpoint ${bpId} added at ' |
| 172 '${script.name}:${line}:${col}'); | 266 '${script.name}:${line}:${col}'); |
| 173 }); | 267 }); |
| 174 } | 268 } |
| 175 } | 269 } |
| 176 }); | 270 }); |
| 177 } else { | 271 } else { |
| 178 debugger.console.printLine(loc.errorMessage); | 272 debugger.console.print(loc.errorMessage); |
| 179 } | 273 } |
| 180 }); | 274 }); |
| 181 } | 275 } |
| 182 | 276 |
| 183 Future<List<String>> complete(List<String> args) { | 277 Future<List<String>> complete(List<String> args) { |
| 184 if (args.length != 1) { | 278 if (args.length != 1) { |
| 185 return new Future.value([]); | 279 return new Future.value([]); |
| 186 } | 280 } |
| 187 // TODO - fix SourceLocation complete | 281 // TODO - fix SourceLocation complete |
| 188 return new Future.value(SourceLocation.complete(debugger, args[0])); | 282 return new Future.value(SourceLocation.complete(debugger, args[0])); |
| 189 } | 283 } |
| 284 |
| 285 String helpShort = 'Add a breakpoint by source location or function name'; |
| 286 |
| 287 String helpLong = |
| 288 'Add a breakpoint by source location or function name.\n' |
| 289 '\n' |
| 290 'Syntax: break ' |
| 291 '- Break at the current position\n' |
| 292 ' break <line> ' |
| 293 '- Break at a line in the current script\n' |
| 294 ' ' |
| 295 ' (e.g \'break 11\')\n' |
| 296 ' break <line>:<col> ' |
| 297 '- Break at a line:col in the current script\n' |
| 298 ' ' |
| 299 ' (e.g \'break 11:8\')\n' |
| 300 ' break <script>:<line> ' |
| 301 '- Break at a line:col in a specific script\n' |
| 302 ' ' |
| 303 ' (e.g \'break test.dart:11\')\n' |
| 304 ' break <script>:<line>:<col> ' |
| 305 '- Break at a line:col in a specific script\n' |
| 306 ' ' |
| 307 ' (e.g \'break test.dart:11:8\')\n' |
| 308 ' break <function> ' |
| 309 '- Break at the named function\n' |
| 310 ' ' |
| 311 ' (e.g \'break main\' or \'break Class.someFunction\')\n'; |
| 190 } | 312 } |
| 191 | 313 |
| 192 class ClearCommand extends DebuggerCommand { | 314 class ClearCommand extends DebuggerCommand { |
| 193 ClearCommand(Debugger debugger) : super(debugger, 'clear', []); | 315 ClearCommand(Debugger debugger) : super(debugger, 'clear', []); |
| 194 | 316 |
| 195 Future run(List<String> args) { | 317 Future run(List<String> args) { |
| 196 if (args.length > 1) { | 318 if (args.length > 1) { |
| 197 debugger.console.printLine('not implemented'); | 319 debugger.console.print('not implemented'); |
| 198 return new Future.value(null); | 320 return new Future.value(null); |
| 199 } | 321 } |
| 200 var arg = (args.length == 0 ? '' : args[0]); | 322 var arg = (args.length == 0 ? '' : args[0]); |
| 201 return SourceLocation.parse(debugger, arg).then((loc) { | 323 return SourceLocation.parse(debugger, arg).then((loc) { |
| 202 if (loc.valid) { | 324 if (loc.valid) { |
| 203 if (loc.function != null) { | 325 if (loc.function != null) { |
| 204 debugger.console.printLine( | 326 debugger.console.print( |
| 205 'Ignoring breakpoint at $loc: ' | 327 'Ignoring breakpoint at $loc: ' |
| 206 'Function entry breakpoints not yet implemented'); | 328 'Function entry breakpoints not yet implemented'); |
| 207 return null; | 329 return null; |
| 208 } | 330 } |
| 209 if (loc.col != null) { | 331 if (loc.col != null) { |
| 210 // TODO(turnidge): Add tokenPos clear support. | 332 // TODO(turnidge): Add tokenPos clear support. |
| 211 debugger.console.printLine( | 333 debugger.console.print( |
| 212 'Ignoring column: ' | 334 'Ignoring column: ' |
| 213 'clearing breakpoint at a specific column not yet implemented'); | 335 'clearing breakpoint at a specific column not yet implemented'); |
| 214 } | 336 } |
| 215 | 337 |
| 216 for (var bpt in debugger.isolate.breakpoints) { | 338 for (var bpt in debugger.isolate.breakpoints) { |
| 217 var script = bpt['location']['script']; | 339 var script = bpt['location']['script']; |
| 218 if (script.id == loc.script.id) { | 340 if (script.id == loc.script.id) { |
| 219 assert(script.loaded); | 341 assert(script.loaded); |
| 220 var line = script.tokenToLine(bpt['location']['tokenPos']); | 342 var line = script.tokenToLine(bpt['location']['tokenPos']); |
| 221 if (line == loc.line) { | 343 if (line == loc.line) { |
| 222 return debugger.isolate.removeBreakpoint(bpt).then((result) { | 344 return debugger.isolate.removeBreakpoint(bpt).then((result) { |
| 223 if (result is DartError) { | 345 if (result is DartError) { |
| 224 debugger.console.printLine( | 346 debugger.console.print( |
| 225 'Unable to clear breakpoint at ${loc}: ${result.message}')
; | 347 'Unable to clear breakpoint at ${loc}: ${result.message}')
; |
| 226 return; | 348 return; |
| 227 } else { | 349 } else { |
| 228 // TODO(turnidge): Add a BreakpointRemoved event to | 350 // TODO(turnidge): Add a BreakpointRemoved event to |
| 229 // the service instead of printing here. | 351 // the service instead of printing here. |
| 230 var bpId = bpt['breakpointNumber']; | 352 var bpId = bpt['breakpointNumber']; |
| 231 debugger.console.printLine( | 353 debugger.console.print( |
| 232 'Breakpoint ${bpId} removed at ${loc}'); | 354 'Breakpoint ${bpId} removed at ${loc}'); |
| 233 return; | 355 return; |
| 234 } | 356 } |
| 235 }); | 357 }); |
| 236 } | 358 } |
| 237 } | 359 } |
| 238 } | 360 } |
| 239 debugger.console.printLine('No breakpoint found at ${loc}'); | 361 debugger.console.print('No breakpoint found at ${loc}'); |
| 240 } else { | 362 } else { |
| 241 debugger.console.printLine(loc.errorMessage); | 363 debugger.console.print(loc.errorMessage); |
| 242 } | 364 } |
| 243 }); | 365 }); |
| 244 } | 366 } |
| 245 | 367 |
| 246 Future<List<String>> complete(List<String> args) { | 368 Future<List<String>> complete(List<String> args) { |
| 247 if (args.length != 1) { | 369 if (args.length != 1) { |
| 248 return new Future.value([]); | 370 return new Future.value([]); |
| 249 } | 371 } |
| 250 return new Future.value(SourceLocation.complete(debugger, args[0])); | 372 return new Future.value(SourceLocation.complete(debugger, args[0])); |
| 251 } | 373 } |
| 374 |
| 375 String helpShort = 'Remove a breakpoint by source location or function name'; |
| 376 |
| 377 String helpLong = |
| 378 'Remove a breakpoint by source location or function name.\n' |
| 379 '\n' |
| 380 'Syntax: clear ' |
| 381 '- Clear at the current position\n' |
| 382 ' clear <line> ' |
| 383 '- Clear at a line in the current script\n' |
| 384 ' ' |
| 385 ' (e.g \'clear 11\')\n' |
| 386 ' clear <line>:<col> ' |
| 387 '- Clear at a line:col in the current script\n' |
| 388 ' ' |
| 389 ' (e.g \'clear 11:8\')\n' |
| 390 ' clear <script>:<line> ' |
| 391 '- Clear at a line:col in a specific script\n' |
| 392 ' ' |
| 393 ' (e.g \'clear test.dart:11\')\n' |
| 394 ' clear <script>:<line>:<col> ' |
| 395 '- Clear at a line:col in a specific script\n' |
| 396 ' ' |
| 397 ' (e.g \'clear test.dart:11:8\')\n' |
| 398 ' clear <function> ' |
| 399 '- Clear at the named function\n' |
| 400 ' ' |
| 401 ' (e.g \'clear main\' or \'clear Class.someFunction\')\n'; |
| 252 } | 402 } |
| 253 | 403 |
| 254 // TODO(turnidge): Add argument completion. | 404 // TODO(turnidge): Add argument completion. |
| 255 class DeleteCommand extends DebuggerCommand { | 405 class DeleteCommand extends DebuggerCommand { |
| 256 DeleteCommand(Debugger debugger) : super(debugger, 'delete', []); | 406 DeleteCommand(Debugger debugger) : super(debugger, 'delete', []); |
| 257 | 407 |
| 258 Future run(List<String> args) { | 408 Future run(List<String> args) { |
| 259 if (args.length < 1) { | 409 if (args.length < 1) { |
| 260 debugger.console.printLine('delete expects one or more arguments'); | 410 debugger.console.print('delete expects one or more arguments'); |
| 261 return new Future.value(null); | 411 return new Future.value(null); |
| 262 } | 412 } |
| 263 List toDelete = []; | 413 List toDelete = []; |
| 264 for (var arg in args) { | 414 for (var arg in args) { |
| 265 int id = int.parse(arg); | 415 int id = int.parse(arg); |
| 266 var bpt = null; | 416 var bpt = null; |
| 267 for (var candidate in debugger.isolate.breakpoints) { | 417 for (var candidate in debugger.isolate.breakpoints) { |
| 268 if (candidate['breakpointNumber'] == id) { | 418 if (candidate['breakpointNumber'] == id) { |
| 269 bpt = candidate; | 419 bpt = candidate; |
| 270 break; | 420 break; |
| 271 } | 421 } |
| 272 } | 422 } |
| 273 if (bpt == null) { | 423 if (bpt == null) { |
| 274 debugger.console.printLine("Invalid breakpoint id '${id}'"); | 424 debugger.console.print("Invalid breakpoint id '${id}'"); |
| 275 return new Future.value(null); | 425 return new Future.value(null); |
| 276 } | 426 } |
| 277 toDelete.add(bpt); | 427 toDelete.add(bpt); |
| 278 } | 428 } |
| 279 List pending = []; | 429 List pending = []; |
| 280 for (var bpt in toDelete) { | 430 for (var bpt in toDelete) { |
| 281 pending.add(debugger.isolate.removeBreakpoint(bpt).then((_) { | 431 pending.add(debugger.isolate.removeBreakpoint(bpt).then((_) { |
| 282 var id = bpt['breakpointNumber']; | 432 var id = bpt['breakpointNumber']; |
| 283 debugger.console.printLine("Removed breakpoint $id"); | 433 debugger.console.print("Removed breakpoint $id"); |
| 284 })); | 434 })); |
| 285 } | 435 } |
| 286 return Future.wait(pending); | 436 return Future.wait(pending); |
| 287 } | 437 } |
| 438 |
| 439 String helpShort = 'Remove a breakpoint by breakpoint id'; |
| 440 |
| 441 String helpLong = |
| 442 'Remove a breakpoint by breakpoint id.\n' |
| 443 '\n' |
| 444 'Syntax: delete <bp-id>\n' |
| 445 ' delete <bp-id> <bp-id> ...\n'; |
| 288 } | 446 } |
| 289 | 447 |
| 290 class InfoBreakpointsCommand extends DebuggerCommand { | 448 class InfoBreakpointsCommand extends DebuggerCommand { |
| 291 InfoBreakpointsCommand(Debugger debugger) | 449 InfoBreakpointsCommand(Debugger debugger) |
| 292 : super(debugger, 'breakpoints', []); | 450 : super(debugger, 'breakpoints', []); |
| 293 | 451 |
| 294 Future run(List<String> args) { | 452 Future run(List<String> args) { |
| 295 return debugger.isolate.reloadBreakpoints().then((_) { | 453 return debugger.isolate.reloadBreakpoints().then((_) { |
| 296 if (debugger.isolate.breakpoints.isEmpty) { | 454 if (debugger.isolate.breakpoints.isEmpty) { |
| 297 debugger.console.printLine('No breakpoints'); | 455 debugger.console.print('No breakpoints'); |
| 298 } | 456 } |
| 299 for (var bpt in debugger.isolate.breakpoints) { | 457 for (var bpt in debugger.isolate.breakpoints) { |
| 300 var bpId = bpt['breakpointNumber']; | 458 var bpId = bpt['breakpointNumber']; |
| 301 var script = bpt['location']['script']; | 459 var script = bpt['location']['script']; |
| 302 var tokenPos = bpt['location']['tokenPos']; | 460 var tokenPos = bpt['location']['tokenPos']; |
| 303 var line = script.tokenToLine(tokenPos); | 461 var line = script.tokenToLine(tokenPos); |
| 304 var col = script.tokenToCol(tokenPos); | 462 var col = script.tokenToCol(tokenPos); |
| 305 var extras = new StringBuffer(); | 463 var extras = new StringBuffer(); |
| 306 if (!bpt['resolved']) { | 464 if (!bpt['resolved']) { |
| 307 extras.write(' unresolved'); | 465 extras.write(' unresolved'); |
| 308 } | 466 } |
| 309 if (!bpt['enabled']) { | 467 if (!bpt['enabled']) { |
| 310 extras.write(' disabled'); | 468 extras.write(' disabled'); |
| 311 } | 469 } |
| 312 debugger.console.printLine( | 470 debugger.console.print( |
| 313 'Breakpoint ${bpId} at ${script.name}:${line}:${col}${extras}'); | 471 'Breakpoint ${bpId} at ${script.name}:${line}:${col}${extras}'); |
| 314 } | 472 } |
| 315 }); | 473 }); |
| 316 } | 474 } |
| 475 |
| 476 String helpShort = 'List all breakpoints'; |
| 477 |
| 478 String helpLong = |
| 479 'List all breakpoints.\n' |
| 480 '\n' |
| 481 'Syntax: info breakpoints\n'; |
| 317 } | 482 } |
| 318 | 483 |
| 319 class InfoIsolatesCommand extends DebuggerCommand { | 484 class InfoIsolatesCommand extends DebuggerCommand { |
| 320 InfoIsolatesCommand(Debugger debugger) : super(debugger, 'isolates', []); | 485 InfoIsolatesCommand(Debugger debugger) : super(debugger, 'isolates', []); |
| 321 | 486 |
| 322 Future run(List<String> args) { | 487 Future run(List<String> args) { |
| 323 for (var isolate in debugger.isolate.vm.isolates) { | 488 for (var isolate in debugger.isolate.vm.isolates) { |
| 324 debugger.console.printLine( | 489 String current = (isolate == debugger.isolate ? ' *' : ''); |
| 325 "Isolate ${isolate.id} '${isolate.name}'"); | 490 debugger.console.print( |
| 491 "Isolate ${isolate.id} '${isolate.name}'${current}"); |
| 326 } | 492 } |
| 327 return new Future.value(null); | 493 return new Future.value(null); |
| 328 } | 494 } |
| 495 |
| 496 String helpShort = 'List all isolates'; |
| 497 |
| 498 String helpLong = |
| 499 'List all isolates.\n' |
| 500 '\n' |
| 501 'Syntax: info isolates\n'; |
| 329 } | 502 } |
| 330 | 503 |
| 331 class InfoCommand extends DebuggerCommand { | 504 class InfoCommand extends DebuggerCommand { |
| 332 InfoCommand(Debugger debugger) : super(debugger, 'info', [ | 505 InfoCommand(Debugger debugger) : super(debugger, 'info', [ |
| 333 new InfoBreakpointsCommand(debugger), | 506 new InfoBreakpointsCommand(debugger), |
| 334 new InfoIsolatesCommand(debugger), | 507 new InfoIsolatesCommand(debugger), |
| 335 ]); | 508 ]); |
| 336 | 509 |
| 337 Future run(List<String> args) { | 510 Future run(List<String> args) { |
| 338 debugger.console.printLine("Invalid info command"); | 511 debugger.console.print("'info' expects a subcommand (see 'help info')"); |
| 339 return new Future.value(null); | 512 return new Future.value(null); |
| 340 } | 513 } |
| 514 |
| 515 String helpShort = 'Show information on a variety of topics'; |
| 516 |
| 517 String helpLong = |
| 518 'Show information on a variety of topics.\n' |
| 519 '\n' |
| 520 'Syntax: info <subcommand>\n'; |
| 341 } | 521 } |
| 342 | 522 |
| 343 class RefreshCoverageCommand extends DebuggerCommand { | 523 class RefreshCoverageCommand extends DebuggerCommand { |
| 344 RefreshCoverageCommand(Debugger debugger) : super(debugger, 'coverage', []); | 524 RefreshCoverageCommand(Debugger debugger) : super(debugger, 'coverage', []); |
| 345 | 525 |
| 346 Future run(List<String> args) { | 526 Future run(List<String> args) { |
| 347 Set<Script> scripts = debugger.stackElement.activeScripts(); | 527 Set<Script> scripts = debugger.stackElement.activeScripts(); |
| 348 List pending = []; | 528 List pending = []; |
| 349 for (var script in scripts) { | 529 for (var script in scripts) { |
| 350 pending.add(script.refreshCoverage().then((_) { | 530 pending.add(script.refreshCoverage().then((_) { |
| 351 debugger.console.printLine('Refreshed coverage for ${script.name}'); | 531 debugger.console.print('Refreshed coverage for ${script.name}'); |
| 352 })); | 532 })); |
| 353 } | 533 } |
| 354 return Future.wait(pending); | 534 return Future.wait(pending); |
| 355 } | 535 } |
| 536 |
| 537 String helpShort = 'Refresh code coverage information for current frames'; |
| 538 |
| 539 String helpLong = |
| 540 'Refresh code coverage information for current frames.\n' |
| 541 '\n' |
| 542 'Syntax: refresh coverage\n\n'; |
| 543 } |
| 544 |
| 545 class RefreshStackCommand extends DebuggerCommand { |
| 546 RefreshStackCommand(Debugger debugger) : super(debugger, 'stack', []); |
| 547 |
| 548 Future run(List<String> args) { |
| 549 Set<Script> scripts = debugger.stackElement.activeScripts(); |
| 550 List pending = []; |
| 551 return debugger.refreshStack(); |
| 552 } |
| 553 |
| 554 String helpShort = 'Refresh isolate stack'; |
| 555 |
| 556 String helpLong = |
| 557 'Refresh isolate stack.\n' |
| 558 '\n' |
| 559 'Syntax: refresh stack\n'; |
| 356 } | 560 } |
| 357 | 561 |
| 358 class RefreshCommand extends DebuggerCommand { | 562 class RefreshCommand extends DebuggerCommand { |
| 359 RefreshCommand(Debugger debugger) : super(debugger, 'refresh', [ | 563 RefreshCommand(Debugger debugger) : super(debugger, 'refresh', [ |
| 360 new RefreshCoverageCommand(debugger), | 564 new RefreshCoverageCommand(debugger), |
| 565 new RefreshStackCommand(debugger), |
| 361 ]); | 566 ]); |
| 362 | 567 |
| 363 Future run(List<String> args) { | 568 Future run(List<String> args) { |
| 364 return debugger.refreshStack(); | 569 debugger.console.print("'refresh' expects a subcommand (see 'help refresh')"
); |
| 570 return new Future.value(null); |
| 365 } | 571 } |
| 572 |
| 573 String helpShort = 'Refresh debugging information of various sorts'; |
| 574 |
| 575 String helpLong = |
| 576 'Refresh debugging information of various sorts.\n' |
| 577 '\n' |
| 578 'Syntax: refresh <subcommand>\n'; |
| 366 } | 579 } |
| 367 | 580 |
| 368 // Tracks the state for an isolate debugging session. | 581 // Tracks the state for an isolate debugging session. |
| 369 class ObservatoryDebugger extends Debugger { | 582 class ObservatoryDebugger extends Debugger { |
| 370 RootCommand cmd; | 583 RootCommand cmd; |
| 371 DebuggerConsoleElement console; | 584 DebuggerConsoleElement console; |
| 372 DebuggerStackElement stackElement; | 585 DebuggerStackElement stackElement; |
| 373 ServiceMap stack; | 586 ServiceMap stack; |
| 374 int currentFrame = 0; | 587 int currentFrame = 0; |
| 375 | 588 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 444 return isolate.getStack().then((result) { | 657 return isolate.getStack().then((result) { |
| 445 stack = result; | 658 stack = result; |
| 446 // TODO(turnidge): Replace only the changed part of the stack to | 659 // TODO(turnidge): Replace only the changed part of the stack to |
| 447 // reduce flicker. | 660 // reduce flicker. |
| 448 stackElement.updateStack(stack, pauseEvent); | 661 stackElement.updateStack(stack, pauseEvent); |
| 449 }); | 662 }); |
| 450 } | 663 } |
| 451 | 664 |
| 452 void reportStatus() { | 665 void reportStatus() { |
| 453 if (_isolate.idle) { | 666 if (_isolate.idle) { |
| 454 console.printLine('Isolate is idle'); | 667 console.print('Isolate is idle'); |
| 455 } else if (_isolate.running) { | 668 } else if (_isolate.running) { |
| 456 console.printLine("Isolate is running (type 'pause' to interrupt)"); | 669 console.print("Isolate is running (type 'pause' to interrupt)"); |
| 457 } else if (_isolate.pauseEvent != null) { | 670 } else if (_isolate.pauseEvent != null) { |
| 458 _reportPause(_isolate.pauseEvent); | 671 _reportPause(_isolate.pauseEvent); |
| 459 } else { | 672 } else { |
| 460 console.printLine('Isolate is in unknown state'); | 673 console.print('Isolate is in unknown state'); |
| 461 } | 674 } |
| 462 } | 675 } |
| 463 | 676 |
| 464 void _reportPause(ServiceEvent event) { | 677 void _reportPause(ServiceEvent event) { |
| 465 if (event.eventType == 'IsolateCreated') { | 678 if (event.eventType == 'IsolateCreated') { |
| 466 console.printLine( | 679 console.print( |
| 467 "Paused at isolate start (type 'continue' to start the isolate')"); | 680 "Paused at isolate start (type 'continue' to start the isolate')"); |
| 468 } else if (event.eventType == 'IsolateShutdown') { | 681 } else if (event.eventType == 'IsolateShutdown') { |
| 469 console.printLine( | 682 console.print( |
| 470 "Paused at isolate exit (type 'continue' to exit the isolate')"); | 683 "Paused at isolate exit (type 'continue' to exit the isolate')"); |
| 471 } | 684 } |
| 472 if (stack['frames'].length > 0) { | 685 if (stack['frames'].length > 0) { |
| 473 var frame = stack['frames'][0]; | 686 var frame = stack['frames'][0]; |
| 474 var script = frame['script']; | 687 var script = frame['script']; |
| 475 script.load().then((_) { | 688 script.load().then((_) { |
| 476 var line = script.tokenToLine(frame['tokenPos']); | 689 var line = script.tokenToLine(frame['tokenPos']); |
| 477 var col = script.tokenToCol(frame['tokenPos']); | 690 var col = script.tokenToCol(frame['tokenPos']); |
| 478 if (event.breakpoint != null) { | 691 if (event.breakpoint != null) { |
| 479 var bpId = event.breakpoint['breakpointNumber']; | 692 var bpId = event.breakpoint['breakpointNumber']; |
| 480 console.printLine('Breakpoint ${bpId} at ${script.name}:${line}:${col}
'); | 693 console.print('Breakpoint ${bpId} at ${script.name}:${line}:${col}'); |
| 481 } else if (event.exception != null) { | 694 } else if (event.exception != null) { |
| 482 // TODO(turnidge): Test this. | 695 // TODO(turnidge): Test this. |
| 483 console.printLine( | 696 console.print( |
| 484 'Exception ${event.exception} at ${script.name}:${line}:${col}'); | 697 'Exception ${event.exception} at ${script.name}:${line}:${col}'); |
| 485 } else { | 698 } else { |
| 486 console.printLine('Paused at ${script.name}:${line}:${col}'); | 699 console.print('Paused at ${script.name}:${line}:${col}'); |
| 487 } | 700 } |
| 488 }); | 701 }); |
| 489 } | 702 } |
| 490 } | 703 } |
| 491 | 704 |
| 492 void _onEvent(ServiceEvent event) { | 705 void _onEvent(ServiceEvent event) { |
| 493 if (event.owner != isolate) { | 706 if (event.owner != isolate) { |
| 494 return; | 707 return; |
| 495 } | 708 } |
| 496 switch(event.eventType) { | 709 switch(event.eventType) { |
| 497 case 'IsolateShutdown': | 710 case 'IsolateShutdown': |
| 498 console.printLine('Isolate shutdown'); | 711 console.print('Isolate shutdown'); |
| 499 isolate = null; | 712 isolate = null; |
| 500 break; | 713 break; |
| 501 | 714 |
| 502 case 'BreakpointReached': | 715 case 'BreakpointReached': |
| 503 case 'IsolateInterrupted': | 716 case 'IsolateInterrupted': |
| 504 case 'ExceptionThrown': | 717 case 'ExceptionThrown': |
| 505 _refreshStack(event).then((_) { | 718 _refreshStack(event).then((_) { |
| 506 _reportPause(event); | 719 _reportPause(event); |
| 507 }); | 720 }); |
| 508 break; | 721 break; |
| 509 | 722 |
| 510 case 'IsolateResumed': | 723 case 'IsolateResumed': |
| 511 console.printLine('Continuing...'); | 724 console.print('Continuing...'); |
| 512 break; | 725 break; |
| 513 | 726 |
| 514 case 'BreakpointResolved': | 727 case 'BreakpointResolved': |
| 515 var bpId = event.breakpoint['breakpointNumber']; | 728 var bpId = event.breakpoint['breakpointNumber']; |
| 516 var script = event.breakpoint['location']['script']; | 729 var script = event.breakpoint['location']['script']; |
| 517 var tokenPos = event.breakpoint['location']['tokenPos']; | 730 var tokenPos = event.breakpoint['location']['tokenPos']; |
| 518 var line = script.tokenToLine(tokenPos); | 731 var line = script.tokenToLine(tokenPos); |
| 519 var col = script.tokenToCol(tokenPos); | 732 var col = script.tokenToCol(tokenPos); |
| 520 console.printLine( | 733 console.print( |
| 521 'Breakpoint ${bpId} added at ${script.name}:${line}:${col}'); | 734 'Breakpoint ${bpId} added at ${script.name}:${line}:${col}'); |
| 522 break; | 735 break; |
| 523 | 736 |
| 524 case '_Graph': | 737 case '_Graph': |
| 525 case 'IsolateCreated': | 738 case 'IsolateCreated': |
| 526 case 'GC': | 739 case 'GC': |
| 527 // Ignore these events for now. | 740 // Ignore these events for now. |
| 528 break; | 741 break; |
| 529 | 742 |
| 530 default: | 743 default: |
| 531 console.printLine('Unrecognized event: $event'); | 744 console.print('Unrecognized event: $event'); |
| 532 break; | 745 break; |
| 533 } | 746 } |
| 534 } | 747 } |
| 535 | 748 |
| 536 static String _commonPrefix(String a, String b) { | 749 static String _commonPrefix(String a, String b) { |
| 537 int pos = 0; | 750 int pos = 0; |
| 538 while (pos < a.length && pos < b.length) { | 751 while (pos < a.length && pos < b.length) { |
| 539 if (a.codeUnitAt(pos) != b.codeUnitAt(pos)) { | 752 if (a.codeUnitAt(pos) != b.codeUnitAt(pos)) { |
| 540 break; | 753 break; |
| 541 } | 754 } |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 576 String lastCommand; | 789 String lastCommand; |
| 577 | 790 |
| 578 Future run(String command) { | 791 Future run(String command) { |
| 579 if (command == '' && lastCommand != null) { | 792 if (command == '' && lastCommand != null) { |
| 580 command = lastCommand; | 793 command = lastCommand; |
| 581 } | 794 } |
| 582 console.printBold('\$ $command'); | 795 console.printBold('\$ $command'); |
| 583 return cmd.runCommand(command).then((_) { | 796 return cmd.runCommand(command).then((_) { |
| 584 lastCommand = command; | 797 lastCommand = command; |
| 585 }).catchError((e) { | 798 }).catchError((e) { |
| 586 console.printLine('ERROR $e'); | 799 console.print('ERROR $e'); |
| 587 }); | 800 }); |
| 588 } | 801 } |
| 589 } | 802 } |
| 590 | 803 |
| 591 @CustomTag('debugger-page') | 804 @CustomTag('debugger-page') |
| 592 class DebuggerPageElement extends ObservatoryElement { | 805 class DebuggerPageElement extends ObservatoryElement { |
| 593 @published Isolate isolate; | 806 @published Isolate isolate; |
| 594 | 807 |
| 595 isolateChanged(oldValue) { | 808 isolateChanged(oldValue) { |
| 596 if (isolate != null) { | 809 if (isolate != null) { |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 787 }); | 1000 }); |
| 788 } | 1001 } |
| 789 } | 1002 } |
| 790 | 1003 |
| 791 @CustomTag('debugger-console') | 1004 @CustomTag('debugger-console') |
| 792 class DebuggerConsoleElement extends ObservatoryElement { | 1005 class DebuggerConsoleElement extends ObservatoryElement { |
| 793 @published Isolate isolate; | 1006 @published Isolate isolate; |
| 794 | 1007 |
| 795 DebuggerConsoleElement.created() : super.created(); | 1008 DebuggerConsoleElement.created() : super.created(); |
| 796 | 1009 |
| 797 void printLine(String line) { | 1010 void print(String line, { bool newline:true }) { |
| 798 var div = new DivElement(); | 1011 var span = new SpanElement(); |
| 799 div.classes.add('normal'); | 1012 span.classes.add('normal'); |
| 800 div.appendText(line); | 1013 span.appendText(line); |
| 801 $['consoleText'].children.add(div); | 1014 if (newline) { |
| 802 div.scrollIntoView(); | 1015 span.appendText('\n'); |
| 1016 } |
| 1017 $['consoleText'].children.add(span); |
| 1018 span.scrollIntoView(); |
| 803 } | 1019 } |
| 804 | 1020 |
| 805 void printBold(String line) { | 1021 void printBold(String line, { bool newline:true }) { |
| 806 var div = new DivElement(); | 1022 var span = new SpanElement(); |
| 807 div.classes.add('bold'); | 1023 span.classes.add('bold'); |
| 808 div.appendText(line); | 1024 span.appendText(line); |
| 809 $['consoleText'].children.add(div); | 1025 span.appendText('\n'); |
| 810 div.scrollIntoView(); | 1026 $['consoleText'].children.add(span); |
| 1027 span.scrollIntoView(); |
| 811 } | 1028 } |
| 812 | 1029 |
| 813 void newline() { | 1030 void newline() { |
| 814 var br = new BRElement(); | 1031 var br = new BRElement(); |
| 815 $['consoleText'].children.add(br); | 1032 $['consoleText'].children.add(br); |
| 816 br.scrollIntoView(); | 1033 br.scrollIntoView(); |
| 817 } | 1034 } |
| 818 } | 1035 } |
| 819 | 1036 |
| 820 @CustomTag('debugger-input') | 1037 @CustomTag('debugger-input') |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 856 debugger.run(command).whenComplete(_notBusy); | 1073 debugger.run(command).whenComplete(_notBusy); |
| 857 } | 1074 } |
| 858 break; | 1075 break; |
| 859 } | 1076 } |
| 860 }); | 1077 }); |
| 861 } | 1078 } |
| 862 | 1079 |
| 863 DebuggerInputElement.created() : super.created(); | 1080 DebuggerInputElement.created() : super.created(); |
| 864 } | 1081 } |
| 865 | 1082 |
| OLD | NEW |