Winch provides stack traces for call chains that cross between R and C function calls. This is a useful tool for developers of R packages where a substantial portion of the code is C or C++.
Install the released version of winch from CRAN with:
install.packages("winch")
Install the development version from GitHub with:
devtools::install_github("r-lib/winch")
Below is an example where an R function calls into C which calls back into R. Note the second-to-last entry in the trace:
library(winch) foo <- function() { winch_call(bar) } bar <- function() { winch_trace_back() } foo() #> func ip #> 1 Rf_NewFrameConfirm 00007ffa45274480 #> 2 Rf_eval 00007ffa452be4f0 #> 3 R_execMethod 00007ffa452c3550 #> 4 Rf_eval 00007ffa452be4f0 #> 5 R_execMethod 00007ffa452c1e90 #> 6 Rf_eval 00007ffa452be4f0 #> 7 Rf_eval 00007ffa452c0170 #> 8 Rf_applyClosure 00007ffa452c1090 #> 9 Rf_eval 00007ffa452be4f0 #> 10 R_execMethod 00007ffa452c1e90 #> 11 Rf_eval 00007ffa452be4f0 #> 12 Rf_eval 00007ffa452c0170 #> 13 Rf_applyClosure 00007ffa452c1090 #> 14 Rf_eval 00007ffa452be4f0 #> 15 winch_call 00007ffa32145240 #> 16 Rf_NewFrameConfirm 00007ffa452728a0 #> 17 Rf_NewFrameConfirm 00007ffa45274480 #> 18 Rf_eval 00007ffa452be4f0 #> 19 R_execMethod 00007ffa452c1e90 #> 20 Rf_eval 00007ffa452be4f0 #> 21 Rf_eval 00007ffa452c0170 #> 22 Rf_applyClosure 00007ffa452c1090 #> 23 Rf_eval 00007ffa452be4f0 #> 24 R_execMethod 00007ffa452c1e90 #> 25 Rf_eval 00007ffa452be4f0 #> 26 Rf_eval 00007ffa452c0170 #> 27 Rf_applyClosure 00007ffa452c1090 #> 28 Rf_eval 00007ffa452be4f0 #> 29 R_forceAndCall 00007ffa452c43a0 #> 30 do_Rprof 00007ffa452a8b80 #> 31 Rf_eval 00007ffa452be4f0 #> 32 Rf_eval 00007ffa452c0170 #> 33 Rf_applyClosure 00007ffa452c1090 #> pathname #> 1 /usr/lib/R/lib/libR.so #> 2 /usr/lib/R/lib/libR.so #> 3 /usr/lib/R/lib/libR.so #> 4 /usr/lib/R/lib/libR.so #> 5 /usr/lib/R/lib/libR.so #> 6 /usr/lib/R/lib/libR.so #> 7 /usr/lib/R/lib/libR.so #> 8 /usr/lib/R/lib/libR.so #> 9 /usr/lib/R/lib/libR.so #> 10 /usr/lib/R/lib/libR.so #> 11 /usr/lib/R/lib/libR.so #> 12 /usr/lib/R/lib/libR.so #> 13 /usr/lib/R/lib/libR.so #> 14 /usr/lib/R/lib/libR.so #> 15 /home/kirill/git/R/r-prof/winch/src/winch.so #> 16 /usr/lib/R/lib/libR.so #> 17 /usr/lib/R/lib/libR.so #> 18 /usr/lib/R/lib/libR.so #> 19 /usr/lib/R/lib/libR.so #> 20 /usr/lib/R/lib/libR.so #> 21 /usr/lib/R/lib/libR.so #> 22 /usr/lib/R/lib/libR.so #> 23 /usr/lib/R/lib/libR.so #> 24 /usr/lib/R/lib/libR.so #> 25 /usr/lib/R/lib/libR.so #> 26 /usr/lib/R/lib/libR.so #> 27 /usr/lib/R/lib/libR.so #> 28 /usr/lib/R/lib/libR.so #> 29 /usr/lib/R/lib/libR.so #> 30 /usr/lib/R/lib/libR.so #> 31 /usr/lib/R/lib/libR.so #> 32 /usr/lib/R/lib/libR.so #> 33 /usr/lib/R/lib/libR.so #> [ reached 'max' / getOption("max.print") -- omitted 99 rows ]
rlang::entrace()
checks if winch is installed, and adds a native backtrace. As this cannot be easily demonstrated in a knitr document, the output is copied from a GitHub Actions run.
options( error = rlang::entrace, rlang_backtrace_on_error = "full", rlang_trace_use_winch = TRUE ) vctrs::vec_as_location(quote, 2)
Error: Must subset elements with a valid subscript vector.
✖ Subscript has the wrong type `function`.
ℹ It must be logical, numeric, or character.
Backtrace:
█
1. └─vctrs::vec_as_location(quote, 2)
2. └─`/vctrs.so`::vctrs_as_location()
3. └─`/vctrs.so`::vec_as_location_opts()
winch uses a very simple heuristic. R’s traceback (and also profiling) infrastructure introduces the notion of a “context”. Every call to an R function opens a new context and closes it when execution of the function ends. Unfortunately, no new context is established for native code called with .Call()
or .External()
. Establishing contexts expends precious run time, so this may be the reason for the omission.
To work around this limitation, the source code of all R functions along the call chain is scanned for instances of .Call
and .External
. The native call stack (obtained via libunwind or libbacktrace) is scanned for chunks of code outside of libR.so
(R’s main library) – these are assumed to correspond to .Call()
or .External()
. The native traces are embedded as artificial calls into the R stack trace.
- The matching will not be perfect, but it may still lead to faster discovery of the cause of an error.
- On Windows winch only works on x64, and there the traces can be obtained only for one shared library at a time. See
winch_init_library()
for details.
Please note that the winch project is released with a Contributor Code of Conduct. By contributing to this project, you agree to abide by its terms.