【问题标题】:How to debug (line-by-line) Rcpp generated code in Windows?如何在 Windows 中调试(逐行)Rcpp 生成的代码?
【发布时间】:2018-12-04 22:23:03
【问题描述】:

我正在尝试在运行时调试Rcpp 编译的代码。很长一段时间以来,我一直试图让这个工作不成功。 这里提出了一个非常相似的问题:Debugging (line by line) of Rcpp-generated DLL under Windows 提出了同样的问题,但问题和答案都远远超出了我的理解。

这是我所拥有的:

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

程序: 在 Rstudio File->New File->C++ File 中(创建一个带有 timesTwo 函数的示例文件。)

我在这个文件中添加了一个新函数:

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

我在 Save 上检查了 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循环中是&lt;= x.size()所以结果是运行时错误。

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

我可以安装任何其他程序或应用任何其他设置,只要我能找到有关如何执行此操作的详细信息。现在我从头开始只是为了让它工作。谢谢。

更新: 我找到了这个网站:Debugging Rcpp c++ code using gdb 我用gdb安装了最新的gcc 8.1

我在位于C:\Program Files\R\R-3.5.1\etc\x64 的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 中的所有内容都像以前一样运行,没有来自 Rstudio 或 gdb 的额外调试信息。

更新 2: 由于上述错误表明Rgui 没有用于编译的 Rtools,因此我从提供链接安装了 Rtools。它安装在 C:\Rtools 中,而 Rstudio 安装在 C:\RBuildTools 中。所以我现在有 3 个编译器,Rtools、RbuildTools 和 gcc 和 gdb。 它现在可以编译,但仍然给出与我在 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

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

然后我在断点处执行 Ralf 发布的命令:

> 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 扩展了解调用调试器。远非理想,但我们有,除了旧的 print 声明。
  • 我的 cpp 文件在我开发它们时到处都是 Rcpp::Rcout &lt;&lt; thistestobj &lt;&lt; std::endl; 语句。
  • 有时,当我遇到Rcpp 的问题时,我会尝试将所有内容都转换为纯c++(例如NumericVector --&gt;&gt; std::vector&lt;double&gt;)并使用Visual Studio 中的调试器。工作量很大,但有时,仅使用 print 语句就很难找到难以捉摸的错误。
  • Rcpp::Rcput
  • Joseph,转换为 c++ 和 Visual Studio 会有什么帮助?不久前,我能够设置 Eclipse,以便我可以在 Eclipse 中编写代码,但我仍然无法在其中调试,它最终变得不稳定,所以回到 Rstudio。如果在我的小例子中我使用向量而不是数字向量,我将如何继续?谢谢。

标签: r rcpp


【解决方案1】:

通常的方法R -d gdb(我在下面的原始答案中也建议过)不适用于 Windows:

--debugger=名称
-d 名称

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

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

替代方案:

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

第 1 步的替代方法是启动 R,使用 Sys.getpid() 获取 PID,使用 gdb -p &lt;pid&gt; 附加调试器。您将不得不使用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 所说的 Writing R Extensions):

$ 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。

【讨论】:

  • 谢谢,我认为这只适用于 linux。当我有机会时,我会尝试设置 linux box,然后按照您的示例进行操作。如果可行,我可能会在需要时开始在 linux 中进行调试。
  • @MichaelE 你在 Windows 上得到了什么结果?
  • 我可以在安装了 Rtools 的情况下直接在 cmd 中运行代码。它首先说 ARGUMENT 'gdb' ignored 然后它继续运行代码并给出与以前相同的错误。所以看起来 gdb 在 windows 中的工作方式不同。
  • @MichaelE 有趣。请参阅更新的答案以获取替代方案(尚未测试)。
  • 我完全按照您的建议完成了 TimeTwo2 的中断。它说:没有加载符号表。使用“文件”命令。在未来的共享库加载时使断点挂起? (y 或 [n])断点 1 (TimesTwo2) 挂起。当我运行它时,它会像以前一样运行 rgui,但仍然会出现相同的错误并且没有中断。谢谢。
【解决方案2】:

可以使用 Visual Studio Code 完成,因为它可以处理 R 和 C++。这允许您在 GUI 环境中一次单步执行您的 Rcpp 代码一行。

请参阅此demo 以开始使用。

【讨论】:

  • 谢谢,有我需要的特定版本吗?我有 2012 Pro 对我不起作用,但已经有一段时间了。我可以使用我的还是必须升级?再次感谢。
  • @MichaelE 我不熟悉“2012 Pro”。我刚开始使用 VS Code 来解决这个特定问题,我的软件显示版本:1.43.1。我在 Ubuntu(Linux)btw 上,但它应该在 Windows 上工作。请注意,这是 VS Code,而不是 MS Visual Studio IDE。
  • 我没有意识到这一点。我认为 VS Code 只是一个较新版本的 MS Visual Studio。谢谢,如果我能正常工作,我会尝试并更新。再次感谢。
  • @MichaelE 你得到任何的方法了吗?到目前为止,我已经使用“编写 R 扩展”(不是在 Dll 中)中的小示例成功激活了调试器,并且我可以让 VS 代码在标准 c++ 代码上工作,但不能通过 R 执行 dll。甚至更改 makeconf/ makevars/makevars.win 删除 -DNDEBUG 并添加 -g 似乎没有多大作用。
  • 已经有一段时间了,但是是的,我相信我已经成功调试了演示。
猜你喜欢
  • 1970-01-01
  • 2011-11-23
  • 2010-12-17
  • 2015-09-06
  • 1970-01-01
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多