メモ:WindowsでのRのクラッシュをgdbを使って調べる

前回はWinDbgを使ったけど、今回はうまく見れなかったのでgdbを使ってみる。 (gdbなので、Windows以外でも似たような感じでデバッグできるはず)

gdbをインストール

Rtools Bash(これを書いている段階ではRtools40の方)を立ち上げて、以下でgdbをインストール

pacman -S ucrt64/mingw-w64-ucrt-x86_64-gdb

Rをデバッグビルド

このやり方でビルドする。quick-build.shmakeの行にdebug=Tを付け加えるといいらしい(どう違うのかあまり把握してない)。

Rを実行

まずはビルドしたRを実行する。

Sys.getpid()

でPIDが得られるので、別のRtools Bashを立ち上げて、そのPIDをアタッチする。 が、デバッグしたいライブラリを読み込んでからでないとシンボルが見えないので読み込む。 今回はgridの中の関数にブレークポイントを仕掛けたいので、

library(grid)

しておく。準備が終われば、以下のコマンドでアタッチする。

/ucrt64/bin/gdb -p <PID>

bブレークポイントを仕掛ける。今回は、doSetViewport()という関数で問題が起こっているようなのでそこに仕掛ける。

(gdb) b doSetViewport
Breakpoint 1 at 0x7ffacc6b55e0: file grid.c, line 150.

仕掛けられたら、 c で R のセッション側にコントロールを戻す。

(gdb) c
Continuing.

Rのコードを実行して指定したブレークポイントを通過すると、Rの実行が止まってgdbで操作できるようになる。

Thread 1 hit Breakpoint 1, doSetViewport (vp=vp@entry=0x1654e77d080,
    topLevelVP=topLevelVP@entry=TRUE, pushing=pushing@entry=TRUE,
    dd=dd@entry=0x1654cfb3c80) at grid.c:150
150     {

btでバックトレースを見たり、

(gdb) bt
#0  doSetViewport (vp=vp@entry=0x1654e77d080,
    topLevelVP=topLevelVP@entry=TRUE, pushing=pushing@entry=TRUE,
    dd=dd@entry=0x1654cfb3c80) at grid.c:150
#1  0x00007ffacc6cb176 in initVP (dd=dd@entry=0x1654cfb3c80) at viewport.c:406
#2  0x00007ffacc6b53c7 in dirtyGridDevice (dd=dd@entry=0x1654cfb3c80)
    at grid.c:112
#3  0x00007ffacc6b55a8 in dirtyGridDevice (dd=0x1654cfb3c80) at grid.c:124
#4  L_gridDirty () at grid.c:122

...略...

n(先に進む)とかs(先に進む&次の関数に入る)でステップ実行していく

(gdb) n
157         getDeviceSize((dd), &devWidthCM, &devHeightCM);
(gdb) n
158         if (!topLevelVP && pushing) {
(gdb) n
192         if (LOGICAL(gridStateElement(dd, GSS_RESOLVINGPATH))[0]) {
(gdb) n
201         } else if (isClipPath(viewportClipSXP(vp))) {
(gdb) n
219             if (viewportClip(vp) == NA_LOGICAL) {
(gdb) n
232             } else if (viewportClip(vp)) {
(gdb) n
236                 double rotationAngle = REAL(viewportRotation(vp))[0];

...略...


(gdb) n
353             UNPROTECT(1);
(gdb) n
358         if (LOGICAL(gridStateElement(dd, GSS_RESOLVINGPATH))[0]) {
(gdb) s
gridStateElement (dd=dd@entry=0x1654cfb3c80,
    elementIndex=elementIndex@entry=16) at state.c:154
154         return VECTOR_ELT((SEXP) dd->gesd[gridRegisterIndex]->systemSpecific,

で、今回はどうやらここがダメそう、ということが分かった。

(gdb) s
[New Thread 9448.0x54fc]
../../gdb-10.1/gdb/infrun.c:2614: internal-error: void resume_1(gdb_signal): Assertion `pc_in_thread_step_range (pc, tp)' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) [answered Y; input not from terminal]

もうちょっと変数の中身とかを覗けば色々わかるのかもしれないけど今日はこのへんで終わり。バグの原因がぜんぜん特定できない...