gdb上でattachしてプロセスをデバッグ中にCtrl+Cしてinterruptすることができないという問題を追っていたが、結論はgdbコミュニティ本体側のバグ(gdbserver)のバグだった。
以下のユースケースで問題となる。
・a) remoteシステムのプロセスをgdbserverを使ってデバッグしようとしていて
・b) 動いている途中のプロセスをgdbserver --attachで捕まえて
・c) localシステムのgdbでtarget remoteでリモートでバッグ開始し
・d) 一度 continue して
・e) breakやsignalなどで止まる前にlocalシステムのgdb上でCtrl+Cしてinterruptさせる
このとき、interrupt要求を受けたremoteシステムのgdbserverはattach対象のプロセスにSIGINTを送って止めようとするが、この送るときにkill(-pid, SIGINT)している。結果、プロセスにSIGINTが送られるのではなく、プロセスグループにSIGINTが送られる。すると2つの問題が起こる。
・A) attachしているプロセスがプロセスグループリーダじゃない場合、SIGINTは誰にも送られない。つまりattachしているプロセスにもSIGINTが送られず、attachしているプロセスは止まらない。
・B) attachしているプロセスがプロセスグループリーダの場合、attachしているプロセスを止めてデバッグすることはできるが、同じプロセスグループに属するプロセス(このプロセスの子どもや孫たち)にもSIGINTが送られ、たいていはデフォルトのシグナルハンドラが呼ばれ終了してしまう。
該当バグはこのcommitで入った。78708b7c8ccc2138880217de9bd60eceff683f10 デバッグ対象のプロセスのmainスレッドがすでにexitしていた場合にtpkillではSIGINTを送ることができずinterruptできないという問題を直しているように見える。が、多くの人にとってはここで提唱している問題の方が引っかかる気がするんだが。→ 当時のメールのやりとりのログ
ちなみに、a)でattachではなく最初から動かした場合(gdbserver hostpc:2345 /usr/local/bin/test_program など)、gdbserverがデバッグ対象プロセスをsetpgid()した状態で動かすため、必ずプロセスグループリーダになる。つまり、A)は起こらない、がB)は起こる。
この記事を書くために改めて調べ直してみると、同じことを指摘するBugzilla(#18945)が登録されていた。が、そもそもSIGINTの仕様がはっきりしていない上に、元バグと指摘バグのどっちが大事か(mainスレッドがいない場合に止められないというバグと、gdbserverでattachした場合に止められないというバグ)のどちらを直す方がいいのか、のところで話が止まっている。
でも、ConsoleでCtrl+CしたときにforegroundなプロセスグループにSIGINTが飛ぶ話と、gdbでデバッグ途中にinterruptしようと思ったときにgdbがデバッグ対象プロセスにSIGINTを送るというやり方をしている話とは、仕様の上では全く関係ない話だと思うんだが・・・
というわけで、少なくとも私のところでは非常に困るので、78708b7c8ccc2138880217de9bd60eceff683f10 をrevertさせてもらった。
ちなみに、プロセスグループやConsole上でのCtrl+Cの話を理解してない場合は話についてこれないので、そういう場合はこの辺を読んでください。