SystemTap是一个强大的调试工具,确切的说应该是一门调试语言,因为它有自己的语法,也有解析、编译、运行等过程(准确的说有五个阶段),但它主要解决的问题是收集Linux内核或者用户进程的信息,主要目的是调试。我一直以为gdb、kgdb是Linux最强大的调试器,曾经爱不释手,自从发现了SystemTap之后,又有了当初喜欢gdb的那种感觉了,真的是相见恨晚啊。gdb和SystemTap不是竞争关系,而是互补关系,gdb能做的事情SystemTap做不到,比如断点/watch变量等等这些SystemTap都做不到,而SystemTap能做的事情gdb做不到或者非常麻烦才做到,比如很方便查看内核调试栈/嵌入C语言等等gdb就傻眼了。SystemTap真的非常强大,会了这个又觉得自己帅几个百分点了。来看一下几个技巧吧:
root@jusse ~/linux-source-3.13.0# grep -nr 'SYSCALL_DEFINE3(open' ./
./fs/compat.c:1075:COMPAT_SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
./fs/compat.c:1461:COMPAT_SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
./fs/open.c:1011:SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
./fs/fhandle.c:254:SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
root@jusse ~/linux-source-3.13.0# stap -l 'kernel.function("sys_open")'
kernel.function("SyS_open@/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1011")
root@jusse ~/develop# stap -l 'process("/opt/nginx/sbin/nginx").function("ngx_shmtx_trylock")'
process("/opt/nginx/sbin/nginx").function("ngx_shmtx_trylock@src/core/ngx_shmtx.c:63")
ngx_shmtx.c第63行就是编译出来的版本。
root@jusse ~/work/nginx# stap -l 'process("/opt/nginx/sbin/nginx").function("ngx_epoll_*")'
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_add_connection@src/event/modules/ngx_epoll_module.c:502")
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_add_event@src/event/modules/ngx_epoll_module.c:386")
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_create_conf@src/event/modules/ngx_epoll_module.c:807")
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_del_connection@src/event/modules/ngx_epoll_module.c:526")
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_del_event@src/event/modules/ngx_epoll_module.c:444")
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_done@src/event/modules/ngx_epoll_module.c:348")
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_init@src/event/modules/ngx_epoll_module.c:295")
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_init_conf@src/event/modules/ngx_epoll_module.c:824")
process("/opt/nginx-dso/sbin/nginx").function("ngx_epoll_process_events@src/event/modules/ngx_epoll_module.c:564")
root@jusse ~# ps -ef -o pid,ppid,pgid,comm
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
PID PPID PGID COMMAND
28400 28399 28400 bash
5637 28400 5637 \_ cc_epoll
5638 5637 5637 \_ cc_epoll
5639 5637 5637 \_ cc_epoll
5640 5637 5637 \_ cc_epoll
5641 5637 5637 \_ cc_epoll
5642 5637 5637 \_ cc_epoll
root@jusse ~# strace ps -ef -o pid,ppid,pgid,comm
……
stat("/proc/5640", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
open("/proc/5640/stat", O_RDONLY) = 6
read(6, "5640 (cc_epoll) S 5637 5637 2840"..., 1024) = 324
read(6, "", 700) = 0
close(6) = 0
……
root@jusse ~# cat /proc/5640/stat
5640 (cc_epoll) S 5637 5637 28400 34828 5637 4219200 63 0 0 0 0 0 0 0 20 0 1 0 746032770 8626176 30 18446744073709551615 4194304 4200196 140734779287888 140734779166376 140232232810899 0 0 0 0 18446744071581020176 0 0 17 0 0 0 0 0 0 6299160 6299888 32350208 140734779291850 140734779291861 140734779291861 140734779293677 0
root@jusse ~/systemtap# strace cat /proc/5640/stat
……
open("/proc/5640/stat", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
fadvise64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
read(3, "5640 (cc_epoll) S 5637 5637 2840"..., 32768) = 324
write(1, "5640 (cc_epoll) S 5637 5637 2840"..., 3245640 (cc_epoll) S 5637 5637 28400 34828 5637 4219200 63 0 0 0 0 0 0 0 20 0 1 0 746032770 8626176 30 18446744073709551615 4194304 4200196 140734779287888 140734779166376 140232232810899 0 0 0 0 18446744071581020176 0 0 17 0 0 0 0 0 0 6299160 6299888 32350208 140734779291850 140734779291861 140734779291861 140734779293677 0
) = 324
read(3, "", 32768) = 0
close(3) = 0
……
probe kernel.statement("vfs_read@/build/buildd/linux-lts-trusty-3.13.0/fs/read_write.c:392")
{
if ($count == 32768) { //这里设置条件是为了过滤只留下需要的信息,32768是上面用strace跟踪得到read的第三个参数
printf("open: %s, read: %s\n", symname($file->f_op->open), symname($file->f_op->read));
}
}
root@jusse ~/systemtap# stap vfs_read.stp
open: 0x0, read: inotify_read
open: 0x0, read: inotify_read
open: proc_single_open, read: seq_read
open: proc_single_open, read: seq_read
root@jusse ~/systemtap# stap copy_process.stap
semantic error: no line records for /build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1235 [man error::dwarf] (try :1234 or :1236)
semantic error: while resolving probe point: identifier 'kernel' at copy_process.stap:1:7
source: probe kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1235")
^
semantic error: no match
Pass 2: analysis failed. [man error::pass2]
root@jusse ~/systemtap# stap copy_process.stap
semantic error: failed to retrieve location attribute for 'p' [man error::dwarf]: identifier '$p' at copy_process.stap:3:29
dieoffset: 0x9d4ac2 from unknown debug file for kernel
function: copy_process at /build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1236
source: printf("$p->pid: %d\n", $p->pid);
^
Pass 2: analysis failed. [man error::pass2]
root@jusse ~/systemtap# stap -L 'kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:*")'
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1134")
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1140")
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1144") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1145") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1147") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1154") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1162") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1171") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
kernel.statement("copy_process@/build/buildd/linux-lts-trusty-3.13.0/kernel/fork.c:1172") $clone_flags:long unsigned int $stack_start:long unsigned int $stack_size:long unsigned int $child_tidptr:int* $pid:struct pid* $trace:int $p:struct task_struct*
probe kernel.function("copy_process").return
{
printf("pid: %d\n", $return->pid);
}
root@jusse ~/systemtap# stap -L 'kernel.function("sys_open")'
kernel.function("SyS_open@/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1011") $ret:long int
root@jusse ~/systemtap# stap -L 'kernel.statement("sys_open@/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:*")'
kernel.statement("SyS_open@/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1011") $ret:long int
kernel.statement("SyS_open@/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1014") $filename:long int $flags:long int $ret:long int
kernel.statement("SyS_open@/build/buildd/linux-lts-trusty-3.13.0/fs/open.c:1016") $filename:long int $ret:long int
root@jusse ~/systemtap# cat sys_open.stp
probe kernel.function("sys_open").call
{
printf("filename: %p, flags: %d, mode: %x\n", pointer_arg(1), int_arg(2), int_arg(3));
}
ERROR: user string copy fault -14 at 00007f16323ffd90 [man error::fault] near identifier 'user_string_n' at /usr/local/share/systemtap/tapset/uconversions.stp:120:10
ERROR: kernel string copy fault at 0x00007fde54e79d90 [man error::fault] near identifier 'kernel_string' at /usr/local/share/systemtap/tapset/linux/conversions.stp:18:10
root@jusse ~/systemtap# cat sys_open.stp
probe kernel.function("sys_open").call
{
filename = user_string_quoted(pointer_arg(1));
printf("filename: %s, execname: %s\n", filename, execname());
}
root@jusse ~/systemtap# cat netif_receive_skb.stp
probe kernel.function("netif_receive_skb")
{
printf("--------------------------------------------------------\n");
print_backtrace();
printf("--------------------------------------------------------\n");
}
root@jusse ~/systemtap# stap netif_receive_skb.stp
--------------------------------------------------------
0xffffffff8164dc00 : netif_receive_skb+0x0/0x90 [kernel]
0xffffffff8164e280 : napi_gro_receive+0xb0/0x130 [kernel]
0xffffffff81554537 : handle_incoming_queue+0xe7/0x100 [kernel]
0xffffffff815555d9 : xennet_poll+0x279/0x430 [kernel]
0xffffffff8164ee09 : net_rx_action+0x139/0x250 [kernel]
0xffffffff810702cd : __do_softirq+0xdd/0x300 [kernel]
0xffffffff8107088e : irq_exit+0x11e/0x140 [kernel]
0xffffffff8144e785 : xen_evtchn_do_upcall+0x35/0x50 [kernel]
0xffffffff8176c9ed : xen_hvm_callback_vector+0x6d/0x80 [kernel]
--------------------------------------------------------
root@jusse ~/source/systemtap# stap ./testsuite/systemtap.examples/network/nettop.stp
semantic error: not accessible at this address (pc: 0xffffffff8164dc00) [man error::dwarf]: identifier '$skb' at /usr/local/share/systemtap/tapset/linux/networking.stp:64:27
dieoffset: 0x5cb9e3e from unknown debug file for kernel
function: netif_receive_skb at /build/buildd/linux-lts-trusty-3.13.0/net/core/dev.c:3694
source: dev_name = kernel_string($skb->dev->name)
^
Pass 2: analysis failed. [man error::pass2]
root@jusse ~/source/systemtap# stap ./testsuite/systemtap.examples/network/nettop.stp
semantic error: unknown type in dereference: operator '->' at /usr/local/share/systemtap/tapset/linux/networking.stp:65:30
source: dev_name = kernel_string(skb->dev->name)
^
Pass 2: analysis failed. [man error::pass2]
root@jusse ~/source/systemtap# stap ./testsuite/systemtap.examples/network/nettop.stp
semantic error: 'struct sk_buff' is being accessed instead of a member: operator '@cast' at /usr/local/share/systemtap/tapset/linux/networking.stp:64:11
source: skb = @cast(pointer_arg(1), "struct sk_buff")
^
Pass 2: analysis failed. [man error::pass2]
root@jusse ~/source/systemtap# stap ./testsuite/systemtap.examples/network/nettop.stp
PID UID DEV XMIT_PK RECV_PK XMIT_KB RECV_KB COMMAND
0 0 eth1 7 357 0 14 swapper/0
0 0 eth0 0 12 0 0 swapper/0
3 0 eth1 0 2 0 0 ksoftirqd/0
9838 0 eth1 0 2 0 0 redis-server
8 0 eth1 0 1 0 0 rcuos/0
function deref:long(ptr:long)
%{
STAP_RETURN(*(void **)STAP_ARG_ptr);
%}
root@jusse ~/develop# cat cc_multi_pointer.c
#include
struct test {
int count;
};
int main(int argc, char *argv[])
{
struct test t = {.count = 5566};
struct test *pt = &t;
struct test **ppt = &pt;
printf("t.count: %d, pt->count: %d, ppt->count: %d\n", t.count, pt->count, (*ppt)->count);
return 0;
}
root@jusse ~/develop# gcc -Wall -g -o cc_multi_pointer ./cc_multi_pointer.c
root@jusse ~/develop# cat cc_multi_pointer.stp
probe process("./cc_multi_pointer").statement("main@./cc_multi_pointer.c:13")
{
printf("$t->count: %d, $pt->count: %d, $ppt->count: %d", $t->count, $pt->count, $ppt[0]->count);
}
root@jusse ~/develop# ./cc_multi_pointer
t.count: 5566, pt->count: 5566, ppt->count: 5566
root@jusse ~/develop# stap ./cc_multi_pointer.stp -c './cc_multi_pointer'
t.count: 5566, pt->count: 5566, ppt->count: 5566
$t->count: 5566, $pt->count: 5566, $ppt->count: 5566
root@jusse ~/develop# vim cc_multi_pointer.stp
root@jusse ~/develop#
root@jusse ~/systemtap# cat copy_process.stp
function getprocname:string(task:long)
%{
struct task_struct *task = (struct task_struct *)STAP_ARG_task;
snprintf(STAP_RETVALUE, MAXSTRINGLEN, "pid: %d, comm: %s", task->pid, task->comm);
%}
function getprocid:long(task:long)
%{
struct task_struct *task = (struct task_struct *)STAP_ARG_task;
STAP_RETURN(task->pid);
%}
probe kernel.function("copy_process").return
{
printf("copy_process return: %p, pid: %d, getprocname: %s, getprocid: %d\n", $return, $return->pid, getprocname($return), getprocid($return));
}
root@jusse ~/systemtap# stap -g copy_process.stp
copy_process return: 0xffff880039f61800, pid: 12212, getprocname: pid: 12212, comm: bash, getprocid: 12212
copy_process return: 0xffff880039f61800, pid: 12212, getprocname: pid: 12212, comm: bash, getprocid: 12212
copy_process return: 0xffff880039f63000, pid: 12213, getprocname: pid: 12213, comm: cc_epoll, getprocid: 12213
copy_process return: 0xffff880039f63000, pid: 12213, getprocname: pid: 12213, comm: cc_epoll, getprocid: 12213
copy_process return: 0xffff8800081a9800, pid: 12214, getprocname: pid: 12214, comm: cc_epoll, getprocid: 12214
copy_process return: 0xffff8800081a9800, pid: 12214, getprocname: pid: 12214, comm: cc_epoll, getprocid: 12214
copy_process return: 0xffff8800004d8000, pid: 12215, getprocname: pid: 12215, comm: cc_epoll, getprocid: 12215
copy_process return: 0xffff8800004d8000, pid: 12215, getprocname: pid: 12215, comm: cc_epoll, getprocid: 12215
copy_process return: 0xffff880000564800, pid: 12216, getprocname: pid: 12216, comm: cc_epoll, getprocid: 12216
copy_process return: 0xffff880000564800, pid: 12216, getprocname: pid: 12216, comm: cc_epoll, getprocid: 12216
copy_process return: 0xffff880000566000, pid: 12217, getprocname: pid: 12217, comm: cc_epoll, getprocid: 12217
copy_process return: 0xffff880000566000, pid: 12217, getprocname: pid: 12217, comm: cc_epoll, getprocid: 12217
root@jusse ~/systemtap# uname -a
Linux jusse 3.13.0-36-generic #63~precise1-Ubuntu SMP Thu Sep 4 22:28:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
root@jusse ~/systemtap# stap -V
Systemtap translator/driver (version 2.7/0.152, commit release-2.6-65-g89538c0b970d)
Copyright (C) 2005-2014 Red Hat, Inc. and others
This is free software; see the source for copying conditions.
enabled features: TR1_UNORDERED_MAP NLS