Mitigating Stalls Caused by Call Deparse on Error

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Mitigating Stalls Caused by Call Deparse on Error

R devel mailing list
When large calls cause errors R may stall for extended periods.  This
is particularly likely to happen with `do.call`, as in this example
with a 24 second stall:

    x <- runif(1e7)
    system.time(do.call(paste0, list(abs, x)))  # intentional error
    ## Error in (function (..., collapse = NULL)  :
    ##   cannot coerce type 'builtin' to vector of type 'character'
    ## Calls: system.time -> do.call -> <Anonymous>
    ## Timing stopped at: 23.81 0.149 24.04

    str(.Traceback)
    ## Dotted pair list of 3
    ##  $ : chr [1:2500488] "(function (..., collapse = NULL) " ".Internal(paste0(list(...), collapse)))(.Primitive(\"abs\"), c(0.718117154669017, " "0.494785501621664, 0.1453434410505, 0.635028422810137, 0.0353180423844606, " "0.688418723642826, 0.889682895969599, 0.728154224809259, 0.292572240810841, " ...
    ##  $ : chr "do.call(paste0, list(abs, x))"
    ##  $ : chr "system.time(do.call(paste0, list(abs, x)))"

The first time I noticed this I thought my session had frozen/crashed
as the standard interrupt ^C does not work during the deparse.  The
stall happens when on error the call stack is deparsed prior to being
saved to `.Traceback`.  The deparsing is done by `deparse1m` in native
code, with the value of `getOption('deparse.max.lines')` which
defaults to all lines.

Since there is little value to seeing millions of lines of deparsed
objects in `traceback()`, a simple work-around is to change the
`deparse.max.lines` value:

    options(deparse.max.lines=1)
    system.time(do.call(paste0, list(abs, x)))
    ## Error in (function (..., collapse = NULL)  :
    ##   cannot coerce type 'builtin' to vector of type 'character'
    ## Calls: system.time -> do.call -> <Anonymous>
    ## Timing stopped at: 0 0 0

Unfortunately this will affect all `deparse` calls, and it seems
undesirable to pre-emptively enable it just for calls that might cause
large deparses on error.

An alternative is to store the actual calls instead of their deparsed
character equivalents in `.Traceback`.  This defers the deparsing to
when `traceback()` is used.  As per `?traceback`, it should be
relatively safe to modify `.Traceback` in this way:

> It is undocumented where .Traceback is stored nor that it is
> visible, and this is subject to change.

Deferring the deparsing to `traceback()` will give us the 
opportunity to use a different `max.lines` setting as we do here
with the patch applied:

    system.time(do.call(paste0, list(abs, x)))
    ## Error in (function (..., collapse = NULL)  :
    ##   cannot coerce type 'builtin' to vector of type 'character'
    ## Timing stopped at: 0.028 0 0.029

    system.time(traceback(max.lines=3))
    ## 3: (function (..., collapse = NULL)
    ##    .Internal(paste0(list(...), collapse)))(.Primitive("abs"), c(0.535468587651849,
    ##    0.0540027911774814, 0.732930393889546, 0.565360915614292, 0.544816034380347,
    ##     ...
    ## 2: do.call(paste0, list(abs, x))
    ## 1: system.time(do.call(paste0, list(abs, x)))
    ##    user  system elapsed
    ##   0.000   0.000   0.003


More generally, it might be better to have a different smaller default
value for the lines to deparse when calls  are _displayed_ as parts of
lists, as is the case with `traceback()`, or in `print(sys.calls())` and
similar.

I attach a patch that does this.  I have run some basic tests
and `make check-devel` passes. I can file an issue on bugzilla
if that is a better place to have this conversation (assuming there
is interest in it).

Best,

Brodie

PS: for some reason my mail client is refusing to attach the patch so I paste it
starting on the next line.
Index: src/gnuwin32/Rdll.hide
===================================================================
--- src/gnuwin32/Rdll.hide    (revision 76827)
+++ src/gnuwin32/Rdll.hide    (working copy)
@@ -94,6 +94,7 @@
  R_GetMaxNSize
  R_GetMaxVSize
  R_GetTraceback
+ R_GetTracebackParsed
  R_GetVarLocSymbol
  R_GetVarLocValue
  R_HandlerStack
Index: src/include/Defn.h
===================================================================
--- src/include/Defn.h    (revision 76827)
+++ src/include/Defn.h    (working copy)
@@ -1296,6 +1296,7 @@
 void NORET ErrorMessage(SEXP, int, ...);
 void WarningMessage(SEXP, R_WARNING, ...);
 SEXP R_GetTraceback(int);
+SEXP R_GetTracebackParsed(int);
 
 R_size_t R_GetMaxVSize(void);
 void R_SetMaxVSize(R_size_t);
Index: src/library/base/R/traceback.R
===================================================================
--- src/library/base/R/traceback.R    (revision 76827)
+++ src/library/base/R/traceback.R    (working copy)
@@ -16,9 +16,19 @@
 #  A copy of the GNU General Public License is available at
 #  https://www.R-project.org/Licenses/
 
-.traceback <- function(x = NULL) {
-    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv())))
-    {}
+.traceback <- function(x = NULL, max.lines=getOption("deparse.max.lines")) {
+    if(!(is.numeric(max.lines) && !is.na(max.lines) &&
+         as.integer(max.lines) > 0L)
+    ) {
+        max.lines <- -1L
+    }
+    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv()))) {
+        for(i in seq_along(x)) {
+            srcref <- attr(x[[i]], 'srcref')
+            x[[i]] <- deparse(x[[i]], nlines=max.lines)
+            attr(x[[i]], 'srcref') <- srcref
+        }
+    }
     else if (is.numeric(x))
         x <- .Internal(traceback(x))
     x
