| 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"; |
| 11 import "dart:math"; | 11 import "dart:math"; |
| 12 import "dart:utf"; | 12 import "dart:utf"; |
| 13 import "dart:json" as JSON; | 13 import "dart:json" as JSON; |
| 14 | 14 |
| 15 // Whether or not to print the debugger wire messages on the console. | 15 // Whether or not to print the debugger wire messages on the console. |
| 16 var verboseWire = false; | 16 var verboseWire = false; |
| 17 | 17 |
| 18 // The number of attempts made to find an unused debugger port. | |
| 19 var retries = 0; | |
| 20 | |
| 21 // Class to buffer wire protocol data from debug target and | 18 // Class to buffer wire protocol data from debug target and |
| 22 // break it down to individual json messages. | 19 // break it down to individual json messages. |
| 23 class JsonBuffer { | 20 class JsonBuffer { |
| 24 String buffer = null; | 21 String buffer = null; |
| 25 | 22 |
| 26 append(String s) { | 23 append(String s) { |
| 27 if (buffer == null || buffer.length == 0) { | 24 if (buffer == null || buffer.length == 0) { |
| 28 buffer = s; | 25 buffer = s; |
| 29 } else { | 26 } else { |
| 30 buffer += s; | 27 buffer += s; |
| (...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 290 bool get isEmpty => entries.isEmpty; | 287 bool get isEmpty => entries.isEmpty; |
| 291 get currentEntry => entries.last; | 288 get currentEntry => entries.last; |
| 292 advance() => entries.removeLast(); | 289 advance() => entries.removeLast(); |
| 293 add(entry) => entries.add(entry); | 290 add(entry) => entries.add(entry); |
| 294 } | 291 } |
| 295 | 292 |
| 296 | 293 |
| 297 class Debugger { | 294 class Debugger { |
| 298 // Debug target process properties. | 295 // Debug target process properties. |
| 299 Process targetProcess; | 296 Process targetProcess; |
| 300 int portNumber; | |
| 301 Socket socket; | 297 Socket socket; |
| 302 JsonBuffer responses = new JsonBuffer(); | 298 JsonBuffer responses = new JsonBuffer(); |
| 303 | 299 |
| 304 DebugScript script; | 300 DebugScript script; |
| 305 int seqNr = 0; // Sequence number of next debugger command message. | 301 int seqNr = 0; // Sequence number of next debugger command message. |
| 306 Command lastCommand = null; // Most recent command sent to target. | 302 Command lastCommand = null; // Most recent command sent to target. |
| 307 List<String> errors = new List(); | 303 List<String> errors = new List(); |
| 308 bool cleanupDone = false; | 304 bool cleanupDone = false; |
| 309 | 305 |
| 310 // Data collected from debug target. | 306 // Data collected from debug target. |
| 311 Map currentMessage = null; // Currently handled message sent by target. | 307 Map currentMessage = null; // Currently handled message sent by target. |
| 312 String scriptUrl = null; | 308 String scriptUrl = null; |
| 313 bool shutdownEventSeen = false; | 309 bool shutdownEventSeen = false; |
| 314 int isolateId = 0; | 310 int isolateId = 0; |
| 315 bool isPaused = false; | 311 bool isPaused = false; |
| 316 | 312 |
| 317 Debugger(this.targetProcess, this.portNumber, this.script) { | 313 Debugger(this.targetProcess, this.script) { |
| 318 stdin.listen((_) {}); | |
| 319 var stdoutStringStream = targetProcess.stdout | 314 var stdoutStringStream = targetProcess.stdout |
| 320 .transform(new StringDecoder()) | 315 .transform(new StringDecoder()) |
| 321 .transform(new LineTransformer()); | 316 .transform(new LineTransformer()); |
| 322 stdoutStringStream.listen((line) { | 317 stdoutStringStream.listen((line) { |
| 323 if (line == "Debugger initialized") { | 318 print("TARG: $line"); |
| 324 openConnection(); | 319 if (line.startsWith("Debugger listening")) { |
| 320 RegExp portExpr = new RegExp(r"\d+"); |
| 321 var port = portExpr.stringMatch(line); |
| 322 print("Debug target found listening at port '$port'"); |
| 323 openConnection(int.parse(port)); |
| 325 } | 324 } |
| 326 print("TARG: $line"); | |
| 327 }); | 325 }); |
| 328 | 326 |
| 329 var stderrStringStream = targetProcess.stderr | 327 var stderrStringStream = targetProcess.stderr |
| 330 .transform(new StringDecoder()) | 328 .transform(new StringDecoder()) |
| 331 .transform(new LineTransformer()); | 329 .transform(new LineTransformer()); |
| 332 stderrStringStream.listen((line) { | 330 stderrStringStream.listen((line) { |
| 333 print("TARG: $line"); | 331 print("TARG: $line"); |
| 334 }); | 332 }); |
| 335 } | 333 } |
| 336 | 334 |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 448 socket.write(jsonMsg); | 446 socket.write(jsonMsg); |
| 449 } | 447 } |
| 450 | 448 |
| 451 bool get errorsDetected => errors.length > 0; | 449 bool get errorsDetected => errors.length > 0; |
| 452 | 450 |
| 453 // Record error message. | 451 // Record error message. |
| 454 void error(String s) { | 452 void error(String s) { |
| 455 errors.add(s); | 453 errors.add(s); |
| 456 } | 454 } |
| 457 | 455 |
| 458 void openConnection() { | 456 void openConnection(int portNumber) { |
| 459 Socket.connect("127.0.0.1", portNumber).then((s) { | 457 Socket.connect("127.0.0.1", portNumber).then((s) { |
| 460 this.socket = s; | 458 this.socket = s; |
| 461 var stringStream = socket.transform(new StringDecoder()); | 459 var stringStream = socket.transform(new StringDecoder()); |
| 462 stringStream.listen((str) { | 460 stringStream.listen((str) { |
| 463 try { | 461 try { |
| 464 responses.append(str); | 462 responses.append(str); |
| 465 handleMessages(); | 463 handleMessages(); |
| 466 } catch(e, trace) { | 464 } catch(e, trace) { |
| 467 print("Unexpected exception:\n$e\n$trace"); | 465 print("Unexpected exception:\n$e\n$trace"); |
| 468 cleanup(); | 466 cleanup(); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 492 if (cleanupDone) return; | 490 if (cleanupDone) return; |
| 493 if (socket != null) { | 491 if (socket != null) { |
| 494 socket.close().catchError((error) { | 492 socket.close().catchError((error) { |
| 495 // Print this directly in addition to adding it to the | 493 // Print this directly in addition to adding it to the |
| 496 // error message queue, in case the error message queue | 494 // error message queue, in case the error message queue |
| 497 // gets printed before this error handler is called. | 495 // gets printed before this error handler is called. |
| 498 print("Error occurred while closing socket: $error"); | 496 print("Error occurred while closing socket: $error"); |
| 499 error("Error while closing socket: $error"); | 497 error("Error while closing socket: $error"); |
| 500 }); | 498 }); |
| 501 } | 499 } |
| 500 var targetPid = targetProcess.pid; |
| 501 print("Sending kill signal to process $targetPid..."); |
| 502 targetProcess.kill(); | 502 targetProcess.kill(); |
| 503 // If the process was already dead exitCode is already | 503 // If the process was already dead exitCode is already |
| 504 // available and we call exit() in the next event loop cycle. | 504 // available and we call exit() in the next event loop cycle. |
| 505 // Otherwise this will wait for the process to exit. | 505 // Otherwise this will wait for the process to exit. |
| 506 targetProcess.exitCode.then((exitCode) { | 506 targetProcess.exitCode.then((exitCode) { |
| 507 print("process $targetPid terminated with exit code $exitCode."); |
| 507 if (errorsDetected) { | 508 if (errorsDetected) { |
| 508 print("\n===== Errors detected: ====="); | 509 print("\n===== Errors detected: ====="); |
| 509 for (int i = 0; i < errors.length; i++) print(errors[i]); | 510 for (int i = 0; i < errors.length; i++) print(errors[i]); |
| 510 print("============================\n"); | 511 print("============================\n"); |
| 511 } | 512 } |
| 512 exit(errors.length); | 513 exit(errors.length); |
| 513 }); | 514 }); |
| 514 cleanupDone = true; | 515 cleanupDone = true; |
| 515 } | 516 } |
| 516 } | 517 } |
| 517 | 518 |
| 518 | 519 |
| 519 bool RunScript(List script) { | 520 bool RunScript(List script) { |
| 520 var options = new Options(); | 521 var options = new Options(); |
| 521 if (options.arguments.contains("--debuggee")) { | 522 if (options.arguments.contains("--debuggee")) { |
| 522 return false; | 523 return false; |
| 523 } | 524 } |
| 524 verboseWire = options.arguments.contains("--wire"); | 525 verboseWire = options.arguments.contains("--wire"); |
| 525 | 526 |
| 526 // Pick a port in the upper half of the port number range. | 527 // Port number 0 means debug target picks a free port dynamically. |
| 527 var seed = new DateTime.now().millisecondsSinceEpoch; | 528 var targetOpts = [ "--debug:0" ]; |
| 528 Random random = new Random(seed); | 529 targetOpts.add(options.script); |
| 529 var debugPort = random.nextInt(32000) + 32000; | 530 targetOpts.add("--debuggee"); |
| 530 print('using debug port $debugPort ...'); | 531 print('args: ${targetOpts.join(" ")}'); |
| 531 ServerSocket.bind('127.0.0.1', debugPort).then((ServerSocket s) { | |
| 532 s.close(); | |
| 533 var targetOpts = [ "--debug:$debugPort" ]; | |
| 534 // --verbose_debug is necessary so the test knows when the debuggee | |
| 535 // is initialized. | |
| 536 targetOpts.add("--verbose_debug"); | |
| 537 targetOpts.add(options.script); | |
| 538 targetOpts.add("--debuggee"); | |
| 539 print('args: ${targetOpts.join(" ")}'); | |
| 540 | 532 |
| 541 Process.start(options.executable, targetOpts).then((Process process) { | 533 Process.start(options.executable, targetOpts).then((Process process) { |
| 542 print("Debug target process started"); | 534 print("Debug target process started, pid ${process.pid}."); |
| 543 process.stdin.close(); | 535 process.stdin.close(); |
| 544 process.exitCode.then((int exitCode) { | 536 var debugger = new Debugger(process, new DebugScript(script)); |
| 545 if (exitCode != 0) throw "bad exit code: $exitCode"; | 537 }); |
| 546 print("Debug target process exited with exit code $exitCode"); | |
| 547 }); | |
| 548 var debugger = | |
| 549 new Debugger(process, debugPort, new DebugScript(script)); | |
| 550 }); | |
| 551 }, | |
| 552 onError: (e) { | |
| 553 if (++retries >= 3) { | |
| 554 print('unable to find unused port: $e'); | |
| 555 var trace = getAttachedStackTrace(e); | |
| 556 if (trace != null) print("StackTrace: $trace"); | |
| 557 return -1; | |
| 558 } else { | |
| 559 // Retry with another random port. | |
| 560 RunScript(script); | |
| 561 } | |
| 562 }); | |
| 563 return true; | 538 return true; |
| 564 } | 539 } |
| OLD | NEW |