Thursday, February 9, 2012

PHP debug process internals with xdebug


Xdebug debugger allows you to interactively walk through script execution and inspect the call traces and variable values in each appropriate scope.






Debugger client listens on 9000 port by default. Browser initiates regular HTTP request to 80 port indicating that debug session should be started (by parameter in request URI or cookie) . Debugger starts the debug session and connects to your debugger client. Then you will be able to send commands to debugger and control the execution flow.

DBG client uses plain text messages to deliver commands to server and server returns data in XML format. Just to understand how everything works - here is the hypothetical communication between server and debug client:


run -i transaction_id
<response command="run" 
    status="starting" 
    reason="ok" 
    transaction_id="transaction_id"/>
breakpoint_set -i 1 -t line -f test.pl -n 20 -- base64($x > 3)
<response command="breakpoint_set"
    transaction_id="TRANSACTION_ID" 
    state="STATE"id="BREAKPOINT_ID"/>
breakpoint_get -i TRANSACTION_ID -d BREAKPOINT_ID
<response command="breakpoint_get"
          transaction_id="TRANSACTION_ID">
    <breakpoint id="BREAKPOINT_ID"
                type="TYPE"
                state="STATE"
                filename="FILENAME"
                lineno="LINENO"
                function="FUNCTION"
                exception="EXCEPTION"
                hit_value="HIT_VALUE"
                hit_condition="HIT_CONDITION"
                hit_count="HIT_COUNT">
        $expression$$EXPRESSION$/expression>
    </breakpoint>
</response>
breakpoint_remove -i TRANSACTION_ID -d BREAKPOINT_ID
<response command="breakpoint_remove"
          transaction_id="TRANSACTION_ID"/>

XML was used due to its flexibility when it comes to passing unpredictable data structures. Why not use XML for both sides? That is because XML is easy to be generated but not so easy to be parsed. So to avoid dependency on XML parsing libraries on server it was decided to use plain text for commands from client.

If your IDE supports GDBp protocol you don't need to learn all these commands, IDE it will provide friendly graphical UI to control script execution flow, place breakpoints, view variables, etc...

Debug in your IDE

IDE configuration

Debugger configuration depends on your IDE so you should look into the appropriate documentation for your editor. Debug itself looks straight forward and widely described on the internet.
I use Eclipse PDT so can recommend the following article to read: http://devzone.zend.com/article/2930

Browser plug-in

There are also browser plugins that will help you to set the debugging cookies in order to initiate debugger session.

easy Xdebug for FireFox: https://addons.mozilla.org/en-US/firefox/addon/58688/
Don't forget to install  Web Developer extention that can help easy manage caches and cookies: https://addons.mozilla.org/en-US/firefox/addon/web-developer/
Xdebug Helper for Chrome:https://chrome.google.com/extensions/detail/eadndfjplgieldjbigjakmdgkmoaaaoc

Debug on remote server behind NAT/Firewall

As debug is based on TCP connections it means that there is no any difference between debug locally and between debug on remote host.

The only problem you can face with is when you need to debug on remove server that is not in your sub-network, you don't have a real IP address (NAT) or you're behind the firewall. In this case debugger will not be able to connect from remote computer to your client on development machine. 

Anyway if you have a SSH access to remote server - you can easy do a port forwarding via SSH
$ ssh -R 9000:localhost:9000 youruser@www.dev.remote-server.com

So remote debugger will connect to remote localhost:9000 port and it will be automatically forwarded to your local development machine localhost:9000
Credits to the following article for this solution: 
In some cases when your SSH session was closed by server - the SSH tunnelling daemon can still be running on remote server occupying the 9000 port. So trying to initiate new SSH forwarding will fail with error: "Warning: remote port forwarding failed for listen port 9000". Use:
# netstat -nap | grep 9000
to see the opened port and then
# kill 12345
where "12345" is the PID of the process
WARNING! Make sure you will not shut shown something really important %)
In worst case you should restart Apache in order to close debugger if it is still active
# /etc/init.d/httpd restart

Troubleshooting xdebug server/IDE connectivity

You can use nc to see if xdebug client actually initiates connection to IDE debug server.

Just stop IDE debug server and run the following on your machine:
# nc -l -p 9000

Then enable debug mode (set debug cookie) with browser plugin and refresh the page, to force xdebug to connect to debug server. NC should throw the message into CLI indicating that connection was initiated.

If NC doesn't throws anything - the problem on PHP server configuration side (see xdebug.ini or cookie).

Ignored breakpoints

In Eclipse go to debug configuration dialog, in the server area, click Configure, go to Path Mapping, click the path that's there and click edit, change to Path in file system and navigate to the correct file.
See following article for more information: