Installing XHProf for MAMP and PHP 5.2 on Mac OS 10.6 (Snow Leopard)

sudo pecl install xhprof #fail

About Lion (10.7) Compatibility: The vast majority of this guide works as-is with MAMP 2.0 under OS X 10.7. The most significant changes are the file paths, which can be found by running the "locate" command as detailed below. Look for the notes below on where the setup does differ.

Exciting discussions have been bubbling up in the Drupal community about using XHProf to analyze the performance of PHP code. Developed by Facebook and released as open source, XHProf is a PHP extension that can track the progress of any PHP application (Drupal included) and generate detailed analytics that help developers isolate performance hot spots.

Unfortunately, if you're using a Mac for PHP development and rely on the MAMP package to manage your sites, installing PHP extensions can be a real pain. A "stab yourself in the eye" kind of pain. In this article, I'll walk you through the process of installing and building XHProf, while avoiding some of the common pitfalls that occur when compiling extensions for MAMP.

MAMP Component Source

The first step to adding an extension to MAMP is downloading the source code for the various components that come with MAMP, and ensuring that you grab the versions that match the MAMP installation you're already using. To find out what version of MAMP you're currently running, launch the appplication and then click 'About MAMP' or 'About MAMP PRO' in the menu bar. The absolute latest version at the time of this writing is 1.9.6.1, but I'm running MAMP 1.9.

To grab the actual downloadable source, visit the SourceForge page for MAMP, and locate the component source that matches your version. To do that click on 'Files', then 'mamp', then to click the link for your version. On the resulting page, you should see a link to a component source download. In my case it was titled MAMP_components_1.9.dmg. Let's go ahead and download that bad boy! And… wait… and wait some more. It's about 120MB, and SourceForge is clocking in at 85KB/sec for me. How about for you?

ZZZZzzzzzzzzzzz…

"Anything we can do while we wait?"

Actually there is! Another reason that installing PHP extensions for MAMP can be so challenging is that Mac OS already comes with PHP installed, so sometimes the wires get crossed between the different PHP binary files. For this reason, I also like to ensure that by default I am using my preferred MAMP version of the PHP binaries.

To do this, pop open a Terminal window and use the which command to see where these binaries are found. If you're like me, you probably see something like this:

  
$ which php
/usr/bin/php

$ which php-config
/usr/bin/php-config

$ which phpize
/usr/bin/phpize

$ which pecl
/usr/bin/pecl
  

These files are the PHP binaries that come already installed on Mac OS. What I'd like to do is have it look to the MAMP versions of these binaries, so that we can be sure XHProf gets installed for the right version of PHP.

Well, where do the MAMP versions of these binaries reside? Good question. I always forget, so instead I just use locate to find where the files are. pecl is a file that isn't as frequent as php, so I'll search for that.

  
$ locate bin/pecl
/Applications/MAMP/bin/php5.2/bin/pecl
/Applications/MAMP/bin/php5.3/bin/pecl
/usr/bin/pecl
  

Since I do most of my work using PHP 5.2, I need to make sure that the binaries inside of Applications/MAMP/bin/php5.2/bin are used. The way that I'll do this is by symlinking all the binaries in there. I don't want to actually overwrite the files that are in /usr/bin, as Mac OS will happily overwrite them later, forcing me to do this all over again. Instead, I'll symlink them into /usr/local/bin.

  
# First, find out what binaries I need to symlink.
$ ls /Applications/MAMP/bin/php5.2/bin
pear		pecl		php-config
peardev		php		phpize

# Then, we'll create symlinks for each of these into /usr/local/bin. We
# could use sed to make a fancy script to symlink each binary in this
# directory, but for clarity, let's just do it all by hand:
sudo ln -s /Applications/MAMP/bin/php5.2/bin/pear /usr/local/bin/pear
sudo ln -s /Applications/MAMP/bin/php5.2/bin/pecl /usr/local/bin/pecl
sudo ln -s /Applications/MAMP/bin/php5.2/bin/php-config /usr/local/bin/php-config
sudo ln -s /Applications/MAMP/bin/php5.2/bin/peardev /usr/local/bin/peardev
sudo ln -s /Applications/MAMP/bin/php5.2/bin/php  /usr/local/bin/php
sudo ln -s /Applications/MAMP/bin/php5.2/bin/phpize  /usr/local/bin/phpize

# While we're here, let's make sure all these binaries are executable.
$ sudo chmod a+x /Applications/MAMP/bin/php5.2/bin/*
  

Now, just to be sure it worked:

  
$ ls -la /usr/local/bin/php
lrwxr-xr-x  1 root  staff  37 Jul 28  2010 /usr/local/bin/php -> /Applications/MAMP/bin/php5.2/bin/php
  

Woohoo! That "->" you see there indicates that /usr/local/bin/php is a symlink to our version of PHP. The true test is to run the which command again and see what output we get:

  
$ which php
/usr/bin/php
  

Aww, shucks. It didn't work for me! It turns out the reason for my woes is that /usr/bin comes before /usr/local bin in my PATH variable:

  
$ echo $PATH
/opt/local/bin:/opt/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/X11/bin:/Users/jsansbury/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin:/usr/libexec
  

To fix this, I'll pop open my .bash_profile file inside my user's home directory and be sure that /usr/local/bin is before /usr/bin. Keep in mind, you'll probably want to make this permanent by editing your .bash_profile as well, but for the sake of brevity, let's just change this variable for this session.

  
# You'll probably want to change 'jsansbury' to your username. ;)
$ PATH=/Users/jsansbury/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
$ export PATH
$ echo $PATH
/Users/jsansbury/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin
$ which php
/usr/local/bin/php

# To be doubly sure, let's run php -i and see what version of PHP is
# executed.
$ php -i | grep "PHP Version"
PHP Version => 5.2.13
  

Yay! which php now shows /usr/local/bin/php as my php binary, and php -i confirms that we are using PHP 5.2. Phew, that was kind of a mess, huh? Thankfully things only get worse from here. Errr... Scratch that. Reverse it.

"Hey my component source download is done!"

Great! So, now that we have the component source for our version of MAMP downloaded, let's pop it open and figure out what needs to be done. We'll cd into the directory and move some files into our MAMP installation.

  
mkdir /Applications/MAMP/bin/php5.2/include
# cd into our mounted DMG.
cd /Volumes/MAMP_components_1.9/MAMP_src

# Extract the PHP source tarball into 
# /Applications/MAMP/bin/php5.2/include/. The specific filenames will
# be different if you're using newer or older versions of MAMP.
tar -C /Applications/MAMP/bin/php5.2/include/ -xvzf php-5.2.13.tar.gz
mv /Applications/MAMP/bin/php5.2/include/php-5.2.13/ /Applications/MAMP/bin/php5.2/include/php
  

Now that we've got the PHP source, we need to configure it for our machine. This is a pretty simple step:

  
 cd /Applications/MAMP/bin/php5.2/include/php
 ./configure
  

Voilà!

"Can we install XHProf yet?"

Ok, ok! Let's install XHProf. But, I'm warning you, it's not as simple as sudo pecl install xhprof. This is due mostly to some issues with how PECL expects the package tree to be formed.

To get around this, we'll first need to download XHProf directly. To do this, head over to the PECL XHProf project page and see what the latest version of XHProf is. For me, it shows XHProf 0.9.2.

Now that we know the version of XHProf, we can go ahead and download the source. Normally I'd download this into /tmp, but in the case of XHProf, we want to keep the source handy, as it includes web scripts used to display the metrics. For this reason, I'm instead going to download the XHProf source into my MAMP web root. For me, I have this at ~/Sites, but you may have it some other place, like /Applications/MAMP/htdocs. Regardless of where your web root is, just cd into that directory.

  
# Change directories into your MAMP web root. For you, this may be
# /Applications/MAMP/htdocs
cd ~/Sites
# Running with the d flag let's the pecl binary know to just download
# the tarball.
sudo pecl d xhprof-0.9.2
# Now, untar the tarball we got
tar -xzf xhprof-0.9.2.tgz
# Now let's do a bit of clean up:
mv xhprof-0.9.2 xhprof
rm xhprof-0.9.2.tgz
  

Ok, now that we've gotten XHProf downloaded, we need to get it ready for compiling. To do this, we need to set up some environment variables. The reason for this is that MAMP PHP is compiled as a 32 bit application, but if we don't tell that to XHProf when we configure it, it will get compiled as 64 bit. With MAMP 2 on OS X 10.7, this step isn't required, as MAMP is a universal binary. It's likely not required with MAMP 2 on OS X 10.6, though we haven't tested that combination ourselves.

Here goes!

  
MACOSX_DEPLOYMENT_TARGET=10.6 CFLAGS="-arch i386 -arch x86_64 -g -Os -pipe -no-cpp-precomp" CCFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" CXXFLAGS="-arch i386 -arch x86_64 -g -Os -pipe" LDFLAGS="-arch i386 -arch x86_64 -bind_at_load"
export CFLAGS CXXFLAGS LDFLAGS CCFLAGS MACOSX_DEPLOYMENT_TARGET
  

Ok, the somewhat icky part is out of the way, now let's move forward and configure XHProf! Before we run these commands, though, let's just double check to make sure phpize and php-config are set up properly.

  
$ which phpize
/usr/bin/phpize
  

Whoops! What happened there? There was a small step that I missed above, after we symlinked. Some quick debugging shows me that the PHP binaries were not set up as executable. Note: if you didn't run into this, that means you followed my instructions above perfectly. 2 gold stars, my friend.

  
$ ls -l /usr/local/bin/phpize
lrwxr-xr-x  1 root  staff  40 Apr 27 13:07 /usr/local/bin/phpize -> /Applications/MAMP/bin/php5.2/bin/phpize

# Looks good, what about phpize itself?
$ ls -l /Applications/MAMP/bin/php5.2/bin/phpize
-rw-rw-r--@ 1 jsansbury  admin  4564 Mar  6  2010 /Applications/MAMP/bin/php5.2/bin/phpize

# Ruh roh! phpize is not executable. Let's fix it:
$ sudo chmod a+x /Applications/MAMP/bin/php5.2/bin/*
$ which phpize
/usr/local/bin/phpize
  

Success! Ok, now let's move forward with configuring and installing XHProf.

  
$ cd xhprof/extension
$ sudo phpize
PHP Api Version:         20041225
Zend Module Api No:      20060613
Zend Extension Api No:   220060519
  

If you see a API version starting with 2009 above, you probably did something wrong (unless you're using PHP 5.3 on MAMP 2). Retrace your steps, and ensure you are using MAMP's phpize. Let's move on by configuring and installing.

  
./configure
make
sudo make install
  

After running all that, if you see something similar to "Installing shared extensions: /Applications/MAMP/bin/php5.2/lib/php/extensions/no-debug-non-zts-20060613/", you should be good! If not, try making some burnt offerings on your Steve Jobs alter... Or just leave a comment.

"Yay! I think... Now what?"

Ok, now we need to actually let PHP know to use our new extension we just created. To do this we need to edit php.ini. If you have MAMP Pro, you can open it up, click 'File', then 'Edit Template' and then select the appropriate version of PHP. If not, just search for php.ini:

  
locate php5.2/php.ini
  

Once you get it open, add these lines to it:

  
[xhprof]
extension=xhprof.so
; This is the directory that XHProf stores it's profile runs in. You'll probably want to modify this,
; unless you happen to be jsansbury.
xhprof.output_dir=/Users/jsansbury/Sites/xhprof/runs
  

The xhprof.output_dir can be any directory you like, and you may want it to live inside /tmp instead. You might need to create the directory before it actually works, though, so just keep that in mind.

If you do have MAMP Pro, you'll also want to make the same change in /Applications/MAMP/conf/php5.2/php.ini. This is the php.ini that is used for command line PHP, which we are about to use to see if XHProf is working. Taking this extra step will also allow you to use XHProf inside of Drush scripts, which is actually pretty nifty!

Once you've edited the php.ini files, let's run a little test to see if XHProf is getting loaded properly.

  
$ php -i | grep xhprof
xhprof => 0.9.2
  

If you see that 0.9.2 in there, you're good to go.

"Can I go home now?"

That's it! Phew, that was rough, huh? If your brain is feeling a bit like cottage cheese right now, don't worry—the hard part is over. We now have XHProf compiled for the version of PHP that came with MAMP. Stay tuned for a follow up entry on how to get this wired up to work with Devel module, and how we can use this new tool to help isolate performance bottlenecks and other issues.

Get in touch with us

Tell us about your project or drop us a line. We'd love to hear from you!