Write your own code profiler in php


I often find myself saying, “Yes, you can do that in PHP.” When I was challenged with writing a code profiler in PHP, I thought I would choke on those words. However, I discovered that it’s both possible and incredibly useful to write your own code profiler in PHP. So, I’d like to share my findings about how to write a code profiler, what pitfalls you will encounter, and offer you some working code to get you started. You may have used a code profiler such as xdebug or xhprof before. These profilers require you to load a PHP extension.

Profiling Without Extensions
If you were not able to load any new extensions into your environment, you would not be able to use classic profiling tools like xdebug. What’s the best way to solve that problem?

Scriptable Profiling
Consider the use case of a custom profiler. For example, in WordPress, plugins are confined to a known set of files.

plugin-file-listing

The “my-plugin” plugin is entirely contained within the wp-content/plugins/my-plugin folder.

We found it incredibly useful to have a profiler that knew the difference between a WordPress plugin, a WordPress theme, and the WordPress core based on what was happening in the code or where the function was defined.

Or, perhaps you’d like your profiler to automatically capture the function arguments every timeDbAdapter::query() is called and log them to a file. You can do this with a custom profiler.

Writing the Profiler
PHP has an internal function called register_tick_function() that lets you call a function whenever a tickable event occurs. A tickable event is a low level parser operation. Generally, this is one tick per statement, but there are exceptions. You can read more on the PHP documentation site

declare(ticks=1);
register_tick_function('do_profile');

func_one();

function do_profile() {
	$bt = debug_backtrace();
	if (count($bt) <=1) {
		return;
	}
	$frame = $bt[1];
	unset($bt);
	$function = $frame['function'];
	echo __FUNCTION__ . ' :: ' . $function . PHP_EOL;
}

function func_one() {
	echo __FUNCTION__ . PHP_EOL;
	func_two();
}

function func_two() {
	echo __FUNCTION__ . PHP_EOL;
	func_three();
}

function func_three() {
	echo __FUNCTION__ . PHP_EOL;
}

Note: declare(ticks=1); is mandatory. This code tells PHP to call the tick function for a tickable event.

Here’s the output for the above code:

func_one
do_profile :: func_one
func_two
do_profile :: func_two
func_three
do_profile :: func_three
do_profile :: func_two
do_profile :: func_one

You can see in the output that the tick function is called when the function is pushed onto the call stack, and again when the function is popped off of the call stack. Notice that echo is not captured.

Timing Code
Here’s a very simple example of a working profiler. It’s the same code as above, with one important enhancement: do_profile keeps track of the functions and how much runtime was spent in each function. This information is stored in the global $profile array.

Instead of each function printing its name, the functions pause (or sleep) for a specified amount of time. To get a quick overview of the accuracy of the profiler, let’s compare the profiler results with the amount of time we programmed the functions to sleep.

declare(ticks=1);
register_tick_function('do_profile');

$profile = array();
$last_time = microtime(true);

func_one();
show_profile();

function do_profile() {
    global $profile, $last_time;
    $bt = debug_backtrace();
    if (count($bt) <= 1) {
        return ;
    }
    $frame = $bt[1];
    unset($bt);
    $function = $frame['function'];
    if (!isset($profile[$function])) {
        $profile[$function] = 0;
    }
    $profile[$function] += (microtime(true) - $last_time);
    $last_time = microtime(true);
}

function show_profile() {
    global $profile;
    print_r($profile);
}

function func_one() {
    usleep(50 * 1000);
    func_two();
}

function func_two() {
    usleep(500 * 1000);
    func_three();
}

function func_three() {
    usleep(5000 * 1000);
}

After running the above code, here’s the output:

Array (
    [func_one] => 0.050179958343506
    [func_two] => 0.50016498565674
    [func_three] => 5.0001859664917
)

These results match the programmed pauses that were placed in the code.

At this point, I hope your mind is swarming with possibilities. You may have some questions like, “How well does this scale to large projects?” or “Does it really work across platforms?” or “How reliable are these numbers?” or “What’s the performance penalty?”

Check out the P3 Plugin for WordPress. Go Daddy released a performance tool for WordPress (shown below), using this exact technology and it has been used successfully by thousands of customers across dozens of server configurations. This method isn’t perfect (I’ll discuss some known issues later), but it’s perfectly usable.

screenshot-2

P3 Profiler Results

A Workable Solution
Things get complex from here on out. There are many types of callable functions in PHP that are detected using debug_backtrace(), such as includes, object calls, static method calls, function calls, and anonymous functions. There are also slight variations in the arguments for debug_backtrace() between PHP versions.

The SimpleProfiler class below should get you started on your own custom PHP profiling solution. It’s very similar to what we’re using in P3.

SimpleProfiler.class.php

