/// <summary>
        /// Sends packets queued by <see cref="SendMessage"/>.
        /// </summary>
        private async void SendPacketsWorker()
        {
            INetworkClient networkClient;

            lock (_networkClientLock) {
                networkClient = _networkClient;
            }
            if (networkClient == null)
            {
                return;
            }

            try {
                var stream = networkClient.GetStream();
                while (Connected)
                {
                    byte[] packet = await _packetsToSend.TakeAsync().ConfigureAwait(false);

                    await stream.WriteAsync(packet, 0, packet.Length).ConfigureAwait(false);

                    await stream.FlushAsync().ConfigureAwait(false);
                }
            } catch (SocketException) {
            } catch (ObjectDisposedException) {
            } catch (InvalidOperationException) {
            } catch (IOException) {
            } catch (Exception e) {
                LiveLogger.WriteLine(string.Format(CultureInfo.CurrentCulture, "Failed to write message {0}.", e), typeof(DebuggerConnection));
                throw;
            }
        }
Ejemplo n.º 2
0
        private void TransportLoop()
        {
            while (!_bQuit)
            {
                string line = GetLine();
                LiveLogger.WriteLine("->" + line);

                try
                {
                    if (!String.IsNullOrWhiteSpace(line))
                    {
                        _callback.OnStdOutLine(line);
                    }
                }
                catch (ObjectDisposedException)
                {
                    Debug.Assert(_bQuit);
                    break;
                }
            }
            if (!_bQuit)
            {
                OnReadStreamAborted();
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Connect to specified debugger endpoint.
        /// </summary>
        /// <param name="uri">URI identifying the endpoint to connect to.</param>
        public void Connect(Uri uri)
        {
            Utilities.ArgumentNotNull("uri", uri);
            LiveLogger.WriteLine("Debugger connecting to URI: {0}", uri);

            Close();
            lock (this._networkClientLock)
            {
                var       connection_attempts = 0;
                const int MAX_ATTEMPTS        = 5;
                while (true)
                {
                    connection_attempts++;
                    try
                    {
                        // TODO: This currently results in a call to the synchronous TcpClient
                        // constructor, which is a blocking call, and can take a couple of seconds
                        // to connect (with timeouts and retries). This code is running on the UI
                        // thread. Ideally this should be connecting async, or moved off the UI thread.
                        this._networkClient = this._networkClientFactory.CreateNetworkClient(uri);

                        // Unclear if the above can succeed and not be connected, but check for safety.
                        // The code needs to either break out the while loop, or hit the retry logic
                        // in the exception handler.
                        if (this._networkClient.Connected)
                        {
                            LiveLogger.WriteLine("Debugger connected successfully");
                            break;
                        }
                        else
                        {
                            throw new SocketException();
                        }
                    }
                    catch (Exception ex)
                    {
                        if (ex.IsCriticalException())
                        {
                            throw;
                        }
                        LiveLogger.WriteLine("Connection attempt {0} failed with: {1}", connection_attempts, ex);
                        if (this._isClosed || connection_attempts >= MAX_ATTEMPTS)
                        {
                            throw;
                        }
                        else
                        {
                            // See above TODO. This should be moved off the UI thread or posted to retry
                            // without blocking in the meantime. For now, this seems the lesser of two
                            // evils. (The other being the debugger failing to attach on launch if the
                            // debuggee socket wasn't open quickly enough).
                            System.Threading.Thread.Sleep(200);
                        }
                    }
                }
            }

            Task.Factory.StartNew(this.ReceiveAndDispatchMessagesWorker);
            Task.Factory.StartNew(this.SendPacketsWorker);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Send a message.
        /// </summary>
        /// <param name="message">Message.</param>
        public void SendMessage(string message)
        {
            Utilities.ArgumentNotNullOrEmpty("message", message);

            if (!Connected)
            {
                return;
            }

            LiveLogger.WriteLine("Request: " + message, typeof(DebuggerConnection));

            var messageBody   = _encoding.GetBytes(message);
            var messageHeader = _encoding.GetBytes(string.Format("Content-Length: {0}\r\n\r\n", messageBody.Length));

            _packetsToSend.Add(messageHeader);
            _packetsToSend.Add(messageBody);
        }
        /// <summary>
        /// Send a message.
        /// </summary>
        /// <param name="message">Message.</param>
        public void SendMessage(string message)
        {
            if (!Connected)
            {
                return;
            }

            LiveLogger.WriteLine("Request: " + message, typeof(DebuggerConnection));

            var messageBody   = _encoding.GetBytes(message);
            var messageHeader = _encoding.GetBytes(messageBody.Length + ":");//  string.Format(CultureInfo.InvariantCulture, "Content-Length: {0}\r\n\r\n", messageBody.Length));

            lock (lock1)
            {
                _packetsToSend.Add(messageHeader);
                _packetsToSend.Add(messageBody);
            }
        }
Ejemplo n.º 6
0
        public static bool BeforeCatch(Exception currentException, bool reportOnlyCorrupting)
        {
            if (reportOnlyCorrupting && !IsCorruptingException(currentException))
            {
                return(true); // ignore non-corrupting exceptions
            }

            try
            {
                LiveLogger.WriteLine("EXCEPTION: " + currentException.GetType());
                LiveLogger.WriteTextBlock("EXCEPTION: ", currentException.StackTrace);
            }
            catch
            {
                // If anything goes wrong, ignore it. We want to report the original exception, not a telemetry problem
            }

            return(true);
        }
Ejemplo n.º 7
0
        private void SendStartDebuggingError(Exception exception)
        {
            if (exception is OperationCanceledException)
            {
                return; // don't show a message in this case
            }

            string description = EngineUtils.GetExceptionDescription(exception);
            string message     = string.Format(CultureInfo.CurrentCulture, MICoreResources.Error_UnableToStartDebugging, description);

            var initializationException = exception as MIDebuggerInitializeFailedException;

            if (initializationException != null)
            {
                string outputMessage = string.Join("\r\n", initializationException.OutputLines) + "\r\n";

                // NOTE: We can't write to the output window by sending an AD7 event because this may be called before the session create event
                LiveLogger.WriteLine(outputMessage);
            }

            _engineCallback.OnErrorImmediate(message);
        }
        /// <summary>
        /// Receives messages from debugger, parses them to extract the body, and dispatches them to <see cref="OutputMessage"/> listeners.
        /// </summary>
        private async void ReceiveAndDispatchMessagesWorker()
        {
            LiveLogger.WriteLine("Established connection.", typeof(DebuggerConnection));

            INetworkClient networkClient;

            lock (_networkClientLock) {
                networkClient = _networkClient;
            }
            if (networkClient == null)
            {
                return;
            }

            try {
                var stream = networkClient.GetStream();

                // Use a single read buffer and a single StringBuilder (periodically cleared) across loop iterations,
                // to avoid costly repeated allocations.
                var buffer = new byte[0x1000];
                var sb     = new StringBuilder();

                // Read and process incoming messages until disconnected.
                while (true)
                {
                    // Read the header of this message.
                    int contentLength = 0;
                    while (true)
                    {
                        // Read a single header field.
                        string field;
                        sb.Clear();
                        while (true)
                        {
                            int bytesRead = await stream.ReadAsync(buffer, 0, 1).ConfigureAwait(false);

                            if (bytesRead < 1)
                            {
                                // End of stream - we are disconnected from debuggee.
                                throw new EndOfStreamException();
                            }

                            var ch = (char)buffer[0];
                            if (ch == ':')
                            {
                                field = sb.ToString();
                                break;
                            }
                            sb.Append(ch);
                        }

                        // Blank line terminates the header.
                        if (string.IsNullOrEmpty(field))
                        {
                            break;
                        }

                        int.TryParse(field, out contentLength);
                        break;
                    }

                    if (contentLength == 0)
                    {
                        continue;
                    }

                    // Read the body of this message.

                    // If our preallocated buffer is large enough, use it - this should be true for vast majority of messages.
                    // If not, allocate a buffer that is large enough and use that, then throw it away - don't replace the original
                    // buffer with it, so that we don't hold onto a huge chunk of memory for the rest of the debugging session just
                    // because of a single long message.
                    var bodyBuffer = buffer.Length >= contentLength ? buffer : new byte[contentLength];

                    for (int i = 0; i < contentLength;)
                    {
                        i += await stream.ReadAsync(bodyBuffer, i, contentLength - i).ConfigureAwait(false);
                    }

                    string message = _encoding.GetString(bodyBuffer, 0, contentLength);
                    LiveLogger.WriteLine("Response: " + message, typeof(DebuggerConnection));

                    // Notify subscribers.
                    OutputMessage?.Invoke(this, new MessageEventArgs(message));
                }
            } catch (SocketException) {
            } catch (IOException) {
            } catch (ObjectDisposedException) {
            } catch (InvalidOperationException) {
            } catch (DecoderFallbackException ex) {
                LiveLogger.WriteLine(string.Format(CultureInfo.InvariantCulture, "Error decoding response body: {0}", ex), typeof(DebuggerConnection));
            } catch (JsonReaderException ex) {
                LiveLogger.WriteLine(string.Format(CultureInfo.InvariantCulture, "Error parsing JSON response: {0}", ex), typeof(DebuggerConnection));
            } catch (Exception ex) {
                LiveLogger.WriteLine(string.Format(CultureInfo.InvariantCulture, "Message processing failed: {0}", ex), typeof(DebuggerConnection));
                //throw;
            } finally {
                LiveLogger.WriteLine("Connection was closed.", typeof(DebuggerConnection));

                ConnectionClosed?.Invoke(this, EventArgs.Empty);
            }
        }
        // Connect to the remote debugging server. If any errors occur, display an error dialog, and keep
        // trying for as long as user clicks "Retry".
        private static NodeRemoteDebugProcess Connect(NodeRemoteDebugPort port, INetworkClientFactory networkClientFactory)
        {
            if (port.Uri.Fragment == "#ping=0")
            {
                return(new NodeRemoteDebugProcess(port, "node.exe", String.Empty, String.Empty));
            }

            NodeRemoteDebugProcess process = null;

            while (true)
            {
                Exception exception = null;
                try {
                    LiveLogger.WriteLine("NodeRemoteEnumDebugProcesses pinging remote host ...");
                    using (var client = networkClientFactory.CreateNetworkClient(port.Uri))
                        using (var stream = client.GetStream()) {
                            // https://nodejstools.codeplex.com/workitem/578

                            // Node.js (V8) debugger is fragile during attach, and it's easy to put it into a bad state where it refuses
                            // future connections altogether, or accepts them but send responses that it queued up for another client.
                            // To avoid this, our ping needs to look like a proper debug session to the debuggee. For this, we need to
                            // do the following steps in order:
                            //
                            // 1. Receive the debugger's greeting message.
                            // 2. Send the "disconnect" request.
                            // 3. Receive the "disconnect" response.
                            //
                            // Only then can the socket be closed safely without disrupting V8.

                            // Receive greeting.
                            var    buffer   = new byte[1024];
                            int    len      = stream.ReadAsync(buffer, 0, buffer.Length, new CancellationTokenSource(5000).Token).GetAwaiter().GetResult();
                            string response = Encoding.UTF8.GetString(buffer, 0, len);
                            LiveLogger.WriteLine("NodeRemoteEnumDebugProcesses debugger greeting: " + response);

                            // There's no error code, so we have to do the string comparison. Luckily, it is hardcoded into V8 and is not localized.
                            if (response == "Remote debugging session already active\r\n")
                            {
                                throw new DebuggerAlreadyAttachedException();
                            }

                            // Send "disconnect" request.
                            string request = @"{""command"":""disconnect"",""seq"":1,""type"":""request"",""arguments"":null}";
                            request = string.Format(CultureInfo.InvariantCulture, "Content-Length: {0}\r\n\r\n{1}", request.Length, request);
                            buffer  = Encoding.UTF8.GetBytes(request);
                            stream.WriteAsync(buffer, 0, buffer.Length, new CancellationTokenSource(5000).Token).GetAwaiter().GetResult();

                            // Receive "disconnect" response.
                            buffer   = new byte[1024];
                            len      = stream.ReadAsync(buffer, 0, buffer.Length, new CancellationTokenSource(5000).Token).GetAwaiter().GetResult();
                            response = Encoding.UTF8.GetString(buffer, 0, len);
                            LiveLogger.WriteLine("NodeRemoteEnumDebugProcesses debugger response: " + response);

                            // If we got to this point, the debuggee is behaving as expected, and we can report it as a valid Node.js process.
                            process = new NodeRemoteDebugProcess(port, "node.exe", String.Empty, String.Empty);
                            LiveLogger.WriteLine("NodeRemoteEnumDebugProcesses ping successful.");
                            break;
                        }
                } catch (OperationCanceledException) {
                    LiveLogger.WriteLine("NodeRemoteEnumDebugProcesses ping timed out.");
                } catch (DebuggerAlreadyAttachedException ex) {
                    LiveLogger.WriteLine("DebuggerAlreadyAttachedException connecting to remote debugger");
                    exception = ex;
                } catch (AggregateException ex) {
                    LiveLogger.WriteLine("AggregateException connecting to remote debugger");
                    exception = ex;
                } catch (IOException ex) {
                    LiveLogger.WriteLine("IOException connecting to remote debugger");
                    exception = ex;
                } catch (InvalidOperationException ex) {
                    LiveLogger.WriteLine("InvalidOperationException connecting to remote debugger");
                    exception = ex;
                } catch (SocketException ex) {
                    LiveLogger.WriteLine("SocketException connecting to remote debugger");
                    exception = ex;
                } catch (WebSocketException ex) {
                    LiveLogger.WriteLine("WebSocketException connecting to remote debugger");
                    exception = ex;
                } catch (PlatformNotSupportedException) {
                    LiveLogger.WriteLine("PlatformNotSupportedException connecting to remote debugger");
                    MessageBox.Show(
                        "Remote debugging of node.js Microsoft Azure applications is only supported on Windows 8 and above.",
                        null, MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return(null);
                }

                if (exception != null)
                {
                    while (exception.InnerException != null)
                    {
                        exception = exception.InnerException;
                    }
                }

                string errText = string.Format(CultureInfo.CurrentCulture,
                                               "Could not attach to Node.js process at {0}{1}\r\n\r\n",
                                               port.Uri,
                                               exception != null ? ":\r\n\r\n" + exception.Message : ".");
                if (!(exception is DebuggerAlreadyAttachedException))
                {
                    if (port.Uri.Scheme == "ws" || port.Uri.Scheme == "wss")
                    {
                        errText +=
                            "Make sure that the Azure web site is deployed in the Debug configuration, and web sockets " +
                            "are enabled for it in the Azure management portal.";
                    }
                    else
                    {
                        errText += string.Format(CultureInfo.CurrentCulture,
                                                 "Make sure that the process is running behind the remote debug proxy (RemoteDebug.js), " +
                                                 "and the debugger port (default {0}) is open on the target host.",
                                                 NodejsConstants.DefaultDebuggerPort);
                    }
                }

                DialogResult dlgRes = MessageBox.Show(errText, null, MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                if (dlgRes != DialogResult.Retry)
                {
                    break;
                }
            }

            return(process);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Receives messages from debugger, parses them to extract the body, and dispatches them to <see cref="OutputMessage"/> listeners.
        /// </summary>
        private async void ReceiveAndDispatchMessagesWorker()
        {
            LiveLogger.WriteLine("Established connection.", typeof(DebuggerConnection));

            INetworkClient networkClient;

            lock (_networkClientLock) {
                networkClient = _networkClient;
            }
            if (networkClient == null)
            {
                return;
            }

            try {
                var stream = networkClient.GetStream();

                // Use a single read buffer and a single StringBuilder (periodically cleared) across loop iterations,
                // to avoid costly repeated allocations.
                var buffer = new byte[0x1000];
                var sb     = new StringBuilder();

                // Read and process incoming messages until disconnected.
                while (true)
                {
                    // Read the header of this message.
                    int contentLength = 0;
                    while (true)
                    {
                        // Read a single header field.
                        string field;
                        sb.Clear();
                        while (true)
                        {
                            int bytesRead = await stream.ReadAsync(buffer, 0, 1).ConfigureAwait(false);

                            if (bytesRead < 1)
                            {
                                // End of stream - we are disconnected from debuggee.
                                throw new EndOfStreamException();
                            }

                            // All fields that we care about are ASCII, and for all the other fields we only need to recognize
                            // the trailing \r\n, so there's no need to do proper decoding here.
                            sb.Append((char)buffer[0]);

                            // "\r\n" terminates the field.
                            if (sb.Length >= 2 && sb[sb.Length - 2] == '\r' && sb[sb.Length - 1] == '\n')
                            {
                                field = sb.ToString(0, sb.Length - 2);
                                break;
                            }
                        }

                        // Blank line terminates the header.
                        if (string.IsNullOrEmpty(field))
                        {
                            break;
                        }

                        // Otherwise, it's an actual field. Parse it if it's something we care about.

                        // Content-Length
                        var match = _contentLengthFieldRegex.Match(field);
                        if (match.Success)
                        {
                            int.TryParse(match.Groups[1].Value, out contentLength);
                            continue;
                        }

                        // Embedding-Host, which contains the Node.js version number. Only try parsing that if we don't know the version yet -
                        // it normally comes in the very first packet, so this saves time trying to parse all the consequent ones.
                        if (NodeVersion == null)
                        {
                            match = _nodeVersionFieldRegex.Match(field);
                            if (match.Success)
                            {
                                Version nodeVersion;
                                Version.TryParse(match.Groups[1].Value, out nodeVersion);
                                _nodeVersion = nodeVersion;
                            }
                        }
                    }

                    if (contentLength == 0)
                    {
                        continue;
                    }

                    // Read the body of this message.

                    // If our preallocated buffer is large enough, use it - this should be true for vast majority of messages.
                    // If not, allocate a buffer that is large enough and use that, then throw it away - don't replace the original
                    // buffer with it, so that we don't hold onto a huge chunk of memory for the rest of the debugging session just
                    // because of a single long message.
                    var bodyBuffer = buffer.Length >= contentLength ? buffer : new byte[contentLength];

                    for (int i = 0; i < contentLength;)
                    {
                        i += await stream.ReadAsync(bodyBuffer, i, contentLength - i).ConfigureAwait(false);
                    }

                    string message = _encoding.GetString(bodyBuffer, 0, contentLength);
                    LiveLogger.WriteLine("Response: " + message, typeof(DebuggerConnection));

                    // Notify subscribers.
                    var outputMessage = OutputMessage;
                    if (outputMessage != null)
                    {
                        outputMessage(this, new MessageEventArgs(message));
                    }
                }
            } catch (SocketException) {
            } catch (IOException) {
            } catch (ObjectDisposedException) {
            } catch (DecoderFallbackException ex) {
                LiveLogger.WriteLine(string.Format("Error decoding response body: {0}", ex), typeof(DebuggerConnection));
            } catch (JsonReaderException ex) {
                LiveLogger.WriteLine(string.Format("Error parsing JSON response: {0}", ex), typeof(DebuggerConnection));
            } catch (Exception ex) {
                LiveLogger.WriteLine(string.Format("Message processing failed: {0}", ex), typeof(DebuggerConnection));
                throw;
            } finally {
                LiveLogger.WriteLine("Connection was closed.", typeof(DebuggerConnection));

                var connectionClosed = ConnectionClosed;
                if (connectionClosed != null)
                {
                    connectionClosed(this, EventArgs.Empty);
                }
            }
        }
Ejemplo n.º 11
0
 public AD7Engine()
 {
     LiveLogger.WriteLine("--------------------------------------------------------------------------------");
     LiveLogger.WriteLine("AD7Engine Created ({0})", GetHashCode());
 }
Ejemplo n.º 12
0
 public void Send(string cmd)
 {
     LiveLogger.WriteLine("<-" + cmd);
     _writer.WriteLine(cmd);
     _writer.Flush();
 }
Ejemplo n.º 13
0
        public void OnStdOutLine(string line)
        {
            if (line.Length == 0)
            {
                return;
            }
            if (!_initialized && !line.Contains("------ Running dev '"))
            {
                return;
            }

            if (!_initialized)
            {
                var index = line.LastIndexOf("------ Running dev '", StringComparison.Ordinal);
                if (index > 0)
                {
                    line = Environment.NewLine + line.Substring(index);
                    if (line.Contains("------ Compiling dev '"))
                    {
                        return;
                    }
                    _initialized = true;
                }
            }

            if (_lasCommand.HasValue)
            {
                if (_injectStrings.ContainsKey(_lasCommand.Value))
                {
                    line = _injectStrings[_lasCommand.Value] + Environment.NewLine + line;
                }


                _lasCommand = null;
            }

            OnOutput?.Invoke(line);

            DispatchGeneric(line);

            try
            {
                using (var ms = new MemoryStream())
                {
                    using (var sw = new StreamWriter(ms))
                    {
                        sw.WriteLine(line);
                        sw.Flush();
                        ms.Position = 0;

                        // parse input args, and open input file
                        var scanner = new TelnetScanner(ms);
                        scanner.ErrorPorcessed += PublishError;

                        var parser = new BrightScriptDebug.Compiler.Parser(scanner);

                        parser.BacktraceProcessed       += ParserOnBacktraceProcessed;
                        parser.VariablesProcessed       += ParserOnVariablesProcessed;
                        parser.DebugPorcessed           += ParserOnDebugPorcessed;
                        parser.AppCloseProcessed        += ParserOnAppCloseProcessed;
                        parser.AppOpenProcessed         += ParserOnAppOpenProcessed;
                        parser.CurrentFunctionProcessed += ParserOnCurrentFunctionProcessed;
                        parser.StepPorcessed            += ParserOnStepPorcessed;

                        try
                        {
                            parser.Parse();
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                        parser.BacktraceProcessed       -= ParserOnBacktraceProcessed;
                        parser.VariablesProcessed       -= ParserOnVariablesProcessed;
                        parser.DebugPorcessed           -= ParserOnDebugPorcessed;
                        parser.AppCloseProcessed        -= ParserOnAppCloseProcessed;
                        parser.AppOpenProcessed         -= ParserOnAppOpenProcessed;
                        parser.CurrentFunctionProcessed -= ParserOnCurrentFunctionProcessed;
                        parser.StepPorcessed            += ParserOnStepPorcessed;
                    }
                }
            }
            catch (Exception ex)
            {
                LiveLogger.WriteLine(ex.Message);
            }
        }
Ejemplo n.º 14
0
 private void PublishError(string error)
 {
     LiveLogger.WriteLine(error);
 }