gdbを経由して、Rの内部構造を知る

Rのデータ構造はstrとかで知ることができますが、Cレベルでもどういう風になっているか知りたい!!という時のためのメモ。

まず、Rを立ち上げます。次に起動しているプロセスを調べます。

/Users/yasuhisa% ps ax | grep R.framework 
/Users/yasuhisa% ps ax | grep R.framework 
51048   p0  Ss+    0:00.94 /Library/Frameworks/R.framework/Resources/bin/exec/R --no-readline

gdbをpオプション付きで起動させます。こういうのをattachというそうです。

gdb -p 51048

すると下のようなのが表示されます。"Attaching to process 51048."となって、attachできたことが分かります。

Current directory is ~/
GNU gdb 6.3.50-20050815 (Apple version gdb-768) (Tue Oct  2 04:07:49 UTC 2007)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".
/Users/yasuhisa/51048: No such file or directory.
Attaching to process 51048.
Reading symbols for shared libraries . done
Reading symbols for shared libraries ...................................................................................... done
0x9037c5e2 in select$DARWIN_EXTSN ()
(gdb) 

何でもいいんですが、実験してみましょう。Rでrnormを発生させるとどうなるかを見てみます。ここで勉強したことを参考にrnorm.cを探し出します。

/Users/yasuhisa/Downloads/R-2.8.0/src% find . -name rnorm.c  
./nmath/rnorm.c

次にR側で、以下のコマンドを実行しようとします。入力されただけで実行はまだされないかと思います。

a <- rnorm(10,mean=500,sd=20)
a

このままcontinueをしても何も情報を見れないのでrnorm.cの終わりのほうでブレイクポイントを作って止めることにしてみます。

0x9037c5e2 in select$DARWIN_EXTSN ()
(gdb) b rnorm.c:41
Breakpoint 1 at 0x4f345a: file rnorm.c, line 41.

ブレイクポイントができたことが確認できます。ブレイクポイントのところまで実行させてみましょう。

(gdb) c
Continuing.

Breakpoint 1, Rf_rnorm (mu=500, sigma=0) at rnorm.c:41
41	}

41行目まで実行されたようです。lとtypeすると周辺のコードがどうなっているかを見ることができます。

(gdb) l
36		ML_ERR_return_NAN;
37	    if (sigma == 0. || !R_FINITE(mu))
38		return mu; /* includes mu = +/- Inf with finite sigma */
39	    else
40		return mu + sigma * norm_rand();
41	}

rnormの変数muとsigmaがどうなっているか見てみましょう。

(gdb) p mu
$1 = 500
(gdb) p sigma
$2 = 0
(gdb) c
Continuing.

muの500はきちんと表示されました。が、sigmaのほうはなぜか表示されません。理由はまだよく分かっていませんw。


今度は違う例でやってみましょう。apply関数の中でどうなっているかを見てみようと思います。その前にrnormで作ったブレイクポイントがもう必要ないので消します。i bで確認して、deleteで消せます。

(gdb) i b
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x004f345a in Rf_rnorm at rnorm.c:41
	breakpoint already hit 23 times
(gdb) delete 1
(gdb) i b
No breakpoints or watchpoints.

apply.cを探します。

/Users/yasuhisa/Downloads/R-2.8.0/src% find . -name apply.c
./main/apply.c

apply.cの43行目に適当にブレイクポイントを作ります。

(gdb) b apply.c:43
Breakpoint 2 at 0x336b98: file apply.c, line 44.
(gdb) i b
Num Type           Disp Enb Address    What
2   breakpoint     keep y   0x00336b98 in do_lapply at apply.c:44

関数の引数のうちの一つargsがどうなっているか調べてみましょう。

(gdb) p args
$4 = (SEXP) 0x1159ca0
(gdb) p *args
$5 = {
  sxpinfo = {
    type = 2, 
    obj = 0, 
    named = 0, 
    gp = 0, 
    mark = 1, 
    debug = 0, 
    trace = 0, 
    spare = 0, 
    gcgen = 0, 
    gccls = 0
  }, 
  attrib = 0x10073b0, 
  gengc_next_node = 0x1159c84, 
  gengc_prev_node = 0x1159cbc, 
  u = {
    primsxp = {
      offset = 18187440
    }, 
    symsxp = {
      pname = 0x11584b0, 
      value = 0x1159c84, 
      internal = 0x10073b0
    }, 
    listsxp = {
      carval = 0x11584b0, 
      cdrval = 0x1159c84, 
      tagval = 0x10073b0
    }, 
    envsxp = {
      frame = 0x11584b0, 
      enclos = 0x1159c84, 
      hashtab = 0x10073b0
    }, 
    closxp = {
      formals = 0x11584b0, 
      body = 0x1159c84, 
      env = 0x10073b0
    }, 
    promsxp = {
      value = 0x11584b0, 
      expr = 0x1159c84, 
      env = 0x10073b0
    }
  }
}
(gdb) p (*args).u.listsxp.carval
$8 = (struct SEXPREC *) 0x11584b0

おお、いかにもS式っぽいのが出てきました。が、carsとかsumの文字らしきものが出てこないのでよく分かりません><。最後の「(*args).u.listsxp.carval」からこれ以上どうやって情報を得ればいいかもよく分からない><。とりあえず今日はここまで。