/**
 * A very simple php code profiler. No extensions required.
 *
 * Example usage:
 * <code>
 * declare(ticks=1);
 * require_once('./SimpleProfiler.class.php');
 * SimpleProfiler::start_profile();
 * // your code here
 * SimpleProfiler::stop_profile();
 * print_r(SimpleProfiler::get_profile());
 * </code>
 *
 * Consider combining with auto_prepend_file and auto_append_file
 *
 * @package SimpleProfiler
 * @author Kurt Payne
 * @copyright 2012 Go Daddy
 * @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
 * @version Release: 1.0
 * @link http://inside.godaddy.com/write-code-profiler-php
 */
class SimpleProfiler {

    /**
     * Profile information
     * [file] => (
     *     [function] => runtime in microseconds
     * )
     * @access protected
     * @var array
     */
    protected static $_profile = array();

    /**
     * Remember the last time a tickable event was encountered
     * @access protected
     * @var float
     */
    protected static $_last_time = 0;

    /**
     * Return profile information
     * [file] => (
     *     [function] => runtime in microseconds
     * )
     * @access public
     * @return array
     */
    public static function get_profile() {
        return self::$_profile;
    }

    /**
     * Attempt to disable any detetected opcode caches / optimizers
     * @access public
     * @return void
     */
    public static function disable_opcode_cache() {
        if ( extension_loaded( 'xcache' ) ) {
            @ini_set( 'xcache.optimizer', false ); // Will be implemented in 2.0, here for future proofing
            // XCache seems to do some optimizing, anyway.
            // The recorded number of ticks is smaller with xcache.cacher enabled than without.
        } elseif ( extension_loaded( 'apc' ) ) {
            @ini_set( 'apc.optimization', 0 ); // Removed in APC 3.0.13 (2007-02-24)
            apc_clear_cache();
        } elseif ( extension_loaded( 'eaccelerator' ) ) {
            @ini_set( 'eaccelerator.optimizer', 0 );
            if ( function_exists( 'eaccelerator_optimizer' ) ) {
                @eaccelerator_optimizer( false );
            }
            // Try setting eaccelerator.optimizer = 0 in a .user.ini or .htaccess file
        } elseif (extension_loaded( 'Zend Optimizer+' ) ) {
            @ini_set('zend_optimizerplus.optimization_level', 0);
        }
    }

    /**
     * Start profiling
     * @access public
     * @return void
     */
    public static function start_profile() {
        if (0 === self::$_last_time) {
            self::$_last_time = microtime(true);
            self::disable_opcode_cache();
        }
        register_tick_function(array(__CLASS__, 'do_profile'));
    }

    /**
     * Stop profiling
     * @access public
     * @return void
     */
    public static function stop_profile() {
        unregister_tick_function(array(__CLASS__, 'do_profile'));
    }

    /**
     * Profile.
     * This records the source class / function / file of the current tickable event
     * and the time between now and the last tickable event. This information is
     * stored in self::$_profile
     * @access public
     * @return void
     */
    public static function do_profile() {

        // Get the backtrace, keep the object in case we need to reflect
        // upon it to find the original source file
        if ( version_compare( PHP_VERSION, '5.3.6' ) < 0 ) {
            $bt = debug_backtrace( true );
        } elseif ( version_compare( PHP_VERSION, '5.4.0' ) < 0 ) {
            $bt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT );
        } else {
            // Examine the last 2 frames
            $bt = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT, 2 );
        }

        // Find the calling function $frame = $bt[0];
        if ( count( $bt ) >= 2 ) {
            $frame = $bt[1];
        }

        // If the calling function was a lambda, the original file is stored here.
        // Copy this elsewhere before unsetting the backtrace
        $lambda_file = @$bt[0]['file'];

        // Free up memory
        unset( $bt );

        // Include/require
        if ( in_array( strtolower( $frame['function'] ), array( 'include', 'require', 'include_once', 'require_once' ) ) ) {
            $file = $frame['args'][0];

        // Object instances
        } elseif ( isset( $frame['object'] ) && method_exists( $frame['object'], $frame['function'] ) ) {
            try {
                $reflector = new ReflectionMethod( $frame['object'], $frame['function'] );
                $file = $reflector->getFileName();
            } catch ( Exception $e ) {
            }

        // Static method calls
        } elseif ( isset( $frame['class'] ) && method_exists( $frame['class'], $frame['function'] ) ) {
            try {
                $reflector = new ReflectionMethod( $frame['class'], $frame['function'] );
                $file = $reflector->getFileName();
            } catch ( Exception $e ) {
            }

        // Functions
        } elseif ( !empty( $frame['function'] ) && function_exists( $frame['function'] ) ) {
            try {
                $reflector = new ReflectionFunction( $frame['function'] );
                $file = $reflector->getFileName();
            } catch ( Exception $e ) {
            }

        // Lambdas / closures
        } elseif ( '__lambda_func' == $frame['function'] || '{closure}' == $frame['function'] ) {
            $file = preg_replace( '/\(\d+\)\s+:\s+runtime-created function/', '', $lambda_file );

        // File info only
        } elseif ( isset( $frame['file'] ) ) {
            $file = $frame['file'];

        // If we get here, we have no idea where the call came from.
        // Assume it originated in the script the user requested.
        } else {
            $file = $_SERVER['SCRIPT_FILENAME'];
        }

        // Function
        $function = $frame['function'];
        if (isset($frame['object'])) {
            $function = get_class($frame['object']) . '::' . $function;
        }

        // Create the entry for the file
        if (!isset(self::$_profile[$file])) {
            self::$_profile[$file] = array();
        }

        // Create the entry for the function
        if (!isset(self::$_profile[$file][$function])) {
            self::$_profile[$file][$function] = 0;
        }

        // Record the call
        self::$_profile[$file][$function] += (microtime(true) - self::$_last_time);
        self::$_last_time = microtime(true);
    }
}

