| 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;
 | 
| 
 |