# Basic SystemTap Handler Constructs

SystemTap supports the use of several basic constructs in handlers. The syntax for most of these handler constructs are mostly based on C and `awk` syntax. This section describes several of the most useful SystemTap handler constructs, which should provide you with enough information to write simple yet useful SystemTap scripts.

## Variables

Variables can be used freely throughout a handler; simply choose a name, assign a value from a function or expression to it, and use it in an expression. SystemTap automatically identifies whether a variable should be typed as a string or integer, based on the type of the values assigned to it. For instance, if you use set the variable `foo` to `gettimeofday_s()` (as in `foo = gettimeofday_s()`), then `foo` is typed as a number and can be printed in a `printf()` with the integer format specifier (`%d`).

Note, however, that by default variables are only local to the probe they are used in. This means that variables are initialized, used and disposed at each probe handler invocation. To share a variable between probes, declare the variable name using `global` outside of the probes. Consider the following example:

timer-jiffies.stp

    global count_jiffies, count_ms
    probe timer.jiffies(100) { count_jiffies ++ }
    probe timer.ms(100) { count_ms ++ }
    probe timer.ms(12345)
    {
      hz=(1000*count_jiffies) / count_ms
      printf ("jiffies:ms ratio %d:%d => CONFIG_HZ=%d\n",
        count_jiffies, count_ms, hz)
      exit ()
    }

computes the `CONFIG_HZ` setting of the kernel using timers that count jiffies and milliseconds, then computing accordingly. The `global` statement allows the script to use the variables `count_jiffies` and `count_ms` (set in their own respective probes) to be shared with `probe timer.ms(12345)`.

Note

The `++` notation in (that is, `count_jiffies ++` and `count_ms ++`) is used to increment the value of a variable by 1. In the following probe, `count_jiffies` is incremented by 1 every 100 jiffies:

    probe timer.jiffies(100) { count_jiffies ++ }

In this instance, SystemTap understands that `count_jiffies` is an integer. Because no initial value was assigned to `count_jiffies`, its initial value is zero by default.

handlers SystemTap handler constructs variables
SystemTap statements SystemTap handler constructs variables
variables SystemTap handler constructs handlers
format and syntax SystemTap handler constructs handlers
handlers SystemTap handler constructs global
SystemTap statements SystemTap handler constructs global
global SystemTap handler constructs handlers
CONFIG_HZ, computing for

## Target Variables

The probe events that map to actual locations in the code (for example `kernel.function("function")` and `kernel.statement("statement")`) allow the use of target variables to obtain the value of variables visible at that location in the code. You can use the `-L` option to list the target variable available at a probe point. If the debug information is installed for the running kernel, you can run the following command to find out what target variables are available for the `vfs_read` function:

    stap -L 'kernel.function("vfs_read")'

This will yield something similar to the following:

    kernel.function("vfs_read@fs/read_write.c:277") $file:struct file* $buf:char* $count:size_t $pos:loff_t*

Each target variable is proceeded by a `$` and the type of the target variable follows the `:`. The kernel's `vfs_read` function has `$file` (pointer to structure describing the file), `$buf` (pointer to the user-space memory to store the read data), `$count` (number of bytes to read), and `$pos` (position to start reading from in the file) target variables at the entry to the function.

When a target variable is not local to the probe point, like a global external variable or a file local static variable defined in another file then it can be referenced through `@var("varname@src/file.c")`.

It is also supported to specify an executable or library file path as the second argument, as in `@var("varname", "/path/to/exe/or/lib")`

SystemTap tracks the typing information of the target variable and can examine the fields of a structure with the `->` operator. The `->` operator can be chained to look at data structures contained within data structures and follow pointers to other data structures. The `->` operator will obtain the value in the field of the structure. The `->` operator is used regardless whether accessing a field in a substructure or accessing another structure through a pointer.

For example to access a field of the static files_stat target variable defined in fs/file_table.c (which holds some of the current file system sysctl tunables), one could write:

    stap -e 'probe kernel.function("vfs_read") {
               printf ("current files_stat max_files: %d\n",
                       @var("files_stat@fs/file_table.c")->max_files);
               exit(); }'

Which will yield something similar to the following:

    current files_stat max_files: 386070

For pointers to base types such as integers and strings there are a number of functions listed below to access kernel-space data. The first argument for each functions is the pointer to the data item. Similar functions are described in for accessing target variables in user-space code.

kernel_char(address)

  Obtain the character at address from kernel memory.

kernel_short(address)

  Obtain the short at address from kernel memory.

kernel_int(address)

  Obtain the int at address from kernel memory.

kernel_long(address)

  Obtain the long at address from kernel memory

kernel_string(address)

  Obtain the string at address from kernel memory.

kernel_string_n(address, n)

  Obtain the string at address from the kernel memory and limits the string to n bytes.

### Pretty Printing Target Variables

SystemTap scripts are often used to observe what is happening within the code. In many cases just printing the values of the various context variables is sufficient. SystemTap makes a number operations available that can generate printable strings for target variables:

$$vars

  Expands to a character string that is equivalent to `sprintf("parm1=%x ... parmN=%x var1=%x ... varN=%x", parm1, ..., parmN, var1, ..., varN)` for each variable in scope at the probe point. Some values may be printed as `=?` if their run-time location cannot be found.

$$locals

  Expands to a subset of `$$vars` containing only the local variables.

$$parms

  Expands to a subset of `$$vars` containing only the function parameters.

$$return

  Is available in return probes only. It expands to a string that is equivalent to `sprintf("return=%x", $return)` if the probed function has a return value, or else an empty string.

Below is a command-line script that prints the values of the parameters passed into the function `vfs_read`:

    stap -e 'probe kernel.function("vfs_read") {printf("%s\n", $$parms); exit(); }'

There are four parameters passed into `vfs_read`: `file`, `buf`, `count`, and `pos`. The `$$parms` generates a string for the parameters passed into the function. In this case all but the `count` parameter are pointers. The following is an example of the output from the previous command-line script:

    file=0xffff8800b40d4c80 buf=0x7fff634403e0 count=0x2004 pos=0xffff8800af96df48

Having the address a pointer points to may not be useful. Instead the fields of the data structure the pointer points to may be of more use. Use the `$` suffix to pretty print the data structure. The following command-line example uses the pretty printing suffix to print more details about the data structures passed into the function `vfs_read`:

    stap -e 'probe kernel.function("vfs_read") {printf("%s\n", $$parms$); exit(); }'

The previous command line will generate something similar to the following with the fields of the data structure included in the output:

    file={.f_u={...}, .f_path={...}, .f_op=0xffffffffa06e1d80, .f_lock={...}, .f_count={...}, .f_flags=34818, .f_mode=31, .f_pos=0, .f_owner={...}, .f_cred=0xffff88013148fc80, .f_ra={...}, .f_version=0, .f_security=0xffff8800b8dce560, .private_data=0x0, .f_ep_links={...}, .f_mapping=0xffff880037f8fdf8} buf="" count=8196 pos=-131938753921208

With the `$` suffix fields that are composed of data structures are not expanded. The `$$` suffix will print the values contained within the nested data structures. Below is an example using the `$$` suffix:

    stap -e 'probe kernel.function("vfs_read") {printf("%s\n", $$parms$$); exit(); }'

The `$$` suffix, like all strings, is limited to the maximum string size. Below is a representative output from the previous command-line script, which is truncated because of the string size limit:

    file={.f_u={.fu_list={.next=0xffff8801336ca0e8, .prev=0xffff88012ded0840}, .fu_rcuhead={.next=0xffff8801336ca0e8, .func=0xffff88012ded0840}}, .f_path={.mnt=0xffff880132fc97c0, .dentry=0xffff88001a889cc0}, .f_op=0xffffffffa06f64c0, .f_lock={.raw_lock={.slock=196611}}, .f_count={.counter=2}, .f_flags=34818, .f_mode=31, .f_pos=0, .f_owner={.lock={.raw_lock={.lock=16777216}}, .pid=0x0, .pid_type=0, .uid=0, .euid=0, .signum=0}, .f_cred=0xffff880130129a80, .f_ra={.start=0, .size=0, .async_size=0, .ra_pages=32, .

target variables pretty printing

### Typecasting

In most cases SystemTap can determine a variable's type from the debug information. However, code may use void pointers for variables (for example memory allocation routines) and typing information is not available. Also the typing information available within a probe handler is not available within a function; SystemTap functions arguments use a long in place of a typed pointer. SystemTap's `@cast` operator (first available in SystemTap 0.9) can be used to indicate the correct type of the object.

The is from the `task.stp` tapset. The function returns the value of the `state` field from a `task_struct` pointed to by the long `task`. The first argument of the `@cast` operator, `task`, is the pointer to the object. The second argument is the type to cast the object to, `task_struct`. The third argument lists what file that the type definition information comes from and is optional. With the `@cast` operator the various fields of this particular `task_struct` `task` can be accessed; in this example the `state` field is obtained.

Casting Example

target variables typecasting

### Checking Target Variable Availability

As code evolves the target variables available may change. The `@defined` makes it easier to handle those variations in the available target variables. The `@defined` provides a test to see if a particular target variable is available. The result of this test can be used to select the appropriate expression.

The from the `memory.stp` tapset provides an probe event alias. Some version of the kernel functions being probed have an argument `$flags`. When available, the `$flags` argument is used to generate the local variable `write_access`. The versions of the probed functions that do not have the `$flags` argument have a `$write` argument and that is used instead for the local variable `write_access`.

Testing target variable available Example

target variables variable availability
handlers target variables
target variables

## Conditional Statements

In some cases, the output of a SystemTap script may be too large. To address this, you need to further refine the script's logic in order to delimit the output into something more relevant or useful to your probe.

Do this by using conditionals in handlers. SystemTap accepts the following types of conditional statements:

If/Else Statements

  Format:

      if (condition)
        statement1
      else
        statement2

  The `statement1` is executed if the `condition` expression is non-zero. The `statement2` is executed if the `condition` expression is zero. The `else` clause (`else` statement2) is optional. Both `statement1` and `statement2` can be statement blocks.

  ifelse.stp

      global countread, countnonread
      probe kernel.function("vfs_read"),kernel.function("vfs_write")
      {
        if (probefunc()=="vfs_read")
          countread ++
        else
          countnonread ++
      }
      probe timer.s(5) { exit() }
      probe end
      {
        printf("VFS reads total %d\n VFS writes total %d\n", countread, countnonread)
      }

  is a script that counts how many virtual file system reads (`vfs_read`) and writes (`vfs_write`) the system performs within a 5-second span. When run, the script increments the value of the variable `countread` by 1 if the name of the function it probed matches `vfs_read` (as noted by the condition `if (probefunc()=="vfs_read")`); otherwise, it increments `countnonread` (`else {countnonread ++}`).

handlers conditional statements if/else
SystemTap statements conditional statements if/else
if/else conditional statements handlers
While Loops

  Format:

      while (condition)
        statement

  So long as `condition` is non-zero the block of statements in `statement` are executed. The `statement` is often a statement block and it must change a value so `condition` will eventually be zero.

handlers conditional statements while loops
SystemTap statements conditional statements while loops
while loops conditional statements handlers
For Loops

  Format:

      for (initialization; conditional; increment) statement

  The `for` loop is shorthand for a while loop. The following is the equivalent `while` loop:

      initialization
      while (conditional) {
         statement
         increment
      }

handlers conditional statements for loops
SystemTap statements conditional statements for loops
for loops conditional statements handlers

need simple, simple examples for FOR and WHILE

Conditional Operators

Aside from `==` ("is equal to"), following operators can also be used in conditional statements:

handlers conditional statements conditional operators
SystemTap statements conditional statements conditional operators
conditional operators conditional statements handlers

>=

  Greater than or equal to

<=

  Less than or equal to

!=

  Is not equal to

handlers conditional statements
SystemTap statements conditional statements

## Command-Line Arguments

A SystemTap script can also accept simple command-line arguments using a `$` or `@` immediately followed by the number of the argument on the command line. Use `$` if you are expecting the user to enter an integer as a command-line argument, and `@` if you are expecting a string.

commandlineargs.stp

    probe kernel.function(@1) { }
    probe kernel.function(@1).return { }

is similar to , except that it allows you to pass the kernel function to be probed as a command-line argument (as in `stap commandlineargs.stp kernel function`). You can also specify the script to accept multiple command-line arguments, noting them as `@1`, `@2`, and so on, in the order they are entered by the user.

handlers SystemTap handler constructs command-line arguments
SystemTap statements SystemTap handler constructs command-line arguments
command-line arguments SystemTap handler constructs handlers
handlers SystemTap handler constructs variable notations
SystemTap statements SystemTap handler constructs variable notations
variable notations SystemTap handler constructs handlers
handlers SystemTap handler constructs
SystemTap handlers SystemTap handler constructs
handlers SystemTap handler constructs syntax and format
SystemTap handlers SystemTap handler constructs syntax and format
syntax and format SystemTap handler constructs handlers