如何在Docker容器中的linux perf工具中获得调试符号?

时间:2016-08-12 23:56:56

标签: linux docker linux-kernel perf

我正在使用基于" ubuntu"的Docker容器。标签并不能让linux perf工具显示调试符号。

以下是我为证明此问题所做的工作。

首先我启动一个容器,这里有一个交互式shell。

docker run -t -i ubuntu:14.04 /bin/bash

然后从容器提示符我安装linux perf工具。

apt-get update
apt-get install -y linux-tools-common linux-tools-generic linux-tools-`uname -r`

我现在可以使用perf工具了。我的内核是3.16.0-77-generic

现在我安装gcc,编译测试程序,并尝试在perf record下运行。

apt-get install -y gcc

我将测试程序粘贴到test.c

#include <stdio.h>

int function(int i) {
    int j;
    for(j = 2; j <= i / 2; j++) {
        if (i % j == 0) {
            return 0;
        }
    }
    return 1;
}

int main() {
    int i;
    for(i = 2; i < 100000; i++) {
        if(function(i)) {
            printf("%d\n", i);
        }
    }
}

然后编译,运行和报告: gcc -g -O0 test.c && perf record ./a.out && perf report

输出看起来像这样: 72.38% a.out a.out [.] 0x0000000000000544 8.37% a.out a.out [.] 0x000000000000055a 8.30% a.out a.out [.] 0x000000000000053d 7.81% a.out a.out [.] 0x0000000000000551 0.40% a.out a.out [.] 0x0000000000000540

即使可执行文件具有符号信息,也没有符号。

在容器外执行相同的一般步骤可以正常工作,并显示如下内容: 96.96% a.out a.out [.] function 0.35% a.out libc-2.19.so [.] _IO_file_xsputn@@GLIBC_2.2.5 0.14% a.out [kernel.kallsyms] [k] update_curr 0.12% a.out [kernel.kallsyms] [k] update_cfs_shares 0.11% a.out [kernel.kallsyms] [k] _raw_spin_lock_irqsave

在主机系统中,我已经通过成为root并执行以下操作来打开内核符号: echo 0 > /proc/sys/kernel/kptr_restrict

如何让容器化版本正常工作并显示调试符号?

2 个答案:

答案 0 :(得分:7)

使用-v /:/host标志运行容器并在容器中使用perf report标记运行--symfs /host修复它:

Samples: 4K of event 'cycles', Event count (approx.): 3420992473 96.59% a.out a.out [.] function 2.93% a.out [kernel.kallsyms] [k] 0xffffffff8105144a 0.13% a.out [nvidia] [k] 0x00000000002eda57 0.11% a.out libc-2.19.so [.] vfprintf 0.11% a.out libc-2.19.so [.] 0x0000000000049980 0.09% a.out a.out [.] main 0.02% a.out libc-2.19.so [.] _IO_file_write 0.02% a.out libc-2.19.so [.] write

为什么它不能正常工作,perf script的输出有点说明:

...
           a.out    24 3374818.880960: cycles:  ffffffff81141140 __perf_event__output_id_sample ([kernel.kallsyms])
           a.out    24 3374818.881012: cycles:  ffffffff817319fd _raw_spin_lock_irqsave ([kernel.kallsyms])
           a.out    24 3374818.882217: cycles:  ffffffff8109aba3 ttwu_do_activate.constprop.75 ([kernel.kallsyms])
           a.out    24 3374818.884071: cycles:            40053d [unknown] (/var/lib/docker/aufs/diff/9bd2d4389cf7ad185405245b1f5c7d24d461bd565757880bfb4f970d3f4f7915/a.out)
           a.out    24 3374818.885329: cycles:            400544 [unknown] (/var/lib/docker/aufs/diff/9bd2d4389cf7ad185405245b1f5c7d24d461bd565757880bfb4f970d3f4f7915/a.out)
...

请注意/var/lib/docker/aufs路径。这是来自主机,因此它不会存在于容器中,您需要帮助perf report找到它。这可能是因为mmap事件由任何cgroup外部的perf跟踪,而perf不会尝试重新映射路径。

另一个选择是运行perf主机端,如sudo perf record -a docker run -ti <container name>。但是集合必须在这里是系统范围的(-a标志),因为容器是由docker守护程序进程生成的,该进程不在我们运行的docker客户端工具的进程层次结构中。

答案 1 :(得分:3)

另一种不需要更改运行容器的方式(因此您可以分析已经运行的进程)是使用bindfs在主机上安装容器的根目录:

bindfs /proc/$(docker inspect --format {{.State.Pid}} $CONTAINER_ID)/root /foo

然后将perf报告作为perf report --symfs /foo

运行

您必须在系统范围内运行perf record,但您可以将其限制为仅收集特定容器的事件:

perf record -g -a -F 100 -e cpu-clock -G docker/$(docker inspect --format {{.Id}} $CONTAINER_ID) sleep 90