gpt4 book ai didi

c - 如何使用 C 以编程方式回溯 fork 子项的崩溃

转载 作者:太空宇宙 更新时间:2023-11-04 07:53:33 26 4
gpt4 key购买 nike

是否有可能使用 C/C++ 代码回溯子进程在 Linux 中崩溃的位置?我想要做的是:

  1. fork 一个新的子进程并获取它的 PID
  2. 等待 fork 的子进程崩溃...可能使用 SIGCHLD 的信号处理程序,或使用 waitpid()/waitid()
  3. 在崩溃的位置检索 child 的堆栈跟踪

这将使父进程在附加进程崩溃时表现得类似于调试器。您可以假设子进程是使用调试符号编译的,而父进程具有 root 权限。

实现此类功能的最简单方法是什么?

最佳答案

在 Linux 中使用作为 GNU C 库的一部分提供的 libSegFault 库要简单得多。在我的系统上,它安装在 /lib/x86_64-linux-gnu/libSegFault.so 中。

您需要做的就是将 SEGFAULT_SIGNALS 环境变量设置为 all(这样您就可以捕获库支持的所有崩溃原因),可选 SEGFAULT_OUTPUT_NAME 指向写入堆栈跟踪的文件(默认为标准错误),LD_PRELOAD 指向段错误库。只要进程不修改这些环境变量,它们也适用于所有子进程。

例如,如果 ./yourprog 是 fork 一个崩溃的子程序的程序,并且您希望堆栈跟踪到 ./yourprog.stacktrace,运行

SEGFAULT_SIGNALS=all \
SEGFAULT_OUTPUT_NAME=./yourprog.stacktrace \
LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so \
./yourprog

或全部在一行中,不带反斜杠 (\)。

唯一的缺点是每次崩溃都会覆盖现有文件,因此您只能看到最新的文件。如果您安装了 /proc,则故障转储包括崩溃时刻进程的回溯和内存映射。


如果你坚持要在自己的C程序中做,我建议你先看看libSegFault sources .

重点是,堆栈跟踪必须由进程本身转储; parent 无法访问它。为此,您可以使用例如将代码注入(inject)子进程。 LD_PRELOAD 环境变量(Linux 中的动态链接器控制变量之一)。 (请注意,堆栈跟踪等是在信号处理程序上下文中完成的,因此只能使用异步信号安全函数。)

例如,父进程可以创建一个管道,并在执行目标进程之前将其写入端移动到子进程中的特定描述符,您的助手预加载库路径在 LD_PRELOAD 中。

辅助预加载库插入signal()sigaction(),可能还有sigprocmask()sigwait()sigwaitinfo()pthread_sigmask(),以确保在传递此类信号时执行辅助库崩溃转储信号处理程序 (SIGSEGVSIGBUSSIGILL,可能还有 SIGTRAP)。信号处理程序执行堆栈转储(并打印/proc/PID/maps),然后将信号处理设置为默认值,并重新发出信号(使用 raise())。

本质上,它归结为与上述 libSegFault 相同的操作,除了使用您自己的 C 代码。


如果您不想向子进程注入(inject)代码,或者管理信号处理程序过于复杂,您可以使用ptrace。相反。

当 tracee 被信号(SIGKILL 除外)杀死时,接收信号的线程首先停止(“signal-delivery-stop”),所以在让子进程继续/死亡之前,跟踪器可以检查其堆栈(和被跟踪者的内存映射)。

在实践中,ptracing 更具侵入性,因为有许多事件会导致 tracees 线程停止。对于多线程进程,它也比 LD_PRELOAD 方法复杂得多,因为 ptrace 可以控制 tracee 中的各个线程;还有更多细节需要正确处理。

关于c - 如何使用 C 以编程方式回溯 fork 子项的崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52149094/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com