#! /usr/bin/stap
#
# Copyright (C) 2010 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
# ` = 0' is there to prevent: WARNING: never-assigned global variable
# `opt_f' is a boolean for both -f and -ff.
global opt_f = 0
# Modify the [pid %5d] output to [%d] for the -ff postprocessor.
global opt_ff = 0
global opt_q = 0
# Override target().
global opt_child = 0
global trace
global last_tid
probe begin
{
tid = opt_child > 0 ? opt_child : target ()
trace[tid] = 1
last_tid = tid
}
function heading (tid:long)
{
if (opt_ff)
return printf ("[%d]", tid)
foreach (tidi in trace limit 2)
tid_count++
if (tid_count > 1)
{
/* strace really calls TID a "pid". */
return printf ("[pid %5d] ", tid)
}
}
global funcname
global nest
function realnl ()
{
if (opt_ff)
print ("n\n")
else
print ("\n")
}
probe end
{
if (nest[last_tid] && funcname[last_tid] != "")
{
printf (") = ? ")
realnl ()
}
foreach (tid in nest)
if (tid != last_tid)
{
heading (tid)
printf ("<... %s resumed> ) = ? ",
funcname[tid] != "" ? funcname[tid] : "?");
realnl ()
}
}
/* BUG: sleeping function called from invalid context at kernel/mutex.c:94 */
%{static DEFINE_MUTEX (tracer_mutex);%}
function lock:long ()
%{
if (in_atomic () || irqs_disabled())
{
THIS->__retvalue = 0;
return;
}
mutex_lock (&tracer_mutex);
THIS->__retvalue = 1;
%}
function unlock ()
%{
mutex_unlock (&tracer_mutex);
%}
/*
global lockvar
function lock:long ()
{
lockvar++
return 1
}
function unlock ()
{
lockvar--
}
*/
#probe kprocess.start
#{
# trace[pid ()] = 1
#}
#probe kprocess.exit
#{
# trace[pid ()] = 0
#}
probe kprocess.create
{
if (opt_f && trace[tid ()] != 0)
{
new_tid = task_tid (task)
trace[new_tid] = 1
if (!opt_q)
{
printf ("Process %d attached", new_tid);
realnl ()
}
}
}
probe kprocess.release
{
tid = task_tid (task)
if (trace[tid] != 0)
{
if (nest[tid])
{
heading (tid)
/* FIXME: Do not assume "exit" for the nested calls. */
printf ("<... %s resumed> = ?",
funcname[tid] != "" ? funcname[tid] : "exit")
realnl ()
delete nest[tid]
delete funcname[tid]
}
delete trace[tid]
if (!opt_q)
{
printf ("Process %d detached", tid);
realnl ()
}
}
}
probe syscall.*
{
if (trace[tid ()] != 0 && lock ())
{
/* Why is mmap() called recursively twice? */
if (nest[last_tid] && funcname[last_tid] != "")
{
if (last_tid != tid ())
{
if (opt_ff)
print ("c\n")
else
print (" \n")
}
else
{
print (" ")
realnl ()
}
}
last_tid = tid ();
funcname[tid ()] = name
nest[tid ()]++
heading (tid ())
printf ("%s(%s",name, argstr)
unlock ()
}
}
probe syscall.*.return
{
if (trace[tid ()] != 0 && lock ())
{
if (last_tid != tid () && nest[last_tid] && funcname[last_tid] != "")
{
if (opt_ff)
print ("c\n")
else
print (" \n")
}
if (last_tid != tid ())
{
heading (tid ())
if (!opt_ff)
printf ("<... %s resumed> ", name);
}
if (last_tid == tid () && funcname[last_tid] == "")
{
heading (tid ())
printf ("<... %s resumed> ", name);
}
last_tid = tid ();
printf (") = %s", retstr)
realnl ()
if (!--nest[tid ()])
delete nest[tid ()]
delete funcname[tid ()]
unlock ()
}
}