c++ - docker 容器中的可执行文件不会从 gdb 远程调试中注册断点
问题描述
远程设置
我需要调试一个复杂的 C++ 程序,该程序安装在由 Kubernetes 控制的 docker 容器中。docker 容器还提供了一个 gdbserver 并暴露了容器端口 44444。
主机设置
控制和检查程序的 gdb 部分设置在另一个 docker 容器中。这是因为 SUSE 环境仅在此容器中可用,而不是在 VM Box 中的我的 Ubuntu 18.04 机器上。
本地调试效果很好
在 SUSE docker 容器中本地调试程序效果很好。程序在指定的断点处停止,这些断点也在远程调试中指定。所有断点都单独定义在程序的基本源代码文件中,而不是任何库中。
已验证,远程 docker 容器中的可执行文件与主机容器中的可执行文件相同;它已使用调试符号和非优化代码 (-ggdb -O0) 进行编译。
问题
远程调试程序只缺少在主机上定义的断点处停止。容器中的程序在后台启动。当 gdbserver 附加它的 process_id 时,程序会暂停,直到在 gdb 主机会话中发出“继续”并转发到远程容器中的 gdbserver。
该程序部署了基本的 C++ 类文件和共享程序库以及共享项目库。它以参数启动并在作业完成后退出。
当程序启动时,它会读取配置文件、连接到数据库、读取数据库条目、准备数据并将其格式化为 XML 格式的条目,并将它们写入输出文件。
HelloWorld 远程调试测试效果很好
为了验证通过 gdbserver 端口的远程调试设置和连接是否正常工作,我创建了一个简单的 HelloWorld C++ 程序并将其复制到同一个远程 docker 容器中并测试其中的断点行为。
当 HelloWorld 程序在容器中运行时,远程调试测试场景运行成功:
内部容器端口 44444 映射到相同的外部端口 id 44444:
$ kubectl port-forward eric-bss-eb-tools-65c4955565-xdqtx 44444:44444
Forwarding from 127.0.0.1:44444 -> 44444
Forwarding from [::1 ]:44444 -> 44444远程容器中的 HelloWorld 在后台启动并休眠几秒钟
bash-4.4$ ./HelloWorld &
[1] 1068gdbserver 附加到 HelloWorld process_id 并等待转发 gdb 命令
bash-4.4$ ./gdbserver :44444 --attach 1068 // gdbserver 使用暴露的端口
Attached; pid = 1068
监听端口 44444主机容器中的 gdb 在 HelloWorld 源代码文件夹中以 TUI 模式启动
$ gdb -tui HelloWorld
从 HelloWorld 读取符号...完成。
(gdb) b 13
0x400b2d 处的断点 1:文件 HelloWorld.cpp,第 13 行。
(gdb) b 15
0x400b37 处的断点 2:文件 HelloWorld.cpp,第 15 行。
gdb 通过 localhost 和(外部)端口 id 44444 (gdb) target remote :44444
(gdb) c
继续连接到 gdbserver 。远程 HelloWorld 在断点 2 处停止;可以检查变量;可以发出进一步的 gdb 命令,例如“下一步”和“步骤”;一切都很聪明
目标程序远程调试不会在断点处停止
当容器中的目标 C++ 程序使用相同的场景进行调试时,它不会在定义的断点处停止:
工作流与 HelloWorld 测试场景相同,但断点是在gdb 与 gdbserver 建立连接后定义的(目标远程:44444)。
这已按照此答案的第二条评论中的建议完成:(远程 gdb 调试不会在断点处停止)。尽管如此,即使在建立与远程目标的连接之后定义断点,它们仍然会被忽略。
远程 docker 容器中的程序由 gdbserver 暂停,并在 gdb 发出“继续”命令时继续执行,但不会在任何断点处停止。
根据其他类似的问题描述,我尝试了几个提示,但仍然忽略断点。
例如,使用硬件断点,如在此处相同请求的答案中所建议的那样:(远程 gdb 调试不会在断点处停止)在我的环境中禁止使用 securityContext: privileged=true 运行远程 docker 容器,因此无法对其进行测试。请参阅此处的建议:(当我从 Docker 容器内部运行 gdb 时,它没有遇到任何断点)
在定义的断点处停止的 docker 容器中进行远程调试时,我缺少什么?
解决方案
由于 Ubuntu(版本 >= 10.10)中的安全性增强,不允许用户对不是调试器后代的进程进行 ptrace。
默认情况下,进程 A 无法跟踪正在运行的进程 B,除非 B 是 A 的直接子级(或 A 以 root 身份运行)。
仍然始终允许直接调试,例如gdb EXE
and strace EXE
。
可以通过将值/proc/sys/kernel/yama/ptrace_scope
从 1(=默认)更改为 0(=所有进程允许跟踪)来放松限制。可以通过以下方式更改安全设置:
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
HelloWorld 远程调试测试效果很好
HelloWorld 容器中的远程调试是怎么发生的?
HelloWorld 容器是USER userName
在 Dockerfile 中创建的,该用户名与登录到 Ubuntu 的用户名相同。
用于部署开发容器(使用要调试的 C++ 程序)的 Dockerfile 定义了与我的 Ubuntu 登录中使用的不同的用户名和组名。
ptrace 范围描述的所有学分都属于以下帖子,请参阅 Eliah Kagan 的第二个回答 - 感谢您的详尽解释!- 这里:
https ://askubuntu.com/questions/143561/why-wont-strace-gdb-attach-to-a-process-even-though-im-root
推荐阅读
- sharepoint - SharePoint 定期活动开始日期
- r - 基于另一列在 R 是/否中创建新列
- angular - Angular 6 路由器无法导航
- python - 如何使用 django 和 celery 在特定时间开始任务
- windows - nssm + Selenium Server 3 + Windows 10:Behat 测试未启动 Chrome
- android - Android:如何创建不受限制的后台服务,超出 APK 权限?
- java - jsonschema2pojo maven 插件不生成 Java 类
- javascript - 如何使用 Cucumber\Ruby\PageObject 从浏览器 JavaScript 全局变量中获取值?
- mysql - 如何根据 MySQL 中另一列的值输出包含组合总数的行?
- proftpd - 如何使用proftp 完全关闭ftp?