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 |