Tuesday Aug 14, 2012

Using the PHP CLI Webserver to Identify and Test Memory Issues in PHP

The PHP 5.4 CLI webserver is being used more and more to identify and resolve PHP bugs. Following on from the CLI webserver tests that Xinchen Hui (aka "laruence") instigated for the PHP 5.4 release, Anatoliy Belsky (aka "weltling") begain adding CLI webserver tests for the APC package, which is currently undergoing some much needed fixups for PHP 5.4.

Rasmus Lerdorf has also been running some of the PHP command line tests through the CLI web server to identify crashes and memory corruption issues that the existing one-test-per-process tests can't identify. Even today a fix for a session issue was merged by Laruence. The symptom would have been a random crash that would have been very hard to reproduce and explain to the PHP developers.

Rasmus mentioned on IRC how he ran the tests: simply renaming the ".phpt" test files as ".php", and invoking them through the CLI webserver. The SKIPIF sections get executed, but that doesn't affect the desired outcome of testing multiple HTTP requests with different input scripts.

Below are some quick shell scripts I put together to automate testing the OCI8 extension with valgrind.

There are three scripts. The first creates a copy of all OCI8 tests with names changed to ".php":

    #!/bin/sh
    # NAME:    setup_tests.sh
    # PURPOSE: Copy .phpt files as .php
    #          Assumes a clean PHP-5.4 branch with no tests/*.diff files

    SD=$HOME/php-5.4/ext/oci8/tests

    cd /tmp && rm -rf tests && mkdir tests

    for F in $(echo $SD/*)
    do
        if [ "${F##*.}" = "phpt" ]; then
            # sym link with a .php file extension instead of .phpt
            N=$(basename $F .phpt).php
        else
            # sym link the unchanged filename
            N=$(basename $F)
        fi
        ln -s $F tests/$N
    done

The second script starts the PHP CLI webserver:

    #!/bin/sh
    # NAME:    start_ws.sh
    # PURPOSE: Start the CLI webserver

    PHP="$HOME/phpbuild/php54/sapi/cli/php"
    PHPOPTS="-d max_execution_time=600 -d log_errors=Off"
    PORT=4444
    VALGRIND=valgrind
    VALGRINDOPTS=" --tool=memcheck --suppressions=$HOME/.valgrind_supp"

    USE_ZEND_ALLOC=0 $VALGRIND $VALGRINDOPTS $PHP $PHPOPTS \
                               -t /tmp/tests -S localhost:$PORT

Here I'm running valgrind to check for memory issues. I set the execution time large, because several of the OCI8 tests take time, especially with valgrind.

The third script invokes all PHP tests sequentially:

    #!/bin/sh
    # NAME:    wg_all.sh
    # PURPOSE: Load all .php scripts sequentially

    PORT=4444

    cd /tmp/tests

    for F in *.php
    do
        wget -O /tmp/L.php_cli_test http://localhost:$PORT/$F
    done

You can change this script to call subsets of your own scripts, or repeatedly call scripts to check memory usage and cleanup code.

To use the scripts, start the webserver and wait for it to initialize:

    $ ./start_ws.sh 
    ==14170== Memcheck, a memory error detector
    ==14170== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
    ==14170== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
    ==14170== Command: php -d max_execution_time=600 -d log_errors=Off -t /tmp/tests -S localhost:4444
    ==14170== 
    PHP 5.4.5-dev Development Server started at Tue Aug 14 14:53:34 2012
    Listening on http://localhost:4444
    Document root is /tmp/tests
    Press Ctrl-C to quit.

This sits, waiting to handle requests.

In a second terminal run wg_all.sh to initiate requests:

    $ ./wg_all.sh
    --2012-08-14 14:38:41--  http://localhost:4444/tests/array_bind_001.php
    Resolving localhost... 127.0.0.1
    Connecting to localhost|127.0.0.1|:4444... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: unspecified [text/html]
    Saving to: `/tmp/L.php_cli_test'

        [ <=>                                   ] 707         --.-K/s   in 0.003s  

    2012-08-14 14:38:44 (231 KB/s) - `/tmp/L.php_cli_test' saved [707]
    . . .

You could suppress this output by adding the option "-o /tmp/L.wg_output" to wg_all.sh. I didn't add it because, although I'm ignoring output, I like to see that the tests are really returning something.

The first terminal running the CLI web server shows each requested script:

    [Tue Aug 14 14:35:06 2012] 127.0.0.1:34006 [200]: /tests/array_bind_001.php
    [Tue Aug 14 14:35:06 2012] 127.0.0.1:34008 [200]: /tests/array_bind_002.php
    [Tue Aug 14 14:35:07 2012] 127.0.0.1:34010 [200]: /tests/array_bind_003.php
    [Tue Aug 14 14:35:07 2012] 127.0.0.1:34013 [200]: /tests/array_bind_004.php
    [Tue Aug 14 14:35:07 2012] 127.0.0.1:34015 [200]: /tests/array_bind_005.php
    [Tue Aug 14 14:35:07 2012] 127.0.0.1:34017 [200]: /tests/array_bind_006.php
    [Tue Aug 14 14:35:07 2012] 127.0.0.1:34019 [200]: /tests/array_bind_007.php
    [Tue Aug 14 14:35:08 2012] 127.0.0.1:34021 [200]: /tests/array_bind_008.php
    . . .

This is the output stream to monitor for valgrind issues.

You can remove the wget "-O /tmp/L.php_cli_test" option if you want to see the returned output from each test, e.g. to check that the tests are connecting to the database correctly.

Removing the php "-d log_errors=Off" option will show you errors in the CLI web server output. Many tests deliberately create errors, but issues with .phpt SKIPIF sections will be more obvious.

You can customize these scripts for your applications and help discover reproducible PHP bugs. With valid test cases, the PHP development team has a much better chance of resolving any problems.

About

Tourists looking out over an Opal mine
I work in the Linux Engineering team at Oracle, espousing the Oracle-PHP-Apache-Linux ("OPAL") stack. I also assist with Python, Perl and Ruby.
Email: christopher.jones@oracle.com
Twitter: http://twitter.com/ghrd
Book: Free PHP Oracle book
Download: PHP Linux RPMs with the OCI8 extension
Links: OTN PHP Developer Center

Search

Archives
« August 2012 »
SunMonTueWedThuFriSat
   
1
2
3
4
5
6
7
8
9
10
11
12
13
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 
       
Today