Debugging

Shannon Pileggi

Agenda

Getting started

Troubleshooting

Moving on

Debugging your code

Debugging in RStudio

Debugging their code

Special cases

Read the source

Getting started

Licensing



This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License (CC BY-SA4.0).

Checklist


R installed? Pretty recent?

     Recommended R ≥ 4.2.0

RStudio installed?

     I’m on 2022.02.3+492

Ready to build packages?

     devtools::has_devel()
     Your system is ready to build packages!

Additional resources

A cartoon of a fuzzy round monster face showing 10 different emotions experienced during the process of debugging code. The progression goes from (1) “I got this” - looking determined and optimistic; (2) “Huh. Really thought that was it.” - looking a bit baffled; (3) “...” - looking up at the ceiling in thought; (4) “Fine. Restarting.” - looking a bit annoyed; (5) “OH WTF.” Looking very frazzled and frustrated; (6) “Zombie meltdown.” - looking like a full meltdown; (7) (blank) - sleeping; (8) “A NEW HOPE!” - a happy looking monster with a lightbulb above; (9) “insert awesome theme song” - looking determined and typing away; (10) “I love coding” - arms raised in victory with a big smile, with confetti falling.

Troubleshooting

2. Reset

Have you tried turning it OFF and ON again?

Restart R, especially when things get weird.

Session -> Restart R or

  • Ctrl + Shift + F10 (Windows),

  • Cmd + Shift + 0 / Cmd + Shift + F10 (Mac)

Tools -> Global Options -> Workspace

3. Reprex

minimum reproducible example

minimum

reproducible example

why reprex

Review


Troubleshooting strategies:


  1. Search

  2. Reset

  3. Reprex

But what if

Moving on


from troubleshooting to…

formal debugging techniques.

Key concepts


traceback

   location where did the error occur


interactive debugger

   context why did the error occur


your code   vs   their code

Debugging tools

This is a lot.


These tools achieve similar objectives slightly differently.


People don’t generally use all of these tools at once. They pick and choose the ones they like.

Sourcing


⚠️ name your script with functions

⚠️ source your script with functions

for the best debugging experience 😎


Set up


f <- function(x) {
  x + 1
}

g <- function(x) f(x)

g("a")
Error in x + 1: non-numeric argument to binary operator

Debugging your code

print()

f <- function(x) {
  print(x)
  x + 1
}

g <- function(x) f(x)
g("a")
[1] "a"
Error in x + 1: non-numeric argument to binary operator

cat()

f <- function(x) {
  cat("f()\n")
  cat("x =", x, "\n")
  x + 1
}

g <- function(x) f(x)

g("a")
f()
x = a 
Error in x + 1: non-numeric argument to binary operator

traceback()


source("demo/my_functions.R")
g("a")
traceback()


shows the sequence of calls that lead to the error.

Richer traceback


source("demo/my_functions.R")
options(error = rlang::entrace) 
g("a")
rlang::last_error()

Richer traceback


source("demo/my_functions.R")
options(error = rlang::entrace) 
g("a")
rlang::last_error()
rlang::last_trace()


options(error = rlang::entrace)
could go in your .Rprofile

traceback vs rlang functions


Numbering and ordering differs between traceback() and rlang functions.

browser()

f <- function(x) {
  browser()
  x + 1
}

g <- function(x) f(x)


browser() opens the interactive debugger.

  1. Modify the function by inserting a browser() statement.

  2. Source the function.

  3. Execute the function.

Interactive debugger

Interactive debugger tips


Investigate objects



Control execution

ls(), ls.str(),
str(), print()


command operation
n next statement
c continue (leave interactive debugging)
s step into function call
f finish loop / function
where show previous calls
Q quit debugger

Debugging your code

Your turn, exercise 01

usethis::use_course("rstats-wtf/wtf-debugging")

Complete 01_exercise to practice debugging your own code.

Check out the README.md for some getting started tips!

  • 01_debugging_spartan.R (directions to explore without suggested code)

  • 01_debugging_comfy.R (directions to explore with suggested code)

  • 01_debugging_solution.R (directions to explore with code solutions)

15:00

Debugging in RStudio

Editor breakpoints

red circle = breakpoint = browser()

(but you don’t have to change your code)

Set / re-set an editor breakpoint:

  • click to the left of the line number in the source file

  • press Shift+F9 with your cursor on the line

Editor breakpoints

red circle = breakpoint = browser()

(but you don’t have to change your code)

To activate, either

  • click IDE Source button, or

  • debugSource(“demo/my_functions.R”)

Debugging console

IDE on error

Automatically invoke actions on error.

New set up for demonstration.

f <- function(x) { strsplit(x, ",") }
g <- function(x) { f(x) }
g("a,b")
[[1]]
[1] "a" "b"


g(factor("a,b"))
Error in strsplit(x, ","): non-character argument

IDE message only


IDE error inspector


IDE break in code


Error inspector

