RStudio integrates a set of debugging tools that R interpreter has. In comparison with Perl R debugger is much less powerful but has some interesting ideas implemented more cleanly, such as browse() function which is equivalent to Perl's debugger $DB::single = 1;
To debug a function you need to call it via special function, that as a side effect activates debug
mode of interpreter. Normally, debug mode in Rstudio is entered by calling a function using
debug(f)
or debugonce(f)
. R’s debugging can't work outside function, but you
can always convert this part of your program into main function and call it using debug.
Debugger works only within functions. Before a function can be debugged, it requires to be saved to a file and the file itself need to sourced using source statement.
In case you need to debug a function not yet saved nor sourced, a simple function like:
myDebug = function(f) { fname = deparse(substitute(f)) dump(fname, file = "tmp.R") source("tmp.R") do.call("debugonce", args = list(fname), envir = globalenv()) invisible(NULL) }allows to start the visual debugger immediately.
Readers who have used Perl debugger, or GDB (the
GNU debugger) will find similarities, but some aspects will come as a surprise. One unpleasant surprise
is that debug()
is called on the function level, not on the overall program level.
If you believe you have bugs in several of your functions, you’ll need to call debug()
on each one.
It you want to execute step by step function only once use debugonce()
instead; calling
debugonce(f)
puts f()
into debugging status the first time you execute it,
but that status is reversed immediately upon exit from the function.
The core of R’s debugging facility consists of the browser. It allows you to single-step through your code, line by line from the point of its invocation.. You need to place the call into your code and resave this function.
When control reaches this point, you will automatically enter the debugging mode. So you can start debugging from a specific point instead from a beginning of the functions as with debug.
Calling undebug(f)
will unset the debug status of the function so that entry to the
function will no longer invoke the browser.
You can move the point of invocation of browser function as you progress and have several points of invocation.
When you pause in the middle of running a function and get control back to you with browser, you can enter new commands at the command line. The active environment for these commands will not be the global environment (as usual); it will be the runtime environment of the function that you have paused. As a result, you can look at the objects that the function is using, look up their values with the same scoping rules that the function would use, and run code under the same conditions that the function would run it in. This arrangement provides the best chance for spotting the source of bugs in a function.
You can make invoking the browser conditional so that it is entered only in specified situations.
Use the expr
argument to define those situations. For instance, suppose you suspect that
your bug arises only when a certain variable s
is larger than 1. You could use this code:
browser(s > 1)
The browser will be invoked only if s
is larger than 1. The following would have the
same effect:
if (s > 1) browser()
Calling the browser directly, rather than entering the debugger via debug()
is very
useful in situations in which you have a loop with many iterations and the bug surfaces only after,
say, the 50th iteration. If the loop index is i
, then you could write this:
if (i > 49) browser()
That way, you would avoid the tedium of stepping through the first 49 iterations!
To use browser, add the call browser() to the body of a function and then resave the function. Press 'n' to go step by step.
The trace()
in its simples form cn be invokes as following:
> trace(f,t)
This call instructs R to call the function t()
every time we enter the function
f()
. For instance, say we wish to set a breakpoint at the beginning of the function
main()
. We could use this command:
> trace(main,browser)
This has the same effect as placing the command browser()
in our source code for
gy()
, but it’s quicker and more convenient than inserting such a line, saving the file,
and rerunning source()
to load in the new version of the file. Calling trace()
does not change your source file, though it does change a temporary version of
your file maintained by R. It would also be quicker and more convenient to undo, by simply running
untrace
:
> untrace(gy)
You can also turn tracing on or off globally by calling tracingState()
, using the argument
TRUE
to turn it on or FALSE
to turn it off.
While you are in the browser, the prompt changes from >
to Browse[d]>
. (Here,
d
is the depth of the call chain.)
You may submit any of the following commands at that prompt:
n
(for next): Tells R to execute the next line and then pause
again. Hitting enter causes this action, too.c
(for continue): This is like n
, except that
several lines of code may be executed before the next pause. If you are currently in a loop, this
command will result in the remainder of the loop being executed and then pausing upon exit from the
loop. If you are in a function but not in a loop, the remainder of the function will be executed
before the next pause.x
by simply typing x
. Of course, if you have a variable
with the same name as a browser command, you must explicitly call something like print()
,
as in print(n)
.where
: This prints a stack trace. It displays what sequence
of function calls led execution to the current location.Q
: This quits the browser, bringing you back to R’s main interactive mode.Calling debug(f)
places a call to browser()
at the beginning of f()
.
However, this may be too coarse a tool in some cases. If you suspect that the bug is in the middle of
the function, it’s wasteful to trudge through all the intervening code.
The solution is to set breakpoints at certain key locations of your code—places
where you want execution to be paused. How can this be done in R? You can call browser
directly or use the setBreakpoint()
function.
Starting with R 2.10, you can use setBreakpoint()
in the format
setBreakpoint((*@filename,linenumber
@*))
This will result in browser()
being called at line linenumber
of our source file filename
.
This is especially useful when you are in the midst of using the debugger, single-stepping through
code. Say you are currently at line 12 of your source file x.R and want to have
a breakpoint at line 28. Instead of exiting the debugger, adding a call to browser()
at
line 28, and then re-entering the function, you could simply type this:
> setBreakpoint("x.R",28)
You could then resume execution within the debugger, say by issuing the c
command.
The setBreakpoint()
function works by calling the trace()
function, discussed
in the next section. Thus, to cancel the breakpoint, you cancel the trace. For instance, if we had called
setBreakpoint()
at a line in the function g()
, we would cancel the breakpoint
by typing the following:
> untrace(g)
You can call setBreakpoint()
whether or not you are currently in the debugger. If you
are not currently running the debugger and you execute the affected function and hit the breakpoint
during that execution, you will be put into the browser automatically. This is similar to the case of
browser()
, but using this approach, you save yourself the trouble of changing your code
via your text editor.
Say your R code crashes when you are not running the debugger. There is still a debugging tool available
to you after the fact. You can do a “postmortem” by simply calling traceback()
. It will
tell you in which function the problem occurred and the call chain that led to that function.
You can get a lot more information if you set up R to dump frames in the event of a crash:
> options(error=dump.frames)
If you’ve done this, then after a crash, run this command:
> debugger()
You will then be presented with a choice of levels of function calls to view. For each one that you
choose, you can take a look at the values of the variables there. After browsing through one level,
you can return to the debugger()
main menu by hitting N.
You can arrange to automatically enter the debugger by writing this code:
> options(error=recover)
Note, though, that if you do choose this automatic route, it will whisk you into the debugger, even if you simply have a syntax error (not a useful time to enter the debugger).
To turn off any of this behavior, type the following:
> options(error=NULL)
You’ll see a demonstration of this approach in the next section.
Dr Nikolai Bezroukov
|
Switchboard | ||||
Latest | |||||
Past week | |||||
Past month |
First recall our extended example of finding runs of 1s in Chapter 2. Here is a buggy version of the code:
1 findruns <- function(x,k) { 2 n <- length(x) 3 runs <- NULL 4 for (i in 1:(n-k)) { 5 if (all(x[i:i+k-1]==1)) runs <- c(runs,i) 6 } 7 return(runs) 8 }Let's try it on a small test case:
> source("findruns.R") > findruns(c(1,0,0,1,1,0,1,1,1),2) [1] 3 4 6 7The function was supposed to report runs at indices 4, 7, and 8, but it found some indices that it shouldn't have and missed some as well. Something is wrong. Let's enter the debugger and take a look around.
> debug(findruns) > findruns(c(1,0,0,1,1,0,1,1,1),2) debugging in: findruns(c(1, 0, 0, 1, 1, 0, 1, 1, 1), 2) debug at findruns.R#1: { n <- length(x) runs <- NULL for (i in 1:(n - k)) { if (all(x[i:i + k - 1] == 1)) runs <- c(runs, i) } return(runs) } attr(,"srcfile") findruns.RSo, according to the principle of confirmation, let's first make sure our test vector was received correctly:
Browse[2]> x [1] 1 0 0 1 1 0 1 1 1So far, so good. Let's step through the code a bit. We hit n a couple of times to single-step through the code.
Browse[2]> n debug at findruns.R#2: n <- length(x) Browse[2]> n debug at findruns.R#3: runs <- NULL Browse[2]> print(n) [1] 9Note that after each single step, R tells us which statement would be the next one to execute. In other words, at the time we executed
print(n)
, we had not yet executed the assignment ofNULL
toruns
.Note, too, that although normally you can print out the value of a variable by simply typing its name, we could not do so here for our variable
n
, becausen
is also the abbreviation for the debugger's next command. Thus, we neededprint()
.At any rate, we found that the length of our test vector was 9, confirming what we knew. Now, let's single-step some more, getting into the loop.
Browse[2]> n debug at findruns.R#4: for (i in 1:(n - k + 1)) { if (all(x[i:i + k - 1] == 1)) runs <- c(runs, i) } Browse[2]> n debug at findruns.R#4: i Browse[2]> n debug at findruns.R#5: if (all(x[i:i + k - 1] == 1)) runs <- c(runs, i)Since
k
is 2-that is, we are checking for runs of length 2- theif()
statement should be checking the first two elements ofx
, which are (1,0). Let's confirm:Browse[2]> x[i:i + k - 1] [1] 0So, it did not confirm. Let's check that we have the correct subscript range, which should be 1:2. Is it?
Browse[2]> i:i + k - 1 [1] 2Also wrong. Well, how about
i
andk
? They should be 1 and 2, respectively. Are they?Browse[2]> i [1] 1 Browse[2]> k [1] 2Well, those do confirm. Thus, our problem must be with the expression
i:i + k - 1
. After some thought, we realize there is an operator precedence problem there, and we correct it toi:(i + k - 1)
.Is it okay now?
> source("findruns.R") > findruns(c(1,0,0,1,1,0,1,1,1),2) [1] 4 7No, as mentioned, it should be (4,7,8).
Let's set a breakpoint inside the loop and take a closer look.
> setBreakpoint("findruns.R",5) /home/nm/findruns.R#5: findruns step 4,4,2 in <environment: R_GlobalEnv> > findruns(c(1,0,0,1,1,0,1,1,1),2) findruns.R#5 Called from: eval(expr, envir, enclos) Browse[1]> x[i:(i+k-1)] [1] 1 0Good, we're dealing with the first two elements of the vector, so our bug fix is working so far. Let's look at the second iteration of the loop.
Browse[1]> c findruns.R#5 Called from: eval(expr, envir, enclos) Browse[1]> i [1] 2 Browse[1]> x[i:(i+k-1)] [1] 0 0That's right, too. We could go another iteration, but instead, let's look at the last iteration, a place where bugs frequently arise in loops. So, let's add a conditional breakpoint, as follows:
findruns <- function(x,k) { n <- length(x) runs <- NULL for (i in 1:(n-k)) { if (all(x[i:(i+k-1)]==1)) runs <- c(runs,i) if (i == n-k) browser() # break in last iteration of loop } return(runs) }And now run it again.
> source("findruns.R") > findruns(c(1,0,0,1,1,0,1,1,1),2) Called from: findruns(c(1, 0, 0, 1, 1, 0, 1, 1, 1), 2) Browse[1]> i [1] 7This shows the last iteration was for
i = 7
. But the vector is nine elements long, andk = 2
, so our last iteration should bei = 8
. Some thought then reveals that the range in the loop should have been written as follows:for (i in 1:(n-k+1)) {By the way, note that the breakpoint that we set using
setBreakpoint()
is no longer valid, now that we've replaced the old version of the objectfindruns
.Subsequent testing (not shown here) indicates the code now works. Let's move on to a more complex example.
Recall our code in Section 3.4.2, which found the pair of cities with the closest distance between them. Here is a buggy version of that code:
1 returns the minimum value of d[i,j], i != j, and the row/col attaining 2 that minimum, for square symmetric matrix d; no special policy on 3 ties; 4 motivated by distance matrices 5 mind <- function(d) { 6 n <- nrow(d) 7 add a column to identify row number for apply() 8 dd <- cbind(d,1:n) 9 wmins <- apply(dd[-n,],1,imin) 10 wmins will be 2xn, 1st row being indices and 2nd being values 11 i <- which.min(wmins[1,]) 12 j <- wmins[2,i] 13 return(c(d[i,j],i,j)) 14 } 15 16 finds the location, value of the minimum in a row x 17 imin <- function(x) { 18 n <- length(x) 19 i <- x[n] 20 j <- which.min(x[(i+1):(n-1)]) 21 return(c(j,x[j])) 22 }Let's use R's debugging tools to find and fix the problems.
We'll run it first on a small test case:
> source("cities.R") > m <- rbind(c(0,12,5),c(12,0,8),c(5,8,0)) > m [,1] [,2] [,3] [1,] 0 12 5 [2,] 12 0 8 [3,] 5 8 0 > mind(m) Error in mind(m) : subscript out of boundsNot an auspicious start! Unfortunately, the error message doesn't tell us where the code blew up. But the debugger will give us that information:
> options(error=recover) > mind(m) Error in mind(m) : subscript out of bounds Enter a frame number, or 0 to exit 1: mind(m) Selection: 1 Called from: eval(expr, envir, enclos) Browse[1]> where where 1: eval(expr, envir, enclos) where 2: eval(quote(browser()), envir = sys.frame(which)) where 3 at cities.R#13: function () { if (.isMethodsDispatchOn()) { tState <- tracingState(FALSE) ...Okay, so the problem occurred in
mind()
rather thanimin()
and in particular at line 13. It still could be the fault ofimin()
, but for now, let's deal with the former.Note
There is another way we could have determined that the blowup occurred on line 13. We would enter the debugger as before but probe the local variables. We could reason that if the subscript bounds error had occurred at line 9, then the variable
wmins
would not have been set, so querying it would give us an error message like Error: object 'wmins' not found. On the other hand, if the blowup occurred on line 13, evenj
would have been set.Since the error occurred with
d[i,j]
, let's look at those variables:Browse[1]> d [,1] [,2] [,3] [1,] 0 12 5 [2,] 12 0 8 [3,] 5 8 0 Browse[1]> i [1] 2 Browse[1]> j [1] 12This is indeed a problem-
d
only has three columns, yetj
, a column subscript, is 12.Let's look at the variable from which we gleaned
j
,wmins
:Browse[1]> wmins [,1] [,2] [1,] 2 1 [2,] 12 12If you recall how the code was designed, column
k
ofwmins
is supposed to contain information about the minimum value in rowk
ofd
. So herewmins
is saying that in the first row (k
= 1) ofd
,(0,12,5), the minimum value is 12, occurring at index 2. But it should be 5 at index 3. So, something went wrong with this line:wmins <- apply(dd[-n, ], 1, imin)There are several possibilities here. But since ultimately
imin()
is called, we can check them all from within that function. So, let's set the debug status ofimin()
, quit the debugger, and rerun the code.Browse[1]> Q > debug(imin) > mind(m) debugging in: FUN(newX[, i], ...) debug at cities.R#17: { n <- length(x) i <- x[n] j <- which.min(x[(i + 1):(n - 1)]) return(c(j, x[j])) } ...So, we're in
imin()
. Let's see if it properly received the first row ofdd
, which should be (0,12,5,1).Browse[4]> x [1] 0 12 5 1It's confirmed. This seems to indicate that the first two arguments to
apply()
were correct and that the problem is instead withinimin()
, though that remains to be seen.Let's single-step through, occasionally typing confirmational queries:
Browse[2]> n debug at cities.r#17: n <- length(x) Browse[2]> n debug at cities.r#18: i <- x[n] Browse[2]> n debug at cities.r#19: j <- which.min(x[(i + 1):(n - 1)]) Browse[2]> n debug at cities.r#20: return(c(j, x[j])) Browse[2]> print(n) [1] 4 Browse[2]> i [1] 1 Browse[2]> j [1] 2Recall that we designed our call
which.min(x[(i + 1):(n - 1)]
to look only at the above-diagonal portion of this row. This is because the matrix is symmetric and because we don't want to consider the distance between a city and itself.But the value
j = 2
does not confirm. The minimum value in (0,12,5) is 5, which occurs at index 3 of that vector, not index 2. Thus, the problem is in this line:j <- which.min(x[(i + 1):(n - 1)])What could be wrong?
After taking a break, we realize that although the minimum value of (0,12,5) occurs at index 3 of that vector, that is not what we asked
which.min()
to find for us. Instead, thati + 1
term means we asked for the index of the minimum in (12,5), which is 2.We did ask
which.min()
for the correct information, but we failed to use it correctly, because we do want the index of the minimum in (0,12,5). We need to adjust the output ofwhich.min()
accordingly, as follows:j <- which.min(x[(i+1):(n-1)]) k <- i + j return(c(k,x[k]))We make the fix and try again.
> mind(m) Error in mind(m) : subscript out of bounds Enter a frame number, or 0 to exit 1: mind(m) Selection:Oh no, another bounds error! To see where the blowup occurred this time, we issue the
where
command as before, and we find it was at line 13 again. What abouti
andj
now?Browse[1]> i [1] 1 Browse[1]> j [1] 5The value of
j
is still wrong; it cannot be larger than 3, as we have only three columns in this matrix. On the other hand,i
is correct. The overall minimum value indd
is 5, occurring in row 1, column 3.So, let's check the source of
j
again, the matrixwmins
:Browse[1]> wmins [,1] [,2] [1,] 3 3 [2,] 5 8Well, there are the 3 and 5 in column 1, just as should be the case. Remember, column 1 here contains the information for row 1 in
d
, sowmins
is saying that the minimum value in row 1 is 5, occurring at index 3 of that row, which is correct.After taking another break, though, we realize that while
wmins
is correct, our use of it isn't. We have the rows and columns of that matrix mixed up. This code:i <- which.min(wmins[1,]) j <- wmins[2,i]should be like this:
i <- which.min(wmins[2,]) j <- wmins[1,i]After making that change and resourcing our file, we try it out.
> mind(m) [1] 5 1 3This is correct, and subsequent tests with larger matrices worked, too.
Google matched content |
Society
Groupthink : Two Party System as Polyarchy : Corruption of Regulators : Bureaucracies : Understanding Micromanagers and Control Freaks : Toxic Managers : Harvard Mafia : Diplomatic Communication : Surviving a Bad Performance Review : Insufficient Retirement Funds as Immanent Problem of Neoliberal Regime : PseudoScience : Who Rules America : Neoliberalism : The Iron Law of Oligarchy : Libertarian Philosophy
Quotes
War and Peace : Skeptical Finance : John Kenneth Galbraith :Talleyrand : Oscar Wilde : Otto Von Bismarck : Keynes : George Carlin : Skeptics : Propaganda : SE quotes : Language Design and Programming Quotes : Random IT-related quotes : Somerset Maugham : Marcus Aurelius : Kurt Vonnegut : Eric Hoffer : Winston Churchill : Napoleon Bonaparte : Ambrose Bierce : Bernard Shaw : Mark Twain Quotes
Bulletin:
Vol 25, No.12 (December, 2013) Rational Fools vs. Efficient Crooks The efficient markets hypothesis : Political Skeptic Bulletin, 2013 : Unemployment Bulletin, 2010 : Vol 23, No.10 (October, 2011) An observation about corporate security departments : Slightly Skeptical Euromaydan Chronicles, June 2014 : Greenspan legacy bulletin, 2008 : Vol 25, No.10 (October, 2013) Cryptolocker Trojan (Win32/Crilock.A) : Vol 25, No.08 (August, 2013) Cloud providers as intelligence collection hubs : Financial Humor Bulletin, 2010 : Inequality Bulletin, 2009 : Financial Humor Bulletin, 2008 : Copyleft Problems Bulletin, 2004 : Financial Humor Bulletin, 2011 : Energy Bulletin, 2010 : Malware Protection Bulletin, 2010 : Vol 26, No.1 (January, 2013) Object-Oriented Cult : Political Skeptic Bulletin, 2011 : Vol 23, No.11 (November, 2011) Softpanorama classification of sysadmin horror stories : Vol 25, No.05 (May, 2013) Corporate bullshit as a communication method : Vol 25, No.06 (June, 2013) A Note on the Relationship of Brooks Law and Conway Law
History:
Fifty glorious years (1950-2000): the triumph of the US computer engineering : Donald Knuth : TAoCP and its Influence of Computer Science : Richard Stallman : Linus Torvalds : Larry Wall : John K. Ousterhout : CTSS : Multix OS Unix History : Unix shell history : VI editor : History of pipes concept : Solaris : MS DOS : Programming Languages History : PL/1 : Simula 67 : C : History of GCC development : Scripting Languages : Perl history : OS History : Mail : DNS : SSH : CPU Instruction Sets : SPARC systems 1987-2006 : Norton Commander : Norton Utilities : Norton Ghost : Frontpage history : Malware Defense History : GNU Screen : OSS early history
Classic books:
The Peter Principle : Parkinson Law : 1984 : The Mythical Man-Month : How to Solve It by George Polya : The Art of Computer Programming : The Elements of Programming Style : The Unix Hater’s Handbook : The Jargon file : The True Believer : Programming Pearls : The Good Soldier Svejk : The Power Elite
Most popular humor pages:
Manifest of the Softpanorama IT Slacker Society : Ten Commandments of the IT Slackers Society : Computer Humor Collection : BSD Logo Story : The Cuckoo's Egg : IT Slang : C++ Humor : ARE YOU A BBS ADDICT? : The Perl Purity Test : Object oriented programmers of all nations : Financial Humor : Financial Humor Bulletin, 2008 : Financial Humor Bulletin, 2010 : The Most Comprehensive Collection of Editor-related Humor : Programming Language Humor : Goldman Sachs related humor : Greenspan humor : C Humor : Scripting Humor : Real Programmers Humor : Web Humor : GPL-related Humor : OFM Humor : Politically Incorrect Humor : IDS Humor : "Linux Sucks" Humor : Russian Musical Humor : Best Russian Programmer Humor : Microsoft plans to buy Catholic Church : Richard Stallman Related Humor : Admin Humor : Perl-related Humor : Linus Torvalds Related humor : PseudoScience Related Humor : Networking Humor : Shell Humor : Financial Humor Bulletin, 2011 : Financial Humor Bulletin, 2012 : Financial Humor Bulletin, 2013 : Java Humor : Software Engineering Humor : Sun Solaris Related Humor : Education Humor : IBM Humor : Assembler-related Humor : VIM Humor : Computer Viruses Humor : Bright tomorrow is rescheduled to a day after tomorrow : Classic Computer Humor
The Last but not Least Technology is dominated by two types of people: those who understand what they do not manage and those who manage what they do not understand ~Archibald Putt. Ph.D
Copyright © 1996-2021 by Softpanorama Society. www.softpanorama.org was initially created as a service to the (now defunct) UN Sustainable Development Networking Programme (SDNP) without any remuneration. This document is an industrial compilation designed and created exclusively for educational use and is distributed under the Softpanorama Content License. Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.
FAIR USE NOTICE This site contains copyrighted material the use of which has not always been specifically authorized by the copyright owner. We are making such material available to advance understanding of computer science, IT technology, economic, scientific, and social issues. We believe this constitutes a 'fair use' of any such copyrighted material as provided by section 107 of the US Copyright Law according to which such material can be distributed without profit exclusively for research and educational purposes.
This is a Spartan WHYFF (We Help You For Free) site written by people for whom English is not a native language. Grammar and spelling errors should be expected. The site contain some broken links as it develops like a living tree...
|
You can use PayPal to to buy a cup of coffee for authors of this site |
Disclaimer:
The statements, views and opinions presented on this web page are those of the author (or referenced source) and are not endorsed by, nor do they necessarily reflect, the opinions of the Softpanorama society. We do not warrant the correctness of the information provided or its fitness for any purpose. The site uses AdSense so you need to be aware of Google privacy policy. You you do not want to be tracked by Google please disable Javascript for this site. This site is perfectly usable without Javascript.
Last modified: March, 12, 2019