Interactive commands in LLDB

Hello,

This is more like a question through a proposal on how to add
interactive commands to LLDB.

By an interactive command, I mean that when one runs the command, it
asks for more input from the user. AFAICT, there is no way currently
to do that in LLDB. There is an IOHandlerConfirm which can be used to
get a yes/no response from the user, but what I am looking for is
something which can read in "richer" inputs. Could we extend
IOHandlerConfirm as follows:

1. Rename IOHandlerConfirm to IOHandlerUserResponse
2. To support the existing use case of "confirm", add two methods,
UserResponseIsYes and UserResponseIsNo to it.
3. Make its GetResponse method return the raw user input.
4. Add few convenience methods to it like GetResponseAsUint8,
GetResponseAsString etc.

The above is a big picture idea; there could be a few details to
resolve when getting down to implementation. My motivation to require
a facility like this is to implement something like the "explore"
command of GDB:
Data (Debugging with GDB).

Thanks,
Siva Chandra

I would rather not conflate simple "confirm" dialogs and something really interactive like what you are describing. For instance, if you run into a confirm dialog in a non-interactive environment (breakpoint command, for example) it makes sense to choose the default value and continue on. But for an interactive command you probably want to do something else, either assume that the current data source (e.g. a "command source" file) is going to provide more data.

The IOHandlerConfirm does so little work, I don't think anyway you'd find all that much to reuse. It seems to me better to make a new IOHandler class to deal with this more complex interaction.

Jim

I know you answered this briefly on IRC, but i had to run so I didn’t have time to ask for clarification. What is the benefit of this approach over a stateful command similar to git bisect? I know you said something about exploring very large types, but I didn’t quite follow why this approach lends itself better.

There are a number of advantages of the stateful approach. One is that you can run arbitrary commands between stages of the traversal. Maybe you see a pointer whose value is a module load address , and you want to dump info about the module loaded at that address.

It also provides more flexibility even in the exploration process. If a field is a void* for example, an interactive command might be forced to give up, whereas a stateful non interactive command may be able to be forced into treating it as a specific type through a cast expression. It seems like the number of times you would know more than the type information lets on might warrant the additional flexibility.

It might also be difficult to test an interactive command. Would the command callback into python? Seems difficult.

Anyways, these are just ideas. I do like the general idea of a command that explores type hierarchies this way

I haven't used the gdb "explore". It does seem a little odd to me, like something that is well modeled by a turn-down GUI variable display but not so much by this kind of question & answer session. But I haven't used it so I can't say.

OTOH, I do have other uses for a slightly more advanced interactive IO handler. For instance, it you do "process attach -n" and there are multiple processes with the same name, we show you a nice little listing of the processes and their arguments so you can figure out which PID you actually want to attach to. But it would be nicer to say "here are these five processes, type the index of the one you want to attach to." Maybe you could also have "show me more about process 3" which could dump a more verbose output, and then you would decide to attach to 3...

Jim

I haven't used the gdb "explore". It does seem a little odd to me,
like something that is well modeled by a turn-down GUI variable
display but not so much by this kind of question & answer session.
But I haven't used it so I can't say.

My own point of view: I have hardly ever used an IDE or GUI for dev
work or debugging. Having a CLI for this would likely be of value to
people like me. However, I do agree that if one is using a GUI, this
should not be required. But would that GUI require to understand the
current source language? For example, it should be able to do produce
syntax for field access like a.b->c.d. May be its not an issue.

OTOH, I do have other uses for a slightly more advanced interactive
IO handler. For instance, it you do "process attach -n" and there are
multiple processes with the same name, we show you a nice little
listing of the processes and their arguments so you can figure out
which PID you actually want to attach to. But it would be nicer to say
"here are these five processes, type the index of the one you want to
attach to."

One could argue that this case can also be handled by a GUI more
effectively? :slight_smile:

Maybe you could also have "show me more about process 3" which
could dump a more verbose output, and then you would decide to
attach to 3...

So, do you envision this to happen in a single command "session", or
do you envision something like what Zach talked about: a command
retaining state across sessions. I am not a fan of keeping state
across sessions as managing state between two sessions could get
tricky and IMO not worth it for the command I have in mind.

I think the use case required for something like an "explore" command
is much simpler. As in, it would always require a real user
interacting with it (like with the "breakpoint delete" command). IIRC,
"explore" does have a default value (of exiting the session).

