#! /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 () } }