@@ -26,7 +36,16 @@
 
 traceback <- function(x = NULL, max.lines = getOption("deparse.max.lines"))
 {
-    n <- length(x <- .traceback(x))
+    valid.max.lines <- is.numeric(max.lines) && !is.na(max.lines) &&
+         as.integer(max.lines) > 0L
+
+    if(valid.max.lines) {
+        # max.lines + 1L so we can know that output was truncated by .traceback
+        max.lines.2 <- as.integer(max.lines) + 1L
+    } else {
+        max.lines.2 <- -1L
+    }
+    n <- length(x <- .traceback(x, max.lines=max.lines.2))
     if(n == 0L)
         cat(gettext("No traceback available"), "\n")
     else {
@@ -40,7 +59,7 @@
                 paste0(" at ", basename(srcfile$filename), "#", srcref[1L])
             }
             ## Truncate deparsed code (destroys attributes of xi)
-            if(is.numeric(max.lines) && max.lines > 0L && max.lines < m) {
+            if(valid.max.lines &&  max.lines < m) {
                 xi <- c(xi[seq_len(max.lines)], " ...")
                 m <- length(xi)
             }
Index: src/library/base/man/traceback.Rd
===================================================================
--- src/library/base/man/traceback.Rd    (revision 76827)
+++ src/library/base/man/traceback.Rd    (working copy)
@@ -21,7 +21,7 @@
 }
 \usage{
 traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
-.traceback(x = NULL)
+.traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
 }
 \arguments{
   \item{x}{\code{NULL} (default, meaning \code{.Traceback}), or an
Index: src/main/errors.c
===================================================================
--- src/main/errors.c    (revision 76827)
+++ src/main/errors.c    (working copy)
@@ -1008,7 +1008,7 @@
        (which should not happen) */
     if (traceback && inError < 2 && inError == oldInError) {
         inError = 2;
-        PROTECT(s = R_GetTraceback(0));
+        PROTECT(s = R_GetTracebackParsed(0));
         SET_SYMVALUE(install(".Traceback"), s);
         /* should have been defineVar
            setVar(install(".Traceback"), s, R_GlobalEnv); */
@@ -1440,9 +1440,11 @@
     PrintWarnings();
     }
 }
-
+/*
+ * Return the traceback without deparsing the calls
+ */
 attribute_hidden
-SEXP R_GetTraceback(int skip)
+SEXP R_GetTracebackParsed(int skip)
 {
     int nback = 0, ns;
     RCNTXT *c;
@@ -1467,7 +1469,9 @@
         if (skip > 0)
         skip--;
         else {
-        SETCAR(t, deparse1m(c->call, 0, DEFAULTDEPARSE));
+                // Extra paranoid PROTECTS
+        SETCAR(t, PROTECT(duplicate(c->call)));
+                UNPROTECT(1);
         if (c->srcref && !isNull(c->srcref)) {
             SEXP sref;
             if (c->srcref == R_InBCInterpreter)
@@ -1482,7 +1486,26 @@
     UNPROTECT(1);
     return s;
 }
+/*
+ * Return the traceback with calls deparsed
+ */
+attribute_hidden
+SEXP R_GetTraceback(int skip)
+{
+    int nback = 0;
+    SEXP s, t, u, v;
+    s = PROTECT(R_GetTracebackParsed(skip));
+    for(t = s; t != R_NilValue; t = CDR(t)) nback++;
+    u = v = PROTECT(allocList(nback));
 
+    for(t = s; t != R_NilValue; t = CDR(t), v=CDR(v)) {
+        SETCAR(v, PROTECT(deparse1m(CAR(t), 0, DEFAULTDEPARSE)));
+        UNPROTECT(1);
+    }
+    UNPROTECT(1);
+    return u;
+}
+
 SEXP attribute_hidden do_traceback(SEXP call, SEXP op, SEXP args, SEXP rho)
 {
     int skip;

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: [External] Mitigating Stalls Caused by Call Deparse on Error

Tierney, Luke
This is probably best viewed in the context of other issue with
displaying calls, such as issues arising from calls constructed in
non-standard evaluation contexts. Might be good to move to a wishlist
item in bugzilla.

Best,

luke

On Sat, 13 Jul 2019, brodie gaslam via R-devel wrote:

> When large calls cause errors R may stall for extended periods.  This
> is particularly likely to happen with `do.call`, as in this example
> with a 24 second stall:
>
>     x <- runif(1e7)
>     system.time(do.call(paste0, list(abs, x)))  # intentional error
>     ## Error in (function (..., collapse = NULL)  :
>     ##   cannot coerce type 'builtin' to vector of type 'character'
>     ## Calls: system.time -> do.call -> <Anonymous>
>     ## Timing stopped at: 23.81 0.149 24.04
>
>     str(.Traceback)
>     ## Dotted pair list of 3
>     ##  $ : chr [1:2500488] "(function (..., collapse = NULL) " ".Internal(paste0(list(...), collapse)))(.Primitive(\"abs\"), c(0.718117154669017, " "0.494785501621664, 0.1453434410505, 0.635028422810137, 0.0353180423844606, " "0.688418723642826, 0.889682895969599, 0.728154224809259, 0.292572240810841, " ...
>     ##  $ : chr "do.call(paste0, list(abs, x))"
>     ##  $ : chr "system.time(do.call(paste0, list(abs, x)))"
>
> The first time I noticed this I thought my session had frozen/crashed
> as the standard interrupt ^C does not work during the deparse.  The
> stall happens when on error the call stack is deparsed prior to being
> saved to `.Traceback`.  The deparsing is done by `deparse1m` in native
> code, with the value of `getOption('deparse.max.lines')` which
> defaults to all lines.
>
> Since there is little value to seeing millions of lines of deparsed
> objects in `traceback()`, a simple work-around is to change the
> `deparse.max.lines` value:
>
>     options(deparse.max.lines=1)
>     system.time(do.call(paste0, list(abs, x)))
>     ## Error in (function (..., collapse = NULL)  :
>     ##   cannot coerce type 'builtin' to vector of type 'character'
>     ## Calls: system.time -> do.call -> <Anonymous>
>     ## Timing stopped at: 0 0 0
>
> Unfortunately this will affect all `deparse` calls, and it seems
> undesirable to pre-emptively enable it just for calls that might cause
> large deparses on error.
>
> An alternative is to store the actual calls instead of their deparsed
> character equivalents in `.Traceback`.  This defers the deparsing to
> when `traceback()` is used.  As per `?traceback`, it should be
> relatively safe to modify `.Traceback` in this way:
>
>> It is undocumented where .Traceback is stored nor that it is
>> visible, and this is subject to change.
>
> Deferring the deparsing to `traceback()` will give us the 
> opportunity to use a different `max.lines` setting as we do here
> with the patch applied:
>
>     system.time(do.call(paste0, list(abs, x)))
>     ## Error in (function (..., collapse = NULL)  :
>     ##   cannot coerce type 'builtin' to vector of type 'character'
>     ## Timing stopped at: 0.028 0 0.029
>
>     system.time(traceback(max.lines=3))
>     ## 3: (function (..., collapse = NULL)
>     ##    .Internal(paste0(list(...), collapse)))(.Primitive("abs"), c(0.535468587651849,
>     ##    0.0540027911774814, 0.732930393889546, 0.565360915614292, 0.544816034380347,
>     ##     ...
>     ## 2: do.call(paste0, list(abs, x))
>     ## 1: system.time(do.call(paste0, list(abs, x)))
>     ##    user  system elapsed
>     ##   0.000   0.000   0.003
>
>
> More generally, it might be better to have a different smaller default
> value for the lines to deparse when calls  are _displayed_ as parts of
> lists, as is the case with `traceback()`, or in `print(sys.calls())` and
> similar.
>
> I attach a patch that does this.  I have run some basic tests
> and `make check-devel` passes. I can file an issue on bugzilla
> if that is a better place to have this conversation (assuming there
> is interest in it).
>
> Best,
>
> Brodie
>
> PS: for some reason my mail client is refusing to attach the patch so I paste it
> starting on the next line.
> Index: src/gnuwin32/Rdll.hide
> ===================================================================
> --- src/gnuwin32/Rdll.hide    (revision 76827)
> +++ src/gnuwin32/Rdll.hide    (working copy)
> @@ -94,6 +94,7 @@
>   R_GetMaxNSize
>   R_GetMaxVSize
>   R_GetTraceback
> + R_GetTracebackParsed
>   R_GetVarLocSymbol
>   R_GetVarLocValue
>   R_HandlerStack
> Index: src/include/Defn.h
> ===================================================================
> --- src/include/Defn.h    (revision 76827)
> +++ src/include/Defn.h    (working copy)
> @@ -1296,6 +1296,7 @@
>  void NORET ErrorMessage(SEXP, int, ...);
>  void WarningMessage(SEXP, R_WARNING, ...);
>  SEXP R_GetTraceback(int);
> +SEXP R_GetTracebackParsed(int);
>  
>  R_size_t R_GetMaxVSize(void);
>  void R_SetMaxVSize(R_size_t);
> Index: src/library/base/R/traceback.R
> ===================================================================
> --- src/library/base/R/traceback.R    (revision 76827)
> +++ src/library/base/R/traceback.R    (working copy)
> @@ -16,9 +16,19 @@
>  #  A copy of the GNU General Public License is available at
>  #  https://www.R-project.org/Licenses/
>  
> -.traceback <- function(x = NULL) {
> -    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv())))
> -    {}
> +.traceback <- function(x = NULL, max.lines=getOption("deparse.max.lines")) {
> +    if(!(is.numeric(max.lines) && !is.na(max.lines) &&
> +         as.integer(max.lines) > 0L)
> +    ) {
> +        max.lines <- -1L
> +    }
> +    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv()))) {
> +        for(i in seq_along(x)) {
> +            srcref <- attr(x[[i]], 'srcref')
> +            x[[i]] <- deparse(x[[i]], nlines=max.lines)
> +            attr(x[[i]], 'srcref') <- srcref
> +        }
> +    }
>      else if (is.numeric(x))
>          x <- .Internal(traceback(x))
>      x
> @@ -26,7 +36,16 @@
>  
>  traceback <- function(x = NULL, max.lines = getOption("deparse.max.lines"))
>  {
> -    n <- length(x <- .traceback(x))
> +    valid.max.lines <- is.numeric(max.lines) && !is.na(max.lines) &&
> +         as.integer(max.lines) > 0L
> +
> +    if(valid.max.lines) {
> +        # max.lines + 1L so we can know that output was truncated by .traceback
> +        max.lines.2 <- as.integer(max.lines) + 1L
> +    } else {
> +        max.lines.2 <- -1L
> +    }
> +    n <- length(x <- .traceback(x, max.lines=max.lines.2))
>      if(n == 0L)
>          cat(gettext("No traceback available"), "\n")
>      else {
> @@ -40,7 +59,7 @@
>                  paste0(" at ", basename(srcfile$filename), "#", srcref[1L])
>              }
>              ## Truncate deparsed code (destroys attributes of xi)
> -            if(is.numeric(max.lines) && max.lines > 0L && max.lines < m) {
> +            if(valid.max.lines &&  max.lines < m) {
>                  xi <- c(xi[seq_len(max.lines)], " ...")
>                  m <- length(xi)
>              }
> Index: src/library/base/man/traceback.Rd
> ===================================================================
> --- src/library/base/man/traceback.Rd    (revision 76827)
> +++ src/library/base/man/traceback.Rd    (working copy)
> @@ -21,7 +21,7 @@
>  }
>  \usage{
>  traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
> -.traceback(x = NULL)
> +.traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
>  }
>  \arguments{
>    \item{x}{\code{NULL} (default, meaning \code{.Traceback}), or an
> Index: src/main/errors.c
> ===================================================================
> --- src/main/errors.c    (revision 76827)
> +++ src/main/errors.c    (working copy)
> @@ -1008,7 +1008,7 @@
>         (which should not happen) */
>      if (traceback && inError < 2 && inError == oldInError) {
>          inError = 2;
> -        PROTECT(s = R_GetTraceback(0));
> +        PROTECT(s = R_GetTracebackParsed(0));
>          SET_SYMVALUE(install(".Traceback"), s);
>          /* should have been defineVar
>             setVar(install(".Traceback"), s, R_GlobalEnv); */
> @@ -1440,9 +1440,11 @@
>      PrintWarnings();
>      }
>  }
> -
> +/*
> + * Return the traceback without deparsing the calls
> + */
>  attribute_hidden
> -SEXP R_GetTraceback(int skip)
> +SEXP R_GetTracebackParsed(int skip)
>  {
>      int nback = 0, ns;
>      RCNTXT *c;
> @@ -1467,7 +1469,9 @@
>          if (skip > 0)
>          skip--;
>          else {
> -        SETCAR(t, deparse1m(c->call, 0, DEFAULTDEPARSE));
> +                // Extra paranoid PROTECTS
> +        SETCAR(t, PROTECT(duplicate(c->call)));
> +                UNPROTECT(1);
>          if (c->srcref && !isNull(c->srcref)) {
>              SEXP sref;
>              if (c->srcref == R_InBCInterpreter)
> @@ -1482,7 +1486,26 @@
>      UNPROTECT(1);
>      return s;
>  }
> +/*
> + * Return the traceback with calls deparsed
> + */
> +attribute_hidden
> +SEXP R_GetTraceback(int skip)
> +{
> +    int nback = 0;
> +    SEXP s, t, u, v;
> +    s = PROTECT(R_GetTracebackParsed(skip));
> +    for(t = s; t != R_NilValue; t = CDR(t)) nback++;
> +    u = v = PROTECT(allocList(nback));
>  
> +    for(t = s; t != R_NilValue; t = CDR(t), v=CDR(v)) {
> +        SETCAR(v, PROTECT(deparse1m(CAR(t), 0, DEFAULTDEPARSE)));
> +        UNPROTECT(1);
> +    }
> +    UNPROTECT(1);
> +    return u;
> +}
> +
>  SEXP attribute_hidden do_traceback(SEXP call, SEXP op, SEXP args, SEXP rho)
>  {
>      int skip;
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

--
Luke Tierney
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:   [hidden email]
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: [External] Mitigating Stalls Caused by Call Deparse on Error

R devel mailing list
Luke, thanks for considering the issue.  I would like to
try to separate the problem into two parts, as I _think_
your comments address primarily part 2 below:

1. How can we avoid significant and possibly crippling
   stalls on error with these non-standard calls.
2. What is the best way to view these non-standard calls.

I agree that issue 2. requires further thought and
discussion under a wishlist issue ([on bugzilla now][1]). 
While I did raise issue 2., the patch itself makes no
attempt to resolve it.

The proposed patch resolves issue 1., which is a big
 usability problem.  Right now if you have the misfortune of
 using `do.call` with a big object and trigger an error, you
have the choice of waiting a possibly long time for
the deparse to complete, or killing your entire R session
 externally.

It seems a shame to allow a big usability issue for `do.call`
to remain when there is a simple solution at hand, especially
since the complete deparse of large objects likely serves no
purpose in this case. Obviously, if storing the actual calls
 instead of their deparsed equivalents in .Traceback causes
problems I'm not anticipating, then that's different. 
Is that the case?

Best,

Brodie.

[1]: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17580

On Sunday, July 14, 2019, 8:52:45 AM EDT, Tierney, Luke <[hidden email]> wrote:





This is probably best viewed in the context of other issue with
displaying calls, such as issues arising from calls constructed in
non-standard evaluation contexts. Might be good to move to a wishlist
item in bugzilla.

Best,

luke

On Sat, 13 Jul 2019, brodie gaslam via R-devel wrote:

> When large calls cause errors R may stall for extended periods.  This
> is particularly likely to happen with `do.call`, as in this example
> with a 24 second stall:
>
>     x <- runif(1e7)
>     system.time(do.call(paste0, list(abs, x)))  # intentional error
>     ## Error in (function (..., collapse = NULL)  :
>     ##   cannot coerce type 'builtin' to vector of type 'character'
>     ## Calls: system.time -> do.call -> <Anonymous>
>     ## Timing stopped at: 23.81 0.149 24.04
>
>     str(.Traceback)
>     ## Dotted pair list of 3
>     ##  $ : chr [1:2500488] "(function (..., collapse = NULL) " ".Internal(paste0(list(...), collapse)))(.Primitive(\"abs\"), c(0.718117154669017, " "0.494785501621664, 0.1453434410505, 0.635028422810137, 0.0353180423844606, " "0.688418723642826, 0.889682895969599, 0.728154224809259, 0.292572240810841, " ...
>     ##  $ : chr "do.call(paste0, list(abs, x))"
>     ##  $ : chr "system.time(do.call(paste0, list(abs, x)))"
>
> The first time I noticed this I thought my session had frozen/crashed
> as the standard interrupt ^C does not work during the deparse.  The
> stall happens when on error the call stack is deparsed prior to being
> saved to `.Traceback`.  The deparsing is done by `deparse1m` in native
> code, with the value of `getOption('deparse.max.lines')` which
> defaults to all lines.
>
> Since there is little value to seeing millions of lines of deparsed
> objects in `traceback()`, a simple work-around is to change the
> `deparse.max.lines` value:
>
>     options(deparse.max.lines=1)
>     system.time(do.call(paste0, list(abs, x)))
>     ## Error in (function (..., collapse = NULL)  :
>     ##   cannot coerce type 'builtin' to vector of type 'character'
>     ## Calls: system.time -> do.call -> <Anonymous>
>     ## Timing stopped at: 0 0 0
>
> Unfortunately this will affect all `deparse` calls, and it seems
> undesirable to pre-emptively enable it just for calls that might cause
> large deparses on error.
>
> An alternative is to store the actual calls instead of their deparsed
> character equivalents in `.Traceback`.  This defers the deparsing to
> when `traceback()` is used.  As per `?traceback`, it should be
> relatively safe to modify `.Traceback` in this way:
>
>> It is undocumented where .Traceback is stored nor that it is
>> visible, and this is subject to change.
>
> Deferring the deparsing to `traceback()` will give us the 
> opportunity to use a different `max.lines` setting as we do here
> with the patch applied:
>
>     system.time(do.call(paste0, list(abs, x)))
>     ## Error in (function (..., collapse = NULL)  :
>     ##   cannot coerce type 'builtin' to vector of type 'character'
>     ## Timing stopped at: 0.028 0 0.029
>
>     system.time(traceback(max.lines=3))
>     ## 3: (function (..., collapse = NULL)
>     ##    .Internal(paste0(list(...), collapse)))(.Primitive("abs"), c(0.535468587651849,
>     ##    0.0540027911774814, 0.732930393889546, 0.565360915614292, 0.544816034380347,
>     ##     ...
>     ## 2: do.call(paste0, list(abs, x))
>     ## 1: system.time(do.call(paste0, list(abs, x)))
>     ##    user  system elapsed
>     ##   0.000   0.000   0.003
>
>
> More generally, it might be better to have a different smaller default
> value for the lines to deparse when calls  are _displayed_ as parts of
> lists, as is the case with `traceback()`, or in `print(sys.calls())` and
> similar.
>
> I attach a patch that does this.  I have run some basic tests
> and `make check-devel` passes. I can file an issue on bugzilla
> if that is a better place to have this conversation (assuming there
> is interest in it).
>
> Best,
>
> Brodie
>
> PS: for some reason my mail client is refusing to attach the patch so I paste it
> starting on the next line.
> Index: src/gnuwin32/Rdll.hide
> ===================================================================
> --- src/gnuwin32/Rdll.hide    (revision 76827)
> +++ src/gnuwin32/Rdll.hide    (working copy)
> @@ -94,6 +94,7 @@
>   R_GetMaxNSize
>   R_GetMaxVSize
>   R_GetTraceback
> + R_GetTracebackParsed
>   R_GetVarLocSymbol
>   R_GetVarLocValue
>   R_HandlerStack
> Index: src/include/Defn.h
> ===================================================================
> --- src/include/Defn.h    (revision 76827)
> +++ src/include/Defn.h    (working copy)
> @@ -1296,6 +1296,7 @@
>  void NORET ErrorMessage(SEXP, int, ...);
>  void WarningMessage(SEXP, R_WARNING, ...);
>  SEXP R_GetTraceback(int);
> +SEXP R_GetTracebackParsed(int);
>  
>  R_size_t R_GetMaxVSize(void);
>  void R_SetMaxVSize(R_size_t);
> Index: src/library/base/R/traceback.R
> ===================================================================
> --- src/library/base/R/traceback.R    (revision 76827)
> +++ src/library/base/R/traceback.R    (working copy)
> @@ -16,9 +16,19 @@
>  #  A copy of the GNU General Public License is available at
>  #  https://www.R-project.org/Licenses/
>  
> -.traceback <- function(x = NULL) {
> -    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv())))
> -    {}
> +.traceback <- function(x = NULL, max.lines=getOption("deparse.max.lines")) {
> +    if(!(is.numeric(max.lines) && !is.na(max.lines) &&
> +         as.integer(max.lines) > 0L)
> +    ) {
> +        max.lines <- -1L
> +    }
> +    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv()))) {
> +        for(i in seq_along(x)) {
> +            srcref <- attr(x[[i]], 'srcref')
> +            x[[i]] <- deparse(x[[i]], nlines=max.lines)
> +            attr(x[[i]], 'srcref') <- srcref
> +        }
> +    }
>      else if (is.numeric(x))
>          x <- .Internal(traceback(x))
>      x
> @@ -26,7 +36,16 @@
>  
>  traceback <- function(x = NULL, max.lines = getOption("deparse.max.lines"))
>  {
> -    n <- length(x <- .traceback(x))
> +    valid.max.lines <- is.numeric(max.lines) && !is.na(max.lines) &&
> +         as.integer(max.lines) > 0L
> +
> +    if(valid.max.lines) {
> +        # max.lines + 1L so we can know that output was truncated by .traceback
> +        max.lines.2 <- as.integer(max.lines) + 1L
> +    } else {
> +        max.lines.2 <- -1L
> +    }
> +    n <- length(x <- .traceback(x, max.lines=max.lines.2))
>      if(n == 0L)
>          cat(gettext("No traceback available"), "\n")
>      else {
> @@ -40,7 +59,7 @@
>                  paste0(" at ", basename(srcfile$filename), "#", srcref[1L])
>              }
>              ## Truncate deparsed code (destroys attributes of xi)
> -            if(is.numeric(max.lines) && max.lines > 0L && max.lines < m) {
> +            if(valid.max.lines &&  max.lines < m) {
>                  xi <- c(xi[seq_len(max.lines)], " ...")
>                  m <- length(xi)
>              }
> Index: src/library/base/man/traceback.Rd
> ===================================================================
> --- src/library/base/man/traceback.Rd    (revision 76827)
> +++ src/library/base/man/traceback.Rd    (working copy)
> @@ -21,7 +21,7 @@
>  }
>  \usage{
>  traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
> -.traceback(x = NULL)
> +.traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
>  }
>  \arguments{
>    \item{x}{\code{NULL} (default, meaning \code{.Traceback}), or an
> Index: src/main/errors.c
> ===================================================================
> --- src/main/errors.c    (revision 76827)
> +++ src/main/errors.c    (working copy)
> @@ -1008,7 +1008,7 @@
>         (which should not happen) */
>      if (traceback && inError < 2 && inError == oldInError) {
>          inError = 2;
> -        PROTECT(s = R_GetTraceback(0));
> +        PROTECT(s = R_GetTracebackParsed(0));
>          SET_SYMVALUE(install(".Traceback"), s);
>          /* should have been defineVar
>             setVar(install(".Traceback"), s, R_GlobalEnv); */
> @@ -1440,9 +1440,11 @@
>      PrintWarnings();
>      }
>  }
> -
> +/*
> + * Return the traceback without deparsing the calls
> + */
>  attribute_hidden
> -SEXP R_GetTraceback(int skip)
> +SEXP R_GetTracebackParsed(int skip)
>  {
>      int nback = 0, ns;
>      RCNTXT *c;
> @@ -1467,7 +1469,9 @@
>          if (skip > 0)
>          skip--;
>          else {
> -        SETCAR(t, deparse1m(c->call, 0, DEFAULTDEPARSE));
> +                // Extra paranoid PROTECTS
> +        SETCAR(t, PROTECT(duplicate(c->call)));
> +                UNPROTECT(1);
>          if (c->srcref && !isNull(c->srcref)) {
>              SEXP sref;
>              if (c->srcref == R_InBCInterpreter)
> @@ -1482,7 +1486,26 @@
>      UNPROTECT(1);
>      return s;
>  }
> +/*
> + * Return the traceback with calls deparsed
> + */
> +attribute_hidden
> +SEXP R_GetTraceback(int skip)
> +{
> +    int nback = 0;
> +    SEXP s, t, u, v;
> +    s = PROTECT(R_GetTracebackParsed(skip));
> +    for(t = s; t != R_NilValue; t = CDR(t)) nback++;
> +    u = v = PROTECT(allocList(nback));
>  
> +    for(t = s; t != R_NilValue; t = CDR(t), v=CDR(v)) {
> +        SETCAR(v, PROTECT(deparse1m(CAR(t), 0, DEFAULTDEPARSE)));
> +        UNPROTECT(1);
> +    }
> +    UNPROTECT(1);
> +    return u;
> +}
> +
>  SEXP attribute_hidden do_traceback(SEXP call, SEXP op, SEXP args, SEXP rho)
>  {
>      int skip;

>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

--
Luke Tierney
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:            319-335-3386
Department of Statistics and        Fax:              319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:  [hidden email]
Iowa City, IA 52242                WWW:  http://www.stat.uiowa.edu

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: [External] Mitigating Stalls Caused by Call Deparse on Error

Tierney, Luke
Better to add this to the wishlist item. This all needs to be looked
at together, and nothing is likely to happen until after
vacation/conference season.  It will disappear from everyone's radar
if it is just in R_devel.

Best,

luke

On Sun, 14 Jul 2019, brodie gaslam wrote:

> Luke, thanks for considering the issue.  I would like to
> try to separate the problem into two parts, as I _think_
> your comments address primarily part 2 below:
>
> 1. How can we avoid significant and possibly crippling
>    stalls on error with these non-standard calls.
> 2. What is the best way to view these non-standard calls.
>
> I agree that issue 2. requires further thought and
> discussion under a wishlist issue ([on bugzilla now][1]). 
> While I did raise issue 2., the patch itself makes no
> attempt to resolve it.
>
> The proposed patch resolves issue 1., which is a big
> usability problem.  Right now if you have the misfortune of
> using `do.call` with a big object and trigger an error, you
> have the choice of waiting a possibly long time for
> the deparse to complete, or killing your entire R session
> externally.
>
> It seems a shame to allow a big usability issue for `do.call`
> to remain when there is a simple solution at hand, especially
> since the complete deparse of large objects likely serves no
> purpose in this case. Obviously, if storing the actual calls
> instead of their deparsed equivalents in .Traceback causes
> problems I'm not anticipating, then that's different. 
> Is that the case?
>
> Best,
>
> Brodie.
>
> [1]: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17580
>
> On Sunday, July 14, 2019, 8:52:45 AM EDT, Tierney, Luke <[hidden email]> wrote:
>
>
>
>
>
> This is probably best viewed in the context of other issue with
> displaying calls, such as issues arising from calls constructed in
> non-standard evaluation contexts. Might be good to move to a wishlist
> item in bugzilla.
>
> Best,
>
> luke
>
> On Sat, 13 Jul 2019, brodie gaslam via R-devel wrote:
>
>> When large calls cause errors R may stall for extended periods.  This
>> is particularly likely to happen with `do.call`, as in this example
>> with a 24 second stall:
>>
>>     x <- runif(1e7)
>>     system.time(do.call(paste0, list(abs, x)))  # intentional error
>>     ## Error in (function (..., collapse = NULL)  :
>>     ##   cannot coerce type 'builtin' to vector of type 'character'
>>     ## Calls: system.time -> do.call -> <Anonymous>
>>     ## Timing stopped at: 23.81 0.149 24.04
>>
>>     str(.Traceback)
>>     ## Dotted pair list of 3
>>     ##  $ : chr [1:2500488] "(function (..., collapse = NULL) " ".Internal(paste0(list(...), collapse)))(.Primitive(\"abs\"), c(0.718117154669017, " "0.494785501621664, 0.1453434410505, 0.635028422810137, 0.0353180423844606, " "0.688418723642826, 0.889682895969599, 0.728154224809259, 0.292572240810841, " ...
>>     ##  $ : chr "do.call(paste0, list(abs, x))"
>>     ##  $ : chr "system.time(do.call(paste0, list(abs, x)))"
>>
>> The first time I noticed this I thought my session had frozen/crashed
>> as the standard interrupt ^C does not work during the deparse.  The
>> stall happens when on error the call stack is deparsed prior to being
>> saved to `.Traceback`.  The deparsing is done by `deparse1m` in native
>> code, with the value of `getOption('deparse.max.lines')` which
>> defaults to all lines.
>>
>> Since there is little value to seeing millions of lines of deparsed
>> objects in `traceback()`, a simple work-around is to change the
>> `deparse.max.lines` value:
>>
>>     options(deparse.max.lines=1)
>>     system.time(do.call(paste0, list(abs, x)))
>>     ## Error in (function (..., collapse = NULL)  :
>>     ##   cannot coerce type 'builtin' to vector of type 'character'
>>     ## Calls: system.time -> do.call -> <Anonymous>
>>     ## Timing stopped at: 0 0 0
>>
>> Unfortunately this will affect all `deparse` calls, and it seems
>> undesirable to pre-emptively enable it just for calls that might cause
>> large deparses on error.
>>
>> An alternative is to store the actual calls instead of their deparsed
>> character equivalents in `.Traceback`.  This defers the deparsing to
>> when `traceback()` is used.  As per `?traceback`, it should be
>> relatively safe to modify `.Traceback` in this way:
>>
>>> It is undocumented where .Traceback is stored nor that it is
>>> visible, and this is subject to change.
>>
>> Deferring the deparsing to `traceback()` will give us the 
>> opportunity to use a different `max.lines` setting as we do here
>> with the patch applied:
>>
>>     system.time(do.call(paste0, list(abs, x)))
>>     ## Error in (function (..., collapse = NULL)  :
>>     ##   cannot coerce type 'builtin' to vector of type 'character'
>>     ## Timing stopped at: 0.028 0 0.029
>>
>>     system.time(traceback(max.lines=3))
>>     ## 3: (function (..., collapse = NULL)
>>     ##    .Internal(paste0(list(...), collapse)))(.Primitive("abs"), c(0.535468587651849,
>>     ##    0.0540027911774814, 0.732930393889546, 0.565360915614292, 0.544816034380347,
>>     ##     ...
>>     ## 2: do.call(paste0, list(abs, x))
>>     ## 1: system.time(do.call(paste0, list(abs, x)))
>>     ##    user  system elapsed
>>     ##   0.000   0.000   0.003
>>
>>
>> More generally, it might be better to have a different smaller default
>> value for the lines to deparse when calls  are _displayed_ as parts of
>> lists, as is the case with `traceback()`, or in `print(sys.calls())` and
>> similar.
>>
>> I attach a patch that does this.  I have run some basic tests
>> and `make check-devel` passes. I can file an issue on bugzilla
>> if that is a better place to have this conversation (assuming there
>> is interest in it).
>>
>> Best,
>>
>> Brodie
>>
>> PS: for some reason my mail client is refusing to attach the patch so I paste it
>> starting on the next line.
>> Index: src/gnuwin32/Rdll.hide
>> ===================================================================
>> --- src/gnuwin32/Rdll.hide    (revision 76827)
>> +++ src/gnuwin32/Rdll.hide    (working copy)
>> @@ -94,6 +94,7 @@
>>   R_GetMaxNSize
>>   R_GetMaxVSize
>>   R_GetTraceback
>> + R_GetTracebackParsed
>>   R_GetVarLocSymbol
>>   R_GetVarLocValue
>>   R_HandlerStack
>> Index: src/include/Defn.h
>> ===================================================================
>> --- src/include/Defn.h    (revision 76827)
>> +++ src/include/Defn.h    (working copy)
>> @@ -1296,6 +1296,7 @@
>>  void NORET ErrorMessage(SEXP, int, ...);
>>  void WarningMessage(SEXP, R_WARNING, ...);
>>  SEXP R_GetTraceback(int);
>> +SEXP R_GetTracebackParsed(int);
>>  
>>  R_size_t R_GetMaxVSize(void);
>>  void R_SetMaxVSize(R_size_t);
>> Index: src/library/base/R/traceback.R
>> ===================================================================
>> --- src/library/base/R/traceback.R    (revision 76827)
>> +++ src/library/base/R/traceback.R    (working copy)
>> @@ -16,9 +16,19 @@
>>  #  A copy of the GNU General Public License is available at
>>  #  https://www.R-project.org/Licenses/
>>  
>> -.traceback <- function(x = NULL) {
>> -    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv())))
>> -    {}
>> +.traceback <- function(x = NULL, max.lines=getOption("deparse.max.lines")) {
>> +    if(!(is.numeric(max.lines) && !is.na(max.lines) &&
>> +         as.integer(max.lines) > 0L)
>> +    ) {
>> +        max.lines <- -1L
>> +    }
>> +    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv()))) {
>> +        for(i in seq_along(x)) {
>> +            srcref <- attr(x[[i]], 'srcref')
>> +            x[[i]] <- deparse(x[[i]], nlines=max.lines)
>> +            attr(x[[i]], 'srcref') <- srcref
>> +        }
>> +    }
>>      else if (is.numeric(x))
>>          x <- .Internal(traceback(x))
>>      x
>> @@ -26,7 +36,16 @@
>>  
>>  traceback <- function(x = NULL, max.lines = getOption("deparse.max.lines"))
>>  {
>> -    n <- length(x <- .traceback(x))
>> +    valid.max.lines <- is.numeric(max.lines) && !is.na(max.lines) &&
>> +         as.integer(max.lines) > 0L
>> +
>> +    if(valid.max.lines) {
>> +        # max.lines + 1L so we can know that output was truncated by .traceback
>> +        max.lines.2 <- as.integer(max.lines) + 1L
>> +    } else {
>> +        max.lines.2 <- -1L
>> +    }
>> +    n <- length(x <- .traceback(x, max.lines=max.lines.2))
>>      if(n == 0L)
>>          cat(gettext("No traceback available"), "\n")
>>      else {
>> @@ -40,7 +59,7 @@
>>                  paste0(" at ", basename(srcfile$filename), "#", srcref[1L])
>>              }
>>              ## Truncate deparsed code (destroys attributes of xi)
>> -            if(is.numeric(max.lines) && max.lines > 0L && max.lines < m) {
>> +            if(valid.max.lines &&  max.lines < m) {
>>                  xi <- c(xi[seq_len(max.lines)], " ...")
>>                  m <- length(xi)
>>              }
>> Index: src/library/base/man/traceback.Rd
>> ===================================================================
>> --- src/library/base/man/traceback.Rd    (revision 76827)
>> +++ src/library/base/man/traceback.Rd    (working copy)
>> @@ -21,7 +21,7 @@
>>  }
>>  \usage{
>>  traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
>> -.traceback(x = NULL)
>> +.traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
>>  }
>>  \arguments{
>>    \item{x}{\code{NULL} (default, meaning \code{.Traceback}), or an
>> Index: src/main/errors.c
>> ===================================================================
>> --- src/main/errors.c    (revision 76827)
>> +++ src/main/errors.c    (working copy)
>> @@ -1008,7 +1008,7 @@
>>         (which should not happen) */
>>      if (traceback && inError < 2 && inError == oldInError) {
>>          inError = 2;
>> -        PROTECT(s = R_GetTraceback(0));
>> +        PROTECT(s = R_GetTracebackParsed(0));
>>          SET_SYMVALUE(install(".Traceback"), s);
>>          /* should have been defineVar
>>             setVar(install(".Traceback"), s, R_GlobalEnv); */
>> @@ -1440,9 +1440,11 @@
>>      PrintWarnings();
>>      }
>>  }
>> -
>> +/*
>> + * Return the traceback without deparsing the calls
>> + */
>>  attribute_hidden
>> -SEXP R_GetTraceback(int skip)
>> +SEXP R_GetTracebackParsed(int skip)
>>  {
>>      int nback = 0, ns;
>>      RCNTXT *c;
>> @@ -1467,7 +1469,9 @@
>>          if (skip > 0)
>>          skip--;
>>          else {
>> -        SETCAR(t, deparse1m(c->call, 0, DEFAULTDEPARSE));
>> +                // Extra paranoid PROTECTS
>> +        SETCAR(t, PROTECT(duplicate(c->call)));
>> +                UNPROTECT(1);
>>          if (c->srcref && !isNull(c->srcref)) {
>>              SEXP sref;
>>              if (c->srcref == R_InBCInterpreter)
>> @@ -1482,7 +1486,26 @@
>>      UNPROTECT(1);
>>      return s;
>>  }
>> +/*
>> + * Return the traceback with calls deparsed
>> + */
>> +attribute_hidden
>> +SEXP R_GetTraceback(int skip)
>> +{
>> +    int nback = 0;
>> +    SEXP s, t, u, v;
>> +    s = PROTECT(R_GetTracebackParsed(skip));
>> +    for(t = s; t != R_NilValue; t = CDR(t)) nback++;
>> +    u = v = PROTECT(allocList(nback));
>>  
>> +    for(t = s; t != R_NilValue; t = CDR(t), v=CDR(v)) {
>> +        SETCAR(v, PROTECT(deparse1m(CAR(t), 0, DEFAULTDEPARSE)));
>> +        UNPROTECT(1);
>> +    }
>> +    UNPROTECT(1);
>> +    return u;
>> +}
>> +
>>  SEXP attribute_hidden do_traceback(SEXP call, SEXP op, SEXP args, SEXP rho)
>>  {
>>      int skip;
>
>>
>> ______________________________________________
>> [hidden email] mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>
>
>

--
Luke Tierney
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:   [hidden email]
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: [External] Mitigating Stalls Caused by Call Deparse on Error

Lionel Henry
We also have a few other suggestions and wishes about backtrace
storage and display on the one hand, and display of constructed calls
on the other hand. Perhaps it would be better to open a different
wishlist item for traceback() to keep the discussions focused?

FWIW I think deparsing backtraces lazily is a great idea. Displaying 1
line per call by default in interactive sessions, while being able to
get a more exhaustive output by calling `traceback(max.lines = Inf)`,
offers the best of both worlds. Understanding the structure of the
backtrace quickly is often more helpful than having immediate access
to all the information.

Best,
Lionel


> On 15 Jul 2019, at 16:04, Tierney, Luke <[hidden email]> wrote:
>
> Better to add this to the wishlist item. This all needs to be looked
> at together, and nothing is likely to happen until after
> vacation/conference season.  It will disappear from everyone's radar
> if it is just in R_devel.
>
> Best,
>
> luke
>
> On Sun, 14 Jul 2019, brodie gaslam wrote:
>
>> Luke, thanks for considering the issue.  I would like to
>> try to separate the problem into two parts, as I _think_
>> your comments address primarily part 2 below:
>>
>> 1. How can we avoid significant and possibly crippling
>>    stalls on error with these non-standard calls.
>> 2. What is the best way to view these non-standard calls.
>>
>> I agree that issue 2. requires further thought and
>> discussion under a wishlist issue ([on bugzilla now][1]).
>> While I did raise issue 2., the patch itself makes no
>> attempt to resolve it.
>>
>> The proposed patch resolves issue 1., which is a big
>> usability problem.  Right now if you have the misfortune of
>> using `do.call` with a big object and trigger an error, you
>> have the choice of waiting a possibly long time for
>> the deparse to complete, or killing your entire R session
>> externally.
>>
>> It seems a shame to allow a big usability issue for `do.call`
>> to remain when there is a simple solution at hand, especially
>> since the complete deparse of large objects likely serves no
>> purpose in this case. Obviously, if storing the actual calls
>> instead of their deparsed equivalents in .Traceback causes
>> problems I'm not anticipating, then that's different.
>> Is that the case?
>>
>> Best,
>>
>> Brodie.
>>
>> [1]: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17580
>>
>> On Sunday, July 14, 2019, 8:52:45 AM EDT, Tierney, Luke <[hidden email]> wrote:
>>
>>
>>
>>
>>
>> This is probably best viewed in the context of other issue with
>> displaying calls, such as issues arising from calls constructed in
>> non-standard evaluation contexts. Might be good to move to a wishlist
>> item in bugzilla.
>>
>> Best,
>>
>> luke
>>
>> On Sat, 13 Jul 2019, brodie gaslam via R-devel wrote:
>>
>>> When large calls cause errors R may stall for extended periods.  This
>>> is particularly likely to happen with `do.call`, as in this example
>>> with a 24 second stall:
>>>
>>>     x <- runif(1e7)
>>>     system.time(do.call(paste0, list(abs, x)))  # intentional error
>>>     ## Error in (function (..., collapse = NULL)  :
>>>     ##   cannot coerce type 'builtin' to vector of type 'character'
>>>     ## Calls: system.time -> do.call -> <Anonymous>
>>>     ## Timing stopped at: 23.81 0.149 24.04
>>>
>>>     str(.Traceback)
>>>     ## Dotted pair list of 3
>>>     ##  $ : chr [1:2500488] "(function (..., collapse = NULL) " ".Internal(paste0(list(...), collapse)))(.Primitive(\"abs\"), c(0.718117154669017, " "0.494785501621664, 0.1453434410505, 0.635028422810137, 0.0353180423844606, " "0.688418723642826, 0.889682895969599, 0.728154224809259, 0.292572240810841, " ...
>>>     ##  $ : chr "do.call(paste0, list(abs, x))"
>>>     ##  $ : chr "system.time(do.call(paste0, list(abs, x)))"
>>>
>>> The first time I noticed this I thought my session had frozen/crashed
>>> as the standard interrupt ^C does not work during the deparse.  The
>>> stall happens when on error the call stack is deparsed prior to being
>>> saved to `.Traceback`.  The deparsing is done by `deparse1m` in native
>>> code, with the value of `getOption('deparse.max.lines')` which
>>> defaults to all lines.
>>>
>>> Since there is little value to seeing millions of lines of deparsed
>>> objects in `traceback()`, a simple work-around is to change the
>>> `deparse.max.lines` value:
>>>
>>>     options(deparse.max.lines=1)
>>>     system.time(do.call(paste0, list(abs, x)))
>>>     ## Error in (function (..., collapse = NULL)  :
>>>     ##   cannot coerce type 'builtin' to vector of type 'character'
>>>     ## Calls: system.time -> do.call -> <Anonymous>
>>>     ## Timing stopped at: 0 0 0
>>>
>>> Unfortunately this will affect all `deparse` calls, and it seems
>>> undesirable to pre-emptively enable it just for calls that might cause
>>> large deparses on error.
>>>
>>> An alternative is to store the actual calls instead of their deparsed
>>> character equivalents in `.Traceback`.  This defers the deparsing to
>>> when `traceback()` is used.  As per `?traceback`, it should be
>>> relatively safe to modify `.Traceback` in this way:
>>>
>>>> It is undocumented where .Traceback is stored nor that it is
>>>> visible, and this is subject to change.
>>>
>>> Deferring the deparsing to `traceback()` will give us the
>>> opportunity to use a different `max.lines` setting as we do here
>>> with the patch applied:
>>>
>>>     system.time(do.call(paste0, list(abs, x)))
>>>     ## Error in (function (..., collapse = NULL)  :
>>>     ##   cannot coerce type 'builtin' to vector of type 'character'
>>>     ## Timing stopped at: 0.028 0 0.029
>>>
>>>     system.time(traceback(max.lines=3))
>>>     ## 3: (function (..., collapse = NULL)
>>>     ##    .Internal(paste0(list(...), collapse)))(.Primitive("abs"), c(0.535468587651849,
>>>     ##    0.0540027911774814, 0.732930393889546, 0.565360915614292, 0.544816034380347,
>>>     ##     ...
>>>     ## 2: do.call(paste0, list(abs, x))
>>>     ## 1: system.time(do.call(paste0, list(abs, x)))
>>>     ##    user  system elapsed
>>>     ##   0.000   0.000   0.003
>>>
>>>
>>> More generally, it might be better to have a different smaller default
>>> value for the lines to deparse when calls  are _displayed_ as parts of
>>> lists, as is the case with `traceback()`, or in `print(sys.calls())` and
>>> similar.
>>>
>>> I attach a patch that does this.  I have run some basic tests
>>> and `make check-devel` passes. I can file an issue on bugzilla
>>> if that is a better place to have this conversation (assuming there
>>> is interest in it).
>>>
>>> Best,
>>>
>>> Brodie
>>>
>>> PS: for some reason my mail client is refusing to attach the patch so I paste it
>>> starting on the next line.
>>> Index: src/gnuwin32/Rdll.hide
>>> ===================================================================
>>> --- src/gnuwin32/Rdll.hide    (revision 76827)
>>> +++ src/gnuwin32/Rdll.hide    (working copy)
>>> @@ -94,6 +94,7 @@
>>>   R_GetMaxNSize
>>>   R_GetMaxVSize
>>>   R_GetTraceback
>>> + R_GetTracebackParsed
>>>   R_GetVarLocSymbol
>>>   R_GetVarLocValue
>>>   R_HandlerStack
>>> Index: src/include/Defn.h
>>> ===================================================================
>>> --- src/include/Defn.h    (revision 76827)
>>> +++ src/include/Defn.h    (working copy)
>>> @@ -1296,6 +1296,7 @@
>>>  void NORET ErrorMessage(SEXP, int, ...);
>>>  void WarningMessage(SEXP, R_WARNING, ...);
>>>  SEXP R_GetTraceback(int);
>>> +SEXP R_GetTracebackParsed(int);
>>>  
>>>  R_size_t R_GetMaxVSize(void);
>>>  void R_SetMaxVSize(R_size_t);
>>> Index: src/library/base/R/traceback.R
>>> ===================================================================
>>> --- src/library/base/R/traceback.R    (revision 76827)
>>> +++ src/library/base/R/traceback.R    (working copy)
>>> @@ -16,9 +16,19 @@
>>>  #  A copy of the GNU General Public License is available at
>>>  #  https://www.R-project.org/Licenses/
>>>  
>>> -.traceback <- function(x = NULL) {
>>> -    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv())))
>>> -    {}
>>> +.traceback <- function(x = NULL, max.lines=getOption("deparse.max.lines")) {
>>> +    if(!(is.numeric(max.lines) && !is.na(max.lines) &&
>>> +         as.integer(max.lines) > 0L)
>>> +    ) {
>>> +        max.lines <- -1L
>>> +    }
>>> +    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv()))) {
>>> +        for(i in seq_along(x)) {
>>> +            srcref <- attr(x[[i]], 'srcref')
>>> +            x[[i]] <- deparse(x[[i]], nlines=max.lines)
>>> +            attr(x[[i]], 'srcref') <- srcref
>>> +        }
>>> +    }
>>>      else if (is.numeric(x))
>>>          x <- .Internal(traceback(x))
>>>      x
>>> @@ -26,7 +36,16 @@
>>>  
>>>  traceback <- function(x = NULL, max.lines = getOption("deparse.max.lines"))
>>>  {
>>> -    n <- length(x <- .traceback(x))
>>> +    valid.max.lines <- is.numeric(max.lines) && !is.na(max.lines) &&
>>> +         as.integer(max.lines) > 0L
>>> +
>>> +    if(valid.max.lines) {
>>> +        # max.lines + 1L so we can know that output was truncated by .traceback
>>> +        max.lines.2 <- as.integer(max.lines) + 1L
>>> +    } else {
>>> +        max.lines.2 <- -1L
>>> +    }
>>> +    n <- length(x <- .traceback(x, max.lines=max.lines.2))
>>>      if(n == 0L)
>>>          cat(gettext("No traceback available"), "\n")
>>>      else {
>>> @@ -40,7 +59,7 @@
>>>                  paste0(" at ", basename(srcfile$filename), "#", srcref[1L])
>>>              }
>>>              ## Truncate deparsed code (destroys attributes of xi)
>>> -            if(is.numeric(max.lines) && max.lines > 0L && max.lines < m) {
>>> +            if(valid.max.lines &&  max.lines < m) {
>>>                  xi <- c(xi[seq_len(max.lines)], " ...")
>>>                  m <- length(xi)
>>>              }
>>> Index: src/library/base/man/traceback.Rd
>>> ===================================================================
>>> --- src/library/base/man/traceback.Rd    (revision 76827)
>>> +++ src/library/base/man/traceback.Rd    (working copy)
>>> @@ -21,7 +21,7 @@
>>>  }
>>>  \usage{
>>>  traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
>>> -.traceback(x = NULL)
>>> +.traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
>>>  }
>>>  \arguments{
>>>    \item{x}{\code{NULL} (default, meaning \code{.Traceback}), or an
>>> Index: src/main/errors.c
>>> ===================================================================
>>> --- src/main/errors.c    (revision 76827)
>>> +++ src/main/errors.c    (working copy)
>>> @@ -1008,7 +1008,7 @@
>>>         (which should not happen) */
>>>      if (traceback && inError < 2 && inError == oldInError) {
>>>          inError = 2;
>>> -        PROTECT(s = R_GetTraceback(0));
>>> +        PROTECT(s = R_GetTracebackParsed(0));
>>>          SET_SYMVALUE(install(".Traceback"), s);
>>>          /* should have been defineVar
>>>             setVar(install(".Traceback"), s, R_GlobalEnv); */
>>> @@ -1440,9 +1440,11 @@
>>>      PrintWarnings();
>>>      }
>>>  }
>>> -
>>> +/*
>>> + * Return the traceback without deparsing the calls
>>> + */
>>>  attribute_hidden
>>> -SEXP R_GetTraceback(int skip)
>>> +SEXP R_GetTracebackParsed(int skip)
>>>  {
>>>      int nback = 0, ns;
>>>      RCNTXT *c;
>>> @@ -1467,7 +1469,9 @@
>>>          if (skip > 0)
>>>          skip--;
>>>          else {
>>> -        SETCAR(t, deparse1m(c->call, 0, DEFAULTDEPARSE));
>>> +                // Extra paranoid PROTECTS
>>> +        SETCAR(t, PROTECT(duplicate(c->call)));
>>> +                UNPROTECT(1);
>>>          if (c->srcref && !isNull(c->srcref)) {
>>>              SEXP sref;
>>>              if (c->srcref == R_InBCInterpreter)
>>> @@ -1482,7 +1486,26 @@
>>>      UNPROTECT(1);
>>>      return s;
>>>  }
>>> +/*
>>> + * Return the traceback with calls deparsed
>>> + */
>>> +attribute_hidden
>>> +SEXP R_GetTraceback(int skip)
>>> +{
>>> +    int nback = 0;
>>> +    SEXP s, t, u, v;
>>> +    s = PROTECT(R_GetTracebackParsed(skip));
>>> +    for(t = s; t != R_NilValue; t = CDR(t)) nback++;
>>> +    u = v = PROTECT(allocList(nback));
>>>  
>>> +    for(t = s; t != R_NilValue; t = CDR(t), v=CDR(v)) {
>>> +        SETCAR(v, PROTECT(deparse1m(CAR(t), 0, DEFAULTDEPARSE)));
>>> +        UNPROTECT(1);
>>> +    }
>>> +    UNPROTECT(1);
>>> +    return u;
>>> +}
>>> +
>>>  SEXP attribute_hidden do_traceback(SEXP call, SEXP op, SEXP args, SEXP rho)
>>>  {
>>>      int skip;
>>
>>>
>>> ______________________________________________
>>> [hidden email] mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>
>>
>>
>
> --
> Luke Tierney
> Ralph E. Wareham Professor of Mathematical Sciences
> University of Iowa                  Phone:             319-335-3386
> Department of Statistics and        Fax:               319-335-3017
>    Actuarial Science
> 241 Schaeffer Hall                  email:   [hidden email]
> Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: [External] Mitigating Stalls Caused by Call Deparse on Error

R devel mailing list
As per Luke's instructions I've updated the [wishlist item][1]
 to include the deparse-on-error issue, and also renamed it to
something more appropriate for its broader scope.

I do share Lionel's concern that the deparse-on-error issue
could get unnecessarily delayed in the hopes of finding a more
comprehensive issue for all the problems. I would really appreciate
 it if and when you all have a chance to look at this issue
you could consider the possibility of resolving the deparse-on-error
 one separately if a global solution seems out of reach.

Thank you for all your work making R better and more
performant.

Best,

Brodie.


[1]: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17580


On Tuesday, July 16, 2019, 4:53:37 AM EDT, Lionel Henry <[hidden email]> wrote:





We also have a few other suggestions and wishes about backtrace
storage and display on the one hand, and display of constructed calls
on the other hand. Perhaps it would be better to open a different
wishlist item for traceback() to keep the discussions focused?

FWIW I think deparsing backtraces lazily is a great idea. Displaying 1
line per call by default in interactive sessions, while being able to
get a more exhaustive output by calling `traceback(max.lines = Inf)`,
offers the best of both worlds. Understanding the structure of the
backtrace quickly is often more helpful than having immediate access
to all the information.

Best,
Lionel


> On 15 Jul 2019, at 16:04, Tierney, Luke <[hidden email]> wrote:
>
> Better to add this to the wishlist item. This all needs to be looked
> at together, and nothing is likely to happen until after
> vacation/conference season.  It will disappear from everyone's radar
> if it is just in R_devel.
>
> Best,
>
> luke
>
> On Sun, 14 Jul 2019, brodie gaslam wrote:
>
>> Luke, thanks for considering the issue.  I would like to
>> try to separate the problem into two parts, as I _think_
>> your comments address primarily part 2 below:
>>
>> 1. How can we avoid significant and possibly crippling
>>    stalls on error with these non-standard calls.
>> 2. What is the best way to view these non-standard calls.
>>
>> I agree that issue 2. requires further thought and
>> discussion under a wishlist issue ([on bugzilla now][1]).
>> While I did raise issue 2., the patch itself makes no
>> attempt to resolve it.
>>
>> The proposed patch resolves issue 1., which is a big
>> usability problem.  Right now if you have the misfortune of
>> using `do.call` with a big object and trigger an error, you
>> have the choice of waiting a possibly long time for
>> the deparse to complete, or killing your entire R session
>> externally.
>>
>> It seems a shame to allow a big usability issue for `do.call`
>> to remain when there is a simple solution at hand, especially
>> since the complete deparse of large objects likely serves no
>> purpose in this case. Obviously, if storing the actual calls
>> instead of their deparsed equivalents in .Traceback causes
>> problems I'm not anticipating, then that's different.
>> Is that the case?
>>
>> Best,
>>
>> Brodie.
>>
>> [1]: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17580
>>
>> On Sunday, July 14, 2019, 8:52:45 AM EDT, Tierney, Luke <[hidden email]> wrote:
>>
>>
>>
>>
>>
>> This is probably best viewed in the context of other issue with
>> displaying calls, such as issues arising from calls constructed in
>> non-standard evaluation contexts. Might be good to move to a wishlist
>> item in bugzilla.
>>
>> Best,
>>
>> luke
>>
>> On Sat, 13 Jul 2019, brodie gaslam via R-devel wrote:
>>
>>> When large calls cause errors R may stall for extended periods.  This
>>> is particularly likely to happen with `do.call`, as in this example
>>> with a 24 second stall:
>>>
>>>    x <- runif(1e7)
>>>    system.time(do.call(paste0, list(abs, x)))  # intentional error
>>>    ## Error in (function (..., collapse = NULL)  :
>>>    ##  cannot coerce type 'builtin' to vector of type 'character'
>>>    ## Calls: system.time -> do.call -> <Anonymous>
>>>    ## Timing stopped at: 23.81 0.149 24.04
>>>
>>>    str(.Traceback)
>>>    ## Dotted pair list of 3
>>>    ##  $ : chr [1:2500488] "(function (..., collapse = NULL) " ".Internal(paste0(list(...), collapse)))(.Primitive(\"abs\"), c(0.718117154669017, " "0.494785501621664, 0.1453434410505, 0.635028422810137, 0.0353180423844606, " "0.688418723642826, 0.889682895969599, 0.728154224809259, 0.292572240810841, " ...
>>>    ##  $ : chr "do.call(paste0, list(abs, x))"
>>>    ##  $ : chr "system.time(do.call(paste0, list(abs, x)))"
>>>
>>> The first time I noticed this I thought my session had frozen/crashed
>>> as the standard interrupt ^C does not work during the deparse.  The
>>> stall happens when on error the call stack is deparsed prior to being
>>> saved to `.Traceback`.  The deparsing is done by `deparse1m` in native
>>> code, with the value of `getOption('deparse.max.lines')` which
>>> defaults to all lines.
>>>
>>> Since there is little value to seeing millions of lines of deparsed
>>> objects in `traceback()`, a simple work-around is to change the
>>> `deparse.max.lines` value:
>>>
>>>    options(deparse.max.lines=1)
>>>    system.time(do.call(paste0, list(abs, x)))
>>>    ## Error in (function (..., collapse = NULL)  :
>>>    ##  cannot coerce type 'builtin' to vector of type 'character'
>>>    ## Calls: system.time -> do.call -> <Anonymous>
>>>    ## Timing stopped at: 0 0 0
>>>
>>> Unfortunately this will affect all `deparse` calls, and it seems
>>> undesirable to pre-emptively enable it just for calls that might cause
>>> large deparses on error.
>>>
>>> An alternative is to store the actual calls instead of their deparsed
>>> character equivalents in `.Traceback`.  This defers the deparsing to
>>> when `traceback()` is used.  As per `?traceback`, it should be
>>> relatively safe to modify `.Traceback` in this way:
>>>
>>>> It is undocumented where .Traceback is stored nor that it is
>>>> visible, and this is subject to change.
>>>
>>> Deferring the deparsing to `traceback()` will give us the
>>> opportunity to use a different `max.lines` setting as we do here
>>> with the patch applied:
>>>
>>>    system.time(do.call(paste0, list(abs, x)))
>>>    ## Error in (function (..., collapse = NULL)  :
>>>    ##  cannot coerce type 'builtin' to vector of type 'character'
>>>    ## Timing stopped at: 0.028 0 0.029
>>>
>>>    system.time(traceback(max.lines=3))
>>>    ## 3: (function (..., collapse = NULL)
>>>    ##    .Internal(paste0(list(...), collapse)))(.Primitive("abs"), c(0.535468587651849,
>>>    ##    0.0540027911774814, 0.732930393889546, 0.565360915614292, 0.544816034380347,
>>>    ##    ...
>>>    ## 2: do.call(paste0, list(abs, x))
>>>    ## 1: system.time(do.call(paste0, list(abs, x)))
>>>    ##    user  system elapsed
>>>    ##  0.000  0.000  0.003
>>>
>>>
>>> More generally, it might be better to have a different smaller default
>>> value for the lines to deparse when calls  are _displayed_ as parts of
>>> lists, as is the case with `traceback()`, or in `print(sys.calls())` and
>>> similar.
>>>
>>> I attach a patch that does this.  I have run some basic tests
>>> and `make check-devel` passes. I can file an issue on bugzilla
>>> if that is a better place to have this conversation (assuming there
>>> is interest in it).
>>>
>>> Best,
>>>
>>> Brodie
>>>
>>> PS: for some reason my mail client is refusing to attach the patch so I paste it
>>> starting on the next line.
>>> Index: src/gnuwin32/Rdll.hide
>>> ===================================================================
>>> --- src/gnuwin32/Rdll.hide    (revision 76827)
>>> +++ src/gnuwin32/Rdll.hide    (working copy)
>>> @@ -94,6 +94,7 @@
>>>  R_GetMaxNSize
>>>  R_GetMaxVSize
>>>  R_GetTraceback
>>> + R_GetTracebackParsed
>>>  R_GetVarLocSymbol
>>>  R_GetVarLocValue
>>>  R_HandlerStack
>>> Index: src/include/Defn.h
>>> ===================================================================
>>> --- src/include/Defn.h    (revision 76827)
>>> +++ src/include/Defn.h    (working copy)
>>> @@ -1296,6 +1296,7 @@
>>>  void NORET ErrorMessage(SEXP, int, ...);
>>>  void WarningMessage(SEXP, R_WARNING, ...);
>>>  SEXP R_GetTraceback(int);
>>> +SEXP R_GetTracebackParsed(int);
>>> 
>>>  R_size_t R_GetMaxVSize(void);
>>>  void R_SetMaxVSize(R_size_t);
>>> Index: src/library/base/R/traceback.R
>>> ===================================================================
>>> --- src/library/base/R/traceback.R    (revision 76827)
>>> +++ src/library/base/R/traceback.R    (working copy)
>>> @@ -16,9 +16,19 @@
>>>  #  A copy of the GNU General Public License is available at
>>>  #  https://www.R-project.org/Licenses/
>>> 
>>> -.traceback <- function(x = NULL) {
>>> -    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv())))
>>> -    {}
>>> +.traceback <- function(x = NULL, max.lines=getOption("deparse.max.lines")) {
>>> +    if(!(is.numeric(max.lines) && !is.na(max.lines) &&
>>> +        as.integer(max.lines) > 0L)
>>> +    ) {
>>> +        max.lines <- -1L
>>> +    }
>>> +    if(is.null(x) && !is.null(x <- get0(".Traceback", envir = baseenv()))) {
>>> +        for(i in seq_along(x)) {
>>> +            srcref <- attr(x[[i]], 'srcref')
>>> +            x[[i]] <- deparse(x[[i]], nlines=max.lines)
>>> +            attr(x[[i]], 'srcref') <- srcref
>>> +        }
>>> +    }
>>>      else if (is.numeric(x))
>>>          x <- .Internal(traceback(x))
>>>      x
>>> @@ -26,7 +36,16 @@
>>> 
>>>  traceback <- function(x = NULL, max.lines = getOption("deparse.max.lines"))
>>>  {
>>> -    n <- length(x <- .traceback(x))
>>> +    valid.max.lines <- is.numeric(max.lines) && !is.na(max.lines) &&
>>> +        as.integer(max.lines) > 0L
>>> +
>>> +    if(valid.max.lines) {
>>> +        # max.lines + 1L so we can know that output was truncated by .traceback
>>> +        max.lines.2 <- as.integer(max.lines) + 1L
>>> +    } else {
>>> +        max.lines.2 <- -1L
>>> +    }
>>> +    n <- length(x <- .traceback(x, max.lines=max.lines.2))
>>>      if(n == 0L)
>>>          cat(gettext("No traceback available"), "\n")
>>>      else {
>>> @@ -40,7 +59,7 @@
>>>                  paste0(" at ", basename(srcfile$filename), "#", srcref[1L])
>>>              }
>>>              ## Truncate deparsed code (destroys attributes of xi)
>>> -            if(is.numeric(max.lines) && max.lines > 0L && max.lines < m) {
>>> +            if(valid.max.lines &&  max.lines < m) {
>>>                  xi <- c(xi[seq_len(max.lines)], " ...")
>>>                  m <- length(xi)
>>>              }
>>> Index: src/library/base/man/traceback.Rd
>>> ===================================================================
>>> --- src/library/base/man/traceback.Rd    (revision 76827)
>>> +++ src/library/base/man/traceback.Rd    (working copy)
>>> @@ -21,7 +21,7 @@
>>>  }
>>>  \usage{
>>>  traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
>>> -.traceback(x = NULL)
>>> +.traceback(x = NULL, max.lines = getOption("deparse.max.lines"))
>>>  }
>>>  \arguments{
>>>    \item{x}{\code{NULL} (default, meaning \code{.Traceback}), or an
>>> Index: src/main/errors.c
>>> ===================================================================
>>> --- src/main/errors.c    (revision 76827)
>>> +++ src/main/errors.c    (working copy)
>>> @@ -1008,7 +1008,7 @@
>>>        (which should not happen) */
>>>      if (traceback && inError < 2 && inError == oldInError) {
>>>          inError = 2;
>>> -        PROTECT(s = R_GetTraceback(0));
>>> +        PROTECT(s = R_GetTracebackParsed(0));
>>>          SET_SYMVALUE(install(".Traceback"), s);
>>>          /* should have been defineVar
>>>            setVar(install(".Traceback"), s, R_GlobalEnv); */
>>> @@ -1440,9 +1440,11 @@
>>>      PrintWarnings();
>>>      }
>>>  }
>>> -
>>> +/*
>>> + * Return the traceback without deparsing the calls
>>> + */
>>>  attribute_hidden
>>> -SEXP R_GetTraceback(int skip)
>>> +SEXP R_GetTracebackParsed(int skip)
>>>  {
>>>      int nback = 0, ns;
>>>      RCNTXT *c;
>>> @@ -1467,7 +1469,9 @@
>>>          if (skip > 0)
>>>          skip--;
>>>          else {
>>> -        SETCAR(t, deparse1m(c->call, 0, DEFAULTDEPARSE));
>>> +                // Extra paranoid PROTECTS
>>> +        SETCAR(t, PROTECT(duplicate(c->call)));
>>> +                UNPROTECT(1);
>>>          if (c->srcref && !isNull(c->srcref)) {
>>>              SEXP sref;
>>>              if (c->srcref == R_InBCInterpreter)
>>> @@ -1482,7 +1486,26 @@
>>>      UNPROTECT(1);
>>>      return s;
>>>  }
>>> +/*
>>> + * Return the traceback with calls deparsed
>>> + */
>>> +attribute_hidden
>>> +SEXP R_GetTraceback(int skip)
>>> +{
>>> +    int nback = 0;
>>> +    SEXP s, t, u, v;
>>> +    s = PROTECT(R_GetTracebackParsed(skip));
>>> +    for(t = s; t != R_NilValue; t = CDR(t)) nback++;
>>> +    u = v = PROTECT(allocList(nback));
>>> 
>>> +    for(t = s; t != R_NilValue; t = CDR(t), v=CDR(v)) {
>>> +        SETCAR(v, PROTECT(deparse1m(CAR(t), 0, DEFAULTDEPARSE)));
>>> +        UNPROTECT(1);
>>> +    }
>>> +    UNPROTECT(1);
>>> +    return u;
>>> +}
>>> +
>>>  SEXP attribute_hidden do_traceback(SEXP call, SEXP op, SEXP args, SEXP rho)
>>>  {
>>>      int skip;
>>
>>>
>>> ______________________________________________
>>> [hidden email] mailing list
>>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>>
>>
>>
>
> --
> Luke Tierney
> Ralph E. Wareham Professor of Mathematical Sciences
> University of Iowa                  Phone:            319-335-3386
> Department of Statistics and        Fax:              319-335-3017
>    Actuarial Science
> 241 Schaeffer Hall                  email:  [hidden email]
> Iowa City, IA 52242                WWW:  http://www.stat.uiowa.edu

> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel