| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 used by debugger wire protocol tests (standalone VM debugging). | 5 // Library used by debugger wire protocol tests (standalone VM debugging). |
| 6 | 6 |
| 7 library DartDebugger; | 7 library DartDebugger; |
| 8 | 8 |
| 9 import "dart:async"; | 9 import "dart:async"; |
| 10 import "dart:convert"; | 10 import "dart:convert"; |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 173 void matchResponse(Debugger debugger) { | 173 void matchResponse(Debugger debugger) { |
| 174 Map response = debugger.currentMessage; | 174 Map response = debugger.currentMessage; |
| 175 var id = template["id"]; | 175 var id = template["id"]; |
| 176 assert(id != null && id >= 0); | 176 assert(id != null && id >= 0); |
| 177 if (response["id"] != id) { | 177 if (response["id"] != id) { |
| 178 debugger.error("Error: expected messaged id $id but got ${response["id"]}.
"); | 178 debugger.error("Error: expected messaged id $id but got ${response["id"]}.
"); |
| 179 } | 179 } |
| 180 } | 180 } |
| 181 } | 181 } |
| 182 | 182 |
| 183 class GetLineTableCmd extends Command { |
| 184 GetLineTableCmd() { |
| 185 template = {"id": 0, |
| 186 "command": "getLineNumberTable", |
| 187 "params": {"isolateId": 0, "libraryId": 0, "url": ""}}; |
| 188 } |
| 189 |
| 190 void send(Debugger debugger) { |
| 191 assert(debugger.scriptUrl != null); |
| 192 template["params"]["url"] = debugger.scriptUrl; |
| 193 template["params"]["libraryId"] = debugger.libraryId; |
| 194 debugger.sendMessage(template); |
| 195 } |
| 196 |
| 197 void matchResponse(Debugger debugger) { |
| 198 super.matchResponse(debugger); |
| 199 List<List<int>> table = getJsonValue(debugger.currentMessage, "result:lines"
); |
| 200 debugger.tokenToLine = {}; |
| 201 for (var line in table) { |
| 202 // Each entry begins with a line number... |
| 203 var lineNumber = line[0]; |
| 204 for (var pos = 1; pos < line.length; pos += 2) { |
| 205 // ...and is followed by (token offset, col number) pairs. |
| 206 var tokenOffset = line[pos]; |
| 207 debugger.tokenToLine[tokenOffset] = lineNumber; |
| 208 } |
| 209 } |
| 210 } |
| 211 } |
| 212 |
| 213 |
| 214 class LineMatcher extends Command { |
| 215 int expectedLineNumber; |
| 216 |
| 217 LineMatcher(this.expectedLineNumber) { |
| 218 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; |
| 219 } |
| 220 |
| 221 void matchResponse(Debugger debugger) { |
| 222 assert(debugger.tokenToLine != null); |
| 223 super.matchResponse(debugger); |
| 224 var msg = debugger.currentMessage; |
| 225 List frames = getJsonValue(msg, "result:callFrames"); |
| 226 assert(frames != null); |
| 227 var tokenOffset = frames[0]["location"]["tokenOffset"]; |
| 228 assert(tokenOffset != null); |
| 229 var lineNumber = debugger.tokenToLine[tokenOffset]; |
| 230 assert(lineNumber != null); |
| 231 if (expectedLineNumber != lineNumber) { |
| 232 debugger.error("Error: expected pause at line $expectedLineNumber " |
| 233 "but reported line is $lineNumber."); |
| 234 return; |
| 235 } |
| 236 print("Matched line number $lineNumber"); |
| 237 } |
| 238 } |
| 239 |
| 240 MatchLine(lineNumber) { |
| 241 return new LineMatcher(lineNumber); |
| 242 } |
| 243 |
| 183 | 244 |
| 184 class FrameMatcher extends Command { | 245 class FrameMatcher extends Command { |
| 185 int frameIndex; | 246 int frameIndex; |
| 186 List<String> functionNames; | 247 List<String> functionNames; |
| 187 bool exactMatch; | 248 bool exactMatch; |
| 188 | 249 |
| 189 FrameMatcher(this.frameIndex, this.functionNames, this.exactMatch) { | 250 FrameMatcher(this.frameIndex, this.functionNames, this.exactMatch) { |
| 190 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; | 251 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; |
| 191 } | 252 } |
| 192 | 253 |
| 193 void matchResponse(Debugger debugger) { | 254 void matchResponse(Debugger debugger) { |
| 194 super.matchResponse(debugger); | 255 super.matchResponse(debugger); |
| 195 var msg = debugger.currentMessage; | 256 var msg = debugger.currentMessage; |
| 196 List frames = getJsonValue(msg, "result:callFrames"); | 257 List frames = getJsonValue(msg, "result:callFrames"); |
| 197 assert(frames != null); | 258 assert(frames != null); |
| 198 if (debugger.scriptUrl == null) { | 259 if (debugger.scriptUrl == null) { |
| 199 var name = frames[0]["functionName"]; | 260 var name = frames[0]["functionName"]; |
| 200 if (name == "main") { | 261 if (name == "main") { |
| 201 // Extract script url of debugged script. | 262 // Extract script url of debugged script. |
| 202 debugger.scriptUrl = frames[0]["location"]["url"]; | 263 debugger.scriptUrl = frames[0]["location"]["url"]; |
| 203 assert(debugger.scriptUrl != null); | 264 assert(debugger.scriptUrl != null); |
| 265 debugger.libraryId = frames[0]["location"]["libraryId"]; |
| 266 assert(debugger.libraryId != null); |
| 204 } | 267 } |
| 205 } | 268 } |
| 206 if (frames.length < functionNames.length) { | 269 if (frames.length < functionNames.length) { |
| 207 debugger.error("Error: stack trace not long enough " | 270 debugger.error("Error: stack trace not long enough " |
| 208 "to match ${functionNames.length} frames"); | 271 "to match ${functionNames.length} frames"); |
| 209 return; | 272 return; |
| 210 } | 273 } |
| 211 for (int i = 0; i < functionNames.length; i++) { | 274 for (int i = 0; i < functionNames.length; i++) { |
| 212 var idx = i + frameIndex; | 275 var idx = i + frameIndex; |
| 213 var name = frames[idx]["functionName"]; | 276 var name = frames[idx]["functionName"]; |
| 214 assert(name != null); | 277 assert(name != null); |
| 215 bool isMatch = exactMatch ? name == functionNames[i] | 278 bool isMatch = exactMatch ? name == functionNames[i] |
| 216 : name.contains(functionNames[i]); | 279 : name.contains(functionNames[i]); |
| 217 if (!isMatch) { | 280 if (!isMatch) { |
| 218 debugger.error("Error: call frame $idx: " | 281 debugger.error("Error: call frame $idx: " |
| 219 "expected function name '${functionNames[i]}' but found '$name'"); | 282 "expected function name '${functionNames[i]}' but found '$name'"); |
| 220 return; | 283 return; |
| 221 } | 284 } |
| 222 } | 285 } |
| 286 print("Matched frames: $functionNames"); |
| 223 } | 287 } |
| 224 } | 288 } |
| 225 | 289 |
| 226 | 290 |
| 227 MatchFrame(int frameIndex, String functionName, {exactMatch: false}) { | 291 MatchFrame(int frameIndex, String functionName, {exactMatch: false}) { |
| 228 return new FrameMatcher(frameIndex, [functionName], exactMatch); | 292 return new FrameMatcher(frameIndex, [functionName], exactMatch); |
| 229 } | 293 } |
| 230 | 294 |
| 231 MatchFrames(List<String> functionNames, {exactMatch: false}) { | 295 MatchFrames(List<String> functionNames, {exactMatch: false}) { |
| 232 return new FrameMatcher(0, functionNames, exactMatch); | 296 return new FrameMatcher(0, functionNames, exactMatch); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 257 return; | 321 return; |
| 258 } | 322 } |
| 259 String expected = locals[key]; | 323 String expected = locals[key]; |
| 260 String actual = reportedLocals[key]['text']; | 324 String actual = reportedLocals[key]['text']; |
| 261 if (expected != actual) { | 325 if (expected != actual) { |
| 262 debugger.error("Error in $functionName(): got '$actual' for local " | 326 debugger.error("Error in $functionName(): got '$actual' for local " |
| 263 "variable $key, but expected '$expected'"); | 327 "variable $key, but expected '$expected'"); |
| 264 return; | 328 return; |
| 265 } | 329 } |
| 266 } | 330 } |
| 331 print("Matched locals ${locals.keys}"); |
| 267 } | 332 } |
| 268 } | 333 } |
| 269 | 334 |
| 270 | 335 |
| 271 MatchLocals(Map localValues) { | 336 MatchLocals(Map localValues) { |
| 272 return new LocalsMatcher(localValues); | 337 return new LocalsMatcher(localValues); |
| 273 } | 338 } |
| 274 | 339 |
| 275 | 340 |
| 276 class EventMatcher { | 341 class EventMatcher { |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 RunCommand.stepInto() { | 376 RunCommand.stepInto() { |
| 312 template = {"id": 0, "command": "stepInto", "params": {"isolateId": 0}}; | 377 template = {"id": 0, "command": "stepInto", "params": {"isolateId": 0}}; |
| 313 } | 378 } |
| 314 RunCommand.stepOut() { | 379 RunCommand.stepOut() { |
| 315 template = {"id": 0, "command": "stepOut", "params": {"isolateId": 0}}; | 380 template = {"id": 0, "command": "stepOut", "params": {"isolateId": 0}}; |
| 316 } | 381 } |
| 317 void send(Debugger debugger) { | 382 void send(Debugger debugger) { |
| 318 debugger.sendMessage(template); | 383 debugger.sendMessage(template); |
| 319 debugger.isPaused = false; | 384 debugger.isPaused = false; |
| 320 } | 385 } |
| 386 void matchResponse(Debugger debugger) { |
| 387 super.matchResponse(debugger); |
| 388 print("Command: ${template['command']}"); |
| 389 } |
| 321 } | 390 } |
| 322 | 391 |
| 323 | 392 |
| 324 Resume() => new RunCommand.resume(); | 393 Resume() => new RunCommand.resume(); |
| 325 Step() => new RunCommand.step(); | 394 Step() => new RunCommand.step(); |
| 326 StepInto() => new RunCommand.stepInto(); | 395 StepInto() => new RunCommand.stepInto(); |
| 327 StepOut() => new RunCommand.stepOut(); | 396 StepOut() => new RunCommand.stepOut(); |
| 328 | 397 |
| 329 class SetBreakpointCommand extends Command { | 398 class SetBreakpointCommand extends Command { |
| 330 int line; | 399 int line; |
| 331 SetBreakpointCommand(int this.line) { | 400 SetBreakpointCommand(int this.line) { |
| 332 template = {"id": 0, | 401 template = {"id": 0, |
| 333 "command": "setBreakpoint", | 402 "command": "setBreakpoint", |
| 334 "params": { "isolateId": 0, | 403 "params": { "isolateId": 0, |
| 335 "url": null, | 404 "url": null, |
| 336 "line": null }}; | 405 "line": null }}; |
| 337 } | 406 } |
| 338 | 407 |
| 339 void send(Debugger debugger) { | 408 void send(Debugger debugger) { |
| 340 assert(debugger.scriptUrl != null); | 409 assert(debugger.scriptUrl != null); |
| 341 template["params"]["url"] = debugger.scriptUrl; | 410 template["params"]["url"] = debugger.scriptUrl; |
| 342 template["params"]["line"] = line; | 411 template["params"]["line"] = line; |
| 343 debugger.sendMessage(template); | 412 debugger.sendMessage(template); |
| 344 } | 413 } |
| 414 |
| 415 void matchResponse(Debugger debugger) { |
| 416 super.matchResponse(debugger); |
| 417 print("Set breakpoint at line $line"); |
| 418 } |
| 345 } | 419 } |
| 346 | 420 |
| 347 SetBreakpoint(int line) => new SetBreakpointCommand(line); | 421 SetBreakpoint(int line) => new SetBreakpointCommand(line); |
| 348 | 422 |
| 349 class Event { | 423 class Event { |
| 350 String name; | 424 String name; |
| 351 Map params; | 425 Map params; |
| 352 | 426 |
| 353 Event(Map json) { | 427 Event(Map json) { |
| 354 name = json['event']; | 428 name = json['event']; |
| 355 params = json['params']; | 429 params = json['params']; |
| 356 } | 430 } |
| 357 } | 431 } |
| 358 | 432 |
| 359 | 433 |
| 360 // A debug script is a list of Command objects. | 434 // A debug script is a list of Command objects. |
| 361 class DebugScript { | 435 class DebugScript { |
| 362 List entries; | 436 List entries; |
| 363 DebugScript(List scriptEntries) { | 437 DebugScript(List scriptEntries) { |
| 364 entries = new List.from(scriptEntries.reversed); | 438 entries = new List.from(scriptEntries.reversed); |
| 439 entries.add(new GetLineTableCmd()); |
| 365 entries.add(MatchFrame(0, "main")); | 440 entries.add(MatchFrame(0, "main")); |
| 366 } | 441 } |
| 367 bool get isEmpty => entries.isEmpty; | 442 bool get isEmpty => entries.isEmpty; |
| 368 bool get isNextEventMatcher => !isEmpty && currentEntry is EventMatcher; | 443 bool get isNextEventMatcher => !isEmpty && currentEntry is EventMatcher; |
| 369 get currentEntry => entries.last; | 444 get currentEntry => entries.last; |
| 370 advance() => entries.removeLast(); | 445 advance() => entries.removeLast(); |
| 371 add(entry) => entries.add(entry); | 446 add(entry) => entries.add(entry); |
| 372 } | 447 } |
| 373 | 448 |
| 374 | 449 |
| 375 class Debugger { | 450 class Debugger { |
| 376 // Debug target process properties. | 451 // Debug target process properties. |
| 377 Process targetProcess; | 452 Process targetProcess; |
| 378 Socket socket; | 453 Socket socket; |
| 379 JsonBuffer responses = new JsonBuffer(); | 454 JsonBuffer responses = new JsonBuffer(); |
| 380 | 455 |
| 381 DebugScript script; | 456 DebugScript script; |
| 382 int seqNr = 0; // Sequence number of next debugger command message. | 457 int seqNr = 0; // Sequence number of next debugger command message. |
| 383 Command lastCommand = null; // Most recent command sent to target. | 458 Command lastCommand = null; // Most recent command sent to target. |
| 384 List<String> errors = new List(); | 459 List<String> errors = new List(); |
| 385 List<Event> events = new List(); | 460 List<Event> events = new List(); |
| 386 bool cleanupDone = false; | 461 bool cleanupDone = false; |
| 387 | 462 |
| 388 // Data collected from debug target. | 463 // Data collected from debug target. |
| 389 Map currentMessage = null; // Currently handled message sent by target. | 464 Map currentMessage = null; // Currently handled message sent by target. |
| 390 String scriptUrl = null; | 465 String scriptUrl = null; |
| 466 int libraryId = null; |
| 467 Map<int,int> tokenToLine = null; |
| 391 bool shutdownEventSeen = false; | 468 bool shutdownEventSeen = false; |
| 392 int isolateId = 0; | 469 int isolateId = 0; |
| 393 bool isPaused = false; | 470 bool isPaused = false; |
| 394 | 471 |
| 395 Debugger(this.targetProcess, this.script) { | 472 Debugger(this.targetProcess, this.script) { |
| 396 var stdoutStringStream = targetProcess.stdout | 473 var stdoutStringStream = targetProcess.stdout |
| 397 .transform(UTF8.decoder) | 474 .transform(UTF8.decoder) |
| 398 .transform(new LineSplitter()); | 475 .transform(new LineSplitter()); |
| 399 stdoutStringStream.listen((line) { | 476 stdoutStringStream.listen((line) { |
| 400 print("TARG: $line"); | 477 print("TARG: $line"); |
| (...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 617 targetOpts.add("--debuggee"); | 694 targetOpts.add("--debuggee"); |
| 618 print('args: ${targetOpts.join(" ")}'); | 695 print('args: ${targetOpts.join(" ")}'); |
| 619 | 696 |
| 620 Process.start(Platform.executable, targetOpts).then((Process process) { | 697 Process.start(Platform.executable, targetOpts).then((Process process) { |
| 621 print("Debug target process started, pid ${process.pid}."); | 698 print("Debug target process started, pid ${process.pid}."); |
| 622 process.stdin.close(); | 699 process.stdin.close(); |
| 623 var debugger = new Debugger(process, new DebugScript(script)); | 700 var debugger = new Debugger(process, new DebugScript(script)); |
| 624 }); | 701 }); |
| 625 return true; | 702 return true; |
| 626 } | 703 } |
| OLD | NEW |