【问题标题】:Is it possible to use eBPF or perf to calculate time spent in individual traced functions?是否可以使用 eBPF 或 perf 来计算在单个跟踪函数中花费的时间?
【发布时间】:2018-04-11 17:16:55
【问题描述】:

目前,我可以告诉trace-cmd(一个 ftrace 前端)使用 ftrace 的函数图基础架构来跟踪指定的工作负载。使用一些跟踪数据处理工具,我可以查看调用函数的频率以及每个函数调用的相应总持续时间。示例输出可能如下所示,其中我跟踪工作负载生成的 ext4 函数调用:

# trace-cmd post-processing example output
# function name, frequency, total duration (us)
ext4_alloc_inode(),1,35.567
ext4_read_inode_bitmap(),1,12.076
ext4_init_io_end(),2,41.216
ext4_journal_check_start(),39,716.3
ext4_da_write_end(),10,102.661
ext4_da_get_block_prep(),9,257.46999999999997
ext4_block_write_begin(),2,88.069
ext4_bio_write_page(),1,33.016

我看到了eBPFsystemtapperf 等可以生成的不可思议的flame graphs,这让我相信我应该能够实现类似于我的@ 的输出987654328@ 使用perfeBPF 输出。但是,有一些障碍:

  1. 我的工作负载最多可以运行 15 分钟,如果我使用 perf,这可能会生成大量日志。作为参考,您可以查看example output 跟踪 bash 的时间要少得多。
  2. 我对@9​​87654332@ 不够熟悉,无法确定是否可以实现与trace-cmd 后处理示例输出类似的数据/输出。

对于那些比我更熟悉eBPFperf的人,我想知道:

  • 是否可以使用eBPFperf 来实现我的目标?
  • 如果有,为什么会这样?

如果您觉得特别慷慨,我们将不胜感激可以帮助我实现目标的示例代码/命令。

【问题讨论】:

    标签: trace perf bpf ftrace ebpf


    【解决方案1】:

    当然,可以使用 eBPF 来做到这一点。

    实际上在the bcc tools 中有一个工具可以做类似的事情。 funclatency 跟踪一组函数并显示其延迟的直方图(每次调用在函数中花费的时间):

    # ./funclatency do_sys_open
    Tracing do_sys_open... Hit Ctrl-C to end.
    ^C
         nsecs           : count     distribution
           0 -> 1        : 0        |                                      |
           2 -> 3        : 0        |                                      |
           4 -> 7        : 0        |                                      |
           8 -> 15       : 0        |                                      |
          16 -> 31       : 0        |                                      |
          32 -> 63       : 0        |                                      |
          64 -> 127      : 0        |                                      |
         128 -> 255      : 0        |                                      |
         256 -> 511      : 0        |                                      |
         512 -> 1023     : 0        |                                      |
        1024 -> 2047     : 0        |                                      |
        2048 -> 4095     : 124      |****************                      |
        4096 -> 8191     : 291      |**************************************|
        8192 -> 16383    : 36       |****                                  |
       16384 -> 32767    : 16       |**                                    |
       32768 -> 65535    : 8        |*                                     |
       65536 -> 131071   : 0        |                                      |
      131072 -> 262143   : 0        |                                      |
      262144 -> 524287   : 0        |                                      |
      524288 -> 1048575  : 0        |                                      |
     1048576 -> 2097151  : 0        |                                      |
     2097152 -> 4194303  : 1        |                                      |
    Detaching...
    

    从funclatency开始,很容易衍生出一个工具来满足你的需求:

    from __future__ import print_function
    from bcc import BPF
    from time import sleep, strftime
    import argparse
    
    parser = argparse.ArgumentParser(
        description="Time functions and print time spent in each function",
        formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("-p", "--pid", type=int, help="trace this PID only")
    parser.add_argument("pattern", help="search expression for functions")
    args = parser.parse_args()
    
    # define BPF program
    bpf_text = """
    #include <uapi/linux/ptrace.h>
    
    struct stats_t {
        u64 time;
        u64 freq;
    };
    BPF_HASH(start, u32);
    BPF_HASH(ipaddr, u32);
    BPF_HASH(stats, u64, struct stats_t);
    
    int trace_func_entry(struct pt_regs *ctx)
    {
        u64 pid_tgid = bpf_get_current_pid_tgid();
        u32 pid = pid_tgid;
        u64 ts = bpf_ktime_get_ns();
    
        u64 ip = PT_REGS_IP(ctx);
        ipaddr.update(&pid, &ip);
        start.update(&pid, &ts);
    
        return 0;
    }
    
    int trace_func_return(struct pt_regs *ctx)
    {
        u64 *tsp, delta;
        u64 pid_tgid = bpf_get_current_pid_tgid();
        u32 pid = pid_tgid;
    
        // calculate delta time
        tsp = start.lookup(&pid);
        if (tsp == 0) {
            return 0;   // missed start
        }
        delta = bpf_ktime_get_ns() - *tsp;
        start.delete(&pid);
    
        // store as histogram
        u64 ip, *ipp = ipaddr.lookup(&pid);
        if (ipp) {
            ip = *ipp;
            struct stats_t *stat = stats.lookup(&ip);
            if (stat) {
                stat->time += delta;
                stat->freq++;
            } else {
                struct stats_t s = {};
                s.time = delta;
                s.freq = 1;
                stats.update(&ip, &s);
            }
            ipaddr.delete(&pid);
        }
    
        return 0;
    }
    """
    
    # load BPF program
    b = BPF(text=bpf_text)
    pattern = args.pattern.replace('*', '.*')
    pattern = '^' + pattern + '$'
    b.attach_kprobe(event_re=pattern, fn_name="trace_func_entry")
    b.attach_kretprobe(event_re=pattern, fn_name="trace_func_return")
    matched = b.num_open_kprobes()
    
    if matched == 0:
        print("0 functions matched by \"%s\". Exiting." % args.pattern)
        exit()
    # header
    print("Tracing %d functions for \"%s\"... Hit Ctrl-C to end." %
        (matched / 2, args.pattern))
    
    while (1):
        try:
            sleep(99999999)
        except KeyboardInterrupt:
            break
    
    print()
    print("%-36s %8s %16s" % ("FUNC", "COUNT", "TIME (nsecs)"))
    stats = b.get_table("stats")
    for k, v in stats.items():
        print("%-36s %8s %16s" % (BPF.sym(k.value, -1), v.freq, v.time))
    

    此工具使用 BPF 直接在内核中计算每个函数调用的频率和总持续时间。在显示它们之前,聚合值仅在最后复制到用户空间。 输出:

    # ./time-spent do_sys_*
    Tracing 6 functions for "do_sys_*"... Hit Ctrl-C to end.
    ^C
    FUNC                                    COUNT     TIME (nsecs)
    do_sys_poll                              6783     213928440379
    do_sys_open                              3127         43172656
    do_sys_ftruncate.constprop.15              39           189623
    

    此解决方案依赖 bcc 在内核中加载 BPF 程序,但也可以在 C 中执行所有操作。您可以找到 get started with bcc on the repository 的教程。

    【讨论】:

      猜你喜欢
      • 2022-07-08
      • 1970-01-01
      • 1970-01-01
      • 2012-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-19
      • 1970-01-01
      相关资源
      最近更新 更多