Known Issues
This approach does have some limitations and we’ve learned every single one of them through trial and error. The following are things that will bite you!

  1. You can only profile using tickable events. The PHP manual defines a tick as:

    An event that occurs for every N low-level tickable statements executed by the parser… Not all statements are tickable. Typically, condition expressions and argument expressions are not tickable.

  2. This means some code won’t generate a callback to your tick function. For example, PHP internal function calls are not tickable. Functions that only return a value and do not perform any other operations will not generate any tick events. For example:
    declare(ticks=1);
    register_tick_function('do_profile');
    
    $profile = array();
    $last_time = microtime(true);
    
    func_one();
    show_profile();
    
    function do_profile() { ... }
    
    function show_profile() { ... }
    
    function func_one() {
        usleep(10 * 1000);
        func_two();
    }
    
    function func_two() {
        return true;
    }

    This profile will not show any calls to “func_two” in the output.

  3. Extensions that cache / optimize opcode can impact your results. After much trial and error and testing, here’s what we found (Your mileage may vary, so feedback is welcome.):
    • eaccelerator – The optimizer impacted results, but the cache did not.
      Workaround 1 – Disable the optimizer by setting eaccelerator.optimizer = 0 in .user.ini or .htaccess
      Workaround 2 – Put your app in eaccelerator’s allowed admin path and calleaccelerator_optimizer(false);.
    • Xcache – The FAQ states that an optimizer is coming in version 2. In our testing, we found that the number of tickable events was reduced with Xcache 1.3.x installed, compared to no opcode cache / optimizer extension. There is no workaround available.
    • Zend Optimizer+ – This impacted our results.
      Workaround – Set zend_optimizerplus.optimization_level = 0 in .user.ini or .htaccess. This value can also be changed via ini_set().
    • APC – There is no optimizer as of 3.0.13. The opcode cache impacted results.
      Workaround – Call apc_clear_cache() before you start profiling. This will empty your entire opcode cache every time you start profiling. Be judicious!Note: Opcode cache and optimizers are generally safe and good for performance. This section offers advice on how to disable opcode caches and optimizers for a special use case. Proceed with caution.
    • The tick function (called do_profile above) will be called for every tickable event. For a large application, this means your profiler function can be called thousands of times. Every optimization counts here. If you’re writing targeted profiling code (i.e., “Look for this certain element in the backtrace.”), you will want to invest some time in optimizing it. I will admit that I’ve used xdebug to find performance issues in early versions of P3.
      profile cat
    • Other php code can interfere with your code. If an engineer re-declares the tick directive, there’s nothing you can do about it.
      // This will disable profiling via register_tick_function
      declare (ticks=0);
    • If a piece of code unregisters your tick function, then you’re equally out of luck.
    • There is a warning that register_tick_function() is not compatible with multi-threaded Web servers and PHP versions older than 5.3. See the documentation on php.net for more details.

Conclusion
I am constantly surprised by the amazing things that can be accomplished with PHP. When we first set out to make P3, I wasn’t sure how we were going to do it. This was a fascinating problem to solve. We couldn’t have done this without the feedback we’ve received from the WordPress community. I hope to post more about the development of P3 in the future.

Why I moved from Android to iPhone


After three years on Android (from Froyo to Jellybean), I moved to an iPhone 5s.  I’ve had a lot of conversations with people about it who share similar thoughts / questions, so I want to write it all down.

I won’t try to change your mind.  I still love Android and Google’s platform.  I’m a chrome user, hangouts user, drive user, gmail user, chromecast owner, and nexus tablet owner.

I will not mention iTunes, media, or the apps (except Google’s).  Those conversations have been had before and they didn’t affect my decision at all.

