/// <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; } }
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(); } }
/// <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); }
/// <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); } }
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); }
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); }
/// <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); } } }
public AD7Engine() { LiveLogger.WriteLine("--------------------------------------------------------------------------------"); LiveLogger.WriteLine("AD7Engine Created ({0})", GetHashCode()); }
public void Send(string cmd) { LiveLogger.WriteLine("<-" + cmd); _writer.WriteLine(cmd); _writer.Flush(); }
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); } }
private void PublishError(string error) { LiveLogger.WriteLine(error); }