Update: added mod_rewrite to apache config.
While working on Baguette AMF, the beta testers and myself have run into the dreaded ‘segmentation fault (11)’ error. By default, you won’t get any information in your logs about what happened and you will be powerless to fix the problem. The aim of this post is to explain to you how to be able to use GDB, the GNU (command line) debugger to get a stack trace that will help you find where your code is failing. Once you’re there, you can also do what you can usually do with a debugger, such as step through the code, get variable values etc.
So, there are various ways to do this, but what I’ve found works for me is rebuilding debug versions of Apache, PHP, and my extension.
Before we Start
- Please note that this is not for the faint of heart, that it will require quite some time and patience, and that this tutorial assumes that you know quite a bit about programming, compiling etc. If you have trouble, please comment and I will try to add any missing info.
- You’ll need root access to your server and the source code for Apache, PHP and the extension you want to debug.
- You will need quite a few libraries installed to be able to build Apache and PHP. If you’re on a Mac like myself, I’ve found the simplest is to use MacPorts to install Apache and PHP, and it will install all the dependencies for you.
There are quite a few tutorials out there on the question, but here is the command line anyway. Obviously, install Macports first.
sudo port install php5 +apache2 +mysql5 +pear
Make sure you have a working Apache+PHP5. If you do, it means all the libraries are installed and you can get started for real.
- You’ll also need developer tools such as the GCC compiler. I’ve found the simplest way to get them is to install XCode.
Building Apache for Debugging
First, download and unzip the Apache Source Code. Note that I’ve pointed you to version 2.2. PHP 5.2 and earlier won’t build with 2.4 so it’s important to get the right version.
open a terminal and go to the source code folder. Then:
sudo ./configure CFLAGS=-g --prefix=/usr/local/apache2 --enable_rewrite
./configure is a script that prepares the way so that the ‘make’ script afterwards can work on your system. I’ve added three options that need explaining:
- CFLAGS=-g tells the compiler to add debug symbols, as described here.
- –prefix=/usr/local/apache2 tells the configure script where apache must look for its configuration file. When you install your new apache, its files will be written to /usr/local/apache2. If you don’t set this, it will read its configuration from /etc/local/apache2.
- –enable_rewrite=shared This adds mod_rewrite as a shared module. Not strictly necessary, but if you want to use your Apache with any modern web package then you’ll probably need mod_rewrite.
Then, if your configuration went well:
sudo make clean all install
This executes the ‘clean’, ‘all’ and ‘install’ commands in the makefile.
- Clean gets rid of any intermediate compiled files. It’s not necessary unless you are having trouble building. But I find it’s good practice to include it just in case. Otherwise it’s happened that I see some meaningless error messages that could be solved with a clean.
- all is the actual compilation.
- install does the actual installation, ie writing the various files to your system, mostly in /usr/local/apache2.
If all went well, you can then run this to start your new Apache. Make sure the port you are using (usually 80) is not used before.
sudo /usr/local/apache2/bin/apachectl start
and open your browser, point it to ‘localhost’, and normally you should see ‘It Works’ in your browser.
It works? Good! Onto PHP then.
Building PHP for Debugging
To build PHP I recommend using PHPFarm. It will allow you to switch easily from one PHP version to another. Please note that sometimes the PHP version you want doesn’t build. The simplest is to try with another version. There’s some good extra info on how to use PHPFarm here. PHPFarm messes up the paths slightly for the CLI version of PHP on Macs, but that shouldn’t stop you from using it.
So first build the CLI version of PHP as described in the links above. Then you can move onto to a debug version built for Apache.
My configuration options for building PHP 5.3 are the following:
configoptions="\ --with-apxs2=/usr/local/apache2/bin/apxs \ --prefix=/usr/local/apache2/php \ --without-iconv\ "
The first 2 tell the script to build for apache and where to put the files. The bit about iconv is because I had trouble building with iconv.
If all went well, you can configure Apache to use PHP. Add the following to your Apache config, and restart it.
LoadModule php5_module modules/libphp5.so AddHandler php5-script .php AddType application/x-httpd-php .php #just add the index.php bit, as the DirectoryIndex bit is already there DirectoryIndex index.html index.php
Now test that you can run some PHP code. Good? On to the PHP extension…
Building your extension
If you got to here, building your extension shouldn’t be too much of an issue. Here’s a typical script:
/php-5.3.2/bin/phpize ./configure --with-php-config=/php-5.3.2/bin/php-config make clean all
Then make sure in your php.ini that your extension is loaded, check that it actually loads with phpinfo, and try to reproduce that segmentation fault!
Got it? Good. Now onto Debugging…
Getting a stack trace with GDB
In our case we want to run apache+PHP+ our extension, attach GDB to the process that will crash, provoke the crash, and gather whatever info we need to fix the issue.
The thing is, apache has multiple processes. So it’s difficult to know what process is going to crash. The simplest thing to do is to make sure it only runs one child process. So, add this to your apache config:
Then you can just run ps or look in the Activity Monitor to get the process number.
Once you can reliably predict which process is going to crash, run the following line(replace 1234 with the relevant process id):
sudo gdb /usr/local/apache2/bin/httpd 1234
This runs gdb on the apache executable, and tells it to attach to the process 1234.
Now make Apache crash!
in gdb type ‘bt’ and you should get a stack trace telling you where your extension’s code failed.
Note: if you don’t see your extension’s code mentioned in the trace, try ‘continue’ then ‘bt’ again.
There you are. Hope that helps, and good luck with debugging!!
Some links that I’ve found helpful: