r-如何在Windows中调试(逐行)Rcpp生成的代码



我正在尝试在运行时调试Rcpp编译的代码。很长一段时间以来,我一直试图让它发挥作用,但没有成功。这里问了一个非常相似的问题:在Windows下调试Rcpp生成的DLL(逐行),它问了同样的问题,但问题和答案都远远超出了我的理解。

这是我所拥有的:

Windows 7 Pro SP1
R 3.5
Rstudio 1.1.463 with Rcpp.
Rbuild Tools from Rstudio. (c++ compiler)

程序:在Rstudio文件->新文件->C++文件(创建一个带有timeTwo函数的示例文件)

我在这个文件中添加了一个新功能:

// [[Rcpp::export]]
NumericVector timesTwo2(NumericVector x) {
for(int ii = 0; ii <= x.size(); ii++)
{
x.at(ii) = x.at(ii) * 2;
}
return x;
}

我在保存时选中了Source,并将文件保存为RcppTest.cpp,该文件成功地来源或符合该文件。

在Rstudio:中运行代码

data = c(1:10)
data
[1]  1  2  3  4  5  6  7  8  9 10
timesTwo2(data)
Error in timesTwo2(data) : Index out of bounds: [index=10; extent=10].

错误是因为for循环中的是<= x.size(),所以结果是运行时错误。

问题是如何获得关于这个错误的调试输出,合理地告诉我发生了什么?至少我想知道代码中触发异常的那一行以及使用哪些参数。此外,我真的很想在异常发生之前逐行执行代码,这样我就可以准确地监控发生了什么。

我可以安装任何额外的程序或应用任何其他设置,只要我能找到如何做到这一点的确切细节。现在我正从头开始,只是为了让它发挥作用。非常感谢。

更新:我找到了这个网站:使用gdb调试Rcpp c++代码我安装了最新的gcc8.1和gdb

我在C:Program FilesRR-3.5.1etcx64中的makeconf文件中找到了CXXFLAGS然后我按照建议启动了Rgui,但当我尝试Rcpp:::sourceCpp时,我得到了一个错误:

> library(Rcpp)
> Rcpp::sourceCpp('Rcpptest.cpp')
C:/PROGRA~1/R/R-35~1.1/etc/x64/Makeconf:230: warning: overriding recipe for target '.m.o'
C:/PROGRA~1/R/R-35~1.1/etc/x64/Makeconf:223: warning: ignoring old recipe for target '.m.o'
c:/Rtools/mingw_64/bin/g++  -I"C:/PROGRA~1/R/R-35~1.1/include" -DNDEBUG   -I"C:/Users/Michael/Documents/R/win-library/3.5/Rcpp/include" -I"C:/PROGRA~1/R/R-35~1.1/bin/x64"        -ggdb -O0 -Wall -gdwarf-2 -mtune=generic -c Rcpptest.cpp -o Rcpptest.o
process_begin: CreateProcess(NULL, c:/Rtools/mingw_64/bin/g++ -IC:/PROGRA~1/R/R-35~1.1/include -DNDEBUG -IC:/Users/Michael/Documents/R/win-library/3.5/Rcpp/include -IC:/PROGRA~1/R/R-35~1.1/bin/x64 -ggdb -O0 -Wall -gdwarf-2 -mtune=generic -c Rcpptest.cpp -o Rcpptest.o, ...) failed.
make (e=2): The system cannot find the file specified.
make: *** [C:/PROGRA~1/R/R-35~1.1/etc/x64/Makeconf:215: Rcpptest.o] Error 2
Error in Rcpp::sourceCpp("Rcpptest.cpp") : 
Error 1 occurred building shared library.
WARNING: The tools required to build C++ code for R were not found.
Please download and install the appropriate version of Rtools:
http://cran.r-project.org/bin/windows/Rtools/

看起来它正在加载新的CXXFLAGS,并且正在使用DEBUG,但似乎仍然无法编译。有人从错误中知道原因吗?

我尝试以与Rgui相同的方式运行Rstudio,它从gdb窗口中显示的许多线程开始,但Rstudio中的所有内容都与以前完全一样运行,没有来自Rstudior或gdb的额外调试信息。

更新2:由于上面的错误表明Rgui没有用于编译的Rtools,所以我从provide链接安装了Rtools。它安装在C:\Rtools中,而Rstudio安装在C:\RBuildTools中。因此,我现在有3个编译器,Rtools、RbuildTools和带有gdbgcc。它现在正在编译,但仍然会出现与我在Rstudio中相同的错误。我希望至少能得到更好的错误输出,比如传递的行和值。指令说Rgui应该有一个断点,但我找不到这样的选项。

更新3我终于可以安装并运行Linux了(Ubuntu 16.04.05)。首先是我的CXXFLAGS:

$ R CMD config CXXFLAGS
-g -O0 -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g

我必须在我的主目录中创建一个.R文件夹,并在其中创建一个只有行的Makevar文件

CXXFLAGS = -g -O0 -Wall -pedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2

仅此一项就花了几个小时,因为它实际上并没有说要制作文件夹和文件。

然后我执行了拉尔夫发布的命令,在断点:

> timesTwo2(d1)
Thread 1 "R" hit Breakpoint 1, timesTwo2 (x=...) at RcppTest.cpp:19
19  NumericVector timesTwo2(NumericVector x) {
(gdb) n
20    for (int ii = 0; ii <= x.size(); ii++)
(gdb) n
22      x.at(ii) = x.at(ii) * 2;
(gdb) display ii
1: ii = 0
(gdb) n
20    for (int ii = 0; ii <= x.size(); ii++)
1: ii = 0
(gdb) n
22      x.at(ii) = x.at(ii) * 2;
1: ii = 1
(gdb) n
20    for (int ii = 0; ii <= x.size(); ii++)
1: ii = 1
(gdb) display x.at(ii)
2: x.at(ii) = <error: Attempt to take address of value not located in memory.>
(gdb) n
22      x.at(ii) = x.at(ii) * 2;
1: ii = 2
2: x.at(ii) = <error: Attempt to take address of value not located in memory.>
(gdb) 

最后在n = 10:

1: ii = 10
2: x.at(ii) = <error: Attempt to take address of value not located in memory.>
(gdb) n
0x00007ffff792d762 in Rf_applyClosure () from /usr/lib/R/lib/libR.so
(gdb) 

这绝对是我调试过的最远的一次,但这是一个非常基本的函数,调试输出甚至错误输出都不是很有用。它给了我它正在执行的行,它可以显示ii,但我不能显示数组值或整个数组。是否可以创建一个更具体的断点,使其仅在ii == 10时断开?理想情况下,我希望在Rstudio或其他可以显示整个矢量的GUI中这样做。还在做更多的测试。

我在下面的原始答案中也建议了R -d gdb的常用方法,但它在Windows:上不起作用

-调试器=名称
-d名称

(仅限UNIX)通过调试器名称运行R。对于大多数调试器(valgrind和gdb的最新版本除外),将忽略其他命令行选项,而应该在从调试器内部启动R可执行文件时提供这些选项。

https://cran.r-project.org/doc/manuals/r-release/R-intro.html#Invoking-R-from-command-line

备选方案:

  1. 在调试器中启动R:gdb.exe Rgui.exe
  2. 设置断点:break TimesTwo2
  3. 运行R:run
  4. 源文件:Rcpp::sourceCpp("debug.cpp")
  5. 使用nextprintdisplay逐步执行代码

步骤1的替代方案。将启动R,用Sys.getpid()获取PID,用gdb -p <pid>附加调试器。您将不得不使用continue而不是run


我现在没有Windows机器,所以下面的操作是在Linux上完成的。不过,我希望它可以转让。让我们从一个简单的cpp文件(在我的例子中是debug.cpp)开始,它包含您的代码:

#include <Rcpp.h>
using Rcpp::NumericVector;
// [[Rcpp::export]]
NumericVector timesTwo2(NumericVector x) {
for(int ii = 0; ii <= x.size(); ii++)
{
x.at(ii) = x.at(ii) * 2;
}
return x;
}
/*** R
data = c(1:10)
data
timesTwo2(data)
*/

我可以通过在命令行上调用R来重现错误:

$ R -e "Rcpp::sourceCpp('debug.cpp')"
R version 3.5.1 (2018-07-02) -- "Feather Spray"
[...]
> Rcpp::sourceCpp('debug.cpp')
> data = c(1:10)
> data
[1]  1  2  3  4  5  6  7  8  9 10
> timesTwo2(data)
Error in timesTwo2(data) : Index out of bounds: [index=10; extent=10].
Calls: <Anonymous> ... source -> withVisible -> eval -> eval -> timesTwo2 -> .Call
Execution halted

接下来,我们可以用gdb作为调试器启动R(参见Dirk所说的编写R扩展):

$ R -d gdb -e "Rcpp::sourceCpp('debug.cpp')"
GNU gdb (Debian 8.2-1) 8.2
[...]
(gdb) break timesTwo2
Function "timesTwo2" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (timesTwo2) pending.
(gdb) run
[...]
> Rcpp::sourceCpp('debug.cpp')
[Thread 0xb40d3b40 (LWP 31793) exited]
[Detaching after fork from child process 31795]
> data = c(1:10)
> data
[1]  1  2  3  4  5  6  7  8  9 10
> timesTwo2(data)
Thread 1 "R" hit Breakpoint 1, 0xb34f3310 in timesTwo2(Rcpp::Vector<14, Rcpp::PreserveStorage>)@plt ()
from /tmp/RtmphgrjLg/sourceCpp-i686-pc-linux-gnu-1.0.0/sourcecpp_7c2d7f56744b/sourceCpp_2.so
(gdb)

此时,您可以使用next(或仅使用n)单步执行程序,并使用print(或仅p)输出变量。一个有用的命令也是display:

Thread 1 "R" hit Breakpoint 1, timesTwo2 (x=...) at debug.cpp:5
5   NumericVector timesTwo2(NumericVector x) {
(gdb) n
6     for(int ii = 0; ii <= x.size(); ii++)
(gdb) n
8       x.at(ii) = x.at(ii) * 2;
(gdb) display ii
2: ii = 0
(gdb) n
8       x.at(ii) = x.at(ii) * 2;
2: ii = 0
[...]
2: ii = 9
(gdb) 
46          inline proxy ref(R_xlen_t i) { return start[i] ; }
2: ii = 9
(gdb) 
6     for(int ii = 0; ii <= x.size(); ii++)
2: ii = 10
(gdb) 
8       x.at(ii) = x.at(ii) * 2;
2: ii = 10
(gdb) 
Error in timesTwo2(data) : Index out of bounds: [index=10; extent=10].
Calls: <Anonymous> ... source -> withVisible -> eval -> eval -> timesTwo2 -> .Call
Execution halted
[Detaching after fork from child process 32698]
[Inferior 1 (process 32654) exited with code 01]

顺便说一句,我使用了以下编译标志:

$ R CMD config CXXFLAGS
-g -O2 -Wall -pedantic -fstack-protector-strong -D_FORTIFY_SOURCE=2

您可能需要切换到-O0

它可以用Visual Studio代码完成,因为它可以处理RC++。这允许您在GUI环境中一次一行地逐步完成Rcpp代码。

请参阅此演示开始。

最新更新