Your typical Xdebug primer for OSX

Simon Hobbs's picture

I will be presenting Xdebug at the Drupal Melbourne Meetup next month. For those who are interested, here is a primer about the various tools I use, and there should be enough information for most people to set these tools up for themselves. I am focussing on my personal development environment, including MAMP (Free version) and Netbeans IDE. The benefit of MAMP is that it now ships with Xdebug. The main points I cover in this article are:

  • MAMP configuration
  • Editing php.ini to enable Xdebug
  • Pretty error messages with Xdebug
  • In-line debugging in Netbeans
  • Creating a cachegrind profile
  • Visualising the cachegrind profile with Graphviz
  • Creating a trace file with Xdebug

MAMP

I use the free version of MAMP. The professional MAMP essentially adds a configuration layer which I don’t need, and I am happy with urls like:
http://localhost:8888/dev/awesome.

Of the MAMP preferences, I turn off APC/XCache/eAccelerator (as I understand that they will interfere with Xdebug). I also change my document root to ~/Sites.

To avoid various WTF moments, I add a few symlinks in /usr/bin so that I’m generally using the MAMP binaries. This means, for example, that Drush will be using the same PHP as Apache. Using /usr/bin is probably not best practice, I believe when I upgrade to OSX Lion that my changes there will be nuked, but that’s OK by me.

MAMP’s php.ini

Now I look for the php.ini file. I have set MAMP to use PHP 5.3 so I want to find the right php.ini file. MAMP will have one for PHP 5.2 and one for PHP 5.3. Since I’ve symlinked /usr/bin/php to the MAMP binary I want to use, I have no problem finding the right .ini file which I will then edit in my favourite text editor.

# Grep the PHP config for the location of php.ini.
php -i | grep php.ini

While I’m here, there are a couple of things I want to change for development, you’ll have to search for each one:

memory_limit = 128M
display_errors = On
display_startup_errors = On

Enable Xdebug

At the bottom of php.ini file there should be the path to Xdebug (commented out). For me it looked like this:

[xdebug]
; zend_extension="/Applications/MAMP/bin/php5.3/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"

You only need to remove the semi-colon, and restart Apache, to enable Xdebug. Straight out of the box, PHP will start printing pretty error messages in your browser, including a stack trace showing which functions were called in the lead up to any error.

My full XDebug config is as follows, separated for each feature of Xdebug we want to utilize. You must restart Apache after making changes here.

[xdebug]
; Turn on Xdebug which will give pretty error messages out of the box.
zend_extension="/Applications/MAMP/bin/php5.3/lib/php/extensions/no-debug-non-zts-20090626/xdebug.so"

; This is what we need for inline-debugging with our IDE.
xdebug.remote_enable = On
xdebug.remote_handler = dbgp
xdebug.remote_host = localhost
xdebug.remote_port = 9000

; Profiling (cachegrind.out)
; xdebug.profiler_enable = On
; Instead we will trigger profiling with ?XDEBUG_PROFILE on the URL.
xdebug.profiler_enable_trigger = On
xdebug.profiler_output_dir = "/tmp/"

; Tracing (.xt)
; Set this to On and restart MAMP when you want to use it.
xdebug.auto_trace = Off
; We'd like to do tracing with ?XDEBUG_TRACE on the URL.
; But this only works in Xdebug 2.2 and MAMP ships with 2.1.
; xdebug.trace_enable_trigger = 1
xdebug.trace_output_dir = "/tmp/"
; Will add (not too much) info about what parameters were passed to each function.
xdebug.collect_params = 3

And that concludes the set-up of Xdebug. On to what we can do in Netbeans.

Netbeans

When I want to debug Drupal (or any PHP) application, I use an IDE that knows how to listen for Xdebug chatter. In this way I’ll be able to use the IDE to:

  • View the progress of code execution
  • Pause and resume the code execution
  • Examine the state of variables

I know that Komodo and Eclipse (and many more IDEs) can do this. I don’t have any opinion about these tools because I don’t use an IDE, except for when I want to run an inline debugger. Netbeans is easy enough. I install the PHP package from the Netbeans downloads page.

In the Netbeans preferences, in the PHP tab, there are some options for debugging. I have changed the Session ID to “nb” - this value will be used in a URL later and “nb” is easier for me to remember.

