[RFC][LLDB] Highlighting function names in LLDB backtraces

This RFC proposes a new frame-format setting that will color-highlight parts of a function name in the backtrace (or frame/thread info status). The motivation (similar to [RFC][ItaniumDemangler] New option to print compact C++ names, but is not mutually exclusive to that RFC), is to provide users with a more user-friendly backtrace when debugging heavily templated C++. Usually when looking at a backtrace, one wants to see the path of function calls that lead us to the frame we’re stopped in. Currently this information is not easy to obtain from a quick glance at a backtrace, especially when the demangled names include long scopes and template arguments (or other C++ constructs like function pointers, decltype etc.). This proposal addresses these issues by providing a visual cue to quickly determine all function names involved in a backtrace.

I’m looking to discuss how much appetite the community has for this feature, and any thoughts/concerns on how the proposal integrates it into LLDB’s existing frame-format language.

Example

This is what the current LLDB backtrace looks like:

Changing the default ${function.name-with-args} frame-format variable to the proposed ${function.name-with-args:%highlight_basename(ansi.fg.cyan)} (the final syntax of this will probably differ) would yield:

Note how the basename of each frame is highlighted with the color specified in the setting.

Implementation Overview

The function names in backtraces are currently the ones we get from the LLVM demangler (more on that in the Alternatives Considered section). So the high-level idea is to make the demangler track where in the demangled name the basename begins and ends. LLDB then takes this information, and when printing the frame name, it makes sure to highlight that portion of the demangled name.

A draft implementation can be found here: [WIP: DO NOT MERGE] [lldb][Format] Add option to highlight function names in backtraces by Michael137 · Pull Request #131836 · llvm/llvm-project · GitHub

Implementation Details

There are three parts to the implementation:

  1. Add infrastructure to LLVM’s ItaniumDemangler to track where certain portions of a C++ function name are in the demangled name.
  2. Expose the new demangler information to LLDB
  3. Invent a mechanism to specify how to highlight a frame name in LLDB’s frame-format setting.

1. Demangler Changes

The idea that a function name can be decomposed into <scope, base, arguments>. The assumption is that given the ranges of those three elements and the demangled name, it is possible to reconstruct the full demangled name. The tracking of those ranges is pretty simple inside the demangler. We don’t ever deal with nesting, so whenever we recurse into a template argument list or another function type, we just stop tracking any positions. Once we recursed out of those, and are back to printing the top-level function name, we continue tracking the positions.

The draft implementation introduces a new structure to the llvm::itanium_demangle::OutputBuffer, which is unfortunately the only way to keep state while printing the demangle tree (it already contains other kinds of information similar to this tracking. In [RFC][ItaniumDemangler] New option to print compact C++ names we propose to refactor this, but shouldn’t be a blocker unless people feel otherwise).

2. Integrating into LLDB

The ItaniumPartialDemangler (which th lldb_private::Mangled object uses) already has access to the OutputBuffer. So LLDB can just fish the basename locations out of that and expose it from lldb_private::Function, which we use in FormatEntity to get the demangled name to print for each frame.

3. Frame Formatting Language Changes

We need to somehow let the user decide to highlight function names. We probably also want to let a user decide which color to use. So the prototype opted for re-using the (undocumented?) printf format specifier infrastructure of the frame-format variable language. That allows us to add additional information to the ${function.name-with-args} variable. We can invent new syntax like ${function.name-with-args:%<some new syntax>}. There is already precendent for this for the %tid format specifier. The prototype opted for:

${function.name-with-args:%highlight_basename(ansi.fg.cyan)}

Meaning we want to highlight the basename of ${function.name-with-args} with ${ansi.fg.cyan}.

Once we determine that a user wants to highlight the basename we pass that info to the CPlusPlusLanguage plugin, which will print the individual portions of the demangled name using the positions it got from the demangler. We already do this kind of printing using the CPlusPlusNameParser. The only difference is that there we determine the positions ourselves instead of asking the demangler.

Handling Other Languages

In the proposed implementation the highlighting is done entirely in the language plugin (and the demangler it chooses to use). The only common interface point is the new FormatEntity::Entry::HighlightSetting which specifies the “kind” of highlighting to perform (currently only Kind::Basename is implemented, but is easily extended to other parts of a function name if a language plugin wants that).

Alternatives Considered

  • Don’t use demangled names for frame names; use the Clang’s type-printer instead

In [RFC][ItaniumDemangler] New option to print compact C++ names we discussed using names from debug-info and letting Clang pretty-print the names. This is currently not possible without changing the way LLDB reconstructs the Clang AST from DWARF. Particularly, template functions/classes AST nodes aren’t constructed in a way that would allow the clang::TypePrinter to print template parameters (currently it would just drop the templates). I’m not sure how much work it would be to address this, though last time I investigated this, the consensus was that we would have to at the very least start representing generic template definitions in DWARF. Also note, this would only help for cases where debug-info is available.

  • Using CPlusPlusNameParser to determine the positions of the basename/template parameters/etc., instead of doing this in the demangler.

This seems like a non-starter, since parsing C++ names is difficult and we don’t want to add additional reliance on CPlusPlusNameParser.

CCing people who I’ve talked to about backtraces in the past
(@adrian.prantl @kastiglione @labath @dblaikie @jingham)

3 Likes

Personally, I always found it ironic that of all the things we’re highlighting by default, we would choose the address, so from a UI perspective, I think this feature would be great, and the screenshot illustrates the “why” pretty well.

I think this would greatly improve the readability of those backtraces, so I’m very supportive of the feature. Deferring this to the language plugins sounds like a good fit.

I’m particularly interested in the changes to the format strings. I’m looking at them in the context of the statusline. A few things I’d like to accomplish are supporting left/right alignment, padding and and support for a default value (if something is empty, like no current target). I also discovered the %tid so using the percentage sign makes sense.

This seems like a natural extension to the format strings. If we’re going to start adding more output specifications like this it might be good to think about how to compose them. Jonas will want “left aligned AND blue”…

This is great feature idea!

To prove that, I thought at first that the two screenshots were the same. Until I realised it was just me giving up reading after a few rounds of :: :slight_smile: Especially when terminal lines wrap, the highlighting would really help.

If you’re trying to compose format strings, might be worth looking at other established applications that have done so. For better or worse. I would assume no-one gets these things right first time.

(also I have never been one to customise prompts and status bars much, so external references would help me understand your choices)

1 Like

I agree. In fact, would there be any appetite for not highlighting the address anymore?

+1 for the overall proposal – this would be super helpful whenever people have to debug templated code, where the function name is nearly impossible to find right now.

1 Like