3.2 Tracing a User-Space Application

The following PHP program, named func.php, runs forever cycling through the heads() and tails() functions to various depths of recursion, depending on random number generation. This example is a PHP program that you run from the command line, but DTrace can just as easily trace PHP web applications. You can think of the infinite loop that calls the function initialize() as simulating multiple requests to a web-based application.

Example 3.1 func.php: Infinitely looping, recursive test script

<?php

/* func.php -- Infinitely looping, recursive test script */

function heads(&$d) {
        $d++;
        $i = @(1/(abs($d)-3));
        switch (mt_rand(0,2)) {
                case 0:
                        heads($d);
                        break;
                case 1:
                        tails($d);
                        break;
                default:
                        sleep(mt_rand(0,2));
                        break;
                }
        $d--;
}

function tails(&$d) {
        $d--;
        $i = @(1/(abs($d)-3));
        switch (mt_rand(0,2)) {
                case 0:
                        tails($d);
                        break;
                case 1:
                        heads($d);
                        break;
                default:
                        sleep(mt_rand(0,2));
                        break;
                }
        $d++;
}

function initialize() {
        $d = 0;
        switch (mt_rand(0,1)) {
                case 0:
                        heads($d);
                        break;
                default:
                        tails($d);
                        break;
                }
}

while (1) initialize();
?>

To allow user programs to be traced, enable the fasttrap provider in DTrace:

# modprobe fasttrap

Set func.php running in a window:

# php ./func.php

We can now start to trace the operation of this program from another window.

# dtrace -q -n 'function-entry {printf("Called %s() in %s at line %d\n", \
  copyinstr(arg0), copyinstr(arg1), arg2)}'
Called initialize() in /root/errfunc.php at line 42
Called heads() in /root/errfunc.php at line 3
Called heads() in /root/errfunc.php at line 3
Called tails() in /root/errfunc.php at line 22
Called initialize() in /root/errfunc.php at line 42
Called heads() in /root/errfunc.php at line 3
Called heads() in /root/errfunc.php at line 3
Called heads() in /root/errfunc.php at line 3
Called heads() in /root/errfunc.php at line 3
Called initialize() in /root/errfunc.php at line 42
Called heads() in /root/errfunc.php at line 3
Called heads() in /root/errfunc.php at line 3
Called tails() in /root/errfunc.php at line 22
Called heads() in /root/errfunc.php at line 3
Called heads() in /root/errfunc.php at line 3
Called heads() in /root/errfunc.php at line 3
Called initialize() in /root/errfunc.php at line 42
^C
Note

When tracing user-space programs, we must always use the argN arguments (whose type is int64_t) and, if necessary, cast the type as appropriate to the real type of the probe argument. The args[N] variables, to which DTrace automatically assigns the type that is defined for the probe, are not available. You must use the appropriate type converter such as stringof() or a cast such as (string).

We use copyinstr() in the example because arg0 and arg1 both refer to the addresses of character arrays in the address space of a user process. copyinstr() copies the data from user space to a DTrace buffer in kernel space and converts it to type string.