#' Generate profiler lines for an expression
#'
#' @param expr Expression to profile. The expression is repeatedly
#' evaluated until `Rprof()` produces an output. Can _be_ a quosure
#' injected with [rlang::inject()] but cannot _contain_ injected
#' quosures.
#' @param ... Arguments passed to `Rprof()`.
#' @param trim_stack Whether to trim the current call stack from the
#' profiles.
#' @param rerun Regexp or `NULL`. If supplied, resamples a new
#' profile until the regexp matches the modal profile
#' stack. Metadata is removed from the profiles before matching and
#' taking the modal value.
#' @noRd
rprof_lines <- function(expr,
...,
interval = 0.010,
filter.callframes = FALSE,
trim_stack = TRUE,
rerun = FALSE) {
c(expr, env) %<-% enquo0_list(expr)
lines <- character()
prof_file <- tempfile("profvis-snapshot", fileext = ".prof")
on.exit(unlink(prof_file), add = TRUE)
if (has_simplify()) {
args <- list(filter.callframes = filter.callframes)
} else {
args <- NULL
}
on.exit(Rprof(NULL), add = TRUE)
while (TRUE) {
env_bind_lazy(current_env(), do = !!expr, .eval_env = env)
gc()
inject(Rprof(
prof_file,
...,
interval = interval,
!!!args
))
do
Rprof(NULL)
lines <- zap_header(readLines(prof_file, warn = FALSE))
if (trim_stack) {
suffix <- rprof_current_suffix(env, filter.callframes, ...)
lines <- gsub(suffix, "", lines)
}
if (prof_matches(lines, rerun)) {
break
}
}
lines
}
re_srcref <- "\\d+#\\d+"
re_srcref_opt <- sprintf(" (%s )?", re_srcref)
rprof_current_suffix <- function(env, simplify, ...) {
if (simplify && getRversion() >= "4.0.3") {
# We need to call the suffix routine from the caller frame. We
# inline a closure in the call so we can refer to here despite
# evaluating in a foreign environment. Evaluation is done through
# a promise to keep the call stack simple.
call <- call2(function() rprof_current_suffix_linear(...))
env_bind_lazy(current_env(), do = !!call, .eval_env = env)
do
} else {
rprof_current_suffix_full(...)
}
}
rprof_current_suffix_full <- function(...) {
lines <- rprof_lines(
pause(RPROF_SUFFIX_PAUSE_TIME),
trim_stack = FALSE,
...,
rerun = "rprof_current_suffix_full"
)
line <- modal_value(zap_meta_data(lines))
pattern <- sprintf(" \"rprof_current_suffix\"( %s)?", re_srcref)
pos <- gregexpr(pattern, line)[[1]]
if (length(pos) != 1 || pos < 0) {
stop("Unexpected state in `rprof_current_suffix()`.")
}
suffix <- substring(line, pos + attr(pos, "match.length"))
suffix <- gsub_srcref_as_wildcards(suffix)
paste0(suffix, "$")
}
rprof_current_suffix_linear <- function(..., filter.callframes = NULL) {
lines <- rprof_lines(
pause(RPROF_SUFFIX_PAUSE_TIME),
trim_stack = FALSE,
...,
filter.callframes = TRUE,
rerun = "rprof_current_suffix_linear"
)
line <- modal_value(zap_meta_data(lines))
pattern <- sprintf(
"^\"pause\"%s\"rprof_current_suffix_linear\"%s\"<Anonymous>\"%s",
re_srcref_opt,
re_srcref_opt,
re_srcref_opt
)
suffix <- sub(pattern, "", line)
suffix <- gsub_srcref_as_wildcards(suffix)
paste0(suffix, "$")
}
RPROF_SUFFIX_PAUSE_TIME <- 0.100
# File labels of the suffix will differ with those of the actual
# profiles
gsub_srcref_as_wildcards <- function(lines) {
# Strip all existing srcrefs
lines <- gsub("\\d+#\\d+ ", "", lines)
# Add wildcards for srcrefs
lines <- gsub("\" \"", sprintf("\"%s\"", re_srcref_opt), lines, fixed = TRUE)
lines
}
utils::globalVariables("do")
has_simplify <- function() {
getRversion() >= "4.0.3"
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.