So why did I switch?  There was no killer feature that drew me toward the iPhone.  It was a death by a thousand cuts that pushed me away from the current suite of Android phones.

Bad hardware.  The Samsung Galaxy Nexus (toro) was a really crappy phone.  It had bad battery life, bad radios, a bad screen, bad memory, and a very short shelf life.  My model also had a blocked headphone jack that I never got fixed (I didn’t use the headphone jack enough to justify it, but it added to my dissatisfaction with the phone). I bought a 3800 mah battery and battery life was between 12-24 hours most days.  This was largely dependent on signal and apps. The radios were famously bad.  Verizon released several updates to fix dropped calls and loss of 4G signal. The screen had a fairly high DPI, but was about 50% blue pixels.  This led to a very blue and dingy experience.  Put on some polarized sunglasses and look at an iPhone and you’ll get a sense of what I mean. The memory on my phone was slower than similar phones (e.g. Galaxy S3) in I/O benchmarks.  There’s also an app to check to see if you have one of the bad memory controllers out there that could be responsible for your phone problems.

Verizon.  Verizon cannot handle a nexus program.  They proved that with the Galaxy Nexus by the weeks (months?) of delay in introducing Google updates.  They disabled Google Wallet, too, with a flimsy explanation, in order to make room for their Isis solution which arrived too late.  There’s a reason that Verizon doesn’t have the Nexus 4 or the Nexus 5.

Bad support.  Really Google / Verizon / Texas Instruments?  A 24 month contract and only 23 months of updates?  No kitkat?

Bad software.  It took more than 30 seconds to open Google Chrome and start typing before the letters would show up in the text box.  Facebook and Google backup caused extremely high amounts of iowait.  Google now ate battery and heated up the phone pretty badly, especially when traveling.  MMS were hit & miss.  The stock e-mail client didn’t work with Exchange properly (it couldn’t see some folders).

Well, the Galaxy Nexus was a self-service device, right?  I did learn a lot about flashing recovery images, unlocking the bootloader, flashing ROMs, wiping the cache partition, what the dalvik cache was, how to root to phone, etc.  I put Cyanogenmod on the phone, got the latest radios, tweaked settings, found a new browser, and fixed as much as I could.  I just got so.  damned.  tired.  I didn’t want to spend that much time fixing something that should just work.  I got tired of bootloops and surprises (hey, half your battery died while you were in the movie!  hey, guess which app won’t open!). So it was definitely time for a new phone.  But which one?  I was pretty set on an Android phone, but …. which one?  After reading all of the reviews, let me summarize them for you:

