跳至内容

如果您是使用 Arrow 代码的开发人员,该软件包对 tidy eval 和 C++ 的使用需要可靠的调试策略。 在本文中,我们推荐几种方法。

调试 R 代码

通常,我们发现使用交互式调试(例如,调用 browser()),您可以在特定环境中检查对象,比诸如 print() 语句之类的简单技术更有效。

在段错误后获取更具描述性的 C++ 错误消息

如果您在 RStudio IDE 中工作,如果发生段错误,您的 R 会话将会中止。 如果您在命令行 R 会话中重新运行代码,会话不会自动中止,因此可以复制伴随段错误的错误消息。 这是一个在编写时存在的错误的示例。

> S3FileSystem$create()

 *** caught segfault ***
address 0x1a0, cause 'memory not mapped'

Traceback:
 1: (function (anonymous, access_key, secret_key, session_token,     role_arn, session_name, external_id, load_frequency, region,     endpoint_override, scheme, background_writes) {    .Call(`_arrow_fs___S3FileSystem__create`, anonymous, access_key,         secret_key, session_token, role_arn, session_name, external_id,         load_frequency, region, endpoint_override, scheme, background_writes)})(access_key = "", secret_key = "", session_token = "", role_arn = "",     session_name = "", external_id = "", load_frequency = 900L,     region = "", endpoint_override = "", scheme = "", background_writes = TRUE,     anonymous = FALSE)
 2: exec(fs___S3FileSystem__create, !!!args)
 3: S3FileSystem$create()

此输出提供 R 回溯; 但是,它没有提供有关段错误的确切 C++ 代码行的任何信息。 为此,您需要使用附加的 C++ 调试器运行 R。

使用附加的 C++ 调试器运行 R 代码

由于 Arrow 以 C++ 代码为核心,因此当错误源自 C++ 而非 R 层时,调试代码有时会很棘手。 如果您添加触发 C++ 错误的新代码(或在现有代码中找到一个错误),这可能会导致段错误。 如果您在 RStudio 中工作,会话将中止,并且您可能无法检索诊断和/或报告错误所需的消息传递。 一种解决方法是找到导致错误的代码,并使用 C++ 调试器运行 R。

如果您使用的是 macOS 并且已使用 Apple 安装程序安装 R,您将无法使用附加的调试器运行 R; 请参阅此处的说明,了解有关原因和解决方法的详细信息。

首先,使用调试器加载 R。最常见的调试器是 gdb(通常在 Linux 上找到,有时在 macOS 上找到,或通过 MinGW 或 Cygwin 在 Windows 上找到)和 lldb(默认的 macOS 调试器)。

就我而言,它是 gdb,但如果您使用的是 lldb 调试器(例如,如果您使用的是 Mac),只需在此处替换该命令即可。

R -d gdb

接下来,运行 R。

run

您现在应该在附加了 C++ 调试器的 R 会话中。 这看起来类似于正常的 R 会话,但具有额外的输出。

现在,运行您的代码 - 直接在会话中运行或从文件中获取它。 如果代码导致段错误,您将获得额外的输出,您可以使用这些输出诊断问题或将其作为额外信息附加到问题。

这是前一个示例中显示的段错误的调试器输出。 您可以在此处看到触发段错误的确切行包含在输出中。

> S3FileSystem$create()

Thread 1 "R" received signal SIGSEGV, Segmentation fault.
0x00007ffff0128369 in std::__atomic_base<long>::operator++ (this=0x178) at /usr/include/c++/9/bits/atomic_base.h:318
318       operator++() noexcept

如果您的会话挂起,则获取调试器输出

上述说明可以在发生段错误时提供有价值的附加上下文。 但是,偶尔会出现这样一种情况,即错误可能会导致您的会话无限期地挂起而不会发生段错误。 在这种情况下,中断调试器并从所有正在运行的线程生成回溯可能在诊断上很有用。

为此,首先,按 Ctrl/Cmd 和 C 以中断调试器,然后运行

thread apply all bt

这将生成大量输出,但在识别问题原因时,此信息非常有用。

延伸阅读

以下资源提供了 R 代码调试的详细指南

有关在 R 中使用 C++ 调试器的出色深入指南,请参阅David Vaughan 的这篇博文。

您可以在 LLDB 网站上找到等效的 gdb 和 lldb 命令列表。