• Original:阿里 姜弋内核月谈译者:姜弋译者注:原作者是大名鼎鼎的性能分析专家:Brendan Gregg,现在工作在Netflix,之前工作在Sun,在Sun公...
        

    Original:阿里 姜弋内核月谈

    译者: 姜弋

    译者注:原作者是大名鼎鼎的性能分析专家:Brendan Gregg,现在工作在Netflix,之前工作在Sun,在Sun公司的时候,他就做了大量的性能分析和tracing相关的工作,在Sun的Solaris上存在一种传说中的性能分析和Debug神器: Dtrace,然而,可惜的是,在我们现在的Linux操作系统上并没有Dtrace神器(这可能是因为Dtrace是从Soloris操作系统的衍生品无法迁移到别的操作系统上),Brendan Gregg 在Netflix后,继续利用他的业余时间,利用他曾经在Soloris上的性能分析经验,和对Dtrace工具的理解,研发基于Linux操作系统上的上类似于Dtrace的工具,曾经他在早期的kernel版本上基于perf研发了perf-tools工具,后面在eBPF进入kernel后,开始基于eBPF做性能工具研发的工作,比如bcc工具集,最近又参与了bpftrace的工具。本文主要是Brendan Gregg在介绍 bpftrace在2018年的开发进展,以及对bpftrace的介绍和对Dtrace的区别介绍。

    bpftrace (DTrace 2.0) for Linux 2018

    告诉大家一个好消息,bpftrace开源啦,这对DTrace粉丝来说是一个绝对的好消息,其项目作者依然是Alastair Robertson, bpftrace是一个开源的高级的tracing(跟踪)工具(high-level tracing front-end),可以让你自定义的方式跟踪。它就像是DTrace version 2.0:实用,从eBPF virtual machine中编译出来。eBPF (extended Berkeley Packet Filter) 是近期kernel上比较热门的一个工程。也正在为兼容BSD而开发,BSD项目也是BPF项目的发源地。

    截图:追踪PID 181的read系统调用的延迟:

    其他单行命令操作(one-liners):

     1# New processes with arguments
    2bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv); }'
    3
    4# Files opened by process
    5bpftrace -e 'tracepoint:syscalls:sys_enter_open { printf("%s %s\n", comm, str(args->filename)); }'
    6
    7# Syscall count by program
    8bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[comm] = count(); }'
    9
    10# Syscall count by syscall
    11bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[probe] = count(); }'
    12
    13# Syscall count by process
    14bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[pid, comm] = count(); }'
    15
    16# Read bytes by process:
    17bpftrace -e 'tracepoint:syscalls:sys_exit_read /args->ret/ { @[comm] = sum(args->ret); }'
    18
    19# Read size distribution by process:
    20bpftrace -e 'tracepoint:syscalls:sys_exit_read { @[comm] = hist(args->ret); }'
    21
    22# Disk size by process
    23bpftrace -e 'tracepoint:block:block_rq_issue { printf("%d %s %d\n", pid, comm, args->bytes); }'
    24
    25# Pages paged in by process
    26bpftrace -e 'software:major-faults:1 { @[comm] = count(); }'
    27
    28# Page faults by process
    29bpftrace -e 'software:faults:1 { @[comm] = count(); }'
    30
    31# Profile user-level stacks at 99 Hertz, for PID 189:
    32bpftrace -e 'profile:hz:99 /pid == 189/ { @[ustack] = count(); }'

    怎么样?看起来很眼熟吧?没错,灵感就是从Solaris上的Dtrace工具而来(传承Dtrace的一行搞定(专业术词:one-liners)的精神),bpftrace将会继续沿袭Dtrace的思想,在后面的milestone中。

    bpftrace

    我第一次提及 bpftrace (aka BPFtrace)工作计划,是在曾经的一次演讲上:DTrace for Linux 2016, 当时,我也介绍了Linux Kernel的eBPF的孵化过程,同时也介绍了我当时在做的bcc (BPF Complier Collection)项目,一个让我在新的架构上重写Dtrace工具集的项目。bcc虽然很强大,但是要实现一个小工具就需要写一个小脚本,还是有点小麻烦(写一个小脚本要写的代码太多,不能用一行命令表达),因此,我准备额外分享另外一个高级工具bpftrace给大家。bpftrace特点在于,当希望编写小工具的时候,可以一行命令表达完整。

    Alastair最近在开发一些框架,并且适配了tracepoints(可以做到one-linner),昨天把kprobes也适配好了,举个例子:

     1# cat path.bt
    2#include <linux/path.h>
    3#include <linux/dcache.h>
    4
    5kprobe:vfs_open
    6{
    7    printf("open path: %s\n", str(((path *)arg0)->dentry->d_name.name));
    8}
    9
    10# bpftrace path.bt
    11Attaching 1 probe...
    12open path: dev
    13open path: if_inet6
    14open path: retrans_time_ms

    我,Willian Gaspar, 和 Matheus Marchini,也提供了一些拓展和bugfix的patch,查看没有处理的问题issue list,查看指南, 查看之前的提交。

    bpftrace使用了已有的Linux kernel中的基础设施(eBPF, kprobes, uprobes, tracepoints, perf_events),同时也有bcc的库,至于bpftrace内部实现,使用了 lex/yacc parser 把程序转化为AST,再到 llvm IR actions, 最后变成BPF。

    640?wx_fmt=png

    mage

    帮助大家学习bpftrace,我创建了几个“参考”

    • one-liners tutorial

    • reference guide

    bpftrace的“单行命令手册” 是基于 FreeBSD DTrace Tutorial, 我觉得这是一个非常高效的方法去学习bpftrace。

    自从我帮助参与开发bpftrace,我发现我也会时不时的引入一些bugs,可能bpftrace会SIGSEGV,或者,coredump?也许吧,我们已经尽力的修复了好多这些问题。也有可能bpftrace会输出一些因为和LVM 或者 BPF verifier不兼容,不适配导致的错误提示(用-v了)?如果是的话,请让我知晓,也许bpftrace会让kernel panic?呵呵,这个很少很少了,我都没有见过,自从使用了eBPF组件到现在这么多年来。如果出现了用户态的bugs,bpftrace的crash或者stuck,hang之类的,需要被killed才可以的情况,请在github上提交一个issue,让我们也知道此事,当然,如果你可以,也欢迎你可以帮我们修复这些bug。bpftrace到现在我们的测试集里已经有200多个test case了。

    bpftrace/eBPF vs DTrace equivalency

    这一个章节,我会写一些“例子”,来对比“bpftrace/eBPF” 和 “Dtrace”,但是,注意一点,并不是说kernel中的eBPF就是仅仅实现了一个 “Linux版的Dtrace” 哦,eBPF还有很多的用处。

    eBPF是其作者 Alexei Starovoitov 在PLUMgrid工作的时候(现在在Facebook)创建,是打算做成一个 Kernel中 通用的 “虚拟机”的,可以帮助实现软件定义网络的场景。很明显,它还有很多其他的场景,比如:eXpress Data Path, container security and networking, iptables, infrared decoding, intrusion detection, hyperupcalls, 和 FUSE performance. 实际上,eBPF直接使用是很费劲的,所以,才产生了bcc上层的python库。

    bpftrace就是希望可以成为一个更加高级的,可以达到“手到擒来”的工具,bpftrace参考了Dtrace的实现,我们已经往bpftrace中添加了很多功能,我也知道,我们还没有做到Dtrace的所有功能,比如:custom aggregation printing, shell arguments, translators, sizeof(), speculative tracing, and forced panic,我们现在添加的,只是我们现在需要的。

    Dtrace同样也并不是包含了bpftrace的所有功能,比如:bpftrace的参数,可以在shell中使用,如果有些功能bpftrace做不到,那就用bcc,它们的关系就像是:

    640?wx_fmt=png

    mage

    同样,也有一些事儿,eBPF可以做到,但是Dtrace不能的。尤其是,可以保存和接受stack trace作为变量的能力。我已经耦合到bpftrace中了,并且使用这个工具,尝试分析了几个fd泄漏的问题,这个工具大概是这样(fdleak.bt):

     1kretprobe:__alloc_fd
    2{
    3    $fd = retval;
    4    @alloc_stack[comm, pid, $fd] = ustack;
    5}
    6
    7kprobe:__close_fd
    8{
    9    $fd = arg1;
    10    delete(@alloc_stack[comm, pid, $fd]);
    11}

    它会打印那些分配但是没有被释放的fd的user-level stacks, 它是在分配fd的时候保存栈信息,在free fd的时候,将对应的栈信息删除。当bpftrace退出的时候,它会打印剩下的@ alloc_stack: 也就是没有被释放掉的fd的stack信息。我还写了一个更长的脚本,包含了timestamp信息的,所以我抓取更长的数据,你可以将这个技术复用到任何一种object分配的场景,或者给你自己的泄漏检测工具增添色彩。如果是Dtrace,这需要dump出所有的stacks到一个文件中,然后事后处理,这个会引起更大的处理开销。eBPF可以在kernel中做这些事情,在kernel中对数据进行过滤,这样效率更高。

    还有一个更重要的案例,在我的另外一个文章off-wake time中有所展示。

    DTrace和bpftrace对比清单

    如果你已经了解Dtrace,这里会花费你10分钟时间,看看Dtrace和bpftrace之间的对比,这里列出主要的区别,截止到2018.08:

    640?wx_fmt=png

    这个清单当然没有罗列bpftrace的所有的功能,比如 access aggregation elements, user-level tracing system wide, save stacks as variables, 更多可以看这里reference_guide

    Dtrace和bpftrace的脚本对比

    这里,我以我在Dtrace上曾经实现的工具(seeksize.d)为例子, 比较一下Dtrace和bpftrace的脚本在实现上的差异:

     1DTrace
    2
    3#pragma D option quiet
    4
    5/*
    6 * Print header
    7 */

    8dtrace:::BEGIN
    9{
    10    printf("Tracing... Hit Ctrl-C to end.\n");
    11}
    12
    13self int last[dev_t];
    14
    15/*
    16 * Process io start
    17 */

    18io:genunix::start
    19/self->last[args[0]->b_edev] != 0/
    20{
    21    /* calculate seek distance */
    22    this->last = self->last[args[0]->b_edev];
    23    this->dist = (int)(args[0]->b_blkno - this->last) > 0 ?
    24        args[0]->b_blkno - this->last : this->last - args[0]->b_blkno;
    25
    26    /* store details */
    27    @Size[pid, curpsinfo->pr_psargs] = quantize(this->dist);
    28}
    29
    30io:genunix::start
    31{
    32    /* save last position of disk head */
    33    self->last[args[0]->b_edev] = args[0]->b_blkno +
    34        args[0]->b_bcount / 512;
    35}
    36
    37/*
    38 * Print final report
    39 */

    40dtrace:::END
    41{
    42    printf("\n%8s  %s\n""PID""CMD");
    43    printa("%8d  %S\n%@d\n", @Size);
    44}
     1bpftrace
    2
    3/*
    4 * Print header
    5 */

    6BEGIN
    7{
    8    printf("Tracing... Hit Ctrl-C to end.\n");
    9}
    10
    11/*
    12 * Process io start
    13 */

    14tracepoint:block:block_rq_insert
    15/@last[args->dev]/
    16{
    17    // calculate seek distance
    18    $last = @last[args->dev];
    19    $dist = (args->sector - $last) > 0 ?
    20        args->sector - $last : $last - args->sector;
    21
    22    // store details
    23    @size[pid, comm] = hist($dist);
    24}
    25
    26tracepoint:block:block_rq_insert
    27{
    28    // save last position of disk head
    29    @last[args->dev] = args->sector + args->nr_sector;
    30}
    31
    32/*
    33 * Print final report
    34 */

    35END
    36{
    37    printf("\n@[PID, COMM]:\n");
    38    print(@size);
    39    clear(@size);
    40    clear(@last);
    41}

    将功能从Soloris上移植到Linux上的主要工作可不是简简单单的语法上移植,而是研究和测试以找到等效的Linux跟踪点及其参数。主要的工作与追踪语言无关。

    既然我移植了它到Linux上,但是我觉得有点奇怪。我猜想这就回答了一个问题:磁盘在寻找吗?但它实际上回答了一个棘手的问题:应用程序是否导致磁盘搜索?我在2004年写了seeksize.d,所以我必须回想一下那个时候才能理解它。当时,我已经可以通过解释iostat(1)输出来判断磁盘是否正在寻找:看到高磁盘延迟,但I/O很小。iostat(1)无法告诉我的是,这是因为使用磁盘的应用程序相互竞争,还是应用程序本身正在应用随机工作负载。这就是我写的seeksize.d要回答的问题,一个指导进一步优化的答案:您是需要在不同的磁盘上分离应用程序,还是调整一个应用程序的磁盘工作负载。

    为何我花那么久时间做这件事?

    我曾经告诉很多工程师和一些公司关于做一个在Linux上的高级trace工具,我认为这个是Linux商业环境下一个比较有趣的课题,所以,我才花那么长的时间来完成它:

    1. Linux isn't a company

    在开发DTrace时,Sun Microsystems的首席执行官Scott McNealy喜欢说“一箭双雕”。如此之多,以至于员工们曾经在他的办公室窗户上安装了一个巨大的箭头,就像愚人节的笑话一样。在Linux中,所有的工作都没有落后于一个跟踪箭头,而是分成14个(systemtap、lttng、ftrace、perf_events、dtrace4linux、oel dtrace、ktap、sysdig、intel pin、bcc、shark、ply和bpftrace)。如果Linux是一家公司,管理层可以合并重叠的项目,并专注于一个或两个跟踪程序。但事实并非如此。

    2. Linux won

    Linux放弃了自己的动态跟踪实现(DProbes,2000年),为Sun创造了一个开发自己的竞争特性的机会。Sun拥有数十亿的收入,无数员工致力于使DTrace成功(包括销售、营销、教育服务等),Linux不存在这种情况。Linux已经赢了,而且没有公司提供任何真正的资源来构建一个有竞争力的跟踪程序,只有一个例外:RedHat。

    3. Most funding went into SystemTap

    询问一位RHEL客户有关Dtrace的情况,他们可能会说他们几年前就有了:systemtap。然而,SystemTap从来没有完全合并到Linux内核中(部分是这样的,比如uprobes),所以作为一个脱离树的项目,它需要维护才能工作,而RedHat只为RHEL做了这一点。作为一个Ubuntu用户,试着向RedHat抱怨:他们经常会说“切换到RHEL”。你能怪他们吗?问题是,这是唯一真正的钱在桌上建立一个LinuxDTrace(特别是自从红帽拿起曾经Sun公司的帐户,谁想要DTrace),它进入了SystemTap。

    4. Good-enough solutions

    另一个障碍是足够好的解决方案。一些公司已经知道如何让SystemTap(或LTTNG、FTrace或Perf)工作得足够好,并且很高兴。他们希望ebpf/bcc/bpftrace完成吗?当然。但它们不会帮助开发它们,因为它们已经有了足够好的解决方案。一些公司很乐意使用我的ftrace性能工具或我的BCC工具,所以我自己以前的工程工作给了他们一个不帮助构建更好的东西的理由。

    5. CTF/BTF

    Dtrace是在Solaris上构建的,它已经有了紧凑的类型格式来提供所需的结构信息。Linux有dwarf和debuginfo,与ctf不同的是,它并不常见。这阻碍了开发真正的类似DTrace的跟踪器。直到最近,在Linux4.18版本中,我们是否已经有了Linux:BPF类型格式(BTF)的CTF技术。

    默认安装

    值得一提的是,Dtrace是Solaris上的默认安装。这确实有助于采用,因为客户没有选择!现在想象一下,要使bpftrace成为所有Linux发行版上的默认安装,需要做什么。我认为这是一个长期的尝试,这意味着Linux可能永远不会拥有与Solaris上DTrace相同的体验。但这是一个可以帮助您的领域:请让您的发行版维护人员在默认情况下包括bpftrace(加上bcc和sysstat)。这些是危机工具,通常用于分析遇到紧急性能问题的系统,安装它们的时间(“apt-get-update;apt-get-install…”)可能意味着问题在您有机会看到之前就消失了。

    Dtrace工具如何?

    仅仅因为Linux有了ebpf,并不能使dtrace一夜之间成为一个糟糕的工具。它在拥有它的操作系统上仍然有用,并且任何具有它的操作系统都可以很好地为将来升级到ebpf做好准备,ebpf可以重用内部组件,比如提供者工具。工作已经开始将ebpf带到bsd,bpf就是在那里产生的。

    值得让那些花时间学习dtrace的人放心的是,我不认为时间浪费了。使用dtrace或bpftrace最困难的部分是知道如何处理它,遵循解决问题的方法。一旦你进入了一些复杂软件的内部,对于一个紧迫的生产问题,无论你输入quantize()还是hist()都是最不重要的问题。在调试dtrace的问题时,也可以帮助bpftrace。

    ply工具如何?

    ply, 作者是: Tobais Waldekranz , 另外一种到BPF的后端(front-end), 我喜欢它,虽然这个项目还没有完成,但是,我发现了很多有意思的区别和bpftrace比较起来: ply执行指令很直接,bpftrace使用了llvm的IR API, ply使用了C语言,bpftrace是C++。

     1# ply -A -c 'kprobe:SyS_read { @start[tid()] = nsecs(); }
    2    kretprobe:SyS_read /@start[tid()]/ { @ns.quantize(nsecs() - @start[tid()]);
    3        @start[tid()] = nil; }'
    42 probes active
    5^Cde-activating probes
    6[...]
    7@ns:
    8
    9    [ 512,   1k)           3 |########                        |
    10    [  1k,   2k)           7 |###################             |
    11    [  2k,   4k)          12 |################################|
    12    [  4k,   8k)           3 |########                        |
    13    [  8k,  16k)           2 |#####                           |
    14    [ 16k,  32k)           0 |                                |
    15    [ 32k,  64k)           0 |                                |
    16    [ 64k, 128k)           3 |########                        |
    17    [128k, 256k)           1 |###                             |
    18    [256k, 512k)           1 |###                             |
    19    [512k,   1M)           2 |#####                           |
    20[...]

    感谢

    bpftrace是由 Alastair Robertson创建的项目,同时在eBPF和bcc项目中做了大量的贡献,可以看看DTrace for Linux 2016 里的感谢部分有另外对Dtrace和早期的tracing工作的对其他人的贡献和鸣谢。如果您在使用的是bpftrace,您使用了Alexei Starovoitov在(ebpf)中开发的代码、包括:Brendan Blanco(libbpf)、我自己、Sasha Goldshtein(USDT)和其他人开发的大量代码。有关代码的lib库疑问,请参阅bcc。Facebook还有许多工程师在BPF和BCC上工作,他们使其成为当今成熟的技术。

    bpftrace本身有一种类似Dtrace的语言,以及awk和c。这不是eBPF上高级语言的第一次尝试:第一次是由Jovi Zangwei开发的Shark,然后是Tobais Waldekranz开发的ply,然后由Alastair Robertson开发的bpftrace。甚至在某种程度上,Richard Henderson正在开发基于eBPF作为SystemTap的后端的工作。感谢所有从事这些工作的人,以及我们使用的所有其他技术。

    最后,感谢Netflix,它为我提供了一个很好的支持环境,帮助我开发和贡献各种技术,包括BPF。

    结局

    自从四年前eBPF开始并入内核以来,它改变了Linux跟踪技术的格局。我们现在有了lib、tools和为eBPF构建的high-level front-end:bpftrace。bpftrace是性能分析利器,可以帮助您分析其他工具做不到的事情。它是对BCC的补充:BCC非常适合于复杂的工具,而bpftrace非常适合于“一行可以表达的命令”(one-liners)。在这篇文章中,我描述了bpftrace以及它如何与Dtrace进行比较。在我的下一篇文章中,我将更加关注bpftrace。

    更多阅读:

    • (https://github.com/iovisor/bpftrace)

    • (https://github.com/iovisor/bpftrace/blob/master/docs/tutorial_one_liners.md)

    • (https://github.com/iovisor/bpftrace/blob/master/docs/reference_guide.md)

    • (https://github.com/iovisor/bcc)

    bpftrace的作者Alastair第一次关于 bpftrace 的演讲 Tracing Summit 在爱丁堡, Oct 25th.

    如果想使用bpftrace,你需要使用比较新的内核版本(4.9以上) 关于安装可以看看bcc repo中的Install部分,或者看看:(https://github.com/iovisor/bpftrace/blob/master/INSTALL.md#ubuntu) 提供的安装包。(如果你是工作在Netflix,可以使用nflx-bpftrace安装包),最后,你会基于bpftrace做什么呢?

    640?wx_fmt=png


    查看我们精华技术文章请移步:

    Linux阅码场原创精华文章汇总

    扫描二维码关注"Linux阅码场"

    640?wx_fmt=png

    展开全文
  • dtrace4linux是 Sun DTrace 的一个用户端口和 Linux 的核心代码。在这个构建中没有触及到 linux 核心代码,但它创建了一个动态的可加载的内核模块。这避免了协议问题和允许人们按照他们的意愿加载和更新 dtrace。 ...

    dtrace4linux是 Sun DTrace 的一个用户端口和 Linux 的核心代码。在这个构建中没有触及到 linux 核心代码,但它创建了一个动态的可加载的内核模块。这避免了协议问题和允许人们按照他们的意愿加载和更新 dtrace。

    这个项目的目标是使 DTrack 对 Linux 平台可用,通过开放给所有人,他们可以用它来优化他们的系统和工具,而作为回报,我可以从他们的工作中受益。

    安装:

    $ tools/get-deps-arch.sh    # if using ArchLinux
    $ tools/get-deps.pl         # if using Ubuntu
    $ tools/get-deps-fedora.sh  # RedHat/Fedora
    
    $ make all
    $ make install
    $ make load           (need to be root or have sudo access)
    展开全文
  • 简介: 本系列文章详细地介绍了一个 Linux 下的全新的调式、诊断和性能测量工具 Systemtap 和它所依赖的基础 kprobe 以及促使开发该工具的先驱 DTrace 并给出实际使用例子使读者更进一步了解和认识这些工具。...

    简介: 本系列文章详细地介绍了一个 Linux 下的全新的调式、诊断和性能测量工具 Systemtap 和它所依赖的基础 kprobe 以及促使开发该工具的先驱 DTrace 并给出实际使用例子使读者更进一步了解和认识这些工具。本文是该系列文章之二,它详细地讲解了 DTrace 的原理。本系列文章之一讲解了 kprobe 的原理、编程接口、局限性和使用注意事项并给出实际使用示例帮助读者理解和认识 kprobe。本系列文章之三讲解了 Systemtap 的原理,以及 Systemtap 与 DTrace 比较,并通过一个例子向读者展示 Systemtap 的工作机理。


    一、DTrace 简介

    DTrace是一个强大的动态跟踪框架,它允许管理员、开发者和服务团队精确地回答关于操作系统和用户程序的任何问题。用户可以使用它管理成千上万个探测点,为每一个探测点指定执行条件(Predicates)和执行的动作(Actions),动态管理跟踪缓存和探测点开销。用户通过它能够对正在运行的系统跟踪来查看问题,也可以根据系统崩溃时产生的dump数据来查看问题。开发人员则可以实现新的提供者(Provider)和消费者(Consumer)以及配置管理探测点的工具。

    DTrace对探测点上下文没有任何限制,也就说可以对任何函数进行探测,这是因为DTrace框架是完全非阻塞的,它基本上没有显式或隐式地调用内核其他功能。

    Dtrace主要包含以下组件:

    消费者(Consumer)

    消费者是一个用户态应用,如dtrace,它能够通过Dtrace框架提供的接口库来访问DTrace内核组件

    提供者(Provider)

    提供者实现作内核模块,每一个模块建立某一种类的探测点,消费者能够使能提供者实现的任何探测点,并且可以把任何动作(action)绑定到这些探测点。提供者也能根据用户的跟踪请求创建新的探测点。

    DTrace已经实现了16个提供者(当然用户可以实现自己的提供者),包括dtrace、lockstat、profile、fbt(Function Boundary Tracing)、syscall、sdt(Statcially Defined Tracing)、sysinfo、vminfo、proc、sched、io、mib、fpuinfo、pid、plockstat和fasttrap。

    探测点(Probes)

    探测点是由提供者创建的用于标识要探测的模块和函数。每一个探测点有一个名字。每一个探测点可以用四元组provider:module:function:name来标识,它也有独一无二的整数标识符。

    预测(Predicates)

    预测是一个用/括起来的表达式,类似于条件,它在探测点命中时计算以决定关联的动作是否被执行。预测是主要的条件结构,在D脚本中的复杂的控制流都是用它来实现的。对于任何探测点,该部分可以被完全省略,这时,关联到探测点的动作(action)将在任何一次命中后都被执行。预测必须是一个可计算的表达式。在D语言中,0值解释为假,1解释为真。

    动作(Actions)

    动作是用D语言编写的一些语句,它被Dtrace虚拟机在内核中执行。关联到探测点的动作会在该探测点命中时执行。一般地,动作用来记录指定的系统状态信息,但是它也能改变系统状态,这种动作称为破坏性动作(destructive action),缺省情况下不允许这种动作。

    D脚本语言

    D语言类似于awk脚本,用户使用它来编写动作和预测。

    DTrace可以被用于性能监视、用户进程跟踪、匿名跟踪和推测跟踪(匿名跟踪没有消费者,这只能被超级用户执行,而且至多只能有一个匿名跟踪;推测跟踪则是指当探测点被命中后,动作是根据条件来执行的,predicates被用于实现推测跟踪),并且在没有使能跟踪的情况下没有任何开销,对系统性能没有任何影响。

    二、DTrace原理

    DTrace的核心组件全部在内核中实现,包括探测点处理、缓存以及instrumentation,用户态进程作为DTrace消费者(consumer)通过利用DTrace库能够和内核中的DTrace组件进行通信,DTrace提供的应用工具(dtrace就是一个典型的消费者,只是它使得用户可以非常方便地使用DTrace提供的任何功能)。DTrace框架不执行任何instrumentation,它只是负责指派instrumentation给对应的提供者(provider)。当DTrace核心组件指令提供者执行instrumentation时,提供者自己确定要instrument的点,然后回调DTrace框架提供的接口来创建探测点。为了创建一个探测点,提供者需要指定模块名、函数名以及探测点名。

    每一个探测点用一个独一无二的四元组表示(简介部分已经讲到),四元组包括提供者、模块、函数名和探测点名,创建的探测点并不会被立即执行,它还需要消费者使能(探测点也可以被消费者失效),只有使能的探测点才会被执行。DTrace框架提供的探测点创建接口函数在执行成功时会给提供者返回一个探测点标识符。这些创建的探测点会通知给消费者,消费者能使能它感兴趣的探测点,当使能探测点时,DTrace将创建一个使能控制块(ECB -- Enabling Control Block)并绑定到该探测点,如果该探测点原来是失效的(即没有其它ECB绑定到该探测点),DTrace还将调用该探测点的提供者来真正使能该探测点(当然,如果该探测点已经是使能的,即它的ECB链不为空,这个操作是不必要的)。当CPU执行到一个使能的探测点(即运行到提供该探测点的提供者)时,对应的提供者会把控制传递到DTrace框架的入口并传递探测点的标识符作为第一个参数,DTrace框架得到控制后会失效当前CPU上的中断,然后执行该探测点ECB链上的每一个ECB指定的活动(一个探测点可以绑定多个动作),然后使能中断并把控制返回到该提供者。提供者不需要考虑如何多路复用处理一个探测点的多个消费者,ECB能很好地处理它。

    每一个ECB可以有预测或条件(predicates),如果ECB有预测或条件,那么动作只有在该条件满足时才被执行。每一个ECB可以有多个动作,它们都会在条件满足时执行。如果动作要求保存一些数据,数据将被保存到对应于相应的消费者的缓存。动作不可以保存数据到内核内存,不可以修改寄存器,也不可以对系统状态做任何变化(破坏性动作除外,那要求用户必须是特权用户)。

    DTrace为每一个DTrace消费者在每一个CPU上分配一些内核缓存,消费者状态指向该缓存,消费者的所有ECB都有一个指向消费者状态的指针。当一个ECB的动作要求保存数据时,数据就被保存到这些缓存中,一个ECB保存的数据量是一个常数,不同的ECB该常数可以不同。在处理ECB之前,会核对那些缓存看是否有足够的空间保存数据,如果没有足够的空间,对应于该缓存的一个丢失计数器会加1,该ECB上的所有动作将被跳过。消费者需要定期地读取缓存中的数据,否则会导致数据丢失。缓存实现完全是免锁的,即消费者读的同时,ECB的动作仍能在需要时立刻保存数据,不必锁等待。它是这样来实现的:每一个CPU上有两个缓存,一个是活动的(active),一个是非活动的(inactive),当消费者想读取指定CPU的缓存时,两个缓存被交换,即活动的变成非活动的,非活动的变成活动的,这个交换操作可以在极其短的时间内完成,并使用中断失效来保护(ECB的动作保存数据或其它消费者的读取操作需要访问这些缓存,因而必须被同步)。在交换之后,消费者从不活动的缓存读取数据,而ECB能够向活动的缓存写数据。在每一个CPU上的缓存中,每一次的数据保存都会把探测点标识符(EPID -- Enabled Probe Identifier,EPID与ECB一一对应,因此可用于查询对应的ECB已经保存的数据的长度)放在首部。

    动作(actions)和预测/条件(predicates)用D语言编写,它们会转换成DIF(D Intermediate Forat,DTrace实现的一种虚拟机指令),DIF简化了仿真和代码生成。

    它DIF指令是解释执行的,安全检查可以在解释时进行,因此安全性可以得到保证。当DIF指令装入内核时,操作码,保留位,寄存器,字符串引用,变量引用以及一些基本的安全检查都要被严格检查。一些运行时错误(如除0)并不能从静态分析中发现,因此它们被DIF虚拟机处理。当遇到这样的指令,DIF虚拟机将不执行它们,并导致当前ECB的处理终止,相应的消费者得到一个运行时错误。从设备的I/O内存区装入数据也是不允许的,DIF可以根据要装入的数据地址很容易地做判断。对于无效的数据装入地址,DIF虚拟机判断它是否有效很复杂,因此借用硬件已有的错误处理机制来实现,DTrace修改页失效处理函数来检查这种情况并做特殊处理,当遇到这样的情况,将导致页失效,页失效处理函数会检查导致页失效的指令是否来自DIF虚拟机,如果是,页失效处理函数将设置一个标志位来表示发生了页失效,然后跳到导致页失效指令的下一条指令执行,也即让DIF虚拟机继续执行其余的DIF虚拟机指令,因此DIF虚拟机能够在执行下一条虚拟机指令前能够有机会检查那个标志位。每次DIF虚拟机在执行一条虚拟机指令前都会检查那个标志位,如果被设置了,当前的ECB处理将被终止,相应的消费者将得到一个运行时错误。这种实现增加了页失效处理函数的开销,但是相对于整个页失效处理函数的开销,它是非常小的,因此对系统性能的影响可以忽略不记。

    D语言类似于awk脚本语言,它支持所有ANSI C支持的操作符,变量类型,也支持typedef并能够定义struct、union和enum。也允许访问内核定义的数据类型和全局变量。D语言支持C语法的变量声明,变量可以声明,也可以不声明,如果不声明,它的类型在赋值时确定,实际类型为所赋值的类型。

    D语言也可以调用DTrace提供的函数和变量。D语言支持全局变量、语句内变量、线程本地变量和关联数组。为了区分语句内变量和线程本地变量,在引用变量时需要有前缀,this->表示语句内变量,self->表示线程本地变量。语句内变量类似于C语言的静态局部变量,线程本地变量就是每一个访问该变量的线程都有自己的变量存储,不同的线程互不影响,C语言没有类似的变量与之对应。D语言也支持一种特殊的数据类型,聚合(Aggregations)。为了实现聚合,需要实现相应的聚合函数。用一个实例可以很好地说明何为聚合:

    syscall::write:entry
    {
    @counts[execname] = count();
    }
    

    其中@表示该变量是聚合,counts就是聚合变量的名称,用户可以随意取名,execname是一个DTrace变量,表示当前进程名,用户可以根据需要指定不同的值,它是这个聚合的索引,类似于数组的索引,只是它可以为整数,也可以是字符串。count()是聚合函数,它返回@counts[execname] + 1,也就说语句@counts[execname] = count()实现了@counts[execname] 加1。

    聚合不同于关联数组,赋值操作必须通过聚合函数来做,索引也不同,关联数组的索引是逗号分割的一个或多个表达式列表。用户态工具dtrace能把D语言转换成DIF指令。一个D程序的结构如下:

    probe-descriptions
    /predicate/
    {
    action-statements
    }
    

    probe-descriptions用于指定探测点,它是一个四元组,与前面讲到的探测点四元组的组成一样,包括提供者、模块、函数名、探测点名,只是这四个元素的每一个都可以不提供(那表示匹配任何值),predicate就是预测或条件,它可以没有,action-statements就是动作,它可以是一条或多条D语句。

    DTrace能够跟踪用户态应用(Systemtap目前不能),这是由pid提供者实现的。

    DTrace还实现了一种特殊的跟踪推测跟踪(Speculative Tracing)。有时消费者在执行探测点时并不知道是否该探测是它所需要的,只有在探测执行后一段时间才能知道,因此预测没法覆盖这种情况。推测跟踪的原理是先执行探测并暂时保存数据在一个临时缓存,如果它发现那些数据是感兴趣的,就提交到真正的缓存,否则就丢弃那些数据。

    三、实例解析

    下面是一个实际的使用示例:

    # dtrace -n syscall:::entry'/pid == 31337/{ @syscalls[probefunc] = count(); }'
    dtrace: description 'syscall:::entry' matched 215 probes
    ^C
    
      open                                                            1
      lwp_park                                                        2
      times                                                            4
      fcntl                                                             5
      close                                                            6
      sigaction                                                         6
      read                                                            10
      ioctl                                                            14
      sigprocmask                                                    106
      write                                                          1092
    

    选项-n用于使能指定的探测点,syscall:::entry就是前面提到的四元组,它用于描述哪些探测点将被使能,如果某个元素为空,表示将匹配该元素对应的任何组件。该例子中的四元组指定的探测点为系统调用入口。/括起来的部分就是预测(Predicate),它表示只有对当前进程PID为31337的进程的系统调用执行后面指定的动作,大括号包含的部分为真正的动作,@表示它为Dtrace会聚,会聚是一个变量类型,它被用于采集到的数据的综合处理,一般在性能监视中使用最为普遍,syscalls为会聚的变量名,probefunc为会聚的键,它是Dtrace内嵌变量,表示被探测的函数的名称,你可以把syscalls理解为关联数组,当然可以随意取名,count为Dtrace为会聚实现的内部函数,它返回它被调用的次数,注意,不同的探测点count互不相干。

    小结

    本文讲解了DTrace以及它的实现原理,并通过一个实际的例子给读者一个直观的认识,如果读者对它有兴趣可以阅读参考资料中的DTrace指南来了解更多详细的信息。本文是系列文章“Linux下的一个全新的性能测量和调式诊断工具 -- Systemtap”之二,有兴趣的读者可以阅读该系列文章之一和三。


    参考资料

    1. Sun Microsystem DTrace Resource
    2. DTrace FAQ
    3. DTrace Tutorial and User Guide
    4. DTrace at OpenSolaris.org
    5. DTrace: An Introduction
    6. DTrace中文简介

    关于作者

    杨毅,计算机科学硕士,毕业于中科院计算技术研究所,有 5 年的 Linux 内核编程经验,目前在 Intel OTC(Open-source Technology Center)工作。您可以通过 yang.y.yi@gmail.com 与作者联系。


    展开全文
  • amp;m=Article&amp;a=show&amp;id=171   时间: 2016-10-09 00:40:04 | 作者: ohsdba | English 如非注明,本站文章皆为原创。...DTrace(dynamic tracing)是Sun Solaris系统上主要的...

    http://www.ohsdba.cn/index.php?g=Home&m=Article&a=show&id=171

     
    DTrace(dynamic tracing)是Sun Solaris系统上主要的性能诊断工具,可以对kernel和用户应用程序进行动态跟踪,并且对系统运行不构成任何危险的技术,后被Oracle公司收购,遵循CDDL(Common Development and Distribution License),后来Oracle把这一技术移植到了Oracle Linux(在2012年12月正式发布)。到目前为止,Oracle Linux的DTrace仅限于Oracle Unbreakable Enterprise Kernel,无法兼容其他发行版Linux,在当时也引起了一场纷争(http://lwn.net/Articles/483107/)。

     

    在Oracle Linux上使用DTrace可以帮你
        观察整个软件体系、操作系统内核、系统库和应用程序的动态运行时性能
        通过在运行时定义实时探测点识别性能瓶颈
        开发探测器在谓词控制下触发时执行的脚本
        检测并报告内存访问错误,而不是听任系统崩溃

     

    如何安装dtrace

    一般情况下,DTrace软件包不能从Public yum上获取,必须先注册ULN(Unbreakable Linux Network),然后才能下载和安装

    yum install dtrace-utils

    注意:在安装dtrace-utils之前需要安装dtrace-modules(这个可以从公共yum上获取)

    其实只需要一个dtrace-utils的包,需要这个包做测试的,可以发信息给我(只限测试学习测试使用)

    [root@ovm ~]# rpm -ivh dtrace-utils-0.5.0-4.el6.x86_64.rpm
    Preparing...                ########################################### [100%]
       1:dtrace-utils           ########################################### [100%]
    [root@ovm ~]#
    [root@ovm ~]# ls -l /usr/sbin/dtrace
    -rwxr-xr-x. 1 root root 36880 Nov  4  2015 /usr/sbin/dtrace
    [root@ovm ~]# 

    dtrace帮助信息
    [root@ovm ~]# dtrace
    Usage: dtrace [-32|-64] [-aACeFGhHlqSvVwZ] [-b bufsz] [-c cmd] [-D name[=def]]
            [-I path] [-L path] [-o output] [-p pid] [-s script] [-U name]
            [-x opt[=val]] [-X a|c|s|t]
    
            [-P provider [[ predicate ] action ]]
            [-m [ provider: ] module [[ predicate ] action ]]
            [-f [[ provider: ] module: ] func [[ predicate ] action ]]
            [-n [[[ provider: ] module: ] func: ] name [[ predicate ] action ]]
            [-i probe-id [[ predicate ] action ]] [ args ... ]
    
            predicate -> '/' D-expression '/'
               action -> '{' D-statements '}'
    
            -32 generate 32-bit D programs and ELF files
            -64 generate 64-bit D programs and ELF files
    
            -a  claim anonymous tracing state
            -A  generate driver.conf(4) directives for anonymous tracing
            -b  set trace buffer size
            -c  run specified command and exit upon its completion
            -C  run cpp(1) preprocessor on script files
            -D  define symbol when invoking preprocessor
            -e  exit after compiling request but prior to enabling probes
            -f  enable or list probes matching the specified function name
            -F  coalesce trace output by function
            -G  generate an ELF file containing embedded dtrace program
            -h  generate a header file with definitions for static probes
            -H  print included files when invoking preprocessor
            -i  enable or list probes matching the specified probe id
            -I  add include directory to preprocessor search path
            -l  list probes matching specified criteria
            -L  add library directory to library search path
            -m  enable or list probes matching the specified module name
            -n  enable or list probes matching the specified probe name
            -o  set output file
            -p  grab specified process-ID and cache its symbol tables
            -P  enable or list probes matching the specified provider name
            -q  set quiet mode (only output explicitly traced data)
            -s  enable or list probes according to the specified D script
            -S  print D compiler intermediate code
            -U  undefine symbol when invoking preprocessor
            -v  set verbose mode (report stability attributes, arguments)
            -V  report DTrace API version
            -w  permit destructive actions
            -x  enable or modify compiler and tracing options
            -X  specify ISO C conformance settings for preprocessor
            -Z  permit probe descriptions that match zero probes        
    [root@ovm ~]# 

     

    探测器和提供程序

     

    要使用DTrace,需要指定内核中感兴趣的位置(称为探测器),DTrace可以绑定一个请求来执行一组操作,如记录堆栈跟踪、时间戳或函数参数。探测器的作用类似于深埋于操作系统中用于记录信息的可编程遥感传感器。探测器触发时,DTrace会从其收集数据并向您回报数据。Oracle Linux DTrace探测器运行于被称作提供程序的DTrace内核模块中,提供程序执行插装以支持探测器。

    如何使用modprobe命令加载支持您需要使用的DTrace探测器的模块。例如,如果要使用proc提供程序发布的探测器,将加载systrace模块:

    # modprobe systrace 

    内核模块列表
     内核模块说明
    dtrace dtrace 为 DTrace 本身提供探测器:BEGIN、END、ERROR,用于可选地在跟踪开始之前初始化 DTrace、执行跟踪后处理、处理执行期间其他探测器中的意外错误。
    io io 提供与数据输入和输出有关的监视探测器。
    proc proc 为监视进程创建和终止、新程序映像执行以及发送和处理信号提供探测器。
    profile profile 提供与定时中断关联的探测器。可以使用这些探测器以固定时间间隔对系统状态进行采样。
    sched sdt 提供与 CPU 调度有关的探测器。
    sdt sdt 提供静态定义的、位于内核中多个感兴趣的重要位置的跟踪探测器。
    syscall systrace 在每个系统调用的入口点和返回点提供探测器。这些探测器对了解应用程序与底层系统之间的交互尤为有用。

    DTrace探测器来自一组被称作提供程序的内核模块,每个提供程序执行特定类型的插装来创建探测器。使用DTrace时,每个提供程序都有机会发布它可以为DTrace框架提供的探测器。然后,您可以对已发布的任何探测器启用和绑定跟踪操作。要列出系统上的所有可用探测器,可以使用dtrace -l。根据Oracle Linux平台、安装的软件和加载的提供程序模块的不同,探测器的数量也不同。
    [root@ovm ~]# dtrace -l|wc -l
    622
    [root@ovm ~]#
    [root@ovm ~]# dtrace -l|more
       ID   PROVIDER            MODULE                          FUNCTION NAME
        1     dtrace                                                     BEGIN
        2     dtrace                                                     END
        3     dtrace                                                     ERROR
    每个探测器都显示一个整数 ID 和一个易于理解的名称,由四部分组成,在 dtrace 输出中显示为单独的四列。
        provider  — 发布此探测器的 DTrace 提供程序的名称。提供程序名称通常对应于执行插装以启用探测器的 DTrace 内核模块的名称
        module    — 如果此探测器对应于特定程序位置,则为探测器所在内核模块的名称
        function  — 如果此探测器对应于特定程序位置,则为探测器所在程序函数的名称
        name      — 探测器名称的最后一部分,即表明探测器语义的名称
    当提及具体探测器时,这些部分一起显示,中间用冒号分隔,如:
    provider:module:function:name

    dtrace命令样本
    # New processes with arguments
    dtrace -n 'proc:::exec-success { trace(curpsinfo->pr_psargs); }'
    
    # Files opened by process
    dtrace -n 'syscall::open*:entry { printf("%s %s",execname,copyinstr(arg0)); }'
    
    # Syscall count by program
    dtrace -n 'syscall:::entry { @num[execname] = count(); }'
    
    # Syscall count by syscall
    dtrace -n 'syscall:::entry { @num[probefunc] = count(); }'
    
    # Syscall count by process
    dtrace -n 'syscall:::entry { @num[pid,execname] = count(); }'
    
    # Disk size by process
    dtrace -n 'io:::start { printf("%d %s %d",pid,execname,args[0]->b_bcount); }'
    
    # Pages paged in by process
    dtrace -n 'vminfo:::pgpgin { @pg[execname] = sum(arg0); }'

    调用D语言脚本syscalls.d显示进程3961正在使用的系统调用及其频率
    [root@ovm ~]# cat syscalls.d
    #!/usr/sbin/dtrace -qs
    syscall:::entry
    /pid == $1/
    {
      @num[probefunc] = count();
    }
    [root@ovm ~]# ./syscalls.d 3961
    ^C
    
      rt_sigreturn                                                      1
      shutdown                                                          1
      accept                                                            2
      clone                                                             3
      connect                                                           3
      gettid                                                            3
      set_robust_list                                                   3
      socket                                                            3
      exit                                                              4
      getsockopt                                                        4
      getsockname                                                       5
      madvise                                                           5
      close                                                             6
      dup2                                                              6
      sched_getaffinity                                                 6
      mmap                                                              7
      epoll_ctl                                                        10
      rt_sigprocmask                                                   13
      setsockopt                                                       13
      fcntl                                                            15
      write                                                            17
      read                                                             23
      epoll_wait                                                       39
      mprotect                                                         51
      newstat                                                          84
      ioctl                                                            95
      poll                                                            140
      sendto                                                          265
      recvfrom                                                        284
      futex                                                          1714
    [root@ovm ~]# 


    Reference
    https://en.wikipedia.org/wiki/DTrace
    http://docs.oracle.com/cd/E37670_01/E50705/html/ol_intro_dtrace.html
    http://docs.oracle.com/cd/E37670_01/E38608/html/dt_gs_about.html
    http://www.oracle.com/technetwork/cn/articles/servers-storage-admin/dtrace-on-linux-1956556-zhs.html
    http://dtrace.org/blogs/
    http://dtrace.org/guide/preface.html
    https://blogs.oracle.com/linux/entry/announcement_dtrace_for_oracle_linux
    http://www.ibm.com/developerworks/cn/aix/library/au-dtraceprobes.html
    http://www.oracle.com/technetwork/server-storage/solaris10/solaris-dtrace-wp-167895.pdf

    https://github.com/dtrace4linux/linux

    展开全文
  • Solaris(包括OpenSolaris),FreeBSD和Mac OS X内置的动态跟踪(DTrace)功能为动态跟踪应用程序提供了简单的环境。 与调试不同,DTrace可以随意打开或关闭,并且您无需提供特殊的应用程序构建即可利用跟踪功能。 ...

    Solaris(包括OpenSolaris),FreeBSD和Mac OS X内置的动态跟踪(DTrace)功能为动态跟踪应用程序提供了简单的环境。 与调试不同,DTrace可以随意打开或关闭,并且您无需提供特殊的应用程序构建即可利用跟踪功能。

    以上所有平台均支持使用标准DTrace探针。 涵盖了操作系统在代码内不同功能边界上公开的内容。 这些探针(称为功能边界跟踪(FBT))使您可以识别何时开始或停止执行给定功能。

    此功能的局限性在于它只能用于探测应用程序的功能,而不能用于探测功能片段。 如果要检查构成单个操作的多个功能的执行情况,或者要查看单个功能的一部分,FBT则无济于事。

    对于您自己的应用程序,您可以使用用户域静态定义的跟踪(USDT)解决此问题。 USDT使您作为开发人员能够在您认为重要的代码点上为应用程序添加特定的探针。 USDT系统还使您能够从正在运行的应用程序中公开数据,在跟踪应用程序时可以将其作为探针的参数进行访问。

    在开始将USDT探针添加到系统之前,首先需要考虑要报告的探针,探针可能提供的信息以及潜在的性能问题。

    探头设计

    一旦确定标准FBT探针不适合您的需求,就需要开始考虑将静态探针添加到您的应用程序中。

    使用DTrace向应用程序添加探针时,首先要考虑的是首先确定实际使用探针的目的。 探针可以帮助您识别各种问题和信息,但是您应该针对探针。 您应该挑选出特定的区域,例如功能,性能或其他可测量的信息,这些信息超出了仅使用所有功能障碍中提供的标准进入/退出探测器所能找到的范围。

    因此,从简单的角度来看,您应该考虑有两种主要的探针类型:

    1. 信息探针 :这些信息探针会暴露或总结一条信息,否则这些信息在程序执行过程中将很难确定。 这里的好例子包括内部结构的大小或内容,或者不是由函数直接处理的事件的操作或触发。 现有的操作系​​统探针中有许多示例。 例如,您可以获得有关磁盘I / O统计信息或虚拟内存系统内故障的信息。
    2. 操作探针 :这些探针将特定事件或语句序列括起来,以便您可以使用它们在序列的开头和结尾获取有关内部结构的特定信息,或监视一组特定语句的执行时间。 因为您可以将这些探针放置在任何地方以指示操作的开始和结束,所以它们可以跨越多个功能,或者仅覆盖给定功能的一小部分。 这些功能比使用可能提供过多或不足范围的功能边界更为实用。 按照惯例,这些探针通常以start和done作为后缀。

    确定探针的类型后,下一步要考虑的是是否要在探针中公开任何其他信息,如果是,则要提供什么信息以及采用哪种格式。 在DTrace中,探针可以通过参数公开信息,这些参数可在编写合适的DTrace脚本或单行代码时进行处理。 例如,如果要检测文件I / O功能,则可以将要写入文件的名称添加到该功能的探针中。

    在定义探测器时, probe write__file__start(int id, char *filename);这些参数指定名称和类型: probe write__file__start(int id, char *filename);

    在监视期间在DTrace脚本中使用时,每个参数都可以在名为arg0arg1等的变量中使用。因此,可以使用第二个参数使用以下命令打印文件名: printf("%s\n", copyinstr(arg1));

    选择合适的数据进行公开是一种在监视应用程序的同时了解要从探针中获取什么的情况。 例如,在上面的I / O函数示例中,如果函数用于写入多个文件,则知道文件名可能很关键。 但是,如果函数仅写入同一文件,则没有必要在探针中公开此信息。

    因此,您必须考虑如何呈现信息。 您是否希望能够按操作类型,文件或网络端口来汇总数据? 您想知道数据大小还是实际写入的数据? 所有这些都是特定于程序和环境的。

    以这种方式提供信息也有开销,您应该尽量避免共享信息,尤其是大型结构。 相反,您应该尝试提供有限的信息,或者在可能的情况下提供摘要信息(尽管请注意,出于DTrace探针的目的而复制,缩短或重新格式化字符串将产生更大的影响)。

    有两种以这种方式引入探针的方法可能会有用:多种探针,以及使用特殊的“是否启用了探针?”。 包围。 当您编写探针并使用dtrace命令生成头文件时,将以非常简单的方式支持后一种解决方案。 您可以围绕代码块以确定是否已启用探针(例如,正在对其进行主动监视),并执行其他操作,这在需要汇总或整理数据时非常有用(请参见清单1 )。 。

    清单1.查看是否已启用探测的代码块
    if (WRITE_FILE_START_ENABLED())
    {
        ...
    }

    前一种方法使用单独的探针,使用户可以使用他们想要获得所需信息的特定探针。 例如,使用我们的I / O示例,您可能具有一个探针结构,该探针结构在不同级别上提供了不同的信息,并且这些探针名义上是嵌套的,以便您可以确定所需的信息:

    • 写文件开始(id)
    • 写文件数据(文件名,缓冲区)
    • 写文件完成(id)

    监视应用程序时,如果只想监视写文件操作的速度,则可以使用提供引用ID的write-file-start和write-file-done探针。 如果需要文件名数据,则可以选择监视“写文件数据”探针以在处理期间输出该信息。

    最后,对于所有探针,都应记住,有权监视DTrace探针的任何人都可以访问您公开的信息。 例如,如果您在探针中公开电子邮件地址或消息内容,则任何具有DTrace权限的用户都将能够阅读该内容。 以这种方式公开潜在的敏感信息时要小心。 如果可以,请仅提供统计数据,或者如果必须公开可能敏感的真实信息,则可以考虑遮盖数据,从而无法确定真实内容。

    定义探针

    在Solaris / OpenSolaris上,可以使用/usr/include/sys/sdt.h中的宏定义探针。 宏使您可以通过对要包含的参数调用适当的宏,在代码中插入探针。 例如,要插入不带参数的探针,可以使用: DTRACE_PROBE("prime","calc-start")

    如果要共享参数,则有另一个宏(编号为1到5),用于在触发探测时共享相应数量的参数。 例如,共享一个参数: DTRACE_PROBE("prime","calc-start",prime)

    仅Solaris / OpenSolaris支持此方法。 要获得Solaris / OpenSolaris,FreeBSD和Mac OS X上支持的更具移植性的版本(并且该版本还提供了一种将探针插入代码的简便方法),则可以创建一个探针定义文件,其中将包含所需的每个探针插入到您的代码中,包括要通过每个探针共享的参数的定义。

    该文件的格式类似于C。您必须指定一个或多个提供程序,并且在每个提供程序中,指定要在代码中支持的每个探针。 您可以在代码清单2中看到探针定义的示例。

    清单2.示例探针定义
    provider primes {
    
    /* Start of the prime calculation */
    
       probe primecalc__start(long prime);
    
    /* End of the prime calculation */
    
       probe primecalc__done(long prime, int isprime);
    
    /* Exposes the size of the table of existing primes */
    
       probe primecalc__tablesize(long tablesize);
    
    };

    一旦在应用程序中安装了探针,提供程序就是提供程序的名称。 DTrace中的探针名称由提供者,模块,功能和探针名称标识: provider:module:function:name 对于USDT探针,您只能指定此规范的提供者和名称部分。

    探针的名称是从probe关键字之后的字符串中获取的。 您可以使用双下划线在探针名称中分隔单词。 要在跟踪过程中使用探针时,它将转换为单个连字符。 例如,可以使用primes::primecalc-start名称(结合提供程序和探针名称)来标识此文件中的primecalc__start()探针名称。

    定义中每个探针的自变量用于帮助识别C代码中自变量的数据类型。 请记住,在跟踪过程中每个参数都可用arg0arg1argN等。 因此,对于primecalc__done探针,素数为arg0而该数是否为素数为arg1

    有了探针定义文件后,可以使用dtrace命令将探针定义转换为头文件: $ dtrace -o probes.h -h -s probes.d

    上面的命令指定输出文件的名称( -o ),以生成标头( -h )和源探针定义文件的名称( -s )。

    生成的头文件包含可以放入代码中以插入探针的宏。 您可以根据需要在代码中触发探针的任意次数使用它们。

    探针宏的格式遵循您定义的探针名称。 例如, primecalc__done宏是PRIMES_PRIMECALC_DONE 现在您已经有了探针定义文件(在构建应用程序时也会使用)和头文件,现在该将探针插入C源代码了。

    确定探针位置

    为了描述放置探针的位置,我们将看一个简单的程序来确定质数。 代码几乎不是最有效的方法,但是DTrace探针可以帮助我们识别问题。

    原始源代码可以在清单3中看到。

    清单3.确定素数的程序的源代码
    #include <stdio.h>
    
    long primes[1000000] = { 3 };
    long primecount = 1;
    
    int main(int argc, char **argv)
    {
      long divisor = 0;
      long currentprime = 5;
      long isprime = 1;
    
      while (currentprime < 1000000)
        {
          isprime = 1;
           for(divisor=0;divisor<primecount;divisor++)
            {
              if (currentprime % primes[divisor] == 0)
                {
                  isprime = 0;
                }
            }
          if (isprime)
            {
              primes[primecount++] = currentprime;
              printf("%d is a prime\n",currentprime);
            }
          currentprime = currentprime + 2;
        }
    }

    清单4显示了修改后的代码,其中添加了DTrace探针。

    清单4.添加了DTrace探针的修改后的代码
    #include <stdio.h>
    #include "probes.h"
    
    long primes[1000000] = { 3 };
    long primecount = 1;
    
    int main(int argc, char **argv)
    {
      long divisor = 0;
      long currentprime = 5;
      long isprime = 1;
    
      while (currentprime < 1000000)
        {
          isprime = 1;
          PRIMES_PRIMECALC_START(currentprime);
          for(divisor=0;divisor<primecount;divisor++)
            {
              if (currentprime % primes[divisor] == 0)
                {
                  isprime = 0;
                }
            }
          PRIMES_PRIMECALC_DONE(currentprime,isprime);
          if (isprime)
            {
              primes[primecount++] = currentprime;
              PRIMES_PRIMECALC_TABLESIZE(primecount);
              printf("%d is a prime\n",currentprime);
            }
          currentprime = currentprime + 2;
        }
    
    }

    探针的位置考虑了许多不同的因素:

    • dtrace生成的头文件已包含在源代码中。
    • 对于primecalc-startprimecalc-done探针,请注意如何将探针直接放置在执行计算的主循环之外。 诱惑是将探针放在循环的开始和结尾,因为这似乎是包含探针的合理位置。 但是,如前所述,对于要监视特定功能区域的此类探针,应尽可能接近要监视的实际操作。 如果将探针放置在while循环的开始和结束处, while包括许多与素数的实际计算无关的操作。 尽管在该应用程序中不太可能产生重大变化,但在其他应用程序中,这样的附加步骤可能会增加您真正要监视的操作的时间。
    • primecalc-tablesize探针的设计不能根据其花费的时间进行监视,但是您确实希望有效地监视表的大小。 最明显的放置位置是最接近值的任何更改。 这一点很重要,因为从跟踪的角度来看,即使您没有监视随时间的变化,您也将希望确切地知道值的变化位置。
    • 请注意, done探针既提供数字,也提供数字是否被确定为素数。 实际上,即使在if语句之前不使用确定的值,即使在for循环结束之后,您实际上也知道该数字是否为质数。 另外,请注意,通过提供isprime变量的值,您可以使用该值作为参数一次将done探针放入代码中,而不是使用其他探针。 在脚本编写方面,您可以使用谓词使用该值来计算素数和非素数所花费的时间。
    • 对于其他应用程序,将应用相同的规则。 您要确保任何提供统计(但不一定是时序)数据的探针都尽可能接近该信息中的任何更改。 请记住,您可以多次将同一探针放入代码中。 在这种情况下,您可以在变量初始化之后立即将PRIMES_PRIMECALC_TABLESIZE宏放在主代码块的开头,以便可以突出显示初始值。

    编译应用

    您可以使用所选的C编译器来编译DTrace应用程序,方法与其他任何应用程序相同。 在Mac OS X / FreeBSD上,您可以简单地正常编译应用程序。 但是在Solaris / OpenSolaris上,您必须修改目标文件并生成一个包含DTrace探针的新目标文件。

    在Solaris / OpenSolaris上,该过程需要在最终链接之前修改您的目标文件,并且您必须在单独生成的目标文件中进行链接,该目标文件包含要在应用程序中启用的探针。 修改目标文件的过程就地进行-即,您指定目标文件,并且该过程修改文件,将所做的更改保存回源文件中。 在此过程中将生成目标文件。 一般顺序为:

    1. 将每个源文件编译为一个目标文件,例如: $ gcc -c primes.c
    2. 编译完所有源文件后,创建一个DTrace探针对象文件,其中包含要链接到主程序的探针。 例如,对于单个文件,可以使用以下命令执行此操作: $ dtrace -G -s probes.d -o probes.o primes.o
    3. 上面的代码读取了目标文件primes.o和探针定义probes.d,然后生成了一个名为probes.o的探针文件。 如果您有多个带有DTrace探针的目标文件,则可以在命令行上指定任何其他目标文件。 例如: $ dtrace -G -s probes.d -o probes.o file1.o file2.o file3.o
    4. 链接您的应用程序,包括所有目标文件和生成的探针目标文件: $ gcc -o primes primes.o probes.o
    5. 最终的prime可执行文件将被探测,启用并准备使用。

    在FreeBSD / Mac OS X上,您不必生成单独的探针对象文件来进行链接。 这使编译过程更加简单:

    1. 将每个源文件编译为一个目标文件,例如: $ gcc -c primes.c
    2. 链接您的应用程序,包括所有目标文件: $ gcc -o primes primes.o
    3. 带有DTrace探针的应用程序已准备就绪,可以使用。

    现在,让我们尝试使用探针。

    编写脚本以使用您的探针

    您仅对基本程序添加了一些非常基本的探针,但是仍然可以从执行中获得一些有用的信息。 例如,您可以使用“开始”和“完成”探针来查找查找所有素数和所有非素数所需的时间。 由于素数在非素数上的分布要小得多,因此您应该看到这两个元素的时序之间存在显着差异。 清单5中显示了一个示例脚本来显示这一点。

    清单5.显示发现质数差异的脚本
    #!/usr/sbin/dtrace -s
    
    #pragma D option quiet
    
    primes*:::primecalc-start
    {
      self->start = timestamp;
    }
    
    primes*:::primecalc-done
    /arg1 == 1/
    {
            @times["prime"] = sum(timestamp - self->start);
    }
    
    primes*:::primecalc-done
    /arg1 == 0/
    {
            @times["nonprime"] = sum(timestamp - self->start);
    }
    
    END
    {
            normalize(@times,1000000);
            printa(@times);
    }

    该脚本使用谓词来标识计算最终是针对质数还是非质数的,从开始探针到完成探针的时序数据汇总。 信息被放入一个关联数组,使用sum()聚合函数进行聚合。

    END块中的normalize()函数将结果除以一百万,以毫秒为单位获取计时。 如果尝试在运行primes程序的同时运行此脚本,则将获得类似于清单6的输出。

    清单6.输出
    $ dtrace -s timing.d            
    ^C
    
      prime                                             10784
      nonprime                                       16340221

    结果表明,寻找非素数而不是素数的预期时间更长。 这是因为非质数比质数要多得多。

    陷阱和陷阱

    包括DTrace探针时,有许多问题要考虑,但是您可能需要注意以下一些已知问题:

    • 避免在功能入口和出口处放置探针。 由于您已经可以使用FBT探针自动访问这些名称,因此此过程的唯一好处是为探针提供了替代名称而不是功能名称。 如果要添加USDT探针,则应根据要探针的操作点而不是功能边界来创建探针。
    • 避免将DTrace探针作为函数中的最后一条语句。 在某些平台和编译器组合上,代码的优化可能导致探针被优化(有效地完全移除了探针),或者探针可能附加到了调用函数上(这可能移除了参数数据,或者导致有趣的结果)。时间问题)。
    • 如果要编译用于链接的库并希望使探针可用,则需要在创建库之前对目标文件运行DTrace进程。 另外,在Solaris / OpenSolaris上,您需要将生成的目标文件与原始目标文件一起包括在库中。 当使用复杂的构建过程(例如由automake应用的过程)时,需要格外小心,该过程可以将目标文件放入临时目录中,以便在生成库时显式使用。 您必须确保在要添加到库中的目标文件上运行dtrace命令。
    • 您生成的探针对象文件及其所基于的对象文件必须匹配。 如果目标文件更改,则必须重新生成探针目标文件,否则链接将失败。
    • 如果您使用的是autoconf,cmake或类似产品,并且希望保持跨平台兼容性,则dtrace的唯一考虑因素是使用-G生成探针对象文件。 您可以在配置过程中轻松为此进行测试。

    摘要

    在DTrace中使用基于函数的跟踪已经为您提供了很多信息,但是最大的灵活性来自于添加您自己的静态探测器。 使用静态探针,您可以选择其位置,名称及其公开的信息,从而使您可以微调公开的信息以匹配要提取的数据。

    添加静态探针是创建适当的定义文件,然后在C源代码中使用从该文件生成的宏的情况。 尽管根据您的平台需要完成一些较小的步骤,但是编译相对简单。 只要为您牢记有关将探针放置在何处以及如何选择要使用的探针的建议,通常所带来的好处将超过将探针实际包含在应用程序中的少量开销。


    翻译自: https://www.ibm.com/developerworks/aix/library/au-dtraceprobes.html

    展开全文
  • Linux 有了 “DTrace

    2016-10-28 21:23:06
    Linux 没有 DTrace(名字),但现在有了DTrace(功能)。DTrace 是源自 Solaris 操作系统的动态跟踪工具,提供了高级性能分析和调试功能,它的源代码采用 CDDL 许可证,不兼容Linux内核使用的 GPLv2 许可证,无法...
  • Linux 中的 DTrace :BPF 进入 4.9 内核 编译自:http://www.brendangregg.com/blog/2016-10-27/dtrace-for-linux-2016.html 作者: Brendan Gregg 原创:LCTT https://linux.cn/article-8038-1.html ...
  • DTrace for Linux 2016

    2016-10-29 09:13:47
    With the final major capability for BPF tracing (timed sampling) merging in Linux 4.9-rc1, the Linux kernel now has raw capabilities similar to those provided by DTrace, the advance
  • dtrace4linux

    2019-01-12 02:40:27
    http://www.oschina.net/p/dtrace4linux?fromerr=ZSxqzDcE
  • dtrace4linux_Example

    2019-08-07 14:35:07
    http://www.slideshare.net/brendangregg/what-linux-can-learn-from-solaris-performance-and-viceversa/86-dtrace4linux_Example_Tracing_TCP_retransmits 转载于:...
  • 一、DTrace 简介 DTrace是一个强大的动态跟踪框架,它允许管理员、开发者和服务团队精确地回答关于操作系统和用户程序的任何问题。用户可以使用它管理成千上万个探测点,为每一个探测点指定执行条件(Predicates)...
  • Dtrace for Linux 2016

    2019-01-05 06:36:51
    http://www.brendangregg.com/blog/2016-10-27/dtrace-for-linux-2016.html
  • DTrace 的下载及安装

    2018-02-07 16:10:52
    如果你使用的Oracle Linux,因为sun被Oracle收购后,Oracle Linux版本的DTrace可以直接在Oracle官网进行下载。 下载地址 ...
  • dtrace4linux的最新信息

    2014-04-19 12:23:37
    Dtrace4linux是一个把
  • http://www.oracle.com/technetwork/server-storage/linux/downloads/linux-dtrace-2800968.html http://blog.csdn.net/msdnchina/art...
  • [root@localhost dtrace]# uname -r 4.1.12-61.1.18.el7uek.x86_64 [root@localhost dtrace]# cat /etc/oracle-release Oracle Linux Server release 7.3 [root@localhost dtrace]# yum repolis...
  • 简要介绍适用于 Oracle LinuxDTrace 探测器和提供程序,以及与 Oracle Solaris 中 DTrace 探测器和提供程序的区别。还介绍了 DTrace 命令和脚本编写。 2013 年 6 月发布 DTrace 是一个全面的动态跟踪工具...
1 2 3 4 5 ... 20
收藏数 3,097
精华内容 1,238
热门标签