| 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:io"; | 10 import "dart:io"; |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 172 | 172 |
| 173 void send(Debugger debugger) { | 173 void send(Debugger debugger) { |
| 174 debugger.sendMessage(template); | 174 debugger.sendMessage(template); |
| 175 } | 175 } |
| 176 | 176 |
| 177 void matchResponse(Debugger debugger) { | 177 void matchResponse(Debugger debugger) { |
| 178 Map response = debugger.currentMessage; | 178 Map response = debugger.currentMessage; |
| 179 var id = template["id"]; | 179 var id = template["id"]; |
| 180 assert(id != null && id >= 0); | 180 assert(id != null && id >= 0); |
| 181 if (response["id"] != id) { | 181 if (response["id"] != id) { |
| 182 debugger.error("Expected messaged id $id but got ${response["id"]}."); | 182 debugger.error("Error: expected messaged id $id but got ${response["id"]}.
"); |
| 183 } | 183 } |
| 184 } | 184 } |
| 185 } | 185 } |
| 186 | 186 |
| 187 | 187 |
| 188 class FrameMatcher extends Command { | 188 class FrameMatcher extends Command { |
| 189 int frameIndex; | 189 int frameIndex; |
| 190 List<String> functionNames; | 190 List<String> functionNames; |
| 191 | 191 |
| 192 FrameMatcher(this.frameIndex, this.functionNames) { | 192 FrameMatcher(this.frameIndex, this.functionNames) { |
| 193 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; | 193 template = {"id": 0, "command": "getStackTrace", "params": {"isolateId": 0}}
; |
| 194 } | 194 } |
| 195 | 195 |
| 196 void matchResponse(Debugger debugger) { | 196 void matchResponse(Debugger debugger) { |
| 197 super.matchResponse(debugger); | 197 super.matchResponse(debugger); |
| 198 var msg = debugger.currentMessage; | 198 var msg = debugger.currentMessage; |
| 199 List frames = getJsonValue(msg, "result:callFrames"); | 199 List frames = getJsonValue(msg, "result:callFrames"); |
| 200 assert(frames != null); | 200 assert(frames != null); |
| 201 if (debugger.scriptUrl == null) { | 201 if (debugger.scriptUrl == null) { |
| 202 var name = frames[0]["functionName"]; | 202 var name = frames[0]["functionName"]; |
| 203 if (name == "main") { | 203 if (name == "main") { |
| 204 // Extract script url of debugged script. | 204 // Extract script url of debugged script. |
| 205 debugger.scriptUrl = frames[0]["location"]["url"]; | 205 debugger.scriptUrl = frames[0]["location"]["url"]; |
| 206 assert(debugger.scriptUrl != null); | 206 assert(debugger.scriptUrl != null); |
| 207 } | 207 } |
| 208 } | 208 } |
| 209 if (frames.length < functionNames.length) { | 209 if (frames.length < functionNames.length) { |
| 210 debugger.error("stack trace not long enough " | 210 debugger.error("Error: stack trace not long enough " |
| 211 "to match ${functionNames.length} frames"); | 211 "to match ${functionNames.length} frames"); |
| 212 return; | 212 return; |
| 213 } | 213 } |
| 214 for (int i = 0; i < functionNames.length; i++) { | 214 for (int i = 0; i < functionNames.length; i++) { |
| 215 var idx = i + frameIndex; | 215 var idx = i + frameIndex; |
| 216 var name = frames[idx]["functionName"]; | 216 var name = frames[idx]["functionName"]; |
| 217 assert(name != null); | 217 assert(name != null); |
| 218 if (name != functionNames[i]) { | 218 if (name != functionNames[i]) { |
| 219 debugger.error("call frame $idx: " | 219 debugger.error("Error: call frame $idx: " |
| 220 "expected function name '${functionNames[i]}' but found '$name'"); | 220 "expected function name '${functionNames[i]}' but found '$name'"); |
| 221 return; | 221 return; |
| 222 } | 222 } |
| 223 } | 223 } |
| 224 } | 224 } |
| 225 } | 225 } |
| 226 | 226 |
| 227 | 227 |
| 228 MatchFrame(int frameIndex, String functionName) { | 228 MatchFrame(int frameIndex, String functionName) { |
| 229 return new FrameMatcher(frameIndex, [ functionName ]); | 229 return new FrameMatcher(frameIndex, [ functionName ]); |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 298 // Debug target process properties. | 298 // Debug target process properties. |
| 299 Process targetProcess; | 299 Process targetProcess; |
| 300 int portNumber; | 300 int portNumber; |
| 301 Socket socket; | 301 Socket socket; |
| 302 JsonBuffer responses = new JsonBuffer(); | 302 JsonBuffer responses = new JsonBuffer(); |
| 303 | 303 |
| 304 DebugScript script; | 304 DebugScript script; |
| 305 int seqNr = 0; // Sequence number of next debugger command message. | 305 int seqNr = 0; // Sequence number of next debugger command message. |
| 306 Command lastCommand = null; // Most recent command sent to target. | 306 Command lastCommand = null; // Most recent command sent to target. |
| 307 List<String> errors = new List(); | 307 List<String> errors = new List(); |
| 308 bool cleanupDone = false; |
| 308 | 309 |
| 309 // Data collected from debug target. | 310 // Data collected from debug target. |
| 310 Map currentMessage = null; // Currently handled message sent by target. | 311 Map currentMessage = null; // Currently handled message sent by target. |
| 311 String scriptUrl = null; | 312 String scriptUrl = null; |
| 312 bool shutdownEventSeen = false; | 313 bool shutdownEventSeen = false; |
| 313 int isolateId = 0; | 314 int isolateId = 0; |
| 314 bool isPaused = false; | 315 bool isPaused = false; |
| 315 | 316 |
| 316 Debugger(this.targetProcess, this.portNumber, this.script) { | 317 Debugger(this.targetProcess, this.portNumber, this.script) { |
| 317 stdin.listen((_) {}); | 318 stdin.listen((_) {}); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 337 void handleEvent(Map<String,dynamic> msg) { | 338 void handleEvent(Map<String,dynamic> msg) { |
| 338 if (msg["event"] == "isolate") { | 339 if (msg["event"] == "isolate") { |
| 339 if (msg["params"]["reason"] == "created") { | 340 if (msg["params"]["reason"] == "created") { |
| 340 isolateId = msg["params"]["id"]; | 341 isolateId = msg["params"]["id"]; |
| 341 assert(isolateId != null); | 342 assert(isolateId != null); |
| 342 print("Debuggee isolate id $isolateId created."); | 343 print("Debuggee isolate id $isolateId created."); |
| 343 } else if (msg["params"]["reason"] == "shutdown") { | 344 } else if (msg["params"]["reason"] == "shutdown") { |
| 344 print("Debuggee isolate id ${msg["params"]["id"]} shut down."); | 345 print("Debuggee isolate id ${msg["params"]["id"]} shut down."); |
| 345 shutdownEventSeen = true; | 346 shutdownEventSeen = true; |
| 346 if (!script.isEmpty) { | 347 if (!script.isEmpty) { |
| 347 error("Premature isolate shutdown event seen."); | 348 error("Error: premature isolate shutdown event seen."); |
| 348 } | 349 } |
| 349 } | 350 } |
| 350 } else if (msg["event"] == "breakpointResolved") { | 351 } else if (msg["event"] == "breakpointResolved") { |
| 351 var bpId = msg["params"]["breakpointId"]; | 352 var bpId = msg["params"]["breakpointId"]; |
| 352 assert(bpId != null); | 353 assert(bpId != null); |
| 353 var isolateId = msg["params"]["isolateId"]; | 354 var isolateId = msg["params"]["isolateId"]; |
| 354 assert(isolateId != null); | 355 assert(isolateId != null); |
| 355 var location = msg["params"]["location"]; | 356 var location = msg["params"]["location"]; |
| 356 assert(location != null); | 357 assert(location != null); |
| 357 print("Isolate $isolateId: breakpoint $bpId resolved" | 358 print("Isolate $isolateId: breakpoint $bpId resolved" |
| 358 " at location $location"); | 359 " at location $location"); |
| 359 // We may want to maintain a table of breakpoints in the future. | 360 // We may want to maintain a table of breakpoints in the future. |
| 360 } else if (msg["event"] == "paused") { | 361 } else if (msg["event"] == "paused") { |
| 361 isPaused = true; | 362 isPaused = true; |
| 362 } else { | 363 } else { |
| 363 error("unknown debugger event received"); | 364 error("Error: unknown debugger event received"); |
| 364 } | 365 } |
| 365 } | 366 } |
| 366 | 367 |
| 367 // Handle one JSON message object and match it to the | 368 // Handle one JSON message object and match it to the |
| 368 // expected events and responses in the debugging script. | 369 // expected events and responses in the debugging script. |
| 369 void handleMessage(Map<String,dynamic> receivedMsg) { | 370 void handleMessage(Map<String,dynamic> receivedMsg) { |
| 370 currentMessage = receivedMsg; | 371 currentMessage = receivedMsg; |
| 371 if (receivedMsg["event"] != null) { | 372 if (receivedMsg["event"] != null) { |
| 372 handleEvent(receivedMsg); | 373 handleEvent(receivedMsg); |
| 373 if (errorsDetected) { | 374 if (errorsDetected) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 402 // Handle data received over the wire from the debug target | 403 // Handle data received over the wire from the debug target |
| 403 // process. Split input from JSON wire format into individual | 404 // process. Split input from JSON wire format into individual |
| 404 // message objects (maps). | 405 // message objects (maps). |
| 405 void handleMessages() { | 406 void handleMessages() { |
| 406 var msg = responses.getNextMessage(); | 407 var msg = responses.getNextMessage(); |
| 407 while (msg != null) { | 408 while (msg != null) { |
| 408 if (verboseWire) print("RECV: $msg"); | 409 if (verboseWire) print("RECV: $msg"); |
| 409 if (responses.haveGarbage()) { | 410 if (responses.haveGarbage()) { |
| 410 error("Error: leftover text after message: '${responses.buffer}'"); | 411 error("Error: leftover text after message: '${responses.buffer}'"); |
| 411 error("Previous message may be malformed, was: '$msg'"); | 412 error("Previous message may be malformed, was: '$msg'"); |
| 412 close(killDebugee: true); | 413 cleanup(); |
| 413 return; | 414 return; |
| 414 } | 415 } |
| 415 var msgObj = JSON.parse(msg); | 416 var msgObj = JSON.parse(msg); |
| 416 handleMessage(msgObj); | 417 handleMessage(msgObj); |
| 417 if (errorsDetected) { | 418 if (errorsDetected) { |
| 418 error("Error while handling script entry"); | 419 error("Error while handling script entry"); |
| 419 error("Message received from debug target: $msg"); | 420 error("Message received from debug target: $msg"); |
| 420 close(killDebugee: true); | 421 cleanup(); |
| 421 return; | 422 return; |
| 422 } | 423 } |
| 423 if (shutdownEventSeen) { | 424 if (shutdownEventSeen) { |
| 424 close(); | 425 cleanup(); |
| 425 return; | 426 return; |
| 426 } | 427 } |
| 427 if (isPaused) sendNextCommand(); | 428 if (isPaused) sendNextCommand(); |
| 428 msg = responses.getNextMessage(); | 429 msg = responses.getNextMessage(); |
| 429 } | 430 } |
| 430 } | 431 } |
| 431 | 432 |
| 432 runScript(List entries) { | 433 runScript(List entries) { |
| 433 script = new DebugScript(entries); | 434 script = new DebugScript(entries); |
| 434 openConnection(); | 435 openConnection(); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 457 void openConnection() { | 458 void openConnection() { |
| 458 Socket.connect("127.0.0.1", portNumber).then((s) { | 459 Socket.connect("127.0.0.1", portNumber).then((s) { |
| 459 this.socket = s; | 460 this.socket = s; |
| 460 var stringStream = socket.transform(new StringDecoder()); | 461 var stringStream = socket.transform(new StringDecoder()); |
| 461 stringStream.listen((str) { | 462 stringStream.listen((str) { |
| 462 try { | 463 try { |
| 463 responses.append(str); | 464 responses.append(str); |
| 464 handleMessages(); | 465 handleMessages(); |
| 465 } catch(e, trace) { | 466 } catch(e, trace) { |
| 466 print("Unexpected exception:\n$e\n$trace"); | 467 print("Unexpected exception:\n$e\n$trace"); |
| 467 close(killDebugee: true); | 468 cleanup(); |
| 468 } | 469 } |
| 469 }, | 470 }, |
| 470 onDone: () { | 471 onDone: () { |
| 471 print("Connection closed by debug target"); | 472 print("Connection closed by debug target"); |
| 472 close(killDebugee: true); | 473 cleanup(); |
| 473 }, | 474 }, |
| 474 onError: (e) { | 475 onError: (e) { |
| 475 print("Error '$e' detected in input stream from debug target"); | 476 print("Error '$e' detected in input stream from debug target"); |
| 476 var trace = getAttachedStackTrace(e); | 477 var trace = getAttachedStackTrace(e); |
| 477 if (trace != null) print("StackTrace: $trace"); | 478 if (trace != null) print("StackTrace: $trace"); |
| 478 close(killDebugee: true); | 479 cleanup(); |
| 479 }); | 480 }); |
| 480 }, | 481 }, |
| 481 onError: (e) { | 482 onError: (e) { |
| 482 String msg = "Error while connecting to debugee: $e"; | 483 String msg = "Error while connecting to debugee: $e"; |
| 483 var trace = getAttachedStackTrace(e); | 484 var trace = getAttachedStackTrace(e); |
| 484 if (trace != null) msg += "\nStackTrace: $trace"; | 485 if (trace != null) msg += "\nStackTrace: $trace"; |
| 485 error(msg); | 486 error(msg); |
| 486 close(killDebugee: true); | 487 cleanup(); |
| 487 }); | 488 }); |
| 488 } | 489 } |
| 489 | 490 |
| 490 void close({killDebugee: false}) { | 491 void cleanup() { |
| 491 void printErrorsAndExit() { | 492 if (cleanupDone) return; |
| 492 if (errorsDetected) throw "Errors detected"; | |
| 493 exit(errors.length); | |
| 494 } | |
| 495 if (errorsDetected) { | |
| 496 for (int i = 0; i < errors.length; i++) print(errors[i]); | |
| 497 } | |
| 498 if (socket != null) { | 493 if (socket != null) { |
| 499 socket.close().catchError((error) { | 494 socket.close().catchError((error) { |
| 500 print("Error occured while closing socket: $error"); | 495 // Print this directly in addition to adding it to the |
| 496 // error message queue, in case the error message queue |
| 497 // gets printed before this error handler is called. |
| 498 print("Error occurred while closing socket: $error"); |
| 499 error("Error while closing socket: $error"); |
| 501 }); | 500 }); |
| 502 } | 501 } |
| 503 if (killDebugee) { | 502 targetProcess.kill(); |
| 504 if (!targetProcess.kill()) { | 503 // If the process was already dead exitCode is already |
| 505 print("Could not send kill signal to target process."); | 504 // available and we call exit() in the next event loop cycle. |
| 506 } else { | 505 // Otherwise this will wait for the process to exit. |
| 507 print("Successfully sent kill signal to target process."); | 506 targetProcess.exitCode.then((exitCode) { |
| 507 if (errorsDetected) { |
| 508 print("\n===== Errors detected: ====="); |
| 509 for (int i = 0; i < errors.length; i++) print(errors[i]); |
| 510 print("============================\n"); |
| 508 } | 511 } |
| 509 // If the process was already dead exitCode is already | 512 exit(errors.length); |
| 510 // available and we call exit() in the next event loop cycle. | 513 }); |
| 511 // Otherwise this will wait for the process to exit. | 514 cleanupDone = true; |
| 512 targetProcess.exitCode.then((exitCode) { | |
| 513 printErrorsAndExit(); | |
| 514 }); | |
| 515 } else { | |
| 516 printErrorsAndExit(); | |
| 517 } | |
| 518 } | 515 } |
| 519 } | 516 } |
| 520 | 517 |
| 521 | 518 |
| 522 bool RunScript(List script) { | 519 bool RunScript(List script) { |
| 523 var options = new Options(); | 520 var options = new Options(); |
| 524 if (options.arguments.contains("--debuggee")) { | 521 if (options.arguments.contains("--debuggee")) { |
| 525 return false; | 522 return false; |
| 526 } | 523 } |
| 527 verboseWire = options.arguments.contains("--wire"); | 524 verboseWire = options.arguments.contains("--wire"); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 558 var trace = getAttachedStackTrace(e); | 555 var trace = getAttachedStackTrace(e); |
| 559 if (trace != null) print("StackTrace: $trace"); | 556 if (trace != null) print("StackTrace: $trace"); |
| 560 return -1; | 557 return -1; |
| 561 } else { | 558 } else { |
| 562 // Retry with another random port. | 559 // Retry with another random port. |
| 563 RunScript(script); | 560 RunScript(script); |
| 564 } | 561 } |
| 565 }); | 562 }); |
| 566 return true; | 563 return true; |
| 567 } | 564 } |
| OLD | NEW |