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 = \ 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 = \ 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 7093104..c1c4f06 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -5814,6 +5814,7 @@ set_raw_breakpoint_without_location (struct gdbarch *gdbarch, b->ops = NULL; b->condition_not_parsed = 0; b->py_bp_object = NULL; + b->py_bp_type = py_bp_none; b->related_breakpoint = b; /* Add this breakpoint to the end of the chain so that a list of diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h index 7fa705f..a1575a5 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -164,6 +164,15 @@ enum bptype bp_gnu_ifunc_resolver_return, }; +/* Type of the Python object associated with a breakpoint. */ + +enum py_bp_type + { + py_bp_none, /* No Python object. */ + py_bp_standard, /* Ordinary breakpoint object. */ + py_bp_finish /* FinishBreakpoint object. */ + }; + /* States of enablement of breakpoint. */ enum enable_state @@ -654,6 +663,9 @@ struct breakpoint types are tracked by the Python scripting API. */ struct breakpoint_object *py_bp_object; + /* Type of the Python Breakpoint object stored in `py_bp_object'. */ + enum py_bp_type py_bp_type; + /* Whether this watchpoint is exact (see target_exact_watchpoints). */ int exact; diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 584a520..27738b4 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -23513,6 +23513,70 @@ commands, separated by newlines. If there are no commands, this attribute is @code{None}. This attribute is not writable. @end defivar +@subsubsection Finish Breakpoints + +@cindex python finish breakpoints +@tindex gdb.FinishBreakpoint + +A @fdb{finish breakpoint} is a breakpoint set at the return address of +a frame, based on the "finish command. @code{gdb.FinishBreakpoint} extends +@code{gdb.Breakpoint} + +@defmethod FinishBreakpoint __init__ frame @r{[}internal@r{]} +Create a FinishBreakpoint at the return of the @code{gdb.Frame} object +@var{frame}. The optional @var{internal} argument allows the breakpoint +to become invisible to the user. @xref{Breakpoints In Python}, for further +details about this argument. +@end defmethod + +@defop Operation {gdb.Breakpoint} out_of_scope (self) +In some circonstances (eg, @code{longjmp}, C++ exceptions, @value{GDBN} +@code{return} command, ...), a function may not properly terminate, and thus +never hit a @{code} FinishBreakpoint. When @value{GDBN} notices such a +situation, the @code{out_of_scope} function will be triggered. + +This method will only be called if the @code{out_of_scope_notif} attribute +is @code{True}. + +Deleting (@code{gdb.Breakpoint.delete ()}) the @code{FinishBreakpoint} is +allowed in this function. + +You may want to sub-class the @code{gdb.FinishBreakpoint} and implement the +@code{out_of_scope} method: + +@smallexample +class MyFinishBreakpoint (gdb.FinishBreakpoint) + def stop (self): + print "normal finish" + return True + + def out_of_scope (): + print "abnormal finish" +@end smallexample +@end defop + +@defmethod FinishBreakpoint check_scope +Because @value{GDBN} currently only checks if @code{FinishBreakpoint}s ran +out of scope at normal stops, this method allows to force the verification +and trigger the @code{out_of_scope} method if necessary. It will return +@code{True} if @code{out_of_scope ()} was triggered, @code{False} otherwise. +@end defmethod + + +@defivar FinishBreakpoint out_of_scope_notif +This attribute will be @code{True} until the @code{out_of_scope} method has +been called and @code{False} afterwards. This attribute is writeable, so out +of scope notifications can be re-enabled. +@end defivar + +@defivar FinishBreakpoint return_value +When @value{GDBN} is stopped at a @code{FinishBreakpoint}, and the frame +used to build the @code{FinishBreakpoint} had debug symbols, this attribute +will contain a @code{gdb.Value} object corresponding to the return value of +the function. The value will be @code{None} if the value was not computable. +This attribute is not writable. +@end defivar + @node Lazy Strings In Python @subsubsection Python representation of lazy strings. @@ -35239,7 +35303,7 @@ is stopped at the user's request. @node List of Supported Calls @subsection List of Supported Calls -@cindex list of supported file-i/o calls +@cindex list of supported fFinile-i/o calls @menu * open:: diff --git a/gdb/infcmd.c b/gdb/infcmd.c index be1f0a5..2d19ea9 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); } -/* 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 = 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 = 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 15e3ca2..e38ce54 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 8d6910d..c90a5df 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 */ @@ -336,7 +337,8 @@ show_stop_on_solib_events (struct ui_file *file, int from_tty, int stop_after_trap; /* Save register contents here when executing a "finish" command or are - about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set. + about to pop a stack dummy frame, if-and-only-if proceed_to_finish is set + or a Python FinishBreakpoint have been hit. Thus this contains the return value from the called function (assuming values are returned in a register). */ @@ -5823,7 +5825,9 @@ 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_bp_type (inferior_thread ()->control.stop_bpstat, + py_bp_finish) + || 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..07247d8 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 = "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 == 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 == 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 { @@ -753,6 +719,23 @@ gdbpy_should_stop (struct breakpoint_object *bp_obj) return stop; } +/* If STOP_BPSTAT contains a Python breakpoint whose type is TYPE, returns + this breakpoint. Returns NULL otherwise. */ + +struct breakpoint * +gdbpy_is_stopped_at_bp_type (bpstat stop_bpstat, enum py_bp_type type) +{ + bpstat bs; + + for (bs = stop_bpstat; bs; bs = bs->next) + { + if (bs->breakpoint_at && bs->breakpoint_at->py_bp_type == type) + return bs->breakpoint_at; + } + + return NULL; +} + /* Checks if the "stop" method exists in this breakpoint. Used by condition_command to ensure mutual exclusion of breakpoint conditions. */ @@ -813,6 +796,11 @@ gdbpy_breakpoint_created (struct breakpoint *bp) newbp->bp->py_bp_object = newbp; Py_INCREF (newbp); ++bppy_live; + + if (bpfinishpy_is_finish_bp ((PyObject *) newbp)) + bp->py_bp_type = py_bp_finish; + else + bp->py_bp_type = py_bp_standard; } else { @@ -971,7 +959,7 @@ static PyMethodDef breakpoint_object_methods[] = { NULL } /* Sentinel. */ }; -static PyTypeObject breakpoint_object_type = +PyTypeObject breakpoint_object_type = { PyObject_HEAD_INIT (NULL) 0, /*ob_size*/ diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c new file mode 100644 index 0000000..26918cc --- /dev/null +++ b/gdb/python/py-finishbreakpoint.c @@ -0,0 +1,478 @@ +/* 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 = "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 callback + has been triggered. */ + int out_of_scope_notif; + /* Type of the function finished by this breakpoint. */ + struct type *function_type; + /* Type of the value return by the breakpointed function. */ + struct type *return_type; + /* When stopped at this FinishBreakpoint, value returned by the function; + Py_None if the value is not computable; + NULL if GDB is not stopped at a FinishBreakpoint. */ + PyObject *return_value; +}; + +/* 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 = + (struct finish_breakpoint_object *) self; + int cmp; + + BPPY_SET_REQUIRE_VALID (&self_finishbp->py_bp); + + if (newvalue == 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 = PyObject_IsTrue (newvalue); + if (cmp < 0) + return -1; + else + self_finishbp->out_of_scope_notif = 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 = + (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; +} + +/* Triggered when GDB stops at PY_BP. Disable `out_of_scope' notifications + and, if possible, computes and caches the `return_value'. */ + +static void +bpfinish_stopped_at_finish_bp (struct finish_breakpoint_object *py_bp) +{ + py_bp->out_of_scope_notif = 0; + + if (py_bp->return_type) + { + struct value *ret = get_return_value (py_bp->function_type, + py_bp->return_type); + if (ret) + { + py_bp->return_value = value_to_value_object (ret); + } + else + py_bp->return_value = Py_None; + } +} + +/* 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 = + (struct finish_breakpoint_object *) self; + + BPPY_REQUIRE_VALID (&self_finishbp->py_bp); + + if (self_finishbp->return_type == NULL) + Py_RETURN_NONE; + + /* Check if we have a cached value. */ + if (!self_finishbp->return_value) + { + bpstat bs; + + for (bs = inferior_thread ()->control.stop_bpstat; + bs; bs = bs->next) + { + struct breakpoint *bp = bs->breakpoint_at; + + if (bp != NULL && (PyObject *) bp->py_bp_object == self) + bpfinish_stopped_at_finish_bp (self_finishbp); + } + } + + if (!self_finishbp->return_value) + Py_RETURN_NONE; + + Py_INCREF (self_finishbp->return_value); + return self_finishbp->return_value; +} + +/* 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 = (breakpoint_object *) bpfinish_obj; + PyObject *py_obj = (PyObject *) bp_obj; + + bpfinish_obj->out_of_scope_notif = 0; + + if (PyObject_HasAttrString (py_obj, outofscope_func)) + { + if (!PyObject_CallMethod (py_obj, outofscope_func, NULL)) + gdbpy_print_stack (); + } +} + +/* Python function to check if the FinishBreakpoint SELF is not anymore + in the callstack. Triggers self->out_of_scope if necessary. */ + +static PyObject * +bpfinishpy_check_scope (PyObject *self, PyObject *args) +{ + struct finish_breakpoint_object *self_finishbp = + (struct finish_breakpoint_object *) self; + + if (self_finishbp->out_of_scope_notif + && frame_find_by_id (self_finishbp->py_bp.bp->frame_id) == 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[] = { "frame", "internal", NULL }; + breakpoint_object *self_bp = (breakpoint_object *) self; + struct finish_breakpoint_object *self_bpfinish = + (struct finish_breakpoint_object *) self; + int type = bp_breakpoint; + PyObject *frame_obj = NULL; + struct frame_info *frame, *prev_frame; + struct frame_id frame_id; + PyObject *internal = NULL; + int internal_bp = 0; + CORE_ADDR finish_pc, pc; + volatile struct gdb_exception except; + char *addr_str, small_buf[100]; + struct symbol *function; + + if (! PyArg_ParseTupleAndKeywords (args, kwargs, "O|O", keywords, + &frame_obj, &internal)) + return -1; + + if (!frame_obj) + goto invalid_frame; + + frame = frame_object_to_frame_info (frame_obj); + if (frame == NULL) + goto invalid_frame; + + prev_frame = get_prev_frame (frame); + if (prev_frame == 0) + { + PyErr_SetString (PyExc_ValueError, + _("\"FinishBreakpoint\" not meaningful in the outermost frame.")); + return -1; + } + + frame_id = get_frame_id (prev_frame); + if (frame_id_eq (frame_id, null_frame_id)) + goto invalid_frame; + + if (internal) + { + internal_bp = PyObject_IsTrue (internal); + if (internal_bp == -1) + { + PyErr_SetString (PyExc_ValueError, + _("The value of `internal' must be a boolean.")); + return -1; + } + } + + /* Find the function we will return from. */ + if (get_frame_pc_if_available (frame, &pc)) + { + function = find_pc_function (pc); + self_bpfinish->return_type = TYPE_TARGET_TYPE (SYMBOL_TYPE (function)); + + /* Remember only non-VOID return types. */ + if (TYPE_CODE (self_bpfinish->return_type) != TYPE_CODE_VOID) + { + self_bpfinish->function_type = SYMBOL_TYPE (function); + } + else + self_bpfinish->return_type = NULL; + } + else + { + self_bpfinish->return_type = NULL; + self_bpfinish->function_type = NULL; + } + + bppy_pending_object = self_bp; + bppy_pending_object->number = -1; + bppy_pending_object->bp = NULL; + + TRY_CATCH (except, RETURN_MASK_ALL) + { + /* Set a breakpoint on the return address. */ + finish_pc = get_frame_pc (prev_frame); + sprintf (small_buf, "*%s", hex_string (finish_pc)); + addr_str = small_buf; + + create_breakpoint (python_gdbarch, + addr_str, NULL, -1, + 0, + 0, bp_breakpoint, + 0, + AUTO_BOOLEAN_TRUE, + NULL, 0, 1, internal_bp); + } + if (except.reason < 0) + { + PyErr_Format (except.reason == RETURN_QUIT + ? PyExc_KeyboardInterrupt : PyExc_RuntimeError, + "%s", except.message); + return -1; + } + BPPY_SET_REQUIRE_VALID (self_bp); + + self_bp->bp->frame_id = frame_id; + + self_bpfinish->out_of_scope_notif = 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. */ + +int +bpfinishpy_is_finish_bp (PyObject *obj) +{ + return obj != NULL + && PyObject_TypeCheck (obj, &finish_breakpoint_object_type); +} + +/* 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 = (struct breakpoint *) args; + PyObject *py_bp = (PyObject *) b->py_bp_object; + struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch (); + struct cleanup *cleanup = ensure_python_env (garch, current_language); + + /* Trigger out_of_scope if this is a FinishBreakpoint its frame is not in the + current callstack and the notification has not been sent yet. */ + if (bpfinishpy_is_finish_bp (py_bp)) + { + struct finish_breakpoint_object *finish_bp = + (struct finish_breakpoint_object *) py_bp; + + if (b == bp_stopped) + bpfinish_stopped_at_finish_bp (finish_bp); + else if (frame_find_by_id(b->frame_id) == 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 == NULL ? NULL : bs->breakpoint_at); +} + +/* Attached to `exit' notifications, triggers all the necessary out of + scope notifications. */ + +static void +bpfinishpy_handle_exit (struct inferior *inf) +{ + iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL); +} + +/* Callback to the bpfinishpy_handle_resume function. */ + +static int +bpfinishpy_handle_resume_cb (struct breakpoint *b, void *args) +{ + if (bpfinishpy_is_finish_bp ((PyObject *) b->py_bp_object)) + { + struct finish_breakpoint_object *finish_bp = + (struct finish_breakpoint_object *) b->py_bp_object; + Py_XDECREF (finish_bp->return_value); + finish_bp->return_value = NULL; + } + return 0; +} + +/* Attached to `target_resume' notifications, resets the `return_value' + cache. */ + +static void +bpfinishpy_handle_resume (ptid_t ptid) +{ + iterate_over_breakpoints (bpfinishpy_handle_resume_cb, NULL); +} + +/* 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); + observer_attach_inferior_exit (bpfinishpy_handle_exit); + observer_attach_target_resumed (bpfinishpy_handle_resume); +} + +static PyMethodDef finish_breakpoint_object_methods[] = { + { "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[] = { + { "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 = +{ + 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 = (frame_object *) obj; struct frame_info *frame; frame = frame_find_by_id (frame_obj->frame_id); @@ -103,7 +104,7 @@ frapy_is_valid (PyObject *self, PyObject *args) { struct frame_info *frame; - frame = frame_object_to_frame_info ((frame_object *) self); + frame = frame_object_to_frame_info (self); if (frame == 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 = get_frame_type (frame); } @@ -174,7 +175,7 @@ frapy_unwind_stop_reason (PyObject *self, PyObject *args) 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = read_var_value (var, frame); } @@ -484,12 +485,11 @@ static PyObject * frapy_select (PyObject *self, PyObject *args) { struct frame_info *fi; - frame_object *frame = (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..e404a32 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 == 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 == 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 *); @@ -181,6 +220,8 @@ struct type *type_object_to_type (PyObject *obj); struct symtab *symtab_object_to_symtab (PyObject *obj); struct symtab_and_line *sal_object_to_symtab_and_line (PyObject *obj); +int bpfinishpy_is_finish_bp (PyObject *obj); + void gdbpy_initialize_auto_load (void); void gdbpy_initialize_values (void); void gdbpy_initialize_frames (void); @@ -194,6 +235,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..f71fd4a 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.")); } +struct breakpoint * +gdbpy_is_stopped_at_bp_type (bpstat stop_bpstat, enum py_bp_type type) +{ + return NULL; +} #endif /* HAVE_PYTHON */ @@ -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..9be6ac9 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,6 @@ int gdbpy_should_stop (struct breakpoint_object *bp_obj); int gdbpy_breakpoint_has_py_cond (struct breakpoint_object *bp_obj); +struct breakpoint *gdbpy_is_stopped_at_bp_type (bpstat stop_bpstat, + enum py_bp_type type); #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 = gdb.breakpoints()" "Get Breakpoint List" gdb_test "python print blist\[0\]" "" "Check obj exists" gdb_test "python print blist\[0\].location" "main." "Check breakpoint location" -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 location" 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 count" diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc new file mode 100644 index 0000000..a0eea06 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.cc @@ -0,0 +1,59 @@ +/* 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 . +*/ + + +#include + +void +throw_exception_1 (int e) +{ + throw new int (e); +} + +void +throw_exception (int e) +{ + throw_exception_1 (e); +} + +int +main (void) +{ + int i; + try + { + throw_exception_1 (10); + } + catch (const int *e) + { + std::cerr << "Exception #" << *e << std::endl; + } + i += 1; /* Break after exception 1. */ + + try + { + throw_exception (10); + } + catch (const int *e) + { + std::cerr << "Exception #" << *e << std::endl; + } + i += 1; /* Break after exception 2. */ + + return i; +} diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp new file mode 100644 index 0000000..e74023d --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.exp @@ -0,0 +1,59 @@ +# 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-cc" +set srcfile ${testfile}.cc +set binfile ${objdir}/${subdir}/${testfile} +set pyfile ${srcdir}/${subdir}/${testfile}.py + +# Start with a fresh gdb. +gdb_exit +gdb_start +gdb_reinitialize_dir $srcdir/$subdir +gdb_load ${binfile} + +if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug c++}] != "" } { + untested "Couldn't compile ${srcfile}" + return -1 +} + +if ![runto_main] then { + fail "Cannot run to main." + return 0 +} + +gdb_test "source $pyfile" ".*Python script imported.*" \ + "import python scripts" + +gdb_test "break [gdb_get_line_number "Break after exception 1."]" "Breakpoint.* at .*" \ + "set watchdog after the exception 1" +gdb_test "break [gdb_get_line_number "Break after exception 2."]" "Breakpoint.* at .*" \ + "set watchdog after the exception 2" + +gdb_test "python ExceptionBreakpoint()" "ExceptionBreakpoint init" "set BP before throwing the exception" +gdb_test "python print len(gdb.breakpoints())" "4" "check number of BPs" +gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()" +gdb_test "python print len(gdb.breakpoints())" "4" "check finish BP removal" + +gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught" diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py new file mode 100644 index 0000000..d0dfe2f --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint-cc.py @@ -0,0 +1,43 @@ +# 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 ExceptionBreakpoint(gdb.Breakpoint): + def __init__(self): + gdb.Breakpoint.__init__(self, spec="throw_exception_1", internal=1) + self.silent = True + print "ExceptionBreakpoint init" + + def stop(self): + ExceptionFinishBreakpoint(gdb.newest_frame()) + return False + +class ExceptionFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, frame): + gdb.FinishBreakpoint.__init__(self, frame, internal=1) + self.silent = True; + + def stop(self): + print "stopped at ExceptionFinishBreakpoint" + gdb.post_event(self.delete) + return True + + def out_of_scope(self): + print "exception did not finish ..." + + +print "Python script imported" 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..57a6e06 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.c @@ -0,0 +1,82 @@ +/* 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 . +*/ +#include + +int increase_1 (int *a) +{ + *a += 1; + return -5; +} + +void increase (int *a) +{ + increase_1 (a); +} + +int +test_1 (int i, int j) +{ + return i == j; +} + +int +test(int i, int j) +{ + return test_1 (i, j); +} + +int +call_longjmp_1 (jmp_buf *buf) +{ + longjmp (*buf, 1); +} + +int +call_longjmp (jmp_buf *buf) +{ + call_longjmp_1 (buf); +} + + +int main (int argc, char *argv[]) +{ + jmp_buf env; + int foo = 5; + int bar = 42; + int i, j; + + i = 0; + /* Break at increase. */ + increase (&i); + increase (&i); + increase (&i); + + for (i = 0; i < 10; i++) + { + j += 1; /* Condition Break. */ + } + + if (setjmp (env) == 0) /* longjmp caught */ + { + call_longjmp (&env); + } + else + j += 1; /* after longjmp. */ + + return j; /* 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..65eebc9 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.exp @@ -0,0 +1,183 @@ +# 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 + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +set remote_python_file [remote_download host ${srcdir}/${subdir}/${testfile}.py] + + +# Skip all tests if Python scripting is not enabled. +if { [skip_python_tests] } { continue } + +# +# Test FinishBreakpoint in function returned by longjmp +# + +clean_restart ${testfile} + +if ![runto call_longjmp_1] then { + perror "couldn't run to breakpoint call_longjmp" + continue +} + +gdb_test "source $remote_python_file" ".*Python script imported.*" \ + "import python scripts" + +gdb_test "python ljmpBP = LongjmpFinishBreakpoint(gdb.newest_frame())" \ + "LongjmpFinishBreakpoint init" \ + "set finish breakpoint" +gdb_test "break [gdb_get_line_number "after longjmp."]" "Breakpoint.* at .*" \ + "set BP after the jump" +gdb_test "continue" ".*Longjmp didn't finish.*" "check FinishBP out of scope notification" + +# +# Test FinishBreakpoint in BP condition evaluation +# (finish in dummy frame) +# + +clean_restart ${testfile} + +set cond_line [gdb_get_line_number "Condition Break."] +if ![runto_main] then { + fail "Cannot run to main." + return 0 +} + +gdb_test "source $remote_python_file" ".*Python script imported.*" \ + "import python scripts" + +gdb_test "break ${cond_line} if test_1(i,8)" ".*Breakpoint .* at .*" "set conditional BP" +gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition" + + +set msg "check FinishBreakpoint don't stop in GDB Dummy Frame" +gdb_test_multiple "continue" $msg { + -re ".*test don't stop 2.*test stop.*test don't stop 4.*" { + pass $msg + } + -re ".*test don't stop 2.*test stop.*$gdb_prompt" { + fail $msg + } +} + +gdb_test "print i" "8" "check stopped location" + +# +# Test FinishBreakpoint in BP condition evaluation +# (finish in normal frame) +# + +clean_restart ${testfile} + +gdb_test "source $remote_python_file" ".*Python script imported.*" \ + "import python scripts" + +if ![runto_main] then { + fail "Cannot run to main." + return 0 +} + +gdb_test "break ${cond_line} if test(i,8)" ".*Breakpoint .* at .*" "set conditional BP" +gdb_test "python TestBreakpoint()" "TestBreakpoint init" "set BP in condition" + +gdb_test "continue" ".*test don't stop 1.*test don't stop 2.*test stop.*Error in testing breakpoint condition.*The program being debugged stopped while in a function called from GDB.*" \ + "stop in condition function" + +setup_kfail "normal_stop_notification not triggered during condition evaluation" *-*-* +gdb_test "python print gdb.breakpoints()\[2\].out_of_scope_notif" ".*False.*" "check out_of_scope notification disabled" +gdb_test_no_output "python gdb.breakpoints()\[2\].out_of_scope_notif = False" "reestablish correct value" + +gdb_test "continue" "Continuing.*" "finish condition evaluation" +gdb_test "continue" "Breakpoint.*" "stop at conditional breakpoint" +gdb_test "print i" "8" "check stopped location" + +# +# Test FinishBreakpoint in normal conditions +# + +clean_restart ${testfile} + +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 = 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_value" + +# check forced return / check out of scpop +gdb_test_no_output "python finishbp.out_of_scope_notif = 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 = 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..f014cc6 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-finish-breakpoint.py @@ -0,0 +1,85 @@ +# 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): + def stop(self): + val = gdb.parse_and_eval ("a") + print "Arrived at MyBreakpoint with %d" % int(val.dereference()) + return True + +class MyFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, val, frame): + super (MyFinishBreakpoint, self).__init__ (frame) + print "MyFinishBreakpoint init" + self.val = val + + def stop(self): + print "MyFinishBreakpoint stop with %d" % int(self.val.dereference()) + gdb.execute("where 1") + return True + + def out_of_scope(self): + print "MyFinishBreakpoint out of scope..." + +test_finish_bp = None +class TestBreakpoint(gdb.Breakpoint): + def __init__(self): + gdb.Breakpoint.__init__(self, spec="test_1", internal=1) + self.silent = True + self.finish = None + print "TestBreakpoint init" + + def stop(self): + global test_finish_bp + if (self.finish == None): + self.finish = TestFinishBreakpoint(gdb.newest_frame()) + test_finish_bp = self.finish + return False + + +class TestFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, frame): + gdb.FinishBreakpoint.__init__(self, frame, internal=1) + self.count = 0 + + def stop(self): + self.count += 1 + if (self.count == 3): + print "test stop ..." + return True + else: + print "test don't stop %d" % self.count + return False + + + def out_of_scope(self): + print "test didn't finish ..." + +class LongjmpFinishBreakpoint(gdb.FinishBreakpoint): + def __init__(self, frame): + gdb.FinishBreakpoint.__init__(self, frame, internal=1) + print "LongjmpFinishBreakpoint init" + + def stop(self): + print "Stopped at LongjmpFinishBreakpoint" + + + def out_of_scope(self): + print "Longjmp didn't finish ..." + +print "Python script imported"