posit-dev / posit-dev/positron

Ark: Crash with profvis

Open
#4,674 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: kernels lang: r
Dominant language
TypeScript
Stars
4.3k
Forks
184
Avg merge
1d 11h
Merged PRs (30d)
206

Description

@hadley reports a sporadic (but easy to reproduce) crash with the dev version of profvis in Positron:

devtools::load_all() # in profvis
profvis(f(), simplify = TRUE, rerun = "pause", interval = 0.005)

The relevant part of the backtrace reported in logs looks like:

[R] >>> Backtrace for signal 11
[R] >>> In thread main
[R]    0: std::backtrace::Backtrace::create
[R]    1: ark::traps::backtrace_handler
[R]    2: __simple_esappend
[R]    3: findProfContext
[R]              at /Volumes/Builds/R4/R-4.4.0/src/main/eval.c:375:23
[R]    4: doprof
[R]              at /Volumes/Builds/R4/R-4.4.0/src/main/eval.c:496:10
[R]    5: __simple_esappend
[R]    6: R_ToplevelExec
[R]              at /Volumes/Builds/R4/R-4.4.0/src/main/context.c:807:5
[R]    7: harp::exec::r_check_stack
[R]    8: _r_polled_events
[R]    9: R_ProcessEvents
[R]              at /Volumes/Builds/R4/R-4.4.0/src/unix/sys-unix.c:1178:5
[R]   10: R_CheckUserInterrupt
[R]              at /Volumes/Builds/R4/R-4.4.0/src/main/errors.c:153:5
[R]   11: profvis_pause
[R]              at ./src/pause.c:34:5

The crash happens in findProfContext which we added to R some time ago to power simplified backtraces in profiles.

The crashing sequence is:

  • profvis calls pause() during the backtrace calibration profile (i.e. the main profile has finished)
  • pause() checks for user interrupts.
  • Ark checks for sufficient stack space within a R_ToplevelExec() context at interrupt time
  • The posix signal handler for profiling kicks in right then.
  • The findProfContext() routine called by the signal handler crashes.

Here is where the crash happens:

Screenshot 2024-09-13 at 09 27 13

cptr is unexpectedly NULL. Worth noting that in a top-level-exec context:

	R_GlobalContext = R_ToplevelContext = &thiscontext;

findProfContext() is called from doprof() within this loop:

https://github.com/r-devel/r-svn/blob/08656ceb6a8c0b6fd31f436a16cea03fb614327a/src/main/eval.c#L494-L496

    for (RCNTXT *cptr = R_GlobalContext;
	 cptr != NULL;
	 cptr = findProfContext(cptr)) {

And the loop in findProfContext() looks for the top-level context:

https://github.com/r-devel/r-svn/blob/08656ceb6a8c0b6fd31f436a16cea03fb614327a/src/main/eval.c#L392-L393

    if (cptr == R_ToplevelContext)
	return NULL;
    ...
    while (cptr->nextcontext != R_ToplevelContext)
	cptr = cptr->nextcontext;

AFAICT, cptr can only become NULL here if the top-level context is not in the linked list. And indeed in the debug session, I see that R_GlobalContext (our starting point) is the parent of the top-level exec context, which is surprising and it also explains why findProfContext() is not able to find the top-level context. We started too high in the chain.

Now why aren't the global and top-level contexts equal, as would be expected at this time? One explanation would be that something is jumping out of our top-level context (that we created to check for the current stack size). A jump sets the global context to an earlier frame: https://github.com/r-devel/r-svn/blob/08656ceb6a8c0b6fd31f436a16cea03fb614327a/src/main/context.c#L231. But it doesn't change the top-level context which is only reset when we leave it. So during that timeframe it's possible for the global context to be a younger frame than the top-level context.

What could cause a jump out of our top-level context?

  • Is the error thrown in the profile expression relevant? I don't think so because when R crashes we are already out of the main profiling loop, and running the "backtrace calibration" profile (that's the only place pause() is called). I think the error just helps to speed up the request and get out of the main profiling loop.

  • Is there somehow a stack overflow error? It doesn't seem like it as a breakpoint on R_SignalCStackOverflow is not triggered.

So I'm not sure yet what is the cause of the crash:

  • There is a bug in R (perhaps we should just check for NULL context in that findProfContext() loop)
  • Accessing the R API at interrupt time is unsafe
  • Accessing the R API from a posix signal handler is unsafe
  • A combination of these

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the crash with the profvis commands in the issue, then inspect src/pause.c and the R eval.c paths for findProfContext() and doprof(). Trace the context state during R_ToplevelExec() and the profiling signal; done means identifying the cause of the invalid context and defining a verified resolution.

Written by the indexing model from the issue text.

Assessment

Tech stack
r
Domain
devtools
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.