From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 27682 invoked by alias); 12 May 2011 10:38:55 -0000 Received: (qmail 27652 invoked by uid 22791); 12 May 2011 10:38:50 -0000 X-SWARE-Spam-Status: No, hits=-0.9 required=5.0 tests=AWL,BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,KAM_STOCKGEN,RCVD_IN_DNSWL_LOW,RFC_ABUSE_POST,TW_BJ,TW_BP,TW_EG,TW_YB,T_TO_NO_BRKTS_FREEMAIL X-Spam-Check-By: sourceware.org Received: from mail-vw0-f41.google.com (HELO mail-vw0-f41.google.com) (209.85.212.41) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 12 May 2011 10:38:02 +0000 Received: by vws4 with SMTP id 4so1329206vws.0 for ; Thu, 12 May 2011 03:38:01 -0700 (PDT) Received: by 10.52.92.19 with SMTP id ci19mr49567vdb.158.1305196680218; Thu, 12 May 2011 03:38:00 -0700 (PDT) MIME-Version: 1.0 Received: by 10.220.188.139 with HTTP; Thu, 12 May 2011 03:37:40 -0700 (PDT) In-Reply-To: References: From: Kevin Pouget Date: Thu, 12 May 2011 10:38:00 -0000 Message-ID: Subject: Re: [RFC] Python Finish Breakpoints To: pmuldoon@redhat.com Cc: gdb-patches@sourceware.org Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-05/txt/msg00305.txt.bz2 Hello, here is an updated version of the patch which address all your remarks >>>> there is one problem behind this function, I had to change the code: >>>> >>>> +++ b/gdb/infrun.c >>>> @@ -5826,7 +5826,7 @@ normal_stop (void) >>>> /* Save the function value return registers, if we care. >>>> We might be about to restore their previous contents. */ >>>> - if (inferior_thread ()->control.proceed_to_finish) >>>> + /*if (inferior_thread ()->control.proceed_to_finish)*/ >>>> ... >>>> stop_registers =3D regcache_dup (get_current_regcache ()); >>>> >>>> to correctly set `stop_registers', but I don't really know the >>>> implication of this modification ... >> >> I don't think you want to universally modify this condition (I am not >> sure of the implications either, maybe Pedro will have some more >> in-depth info). Anyway given this case, I would create a function >> called something like "gdbpy_is_finish_bp" in python.c and add that to >> the condition makeup. > > sounds like a good idea, I don't want to change blindly a code which > was working correctly, so gdbpy_is_finish_bp should do the trick I changed it to: - if (inferior_thread ()->control.proceed_to_finish) + if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpst= at) + || inferior_thread ()->control.proceed_to_finish) which only verifies the IF condition when of the of breakpoint of the stop_bpstat list is a FinishBreakpoint (the function return 0 when Python is not enabled) >>> +extern struct value *get_return_value (struct type *func_type, >>> + struct type *value_type); >>> + >>> /* Address at which inferior stopped. */ >> >> >> This patch context is not wide enough to know, but I this function I >> think should be placed next to the corresponding print_ function. `print_return_value' is declared as static, so I added get_return_value at the end of the function prototypes >> I'm not sure on whether we should be creating header files for >> individual Python objects. Normally, depending on the scope/context of >> the exported functions and macros we place them in >> python/python-internal.h. I'll defer this change to Tom's wisdom. > > my implementation is based on (what I understood of) existing files, > namely py-breakpoint.h here; and I understand only now that > python/python-internal.h plays the role of head for all the python > files. I'll re-organize it, depending on what Tom will say I removed this py-breakpoint.h header and moved the appropriate lines to python-internal.h >>> + return; >>> + >>> + Py_INCREF (&finish_breakpoint_object_type); >>> + PyModule_AddObject (gdb_module, "FinishBreakpoint", >>> + (PyObject *) &finish_breakpoint_object_type); >>> + >>> + observer_attach_normal_stop (bpfinishpy_handle_stop); >>> +} >>> + >>> +static PyGetSetDef finish_breakpoint_object_getset[] =3D { >>> + { "out_of_scoped", bpfinishpy_get_outofscoped, bpfinishpy_set_outofs= coped, >> >> Sounds weird, should it be "out_of_scope"? > > yeah, I wasn't sure how 'out_of_scoped" would sound to a (native) > English hear, "out_of_scope" is the name of the function call when GDB > notices that the frame of the FinishBreakpoint is not anymore in the > callstack, and this flag indicates if the Python script already knows > it or not. `out_of_scope_notification' might be a better naming, > although a bit long it might be more intuitive now, the flag is named `out_of_scope_notif' and indicates if the `out_of_scope' callback should be triggered when GDB notices that the BP is not in the callstack. The attribute is read/writable, so that the user can disable and re-enable it when necessary. It is set to FALSE when the breakpoint is hit and when the callback is triggered. the method check_scope() will force the checking of the scope (normal checking occurs during the `inferior_stop' observer notification) and return TRUE is the callback was triggered (FALSE otherwise) I hope that this implementation will look better to you, and wait for a maintainer review before preparing the documentation/changelog thanks, Kevin -- =46rom 492216ccd8250de3f13038b67b24728038f1e9b7 Mon Sep 17 00:00:00 2001 From: Kevin Pouget Date: Mon, 9 May 2011 15:20:48 -0400 Subject: [PATCH] Python Finish Breakpoints --- gdb/Makefile.in | 6 + gdb/breakpoint.c | 2 +- gdb/breakpoint.h | 10 + gdb/infcmd.c | 20 +- gdb/inferior.h | 3 + gdb/infrun.c | 4 +- gdb/python/py-breakpoint.c | 40 +-- gdb/python/py-finishbreakpoint.c | 436 +++++++++++++++++= ++++ gdb/python/py-frame.c | 32 +- gdb/python/python-internal.h | 44 ++- gdb/python/python.c | 6 + gdb/python/python.h | 2 + gdb/testsuite/gdb.python/py-breakpoint.exp | 7 +- gdb/testsuite/gdb.python/py-finish-breakpoint.c | 42 ++ gdb/testsuite/gdb.python/py-finish-breakpoint.exp | 92 +++++ gdb/testsuite/gdb.python/py-finish-breakpoint.py | 39 ++ 16 files changed, 721 insertions(+), 64 deletions(-) create mode 100644 gdb/python/py-finishbreakpoint.c create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.c create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.exp create mode 100644 gdb/testsuite/gdb.python/py-finish-breakpoint.py diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 5bab360..2507938 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -285,6 +285,7 @@ SUBDIR_PYTHON_OBS =3D \ py-evtregistry.o \ py-evts.o \ py-exitedevent.o \ + py-finishbreakpoint.o \ py-frame.o \ py-function.o \ py-inferior.o \ @@ -315,6 +316,7 @@ SUBDIR_PYTHON_SRCS =3D \ python/py-evtregistry.c \ python/py-evts.c \ python/py-exitedevent.c \ + python/py-finishbreakpoint.c \ python/py-frame.c \ python/py-function.c \ python/py-inferior.c \ @@ -2062,6 +2064,10 @@ py-exitedevent.o: $(srcdir)/python/py-exitedevent.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-exitedevent.c $(POSTCOMPILE) +py-finishbreakpoint.o: $(srcdir)/python/py-finishbreakpoint.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-finishbreakpoint.c + $(POSTCOMPILE) + py-frame.o: $(srcdir)/python/py-frame.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-frame.c $(POSTCOMPILE) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index b5fc448..eff5e23 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -7330,7 +7330,7 @@ bp_loc_is_permanent (struct bp_location *loc) as textual description of the location, and COND_STRING as condition expression. */ -static void +void create_breakpoint_sal (struct gdbarch *gdbarch, struct symtabs_and_lines sals, char *addr_string, char *cond_string, diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 7a9c2d4..a003651 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -986,6 +986,16 @@ extern int create_breakpoint (struct gdbarch *gdbarch, char *arg, int enabled, int internal); +extern void create_breakpoint_sal (struct gdbarch *gdbarch, + struct symtabs_and_lines sals, + char *addr_string, + char *cond_string, + enum bptype type, enum bpdisp dispositi= on, + int thread, int task, int ignore_count, + struct breakpoint_ops *ops, int from_tt= y, + int enabled, int internal, + int display_canonical); + extern void insert_breakpoints (void); extern int remove_breakpoints (void); diff --git a/gdb/infcmd.c b/gdb/infcmd.c index fce1e8f..1b8c612 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -1378,14 +1378,12 @@ advance_command (char *arg, int from_tty) until_break_command (arg, from_tty, 1); } =0C -/* Print the result of a function at the end of a 'finish' command. */ +/* Returns the value of the result at the end of a 'finish' command/BP. */ -static void -print_return_value (struct type *func_type, struct type *value_type) +struct value * +get_return_value (struct type *func_type, struct type *value_type) { struct gdbarch *gdbarch =3D get_regcache_arch (stop_registers); - struct cleanup *old_chain; - struct ui_stream *stb; struct value *value; CHECK_TYPEDEF (value_type); @@ -1415,6 +1413,18 @@ print_return_value (struct type *func_type, struct type *value_type) internal_error (__FILE__, __LINE__, _("bad switch")); } + return value; +} + +/* Print the result of a function at the end of a 'finish' command. */ + +static void +print_return_value (struct type *func_type, struct type *value_type) +{ + struct value *value =3D get_return_value(func_type, value_type); + struct cleanup *old_chain; + struct ui_stream *stb; + if (value) { struct value_print_options opts; diff --git a/gdb/inferior.h b/gdb/inferior.h index f8adb6c..b8d5b13 100644 --- a/gdb/inferior.h +++ b/gdb/inferior.h @@ -285,6 +285,9 @@ extern void detach_command (char *, int); extern void notice_new_inferior (ptid_t, int, int); +extern struct value *get_return_value (struct type *func_type, + struct type *value_type); + /* Address at which inferior stopped. */ extern CORE_ADDR stop_pc; diff --git a/gdb/infrun.c b/gdb/infrun.c index 2d6d523..3cf1cd5 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -54,6 +54,7 @@ #include "inline-frame.h" #include "jit.h" #include "tracepoint.h" +#include "python/python.h" /* Prototypes for local functions */ @@ -5826,7 +5827,8 @@ normal_stop (void) /* Save the function value return registers, if we care. We might be about to restore their previous contents. */ - if (inferior_thread ()->control.proceed_to_finish) + if (gdbpy_is_stopped_at_finish_bp (inferior_thread ()->control.stop_bpst= at) + || inferior_thread ()->control.proceed_to_finish) { /* This should not be necessary. */ if (stop_registers) diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c index 9c33848..f9a3a8f 100644 --- a/gdb/python/py-breakpoint.c +++ b/gdb/python/py-breakpoint.c @@ -31,52 +31,18 @@ #include "arch-utils.h" #include "language.h" -static PyTypeObject breakpoint_object_type; +PyTypeObject breakpoint_object_type; /* Number of live breakpoints. */ static int bppy_live; /* Variables used to pass information between the Breakpoint constructor and the breakpoint-created hook function. */ -static breakpoint_object *bppy_pending_object; +breakpoint_object *bppy_pending_object; /* Function that is called when a Python condition is evaluated. */ static char * const stop_func =3D "stop"; -struct breakpoint_object -{ - PyObject_HEAD - - /* The breakpoint number according to gdb. */ - int number; - - /* The gdb breakpoint object, or NULL if the breakpoint has been - deleted. */ - struct breakpoint *bp; -}; - -/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python - exception if it is invalid. */ -#define BPPY_REQUIRE_VALID(Breakpoint) \ - do { \ - if ((Breakpoint)->bp =3D=3D NULL) \ - return PyErr_Format (PyExc_RuntimeError, \ - _("Breakpoint %d is invalid."), \ - (Breakpoint)->number); \ - } while (0) - -/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python - exception if it is invalid. This macro is for use in setter functions.= */ -#define BPPY_SET_REQUIRE_VALID(Breakpoint) \ - do { \ - if ((Breakpoint)->bp =3D=3D NULL) \ - { \ - PyErr_Format (PyExc_RuntimeError, _("Breakpoint %d is invalid."), \ - (Breakpoint)->number); \ - return -1; \ - } \ - } while (0) - /* This is used to initialize various gdb.bp_* constants. */ struct pybp_code { @@ -971,7 +937,7 @@ static PyMethodDef breakpoint_object_methods[] =3D { NULL } /* Sentinel. */ }; -static PyTypeObject breakpoint_object_type =3D +PyTypeObject breakpoint_object_type =3D { PyObject_HEAD_INIT (NULL) 0, /*ob_size*/ diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpo= int.c new file mode 100644 index 0000000..8c1045b --- /dev/null +++ b/gdb/python/py-finishbreakpoint.c @@ -0,0 +1,436 @@ +/* Python interface to finish breakpoints + + Copyright (C) 2011 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . = */ + + + +#include "defs.h" +#include "exceptions.h" +#include "python-internal.h" +#include "breakpoint.h" +#include "frame.h" +#include "gdbthread.h" +#include "arch-utils.h" +#include "language.h" +#include "observer.h" +#include "inferior.h" + +static PyTypeObject finish_breakpoint_object_type; + +/* Function that is called when a Python finish bp is found out of scope. = */ +static char * const outofscope_func =3D "out_of_scope"; + +/* struct implementing the gdb.FinishBreakpoint object by extending + the gdb.Breakpoint class. */ +struct finish_breakpoint_object +{ + /* gdb.Breakpoint base class. */ + struct breakpoint_object py_bp; + /* Flag indicating that the BP is out of the callstack and Python callba= ck + has been triggered. */ + int out_of_scope_notif; + /* The function finished by this breakpoint. */ + struct symbol *function; +}; + +/* Python function to set the 'out_of_scope_notif' attribute of + FinishBreakpoint. */ + +static int +bpfinishpy_set_outofscope_notif (PyObject *self, PyObject *newvalue, + void *closure) +{ + struct finish_breakpoint_object *self_finishbp =3D + (struct finish_breakpoint_object *) self; + int cmp; + + BPPY_SET_REQUIRE_VALID (&self_finishbp->py_bp); + + if (newvalue =3D=3D NULL) + { + PyErr_SetString (PyExc_TypeError, + _("Cannot delete `out_of_scope_notif' attribute= .")); + return -1; + } + else if (!PyBool_Check (newvalue)) + { + PyErr_SetString (PyExc_TypeError, + _("The value of `out_of_scope_notif' must be a boolean= .")); + return -1; + } + + cmp =3D PyObject_IsTrue (newvalue); + if (cmp < 0) + return -1; + else + self_finishbp->out_of_scope_notif =3D cmp; + + return 0; +} + +/* Python function to update and get the 'out_of_scope_notif' + attribute of FinishBreakpoint. */ + +static PyObject * +bpfinishpy_get_outofscope_notif (PyObject *self, void *closure) +{ + struct finish_breakpoint_object *self_finishbp =3D + (struct finish_breakpoint_object *) self; + + BPPY_REQUIRE_VALID (&self_finishbp->py_bp); + + if (self_finishbp->out_of_scope_notif) + Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +/* Python function to get the 'return_value' attribute of + FinishBreakpoint. */ + +static PyObject * +bpfinishpy_get_returnvalue (PyObject *self, void *closure) +{ + struct finish_breakpoint_object *self_finishbp =3D + (struct finish_breakpoint_object *) self; + + BPPY_REQUIRE_VALID (&self_finishbp->py_bp); + + if (self_finishbp->function =3D=3D NULL) + goto return_none; + + /* Ensure that GDB is stopped at this FinishBreakpoint. */ + if (inferior_thread ()->control.stop_bpstat !=3D NULL) + { + bpstat bs; + + for(bs =3D inferior_thread ()->control.stop_bpstat; + bs; bs =3D bs->next) + { + struct breakpoint *bp =3D bs->breakpoint_at; + if (bp !=3D NULL + && (PyObject *) bp->py_bp_object =3D=3D self) + { + struct type *v_type; + + v_type =3D TYPE_TARGET_TYPE (SYMBOL_TYPE + (self_finishbp->function)); + if (!v_type) + internal_error (__FILE__, __LINE__, + _("bpfinishpy_get_returnvalue: function has no target typ= e")); + + if (TYPE_CODE (v_type) !=3D TYPE_CODE_VOID) + { + struct value *ret =3D get_return_value + (SYMBOL_TYPE (self_finishbp->function), v_type); + PyObject *return_value =3D value_to_value_object (ret); + + Py_INCREF (return_value); + return return_value; + } + else + goto return_none; + } + } + } + +return_none: + Py_RETURN_NONE; +} + +/* Called when GDB notices that the finish breakpoint BP_OBJ is out of + the current callstack. If BP_OBJ has the attribute OUT_OF_SCOPE_NOTIF + and its value is FALSE, trigger the method OUT_OF_SCOPE and set the flag + to TRUE. */ + +static void +gdbpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj) +{ + breakpoint_object *bp_obj =3D (breakpoint_object *) bpfinish_obj; + PyObject *py_obj =3D (PyObject *) bp_obj ; + + bpfinish_obj->out_of_scope_notif =3D 0; + + if (PyObject_HasAttrString (py_obj, outofscope_func)) + { + struct gdbarch *garch =3D bp_obj->bp->gdbarch ? + bp_obj->bp->gdbarch : get_current_arch (); + struct cleanup *cleanup =3D ensure_python_env (garch, current_langua= ge); + + if (!PyObject_CallMethod (py_obj, outofscope_func, NULL)) + gdbpy_print_stack (); + + do_cleanups (cleanup); + } +} + +/* Python function to check if the FinishBreakpoint SELF is not anymore + in the callstack. Triggers self->out_of_scope */ +static PyObject * +bpfinishpy_check_scope (PyObject *self, PyObject *args) +{ + struct finish_breakpoint_object *self_finishbp =3D + (struct finish_breakpoint_object *) self; + + if (self_finishbp->out_of_scope_notif + && frame_find_by_id(self_finishbp->py_bp.bp->frame_id) =3D=3D NULL) + { + gdbpy_out_of_scope (self_finishbp); + + Py_RETURN_TRUE; + } + Py_RETURN_FALSE; +} + +/* Python function to create a new breakpoint. */ + +static int +bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs) +{ + static char *keywords[] =3D { "frame", "internal", NULL }; + breakpoint_object *self_bp =3D (breakpoint_object *) self; + struct finish_breakpoint_object *self_bpfinish =3D + (struct finish_breakpoint_object *) self; + int type =3D bp_breakpoint; + PyObject *frame_obj =3D NULL; + struct frame_info *frame, *prev_frame; + struct frame_id frame_id; + struct symtabs_and_lines sals; + struct symtab_and_line sal; + PyObject *internal =3D NULL; + int internal_bp =3D 0; + CORE_ADDR pc ; + volatile struct gdb_exception except; + + if (! PyArg_ParseTupleAndKeywords (args, kwargs, "O|O", keywords, + &frame_obj, &internal)) + return -1; + + if (!frame_obj) + goto invalid_frame; + + frame =3D frame_object_to_frame_info (frame_obj); + if (frame =3D=3D NULL) + goto invalid_frame; + + prev_frame =3D get_prev_frame(frame); + if (prev_frame =3D=3D 0) + { + PyErr_SetString (PyExc_ValueError, + _("\"FinishBreakpoint\" not meaningful in the outermost frame."= )); + return -1; + } + + frame_id =3D get_frame_id (prev_frame); + if (frame_id_eq(frame_id, null_frame_id)) + goto invalid_frame; + + pc =3D get_frame_pc (prev_frame); + + sal =3D find_pc_line (pc, 0); + sal.pc =3D pc; + sals.sals =3D &sal; + sals.nelts =3D 1; + + /* Find the function we will return from. */ + self_bpfinish->function =3D find_pc_function (get_frame_pc (frame)); + + if (internal) + { + internal_bp =3D PyObject_IsTrue (internal); + if (internal_bp =3D=3D -1) + { + PyErr_SetString (PyExc_ValueError, + _("The value of `internal' must be a boolean.")= ); + return -1; + } + } + + bppy_pending_object =3D self_bp; + bppy_pending_object->number =3D -1; + bppy_pending_object->bp =3D NULL; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + create_breakpoint_sal (python_gdbarch, sals, NULL, NULL, + bp_breakpoint, disp_donttouch, -1, + 0, 0, NULL, 0, 1, internal_bp, 0) ; + } + if (except.reason < 0) + { + PyErr_Format (except.reason =3D=3D RETURN_QUIT + ? PyExc_KeyboardInterrupt : PyExc_RuntimeError, + "%s", except.message); + return -1; + } + BPPY_SET_REQUIRE_VALID (self_bp); + + self_bp->bp->frame_id =3D frame_id; + + self_bpfinish->out_of_scope_notif =3D 1; + + return 0; + +invalid_frame: + PyErr_SetString (PyExc_ValueError, + _("Invalid ID for the `frame' object.")); + return -1; +} + +/* Returns 1 if OBJ is not NULL and references a FinishBreakpoint object. = */ + +static int +bpfinishpy_is_finish_bp (PyObject *obj) +{ + return obj !=3D NULL + && PyObject_TypeCheck (obj, &finish_breakpoint_object_type) ; +} + +/* Returns 1 if STOP_BPSTAT contains a FinishBreakpoint, 0 if not. */ + +int +gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat) +{ + bpstat bs; + + for (bs =3D stop_bpstat; bs; bs =3D bs->next) + { + if (bpfinishpy_is_finish_bp((PyObject *) + bs->breakpoint_at->py_bp_object)) + return 1; + } + + return 0; +} + +/* Callback for `bpfinishpy_detect_out_scope'. Triggers Python's + `B->out_of_scope' function if B is a FinishBreakpoint out of its + scope or turn of out_of_scope notification if B has been hit. */ + +static int +bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args) +{ + struct breakpoint *bp_stopped =3D (struct breakpoint *) args; + PyObject *py_bp =3D (PyObject *) b->py_bp_object; + struct gdbarch *garch =3D b->gdbarch ? b->gdbarch : get_current_arch (); + struct cleanup *cleanup =3D ensure_python_env (garch, current_language); + + /* Trigger out_of_scope if this is a FinishBreakpoint its frame is not i= n the + current callstack and the notification has not been sent yet. */ + if (bpfinishpy_is_finish_bp (py_bp)) + { + struct finish_breakpoint_object *finish_bp =3D + (struct finish_breakpoint_object *) py_bp; + + if (b =3D=3D bp_stopped) + finish_bp->out_of_scope_notif =3D 0; + else if (frame_find_by_id(b->frame_id) =3D=3D NULL + && finish_bp->out_of_scope_notif) + { + gdbpy_out_of_scope (finish_bp); + } + } + + do_cleanups (cleanup); + + return 0; +} + +/* Attached to `stop' notifications, check if the execution has run out + out of the scope of any FinishBreakpoint before it has been hit. */ + +static void +bpfinishpy_handle_stop (struct bpstats *bs, int print_frame) +{ + iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, + bs =3D=3D NULL ? NULL : bs->breakpoint_at); +} + +/* Initialize the Python finish breakpoint code. */ + +void +gdbpy_initialize_finishbreakpoints (void) +{ + if (PyType_Ready (&finish_breakpoint_object_type) < 0) + return; + + Py_INCREF (&finish_breakpoint_object_type); + PyModule_AddObject (gdb_module, "FinishBreakpoint", + (PyObject *) &finish_breakpoint_object_type); + + observer_attach_normal_stop (bpfinishpy_handle_stop); +} + +static PyMethodDef finish_breakpoint_object_methods[] =3D { + { "check_scope", bpfinishpy_check_scope, METH_NOARGS, + "check_scope () -> Boolean.\n\ +Return true if out_of_scope() has been triggered, false if not." }, + {NULL} /* Sentinel */ +}; + +static PyGetSetDef finish_breakpoint_object_getset[] =3D { + { "out_of_scope_notif", bpfinishpy_get_outofscope_notif, bpfinishpy_set_outofscope_notif, + "Boolean telling whether the breakpoint is still within the scope \ +of the current callstack.", NULL }, + { "return_value", bpfinishpy_get_returnvalue, NULL, + "gdb.Value object representing the return value, if any. \ +None otherwise.", NULL }, + { NULL } /* Sentinel. */ +}; + +static PyTypeObject finish_breakpoint_object_type =3D +{ + PyObject_HEAD_INIT (NULL) + 0, /*ob_size*/ + "gdb.FinishBreakpoint", /*tp_name*/ + sizeof (struct finish_breakpoint_object), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro */ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "GDB finish breakpoint object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + finish_breakpoint_object_methods, /* tp_methods */ + 0, /* tp_members */ + finish_breakpoint_object_getset,/* tp_getset */ + &breakpoint_object_type, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + bpfinishpy_init, /* tp_init */ + 0, /* tp_alloc */ + 0 /* tp_new */ +}; diff --git a/gdb/python/py-frame.c b/gdb/python/py-frame.c index d7128a9..2109c73 100644 --- a/gdb/python/py-frame.c +++ b/gdb/python/py-frame.c @@ -60,9 +60,10 @@ static PyTypeObject frame_object_type; object. If the frame doesn't exist anymore (the frame id doesn't correspond to any frame in the inferior), returns NULL. */ -static struct frame_info * -frame_object_to_frame_info (frame_object *frame_obj) +struct frame_info * +frame_object_to_frame_info (PyObject *obj) { + frame_object *frame_obj =3D (frame_object *) obj; struct frame_info *frame; frame =3D frame_find_by_id (frame_obj->frame_id); @@ -103,7 +104,7 @@ frapy_is_valid (PyObject *self, PyObject *args) { struct frame_info *frame; - frame =3D frame_object_to_frame_info ((frame_object *) self); + frame =3D frame_object_to_frame_info (self); if (frame =3D=3D NULL) Py_RETURN_FALSE; @@ -124,7 +125,7 @@ frapy_name (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); find_frame_funname (frame, &name, &lang, NULL); } @@ -153,7 +154,7 @@ frapy_type (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); type =3D get_frame_type (frame); } @@ -174,7 +175,7 @@ frapy_unwind_stop_reason (PyObject *self, PyObject *arg= s) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); } GDB_PY_HANDLE_EXCEPTION (except); @@ -195,7 +196,7 @@ frapy_pc (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); pc =3D get_frame_pc (frame); } @@ -216,7 +217,7 @@ frapy_block (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); block =3D get_frame_block (frame, NULL); } GDB_PY_HANDLE_EXCEPTION (except); @@ -257,7 +258,7 @@ frapy_function (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); sym =3D find_pc_function (get_frame_address_in_block (frame)); } @@ -319,7 +320,7 @@ frapy_older (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); prev =3D get_prev_frame (frame); if (prev) @@ -348,7 +349,7 @@ frapy_newer (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); next =3D get_next_frame (frame); if (next) @@ -377,7 +378,7 @@ frapy_find_sal (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); find_frame_sal (frame, &sal); sal_obj =3D symtab_and_line_to_sal_object (sal); @@ -433,7 +434,7 @@ frapy_read_var (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); if (!block) block =3D get_frame_block (frame, NULL); @@ -461,7 +462,7 @@ frapy_read_var (PyObject *self, PyObject *args) TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID ((frame_object *) self, frame); + FRAPY_REQUIRE_VALID (self, frame); val =3D read_var_value (var, frame); } @@ -484,12 +485,11 @@ static PyObject * frapy_select (PyObject *self, PyObject *args) { struct frame_info *fi; - frame_object *frame =3D (frame_object *) self; volatile struct gdb_exception except; TRY_CATCH (except, RETURN_MASK_ALL) { - FRAPY_REQUIRE_VALID (frame, fi); + FRAPY_REQUIRE_VALID (self, fi); select_frame (fi); } diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index d3cb788..31ec1b8 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -114,9 +114,47 @@ extern PyTypeObject symbol_object_type; extern PyTypeObject event_object_type; extern PyTypeObject events_object_type; extern PyTypeObject stop_event_object_type; +extern PyTypeObject breakpoint_object_type; + +typedef struct breakpoint_object +{ + PyObject_HEAD + + /* The breakpoint number according to gdb. */ + int number; + + /* The gdb breakpoint object, or NULL if the breakpoint has been + deleted. */ + struct breakpoint *bp; +} breakpoint_object; + +/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python + exception if it is invalid. */ +#define BPPY_REQUIRE_VALID(Breakpoint) \ + do { \ + if ((Breakpoint)->bp =3D=3D NULL) = \ + return PyErr_Format (PyExc_RuntimeError, \ + _("Breakpoint %d is invalid."), \ + (Breakpoint)->number); \ + } while (0) + +/* Require that BREAKPOINT be a valid breakpoint ID; throw a Python + exception if it is invalid. This macro is for use in setter functions.= */ +#define BPPY_SET_REQUIRE_VALID(Breakpoint) \ + do { \ + if ((Breakpoint)->bp =3D=3D NULL) = \ + { \ + PyErr_Format (PyExc_RuntimeError, _("Breakpoint %d is invalid.")= , \ + (Breakpoint)->number); \ + return -1; \ + } \ + } while (0) + + +/* Variables used to pass information between the Breakpoint + constructor and the breakpoint-created hook function. */ +extern breakpoint_object *bppy_pending_object; -/* Defined in py-breakpoint.c */ -typedef struct breakpoint_object breakpoint_object; typedef struct { @@ -161,6 +199,7 @@ PyObject *block_to_block_object (struct block *block, struct objfile *objfile); PyObject *value_to_value_object (struct value *v); PyObject *type_to_type_object (struct type *); PyObject *frame_info_to_frame_object (struct frame_info *frame); +struct frame_info *frame_object_to_frame_info (PyObject *frame_obj); PyObject *pspace_to_pspace_object (struct program_space *); PyObject *pspy_get_printers (PyObject *, void *); @@ -194,6 +233,7 @@ void gdbpy_initialize_functions (void); void gdbpy_initialize_pspace (void); void gdbpy_initialize_objfile (void); void gdbpy_initialize_breakpoints (void); +void gdbpy_initialize_finishbreakpoints (void); void gdbpy_initialize_lazy_string (void); void gdbpy_initialize_parameters (void); void gdbpy_initialize_thread (void); diff --git a/gdb/python/python.c b/gdb/python/python.c index 8a7bc66..97007b3 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -926,6 +926,11 @@ gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj) "scripting is not supported.")); } +int +gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat) +{ + return 0; +} #endif /* HAVE_PYTHON */ =0C @@ -1060,6 +1065,7 @@ Enables or disables printing of Python stack traces."= ), gdbpy_initialize_pspace (); gdbpy_initialize_objfile (); gdbpy_initialize_breakpoints (); + gdbpy_initialize_finishbreakpoints (); gdbpy_initialize_lazy_string (); gdbpy_initialize_thread (); gdbpy_initialize_inferior (); diff --git a/gdb/python/python.h b/gdb/python/python.h index ce0eb35..aa2d096 100644 --- a/gdb/python/python.h +++ b/gdb/python/python.h @@ -21,6 +21,7 @@ #define GDB_PYTHON_H #include "value.h" +#include "breakpoint.h" struct breakpoint_object; @@ -47,4 +48,5 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj); int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj); +int gdbpy_is_stopped_at_finish_bp (bpstat stop_bpstat); #endif /* GDB_PYTHON_H */ diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp index f0a83f1..8755888 100644 --- a/gdb/testsuite/gdb.python/py-breakpoint.exp +++ b/gdb/testsuite/gdb.python/py-breakpoint.exp @@ -44,7 +44,8 @@ gdb_py_test_silent_cmd "python blist =3D gdb.breakpoints()" "Get Breakpoint List" gdb_test "python print blist\[0\]" "" "Check obj exists" gdb_test "python print blist\[0\].location" "main." "Check breakpoint loca= tion" -gdb_breakpoint [gdb_get_line_number "Break at multiply."] +set mult_line [gdb_get_line_number "Break at multiply."] +gdb_breakpoint ${mult_line} gdb_continue_to_breakpoint "Break at multiply." # Check that the Python breakpoint code noted the addition of a @@ -54,7 +55,9 @@ gdb_test "python print len(blist)" "2" "Check for two breakpoints" gdb_test "python print blist\[0\]" "" "Check obj exists" gdb_test "python print blist\[0\].location" "main." "Check breakpoint loca= tion" gdb_test "python print blist\[1\]" "" "Check obj exists" -gdb_test "python print blist\[1\].location" "py-breakpoint\.c:41*" "Check breakpoint location" + +gdb_test "python print blist\[1\].location" "py-breakpoint\.c:${mult_line}= *" \ + "Check breakpoint location" # Check hit and ignore counts. gdb_test "python print blist\[1\].hit_count" "1" "Check breakpoint hit cou= nt" diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.c b/gdb/testsuite/gdb.python/py-finish-breakpoint.c new file mode 100644 index 0000000..726a5ee --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c @@ -0,0 +1,42 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2011 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +int increase_1(int *a) +{ + *a +=3D 1; + return -5; +} + +void increase(int *a) +{ + increase_1(a); +} + +int main (int argc, char *argv[]) +{ + int foo =3D 5; + int bar =3D 42; + int i; + i =3D 0 ; + /* Break at increase. */ + increase (&i) ; + increase (&i) ; + increase (&i) ; + + return 0; /* Break at end. */ +} diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp new file mode 100644 index 0000000..ee82374 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp @@ -0,0 +1,92 @@ +# Copyright (C) 2011 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of the GDB testsuite. It tests the mechanism +# exposing values to Python. + +if $tracelevel then { + strace $tracelevel +} + +load_lib gdb-python.exp + +set testfile "py-finish-breakpoint" +set srcfile ${testfile}.c +set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py] + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +if ![runto_main] then { + fail "Cannot run to main." + return 0 +} +gdb_test_no_output "set confirm off" "disable confirmation" +gdb_test "source $remote_python_file" ".*Python script imported.*" \ + "import python scripts" + +gdb_test "python MyBreakpoint(\"increase_1\")" ".*Breakpoint 2.*" \ + "create Python function breakpoint" +gdb_test "continue" ".*Arrived at MyBreakpoint with 0.*" "check MyBreakpoint hit" + +# set FinishBreakpoint + +gdb_test "python finishbp =3D MyFinishBreakpoint (gdb.parse_and_eval (\"a\"), gdb.selected_frame ())" \ + ".*Breakpoint 3.*" "set FinishBreakpoint" +gdb_test "python print finishbp.out_of_scope_notif" ".*True.*" \ + "check out_of_scope_notif at init" +gdb_test "python print finishbp.return_value" ".*None.*" \ + "check return_value at init" + +# check normal bp hit + +gdb_test "continue" ".*MyFinishBreakpoint stop with.*#0.*increase.*" \ + "check MyFinishBreakpoint hit" +gdb_test "python print finishbp.return_value" ".*-5.*" "check return_value" +gdb_test "python print finishbp.out_of_scope_notif" ".*False.*" \ + "check out_of_scope_notif disabled after hit" +gdb_test "finish" ".*main.*" "return to main()" +gdb_test "python print finishbp.return_value" ".*None.*" "check return_val= ue" + +# check forced return / check out of scpop +gdb_test_no_output "python finishbp.out_of_scope_notif =3D True" \ + "re-enable out_of_scope_notif" + +gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check MyBreakpoint second hit" +gdb_test "up" ".*increase_1.*" "go one frame up" +gdb_test_no_output "return" "return from the frame" +gdb_test "python print finishbp.check_scope()" ".*MyFinishBreakpoint out of scope.*True.*" \ + "go one frame up" + +# check forced return / automatic notification + +gdb_test_no_output "python finishbp.out_of_scope_notif =3D True" \ + "re-enable out_of_scope_notif" + +gdb_test "continue" ".*Arrived at MyBreakpoint with.*" "check MyBreakpoint third hit" +gdb_test "up" ".*increase_1.*" "go one frame up" +gdb_test_no_output "return" "return from the frame" +gdb_test "next" ".*MyFinishBreakpoint out of scope.*" "check Finish breakpoint discard" +gdb_test "python print finishbp.out_of_scope_notif" ".*False.*" "check out_of_scope_notif" + +# check FinishBreakpoint in main + +gdb_test "python MyFinishBreakpoint (None, gdb.selected_frame ())" \ + ".*ValueError: \"FinishBreakpoint\" not meaningful in the outermost frame..*" \ + "check FinishBP not allowed in main" diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint.py b/gdb/testsuite/gdb.python/py-finish-breakpoint.py new file mode 100644 index 0000000..bdb142b --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py @@ -0,0 +1,39 @@ +# Copyright (C) 2011 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of the GDB testsuite. It tests python Finish +# Breakpoints. + +class MyBreakpoint(gdb.Breakpoint):=09=09 + def stop(self): + val =3D gdb.parse_and_eval ("a") + print "Arrived at MyBreakpoint with %d" % int(val.dereference()) + return True +=09=09 +class MyFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, val, frame): + super (MyFinishBreakpoint, self).__init__ (frame) + print "MyFinishBreakpoint init" + self.val =3D val +=09=09 + def stop(self): + print "MyFinishBreakpoint stop with %d" % int(self.val.dereference()) + gdb.execute("where 1") + return True +=09 + def out_of_scope(self): + print "MyFinishBreakpoint out of scope..." +=09=09 +print "Python script imported" --=20 1.7.4.4