Discussion:
GDB Python API: stop/continue after breakpoint
Kevin Pouget
2011-03-11 16:08:57 UTC
Permalink
Hello,

I've tried the GDB python interface today, which seems quite
efficient, but there is one important thing I couldn't figure out by
myself:

how to restart GDB when a[n internal] breakpoint is hit ?
from the testsuite I've got this code:

def breakpoint_stop_handler (event):
if (isinstance (event, gdb.StopEvent)):
print "event type: stop"
if (isinstance (event, gdb.BreakpointEvent)):
print "stop reason: breakpoint"
print "breakpoint number: %s" % (event.breakpoint.number)
if ( event.inferior_thread is not None) :
print "thread num: %s" % (event.inferior_thread.num);
else:
print "all threads stopped"

gdb.events.stop.connect (breakpoint_stop_handler)


which where I get the notification of the stop, but I'd to be able to
tell GDB something like

enum bpstat_what_main_action {
/* Remove breakpoints, single step once, then put them back in and
go back to what we were doing. It's possible that this should
be removed from the main_action and put into a separate field,
to more cleanly handle BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE. */
BPSTAT_WHAT_SINGLE,
/* Stop silently. */
BPSTAT_WHAT_STOP_SILENT,

/* Stop and print. */
BPSTAT_WHAT_STOP_NOISY,
...
}

to continue silently, stop silently or print the breakpoint hit.

is it possible at this stage ?

Thanks,

Kevin
Phil Muldoon
2011-03-11 16:25:22 UTC
Permalink
Post by Kevin Pouget
Hello,
I've tried the GDB python interface today, which seems quite
efficient, but there is one important thing I couldn't figure out by
how to restart GDB when a[n internal] breakpoint is hit ?
You almost can. One part is pending:

http://sourceware.org/ml/gdb-patches/2011-03/msg00656.html

The implementation of the "stop" API. The idea behind this is that if a
breakpoint is hit, that is tracked from Python and has an implemented
stop method, that method would be called. You can do what you like in
that method. If you want the inferior process to continue, return True
otherwise return False (and print out/do whatever else you need to do in
Python).

Because internal breakpoints are not tracked by default in the Python
Breakpoint API, you would have to create your breakpoint by
instantiating a gdb.Breakpoint class, and pass the keyword
internal=True.

So, long story short soon. OTOH I'm not sure if there is a unhacky way
of doing it now. You could use a convenience function, but that patch
is replacing that hacky way.

Cheers

Phil
Post by Kevin Pouget
print "event type: stop"
print "stop reason: breakpoint"
print "breakpoint number: %s" % (event.breakpoint.number)
print "thread num: %s" % (event.inferior_thread.num);
print "all threads stopped"
gdb.events.stop.connect (breakpoint_stop_handler)
which where I get the notification of the stop, but I'd to be able to
tell GDB something like
enum bpstat_what_main_action {
/* Remove breakpoints, single step once, then put them back in and
go back to what we were doing. It's possible that this should
be removed from the main_action and put into a separate field,
to more cleanly handle BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE. */
BPSTAT_WHAT_SINGLE,
/* Stop silently. */
BPSTAT_WHAT_STOP_SILENT,
/* Stop and print. */
BPSTAT_WHAT_STOP_NOISY,
...
}
to continue silently, stop silently or print the breakpoint hit.
is it possible at this stage ?
Thanks,
Kevin
Kevin Pouget
2011-03-11 17:52:19 UTC
Permalink
thanks for your answer, the patch seems to feature what I was looking for.

I'm a bit surprised that the stop/continue decision can't be done in
this breakpoint_stop handler, but I guess that was too complicated ?
Post by Phil Muldoon
Post by Kevin Pouget
Hello,
I've tried the GDB python interface today, which seems quite
efficient, but there is one important thing I couldn't figure out by
how to restart GDB when a[n internal] breakpoint is hit ?
http://sourceware.org/ml/gdb-patches/2011-03/msg00656.html
The implementation of the "stop" API.  The idea behind this is that if a
breakpoint is hit, that is tracked from Python and has an implemented
stop method, that method would be called.  You can do what you like in
that method.  If you want the inferior process to continue, return True
otherwise return False (and print out/do whatever else you need to do in
Python).
Because internal breakpoints are not tracked by default in the Python
Breakpoint API, you would have to create your breakpoint by
instantiating a gdb.Breakpoint class, and pass the keyword
internal=True.
So, long story short soon.  OTOH I'm not sure if there is a unhacky way
of doing it now.  You could use a convenience function, but that patch
is replacing that hacky way.
Cheers
Phil
Post by Kevin Pouget
        print "event type: stop"
        print "stop reason: breakpoint"
        print "breakpoint number: %s" % (event.breakpoint.number)
            print "thread num: %s" % (event.inferior_thread.num);
            print "all threads stopped"