“This android phone is great except for [ screen | build quality | battery life | camera | software | it's not available on your carrier ].”

The S4, One, Note 3, and Moto X. seem to be best of breed, but I didn’t really like them when I used them.  Coming from a stock experience, the touchwiz / sense software was appallingly awful and I didn’t like the vendor lock in*.

Moto X was nicer, but was basically a cheaper, neutered nexus device.  Props to Google/Verizon for getting KitKat out to it so fast, though!

* Okay, astute readers will realize that Apple is the ultimate vendor lock in.  To me, here’s the difference:  Google has released new features for KitKat.  Now Samsung has to integrate them and release an update.  Then Verizon has to allow it to be released.  So even in Samsung’s own arsenal (SG3, SG4, note 2, note 3, different carriers) there is feature drift.  Customers are powerless to fix this themselves.  Apple has one “authoritative” version of iOS.  It’s released on the same day for everyone for every iPhone purchased in the past few years.  As long as I’m guaranteed the latest version … I’m happy.

So why did I switch? While I had 18 months of Galaxy Nexus hell, my wife sat next to me with her iPhone 4S and happily had none of the problems I had.  She loved her phone and had consistent performance, very few problems, some delightful little features I envied, and could always outperform my phone in any contest I dreamed up.

The quality. It feels like a tool, not a slippery bar of soap.  It’s a personal preference, and it’s a little moot since most phones go in cases, but I am not excited by slippery plastic phones.  I do like the buttons, though.  I like a physical reaction to a physical action (e.g. I press a button, it clicks).  The volume rocker on the nexus was unresponsive and the virtual home button was laggy, so I was never sure if it was just taking a while, or if it missed my click.

The screen. It gets really bright, and it gets really dim.  The auto-brightness on it is really smooth (I never see it change) and always appropriate.  The super-dim screen is great for dark rooms like theaters or if I’m browsing before bed.

The battery. It is 1500ish mah and I get better battery life than my nexus with 2.5x the juice.  This appears to be due to Apple’s ruthless management of background tasks, appropriately powered vibration, and good management of brightness.  The vibrator on my nexus rattled the plastic shell like a subwoofer in a Honda Civic.

The fingerprint reader.  I thought it was a gimmick when I first saw it.  Then I used it.  I instantly fell in love with not having to enter a PIN (or draw a pattern).  My phone just “knows” me.

The camera.  I’m not really a photographer, so I can use all the help I can get.  The pictures are just a little better.  They take instantly and they are clearer.

Siri.  In every head-to-head siri vs. google voice search competition, Siri was more responsive.  Even if Siri got the answer wrong, it was easier to talk to.  Also, the simple things like “Remind me to water the plants tomorrow at 8am” worked properly.  Google would never get the time right.  It considers “water the plants at 8am” the entire task.

Google.  I can still get google now, google authenticator, gmail, google music, google plus, google hangouts, etc. all as native iOS apps.  I would not have moved without that.

Backups.  The backups in iTunes appear to be much more comprehensive.  When my wife got her phone stolen, she was able to get her new one back up and operational really easily by restoring from a backup.  Conversely, it takes me several hours to rebuild an android phone by installing apps, reconfiguring the launcher screens, logging in to different apps / setting settings, etc.

iMessage.  It’s gimmicky, but I like the enhancements to SMS like delivery confirmation, read receipts, larger messages, typing notification, and computer<->table<->phone messaging without having to do anything special.

Keyboard.  This keyboard is sooo good.  I just stab at it with my whimsical meaty thumbs and it figures out what I meant 99% of the time.  I have used a dozen Android keyboards and have never encountered done this good.

VPN.  iPhone supports Cisco ipsec VPN.  I could never get Android to do it properly.

Less configuration drift.  A coworker corrected me … there’s not “no” configuration drift because when the iPhone 4S came out with Siri, the feature didn’t get back ported to the iPhone 4.  And when iOS7 came out with airdrop, it didn’t get backported to older hardware (maybe due to hardware limitations?).  So while everyone is on the same UI, same version of everything … there is less configuration drift.  Take a look at how many phones / carriers / overlays / Android versions there are on the other side and the matrix is a lot bigger.

Stable & runs fast.  Enough said.  I need the phone to do something and it responds how I think it would/should.  I’ve had 1 surprise reboot since I got it, and it booted really fast.  If you just compare the processor speed and RAM, it has similar hardware to the Galaxy Nexus but it just runs so much faster.

So what will I miss?

Intents. This is the API that Android has that lets apps “hook up” with each other.  So when you click “share” you can see a list of apps that you can share to.  So when you install “New Twitter Client XYZ” then go to Chrome and click “Share Page” you’ll see “New Twitter Client XYZ” in the list.

MightyText.  This app let you send/receive SMS messages from your computer/tablet.  Android is a bit more open with their APIs.  Apple won’t let any app have access to the SMS messages, though, so this isn’t possible (without jailbreak, and even then I’ve heard it doesn’t work well).  If everyone I knew had iMessage, this wouldn’t be a problem.

Keyboards.  I like swype.

Widgets.  I like widgets, too.  The badge notifications are pretty neat, though.

Notification LED.  I got trained really well to leaving my phone on completely silent (no vibrate) mode and looking for the LED during meetings at work.  iPhone doesn’t have a nagging notification system.

Expandable storage.  The nexus didn’t have it.  They didn’t for a reason — so the entire memory (on-board system memory and user memory) could be presented as a single block of memory.  This wouldn’t be possible with a removable storage card.  It would break storage into “user stuff” and “system & apps.”  I suspect Apple made the same decision because it’s a better user experience.  It would still be a nice option.  Even via USB-to-go or something for offloading files fast.

Removable battery.  I carried a replacement battery for my Droid 2, and bought an expandable battery for my Nexus.  I have an external battery pack for my iPhone, but I don’t like the prospect of needing the battery replaced and having to just swap the phone out.

Standard navigation buttons.  The back button at the bottom of the screen was great.  iOS7 apps hide (or forget?) how to get back.  Usually it’s in the top left, but sometimes you just sorta … click somewhere else, or swipe.  Or just hit home and go back into the app.

Browsing while talking.  For verizon, browsing while talking requires two radios.  The iPhone 5s has 1 radio so this isn’t possible without a wifi connection.  I didn’t use this feature often, but I miss it.  Hopefully VoLTE will be here soon and save us all.

And that about covers it.

I hope I made the right call.  And I hope this gives you some food for thought, too!

Thailand Learnings


I just got back from a trip to Thailand for a friend’s wedding.  I learned a few things and am recording them here:

  • If you’re overweight, the best thing you can do to prepare for the trip is to lose weight.  It will make traveling in small seats more comfortable, walking / climbing easier, you will stand out less, and any new clothes you buy will probably fit. Lots of places we visited didn’t sell XL (much less 2X or larger)
  • Learn some Thai words.  I didn’t know any Thai before the trip.  Now I can say:  Hello/Goodbye, thank you (and thank you very much), you’re welcome, cheers (as in “let’s drink”), drunk (and very drunk), glass, and bathroom.  I also learned to gesticulate like a mad man.  When people speak English at you in a foreign country, it’s a bit like saying “I know Windows” on a resume.  Levels of competence vary, so be prepared to act out whatever you want — talking louder doesn’t help.  Also, if you want another something (e.g. you need another purell) bring the thing you want with you.  Acting out “hand sanitizer” is not easy.
  • Luggage — we brought backpacks.  This turned out to be a good decision for some situations (we had to climb stairs, walk on beaches, etc.) where wheeled luggage would be a hassle.  But in other situations, backpacks were a bad idea.  My wife got a bad sunburn on her back so I had to carry 2 backpacks for a few days.  Also, they weren’t organized well (that was probably user error) so staying for 1 night in a hotel meant you had to explode the whole thing and repack it in the morning.

    Luggage.  We got so sick of moving this crap around.

  • Luggage — look up the luggage policies on the airlines before you go.  We flew three different airlines (2 were domestic to Thailand) and one of them had fairly restrictive luggage policies. I paid the extra $7 to upgrade our luggage to a higher weight class when I bought the tickets, but other group members didn’t.  They paid an extra $40 (I think?) to check all of their luggage.
  • Bring Febreeze.  We went to JJ Market and I took my messenger bag to carry some purchases.  It got a serious infection of “shrimp smell.”  That’s just how the market was.  Febreeze cured it when I got home, but I had to endure that smell for a week.  I would’ve paid a small ransom for a 3 oz spray bottle of Febreeze.
  • Bring rehydration powder.  We didn’t, but it was readily available at local pharmacies.  Unfortunately when you’re dehydrated, you don’t feel like traveling. We were also on an island where access to the main land was only between 10am and 6pm (roughly) so if you didn’t have it, you had to wait.
  • Buy TP when you get there.  They have purse packs at 7-11.  Very few places have TP in the stalls.  You have to grab it before you go in (or ladies, have it in your purse).  Also … they have bidets there.  External ones … like the sprayer in your kitchen sink.
  • Ask the hotel staff to show you how to use the hot water.  All 4 hotels we stayed at had different hot water configurations.  We figured them all out, but some other people in our group took cold showers most days.
  • Plan your layovers.  We had an 8 hour layover in Incheon (Seoul’s airport).  We kinda wanted to do a transit tour (it’s a tour around Seoul where you don’t need a visa or have to go through customs/immigration) but we didn’t realize that it was 34 degrees in Seoul this time of year.  We were in tropical weather wear.  So we grabbed a room at the transit hotel, took a nap, used the wifi, and took a shower.  The layover at your airport may offer different amenities.

There’s so much more I could post … this is the stuff I want to remember for next time we travel.

Things we did right:

  • Called the banks / credit card companies.  All of our credit cards / ATM cards worked.  I even found out that one of my credit cards didn’t charge a foreign transaction fee!
  • Bring your own medicine.  A bite stick (mosquito bites), sun screen, bug spray, purell, pepto bismol tablets, immodium, antibiotics (only if needed), neosporin cream.
  • Look at all the clothes you want to pack, and leave half.  I didn’t wear a few of the ones I brought even then.  I bought some clothes while there, and we had laundry done a few times, too.
  • Keep some toiletries in your carry on.  On our trek back, we had 4 flights (20 hours) over a 2 day span including 17 hours of layover time.  We needed a shower, to brush our teeth, and to reapply deodorant during that window.
  • Mobile data plan — this was half right / half wrong.  I borrowed a phone from Verizon (free) and didn’t make any calls, didn’t send any texts, and sipped data for $25.  My wife’s phone didn’t work (VZW didn’t set something up right?) so we bought a burner phone ($26) and 300 baht ($10) worth of time.  Her phone + time was more expensive, but we used the heck out of it.  If we needed a taxi and couldn’t give the driver directions to the hotel, we’d bust out the thai phone and call the hotel.  I don’t know what was said, but we arrived safely and the meter was running.  She still had like 150 baht when we left, too.  So next time, we’ll bring the $26 phone and just pick up a new SIM (if we go to a diff country) and more time at a gas station / airport.  Her thai phone didn’t do data, but we didn’t need that.  My phone did data, but we didn’t really use it — only in a pinch.

Anthony Bourdain is a personal hero.


I have many personal heroes. When I was young, I appreciated Houdini because he was the world’s greatest magician. As I grew older, I appreciated him because of his ability to shape modern magic as he performed his escapes up close and personal. Then he became quite the salesman, and would show up in a new town frequently to put on a free public daredevil act, then let everyone know he’d be selling tickets to his show.

Magic did pay the bills, but he had other passions. He loved his wife Bess, and together they took on all of the psychics and spiritualist frauds during the day.

I would recommend “The Secret Life of Houdini” for further reading.

… but I digress.

I’m inducting Anthony Bourdain into my list of personal heroes. According to his life story so far:

    • Interested in cooking, but got his butt kicked at a real restarant, and fell in love with the lifestyle
    • Went to CIA (Culinary Institute of America)
    • Got back into a restaurant … then catering …. then bigger / better restaurants …. then eventually he became the head chef at Les Halles in Manhattan

That life path is difficult enough to do. But then he took it further. He wrote an article for a newspaper. And it went well. So he wrote a book. And it was a sensation and instantly legitimized him as a culinary insider, not just a poseur.

From there, he’s written a more books and become a world traveller on “No Reservations.” He was never on vacation, but struggled to make sure his trips were varied and interesting. Frequently, he would engage a host family to feed him or tell him stories. He cooked infrequently, but often explained why the food was so good using his frank descriptions and engaging imagery.

And all of this …. and I don’t have any “Tony Bourdain” products in my kitchen. The man has kept his soul intact.

I will miss “No Reservations.” I look forward to what Tony has coming up next.

KCacheGrind Source Code Annotation on WAMP


KCacheGrind is awesome.  But the source code annotation (the “source code” tab) doesn’t work when the profile originated on a WAMP installation. When you take a profile on Windows, the header of the file looks like this:

version: 1
creator: xdebug 2.1.2
cmd: C:\wamp\www\wordpress\wp-admin\index.php
part: 1
positions: line

And all of the file lines contain similar paths (C:\wamp\www\....\functions.php).

The backslashes seem to confuse KCacheGrind’s path resolution. The simple fix was to convert the backslashes to forward slashes. I did this with sed via cygwin:

sed -i 's/\\/\//g' cachegrind.out.*

You could probably achieve similar results with TextPad or GrepWin, though.

After that, all of the source code annotation worked. If it doesn’t work for you, try adding your source folder under KCacheGrind -> Settings -> Source Annotation.

PHPUnit: process isolation and “constant already defined”


This weekend, some WordPress core devs ported the unit tests over to a new phpunit compatible runner. I was very excited about this, particularly because the tests I maintain define a constant (DOING_AJAX) which can impact other tests.  Using process isolation should remove this impact, although at a performance cost.  I used the @runTestsInSeparateProcesses annotation, but the tests failed.  PHPUnit was restoring the global state (e.g. before the test split into a separate process) and it looked like this:

1.) Define all of the constants that were already defined
2.) Include the included files

