当我从 Docker 容器中运行 gdb 时,它没有遇到任何断点

Posted

技术标签:

【中文标题】当我从 Docker 容器中运行 gdb 时,它没有遇到任何断点【英文标题】:gdb does not hit any breakpoints when I run it from inside Docker container 【发布时间】:2016-04-23 16:54:37 【问题描述】:

问题

如果我从主机编译和运行,我可以设置并到达断点,但如果我从 docker 容器中执行此操作,gdb 不会到达设置的断点。

重现步骤(所有 sn-ps 都已准备好复制粘贴)

创建一个 docker 文件:

cat << EOF > Dockerfile
FROM ubuntu
RUN apt-get update
RUN apt-get install -y build-essential gdb
EOF

构建映像并在其中运行交互式会话:

docker build -t gdb_problem_testing . && docker run --rm -it  gdb_problem_testing bash

从容器内部创建小的 main.cpp,编译并运行 gdb:

cat <<EOF > main.cpp && g++ -g main.cpp && gdb -ex 'break 5' -ex 'run' ./a.out
#include <iostream>

int main(int argc, const char *argv[])

    std::cout << "hi\n";
    return 0;

EOF

观察 gdb 输出:

GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
[Skipped gdb greeting]
Reading symbols from ./a.out...done.
Breakpoint 1 at 0x40078c: file main.cpp, line 5.
1   #include <iostream>
2   
3   int main(int argc, const char *argv[])
4   
5       std::cout << "hi\n";
6       return 0;
7   
Starting program: /a.out 
hi
During startup program exited normally.
(gdb) 

从输出可以看出断点没有被命中,虽然程序被执行(打印'hi')并成功退出。我想这里最重要的是程序确实运行了,并且消息在启动程序正常退出期间是一种异常行为(根据GDB ignores my breakpoints)

问题

是什么阻止了 gdb 设置断点以及如何解决这个问题?

到目前为止我尝试了什么

    按照here 的建议,我尝试在/etc/apparmor.d/docker 中更改一行(我是在主机中完成的): 将profile docker-default flags=(attach_disconnected,mediate_deleted) 替换为profile docker-default flags=(attach_disconnected,mediate_deleted,complain) 。然后运行docker容器,编译,gdb。结果是一样的:During startup program exited normally

    按照another answer 中的建议,在容器内,我尝试执行strace -f -o syscall.txt gdb ./a.out,但出现以下错误:

    strace: test_ptrace_setoptions_followfork: PTRACE_TRACEME doesn't work: Permission denied
    strace: test_ptrace_setoptions_followfork: unexpected exit status 1
    

    但我不明白如何解决这个问题。我尝试以 root 身份启动容器:sudo docker run --rm -it gdb_problem_testing bash,然后尝试了 strace——这给了我同样的错误。我必须承认我不明白 docker 是如何管理用户权限的,即容器内的 root 拥有哪些用户权限以及它从谁那里继承了这些权限(来自 docker 守护进程?)。由于我能够打断点,所以当我在主机中运行 gdb 时,我怀疑我的问题会归结为用户权限,但我不知道如何解决。

    在主机中,我尝试按照 another answer 中的建议执行 echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

【问题讨论】:

您的主机操作系统是什么(发行版和内核版本)?我们可以尝试重现错误。您的示例运行良好 - gdb 在第 5 行成功停止 - 在 Ubuntu 15.04 和 14.04.3 x86_64 的香草、完全修补版本上使用 Ubuntu 14.04.2 的 docker 映像。 非常感谢您试一试。它对你有用的事实给了我一个完全不同的观点。我的主机操作系统是 Ubuntu。 $ uname -a Linux LT0377 3.19.0-42-generic #48~14.04.1-Ubuntu SMP Fri Dec 18 10:24:49 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux。我不明白 Docker 映像中的 Ubuntu 版本是什么。如果我在容器内执行 uname -a 操作,我会得到与主机相同的输出。 @MarkPlotnick 你能告诉我你的 Docker 版本是什么吗? 我使用了 Ubuntu 存储库中的 docker 版本。在 14.04.3 和 15.04 上,它都是 Docker version 1.6.2, build 7c8fca2 docker 存储库中的 ubuntu 映像似乎是 14.04.3;我错了,它是 14.04.2。 【参考方案1】:

2020.01.04 更新:使用 Kevin W Matthews 提供的 answer —— 更好,因为它授予必要的个人功能,而无需提升整个容器。


tldr;使用

docker run --privileged

更长:我有一些problems with gdb in docker---它正在尝试(但失败)禁用address space layout randomization---但仅在docker-machine上,而不是在我的本机Linux主机上。

当 gdb 未能禁用 ASLR 时,我的所有断点都将被忽略。使用 --privileged 标志解决了我的问题。您的里程可能会有所不同。

【讨论】:

感谢您的回复!不幸的是,我无法再对其进行测试,因为我无法访问此环境。如果其他人发现您的解决方案有帮助,我会接受它作为答案。 这对我有帮助。令人费解的是为什么没有端点被击中,但随后使用 --privileged 运行容器!我现在可以在容器中正确使用 gdb 也帮了我!在 docker 中,可能需要将当前容器提交到一个新镜像中,然后docker rm &lt;container-name/id&gt;,然后基于新提交的镜像启动一个新容器(使用--privileged)以重新启动作业。 我今天遇到了这个问题, --privileged 参数解决了它。谢谢。【参考方案2】:

感谢@rubicks 的回答。

如果您不能使用--privileged 选项(例如,您正在使用来自云的容器),您可以从您的程序中打印堆栈跟踪:

How to automatically generate a stacktrace when my gcc C++ program crashes

【讨论】:

【参考方案3】:

我没有提升整个容器,而是能够使用该选项

--security-opt seccomp=unconfined

修复地址空间随机化问题。

有些人还建议启用ptrace 功能

--cap-add=SYS_PTRACE

但这似乎对我没有任何影响。

以下是 Docker compose 的相同设置:

security_opt:
  - seccomp:unconfined
cap_add:
  - SYS_PTRACE

详情取自this Stack Overflow post。

【讨论】:

这个答案比我的好。我正在编辑我的以参考您的。

以上是关于当我从 Docker 容器中运行 gdb 时,它没有遇到任何断点的主要内容,如果未能解决你的问题,请参考以下文章

在docker中使用gdb调试程序

Python 脚本在 docker 容器中找不到使用 CRON 运行的 ENV 变量

Docker容器内使用gdb

Docker容器内使用gdb

Docker容器内使用gdb

在 Centos 6.6 docker 容器中安装厨师服务器