| Index: runtime/observatory/lib/src/elements/debugger.dart
|
| diff --git a/runtime/observatory/lib/src/elements/debugger.dart b/runtime/observatory/lib/src/elements/debugger.dart
|
| index 7b9bc053afd758eb68d056b57e69fc6e6e41e3b7..a11f046e66d5616d4b669b5be9bcde23239f5564 100644
|
| --- a/runtime/observatory/lib/src/elements/debugger.dart
|
| +++ b/runtime/observatory/lib/src/elements/debugger.dart
|
| @@ -854,6 +854,67 @@ class RefreshCommand extends DebuggerCommand {
|
| 'Syntax: refresh <subcommand>\n';
|
| }
|
|
|
| +class _VMStreamPrinter {
|
| + ObservatoryDebugger _debugger;
|
| +
|
| + _VMStreamPrinter(this._debugger);
|
| +
|
| + String _savedStream;
|
| + String _savedIsolate;
|
| + String _savedLine;
|
| + List<String> _buffer = [];
|
| +
|
| + void onEvent(String streamName, ServiceEvent event) {
|
| + String isolateName = event.isolate.name;
|
| + // If we get a line from a different isolate/stream, flush
|
| + // any pending output, even if it is not newline-terminated.
|
| + if ((_savedIsolate != null && isolateName != _savedIsolate) ||
|
| + (_savedStream != null && streamName != _savedStream)) {
|
| + flush();
|
| + }
|
| + String data = event.bytesAsString;
|
| + bool hasNewline = data.endsWith('\n');
|
| + if (_savedLine != null) {
|
| + data = _savedLine + data;
|
| + _savedIsolate = null;
|
| + _savedStream = null;
|
| + _savedLine = null;
|
| + }
|
| + var lines = data.split('\n').where((line) => line != '').toList();
|
| + if (lines.isEmpty) {
|
| + return;
|
| + }
|
| + int limit = (hasNewline ? lines.length : lines.length - 1);
|
| + for (int i = 0; i < limit; i++) {
|
| + _buffer.add(_format(isolateName, streamName, lines[i]));
|
| + }
|
| + // If there is no newline, we save the last line of output for next time.
|
| + if (!hasNewline) {
|
| + _savedIsolate = isolateName;
|
| + _savedStream = streamName;
|
| + _savedLine = lines[lines.length - 1];
|
| + }
|
| + }
|
| +
|
| + void flush() {
|
| + // If there is any saved output, flush it now.
|
| + if (_savedLine != null) {
|
| + _buffer.add(_format(_savedIsolate, _savedStream, _savedLine));
|
| + _savedIsolate = null;
|
| + _savedStream = null;
|
| + _savedLine = null;
|
| + }
|
| + if (_buffer.isNotEmpty) {
|
| + _debugger.console.printStdio(_buffer);
|
| + _buffer.clear();
|
| + }
|
| + }
|
| +
|
| + String _format(String isolateName, String streamName, String line) {
|
| + return '${isolateName}:${streamName}> ${line}';
|
| + }
|
| +}
|
| +
|
| // Tracks the state for an isolate debugging session.
|
| class ObservatoryDebugger extends Debugger {
|
| RootCommand cmd;
|
| @@ -898,6 +959,7 @@ class ObservatoryDebugger extends Debugger {
|
| new IsolateCommand(this),
|
| new RefreshCommand(this),
|
| ]);
|
| + _stdioPrinter = new _VMStreamPrinter(this);
|
| }
|
|
|
| VM get vm => page.app.vm;
|
| @@ -997,6 +1059,7 @@ class ObservatoryDebugger extends Debugger {
|
| }
|
|
|
| void reportStatus() {
|
| + flushStdio();
|
| if (_isolate == null) {
|
| console.print('No current isolate');
|
| } else if (_isolate.idle) {
|
| @@ -1116,6 +1179,7 @@ class ObservatoryDebugger extends Debugger {
|
| case ServiceEvent.kPauseException:
|
| if (event.owner == isolate) {
|
| _refreshStack(event).then((_) {
|
| + flushStdio();
|
| _reportPause(event);
|
| });
|
| }
|
| @@ -1123,6 +1187,7 @@ class ObservatoryDebugger extends Debugger {
|
|
|
| case ServiceEvent.kResume:
|
| if (event.owner == isolate) {
|
| + flushStdio();
|
| console.print('Continuing...');
|
| }
|
| break;
|
| @@ -1147,6 +1212,20 @@ class ObservatoryDebugger extends Debugger {
|
| }
|
| }
|
|
|
| + _VMStreamPrinter _stdioPrinter;
|
| +
|
| + void flushStdio() {
|
| + _stdioPrinter.flush();
|
| + }
|
| +
|
| + void onStdout(ServiceEvent event) {
|
| + _stdioPrinter.onEvent('stdout', event);
|
| + }
|
| +
|
| + void onStderr(ServiceEvent event) {
|
| + _stdioPrinter.onEvent('stderr', event);
|
| + }
|
| +
|
| static String _commonPrefix(String a, String b) {
|
| int pos = 0;
|
| while (pos < a.length && pos < b.length) {
|
| @@ -1236,6 +1315,8 @@ class DebuggerPageElement extends ObservatoryElement {
|
|
|
| Future<StreamSubscription> _isolateSubscriptionFuture;
|
| Future<StreamSubscription> _debugSubscriptionFuture;
|
| + Future<StreamSubscription> _stdoutSubscriptionFuture;
|
| + Future<StreamSubscription> _stderrSubscriptionFuture;
|
|
|
| @override
|
| void attached() {
|
| @@ -1269,6 +1350,13 @@ class DebuggerPageElement extends ObservatoryElement {
|
| app.vm.listenEventStream(VM.kIsolateStream, debugger.onEvent);
|
| _debugSubscriptionFuture =
|
| app.vm.listenEventStream(VM.kDebugStream, debugger.onEvent);
|
| + _stdoutSubscriptionFuture =
|
| + app.vm.listenEventStream(VM.kStdoutStream, debugger.onStdout);
|
| + _stderrSubscriptionFuture =
|
| + app.vm.listenEventStream(VM.kStderrStream, debugger.onStderr);
|
| +
|
| + // Turn on the periodic poll timer for this page.
|
| + pollPeriod = const Duration(milliseconds:100);
|
|
|
| onClick.listen((event) {
|
| // Random clicks should focus on the text box. If the user selects
|
| @@ -1280,12 +1368,20 @@ class DebuggerPageElement extends ObservatoryElement {
|
| });
|
| }
|
|
|
| + void onPoll() {
|
| + debugger.flushStdio();
|
| + }
|
| +
|
| @override
|
| void detached() {
|
| cancelFutureSubscription(_isolateSubscriptionFuture);
|
| _isolateSubscriptionFuture = null;
|
| cancelFutureSubscription(_debugSubscriptionFuture);
|
| _debugSubscriptionFuture = null;
|
| + cancelFutureSubscription(_stdoutSubscriptionFuture);
|
| + _stdoutSubscriptionFuture = null;
|
| + cancelFutureSubscription(_stderrSubscriptionFuture);
|
| + _stderrSubscriptionFuture = null;
|
| super.detached();
|
| }
|
| }
|
| @@ -1665,6 +1761,21 @@ class DebuggerConsoleElement extends ObservatoryElement {
|
| span.scrollIntoView();
|
| }
|
|
|
| + void printStdio(List<String> lines) {
|
| + var lastSpan;
|
| + for (var line in lines) {
|
| + var span = new SpanElement();
|
| + span.classes.add('green');
|
| + span.appendText(line);
|
| + span.appendText('\n');
|
| + $['consoleText'].children.add(span);
|
| + lastSpan = span;
|
| + }
|
| + if (lastSpan != null) {
|
| + lastSpan.scrollIntoView();
|
| + }
|
| + }
|
| +
|
| void printRef(Instance ref, { bool newline:true }) {
|
| var refElement = new Element.tag('instance-ref');
|
| refElement.ref = ref;
|
|
|