gdb
非常方便的多重劣势支持来调试进程树:
(gdb) set detach-on-fork off
(gdb) set schedule-multiple on
(gdb) set follow-fork-mode parent
(gdb) break PostgresMain
(gdb) break PostmasterMain
现在需要让事情继续运行,直到我在一些尚未产生的劣质中达到未来的断点之一。
但是,只要下级正常退出,
gdb
似乎就会“有帮助”地暂停,或者至少阻止下级的清理,以便其父级的wait()
可以返回:
(gdb) c
[New process 16505]
process 16505 is executing new program: /home/craig/pg/bdr/bin/pg_config
Reading symbols from /home/craig/pg/bdr/bin/pg_config...done.
[Inferior 2 (process 16505) exited normally]
(gdb) info inferior
Num Description Executable
* 2 <null> /home/craig/pg/bdr/bin/pg_config
1 process 16501 /usr/bin/make
(gdb) inferior 1
[Switching to inferior 1 [process 16501] (/usr/bin/make)]
[Switching to thread 1 (process 16501)]
#0 0x0000003bc68bc502 in __libc_wait (stat_loc=0x7fffffffbc78) at ../sysdeps/unix/sysv/linux/wait.c:30
30 return INLINE_SYSCALL (wait4, 4, WAIT_ANY, stat_loc, 0,
(gdb)
所以我必须没完没了:
(gdb) inferior 1
(gdb) c
继续。大约 70 次,然后我在一个孩子的孩子的孩子中达到了所需的断点。
我认为发生的事情是
gdb
将进程退出视为停止事件,并且由于non-stop
设置为off
(默认值),当一个线程停止时,它会停止所有下级线程中的所有线程。然而,这个下级已经终止了,这不是一个正常的停止事件,所以你不能只是cont
它,你必须先切换到另一个进程。
是否有某种方法可以阻止 gdb 在每个下级出口处暂停?我本来希望
follow-fork-mode parent
和 schedule-multiple on
能够达到目的,但是 gdb
似乎仍然想在劣质退出时停止。
我想我正在寻找类似“skip proc-exit”的东西,或者是一个可以更改处理程序策略的虚拟信号,这样它就不会停止。
set non-stop on
似乎这应该是正确的答案,但我怀疑它对于多个劣等者来说已经被破坏了。
如果我使用
non-stop on
,那么在第一个退出陷阱之后,gdb
的内部状态表明劣质1正在运行:
(gdb) info inferior
Num Description Executable
* 1 process 20540 /usr/bin/make
(gdb) info thread
Id Target Id Frame
* 1 process 20540 "make" (running)
(gdb) cont
Continuing.
Cannot execute this command while the selected thread is running.
但内核认为它在
ptrace_stop
上被阻止:
$ ps -o "cmd,wchan" -p 20540
CMD WCHAN
/usr/bin/make check ptrace_stop
...直到
gdb
分离或被杀死之前它不会取得任何进展。发送给进程的信号将被忽略,并且 interrupt
中的 gdb
没有任何作用。
我在 x86_64 上使用
GNU gdb (GDB) Fedora 7.7.1-18.fc20
。
在偶然发现一篇顺便引用它的帖子后我发现缺少的魔法是
set target-async on
旁边set non-stop on
。
不间断模式,正如预期的那样,意味着每当劣等退出时,gdb 都不会停止一切。需要
target-async
才能使其在 gdb 7.7 上真正正常工作;这是 7.8 的默认值。
所以完整的咒语是:
set detach-on-fork off
set schedule-multiple on
set follow-fork-mode parent
set non-stop on
set target-async on
对于 7.8,删除
target-async on
,并添加 set print symbol-loading off
以减少噪音。
以下 gdb 的 Python 扩展将切换回第一个下级并在每次停止后恢复执行。
这感觉像是一个彻头彻尾的黑客攻击,但它确实有效。当进程退出时,它会设置一个标志,指示它在退出时停止,然后切换到原始进程。然后
gdb
将停止执行,并发出停止事件。我们检查停止是否是由我们的停止事件引起的,如果是,我们立即继续。
代码还设置了我正在使用的断点和多进程设置,所以我可以只
source thescript.py
和run
。
my_stop_request = False
gdb.execute("set python print-stack full")
gdb.execute("set detach-on-fork off")
gdb.execute("set schedule-multiple on")
gdb.execute("set follow-fork-mode parent")
gdb.execute("set breakpoint pending on")
gdb.execute("break PostgresMain")
gdb.execute("break PostmasterMain")
gdb.execute("set breakpoint pending off")
def do_continue():
gdb.execute("continue")
def exit_handler(event):
global my_stop_request
has_threads = [ inferior.num for inferior in gdb.inferiors() if inferior.threads() ]
if has_threads:
has_threads.sort()
gdb.execute("inferior %d" % has_threads[0])
my_stop_request = True
gdb.events.exited.connect(exit_handler)
def stop_handler(event):
global my_stop_request
if isinstance(event, gdb.SignalEvent):
pass
elif isinstance(event, gdb.BreakpointEvent):
pass
elif my_stop_request:
my_stop_request = False
gdb.post_event(do_continue)
gdb.events.stop.connect(stop_handler)
一定有比这更简单的方法。真丑。