| 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 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 46 bool haveGarbage() { | 46 bool haveGarbage() { |
| 47 if (buffer == null || buffer.length == 0) return false; | 47 if (buffer == null || buffer.length == 0) return false; |
| 48 var i = 0, char = " "; | 48 var i = 0, char = " "; |
| 49 while (i < buffer.length) { | 49 while (i < buffer.length) { |
| 50 char = buffer[i]; | 50 char = buffer[i]; |
| 51 if (char != " " && char != "\n" && char != "\r" && char != "\t") break; | 51 if (char != " " && char != "\n" && char != "\r" && char != "\t") break; |
| 52 i++; | 52 i++; |
| 53 } | 53 } |
| 54 if (i >= buffer.length) { | 54 if (i >= buffer.length) { |
| 55 return false; | 55 return false; |
| 56 } else { | 56 } else { |
| 57 return char != "{"; | 57 return char != "{"; |
| 58 } | 58 } |
| 59 } | 59 } |
| 60 | 60 |
| 61 // Returns the character length of the next json message in the | 61 // Returns the character length of the next json message in the |
| 62 // buffer, or 0 if there is only a partial message in the buffer. | 62 // buffer, or 0 if there is only a partial message in the buffer. |
| 63 // The object value must start with '{' and continues to the | 63 // The object value must start with '{' and continues to the |
| 64 // matching '}'. No attempt is made to otherwise validate the contents | 64 // matching '}'. No attempt is made to otherwise validate the contents |
| 65 // as JSON. If it is invalid, a later JSON.decode() will fail. | 65 // as JSON. If it is invalid, a later JSON.decode() will fail. |
| 66 int objectLength() { | 66 int objectLength() { |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 225 return new FrameMatcher(frameIndex, [ functionName ]); | 225 return new FrameMatcher(frameIndex, [ functionName ]); |
| 226 } | 226 } |
| 227 | 227 |
| 228 MatchFrames(List<String> functionNames) { | 228 MatchFrames(List<String> functionNames) { |
| 229 return new FrameMatcher(0, functionNames); | 229 return new FrameMatcher(0, functionNames); |
| 230 } | 230 } |
| 231 | 231 |
| 232 | 232 |
| 233 class LocalsMatcher extends Command { | 233 class LocalsMatcher extends Command { |
| 234 Map locals = {}; | 234 Map locals = {}; |
| 235 | 235 |
| 236 LocalsMatcher(this.locals) { | 236 LocalsMatcher(this.locals) { |
| 237 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; | 237 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; |
| 238 } | 238 } |
| 239 | 239 |
| 240 void matchResponse(Debugger debugger) { | 240 void matchResponse(Debugger debugger) { |
| 241 super.matchResponse(debugger); | 241 super.matchResponse(debugger); |
| 242 | 242 |
| 243 List frames = getJsonValue(debugger.currentMessage, "result:callFrames"); | 243 List frames = getJsonValue(debugger.currentMessage, "result:callFrames"); |
| 244 assert(frames != null); | 244 assert(frames != null); |
| 245 | 245 |
| 246 String functionName = frames[0]['functionName']; | 246 String functionName = frames[0]['functionName']; |
| 247 List localsList = frames[0]['locals']; | 247 List localsList = frames[0]['locals']; |
| 248 Map reportedLocals = {}; | 248 Map reportedLocals = {}; |
| 249 localsList.forEach((local) => reportedLocals[local['name']] = local['value']
); | 249 localsList.forEach((local) => reportedLocals[local['name']] = local['value']
); |
| 250 for (String key in locals.keys) { | 250 for (String key in locals.keys) { |
| 251 if (reportedLocals[key] == null) { | 251 if (reportedLocals[key] == null) { |
| 252 debugger.error("Error in $functionName(): no value reported for local " | 252 debugger.error("Error in $functionName(): no value reported for local " |
| 253 "variable $key"); | 253 "variable $key"); |
| 254 return; | 254 return; |
| 255 } | 255 } |
| 256 String expected = locals[key]; | 256 String expected = locals[key]; |
| 257 String actual = reportedLocals[key]['text']; | 257 String actual = reportedLocals[key]['text']; |
| 258 if (expected != actual) { | 258 if (expected != actual) { |
| 259 debugger.error("Error in $functionName(): got '$actual' for local " | 259 debugger.error("Error in $functionName(): got '$actual' for local " |
| 260 "variable $key, but expected '$expected'"); | 260 "variable $key, but expected '$expected'"); |
| 261 return; | 261 return; |
| 262 } | 262 } |
| 263 } | 263 } |
| 264 } | 264 } |
| 265 } | 265 } |
| 266 | 266 |
| 267 | 267 |
| 268 MatchLocals(Map localValues) { | 268 MatchLocals(Map localValues) { |
| 269 return new LocalsMatcher(localValues); | 269 return new LocalsMatcher(localValues); |
| 270 } | 270 } |
| 271 | 271 |
| 272 | 272 |
| 273 class EventMatcher { | 273 class EventMatcher { |
| 274 String eventName; | 274 String eventName; |
| 275 Map params; | 275 Map params; |
| 276 | 276 |
| 277 EventMatcher(this.eventName, this.params); | 277 EventMatcher(this.eventName, this.params); |
| 278 | 278 |
| 279 void matchEvent(Debugger debugger) { | 279 void matchEvent(Debugger debugger) { |
| 280 for (Event event in debugger.events) { | 280 for (Event event in debugger.events) { |
| 281 if (event.name == eventName) { | 281 if (event.name == eventName) { |
| 282 if (params == null || matchMaps(params, event.params)) { | 282 if (params == null || matchMaps(params, event.params)) { |
| 283 // Remove the matched event, so we don't match against it in the futur
e. | 283 // Remove the matched event, so we don't match against it in the futur
e. |
| 284 debugger.events.remove(event); | 284 debugger.events.remove(event); |
| 285 return; | 285 return; |
| 286 } | 286 } |
| 287 } | 287 } |
| 288 } | 288 } |
| 289 | 289 |
| 290 String msg = params == null ? '' : params.toString(); | 290 String msg = params == null ? '' : params.toString(); |
| 291 debugger.error("Error: could not match event $eventName $msg"); | 291 debugger.error("Error: could not match event $eventName $msg"); |
| 292 } | 292 } |
| 293 } | 293 } |
| 294 | 294 |
| 295 | 295 |
| 296 ExpectEvent(String eventName, [Map params]) { | 296 ExpectEvent(String eventName, [Map params]) { |
| 297 return new EventMatcher(eventName, params); | 297 return new EventMatcher(eventName, params); |
| 298 } | 298 } |
| 299 | 299 |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 339 template["params"]["line"] = line; | 339 template["params"]["line"] = line; |
| 340 debugger.sendMessage(template); | 340 debugger.sendMessage(template); |
| 341 } | 341 } |
| 342 } | 342 } |
| 343 | 343 |
| 344 SetBreakpoint(int line) => new SetBreakpointCommand(line); | 344 SetBreakpoint(int line) => new SetBreakpointCommand(line); |
| 345 | 345 |
| 346 class Event { | 346 class Event { |
| 347 String name; | 347 String name; |
| 348 Map params; | 348 Map params; |
| 349 | 349 |
| 350 Event(Map json) { | 350 Event(Map json) { |
| 351 name = json['event']; | 351 name = json['event']; |
| 352 params = json['params']; | 352 params = json['params']; |
| 353 } | 353 } |
| 354 } | 354 } |
| 355 | 355 |
| 356 | 356 |
| 357 // A debug script is a list of Command objects. | 357 // A debug script is a list of Command objects. |
| 358 class DebugScript { | 358 class DebugScript { |
| 359 List entries; | 359 List entries; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 407 .transform(UTF8.decoder) | 407 .transform(UTF8.decoder) |
| 408 .transform(new LineSplitter()); | 408 .transform(new LineSplitter()); |
| 409 stderrStringStream.listen((line) { | 409 stderrStringStream.listen((line) { |
| 410 print("TARG: $line"); | 410 print("TARG: $line"); |
| 411 }); | 411 }); |
| 412 } | 412 } |
| 413 | 413 |
| 414 // Handle debugger events, updating the debugger state. | 414 // Handle debugger events, updating the debugger state. |
| 415 void handleEvent(Map<String,dynamic> msg) { | 415 void handleEvent(Map<String,dynamic> msg) { |
| 416 events.add(new Event(msg)); | 416 events.add(new Event(msg)); |
| 417 | 417 |
| 418 if (msg["event"] == "isolate") { | 418 if (msg["event"] == "isolate") { |
| 419 if (msg["params"]["reason"] == "created") { | 419 if (msg["params"]["reason"] == "created") { |
| 420 isolateId = msg["params"]["id"]; | 420 isolateId = msg["params"]["id"]; |
| 421 assert(isolateId != null); | 421 assert(isolateId != null); |
| 422 print("Debuggee isolate id $isolateId created."); | 422 print("Debuggee isolate id $isolateId created."); |
| 423 } else if (msg["params"]["reason"] == "shutdown") { | 423 } else if (msg["params"]["reason"] == "shutdown") { |
| 424 print("Debuggee isolate id ${msg["params"]["id"]} shut down."); | 424 print("Debuggee isolate id ${msg["params"]["id"]} shut down."); |
| 425 shutdownEventSeen = true; | 425 shutdownEventSeen = true; |
| 426 if (!script.isEmpty) { | 426 if (!script.isEmpty) { |
| 427 error("Error: premature isolate shutdown event seen."); | 427 error("Error: premature isolate shutdown event seen."); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 465 error("Response received from debug target: $receivedMsg"); | 465 error("Response received from debug target: $receivedMsg"); |
| 466 } | 466 } |
| 467 } | 467 } |
| 468 } | 468 } |
| 469 | 469 |
| 470 // Send next debugger command in the script, if a response | 470 // Send next debugger command in the script, if a response |
| 471 // form the last command has been received and processed. | 471 // form the last command has been received and processed. |
| 472 void sendNextCommand() { | 472 void sendNextCommand() { |
| 473 while (script.isNextEventMatcher) { | 473 while (script.isNextEventMatcher) { |
| 474 EventMatcher matcher = script.currentEntry; | 474 EventMatcher matcher = script.currentEntry; |
| 475 script.advance(); | 475 script.advance(); |
| 476 matcher.matchEvent(this); | 476 matcher.matchEvent(this); |
| 477 } | 477 } |
| 478 | 478 |
| 479 if (lastCommand == null) { | 479 if (lastCommand == null) { |
| 480 if (script.currentEntry is Command) { | 480 if (script.currentEntry is Command) { |
| 481 script.currentEntry.send(this); | 481 script.currentEntry.send(this); |
| 482 lastCommand = script.currentEntry; | 482 lastCommand = script.currentEntry; |
| 483 seqNr++; | 483 seqNr++; |
| 484 script.advance(); | 484 script.advance(); |
| 485 } | 485 } |
| 486 } | 486 } |
| 487 } | 487 } |
| 488 | 488 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 547 handleMessages(); | 547 handleMessages(); |
| 548 } catch(e, trace) { | 548 } catch(e, trace) { |
| 549 print("Unexpected exception:\n$e\n$trace"); | 549 print("Unexpected exception:\n$e\n$trace"); |
| 550 cleanup(); | 550 cleanup(); |
| 551 } | 551 } |
| 552 }, | 552 }, |
| 553 onDone: () { | 553 onDone: () { |
| 554 print("Connection closed by debug target"); | 554 print("Connection closed by debug target"); |
| 555 cleanup(); | 555 cleanup(); |
| 556 }, | 556 }, |
| 557 onError: (e) { | 557 onError: (e, trace) { |
| 558 print("Error '$e' detected in input stream from debug target"); | 558 print("Error '$e' detected in input stream from debug target"); |
| 559 var trace = getAttachedStackTrace(e); | |
| 560 if (trace != null) print("StackTrace: $trace"); | 559 if (trace != null) print("StackTrace: $trace"); |
| 561 cleanup(); | 560 cleanup(); |
| 562 }); | 561 }); |
| 563 }, | 562 }, |
| 564 onError: (e) { | 563 onError: (e, trace) { |
| 565 String msg = "Error while connecting to debugee: $e"; | 564 String msg = "Error while connecting to debugee: $e"; |
| 566 var trace = getAttachedStackTrace(e); | |
| 567 if (trace != null) msg += "\nStackTrace: $trace"; | 565 if (trace != null) msg += "\nStackTrace: $trace"; |
| 568 error(msg); | 566 error(msg); |
| 569 cleanup(); | 567 cleanup(); |
| 570 }); | 568 }); |
| 571 } | 569 } |
| 572 | 570 |
| 573 void cleanup() { | 571 void cleanup() { |
| 574 if (cleanupDone) return; | 572 if (cleanupDone) return; |
| 575 if (socket != null) { | 573 if (socket != null) { |
| 576 socket.close().catchError((error) { | 574 socket.close().catchError((error) { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 614 targetOpts.add("--debuggee"); | 612 targetOpts.add("--debuggee"); |
| 615 print('args: ${targetOpts.join(" ")}'); | 613 print('args: ${targetOpts.join(" ")}'); |
| 616 | 614 |
| 617 Process.start(Platform.executable, targetOpts).then((Process process) { | 615 Process.start(Platform.executable, targetOpts).then((Process process) { |
| 618 print("Debug target process started, pid ${process.pid}."); | 616 print("Debug target process started, pid ${process.pid}."); |
| 619 process.stdin.close(); | 617 process.stdin.close(); |
| 620 var debugger = new Debugger(process, new DebugScript(script)); | 618 var debugger = new Debugger(process, new DebugScript(script)); |
| 621 }); | 619 }); |
| 622 return true; | 620 return true; |
| 623 } | 621 } |
| OLD | NEW |