[Lldb-commits] [lldb] [lldb] Implement basic support for reverse-continue (PR #112079)

via lldb-commits lldb-commits at lists.llvm.org
Tue Nov 12 15:23:33 PST 2024


jimingham wrote:

> > I worry that in a situation like this where there's a pending task people will go the wrong way and lose important state because they forgot that "continue" doesn't mean "continue what I was doing" - which is what it currently means in lldb
> 
> I don't know of any other debugger that supports the equivalent of a thread plan stack, and even though I've been neck-deep in debuggers for a long time I didn't know about this LLDB feature until I started working on this PR, so TBH I think most users will assume "continue" works like any other debugger and just resumes execution forwards, and are surprised (hopefully in a good way) if/when they discover it's more complicated than that. And therefore will be surprised in a bad way if/when they discover that "continue" runs backwards in some situations.

I added a description of how this works a while back to the Tutorial.  Once you get used to this behavior, it is SO handy.  All those times you were doing a "next" and hit a breakpoint somewhere deep under the next, and had to drive your way back out with "finish-es" plus hitting other breakpoints along the way gets replaced by `continue` till the next says it's done.  Once you get used to it working this way, it's hard to go back - at least in my experience...

But for this to be useful it has to be reliable.  If it doesn't work on you a couple of times, you're likely to go back to driving by hand as safer.

> 
> > I think ultimately I'm arguing against requiring: "we either have all forward or all backwards plans on any given thread plan stack" because I don't think that is flexible enough to support all the use cases we want to support.
> 
> I agree and as I said above, I've already removed that requirement in my experimental branch. I haven't added a notion of "no-direction-opinion" plans yet, but most of what you said above makes sense even if we don't have that.
> 

Cool!

> Let's consider a really simple case where the user does a reverse-continue, hits an unconditional breakpoint, and then wants to execute forwards. One question is, what command should they use to execute forwards? I've been assuming that they would just use `process continue`, but if you really want `process continue` to resume the reverse-continue and we should have a separate command or flag to switch to forwards, we can do that. We should probably settle this now because it's quite fundamental.

I expected that we would add a `--direction` to all the execution control commands that can go in either direction.  If you don't supply a `--direction`, it would be set to whatever the last execution control direction was.  That way if you were driving back into the past with a bunch of continues, you would have to say:

(lldb) continue --direction reverse
...<Hit Breakpoint>
(lldb) c
(lldb) c
...

But it would also mean that for folks that want to be explicit about this, they could do:

(lldb) command alias rc continue --direction reverse
(lldb) command alias c  continue --direction forward

> 
> The other question is, what should the thread plan stacks look like during this scenario?

continue is a little bit tricky because it doesn't really correspond to a thread plan per se.  Thread plans are "execution control with intentions" and "continue" doesn't really have an intention.  It just means "restart letting the extant plan stack do its thing".  At one point, I thought it might be useful to be able to have a mode of lldb where you could say:

thread 1 - do a step over
thread 2 - do a step in
continue

and the first two operations would just push thread plans but not set the process going, then "continue" would be set all the threads and their plans running.  I only ever really wanted this a couple of times, however, not enough to actually implement it, but that's partly why "continue" isn't thread-plan based, but rather just runs thread plans on whichever threads want to run that time round.

So in this case, the thread plan stack would just have a "base thread plan" - which is the plan that handles unexplained stops - throughout the process.

However, up to now, the thread plan stacks for individual threads have been independent of each other.  If thread A was doing a "step in" while thread B was in the middle of a "step over", the plan for thread A didn't need to affect how anything but thread A ran.  The fact that it doesn't make sense to run one thread forwards and another thread backwards at the same time adds some thread coupling, which complicates this design.  

Suppose, for instance, you were doing a backwards next on thread A and hit a breakpoint, then decided to do a forward next on thread B and that also hit a breakpoint, then you're going to need to tell thread A - during the forward next for thread B - that it will be moving forwards while the thread B next plan is in force, then should go back to reverse to finish out the Thread A next when the thread B "next" is done.  I think the best way to do this is by having direction switching plans with callbacks for when they are done.  So when you stopped at the second breakpoint, the thread plan state would have:

Thread A                                Thread B
base plan                                base plan
reverse next (1)                     reverse till plan (1) is done
forward till plan (2) is done  forward next (2) 

The "forward till" and "reverse till" plans would be derived from the base thread plan since they should absorb all the non-explained stops.  In this case the callback for "reverse till plan (1) is done" plan checks whether plan (1) has been popped, and pops itself when that is true.

We'll also have to do more "IsPlanStale" checking, for instance if the forward nexts on thread B moves Thread A further in the future than the beginning of the reverse next plan (1) was handling, we probably want to discard it when that happens.

In the case where there aren't any thread plans beyond the base plan and we're just continuing, we might not need to add a directional plans though that should just be an optimization.



https://github.com/llvm/llvm-project/pull/112079


More information about the lldb-commits mailing list