Permission is grantedto make and distribute verbatim copies of this manual provided the
copyright notice and this permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed
under the terms of a permission notice identical to this one.
Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions, except that this permission notice
may be stated in a translation approved by the R Core Team.
c
Copyright ⃝ 1999–2012 R Core Team
ISBN 3-900051-11-9
日本語訳注 このドキュメントの日本語訳は、
:
英語原文と全く同じ条件の下で自由に配布、
利用、
修正可能である。R の開発の早さから、こうした文章の日本語訳は常に"旧式化"していること
をお断りしておく。翻訳の妥当さについては保証しない。これらの理由から、R の最新バージョ
ンに付属する文章を適宜参照していただきたい。なお、本ドキュメントは Twitter ID:@ito yan
が作成した。
Chapter 1: Rのパッケージを作る
AC_ARG_WITH([odbc-manager],
AC_HELP_STRING([--with-odbc-manager=MGR],
[specify the ODBC manager, e.g. odbc or iodbc]),
[odbc_mgr=$withval])
if test $odbc_mgr = odbc ; then
AC_PATH_PROGS(ODBC_CONFIG, odbc_config)
fi
dnl Select an optional include path, from a configure option
dnl or from an environment variable.
AC_ARG_WITH([odbc-include],
AC_HELP_STRING([--with-odbc-include=INCLUDE_PATH],
[the location of ODBC header files]),
[odbc_include_path=$withval])
RODBC_CPPFLAGS=-I.
if test [ -n $odbc_include_path ] ; then
RODBC_CPPFLAGS=-I. -I${odbc_include_path}
else
if test [ -n ${ODBC_INCLUDE} ] ; then
RODBC_CPPFLAGS=-I. -I${ODBC_INCLUDE}
fi
fi
dnl ditto for a library path
AC_ARG_WITH([odbc-lib],
AC_HELP_STRING([--with-odbc-lib=LIB_PATH],
[the location of ODBC libraries]),
[odbc_lib_path=$withval])
if test [ -n $odbc_lib_path ] ; then
LIBS=-L$odbc_lib_path ${LIBS}
else
if test [ -n ${ODBC_LIBS} ] ; then
LIBS=-L${ODBC_LIBS} ${LIBS}
else
if test -n ${ODBC_CONFIG}; then
odbc_lib_path=‘odbc_config --libs | sed s/-lodbc//‘
LIBS=${odbc_lib_path} ${LIBS}
fi
fi
fi
dnl Now find the compiler and compiler flags to use
: ${R_HOME=‘R RHOME‘}
if test -z ${R_HOME}; then
echo could not determine R_HOME
exit 1
fi
CC=‘${R_HOME}/bin/R CMD config CC‘
CPP=‘${R_HOME}/bin/R CMD config CPP‘
CFLAGS=‘${R_HOME}/bin/R CMD config CFLAGS‘
CPPFLAGS=‘${R_HOME}/bin/R CMD config CPPFLAGS‘
AC_PROG_CC
AC_PROG_CPP
if test -n ${ODBC_CONFIG}; then
RODBC_CPPFLAGS=‘odbc_config --cflags‘
26
33.
Chapter 1: Rのパッケージを作る
27
fi
CPPFLAGS=${CPPFLAGS} ${RODBC_CPPFLAGS}
dnl Check the headers can be found
AC_CHECK_HEADERS(sql.h sqlext.h)
if test ${ac_cv_header_sql_h} = no ||
test ${ac_cv_header_sqlext_h} = no; then
AC_MSG_ERROR(ODBC headers sql.h and sqlext.h not found)
fi
dnl search for a library containing an ODBC function
if test [ -n ${odbc_mgr} ] ; then
AC_SEARCH_LIBS(SQLTables, ${odbc_mgr}, ,
AC_MSG_ERROR(ODBC driver manager ${odbc_mgr} not found))
else
AC_SEARCH_LIBS(SQLTables, odbc odbc32 iodbc, ,
AC_MSG_ERROR(no ODBC driver manager found))
fi
dnl for 64-bit ODBC need SQL[U]LEN, and it is unclear where they are defined.
AC_CHECK_TYPES([SQLLEN, SQLULEN], , , [# include sql.h])
dnl for unixODBC header
AC_CHECK_SIZEOF(long, 4)
dnl substitute RODBC_CPPFLAGS and LIBS
AC_SUBST(RODBC_CPPFLAGS)
AC_SUBST(LIBS)
AC_CONFIG_HEADERS([src/config.h])
dnl and do substitution in the src/Makevars.in and src/config.h
AC_CONFIG_FILES([src/Makevars])
AC_OUTPUT
src/Makevars.inは単に次のようになるであろう。
PKG_CPPFLAGS = @RODBC_CPPFLAGS@
PKG_LIBS = @LIBS@
ユーザは以下のようなオプションによって、ODBC ドライバマネージャファイルの位置を
特定する通知を受けられるようになる (可読性のため改行している)。
R CMD INSTALL
--configure-args=’--with-odbc-include=/opt/local/include
--with-odbc-lib=/opt/local/lib --with-odbc-manager=iodbc’
RODBC
あるいは環境変数 ODBC_INCLUDEと ODBC_LIBSを設定することによってもできる。
1.2.3 F95 のコードを使う
R は.fという拡張子を持つソースファイルは FORTRAN 77 であると仮定しており、‘F77’に
よって規定されているコンパイラにそれらのコードを渡す。コンパイラはすべてではないが、
大部分のプラットフォームでは Fortran 90/95 のコードを受け入れる: いくつかのプラット
フォームでは、Fortran 90/95 で別のコンパイラを持ち、わずかな (今ではかなり珍しい 22 ) プ
ラットフォームでは Fortran 90/95 のサポートはない。
22
Cygwin は g77を 2011 年まで利用し、いくつかの Unix OS 用の R のプレビルドバージョンはまだ g77を使
用している。
Chapter 2: Rのドキュメントを書く
62
Usage:
## S3 method for class ’factor’:
x[..., drop = FALSE]
## S3 method for class ’factor’:
x[[...]]
## S3 replacement method for class ’factor’:
x[...] - value
S3methodはmethodに代わるものとして受け入れられている。
arguments{...}
関数の引数の記述であり、引数リストの各要素に対し、次のような形式の項目を
用いたものである。
item{arg_i}{Description of arg_i.}
(項目の 3 つの部分の間には空白がないことに注意。) 例えばパラメータの集合に
関する一般的な情報を与えるため、item項目の外部に任意のテキストがあるか
もしれない。
details{...}
提供される機能の詳細なできるだけ正確な記述であり、descriptionスロット
にある基本的な情報を拡張している。
value{...}
関数の返り値の記述。
もし複数の値を持つリストが返されるのであれば、返されるリストの各要素に対
して次のような形式の項目を使うことができる。
item{comp_i}{Description of comp_i.}
任意のテキストがこのリストに先行している 4 かもしれない (例として rleのヘ
ルプを参照せよ)。valueは暗黙の内にdescribe環境なので、そのため環境は
要素を一覧にするために使ってはならず、単に個々のitem{}{}エントリに対し
て使うべきである。
references{...}
文献への参照に関するセクション。web ポインタにはurl{}やhref{}{}を使用
せよ。
note{...}
指摘しておきたい特記のためにこれを使用せよ。複数のnoteセクションは許可
されているが、エンドユーザを混乱させるかもしれない。
例えば、pie.Rdは次のものを含んでいる。
note{
Pie charts are a very bad way of displaying information.
The eye is good at judging linear measures and bad at
judging relative areas.
......
}
4
リストの項目の間、または後ろにあるテキストは、R 2.10.0 より前では捨てられており、勧められない。
Chapter 3: Rコードの整理とプロファイリング
82
/var/tmp/path/to/R_HOME/library/stats/libs/stats.so.profile
Flat profile:
Each sample counts as 0.01 seconds.
%
cumulative
self
self
time
seconds
seconds
calls us/call
76.19
0.32
0.32
0
0.00
16.67
0.39
0.07
0
0.00
7.14
0.42
0.03
0
0.00
total
us/call
name
numeric_deriv
nls_iter
getListElement
rm /path/to/R_HOME/library/stats/libs/stats.so.profile
... クリーンアップするため ...
root アクセス権がプロファイルデータのために使うディレクトリを作成するために必要に
なる可能性がある。
3.4.1.2 oprofile
oprofileは情報を収集するデーモンを実行することで動作する。デーモンは root として実行
されなければならない。例えば、次のようにする。
% su
% opcontrol --no-vmlinux
% (optional, some platforms) opcontrol --callgraph=5
% opcontrol --start
% exit
そしてユーザとして次のようにする。
% R
... boot の例を実行する
% opcontrol --dump
% opreport -l /path/to/R_HOME/library/stats/libs/stats.so
...
samples %
symbol name
1623
75.5939 anonymous symbol from section .plt
349
16.2552 numeric_deriv
113
5.2632 nls_iter
62
2.8878 getListElement
% opreport -l /path/to/R_HOME/bin/exec/R
...
samples %
symbol name
76052
11.9912 Rf_eval
54670
8.6198 Rf_findVarInFrame3
37814
5.9622 Rf_allocVector
31489
4.9649 Rf_duplicate
28221
4.4496 Rf_protect
26485
4.1759 Rf_cons
23650
3.7289 Rf_matchArgs
21088
3.3250 Rf_findFun
89.
Chapter 3: Rコードの整理とプロファイリング
83
19995
3.1526 findVarLocInFrame
14871
2.3447 Rf_evalList
13794
2.1749 R_Newhashpjw
13522
2.1320 R_gc_internal
...
プロファイラの停止と記録のクリアは root として行われる必要がある。もし適切なソー
スコードがデバッグサポートとコールグラフを生成するための opreport -cを伴ってコンパ
イルされていれば、ソースコードに各セクションで消費された時間の注釈をつけるための
opannotateを使うことができる。
3.4.2 Solaris
64-bit の Solaris 上で、標準的なプロファイリングツール gprofは、-pgをつけてコンパイル
された共有オブジェクトから情報を収集する。
3.4.3 Mac OS X
開発者は sample(または GUI 版である Sampler.app) と Shark(http: / / developer .
apple . com / tools / sharkoptimize . htmlと http: / / developer . apple . com / tools /
shark_optimize.htmlを参照せよ) を推奨している。
90.
Chapter 4: デバッグ作業
84
4デバッグ作業
この章では、役立つエラー情報を取得する方法に始まり、R をクラッシュさせるエラーにどのよ
うに対処するかということが続く、R の拡張のデバッグを取り扱う。他のスタイルを好む人に
は、CRANの debug (http://CRAN.R-project.org/package=debug) のような貢献パッケー
ジがある (R-News 3/3 (http://CRAN.R-project.org/doc/Rnews/Rnews_2003-3.pdf) の
記事に書かれている)。(ここで与えられたものを補完する例を提供した覚書が Roger Peng に
よって 2002 年から http://www.biostat.jhsph.edu/~rpeng/docs/R-debug-tools.pdf
に提供されている。)
4.1 ブラウジング
R レベルのデバッグ機能の大部分は、内蔵されたブラウザの周辺に基づいている。これは
関数のコードに browser()への呼び出しを挿入することで直接使うことができる (例えば、
fix(my_function)を使う)。コードの実行が関数のその位置に到着すると、制御は特別なプ
ロンプトを R コンソールに返す。例えば、
fix(summary.data.frame) ## for ループの後に browser() を挿入する
summary(women)
Called from: summary.data.frame(women)
Browse[1] ls()
[1] digits i
lbs
lw
maxsum nm
nr
[9] object sms
z
Browse[1] maxsum
[1] 7
Browse[1]
height
weight
Min.
:58.0
Min.
:115.0
1st Qu.:61.5
1st Qu.:124.5
Median :65.0
Median :135.0
Mean
:65.0
Mean
:136.7
3rd Qu.:68.5
3rd Qu.:148.0
Max.
:72.0
Max.
:164.0
rm(summary.data.frame)
nv
ブラウザのプロンプトでは、任意の R の式を入力することができる。例えば ls()は現在のフ
レームにあるオブジェクトを一覧表にし、オブジェクト名を入力するとそれを表示する 1 。以
下のコマンドも受け入れられる。
• n
ステップスルーモードに入る。このモードでは、リターンを打つことで、コードの次の
行を実行する (より正確には 1 行と継続行)。cを入力すると現在のコンテキストの最後ま
で続ける。例えば、現在のループ、あるいは関数の終わりまで続ける。
• c
通常モードでは、これはブラウザを終了して実行を継続し、復帰は同様に動作する。cont
は同意語である。
1
以下に挙げられたコマンドの例外を除く: printへの明示的な呼び出しを通して表示されるような名前のオブ
ジェクト
91.
Chapter 4: デバッグ作業
85
•where
コールスタックを表示する。例えば、
summary(women)
Called from: summary.data.frame(women)
Browse[1] where
where 1: summary.data.frame(women)
where 2: summary(women)
Browse[1]
• Q
ブラウザと現在の式を終了し、トップレベルのプロンプトに戻る。
ブラウザプロンプトで実行されたコード内のエラーは、通常は制御をブラウザプロンプト
に返す。オブジェクトは代入によって変更されることもあり、ブラウザを抜けたときにその
変更された値を保持する。もし本当に必要であれば、ブラウザプロンプトからワークスペー
スにオブジェクトを割り当てることができる (スコープに名前がない場合に-を使うことに
よる)。
4.2 R コードのデバッグ
R プログラムがエラーを出したとしよう。最初に調べるべきことは、エラー時に R が何をし
ていたかであり、最も役立つツールは traceback()である。エラーの原因がすぐに明らかに
ならない場合は、いつでも traceback()を実行することを勧める。traceback()がエラーは
他のパッケージあるいは R の base から報告されたことを示したとき、エラーはいくつかの
パッケージにあるものとして、絶えずエラーは R のメーリングリストに報告される。ここに
回帰の一式からの例を示す。
success - c(13,12,11,14,14,11,13,11,12)
failure - c(0,0,0,0,0,0,0,2,2)
resp - cbind(success, failure)
predictor - c(0, 5^(0:7))
glm(resp ~ 0+predictor, family = binomial(link=log))
Error: no valid set of coefficients has been found: please supply starting values
traceback()
3: stop(no valid set of coefficients has been found: please supply
starting values, call. = FALSE)
2: glm.fit(x = X, y = Y, weights = weights, start = start, etastart = etastart,
mustart = mustart, offset = offset, family = family, control = control,
intercept = attr(mt, intercept) 0)
1: glm(resp ~ 0 + predictor, family = binomial(link =log))
アクティブフレームへの呼び出しは逆順で与えられる (最深部から始まる)。そのため、エラー
メッセージが glm.fit内の明示的なチェ
ックから来ていることが見えている。(traceback()
は関数呼び出しのすべての系列を表示する。これは option deparse.max.linesを設定す
ることによって、その数を制限することができる。)
ときおり、トレースバックはエラーがコンパイル済みのコードの内部で検出されたことを
示すことがある。例えば (?nlsから)、
Error in nls(y ~ a + b * x, start = list(a = 0.12345, b = 0.54321), trace = TRUE) :
step factor 0.000488281 reduced below ’minFactor’ of 0.000976563
traceback()
2: .Call(R_nls_iter, m, ctrl, trace)
1: nls(y ~ a + b * x, start = list(a = 0.12345, b = 0.54321), trace = TRUE)
92.
Chapter 4: デバッグ作業
86
これは最深部の呼び出しが.C、.Fortran、.Call、.Externalあるいは.Internalの場合に
当てはまる。そのようなコードはR の式を評価することはできるため、以下のように最深部
の呼び出しである必要はない。
traceback()
9: gm(a, b, x)
8: .Call(R_numeric_deriv, expr, theta, rho, dir)
7: numericDeriv(form[[3]], names(ind), env)
6: getRHS()
5: assign(rhs, getRHS(), envir = thisEnv)
4: assign(resid, .swts * (lhs - assign(rhs, getRHS(), envir = thisEnv)),
envir = thisEnv)
3: function (newPars)
{
setPars(newPars)
assign(resid, .swts * (lhs - assign(rhs, getRHS(), envir = thisEnv)),
envir = thisEnv)
assign(dev, sum(resid^2), envir = thisEnv)
assign(QR, qr(.swts * attr(rhs, gradient)), envir = thisEnv)
return(QR$rank min(dim(QR$qr)))
}(c(-0.00760232418963883, 1.00119632515036))
2: .Call(R_nls_iter, m, ctrl, trace)
1: nls(yeps ~ gm(a, b, x), start = list(a = 0.12345, b = 0.54321))
時折 traceback()は役に立たないことがあり、これは S4 メソッドディスパッチが関係し
ている場合にも当てはまることがある。以下の例を考えよう。
xyd - new(xyloc, x=runif(20), y=runif(20))
Error in as.environment(pkg) : no item called package:S4nswv
on the search list
Error in initialize(value, ...) : S language method selection got
an error when called from internal dispatch for function ’initialize’
traceback()
2: initialize(value, ...)
1: new(xyloc, x = runif(20), y = runif(20))
initializeに as.environmentへの呼び出しがないことから、これはあまり役に立たな
い (“called from internal dispatch” という注意がそのことを知らせている)。この場合、
methods:::.asEnvironmentPackageという 1 箇所でのみ発生した、引用符で囲まれた呼び
出しに対応する R のソースを探索した。そこでどこでエラーが発生したかということを知っ
た (これは珍しく不可解な例であった)。
エラーメッセージ
evaluation nested too deeply: infinite recursion / options(expressions=)?
は既定値 (5000) を使って扱うことが難しい可能性がある。実際に深い再帰が発生しているこ
とを知っていない限り、以下のように設定し、
options(expressions=500)
そしてエラーを示した例を再実行することが役立つ。
時々後のエラーの前兆となる明らかな警告があるが、警告がどこから来たのかははっきり
としない。options(warn = 2)(これは警告をエラーに変換する) と設定することがここでは
役に立つ。
一度エラーを見つけたなら、いくつかの選択がある。処理の 1 つに事後のダンプを見る
ことで、クラッシュの時に何が起こっていたかについてより解明することがある。そのよう
93.
Chapter 4: デバッグ作業
87
にするためには、options(error=dump.frames)を設定し、コードを再度実行せよ。そして
debugger()を起動し、ダンプを調査せよ。我々の例を続ける:
options(error = dump.frames)
glm(resp ~ 0 + predictor, family = binomial(link =log))
Error: no valid set of coefficients has been found: please supply starting values
これは前と同じエラーであるが、last.dumpによって呼ばれたオブジェクトがワークスペー
スに現れる。(そのようなオブジェクトは大きいこともあるので、必要が無くなったときには
削除せよ。) オブジェクトは関数 debuggerを呼び出すことで後に検査することができる。
debugger()
Message: Error: no valid set of coefficients has been found: please supply starting values
Available environments had calls:
1: glm(resp ~ 0 + predictor, family = binomial(link = log))
2: glm.fit(x = X, y = Y, weights = weights, start = start, etastart = etastart, mus
3: stop(no valid set of coefficients has been found: please supply starting values
Enter an environment number, or 0 to exit Selection:
これは tracebackと同じ呼び出しの列を与えるが、呼び出しは外部から順になり、最初の行
だけとなり、現在の幅で切り詰められる。しかし、これでエラー時に何が起きていたか詳細
を検討することができる。環境を選択すると、そのフレーム内のブラウザを開く。そこで、エ
ラーメッセージを発生させた関数呼び出しを選択し、いくつかの変数を検査する (そして 2 つ
の関数呼び出しを実行する)。
Enter an environment number, or 0 to exit Selection: 2
Browsing in the environment with call:
glm.fit(x = X, y = Y, weights = weights, start = start, etas
Called from: debugger.look(ind)
Browse[1] ls()
[1] aic
boundary
coefold
control
conv
[6] dev
dev.resids devold
EMPTY
eta
[11] etastart
family
fit
good
intercept
[16] iter
linkinv
mu
mu.eta
mu.eta.val
[21] mustart
n
ngoodobs
nobs
nvars
[26] offset
start
valideta
validmu
variance
[31] varmu
w
weights
x
xnames
[36] y
ynames
z
Browse[1] eta
1
2
3
4
5
0.000000e+00 -2.235357e-06 -1.117679e-05 -5.588393e-05 -2.794197e-04
6
7
8
9
-1.397098e-03 -6.985492e-03 -3.492746e-02 -1.746373e-01
Browse[1] valideta(eta)
[1] TRUE
Browse[1] mu
1
2
3
4
5
6
7
8
1.0000000 0.9999978 0.9999888 0.9999441 0.9997206 0.9986039 0.9930389 0.9656755
9
0.8397616
Browse[1] validmu(mu)
[1] FALSE
Browse[1] c
Available environments had calls:
1: glm(resp ~ 0 + predictor, family = binomial(link = log))
2: glm.fit(x = X, y = Y, weights = weights, start = start, etastart = etastart
3: stop(no valid set of coefficients has been found: please supply starting v
Enter an environment number, or 0 to exit
rm(last.dump)
Selection: 0
94.
Chapter 4: デバッグ作業
88
last.dumpは後ほど、あるいは別のR セッションでさえも見ることができるので、事後分
析デバッグは R のバッチ利用に対しても可能である。我々はダンプが保存されるよう準備す
る必要がある: これはコマンドラインフラグ--saveを使って実行の最後にワークスペースを
保存するか、以下のような設定を通して行うことができる。
options(error = quote({dump.frames(to.file=TRUE); q()}))
さらなるオプションと動作の例については dump.framesのヘルプを参照せよ。
代わりのエラーアクションは関数 recover()を使うことである:
options(error = recover)
glm(resp ~ 0 + predictor, family = binomial(link = log))
Error: no valid set of coefficients has been found: please supply starting values
Enter a frame number, or 0 to exit
1: glm(resp ~ 0 + predictor, family = binomial(link = log))
2: glm.fit(x = X, y = Y, weights = weights, start = start, etastart = etastart
Selection:
これは dump.framesによく似ている。しかし、ダンプとダンプの再ロードをしなくともプロ
グラムの状態を直接調べることができる。dump.framesのヘルプページでは、非対話的な使用
において dump.framesのように recoverが動作することから、dump.callsと dump.frames
に代わってエラーアクションとして recoverを使うことができるとしている。
事後分析デバッグは何が不味かったのかを正確に調査するのには良いが、なぜなのかを見
出すには必ずしもよくない。代わりのアプローチに、ちょうど前後で何が起こったか詳しく
見ることはあり、それをするのに良い方法として debugを使うことがある。これは関数呼び
出しの始めにブラウザへの呼び出しを挿入し、ステップスルーモードで始める。我々の例で
は、次のように使うことができる。
debug(glm.fit)
glm(resp ~ 0 + predictor, family = binomial(link =log))
debugging in: glm.fit(x = X, y = Y, weights = weights, start = start, etastart = etastart,
mustart = mustart, offset = offset, family = family, control = control,
intercept = attr(mt, intercept) 0)
debug: {
## lists the whole function
Browse[1]
debug: x - as.matrix(x)
...
Browse[1] start
[1] -2.235357e-06
debug: eta - drop(x %*% start)
Browse[1] eta
1
2
3
4
5
0.000000e+00 -2.235357e-06 -1.117679e-05 -5.588393e-05 -2.794197e-04
6
7
8
9
-1.397098e-03 -6.985492e-03 -3.492746e-02 -1.746373e-01
Browse[1]
debug: mu - linkinv(eta - eta + offset)
Browse[1] mu
1
2
3
4
5
6
7
8
1.0000000 0.9999978 0.9999888 0.9999441 0.9997206 0.9986039 0.9930389 0.9656755
9
0.8397616
Chapter 4: デバッグ作業
91
MacOS 10.6/7 上で、valgrind のセッションは R を終了したときにハングしやすい。これ
は valgrind 3.8.1 より前の問題であり、環境変数 R_OSX_VALGRINDを設定することによって
回避することができる。R セッション内で system()を直接的、あるいは間接的に使うことは
避ける必要がある。
valgrindによってサポートされたプラットフォームでは、valgrindが R のヒープから確保
されたメモリの使用で発生するエラーを検出するのに役立つ追加の器具類と共に、 のバージョ
R
ンをビルドすることができる。
設定オプションは--with-valgrind-instrumentation=level
であり、level は 0、1 あるいは 2 である。レベル 0 は既定値で、何も付け加えない。レベル 1
は初期化されていないメモリを検出し、速度にほとんど影響を与えない。レベル 2 は他にも
多くのメモリ使用に関するバグを検出するだろうが、valgrindの下で実行されているときは
R をかなり遅くしてしまう。この設定を gctortureと併せて使うと、より効果的である (そし
てさらに遅くなる)。
valgrindの出力の例は次の通り。
==12539== Invalid read of size 4
==12539==
at 0x1CDF6CBE: csc_compTr (Mutils.c:273)
==12539==
by 0x1CE07E1E: tsc_transpose (dtCMatrix.c:25)
==12539==
by 0x80A67A7: do_dotcall (dotcode.c:858)
==12539==
by 0x80CACE2: Rf_eval (eval.c:400)
==12539==
by 0x80CB5AF: R_execClosure (eval.c:658)
==12539==
by 0x80CB98E: R_execMethod (eval.c:760)
==12539==
by 0x1B93DEFA: R_standardGeneric (methods_list_dispatch.c:624)
==12539==
by 0x810262E: do_standardGeneric (objects.c:1012)
==12539==
by 0x80CAD23: Rf_eval (eval.c:403)
==12539==
by 0x80CB2F0: Rf_applyClosure (eval.c:573)
==12539==
by 0x80CADCC: Rf_eval (eval.c:414)
==12539==
by 0x80CAA03: Rf_eval (eval.c:362)
==12539== Address 0x1C0D2EA8 is 280 bytes inside a block of size 1996 alloc’d
==12539==
at 0x1B9008D1: malloc (vg_replace_malloc.c:149)
==12539==
by 0x80F1B34: GetNewPage (memory.c:610)
==12539==
by 0x80F7515: Rf_allocVector (memory.c:1915)
...
この例は、2006 年 1 月に Matrix (http://CRAN.R-project.org/package=Matrix) パッケー
ジに含まれていたバグを突き止めている間に、R の検出機器を備えたバージョンから出たもの
である。最初の行は R が自由にアクセスできないメモリアドレスから 4 バイト読み込もうとし
たことを示している。これにどこでエラーが起きたのかを表す C のスタックトレースが続いて
いる。次はアクセスしたメモリの説明である。それは GetNewPageから呼び出された malloc
によって確保されたブロックの内側である。すなわち、R ヒープの内部である。このメモリ
がすべて R に属していることから、valgrindは検出機器を備えていない R のビルドでは、問
題を検出できなかったであろう (そしてしなかったであろう)。この例では、スタックトレー
スは tsc_transposeにあるバグを分離、修正するのに十分であった。そして gctorture()を
動作させたことで、何も追加の情報が得られなかった。スタックトレースが十分に有益でな
い場合、valgrindにオプション--db-attach=yesをつけることが役に立つかもしれない。こ
れは C コードの内部の変数が検査できるように (see Section 4.4.2 [R オブジェクトの検査],
page 94)、事後分析デバッガ (初期設定では gdb) を起動する。
valgrindの下で、オプション--use-valgrindを使うことで、R CMD checkによって対象
となるすべての例、テスト、ビニェ
ットを実行することができる。もしこれをするのであれ
ば、valgrindのオプションをいくつかの異なる方法で選択する必要があるだろう。例えば、
次のような内容を含む~/.valgrindrcを持つこと
--tool=memcheck
98.
Chapter 4: デバッグ作業
92
--memcheck:leak-check=full
あるいは、環境変数VALGRIND_OPTSを設定することによる。
Mac OS X では、デバッグシンボルが利用可能であることを保証する必要がある (そのため
valgrindはファイルの行番号を報告する)。.soファイルがロードされたときに、valgrindの
オプション--dysmutil=yesがシンボルにダンプするよう求めることで、これをすることがで
きる。これはパッケージがシステム領域にインストールされた場所 (例えば R.frameworkな
ど) では動作せず、遅くなることがある。R CMD INSTALL --dsymを用いてパッケージをインス
トールすると、ダンプされたシンボルをインストールする。(これは環境変数 PKG_MAKE_DSYM
を空でない値に設定することによってもできる。)
4.4 コンパイル済みコードのデバッグ
遅かれ早かれ、プログラマは R にロードされたコンパイル済みコードのデバッグの必要性に
直面するであろう。この節では、gccによってコンパイルされたコードに gdbを使うプラット
フォームを対象としているが、例えば dddと insightのような gdbのフロントエンド、そして
例えば Sun の dbxのような他のデバッガでもで同様のことが可能である。
最初に ‘クラッシュ’、つまり R がメモリアクセス違反 (‘セグメンテーション違反’ あるい
は ‘バスエラー’)、不当な命令、あるいは同様なもので予想外の終了をしたときを考える。R
の Unix 系のバージョンでは基本的な情報を与えることを目指したシグナルハンドラを利用し
ている。例えば、
*** caught segfault ***
address 0x20000028, cause ’memory not mapped’
Traceback:
1: .identC(class1[[1]], class2)
2: possibleExtends(class(sloti), classi, ClassDef2 = getClassDef(classi,
where = where))
3: validObject(t(cu))
4: stopifnot(validObject(cu - as(tu, dtCMatrix)), validObject(t(cu)),
validObject(t(tu)))
Possible actions:
1: abort (with core dump)
2: normal R exit
3: exit R without saving workspace
4: exit R saving workspace
Selection: 3
R のプロセスはダメージを受けているかもしれないので、本当に安全なオプションは最初の
ものだけである。
‘クラッシュ’ の別の原因は、C スタックが限度を超えることである。R は自身のコードの
内部でそれを追跡しようと試みるが、超過はサードパーティがコンパイルしたコードで起こ
ることがある。現代の POSIX-準拠の OS については、R はそれを安全に受け取ることがで
き、トップレベルのプロンプトへ返す。そのため、次のようなものを受け取る。
.C(aaa)
Error: segfault from C stack overflow
99.
Chapter 4: デバッグ作業
93
しかし、Cのスタックオーバーフローは Windows では致命的で、通常そのプラットフォーム
上でのデバッグ作業を打ち破ってしまう。
もしコアダンプを出すようなクラッシュがあれば、コアダンプを検査するために、次のよ
うなものを使うことができる。
gdb /path/to/R/bin/exec/R core.12345
もしコアダンプが無効、あるいはダンプを生成しないエラーをキャ
ッチする場合は、R が正常
に実行された時点で、例えば次のようにしてデバッガの下で R を直接実行することができる。
$ R -d gdb --vanilla
...
gdb run
うまくいけばデバッガはエラーをキャ
ッチして、プロンプトに戻る。これは無限ループの捕捉
や非常に長時間実行されるコードを中断することにも使用される。単純な例は、
for(i in 1:1e7) x - rnorm(100)
[hit Ctrl-C]
Program received signal SIGINT, Interrupt.
0x00397682 in _int_free () from /lib/tls/libc.so.6
(gdb) where
#0 0x00397682 in _int_free () from /lib/tls/libc.so.6
#1 0x00397eba in free () from /lib/tls/libc.so.6
#2 0xb7cf2551 in R_gc_internal (size_needed=313)
at /users/ripley/R/svn/R-devel/src/main/memory.c:743
#3 0xb7cf3617 in Rf_allocVector (type=13, length=626)
at /users/ripley/R/svn/R-devel/src/main/memory.c:1906
#4 0xb7c3f6d3 in PutRNGstate ()
at /users/ripley/R/svn/R-devel/src/main/RNG.c:351
#5 0xb7d6c0a5 in do_random2 (call=0x94bf7d4, op=0x92580e8, args=0x9698f98,
rho=0x9698f28) at /users/ripley/R/svn/R-devel/src/main/random.c:183
...
後に続くいくつかの “トリック” は知っておく価値がある:
4.4.1 動的にロードされたコードでエントリポイントを見つける
ほとんどのコンパイル環境下では、動的に R へロードされるコンパイル済みコードは、ロー
ドされるまで内部に設定したブレークポイントを持つことができない。Unix 系でそのように
動的にロードされるコードにシンボリックデバッガを使用するためには、以下のものを利用
する。
• R の実行ファイルでデバッガを呼び出す。例えば R -d gdbによる。
• R を開始する。
• R のプロンプトで、共有オブジェクトをロードするために dyn.loadまたは libraryを使
用する。
• 割込み信号を送る。これでデバッガのプロンプトへ戻される。prompt.
• コードにブレークポイントを設定する。
• signal 0RETとタイプして R の実行を継続する。
Windows ではシグナルは使えないかもしれない。
もしそうであれば、
手続きはより複雑にな
る。rw-FAQ と www.stats.uwo.ca/faculty/murdoch/software/debuggingR/gdb.shtml
100.
Chapter 4: デバッグ作業
94
(http:/ /www .stats .uwo .ca /faculty /murdoch /software /debuggingR /gdb .shtml).
を参照せよ。
4.4.2 デバッグ中に R オブジェクトを検査する
コンパイル済みコードから R オブジェクトを検査する鍵は、s によって指される R オブジェ
クトを印字する通常の R の印字の仕組みを使った関数 PrintValue(SEXP s)か、より安全な
‘オブジェクト’ のみを印字する R_PV(SEXP s)である。
PrintValueを使用する 1 つの方法は、デバッグされるコードに適切な呼び出しを挿入す
ることである。
別の方法に、シンボリックデバッガから R_PVを呼び出すことがある。(PrintValueは Rf_
PrintValueとして隠される。) 例えば、もし畳み込みの C コードに適切なブレークポイント
を設置していれば、畳み込みの例からオブジェクト abを使用して、gdbからは次のものを使
うことができる。
(gdb) p R_PV(ab)
任意の R オブジェクトを観察するためには、少しきつい作業をする必要がある。例えば、
次のようにしよう。
R DF - data.frame(a = 1:3, b = 4:6)
ブレークポイントを do_getに設定し、R プロンプトで get(DF)とタイプすることで、DFの
メモリ内のアドレスを見つけることができる。例えば、
Value returned is $1 = (SEXPREC *) 0x40583e1c
(gdb) p *$1
$2 = {
sxpinfo = {type = 19, obj = 1, named = 1, gp = 0,
mark = 0, debug = 0, trace = 0, = 0},
attrib = 0x40583e80,
u = {
vecsxp = {
length = 2,
type = {c = 0x40634700 0X@DX@0X@, i = 0x40634700,
f = 0x40634700, z = 0x40634700, s = 0x40634700},
truelength = 1075851272,
},
primsxp = {offset = 2},
symsxp = {pname = 0x2, value = 0x40634700, internal = 0x40203008},
listsxp = {carval = 0x2, cdrval = 0x40634700, tagval = 0x40203008},
envsxp = {frame = 0x2, enclos = 0x40634700},
closxp = {formals = 0x2, body = 0x40634700, env = 0x40203008},
promsxp = {value = 0x2, expr = 0x40634700, env = 0x40203008}
}
}
(デバッガの出力は読みやすさのために再フォーマットしている)。
R_PV()を使うと、SEXP のさまざまな要素の値を “検査する” ことができる。例えば、