是否有可能使用C / C ++代码回溯Linux中子进程崩溃的位置?我想做的是以下内容:
当附加的进程崩溃时,这将使父进程的行为类似于调试器。您可以假设子进程使用调试符号进行编译,父进程具有root权限。
实现此类功能的最简单方法是什么?
在Linux中使用作为GNU C库一部分提供的libSegFault
库要简单得多。在我的系统上,它安装在/lib/x86_64-linux-gnu/libSegFault.so
中。
您需要做的就是将SEGFAULT_SIGNALS
环境变量设置为all
(这样您就可以捕获库支持的所有崩溃原因),可选择SEGFAULT_OUTPUT_NAME
指向堆栈跟踪写入的文件(默认为标准错误),LD_PRELOAD
为指向segfault库。只要该过程不修改这些环境变量,它们也适用于所有子进程。
例如,如果./yourprog
是一个让孩子崩溃的程序,并且你想要堆栈跟踪到./yourprog.stacktrace
,那么运行
SEGFAULT_SIGNALS=all \
SEGFAULT_OUTPUT_NAME=./yourprog.stacktrace \
LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so \
./yourprog
或全部在一行中没有反斜杠(\
)。
唯一的缺点是每次崩溃都会覆盖现有文件,因此您只会看到最新的文件。如果你安装了/proc
,那么崩溃转储包括一个回溯和崩溃时的进程的内存映射。
如果你坚持在你自己的C程序中这样做,我建议你先看看libSegFault sources。
关键是,堆栈跟踪必须由进程本身转储;父母无法访问它。为此,您可以使用以下方法将代码注入子进程: LD_PRELOAD
环境变量(这是Linux中的动态链接器控制变量之一)。 (请注意,堆栈跟踪等是在信号处理程序上下文中完成的,因此只应使用异步信号安全函数。)
例如,父进程可以创建管道,并在执行目标进程之前将其写入端移动到子进程中的特定描述符,并在LD_PRELOAD中使用帮助程序预加载库路径。
辅助预载库插入signal()
,sigaction()
,可能还有sigprocmask()
,sigwait()
,sigwaitinfo()
,pthread_sigmask()
,以确保在传递此类信号时执行辅助程序库崩溃转储信号处理程序(SIGSEGV
,SIGBUS
,SIGILL
,可能是SIGTRAP
)。信号处理程序执行堆栈转储(并打印/ proc / PID / maps),然后将信号处理设置为默认值,并重新引发信号(使用raise()
)。
从本质上讲,它归结为与上面的libSegFault相同,除了你自己的C代码。
如果您不想将代码注入子进程,或者管理信号处理程序太复杂,则可以使用ptrace。
当tracee被一个信号(SIGKILL
除外)杀死时,接收信号的线程首先停止(“signal-delivery-stop”),因此跟踪器可以检查其堆栈(以及tracee的内存映射),然后再让子进程继续/死亡。
在实践中,ptracing更具侵入性,因为有许多事件会导致跟踪线程停止。对于多线程进程而言,它比LD_PRELOAD方法复杂得多,因为ptrace可以控制tracee中的各个线程;还有更多细节要做对。