/// <summary> /// Send a command to XDebug. Returns a instance of the Response object or null /// upon failure. /// </summary> public XDebug.Response SendCommand(XDebug.Command c) { string transactionId = "xdc" + _cmdCounter.ToString(); string Message = ""; _cmdCounter++; Message = String.Format( "{0} -i {1}", c.CommandText, transactionId ); if (c.OptionsText.Length != 0) { Message += " " + c.OptionsText; } XDebugEventArgs e = new XDebugEventArgs(); e.Message = new XDebug.Response(); e.Message.RawMessage = Message; e.EventType = XDebugEventType.CommandSent; this.EventCallback(e); byte[] msg = System.Text.Encoding.ASCII.GetBytes(Message + "\0"); this._client.Send(msg); return(this.ReceiveMessage()); }
/// <summary> /// Deal with an incoming request. Also called when listening socket /// is terminated. /// </summary> private void OnConnectRequest(IAsyncResult ar) { try { Socket listener = (Socket)ar.AsyncState; _client = listener.EndAccept(ar); _listener.Close(); XDebugEventArgs e = new XDebugEventArgs(); e.EventType = XDebugEventType.DebuggerConnected; this.EventCallback(e); } catch { /* This can probably dealt with in a less hacky way: * * After closing the connection OnConnectRequest will be called. Then it dies * with a ObjectDisposedException. Trap the exception. Not really anything else * to do here. */ } }
/// <summary> /// Deal with the init-message xdebug sends us: /// - See if we seem to be compatible language wise /// - See if we're compatible protocol wise /// - Find the initial file and fire off a ConnectionInitialized "event" /// /// Returns true/false /// </summary> private bool handleInitMessage(XDebug.Response initMessage) { if (initMessage == null) { throw new Exception("Init message was empty."); } /* parse out the filename and check wether the version is * compatible with XdebugClient */ XmlElement d = initMessage.XmlMessage.DocumentElement; if (d.Attributes["protocol_version"] != null) { string remoteVersion = d.Attributes["protocol_version"].Value; if (remoteVersion != supportedProtocolVersion) { throw new Exception( String.Format( "Expected version '{0}' but got version '{1}' which is not supported.'", supportedProtocolVersion, remoteVersion ) ); } } if (d.Attributes["language"] != null) { string remoteLanguage = d.Attributes["language"].Value; if (remoteLanguage.ToLower() != supportedLanguage) { throw new Exception( String.Format( "Expected language '{0}' but got '{1}' which is not supported.", supportedLanguage, remoteLanguage ) ); } } if (d.Attributes["fileuri"] != null) { string absoluteFilename = this.getLocalFilename(d.Attributes["fileuri"].Value); XDebugEventArgs xea = new XDebugEventArgs(); xea.Filename = absoluteFilename; xea.EventType = XDebugEventType.ConnectionInitialized; if (this.EventCallback(xea)) { _State = XdebugClientState.Initialized; } else { return false; } } else { throw new Exception("Missing 'fileuri' attribute."); } return true; }
/// <summary> /// Send a command to XDebug. Returns a instance of the Response object or null /// upon failure. /// </summary> public XDebug.Response SendCommand(XDebug.Command c) { string transactionId = "xdc" + _cmdCounter.ToString(); string Message = ""; _cmdCounter++; Message = String.Format( "{0} -i {1}", c.CommandText, transactionId ); if (c.OptionsText.Length != 0) { Message += " " + c.OptionsText; } XDebugEventArgs e = new XDebugEventArgs(); e.Message = new XDebug.Response(); e.Message.RawMessage = Message; e.EventType = XDebugEventType.CommandSent; this.EventCallback(e); byte[] msg = System.Text.Encoding.ASCII.GetBytes(Message + "\0"); this._client.Send(msg); return this.ReceiveMessage(); }
/// <summary> /// Send a run, step_over or step_in command. /// </summary> public void Run(string command) { XDebug.Command c = new Command(command, ""); XDebug.Response resp = this.SendCommand(c); string status = resp.XmlMessage.DocumentElement.Attributes["status"].Value; string reason = resp.XmlMessage.DocumentElement.Attributes["reason"].Value; this.StackDepth = 0; XDebugEventArgs e; if (reason == "exception") { XmlNode ErrorNode = resp.XmlMessage.DocumentElement.FirstChild; this._State = XdebugClientState.Stopped; e = new XDebugEventArgs(); e.EventType = XDebugEventType.ErrorOccured; e.ErrorMessage = ErrorNode.InnerText; switch ( ErrorNode.Attributes["exception"].Value ) { case Client.FatalErrorExceptionName: e.ErrorType = XDebugErrorType.FatalError; break; case Client.NoticeExceptionName: e.ErrorType = XDebugErrorType.Warning; break; default: throw new Exception("Unknown exception type"); } this.EventCallback(e); return; } else { switch (status) { case "break": /* execution stopped: breakpoint, step_over/step_in result, etc. */ this._State = XdebugClientState.Break; List<StackEntry> CallStack = this.GetCallStack(0); e = new XDebugEventArgs(); e.CurrentLocation = CallStack[0].Location; e.EventType = XDebugEventType.BreakpointHit; this.EventCallback(e); break; case "stopped": case "stopping": /* Script's done. */ this.Disconnect(); e = new XDebugEventArgs(); e.EventType = XDebugEventType.ScriptFinished; this.EventCallback(e); break; default: throw new Exception("Unknown status: " + status); } } }
/// <summary> /// Deal with the init-message xdebug sends us: /// - See if we seem to be compatible language wise /// - See if we're compatible protocol wise /// - Find the initial file and fire off a ConnectionInitialized "event" /// /// Returns true/false /// </summary> private bool handleInitMessage(XDebug.Response initMessage) { if (initMessage == null) { throw new Exception("Init message was empty."); } /* parse out the filename and check wether the version is * compatible with XdebugClient */ XmlElement d = initMessage.XmlMessage.DocumentElement; if (d.Attributes["protocol_version"] != null) { string remoteVersion = d.Attributes["protocol_version"].Value; if (remoteVersion != supportedProtocolVersion) { throw new Exception( String.Format( "Expected version '{0}' but got version '{1}' which is not supported.'", supportedProtocolVersion, remoteVersion ) ); } } if (d.Attributes["language"] != null) { string remoteLanguage = d.Attributes["language"].Value; if (remoteLanguage.ToLower() != supportedLanguage) { throw new Exception( String.Format( "Expected language '{0}' but got '{1}' which is not supported.", supportedLanguage, remoteLanguage ) ); } } if (d.Attributes["fileuri"] != null) { string absoluteFilename = this.getLocalFilename(d.Attributes["fileuri"].Value); XDebugEventArgs xea = new XDebugEventArgs(); xea.Filename = absoluteFilename; xea.EventType = XDebugEventType.ConnectionInitialized; if (this.EventCallback(xea)) { _State = XdebugClientState.Initialized; } else { return(false); } } else { throw new Exception("Missing 'fileuri' attribute."); } return(true); }
/// <summary> /// Parse a response by XDebug. Xdebug sends messages in 2 parts terminated /// by \0. The first part of the message is the length of the second part /// of the message. /// </summary> private XDebug.Response ReceiveMessage() { List <byte> MessageLengthList = new List <byte>(); Byte[] c = new Byte[1]; /* Determine the length of the message byte-by-byte */ do { int bc = _client.Receive(c, 1, SocketFlags.None); if (bc == 0) { // peer close, abort throw new Exception("Socket read error"); } if (c[0] != (byte)0x00) { MessageLengthList.Add(c[0]); } } while (c[0] != (byte)0x00); /* Turn the MessageLengthList into a number by merging it * into a byte array, casting it to a string and then parsing * the integer from it. I wonder if there's a better way to do this.*/ byte[] lengthBytes = MessageLengthList.ToArray(); string lengthStr = System.Text.Encoding.ASCII.GetString(lengthBytes); int length = Convert.ToInt32(lengthStr); /* The message length doesn't include the trailing NULL byte. Add it here */ length++; byte[] messageBytes = new byte[length]; int bytesRead = 0, totalBytesRead = 0, currentByte = 0; do { byte[] xmlMessageBytes = new byte[length]; bytesRead = _client.Receive(xmlMessageBytes, length, SocketFlags.None); if (bytesRead == 0 || bytesRead < 0) { throw new Exception("Socket read error"); } Buffer.BlockCopy(xmlMessageBytes, 0, messageBytes, totalBytesRead, bytesRead); totalBytesRead += bytesRead; //for (int i = 0; i < bytesRead; i++) //{ // messageBytes[currentByte++] = xmlMessageBytes[i]; //} } while (totalBytesRead < length); string xmlMessage = System.Text.Encoding.ASCII.GetString(messageBytes); XDebug.Response resp = XDebug.Response.Parse(xmlMessage); if (resp != null) { if (resp.MessageType == XDebug.Response.MSG_ERROR) { throw new Exception(resp.Attributes["ErrorMessage"]); } XDebugEventArgs xev = new XDebugEventArgs(); xev.Message = resp; xev.EventType = XDebugEventType.MessageReceived; this.EventCallback(xev); } return(resp); }
private void OnXdebugCommandSent(XDebugEventArgs e) { WriteDebugLine(" -> SENT: " + e.Message.RawMessage); }
/// <summary> /// Send a run, step_over or step_in command. /// </summary> public void Run(string command) { XDebug.Command c = new Command(command, ""); XDebug.Response resp = this.SendCommand(c); string status = resp.XmlMessage.DocumentElement.Attributes["status"].Value; string reason = resp.XmlMessage.DocumentElement.Attributes["reason"].Value; this.StackDepth = 0; XDebugEventArgs e; if (reason == "exception") { XmlNode ErrorNode = resp.XmlMessage.DocumentElement.FirstChild; this._State = XdebugClientState.Stopped; e = new XDebugEventArgs(); e.EventType = XDebugEventType.ErrorOccured; e.ErrorMessage = ErrorNode.InnerText; switch (ErrorNode.Attributes["exception"].Value) { case Client.FatalErrorExceptionName: e.ErrorType = XDebugErrorType.FatalError; break; case Client.NoticeExceptionName: e.ErrorType = XDebugErrorType.Warning; break; default: throw new Exception("Unknown exception type"); } this.EventCallback(e); return; } else { switch (status) { case "break": /* execution stopped: breakpoint, step_over/step_in result, etc. */ this._State = XdebugClientState.Break; List <StackEntry> CallStack = this.GetCallStack(0); e = new XDebugEventArgs(); e.CurrentLocation = CallStack[0].Location; e.EventType = XDebugEventType.BreakpointHit; this.EventCallback(e); break; case "stopped": case "stopping": /* Script's done. */ this.Disconnect(); e = new XDebugEventArgs(); e.EventType = XDebugEventType.ScriptFinished; this.EventCallback(e); break; default: throw new Exception("Unknown status: " + status); } } }
/* The methods in this region aren't neccessarily real events as * C# defines them. Some of them (those called from _client_EventCallback) * as just regular methods. * * The callback XDebugEventCallback is used as a centralized * place to further instruct the GUI what to do. We use only one event as * the client uses asynchronized methods (threading). By using 1 callback the * number of threading-related reinvoking (see MainForm.ReinvokeInOwnThread) */ /// <summary> /// The XDebugEventCallback is called whenever something changes within the Xdebug.Client /// implementation. It serves mostly as a dispatcher. /// </summary> private bool XDebugEventCallback(XDebugEventArgs e) { if (!this.ReinvokeInOwnThread(new XdebugClientCallback(XDebugEventCallback), new object[] { e })) { switch (e.EventType) { case XDebugEventType.DebuggerConnected: this.OnXdebuggerConnected(e); break; case XDebugEventType.ConnectionInitialized: return this.OnXdebugConnectionInitialized(e); case XDebugEventType.MessageReceived: this.OnXdebugMessageReceived(e); break; case XDebugEventType.CommandSent: this.OnXdebugCommandSent(e); break; case XDebugEventType.BreakpointHit: this.OnXdebugBreakpointHit(e); break; case XDebugEventType.ErrorOccured: this.OnXdebugErrorOccurred(e); break; case XDebugEventType.ScriptFinished: this.OnXdebugScriptFinished(e); break; default: WriteDebugLine("(!) Unknown event happened."); break; } } return false; }
private void OnXdebugScriptFinished(XDebugEventArgs e) { _statusFrm.WriteStatusLine("(!) Script finished."); this.StopDebuggingSession(); if (xdc.Properties.Settings.Default.auto_restart) { _statusFrm.WriteStatusLine("(-) Automatically restarting debugging."); try { _client.listenForConnection(); startListeningToolStripMenuItem.Enabled = false; stopDebuggingToolStripMenuItem.Enabled = true; } catch (Exception ex) { MessageBox.Show( "Unable to re-create listening socket: " + ex.Message, "Cannot open socket", MessageBoxButtons.OK ); } } }
private void OnXdebugMessageReceived(XDebugEventArgs e) { // Nothing to do for now }
private void OnXdebuggerConnected(XDebugEventArgs e) { _statusFrm.WriteStatusLine("(-) Debugger connected."); try { if (_client.Initialize()) { _statusFrm.WriteStatusLine("(-) XDebugClient initialized."); if (!xdc.Properties.Settings.Default.break_on_script_start) this.SendContinuationCommand("run"); else this.SendContinuationCommand("step_into"); } else { return; } } catch (Exception ex) { _statusFrm.WriteStatusLine("(-) Cannot initialize XDebugClient: " + ex.Message); MessageBox.Show( "XDebugClient was unable to initialize. Debugging session terminated.\r\n\r\n" + ex.Message, "System error", MessageBoxButtons.OK ); this.StopDebuggingSession(); } }
private void OnXdebugErrorOccurred(XDebugEventArgs e) { if (e.ErrorType == XDebugErrorType.Warning) { WriteDebugLine("(!) PHP Notice: " + e.ErrorMessage); this._client.Run(); } else { WriteDebugLine("(!) PHP Fatal error: " + e.ErrorMessage); MessageBox.Show( "A Fatal error occurred:\r\n\r\n" + e.ErrorMessage + "\r\n\r\nYour script has been terminated.", "Fatal Error", MessageBoxButtons.OK ); this.StopDebuggingSession(); } }
private bool OnXdebugConnectionInitialized(XDebugEventArgs e) { if (this.LoadFile(e.Filename)) { this.ToggleMenuItems(true); return true; } return false; }
/// <summary> /// Parse a response by XDebug. Xdebug sends messages in 2 parts terminated /// by \0. The first part of the message is the length of the second part /// of the message. /// </summary> private XDebug.Response ReceiveMessage() { List<byte> MessageLengthList = new List<byte>(); Byte[] c = new Byte[1]; /* Determine the length of the message byte-by-byte */ do { int bc = _client.Receive(c, 1, SocketFlags.None); if (bc == 0) { // peer close, abort throw new Exception("Socket read error"); } if (c[0] != (byte)0x00) { MessageLengthList.Add(c[0]); } } while (c[0] != (byte)0x00); /* Turn the MessageLengthList into a number by merging it * into a byte array, casting it to a string and then parsing * the integer from it. I wonder if there's a better way to do this.*/ byte[] lengthBytes = MessageLengthList.ToArray(); string lengthStr = System.Text.Encoding.ASCII.GetString(lengthBytes); int length = Convert.ToInt32(lengthStr); /* The message length doesn't include the trailing NULL byte. Add it here */ length++; byte[] messageBytes = new byte[length]; int bytesRead = 0, totalBytesRead = 0, currentByte = 0; do { byte[] xmlMessageBytes = new byte[length]; bytesRead = _client.Receive(xmlMessageBytes, length, SocketFlags.None); if (bytesRead == 0 || bytesRead < 0) throw new Exception("Socket read error"); Buffer.BlockCopy(xmlMessageBytes, 0, messageBytes, totalBytesRead, bytesRead); totalBytesRead += bytesRead; //for (int i = 0; i < bytesRead; i++) //{ // messageBytes[currentByte++] = xmlMessageBytes[i]; //} } while (totalBytesRead < length); string xmlMessage = System.Text.Encoding.ASCII.GetString(messageBytes); XDebug.Response resp = XDebug.Response.Parse(xmlMessage); if (resp != null) { if (resp.MessageType == XDebug.Response.MSG_ERROR) { throw new Exception(resp.Attributes["ErrorMessage"]); } XDebugEventArgs xev = new XDebugEventArgs(); xev.Message = resp; xev.EventType = XDebugEventType.MessageReceived; this.EventCallback(xev); } return resp; }
private void OnXdebugBreakpointHit(XDebugEventArgs e) { this.PrepareFileForAccess(e.CurrentLocation.filename); this.SetActiveFileAndLine(e.CurrentLocation); List<StackEntry> callstack = _client.GetCallStack(-1); _callstackFrm.setCallstack(callstack); // local and global context List<Property> ctx = _client.GetContext("0"); _localContextFrm.LoadPropertyList(ctx); ctx = _client.GetContext("1"); _globalContextFrm.LoadPropertyList(ctx); }