My desired features for a Rust debugger

Backward stepping

Most debuggers provide the following commands:

  • Step into the next function (reaching its beginning-point)
  • Hop over the next function call
  • Jump to the end of the current function
  • Run (until next break-point or end of process)

But the most needed commands are rare. They are:

  • Back-step into the previous function (reaching its end-point)
  • Back-hop over the previous function call
  • Back-jump to the beginning of the current function
  • Back-run (until previous break-point or beginning of process)

The key combinations for such commands must be customizable, as many people is already used to specific key combinations for debugging.

Stop-points are not lines

Some debuggers consider any source-code line as a possible point where the program can stop (stop-point). That is not true, for three reasons:

  • Many lines do not contain executable code, and so the program cannot stop in such lines.
  • Many statements span several lines, even if they do not contain function calls. There is no point in stepping through such line, and so the whole statement should be a single stop-point.
  • Many statements contain function calls, sometime disguised as an operator. Such statements contain as many stop-points as the contained function-calls, plus one. Consider this statement:
     let a = f(g(3 + a), h(k()));.

    It contains 4 function calls, and so it contains 5 stop-points:
    – After having entered the statement, before calling “g”.
    – After having called “g”, before calling “k”.
    – After having called “k”, before calling “h”.
    – After having called “h”, before calling “f”.
    – After having called “f”, before assigning a value to “a”.

So, when a program is stopped, there shouldn’t be a current line, but a current stop-point, that is a slice of source code that usually does not begin at the beginning of a line, does not end at the end of a line, and may span several lines. Here are the 5 stop-points of the previous example statement:

let a = f(g(3 + a), h(k()));
          ^^^^^^^^
let a = f(g(3 + a), h(k()));
                      ^^^
let a = f(g(3 + a), h(k()));
                    ^^^^^^
let a = f(g(3 + a), h(k()));
        ^^^^^^^^^^^^^^^^^^^
let a = f(g(3 + a), h(k()));
^^^^^^^^^^^^^^^^^^^^^^^^^^^

To show the current stop-point, it should have a different color, and in addition a marker should be shown at the beginning of its first line, because the current stop-point can be off-screen.
Break-points are particular stop-points, selected to stop the execution of the program. They, too, should be marked both as source-code slices, and as beginning-of-line markers.

Smart watches

When the program is stopped, it should be possible to view the list of all alive variables and function arguments (even if shadowed), in reverse order of appearance (and therefore in order of future destruction).
For each variable, it should be automatically shown its source-code name, its type, and its stack contents. When a variable is selected, its “{:?}” contents should be shown in a scrollable window.
Such variable list should have a horizontal line at each stack frame begins, marked by the name of the beginning function.
Each variable should have a “watch” check-box. If such checkbox is checked, the contents of the variable is shown even when that variable is not selected. The contents of the variable is updated whenever the program is stopped, and the whole watch line should be differently colored if the value of the contents of a variable has changed with respect to the previous stop.
Each variable should have also a “stop when it changes” check-box. If such checkbox is checked, the program stops whenever the value of this variable changes (because of safe statements).

Call stack

When the program is stopped, a list of the current function calls should be listed. If a function call is selected in this list, the source-code view shows the source code of such function, and the watches view scrolls to the variables of such stack frame.

Post-mortem

If a program is executed in debugging-mode, when there is a panic, there should be a debug dump. Such dump should containing enough information to perform all the debugging operations that would be possible if the program were run in the debugger and stopped at the statement causing the panic.

Changing data

It can be useful to be able to change the value of memory. It should be easy to implement, as long as no allocation/deallocation is performed. For example, it should be easy to allow the change of a numeric variable value, while it can be hard to allow the extension of a string variable value.

Moving around

It can be useful to be able to set the current stop-point inside the current function, i.e. to skip or to re-run some statements.

Conclusion

Summarizing, I think GDB is a very bad debugging tool. I think Smalltalk debugger and Elm debugger are very good, instead. For Rust I wish a debugger that has the best features of Smalltalk and Elm.

Look here to see a Smalltalk debugger in action: https://www.youtube.com/watch?v=1kuoS796vNw.

Look here to see an Elm debugger in action: https://www.youtube.com/watch?v=RUeLd7T7Xi4.

Advertisement

About Carlo Milanesi

I am a software developer in Italy. I have developed financial, engineering, commercial, web, and industrial automation software using many programming languages, mainly C, C++, Visual Basic, Java, and C#. Now I am interested in Rust.
This entry was posted in Uncategorized. Bookmark the permalink.

2 Responses to My desired features for a Rust debugger

  1. badboy_ says:

    For your first wish there’s rr, a gdb-enhancement which can record and play back arbitrary execution, allowing you to jump back and forth.
    The other points are not necessarily Rust-specific. Tools like IDA Pro and other graphical debuggers most likely are already able to show stack values, the call stack, etc.

    • rr and gdb are command line tools. I need a full-screen tool.
      IDA Pro is a disassembler. It works with assembler source. I need to see variables as printed by “print!(“{:?}”, variable)”, and to see only source code.
      Most debuggers work line-by-line, but Rust function calls often span several lines, or several calls are contained in a single line. For example, Smalltalk debuggers work call-by-call, not line-by-line.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s