SystemTap工具的使用基础

systemtap工具的安装

准备工作

uname -a

查看当前内核版本是哪一个,然后使用

yum install kernel-devel

安装kernel debuginfo包

rpm -qi kernel-devel

找到内核构建的详细信息,然后去对应发布网站上找kernel-debuginfo和kernel-debuginfo-common包。

安装systemtap包

yum install systemtap

测试

完成安装后可以通过下面命令测试systemtap

stap -ve ‘probe begin { print(“hello world\n”) exit()}’

进行测试,看看systemtap有无安装成功。

systemtap常规命令

查看内核某个函数可以查看的target变量

下面命令演示查看__lookup_hash()函数返回时刻可以查看到的变量

stap -ve ‘probe kernel.function(“__lookup_hash”).return’ //查看__lookup_hash()函数返回时刻可以systemtap工具可以查看的target变量。
kernel.function(“__lookup_hash@fs/namei.c:1383”).return $return:struct dentry* $name:struct qstr* $base:struct dentry* $flags:unsigned int $need_lookup:bool

在上表中显示了lookup_hash在文件中的行号,显示了名为$return 的变量,其实这个return变量就是systemtap表示函数返回值的。而$name,$base,$flag我们对着linux源码看发现这是__lookup_hash的三个入参。
下面命令可以查看__lookup_hash函数入口可以查看的变量

stap -L ‘kernel.function(__lookup_hash)’

也可以通过statement方式查看内核符号表里有的__lookup_hash相关的行

stap -L ‘kernel.statement(“*”)’ |grep __lookup_hash

如果查找的内核函数位于某个模块里可以使用下面命令:

stap -L ‘module(“overlay”).function(“*”)’|grep ovl_lookup
stap -L ‘module(“overlay”).statement(“*”)’|grep ovl_lookup

查看用户态进程的可以stap的函数

通过下面命令可以查看到某个正在运行的进程的函数

stap -L ‘process(“/usr/bin/dockerd”).function(“*”)’|grep -i “syscall.Mount”
process(“/usr/bin/dockerd”).function(“syscall.Mount@/usr/local/go/syscall/syscall_linux.go:796”) $datap:uint8*
$source:struct string $target:struct string fstype:struct stringflags:uintptr data:struct stringerr:error

上例中看到找到了syscall.Mount函数,并且把它的所有参数和参数类型都打印了出来。
后面可以在stap脚本中,这个函数的上下文里直接使用这些参数,例如通过$source可以访问到参数source

使用systemtap打印目标函数的变量

systemtap支持print()和printf()函数,其中printf使用语法和c语言一致。支持%s,%d,%x格式

常用变量

内部变量名 功能 其他
$$locals 探测点上所有的本地变量(含参数) n/a
$$parms 探测点上函数的参数 n/a
$$returns 探测点上所有的返回值 只对return probe生效

变量的显示方式

在systemtap里凡是以$开头的变量都是目标变量,如果目标变量结构体指针或者结构体对象,那么可以使用->直接访问其成员。例如上例中:

$return->d_inode //就是__lookup_hash()返回值dentry* 的d_inode成员的值。

常规情况下,printf()打印target变量时刻,只打印其值。如果需要将其成员(指针类型的target需要将其指向的对象的成员展开)可以在target变量后面加$的方式例如:

$return$ //显示返回值指向的dentry所有成员。

一般情况下对struct的展开只会到成员值一级,如果相对成员内部继续展开可以在目标变量后面跟$$

if逻辑语句

在systemtap中支持逻辑if语句格式为:

if (expr) {
语句
}

逻辑语句支持以下比较
==,!=,>=,>,<,<=

systemtap的内置函数

用户空间下取得目标内的值

函数 含义 备注
User_char(address) 取得地址上的字符串值 Na
User_short(address) 取得short值 na
user_init(address) 取得int值 na
User_long(address) 取得long na
user_string(address) 取得string na
user_string_n(address) 取得string,最长n个byte na

用户空间下堆栈打印

stap -d /bin/ls --ldd -e ‘probe process(“ls”).function(“xmalloc”) {print_usyms(ubackstrace())}’ -c “ls -l”

上述例子对ls -l下的xmalloc进行堆栈回溯:
-d 可执行文件名
--ldd 指明共享库
-c “ls -l” 执行的子进程体