This is the wrong process for the WordPress framework though. The included files were responsible for defining the constants. When they are included, they generate notices for defining constants that are already defined (by PHPUnit).

We all put our heads together and came up with a fix (props nacin) by hijacking PHPUnit_Framework_TestCase::prepareTemplate().

class MyTestCase extends PHPUnit_Framework_TestCase {

	/**
	 * Define constants after requires/includes
	 * @param Text_Template $template
	 * @return void
	 */
	public function prepareTemplate( Text_Template $template ) {
		$template->setVar( array(
			'constants'    => '',
			'zz_constants' => PHPUnit_Util_GlobalState::getConstantsAsString()
		));
		parent::prepareTemplate( $template );
	}
}

Yes, this is hacky, and PHPUnit warns that prepareTemplate() was deprecated in 3.4.0. But when we looked, there were no other solutions, except disabling process isolation.


kurtpayne:

Step by step post on how to make WordPress into a full blown product/category site. No ecomm, but very comprehensive.

Originally posted on Mark on WordPress:

Have Baby. Need Stuff! is a baby gear site that my wife and I just launched. I thought I’d share how I built it.

WordPress Core

WordPress is a Git submodule, with the content directory moved to the /content/ directory. This makes my Git repo smaller, as WordPress isn’t actually in it.

