/*
 * current.stp  (requires Guru mode)
 *
 * Test the validity of the current pointer in various contexts.
 */

global length

function commlen:long () %{
    STAP_RETVALUE = strlen(current->comm);
%}

function pcommlen:long () %{
    STAP_RETVALUE = strlen(current->parent->comm);
%}

probe begin { println("systemtap starting probe") }

probe
    timer.profile,
    %( arch != "ia64" && arch != "arm" %?
      # __switch_to is macro definition in ia64,
      # and ia64_switch_to is defined in assemble language
      # PR13734 arm has a similar issue
      %( arch != "x86_64" %?
        # __switch_to can not be probed on x86_64 - see PR2068
        kernel.function("__switch_to").call ?,
        %( arch != "i386" %?
          # __switch_to.return is broken on i686 - see PR4542
          kernel.function("__switch_to").return ?,
        %)
      %)
    %)

    /* XXX
     * It would be nice if we could just do this:
     *    kernel.statement("__switch_to@*:*")
     * to probe every line in the function
     */

    /* add other kernels/archs here as desired... */
    %( arch == "x86_64" %?
       %( kernel_vr == "2.6.9-22.ELsmp" %?
          /* the lines before, at, and after the update of pcurrent */
          kernel.statement("__switch_to@arch/x86_64/kernel/process.c:508"),
          kernel.statement("__switch_to@arch/x86_64/kernel/process.c:509"),
          kernel.statement("__switch_to@arch/x86_64/kernel/process.c:510"),
       %)
       %( kernel_vr == "2.6.9-24.ELsmp" %?
          /* the lines before, at, and after the update of pcurrent */
          kernel.statement("__switch_to@arch/x86_64/kernel/process.c:501"),
          kernel.statement("__switch_to@arch/x86_64/kernel/process.c:502"),
          kernel.statement("__switch_to@arch/x86_64/kernel/process.c:503"),
       %)
    %)

    kernel.function("*@kernel/time.c").call,
    kernel.function("*@kernel/time.c").return,
    module("usb*").function("*interrupt*").call ?,
    module("usb*").function("*interrupt*").return ?
{
    length <<< commlen()
    length <<< pcommlen()
}

function get_TASK_COMM_LEN:long() %{
    /* TASK_COMM_LEN was introduced in 2.6.11, before which
     * the length of the comm string was hard-coded to 16 */
#ifdef TASK_COMM_LEN
    STAP_RETVALUE = TASK_COMM_LEN;
#else
    STAP_RETVALUE = 16;
#endif
%}

probe end {
    println("systemtap ending probe")
    printf("count = %d\n", @count(length))
    printf("sum = %d\n", @sum(length))
    printf("min = %d\n", @min(length))
    printf("max = %d\n", @max(length))
    printf("avg = %d\n", @avg(length))

    /*
     * Check that the min & max lengths look reasonable.  If any string was
     * either empty or too big, then the current pointer probably wasn't
     * valid, even though it dereferenced without crashing.
     */
    if (@min(length) > 0) {
        println("systemtap test success")
    } else {
        println("unexpected minimum length")
        println("systemtap test failure")
    }
    if (@max(length) < get_TASK_COMM_LEN()) {
        println("systemtap test success")
    } else {
        println("unexpected maximum length")
        println("systemtap test failure")
    }
}