I still don't see what value you get joining the code to do "Give me one answer with a default value" with the much more complex "enter an interactive session that may have many questions (and you might want to alter the prompt to show where you are in the session currently, etc." I have no objection to adding the latter if there's a good use for it. But given that the IOHandlerConfirm is around 10 lines of code, I see no benefit in complicating it with the code to implement the more complex session.

Jim

I haven't used the gdb "explore". It does seem a little odd to me,
like something that is well modeled by a turn-down GUI variable
display but not so much by this kind of question & answer session.
But I haven't used it so I can't say.

My own point of view: I have hardly ever used an IDE or GUI for dev
work or debugging. Having a CLI for this would likely be of value to
people like me. However, I do agree that if one is using a GUI, this
should not be required. But would that GUI require to understand the
current source language? For example, it should be able to do produce
syntax for field access like a.b->c.d. May be its not an issue.

It is driven by the SBValue system, it's up to that system to produce the notion of "child objects", then the GUI just reflects that. The SBValue system clearly has to understand the source language. You can even ask it "give me the expression that produced this child in the current source language, for instance if you then want to pass that value to an expression for some purpose. But for basic data display the SBValue layout works fine.

OTOH, I do have other uses for a slightly more advanced interactive
IO handler. For instance, it you do "process attach -n" and there are
multiple processes with the same name, we show you a nice little
listing of the processes and their arguments so you can figure out
which PID you actually want to attach to. But it would be nicer to say
"here are these five processes, type the index of the one you want to
attach to."

One could argue that this case can also be handled by a GUI more
effectively? :slight_smile:

I wouldn't.

Maybe you could also have "show me more about process 3" which
could dump a more verbose output, and then you would decide to
attach to 3...

So, do you envision this to happen in a single command "session", or
do you envision something like what Zach talked about: a command
retaining state across sessions. I am not a fan of keeping state
across sessions as managing state between two sessions could get
tricky and IMO not worth it for the command I have in mind.

Again, I haven't thought about the best way to implement something like the "explore" command. So I don't have an opinion about how to implement it.

In the simple "pick from a list" version of selecting a process, it would also be a one-shot thing so statefulness isn't really relevant. But if you imagine doing something like:

(lldb) process attach -n Foo
INDEX PID ARGUMENTS
1 111 foo bar
2 222 foo bar bar
Select a index to attach to (?<N> for more info) > ?1
<More info on process 1>
Select a index to attach to (?<N> for more info) > ?2
<More info on process 2>
Select a index to attach to (?<N> for more info) > 2
Attaching to PID 222
(lldb)

Then making it a complete session is much more natural than having to come in & out of "process attach".

Jim

Implementing the explore command as a stateful command in the way I had envisioned it would be a bit different than the process attach example here, so it’s not the best point of comparison. What I imagined a session would go like is something like this:

(lldb) explore begin m_myclass_sp

<std::shared_ptr> m_foo_sp

  • m_impl +0x0 <Foo*>

  • m_ref_count +0x4 4

(lldb) explore follow m_impl
<MyClass*> m_foo_sp->m_impl

  • member1 +0x0 <const char*> "String value

  • member2 +0x4 10

  • member3 +0x8 <Bar*>

(lldb) explore follow (Baz*)member_3
<Baz*> (Baz *)m_foo_sp->m_impl->member3

  • m_baz1 +0x0 3.5

(lldb) explore status

<Baz*> (Baz *)(m_foo_sp->m_impl->member3)

  • m_baz1 +0x0 3.5

(lldb) explore up

<MyClass*> m_foo_sp->m_impl

  • member1 +0x0 <const char*> "String value

  • module_addr +0x4 lldb::addr_t 0x80f23490

  • member3 +0x8 <Bar*>

(lldb) target modules lookup -a 0x80f23490
// Information about the module loaded at 0x80f23490 is dumped

(lldb) explore stop
(lldb)

The nice thing about this approach is that the different subcommands of explore can take command line options, giving much more flexibility over the exploration process. For example, consider replacing “explore status” at the 4th step of my example with “explore status -v” (i.e. verbose)

(lldb) explore status -v
m_foo_sp

  • m_impl +0x0 <Foo*>

  • member1 +0x0 <const char*> "String value

  • member2 +0x4 10

  • (Baz*)member3 +0x8 <Baz*>

  • m_baz1 +0x0 3.5

  • m_ref_count +0x4 4

You can’t get this kind of flexibility, or the ability to run arbitrary commands like the “target modules lookup” with a question / answer session.

I must be missing something. This seems like the barest sugar on top of:

