Arguments

When you bind a probe, you need to collect some data in it. In C, data is usually passed as arguments to a function, or returned as return value. So, when you bind a function boundary tracing probe, you may need to gather them. Argument extraction relies on calling conventions, and extracts data directly from registers or stack.

For example, let's look at Solaris kernel function from ZFS: void spa_sync(spa_t *spa, uint64_t txg);. First argument is ZFS representation of a pool, second is 64-bit unsigned integer which is transaction group number. So when we bind a probe to a spa_sync, we can print both of them:

# dtrace -qn '
    ::spa_sync:entry { 
        printf("synced txg=%d [%s]\n", 
            args[1], args[0]->spa_name); }'

DTrace supports two forms of arguments: arg0, arg1 ... argN are uint64_t values, while args[0], args[1] ... args[N] have actual types if DTrace is able to extract them (i.e. DTrace forbids type hinting for unstable probes). If args[N] is unavailable, you can still treat argN as pointer and covert it as you want:

# dtrace -qn '
    ::spa_sync:entry { 
        printf("synced txg=%ld [%s]\n", 
            (long) arg1, ((spa_t*) arg0)->spa_name); }'

DTrace supplies two arguments for return probes: arg0 is an instruction pointer to a caller, and arg1 or args[1] is a return value.

DWARF format used in Linux is richer than CTF from Solaris and saves not only argument types, but their names too. They are provided in SystemTap in separate namespace beginning with $ and followed by name of argument. It provides access to locals as well as arguments. However, some of them may be unavailable at the probe, because they are overwritten by other data (which is called optimized out). For example, let's look at vfs_read function from Linux kernel:

ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) {
    ssize_t ret;

    [...]

    return ret;
}

Unfortunately, variable ret is inaccessible at the return probe, but you can still get it from %rax register on x86_64 which is used for saving return values. SystemTap supplies return values in $return variable:

# stap -e '
    probe kernel.function("vfs_read").return { 
        printf("VARS:%s\nreturn: %d\n", $$vars, $return);
        exit(); }'
VARS: file=0xcfa79580 buf=0xbf9fa8b8 count=0x2004 pos=0xcf2e9f98 ret=?
return: 12

To handle such situations (and many others, i.e. when name of argument was changed in current kernel), you may use @defined expression, or @choose_defined which works like ternary operator: @choose_defined($a, $b) is equivalent to @defined($a)? $a : $b. Here is an example of @defined:

if (@defined($var->field)) { 
    println($var->field);
}

If you want to print all arguments simultaneously, you should carefully handle each argument. However, SystemTap can do it automatically. Such strings provided in meta-variables:

Finally, SystemTap allows to convert arguments to strings, including pretty representation of structure pointers when all fields are read, if trailing dollar sign is added to an argument:

# stap -e '
    probe kernel.function("vfs_read") { 
        println($file$); }'

References