Underscores

For a theme base, I started with the Underscores starter theme by the theme team at Automattic. Underscores is not a theme itself… it’s a starting point for building your own theme.

Bootstrap

Next, I integrated Bootstrap, by Twitter, to handle the CSS base and the grid system. Bootstrap is a really powerful framework, and version 2.0 has great responsive design support, which allowed me to create a single design that scales up to big screens or down to tablet or phone screen sizes. Try resizing it in your browser to see the responsiveness in…

View original 1,373 more words

Apache Traffic Server as a Reverse Proxy


Varnish is a great tool to have if you need a scriptable proxy server. For most sites, though, this will be more complex than necessary. Check out this article on how to speed up WordPress with Varnish.

I propose that most users don’t need the power of VCL, though. They’d be much better off with a high performance proxy server with RFC 2616 compliance and simple configuration.

Allow me to play devil’s advocate and show you how to build and configure Apache Traffic Server, then show you benchmark results:

# built on centos 6.2
# Get traffic server pre-requisites
yum install gcc-c++ openssl-devel tcl-devel expat-devel pcre-devel make

# Get traffic server
wget http://apache.tradebit.com/pub/trafficserver/trafficserver-3.1.3-unstable.tar.bz2
tar -xf trafficserver-3.1.3-unstable.tar.bz2
cd trafficserver-3.1.3-unstable