# IDE Error Inspector not triggered
f <- function(x) x + 1
g <- function(x) f(x)
g("a")

# IDE Error Inspector not triggered
strsplit(factor("a,b"), ",")

# IDE Error Inspector not triggered
f <- function(x) strsplit(x, ",")
f(factor("a,b"))

# IDE Error Inspector yes triggered
g <- function(x) f(x)
g(factor("a,b"))


The error inspector is only invoked if your code is involved.

Debugging in the RStudio IDE

Your turn, exercise 02


Complete 02_exercise to practice debugging a slightly different version of your own code, using features from the RStudio IDE.

07:00

Debugging their code

debug()

debug() = browser()/ breakpoint in first line of function

# source if code is yours, otherwise not necessary
source("demo/my_functions.R")
# set debugging flag
debug("g")
g("a")
# turn off debugging flag
undebug("g")
  • interactive debugger is initiated every time g() is executed, until undebug("g")

  • depending on function internals, this can… trap you in the debugger 😬

debugonce()

debugonce() = browser()/ breakpoint in first line of function

# source if code is yours, otherwise not necessary
source("demo/my_functions.R")
# set debugging flag
debugonce("g")
g("a")
  • one time only!

  • interactive debugger initiated a single time when g() is executed

options(error = recover)

we already discussed
options(error = rlang::entrace)
for a richer traceback on error


🤠 options(error = recover)

  1. displays an interactive prompt with frames

  2. you select the frame to enter the debugger

recover example

source("demo/my_functions.R")
options(error = recover)
g("a")
options(error = NULL)


trace() overview

trace(what = fun, tracer = browser)
is equivalent to

  • inserting browser() in first line of function

  • debug(fun)

you an also insert any code at any location in function

  • trace(what = fun, tracer = browser, at = 2)
    inserts browser() at second step of fun

untrace(fun) cancels the tracing

trace() without a specified step


trace(what = colSums, tracer = browser)
colSums(1:3)
# ls.str() in browser
untrace(colSums)


trace(what = colSums, tracer = browser)
is equivalent to

  • inserting browser() in first line of colSums
    if we had the source code

  • debug(colSums)

identify function step

trace() at specified step


# identify spot to insert code
as.list(x[[3]][[1]])
# equivalent notation
as.list(x[[c(3, 1)]])

# insert browser at step 3.1
trace(colSums, browser, at = list(c(3, 1)))
# execute function
colSums(1:3)
# ls.str() handy in browser
# cancel tracing
untrace(colSums)

Debugging their code

Your turn, exercise 03


# install.packages("devtools")
devtools::install_github("rstats-wtf/wtfdbg")


Complete 03_exercise to practice debugging others’ code.

10:00

Pick your favorite

Special cases

Rmarkdown


Troubleshooting:

  1. rmarkdown chunk option error = TRUE enables knitting with errors

  2. insert knitr::knit_exit() and interactively work through .Rmd


Debugging:

  1. Adv R Ch 22.5.3 RMarkdown https://adv-r.hadley.nz/debugging.html#rmarkdown

  2. WTF Ch 11.4 Debugging in Rmarkdown documents https://rstats.wtf/debugging-r-code.html#debugging-in-r-markdown-documents

Piped expressions


tracebacks can be verbose with pipes


options(error = rlang::entrace, rlang_backtrace_on_error = "branch")

gives trimmed tracebacks when using pipes

Warnings


If you want to dig deeper into a warning, you can convert them to errors to initiate debugging tools.

?options
options(warn = 0) # default, stores warnings until top-level function returns
options(warn = 1) # warnings are printed as they occur
options(warn = 2) # upgrades warnings to errors


# initiate recover on warning
options(warn = 2, error = recover)
# restore original settings
options(warn = 0, error = NULL)

Debugging special cases

Read the source

(almost) all source on GitHub


Link Description
github.com/search All of GitHub! Can do advanced searches, too.
github.com/cran CRAN packages
github.com/wch/r-source R core


About searching on GitHub

google: cran pkgname


github.com/cran

In R console

pkg::fnc to see exported functions

pkg:::fnc to see non-exported functions

Your turn, investigating a CRAN package


At a high level,
how does readr::read_lines() work?

03:00

github.com/wch/r-source


Location Item
src/main base R
src/library/ packages included in base R
doc/manual R manuals & documentation

Your turn, investigating source code


  1. When was the trimws() function added to base R?

  2. How does is.na() work?

Hints on next slides.

05:00

Investigating source code hints

  1. When was the trimws() function added to base R?

Try restricting search to only commit messages in github.com/wch/r-source.

Investigating source code hints

  1. How does is.na() work?
  1. Look at the function.
is.na
function (x)  .Primitive("is.na")
  1. Look in github.com/wch/r-source/src/main/names.c to find the name of the internal function. Or in GitHub search bar: is.na filename:names.c.

  2. In GitHub search bar: "do_isna" language:c

Go forth,
and learn from your bugs!



Troubleshoot

Debug

Read the source