You’ll have created a new project in Netbeans and you can access the Project Properties through the File menu. Take note of the Run Configuration tab. Click on Advanced Configuration and check the box “Do not Open Web Browser”. This prevents Netbeans popping open a useless tab every-time we run a debugging session. That should be enough configuration for our needs.

Debugging Session

Assuming you have restarted Apache and Xdebug is configured nicely, you can start a debugging session in Netbeans by going to Debug->Debug Project. You might see a button on the toolbar that does the same thing. Once you’ve done that, at the bottom of the Netbeans window will be the message “Waiting for Connection (nb)”.

In your browser, visit the Drupal site and add the GET parameter XDEBUG_SESSION_START=nb. For example, we wish to debug the /user page then the url might be:
http://localhost:8888/drupalsite/user?XDEBUG_SESSION_START=nb

If everything is working, nothing will be happening in the browser - it looks like the white screen of death. But back in Netbeans you should see an indication that execution has stopped on the first line of index.php:

The instinct is to follow Drupal through every line of execution but that will take you a few days, I promise. There are some buttons in the toolbar that will make your life easier. Hover over them to see the tooltip and check the descriptions below:

Continue
Run to the next break-point.
Step Over
On the next line in this script/function. (Code execution will go into other functions but will not break there).
Step Into
Break on the next line, even if it’s inside a function (this will take a long time).
Step Out
You might be inside a function, but don’t break again until you’ve returned from it.
Run to Curser
Click on a line of code and the break point will be there.

You can set “break points” in the code by clicking on the line number to the left of the code, which will replace the line number with a red square. By clicking the green “Continue” arrow button, the code will run to this break point.

Analysing variables

The strength of inline debugging is examining variables. At any given break point, you can see which variables are in scope and what their values are. This is extremely useful if you are debugging a complex piece of iterative code, such as drupal_render().

If you want to get fancy, you can set a “Watch”. Effectively you can say “I want to break the execution when $stored_phase = 5”.

Profiling

That’s enough about Netbeans, let’s move on to some other tricks you can do with Xdebug.

To profile your application you will create something called a cachegrind file. With your php.ini configuration in place, you generate a cachegrind file, say for the /user page, by adding the GET parameter XDEBUG_PROFILE to the URL:
http://localhost:8888/mysite/user/?XDEBUG_PROFILE

The file it creates in the xdebug.profiler_output_dir (eg. cachegrind.out.86550) is not very human readable. A popular program used to read these files is kcachegrind, but that is not available easily on OSX. In our case, we can install a PHP web application called Webgrind on your localhost. This is really ideal for finding bottlenecks, and in the screenshot below you can see, at the top of the list, those functions which comprise the majority of the execution time.

The cachegrind file can also be used to visualise the execution of the Drupal page. You need a couple of tools to do this. We want to create a “Graphviz” graphic of the cachegrind file and we do this in two steps.

Firstly we have to convert it to a format called .dot. To do this I use the python toolset xdebugtoolkit. You can put this anywhere, in your home directory, or some other suitable place, by running the svn checkout command. To convert the cachegrind file you’ll do something like this with the cg2dot.py script:

Now you can download the application Graphviz and use this application to open the .dot file you have just created. This creates an interesting graph of the execution flow of Drupal as well as information that reveals bottlenecks in the execution.

Download the full-sized image

Tracing

Finally, if you are feeling adventurous you could try reading a trace of Drupal execution. A trace file is a file that Xdebug can create, it shows the functions that are called, and is (more or less) human-readable. To turn this on, you need to edit your php.ini file again and set xdebug.auto_trace = On. You can leave this on but it will create large files every-time a page is displayed. XDebug 2.2 has an option to turn this on via the URL, but MAMP currently ships with 2.1. Never mind.

After you have turned this on and refreshed a page, look in the directory that you set with xdebug.trace_output_dir and open the .xt file that you find there. The last one I personally created was called “trace.2043925204.xt”. Open this file in a decent text editor, and I recommend setting the syntax highlighting to PHP for basic colour highlighting.

What you will see is: a sequential list of function calls, which functions have been called from inside other functions, and some information about the parameters which were passed in.

Summary

A lot of people don’t brave Xdebug for the small but sometimes painful step of installing it. Having it available with MAMP is a big win. I hope you get some ideas from this article about how you can start using Xdebug in your local development.