(lldb) expr m_myclass_sp
<regular expr output>
(lldb) <UP ARROW>->m_impl
<regular expr output>

etc. I guess it keeps you from having to know which things are pointers and which are not, but you generally have to know that for you day job anyway.

What am I missing that makes it worth adding & then users having to learn a new command set to do something expr already does pretty well?

Jim

I’m honestly not sure. In the GDB world, or in the world of pretty much any other debugger, I would argue that the value add is that the expression evaluator isn’t actually that good, and breaks down when you try to call an overloaded operator, or basically do anything other than follow raw pointers. Obviously we have a better expression evaluator, so your point is worth thinking about.

Firstly, thanks for taking time to answer and respond to me.

I would like to present some historical context here wrt the "explore"
command in GDB. Back when it was added, my use for it was to
understand GCC's mega structs and unions. There was something like a
flag typically which kind of specified how to interpret (for example,
which field of a union is relevant) the rest of struct and union. So,
it all made sense back then to have an interactive command in single
session which helped me get to the relevant parts of a struct/union
value. IMO, such a use case is still relevant. However, I will go with
what your final take on this.

Firstly, thanks for taking time to answer and respond to me.

I would like to present some historical context here wrt the "explore"
command in GDB. Back when it was added, my use for it was to
understand GCC's mega structs and unions. There was something like a
flag typically which kind of specified how to interpret (for example,
which field of a union is relevant) the rest of struct and union. So,
it all made sense back then to have an interactive command in single
session which helped me get to the relevant parts of a struct/union
value. IMO, such a use case is still relevant. However, I will go with
what your final take on this.

Note, in lldb you could do the same job you are describing here quite handily by writing a synthetic child provider. Since the Python data formatters can do logic, it would be straight-forward to write one that checks field A, and based on that decides which other fields to print. The advantage of this is that then any expression that resolves to a variable of that type will be printed appropriately without requiring any special action on the user's part.

Jim

So, this means that one has to write data formatters (or,
pretty-printers in GDB land) for the data structures in question. Can
I take this as "we prefer data formatters over this explore command"?

Firstly, thanks for taking time to answer and respond to me.

I would like to present some historical context here wrt the “explore”
command in GDB. Back when it was added, my use for it was to
understand GCC’s mega structs and unions. There was something like a
flag typically which kind of specified how to interpret (for example,
which field of a union is relevant) the rest of struct and union. So,
it all made sense back then to have an interactive command in single
session which helped me get to the relevant parts of a struct/union
value. IMO, such a use case is still relevant. However, I will go with
what your final take on this.

Note, in lldb you could do the same job you are describing here quite
handily by writing a synthetic child provider. Since the Python data
formatters can do logic, it would be straight-forward to write one that
checks field A, and based on that decides which other fields to print.
The advantage of this is that then any expression that resolves to a
variable of that type will be printed appropriately without requiring any
special action on the user’s part.

So, this means that one has to write data formatters (or,
pretty-printers in GDB land) for the data structures in question. Can
I take this as "we prefer data formatters over this explore command”?

I can’t speak for Jim (or for the broader “we”), but “I personally do prefer data formatters over this explore command”


lldb-dev mailing list
lldb-dev@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev

Thanks,
- Enrico
:envelope_with_arrow: egranata@.com :phone: 27683

I'd say it another way. Instead of spending the time on this explore command, which requires you to re-do the labor every time you want to inspect data of that type, produce a rough equivalent but whose job is to interactively produce a synthetic child provider for a particular data type. These aren't hard to write, but you have to know Python & the SB API's so there a bit of a barrier to using them. If there was a way to say: if A is 5, then view these three fields, if 6 view these other three, etc, I think that would be pretty neat.

Jim

While I personally disagree* with this, I will stop here.

* There are many reasons. One of them is that, one could be dealing
with moving targets. Hence, moving the data formatter along with the
targets is double the work. Another reason is, if I were a library
dev, an "explore" command is provided by my tool chain and can be used
even before the data formatters are written for my data structures.

This actually sounds like a great command to add as a python command. You could add it to the lldb/examples/python directory. I don't think it is something we want as a permanent command.

This actually sounds like a great command to add as a python command.
You could add it to the lldb/examples/python directory. I don't think it is something
we want as a permanent command.

OK, I will go with it. It is infact present as a Python command in GDB
as well. However, LLDB has more formalism and better IO handling that
I pushed for it to be a builtin command. I am fine with it being a
Python command.