首页 文章

在Windows下调试(逐行)Rcpp生成的DLL

提问于
浏览
18

最近我一直在尝试使用Rcpp(内联)来生成DLL,这些DLL在提供的R输入上执行各种任务 . I'd like to be able to debug the code in these DLLs line by line, given a specific set of R inputs. (我在Windows下工作 . )

为了说明,让我们考虑一个任何人都应该能够运行的具体例子......

下面的代码是一个非常简单的cxx函数,它只是输入向量的两倍 . 但请注意,还有一个额外的变量 myvar ,它会更改值几次,但不能查看调试过程何时正确运行 .

library(inline)
library(Rcpp)

f0 <- cxxfunction(signature(a="numeric"), plugin="Rcpp", body='
    Rcpp::NumericVector xa(a);
    int myvar = 19;
    int na = xa.size();
    myvar = 27;
    Rcpp::NumericVector out1(na);
    for(int i=0; i < na; i++) {
        out1[i] = 2*xa[i];
        myvar++;
    }
    myvar = 101;
    return(Rcpp::List::create( _["out1"] = out1));
')

运行上面的命令之后,输入命令

getLoadedDLLs()

在R会话中显示DLL列表 . 列出的最后一个应该是由上面的过程创建的DLL - 它有一个随机的临时名称,在我的情况下是

file7e61645c

"Filename"列显示cxxfunction已将此DLL放在 tempdir() 的位置,这对我来说当前是

C:/Users/TimP/AppData/Local/Temp/RtmpXuxtpa/file7e61645c.dll

现在,调用DLL的显而易见的方法是通过 f0 ,如下所示

> f0(c(-7,0.7,77))

$out1
[1] -14.0   1.4 154.0

但是我们当然也可以使用 .Call 命令直接通过名称调用DLL:

> .Call("file7e61645c",c(-7,0.7,77))

$out1
[1] -14.0   1.4 154.0

所以我've reached the point where I'm直接使用R输入(这里是向量 c(-7,0.7,77) )调用一个独立的DLL,并让它正确地将答案返回给R.

What I really need, though, is a facility for line-by-line debugging (using gdb, I presume) that will allow me to observe the value of myvar being set to 19, 27, 28, 29, 30, and finally 101 as the code progresses. 故意设置上面的示例,以便调用DLL不会告诉我们myvar .

为了澄清,这里的“胜利条件”能够观察myvar变化(看到值myvar = 19将是第一步!)而不向代码主体添加任何其他东西 . 这显然可能需要改变编译代码的方式(是否有调试模式设置打开?),或者调用R的方式 - 但我不知道从哪里开始 . 如上所述,所有这些都是基于Windows的 .

最后说明:在我的实验中,我实际上对cxxfunction的副本进行了一些小的修改,以便输出DLL - 以及其中的代码 - 接收用户定义的名称并且位于用户定义的目录中,而不是临时名称和位置 . 但这并不影响问题的本质 . 我提到这只是为了强调如果有人给我轻推,改变编译设置应该相当容易:)

为完整起见,在上面的原始cxxfunction调用中设置verbose = TRUE会将编译参数显示为以下形式:

C:/R/R-2.13.2/bin/i386/R CMD SHLIB file7e61645c.cpp 2> file7e61645c.cpp.err.txt 
g++ -I"C:/R/R-213~1.2/include"    -I"C:/R/R-2.13.2/library/Rcpp/include"      -O2 -Wall  -c file7e61645c.cpp -o file7e61645c.o
g++ -shared -s -static-libgcc -o file7e61645c.dll tmp.def file7e61645c.o C:/R/R-2.13.2/library/Rcpp/lib/i386/libRcpp.a -LC:/R/R-213~1.2/bin/i386 -lR

我的改编版本具有与上述相同的编译参数,除了字符串“file7e61645c”被用户选择的名称(例如“testdll”)和相关文件复制到更永久的位置 .

在此先感谢您的帮助:)

1 回答

  • 18

    我对Rcpp用户对inline包及其 cxxfunction() 的痴迷感到震惊 . 是的,它确实非常有用,它确实推动了Rcpp的进一步采用,因为它使快速实验变得如此简单 . 是的,它允许我们在源中使用700个单元测试 . 是的,我一直用它来展示这里的例子,在rcpp-devel list或甚至生活在presentations .

    但这是否意味着我们应该将它用于每项任务?这是否意味着它没有“成本”,如临时目录中的随机文件名等? Romain和我在我们的文档中另有说法 .

    最后,动态加载的R模块的调试很难实现 . 关于它的(强制性)Writing R Extensions中有一个完整的部分,Doug Bates曾经一两次发布了一个关于如何通过ESS和Emacs这样做的教程(虽然我总是忘记他发布的地方;曾经是_RC39034_上的IIRC) .

    Edit 2012-Jul-07:

    这是你的一步一步:

    • (序言:我已经使用了gcc和g多年,甚至当我添加-g时我并不总是将-O2变成-O0 . 我真的不确定你需要它,但是当你要求它时 . ..)

    • 将环境变量CXXFLAGS设置为"-g -O0 -Wall" . 有很多方法可以做到这一点,有些是依赖于平台的(例如Windows控制面板),因此不那么通用和有趣 . 我在Windows和Unix上使用 ~/.R/Makevars . 您可以使用它,或者您可以覆盖R的系统级$ RHOME / etc / Makeconf,或者您可以使用Makeconf.site或...查看完整的文档---但正如我所说, ~/.R/Makevars 是我的首选方式,因为它不干扰R之外的编译 .

    • 现在每个编译R通过R CMD SHLIB,R CMD COMPILE,R CMD INSTALL,...将使用 . 因此,使用内联或本地包不再重要 . 继续内联......

    • 对于其余部分,我们主要遵循“编写R扩展”中的第4.4.1节“查找动态加载代码中的入口点”:

    • 使用R -d gdb启动另一个R会话 .

    • 编译代码 . 对于

    fun < - cxxfunction(signature(),plugin =“Rcpp”,verbose = TRUE,body ='
    int theAnswer = 42;
    return wrap(theAnswer);
    “)

    我明白了

    [...]
    Compilation argument:
     /usr/lib/R/bin/R CMD SHLIB file11673f928501.cpp 2> file11673f928501.cpp.err.txt 
     ccache g++-4.6 -I/usr/share/R/include -DNDEBUG   -I"/usr/local/lib/R/site- library/Rcpp/include"   -fpic  -g -O0 -Wall -c file11673f928501.cpp -o file11673f928501.o
    g++-4.6 -shared -o file11673f928501.so file11673f928501.o -L/usr/local/lib/R/site-library/Rcpp/lib -lRcpp -Wl,-rpath,/usr/local/lib/R/site-library/Rcpp/lib -L/usr/lib/R/lib -lR
    
    • 调用例如 tempdir() 查看临时目录,cd到上面使用的这个临时目录, dyn.load() 上面构建的文件:

    dyn.load(“file11673f928501.so”)

    • 现在通过发送中断信号暂停R(在Emacs中,从下拉列表中选择一个简单的选项) .

    • 在gdb中,设置断点 . 上面的单个作业对我来说变成了第32行,所以

    break file11673f928501.cpp 32

    • 回到R,调用函数:

    有趣()

    • Presto,在我们想要的断点处的调试器中:

    R> fun()

    断点1,file11673f928501(),文件11673f928501.cpp:32
    32 int theAnswer = 42;
    (GDB)

    • 现在你可以通过"just"来了解gdb的魔力

    现在,正如我在第一次尝试时所说的那样,通过一个简单的包可以更容易(在我看来), Rcpp.package.skeleton() 可以为你写,因为你不必处理随机目录和文件名 . 但每个人都自己......

相关问题