gdb.events.stop.connect (breakpoint_stop_handler)
which where I get the notification of the stop, but I'd to be able to
tell GDB something like
enum bpstat_what_main_action {
    /* Remove breakpoints, single step once, then put them back in and
       go back to what we were doing.  It's possible that this should
       be removed from the main_action and put into a separate field,
       to more cleanly handle  BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE.  */
    BPSTAT_WHAT_SINGLE,
    /* Stop silently.  */
    BPSTAT_WHAT_STOP_SILENT,
    /* Stop and print.  */
    BPSTAT_WHAT_STOP_NOISY,
...
}
to continue silently, stop silently or print the breakpoint hit.
is it possible at this stage ?
Thanks,
Kevin
Kevin Pouget
2011-04-20 14:58:46 UTC
Permalink
Hello,

I'm coming back to this post because I've got a bit of an issue here:

basically, I need (and I'm looking for to helping for it!) a sharp
control of the inferior execution from the python interface, but I'm
facing some limitations:

* in MyBreakpoint.stop(), I can say continue/stop the inferior, but I
can't run "finish" (or "next" or what ever, I guess), because the
"gdb.error: Cannot execute this command while the selected thread is running."
* in event.stop.connect() that's possible, but I can't
`gdb.execute("continue")' the execution because I would miss any
"non-python" reasons to stop (ie, a user-breakpoint, a signal ...)

let me know if my problem is not clear, and if you have any comment /
idea on how to solve it


cordially,

Kevin
thanks for your answer, the patch seems to feature what I was looking for.
I'm a bit surprised that the stop/continue decision can't be done in
this breakpoint_stop handler, but I guess that was too complicated ?
Post by Phil Muldoon
Post by Kevin Pouget
Hello,
I've tried the GDB python interface today, which seems quite
efficient, but there is one important thing I couldn't figure out by
how to restart GDB when a[n internal] breakpoint is hit ?
http://sourceware.org/ml/gdb-patches/2011-03/msg00656.html
The implementation of the "stop" API.  The idea behind this is that if a
breakpoint is hit, that is tracked from Python and has an implemented
stop method, that method would be called.  You can do what you like in
that method.  If you want the inferior process to continue, return True
otherwise return False (and print out/do whatever else you need to do in
Python).
Because internal breakpoints are not tracked by default in the Python
Breakpoint API, you would have to create your breakpoint by
instantiating a gdb.Breakpoint class, and pass the keyword
internal=True.
So, long story short soon.  OTOH I'm not sure if there is a unhacky way
of doing it now.  You could use a convenience function, but that patch
is replacing that hacky way.
Cheers
Phil
Post by Kevin Pouget
        print "event type: stop"
        print "stop reason: breakpoint"
        print "breakpoint number: %s" % (event.breakpoint.number)
            print "thread num: %s" % (event.inferior_thread.num);
            print "all threads stopped"
gdb.events.stop.connect (breakpoint_stop_handler)
which where I get the notification of the stop, but I'd to be able to
tell GDB something like
enum bpstat_what_main_action {
    /* Remove breakpoints, single step once, then put them back in and
       go back to what we were doing.  It's possible that this should
       be removed from the main_action and put into a separate field,
       to more cleanly handle  BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE.  */
    BPSTAT_WHAT_SINGLE,
    /* Stop silently.  */
    BPSTAT_WHAT_STOP_SILENT,
    /* Stop and print.  */
    BPSTAT_WHAT_STOP_NOISY,
...
}
to continue silently, stop silently or print the breakpoint hit.
is it possible at this stage ?
Thanks,
Kevin
Phil Muldoon
2011-04-20 15:15:02 UTC
Permalink
Post by Kevin Pouget
Hello,
basically, I need (and I'm looking for to helping for it!) a sharp
control of the inferior execution from the python interface, but I'm
* in MyBreakpoint.stop(), I can say continue/stop the inferior, but I
can't run "finish" (or "next" or what ever, I guess), because the
"gdb.error: Cannot execute this command while the selected thread is running."
At this point GDB is deciding whether to stop the inferior and
return control to the console or not. (Really, at this point, the
inferior is stopped, by ptrace, but GDB is trying to work out if it
should really maintain the stop, or resume the inferior). So in GDB's
internal data store, the inferior (thread) is still "running" and will
be so until a firm stop decision is reached. Because of this
intermediate state, you cannot issue cannot step/next/continue/finish or
other inferior control commands. This is really a very simplistic view
of what is going on. I've glossed over a lot.
Post by Kevin Pouget
* in event.stop.connect() that's possible, but I can't
`gdb.execute("continue")' the execution because I would miss any
"non-python" reasons to stop (ie, a user-breakpoint, a signal ...)
The only thing I can think to do here is set some of your own data in
your Python module. Breakpoint.stop sets some flag or data, and the
event.connect can pick up on that. But there are gaps in the API. File
bugs, I look there daily.

I hope this helped some!

Cheers,

Phil
Tom Tromey
2011-04-20 15:39:30 UTC
Permalink
Kevin> * in MyBreakpoint.stop(), I can say continue/stop the inferior, but I
Kevin> can't run "finish" (or "next" or what ever, I guess), because the
Kevin> inferior is still considered as running:

I think it would be pretty difficult to get this to work.

Kevin> * in event.stop.connect() that's possible, but I can't
Kevin> `gdb.execute("continue")' the execution because I would miss any
Kevin> "non-python" reasons to stop (ie, a user-breakpoint, a signal ...)

I think you should be able to examine the stop event object to see what
caused the event. If the event is a gdb.BreakpointEvent, and if
event.breakpoint is your breakpoint, then do what you want.

Tom
Kevin Pouget
2011-04-21 13:01:19 UTC
Permalink
Post by Tom Tromey
Kevin> * in event.stop.connect() that's possible, but I can't
Kevin> `gdb.execute("continue")' the execution because I would miss any
Kevin> "non-python" reasons to stop (ie, a user-breakpoint, a signal ...)
I think you should be able to examine the stop event object to see what
caused the event.  If the event is a gdb.BreakpointEvent, and if
event.breakpoint is your breakpoint, then do what you want.
I'm afraid that's impossible to do it with the current implementation, look:

def handle_double_stop(event):
print "Stop event: ", event.breakpoint.number
gdb.events.stop.connect(handle_double_stop)

(gdb) break main
Breakpoint 1
(gdb) break main
Breakpoint 2
(gdb) run
Stop event: 1

... as far as I know, there is no 'easy' way to know that Bp 2 was hit as well
Kevin Pouget
2011-04-21 14:31:37 UTC
Permalink
before I start any implementation, would you agree if I change
int
emit_stop_event (struct bpstats *bs, enum target_signal stop_signal)
...
 if (bs && bs->breakpoint_at
     && bs->breakpoint_at->py_bp_object)
   {
     stop_event_obj = create_breakpoint_event_object ((PyObject *) bs
                                                      ->breakpoint_at
                                                      ->py_bp_object);
   }
so that it browses the `bs->next' list  and returns a python tuple
instead of a single object ?
    /* Linked list because there can be more than one breakpoint at
       the same place, and a bpstat reflects the fact that all have
       been hit.  */
    bpstat next;
Cordially,

Kevin
Post by Tom Tromey
Kevin> * in event.stop.connect() that's possible, but I can't
Kevin> `gdb.execute("continue")' the execution because I would miss any
Kevin> "non-python" reasons to stop (ie, a user-breakpoint, a signal ...)
I think you should be able to examine the stop event object to see what
caused the event.  If the event is a gdb.BreakpointEvent, and if
event.breakpoint is your breakpoint, then do what you want.
       print "Stop event: ", event.breakpoint.number
gdb.events.stop.connect(handle_double_stop)
(gdb) break main
Breakpoint 1
(gdb) break main
Breakpoint 2
(gdb) run
Stop event: 1
... as far as I know, there is no 'easy' way to know that Bp 2 was hit as well
Tom Tromey
2011-04-20 15:20:01 UTC
Permalink
Sorry, I didn't notice this until your follow-up.

Kevin> thanks for your answer, the patch seems to feature what I was
Kevin> looking for. I'm a bit surprised that the stop/continue decision
Kevin> can't be done in this breakpoint_stop handler, but I guess that
Kevin> was too complicated ?

We intentionally made event handlers not return values.

My recollection is that this was done both due to simplicity and also
out of the desire to make it clear that event handlers ought to be
logically separate. Once handlers affect other handlers, the coding
gets more complicated, and it seems better to associate such actions
more directly with their causes.

Tom
Kevin Pouget
2011-03-21 14:19:00 UTC
Permalink
cool, that's working perfectly now !
<python breakpoint outputs>
[Switching to Thread 0x7ffff7de1700 (LWP 2417)]
Breakpoint -9, rdb_notify_event () at replay_db.c:11
11 void rdb_notify_event() {}
is there any way / woudn't it be nice to have the ability to disable
the breakpoint hit outputs? at least for the 'internal' breakpoints,
which shouldn't be visible to the user ?

Thanks,

Kevin
Post by Kevin Pouget
Hello,
I've tried the GDB python interface today, which seems quite
efficient, but there is one important thing I couldn't figure out by
how to restart GDB when a[n internal] breakpoint is hit ?
http://sourceware.org/ml/gdb-patches/2011-03/msg00656.html
The implementation of the "stop" API.  The idea behind this is that if a
breakpoint is hit, that is tracked from Python and has an implemented
stop method, that method would be called.  You can do what you like in
that method.  If you want the inferior process to continue, return True
otherwise return False (and print out/do whatever else you need to do in
Python).
Because internal breakpoints are not tracked by default in the Python
Breakpoint API, you would have to create your breakpoint by
instantiating a gdb.Breakpoint class, and pass the keyword
internal=True.
So, long story short soon.  OTOH I'm not sure if there is a unhacky way
of doing it now.  You could use a convenience function, but that patch
is replacing that hacky way.
Cheers
Phil
Post by Kevin Pouget
        print "event type: stop"
        print "stop reason: breakpoint"
        print "breakpoint number: %s" % (event.breakpoint.number)
            print "thread num: %s" % (event.inferior_thread.num);
            print "all threads stopped"
gdb.events.stop.connect (breakpoint_stop_handler)
which where I get the notification of the stop, but I'd to be able to
tell GDB something like
enum bpstat_what_main_action {
    /* Remove breakpoints, single step once, then put them back in and
       go back to what we were doing.  It's possible that this should
       be removed from the main_action and put into a separate field,
       to more cleanly handle  BPSTAT_WHAT_CLEAR_LONGJMP_RESUME_SINGLE.  */
    BPSTAT_WHAT_SINGLE,
    /* Stop silently.  */
    BPSTAT_WHAT_STOP_SILENT,
    /* Stop and print.  */
    BPSTAT_WHAT_STOP_NOISY,
...
}
to continue silently, stop silently or print the breakpoint hit.
is it possible at this stage ?
Thanks,
Kevin
Phil Muldoon
2011-03-21 14:38:58 UTC
Permalink
Post by Kevin Pouget
cool, that's working perfectly now !
<python breakpoint outputs>
[Switching to Thread 0x7ffff7de1700 (LWP 2417)]
Breakpoint -9, rdb_notify_event () at replay_db.c:11
11 void rdb_notify_event() {}
is there any way / woudn't it be nice to have the ability to disable
the breakpoint hit outputs? at least for the 'internal' breakpoints,
which shouldn't be visible to the user ?
You can set the breakpoint to silent:

(gdb) python b = gdb.Breakpoint("hello.c:5", internal=True)
(gdb) run
Breakpoint -1, main () at /home/pmuldoon/hello.c:5
5 printf("Hello world!\n");
(gdb) py b.silent = True
(gdb) run
(gdb) list
(gdb) where
#0 main () at /home/pmuldoon/hello.c:5

There might be a case for setting the breakpoint to 'silent' in the
breakpoint constructor:

python b = gdb.Breakpoint("hello.c:5", internal=True, silent=True)

or just making internal breakpoints silent by default.

I'll implement either. What do you think?

Cheers,

Phil
Kevin Pouget
2011-03-21 15:10:40 UTC
Permalink
Post by Phil Muldoon
(gdb) py b.silent = True
very nice! I was just reading an old post
http://sourceware.org/ml/gdb/2007-04/msg00127.html about a plugin vs
scripting interface, Python finally get the point, it's now easier to
write a Python script which handles breakpoints than a GDB patch!
Post by Phil Muldoon
There might be a case for setting the breakpoint to 'silent' in the
python b = gdb.Breakpoint("hello.c:5", internal=True, silent=True)
or just making internal breakpoints silent by default.
I'll implement either.  What do you think?
from my perspective, _internal breakpoints_ should be silent by
default, because they're 'internal', as their name says.

Option 1) is quite similar `b.silent`, so it's up to you


Cordially,

Kevin
Loading...