# Build traffic server
./configure
make -j 8

# Install traffic server
make install
ln -s /usr/local/bin/trafficserver /etc/init.d/trafficserver
chkconfig --add trafficserver
chkconfig trafficserver on

# Configure traffic server URL map
echo "map http://YOURDOMAIN.COM http://localhost" >> /usr/local/etc/trafficserver/remap.config
echo "reverse_map http://localhost http://YOURDOMAIN.COM" >> /usr/local/etc/trafficserver/remap.config

# Run traffic server
trafficserver restart

Note: Traffic Server runs on port 8080. I used my host’s router to map port 80 -> 8080 and left httpd on port 80.

Since traffic server is now handling all incoming requests, the web server logs will be unreliable. You can rely on an in-browser analytics package, or see how to configure Traffic Server for combined logs.

Configuration done

Let’s benchmark.

Note: my test site is using W3 Total Cache. W3TC sets the cache-control, last-modified, and expires headers on the pages, and static content so that traffic server knows how to best cache your content. You can also use the Varnish integration in W3TC with traffic server. This will purge pages from the traffic server proxy whenever you purge them from W3TC.

Protip: If you need to have Traffic Server work without an expires header or cache-control header, look in records.config and find this section:

# required headers: three options:
#   0 - No required headers to make document cachable
#   1 - "Last-Modified:", "Expires:", or "Cache-Control: max-age" required
#   2 - explicit lifetime required, "Expires:" or "Cache-Control: max-age"
CONFIG proxy.config.http.cache.required_headers INT 2

Look at blitz.io. I used this command in the blitzbar:

# If you paid for blitz, you can max out at 1,000 concurrent users
# -p 1-1000:5,1000-1000:55 -r california -H 'Accept-encoding: gzip' http://YOURDOMAIN.COM/
-p 1-250:1,250-250:59 -r california -H 'Accept-encoding: gzip' http://YOURDOMAIN.COM/

For these benchmarks, I paid to upgrade blitz.io to allow 1,000 concurrent users. At 250 concurrent users, the performance profiles of these systems didn’t really shine through.

Here’s the Varnish benchmark.

blitz.io - Varnish

blitz.io - Varnish

Here’s the Traffic Server benchmark:

blitz.io - Traffic Server

blitz.io - Traffic Server

For comparison, here’s Apache HTTPD alone:

blitz.io - Apache HTTPD

blitz.io - Apache HTTPD

Notice how Varnish doesn’t like the initial influx of connections. Notice how Varnish had more timeouts, too. Notice, too, how much of a difference these caching systems make. Remember, the Apache HTTPD benchmark is already using WordPress + W3 Total Cache.

Let’s look at apachebench for a different view. Using this command, I benchmarked raw Apache HTTPD vs. Varnish vs. Traffic Server. This is all done locally over a loopback connection.

# Change the port to 80 to benchmark apache directly
ab -c 250 -n 1000000 -k -H "Host: YOURDOMAIN.COM" http://127.0.0.1:8080/

Apache results (no chance!):

apr_socket_recv: Connection timed out (110)
Total of 17725 requests completed

Varnish results:

Time taken for tests:   95.049 seconds
Complete requests:      1000000
Failed requests:        0
Requests per second:    10520.83 [#/sec] (mean)
Time per request:       23.762 [ms] (mean)
Time per request:       0.095 [ms] (mean, across all concurrent requests)
Transfer rate:          75257.95 [Kbytes/sec] received

Traffic Server results:

Time taken for tests:   50.971 seconds
Complete requests:      1000000
Failed requests:        0
Requests per second:    19619.00 [#/sec] (mean)
Time per request:       12.743 [ms] (mean)
Time per request:       0.051 [ms] (mean, across all concurrent requests)
Transfer rate:          139371.67 [Kbytes/sec] received
Conclusion

According to blitz.io, Varnish and Traffic Server benchmark results are close. According to ab, Traffic Server is twice as fast as Varnish. Configuration is different in each package. Traffic Server has some unique features like the ability to act as as a forward proxy and a reverse proxy, the ability to proxy SSL connections, a plugin architecture, and will soon have SPDY support. If you haven’t heard of Traffic Server yet, you should check it out. If you’re not using a caching proxy, please start using one!

For more information on Apache Traffic Server, check out this 2011 USENIX presentation (PDF, 10.4 MB) by Leif Hedstrom