Callback API question

Hi Greg, or whoever reads this first,

I have a question about the python API lldb.SBBreakpoint.SetCallback function. According to the docs, the signature is:

SetCallback(self, *args) unbound lldb.SBBreakpoint method
    SetCallback(self, BreakpointHitCallback callback, void baton)

However, I am not sure how to create an instance of the BreakpointHitCallback type that is required for the second parameter. I'm not seeing any class in the Python API that is named that; only the C++ API has a typedef of that name:

typedef bool (*BreakpointHitCallback) (void *baton,
                                       SBProcess &process,
                                       SBThread &thread,
                                       lldb::SBBreakpointLocation &location);

I tried passing a function that has (what I believe is) the correct signature:

class MyTestCase(TestBase):
  def bp_callback(self, process, thread, breakpoint_loc):
    Pass

  def register_callback(self, target)
    breakpoint = target.BreakpointCreateByLocation('main.c', self.line1)
    breakpoint.SetCallback(self.bp_callback, self)

But I am getting a python error:

TypeError: in method 'SBBreakpoint_SetCallback', argument 2 of type 'lldb::SBBreakpoint::BreakpointHitCallback'

So, my question is: is it possible to register a callback to be called when a breakpoint is hit using that function? If so, what am I doing wrong? If not, is it preferable to create an SBListener for this purpose, or is the "target stop-hook add" command the only way to do what I'm trying to do? I'm trying to avoid using the built-in script interpreter..

Thanks,
Dan

I don’t think we currently supporting setting Python breakpoint callbacks through the SB API
We would need to add some code that takes a meaningful representation of a Python callable, and makes a C++ thunk that knows how to call into Python and get the result back to C+±land
It’s definitely an enhancement worth considering

Enrico Granata
:email: egranata@.com
✆ 27683

You don't use the callback directly, you actually tell the breakpoint that is should call a python function:

(lldb) breakpoint command add -F my_module.breakpoint_hit

The signature for the python callback is:

def breakpoint_callback(frame, bp_loc, dict):
  # Your code goes here

So you don't get the specify a baton for the callback, but you can get python to be called when a breakpoint does get hit. You can always make a global variable (dictionary) that can track which breakpoints map to which baton ("self" in your case) and then you could manually call your other functions.

Greg Clayton

Thanks for the explanation Enrico, that's what I thought.

So, is it possible to use SBListener to register some callback on a breakpoint hit? Or is it possible only via the C++ API or the "target stop-hook" commands?

Hi Dan,

You'll have to implement a bridge method, like I did for
LogOutputCallback and the SBInputReader callback.
Check out scripts/Python/python-wrapper.swig and
scripts/Python/python-typemaps.swig and search for mentions of
LogOutputCallback.

Regards,

  Filipe

You can register a listener, but that is more “low-level”, i.e. it will let you know when the process stopped (by giving you an SBEvent), and then you can look around the state of your program to figure out if the stop reason is a breakpoint, and if so “which” breakpoint.
This is similar to what the lldb-perf TestCase class does, so you can refer to TestCase::Loop() for an example of how you would to that.
Unfortunately, what you get from that API is the breakpoint number, which might be not very informative by itself.
We discussed the option of letting you “name” breakpoints and then retrieve that name for situations like this, but haven’t gotten around to doing anything more than discussing it.
You can of course work around the issue by having a number → name table, or by writing a patch for named breakpoints :slight_smile:

Enrico Granata
:email: egranata@.com
✆ 27683

You can register a listener, but that is more “low-level”, i.e. it will let you know when the process stopped (by giving you an SBEvent), and then you can look around the state of your program to figure out if the stop reason is a breakpoint, and if so “which” breakpoint.
This is similar to what the lldb-perf TestCase class does, so you can refer to TestCase::Loop() for an example of how you would to that.
Unfortunately, what you get from that API is the breakpoint number, which might be not very informative by itself.

Actually, if you listen to stop events, you get the "breakpoint site ID" that was stopped at, not the breakpoint ID. After all, there may be many breakpoints that share the same address, and in effect "all of them" caused the stop. You go from the breakpoint site to the BreakpointLocations that own the site, and then to the Breakpoints that own those locations.

Note also that by the time that you get the stop event, all the breakpoint actions have gotten run, so for instance if the breakpoint hit was thread specific for some other thread, or if there was a condition on the breakpoint that evaluated to false, the process will have already been restarted by the time you get the event. To find out whether this has happened, you should call GetRestarted from the event.

We discussed the option of letting you “name” breakpoints and then retrieve that name for situations like this, but haven’t gotten around to doing anything more than discussing it.
You can of course work around the issue by having a number -> name table, or by writing a patch for named breakpoints :slight_smile:

That would still be good to do, but more because then you could do:

break set --breakpoint-name "first-break" -n foo
break command add -F my_python_function first-break

in your command scripts. But if you are generating the breakpoints from Python, this isn't a problem.

Jim