例子

内核态例子

例子1

下面例子将打印__lookup_hash中return返回dentry*里inode指向的i_ino子成员

stap -ve ‘probe kernel.function(“__lookup_hash”).return { if ($return!=0) { if($return!=-2) { if ($return->d_inode!=0){
printf(“return dentry:%x,dentry->d_inode->i_ino:%x”,$return,$return->d_inode->i_ino)}}}}’ -o zxy.txt

这一例子中-o zxy.txt的意思就是将结果写入文件zxy.txt中(默认输出到控制台)

例子2

下面例子将在内核中使用强制类型转换

stap -ve ‘probe module(“overlay”).statement(“ovl_lookup@fs/overlay/super.c:603”){ print_syms(backtrace()) if($dentry->d_inode)’ printf(“overlay_lookup 603: inode :%p,ino:%d \n”,@cast(@cast($dentry,”dentry”,”kernel<linux/dcache.h>”)->d_inode,”inode”,”kernel<linux/fs.h>”)->i_ino)

这里解释一下,内核中方法强制转换

@cast(p,”type_name”[,”module”])->member

例子三 嵌入c语言

在内核中使用systemtap时,可以嵌入c语言。c语言可以定义函数可以使用在该打点行上内核可以调用的函数,定义结构和引用头文件。

function get_mmCount:long()
%{
 struct task_struct *t=current;
if(t!=NULl && t->mm != NULL;
    STAP_RETVALUE =(long)atomic_read(&t->mm->mm_user);
else
  STAP_RETVALUE = 0xFFFF;
}%
 
probe kernel.statement(“get_task_mm@kernel/fork.c:782)
{
 printf(“get_task_mm call pid %d,ref count %d \n”,pid(),get_mmCount());
}

上述例子中在get_mmCount这个自定义函数中引用了内核fork.c 782行上可以使用的atomic_read函数和current这个全局变量

内核态小技巧

在用systemtap跟踪内核时使用堆栈打印命令,常常打印不出来另外模块的函数,这是因为这些模块没有被加载。可以在systemtap启动命令使用--all-modules 方法强制将所有模块符号加载起来。

用户态例子

下面例子对用golang写的dockerd进程syscall.Mount调用入口时刻打印syscall.Mount()函数的参数
source的string字段内容

probe process(“/usr/bin/dockerd”).function(“syscall.Mount”).call{
if($source->str)
printf(“source is %s Len %d \n”,user_string($source->str),$source->len)
print_usyms(ubacktrace())
}

下面例子打印golang写的dockerd进程xxx.Get函数返回时刻的参数情况

probe process(“/usr/bin/dockerd”).function(“github.com/docker/docker/daemon/graphdriver/overlay2.(*Driver).Get”).return{
printf(“return Parma :%s\n”,$$parms$)
}

使用systemtap抓golang

systemtap对golang支持不够完美,用户需要自己解析基本结构例如golang的string,array和slice这些都需要用户自己解析。string被systemtap识别为struct string,此结构systemtap可以识别的定义可以简化为:

Struct string{
int len
char* str
}

需要注意的是通过systemtap打印golang string的string->str会多打很多字符,因为string成员str并非按照c语言定义的字符串以\0表示字符串结束,我们只能结合string的字段len来获取精确的字符串内容
slice完全不被systemtap识别,我们可以将systemtap可以识别的slice简化为此种定义:

struct slice{
array
len
cap
}

其中array就是指向slice存储单元的首地址。
要是我们想获取helo=[]string{“hello”,”world”}这样的字符串slice的内容可以通过systemtap提供的@cast(addr,”type”,”file”)函数将某个地址强转为file中定义的type结构。具体来说可以如下做获取hello的内容

for(i=0;i<$helo->len;i++)
printf(“slice[%d]:%s\n”,i,user_string((&@cast($helo->array,”struct string”)[i])->str))

probe process(“/bin/docker-runc”).function(“github.com/opencontainers/runc/libcontainer/specconv.createDevices”).call{
          for(i=0;i<$spec->Linux->Device->len;i++){
             printf(“%d:Major %d n”,i,user_int((&@cast($spec->Linux->Devices->array,”github.com/opencontainers/runtime-spec/specs-go.Device”)[i])->Major))
         }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342