vadimcn / codelldb

A native debugger extension for VSCode based on LLDB
https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb
MIT License
2.57k stars 246 forks source link

"Step Back" command on backends with time-travel debugging has poor user experience #865

Open PoignardAzur opened 1 year ago

PoignardAzur commented 1 year ago

OS: Pop!_OS 22.04 LTS x64 VSCode version: 1.75.1 441438abd1ac652551dbe4d408dfcec8a499b8bf x64 CodeLLDB version: v1.8.1 Compiler: not important - eg cargo 1.67.0 (8ecd4f20a 2023-01-10) Debuggee: not important - eg ripgrep 44fb9fce2c1ee1a86c450702ea3ca2952bf6c5a7 Backend: rr version 5.5.0

I suspect this is a problem the maintainer is already aware of, but I thought it would be worth documenting anyway.

When using CodeLLDB with a backend with time-travel debugging such as rr, the debug bar looks like this:

image

Two buttons have been added, a "Step Back" button and a "Reverse" button. I think the user experience of the "Step Back" button is lacking:

As an example of the last point, let's say I record a trace of the following program:

#include <stdio.h>

int foobar(void);

int main(void)
{
    printf("Hello, world - 1\n");
    printf("Hello, world - 2\n");
    printf("Hello, world - 3\n");
    foobar();
    return 0; // breakpoint here
}

int foobar(void)
{
    printf("foobar - 1\n");
    printf("foobar - 2\n");
    printf("foobar - 3\n");
    return 1;
}

I then run rr replay -s 4242 on one side and start CodeLLDB with the following config:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "rr replay",
            "type": "lldb",
            "request": "custom",
            "targetCreateCommands": ["target create ${workspaceFolder}/a.out"],
            "processCreateCommands": ["gdb-remote 127.0.0.1:4242"],
            "reverseDebugging": true
        },
    ]
}

The program initially runs and stops at the breakpoint I placed on the return 0; line.

If I press "Step Back", I would expect the control flow to settle on the foobar(); line. Instead it settles on return 1; inside of foobar.


I'm not sure how feasible this is to fix. I peeked at the code and it looks like the reason the code is that way is because of a bunch of workarounds for LLDB not supporting reverse steps.

On the other hand, there might be other workarounds with better UX. "Reverse" seems to work fine; "Step Back" could maybe be implemented as "Create a temporary breakpoint one line before the current instruction, reverse-continue, then delete that breakpoint". Although that workaround would likely have its own problem, so I dunno.

At the very least, a different workaround that didn't involve changing the "show disassembly" parameter would make this feature a lot more usable for me.

PoignardAzur commented 1 year ago

Also, I think there should probably be an issue in the LLVM repository to signal the lack of reverse-debugging support in the server interface, but I don't know the project well enough to open one.

vadimcn commented 1 year ago

Lldb was never designed to support reverse debugging. I've added features that were reasonably easy to add, but the one you are asking for is not one of them.
In the general case, finding the previous line is not trivial (e.g. what if you are at the first line of a loop? What if you are debugging an optimized executable, where instructions may be ordered differently than the lines they originated from? etc) Lldb implements line stepping using something.they call "thread plans". I think the same will be required for reverse line stepping. Unfortunately, this subsystem is not well documented, and I never felt the urge to rspend the time to everse engineer it. For me, breakpoints and reverse execution are "good enough".