Scott M. Mcdermott

UNIX Systems & Network Administrator
available for contract or salaried positions

rwtrace.stp

#!/usr/bin/stap

/*
 * Running print, on file descriptor close, of path, total
 * time spent and kbytes for all reads or writes associated
 * with a particlar pid, or all pids on the system if none
 * specified.
 */

global starts
global times

global fds
global paths
global files

global reads
global writes

/****************************************************************************/

function
logmsg (path:string,
        direction:string,
        duration:long,
        bytes:long)
{
        now_ms = gettimeofday_ms()
        now_s = now_ms / 1000
        now_ms = now_ms % 1000

        duration_ms = duration / 1000
        duration_us = duration % 1000

        printf("pid:%-5u @%10u.%03u (%03u.%03u) %s ~%uk %s\n",
               pid(),
               now_s, now_ms,
               duration_ms, duration_us,
               direction,
               bytes / 1024,
               path
        )
}

function
logerr (path:string)
{
        logmsg(path, "!", 0, 0)
}

function
delete_counters (fd:long)
{
        p = pid()

        delete reads[p,fd]
        delete writes[p,fd]
        delete files[p,fd]
        delete fds[p]
        delete starts[p]
        delete times[p,fd]
}

/*
 * we only care about the pids given as command line
 * arguments, everything else uninteresting/ignore
 */
function
interesting:long ()
{
        /* if no arguments given, trace all processes.  This
         * test is not C like since stap does not include
         * argv[0] in the argument count
         */
        if (argc == 0)
                return 1

        /* otherwise check our pid against those specified
         * to narrow the range to only given processes
         */
        for (i = 1; i <= $#; i++)
                if (pid() == strtol(argv[i], 10))
                        return 1

        return 0
}

/****************************************************************************/

probe
syscall.open
{
        if (!interesting())
                next

        paths[pid()] = user_string($filename)
}

probe
syscall.open.return
{
        if (!interesting())
                next

        fd = $return
        if (fd == -1) {
                logerr(paths[p]);
                next
        }

        p = pid()
        reads[p,fd] = 0
        writes[p,fd] = 0

        files[p,fd] = paths[p]
        delete paths[p]
}

probe
syscall.read
{
        if (!interesting())
                next

        p = pid()
        reads[p,$fd] += $count
        starts[p] = gettimeofday_us()
        fds[p] = $fd
}

probe
syscall.read.return,
syscall.write.return
{
        if (!interesting())
                next

        p = pid()
        fd = fds[p]
        times[p,fd] <<< (gettimeofday_us() - starts[p])
}

probe
syscall.write
{
        if (!interesting())
                next

        p = pid()
        writes[p, $fd] += $count
        starts[p] = gettimeofday_us()
        fds[p] = $fd
}

probe
syscall.close
{
        if (!interesting())
                next

        p = pid()
        if (files[p,$fd] == "") {
                delete_counters($fd)
                next
        }

        read = reads[p,$fd]
        write = writes[p,$fd]
        if (!read && !write) {
                delete_counters($fd)
                next
        } else if (!read && write) {
                direction = ">"
                bytes = write
        } else if (read && !write) {
                direction = "<"
                bytes = read
        } else if (read && write) {
                direction = "="
                bytes = 0
        }

        logmsg(files[p,$fd], direction, @sum(times[p,$fd]), bytes)
}