暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Dtrace 和 Systemtap 的语法对比

原创 zayki 2023-02-17
32871

《性能之巅》内对 Dtrace 和 Systemtap 的语法做了一个对比,对于学习二者是一个不错的资源,现整理如下。

1. DTrace To Systemtap

下面是一份 DTrace 转换成 Systemtap 的简易指南,包括如下几个部分

  1. 语法
  2. 探针
  3. 内置变量
  4. 函数
  5. 转换示例

2. 语法

DTrace Systemtap 描述
探针名 probe 探针名
探针名 {var[a] = } global var; probe 探针名 {var[a]=} systemtap 的全局变量必须事先声明
/predicate/ {if (test) {}}
@a = count(x) printa(@a) a «< x print(count(a)) 聚合变量使用
arg0 …. agrN args[0] … args[N] 目标变量 $var 全局变量 @var(“file_stat@fs/file_table.c”) 如何获取探针中的变量

3. 探针

DTrace Systemtap 描述
BEGIN dtrace:::BEGIN begin probe begin
END dtrace:::END end probe end
syscall:::entry syscall.*
syscall:::return syscall.*.return
syscall::read:entry syscall.read
syscall::read:return syscall.read.return
sched:::on-cpu scheduler.cup_on
sched:::off-cpu scheduler.cpu_off
profile:::profile-100 timer.profile
profile:::tick-10s timer.s(10)
fbt::foo:entry kernel.function(“foo”)
fbt::foo:return kernel.function(“foo”).return
io:::start ioblock.request
io:::done ioblock.end

4. 内置变量

DTrace Systemtap 描述
execname execname() 执行在CPU上的进程名
uid uid() 执行在CPU上的用户ID
pid pid() 执行在CPU上的进程PID
cpu cpu() 进程当前所在的 CPU
timestamp gettimeofday_s() 自启动以来的纳秒数
vtimestamp CPU上的线程时间,单位是纳秒
arg0…N 目标变量 探针参数(uint64_t)
args[0]…[N] 目标变量 探针参数(类型化的)
curthread task_current() 指向当前线程内核结构的指针
probefunc probefunc() 打印探针所在位置的内核函数名称
probename 当前探针名称
curpsinfo 当前进程信息
curpsinfo->pr_psargs cmdline_str() 进程启动的命令
$target target() 返回stap,dtrace 通过命令行设置的进程 pid

5. 函数

Dtrace Systemtap 描述
stringof(addr) kernel_string() 返回来自内核空间的字符串
copyinstr(addr) user_tring() 返回用户空间地址的字符串 内核会执行一次从用户空间到内核空间的复制
stack(count) print_backtrace() 打印内核级别栈追踪
ustack(count) print_ubacktrace() 打印用户级别栈追踪
exit(status) exit() 退出DTrace并返回状态
quantize(value) @hist_log() 用 2 的幂次方直方图统计 value
lquantize(value,min,max,step) @hist_linear() 用给定最下值,最大值和步进值做线性直方图记录 value

6. 转换示例

列出系统调用入口探针

> dtrace -ln syscall:::entry

> stap -l 'syscall.*'
syscall.accept
syscall.accept4
syscall.access

统计 read() 返回大小

# dtrae1: arg1 作为系统调用 read() 的返回
> dtrace -n ‘syscall::read:return { @bytes = quantize(arg1); }’

# stap1: 查看 stap 目标变量,return 是read() 的返回
> stap -L 'syscall.read.return'
syscall.read.return name:string retval:long retstr:string
return:long int $fd:long int $buf:long int $count:long int $ret:long int

# stap2:
> stap -e ‘global bytes;probe syscall.read.return { bytes <<< $return }
probe end { print(@hist_log(bytes)); }’

根据进程名统计系统调用

# dtrace1:
> dtrace -n ‘syscall:::entry { @x[execname] = count(); }’

# stap1: 不便阅读
> stap -e 'global x; probe syscall.* { x[execname()] <<< 1 } '

# stap2: 格式化输出
> stap -ve ‘global x; probe syscall.* { x[execname()] <<< 1 }
probe end { foreach (k in x+) {printf("%-36s %8d\n", k, @count(x[k])); } }’

对 PID 为 123 的进程,根据系统调用名统计系统调用次数

# dtrace1: pid
> dtrace -n ‘syscall:::entry /pid == 123/ { @x[probefunc] == count(); }’

# stap1:
> stap -ve ‘global x; probe syscall.* { if (pid() == 123) { x[probefunc()] <<< 1 }; }
probe end { foreach (k in x+) {printf("%-36s %8d\n", k, @count(x[k])); } }’

对 httpd 进程,根据系统调用名统计系统调用次数

# dtrace1: execname
> dtrace -n ‘syscall:::entry /execname == “httpd”/ { @x[probefunc] == count(); }’

# stap1:
> stap -ve ‘global x; probe syscall.* { if (execname() == “httpd”) { x[probefunc()] <<< 1 }; }
probe end { foreach (k in x+) {printf("%-36s %8d\n", k, @count(x[k])); } }’

用进程名和路径名跟踪文件的open()

# dtrace
> dtrace -l ‘syscall::open.entry { printf("%s, %s", execname, copyinstr(arg0)); }’

# stap
> stap -ve ‘probe syscall.open { filename = user_string_quoted($filename);
printf("%s %s\n", execname(), filename); }’

对 mysqld 进程统计 read() 延时

# dtrace1:
> dtrace -n ‘syscall::read:entry /execname == “mysqld”/ {self->ts = timestamp;}
syscall::read:return /self->ts/ { @[“ns”] = quantize(timestamp - self->ts);self->ts=0}’
> stap1:
gobal t,s;
probe syscall.read {
if (execname() == “mysqld”){
t[tid()] = gettimeofday_ns();
}
}

probe syscall.read.return {
if (t[tid()]){
s <<< gettimeofday_ns() - t[tid()];
delete t[tid()];
}
}

probe end {
printf(“ns\n”);
print(@hist_log(s))
}

> stap2: 在.return探针中,有一个特殊的操作符@entry,用于存储该探针的入口处的表达式的值
> stap -ve ‘global s; probe syscall.read.return {if (execname() == “mysqld”)
{s <<< gettimeofday_ns() - @entry(gettimeofday_ns());}}
probe end{print(@hist_log(s))}’

根据进程名和参数跟踪新进程

# dtrace:
> dtrace -n ‘proc::exec-success { trace(curpsinfo->pr_psargs) }’

# stap
> stap -ve ‘probe process.begin { printf("%s\n", cmdline_str()) }’

以100Hz对内核栈采样

# dtrace
> dtrace -n ‘profile-100 { @[stack()]=count() }’

# stap
> stap -e ‘global s; probe timer.profile { s[backtrace()] <<< 1 }
probe end {foreach (i in s+){ print_stack(i);
printf("\t%d\n", @count(s[i]));}}’
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论

目录
  • 1. DTrace To Systemtap
  • 2. 语法
  • 3. 探针
  • 4. 内置变量
  • 5. 函数
  • 6. 转换示例
    • 列出系统调用入口探针
    • 统计 read() 返回大小
    • 根据进程名统计系统调用
    • 对 PID 为 123 的进程,根据系统调用名统计系统调用次数
    • 对 httpd 进程,根据系统调用名统计系统调用次数
    • 用进程名和路径名跟踪文件的open()
    • 对 mysqld 进程统计 read() 延时
    • 根据进程名和参数跟踪新进程
    • 以100Hz对内核栈采样