From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 18664 invoked by alias); 17 Jan 2011 21:06:31 -0000 Received: (qmail 18628 invoked by uid 22791); 17 Jan 2011 21:06:24 -0000 X-SWARE-Spam-Status: No, hits=-4.9 required=5.0 tests=AWL,BAYES_50,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,TW_EV,TW_VP,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Mon, 17 Jan 2011 21:06:10 +0000 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id p0HL689T023696 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Mon, 17 Jan 2011 16:06:09 -0500 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id p0HL68Cs019021 for ; Mon, 17 Jan 2011 16:06:08 -0500 Received: from [10.15.16.129] (dhcp-10-15-16-129.yyz.redhat.com [10.15.16.129]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id p0HL67eW022556 for ; Mon, 17 Jan 2011 16:06:07 -0500 Message-ID: <4D34AF3F.4090006@redhat.com> Date: Mon, 17 Jan 2011 22:59:00 -0000 From: sami wagiaalla User-Agent: Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.13) Gecko/20101209 Fedora/3.1.7-0.35.b3pre.fc14 Thunderbird/3.1.7 MIME-Version: 1.0 To: gdb-patches@sourceware.org Subject: Re: [patch] Support inferior events in python References: <4D2342A2.7060102@redhat.com> In-Reply-To: Content-Type: multipart/mixed; boundary="------------040502060208050104040703" 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-01/txt/msg00371.txt.bz2 This is a multi-part message in MIME format. --------------040502060208050104040703 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Content-length: 6050 This patch contains changes requested by Eli and Tom, and includes documentation for the new features. > Instead of evregpy_get_nlisteners checking the list size, I think it > would be better to have "evregpy_any_listeners_p" return a boolean, and > then check whether the list is empty. This may be more efficient and is > what you want to do anyhow. > How about evregpy_no_listeners_p ? Fits better with the way the code was written. > Sami> +event_object * > Sami> +create_event_object (PyTypeObject *py_type) > Sami> +{ > Sami> + event_object *event_obj; > Sami> + > Sami> + event_obj = PyObject_New (event_object, py_type); > Sami> + if (!event_obj) > Sami> + goto fail; > Sami> + > Sami> + event_obj->dict = PyDict_New (); > Sami> + if (!event_obj->dict) > Sami> + goto fail; > Sami> + > Sami> + return event_obj; > Sami> + > Sami> + fail: > Sami> + Py_XDECREF (event_obj); > Sami> + Py_XDECREF (event_obj->dict); > > Won't decrefing event_obj automatically free the dict when needed? > I didn't look closely but maybe other create_* functions have this issue. > I fixed all the create_* functions and made sure the dealloc functions are using Py_XDECREF. > Sami> +static int > Sami> +add_new_registry (eventregistry_object **registryp, char *name) > Sami> +{ > Sami> + *registryp = create_eventregistry_object (); > Sami> + if(*registryp == NULL) > > Newline between these lines. > Space before open paren. > > Why not just return the new registry, or NULL on error? > That would be simpler. > Adding the registry, and doing the needed error checking here, makes the calling code simpler. The calling code is what will be extended in future development. > > Sami> +static void > Sami> +python_inferior_exit (struct inferior *inf) > Sami> +{ > Sami> + struct cleanup *cleanup; > Sami> + LONGEST exitcode_val; > Sami> + LONGEST *exit_code; > Sami> + > Sami> + cleanup = ensure_python_env (get_current_arch (), current_language); > Sami> + > Sami> + if (get_internalvar_integer (lookup_internalvar ("_exitcode"),&exitcode_val)) > Sami> + exit_code =&exitcode_val; > Sami> + > Sami> + if (exit_code > Sami> +&& emit_exited_event (exit_code)< 0) > > You have to initialize exit_code to NULL for this to work properly. > Done. > However, I think this is pretty ugly. > It seems like there should be a better way to get this than looking up > a convenience variable. > Hmm I looked through the code to find another way but could not. handle_inferior_event which sets the convenience variable uses execution_control_state which I don't have access to. > I think we need an event representing a thread exit. > It is ok by me if this comes in a separate patch. > (FWIW I have a few new events on my branch; I'll update those once this > patch goes in.) > In the original patch this event was halfway between thread exit and inferior exit. So I decided to make it inferior exit with the intention on adding thread exited/created events in a future patch. > Sami> + signal_event_obj->stop_signal = > Sami> + (PyStringObject *) PyString_FromString (stop_signal); > > It is more usual to just use PyObject* everywhere, and not cast to the > more specific types. > > This change should let you eliminate other casts in the patch. > Done. I also, updated breakpoint_event_object. I was going with the oposite mindset of keeping the type information until a cast is required. > Sami> +stop_event_object * > Sami> +create_stop_event_object (PyTypeObject *py_type, thread_object *thread) > Sami> +{ > Sami> + stop_event_object *stop_event_obj = > Sami> + (stop_event_object *) create_event_object (py_type); > Sami> + > Sami> + if (!stop_event_obj) > Sami> + goto fail; > Sami> + > Sami> + stop_event_obj->inferior_thread = (PyObject *) thread; > Sami> + > Sami> + if (evpy_add_attribute ((event_object *) stop_event_obj, > Sami> + "inferior_thread", > Sami> + stop_event_obj->inferior_thread)< 0) > Sami> + goto fail; > Sami> + > Sami> + > Sami> + return stop_event_obj; > > I think it would be better to just have one cast at the end, instead of > lots of casts in the body. > Hmm if I change stop_event_object* to event_object it would eliminate two casts but also add two. One when setting the inferior thread and one for returning. Same goes for all the create_* functions or should I change all of those to return more generic objects ? > Sami> + if (bs&& bs->breakpoint_at > Sami> +&& bs->breakpoint_at->type == bp_breakpoint) > Sami> + { > Sami> + if (evregpy_get_nlisteners (gdb_py_events.breakpoint) == 0) > Sami> + return 0; > > I think the short-circuiting logic should be hoisted to the top of the > function. This is more efficient and also lets you avoid having to > deal with reference counting problems involving objects made earlier. > I moved the thread creation after the short circuiting, but I cannot be moved up further because we have to figure out the type of event. > Sami> + /* Check if the signal is "Signal 0" or "Trace/breakpoint trap". */ > Sami> + if ((strcmp (stop_signal, "0") != 0) > Sami> +&& (strcmp (stop_signal, "SIGTRAP") != 0)) > > I didn't look this up, but this seems questionable. > Is this really how this is done? > I improved this by using enum target_signal and target_signal_to_name to convert the signal to a string when notifying python listeners. That looks OK IMO, but we can also create a module gdb.signal, create a pyhon Signal type, add Signal types for all signals to gdb.signal, and use a Signal object to notify python listeners. > Sami> +typedef struct > Sami> +{ > Sami> + PyObject *inferior_thread; > Sami> + event_object event; > Sami> +} stop_event_object; > > For the inheritance scheme to work, the 'event' field has to come first. > I didn't audit the other event object types, but please make sure they > are all correct. > Corrected and checked other objects. --------------040502060208050104040703 Content-Type: text/x-patch; name="oguz-python-events.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="oguz-python-events.patch" Content-length: 64018 diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 002f3d2..7e80dd4 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,31 @@ +2010-12-22 Sami Wagiaalla + Oguz Kayral + + * python/py-inferior.c (python_on_normal_stop): New function. + (python_on_resume): New function. + (python_inferior_exit): New function. + (gdbpy_initialize_inferior): Add normal_stop, target_resumed, and + inferior_exit observers. + * python/py-eventregistry.c: New file. + * python/py-event.c: New file. + * python/py-events.c: New file. + * python/py-continueevent.c: New file. + * python/py-breakpointevent.c : New file. + * python/py-signalevent.c : New file. + * python/py-exetiedevent.c : New file. + * python/py-breakpoint.c (gdbpy_breakpoint_from_bpstats): New function. + Move struct breakpoint_object from here... + * python/python-internal.h: ... to here. + * python/py-event.h: New file. + * python/py-events.h: New file. + * Makefile.in (SUBDIR_PYTHON_OBS): Add py-breakpointstopevent.o, + py-continueevent.o, py-event.o, py-eventregistry.o, py-events.o, + py-exitedevent.o, py-signalstopevent.o, and py-stopevent.o. + (SUBDIR_PYTHON_SRCS): Add py-breakpointstopevent.c, + py-continueevent.c, py-event.c, py-eventregistry.c, py-events.c, + py-exitedevent.c, py-signalstopevent.c, and py-stopevent.c. + Add build rules for all the above. + 2010-12-14 Ken Werner * valops.c (value_one): Use get_array_bounds to compute the number diff --git a/gdb/Makefile.in b/gdb/Makefile.in index ff10039..ec309ff 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -272,8 +272,14 @@ SUBDIR_PYTHON_OBS = \ python.o \ py-auto-load.o \ py-block.o \ + py-bpevent.o \ py-breakpoint.o \ py-cmd.o \ + py-continueevent.o \ + py-event.o \ + py-evtregistry.o \ + py-evts.o \ + py-exitedevent.o \ py-frame.o \ py-function.o \ py-inferior.o \ @@ -283,17 +289,26 @@ SUBDIR_PYTHON_OBS = \ py-param.o \ py-prettyprint.o \ py-progspace.o \ + py-signalevent.o \ + py-stopevent.o \ py-symbol.o \ py-symtab.o \ py-type.o \ py-utils.o \ py-value.o + SUBDIR_PYTHON_SRCS = \ python/python.c \ python/py-auto-load.c \ python/py-block.c \ + python/py-bpevent.c \ python/py-breakpoint.c \ python/py-cmd.c \ + python/py-continueevent.c \ + python/py-event.c \ + python/py-evtregistry.c \ + python/py-evts.c \ + python/py-exitedevent.c \ python/py-frame.c \ python/py-function.c \ python/py-inferior.c \ @@ -303,6 +318,8 @@ SUBDIR_PYTHON_SRCS = \ python/py-param.c \ python/py-prettyprint.c \ python/py-progspace.c \ + python/py-signalevent.c \ + python/py-stopevent.c \ python/py-symbol.c \ python/py-symtab.c \ python/py-type.c \ @@ -1992,6 +2009,10 @@ py-block.o: $(srcdir)/python/py-block.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-block.c $(POSTCOMPILE) +py-bpevent.o: $(srcdir)/python/py-bpevent.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-bpevent.c + $(POSTCOMPILE) + py-breakpoint.o: $(srcdir)/python/py-breakpoint.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-breakpoint.c $(POSTCOMPILE) @@ -2000,6 +2021,26 @@ py-cmd.o: $(srcdir)/python/py-cmd.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-cmd.c $(POSTCOMPILE) +py-continueevent.o: $(srcdir)/python/py-continueevent.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-continueevent.c + $(POSTCOMPILE) + +py-event.o: $(srcdir)/python/py-event.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-event.c + $(POSTCOMPILE) + +py-evtregistry.o: $(srcdir)/python/py-evtregistry.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-evtregistry.c + $(POSTCOMPILE) + +py-evts.o: $(srcdir)/python/py-evts.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-evts.c + $(POSTCOMPILE) + +py-exitedevent.o: $(srcdir)/python/py-exitedevent.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-exitedevent.c + $(POSTCOMPILE) + py-frame.o: $(srcdir)/python/py-frame.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-frame.c $(POSTCOMPILE) @@ -2036,6 +2077,14 @@ py-progspace.o: $(srcdir)/python/py-progspace.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-progspace.c $(POSTCOMPILE) +py-signalevent.o: $(srcdir)/python/py-signalevent.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-signalevent.c + $(POSTCOMPILE) + +py-stopevent.o: $(srcdir)/python/py-stopevent.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-stopevent.c + $(POSTCOMPILE) + py-symbol.o: $(srcdir)/python/py-symbol.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-symbol.c $(POSTCOMPILE) diff --git a/gdb/NEWS b/gdb/NEWS index 559609b..a51740e 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,15 @@ *** Changes since GDB 7.2 +* Python Support for Inferior events. + Python scripts can add observers to be notified of events + occurring the in process being debugged. + The following events are currently supported: + - gdb.events.breakpoint Breakpoint hit event. + - gdb.events.cont Continue event. + - gdb.events.signal Signal received event. + - gdb.events.exited Inferior exited event. + * New command line options -data-directory DIR Specify DIR as the "data-directory". diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index dc9630a..31dea4c 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -20700,6 +20700,7 @@ situation, a Python @code{KeyboardInterrupt} exception is thrown. * Selecting Pretty-Printers:: How GDB chooses a pretty-printer. * Writing a Pretty-Printer:: Writing a Pretty-Printer. * Inferiors In Python:: Python representation of inferiors (processes) +* Inferior Events In Python:: Listening for events from the process being debugged. * Threads In Python:: Accessing inferior threads from Python. * Commands In Python:: Implementing new commands in Python. * Parameters In Python:: Adding new @value{GDBN} parameters. @@ -21863,6 +21864,70 @@ the pattern could not be found. @end defmethod @end table +@node Inferior Events In Python +@subsubsection Inferior Events In Python +@cindex inferior events in python + +The Python API allows scripts to listen for events coming from the inferior process +and its threads. In order to listen for events the script must register an observer +by connecting it to the appropriate event registry. Event registries can be accessed +through the @code{gdb.events} module. + +Here is an example: + +@smallexample +def exit_handler (event): + if (isinstance (event, gdb.ExitedEvent)): + print "event type: exit" + print "exit code: %d" % (event.exit_code) + +gdb.events.exited.connect (exit_handler) +@end smallexample + +The following is a listing of the event registries that are available and details +of the events they emit: + +@table @code +@item events.breakpoint +@item events.cont +@item events.exited +@item events.signal +@end table + +These registries emit the following events in respective order: + +@table @code +@item events.BreakpointEvent +Indicates that a breakpoint was hit and has the following attributes +@defivar BreakpointEvent breakpoint +Reference to the breakpoint of type @code{gdb.Breakpoint} that was hit. +@xref{Breakpoints In Python}, for details of the @code{gdb.Breakpoint} +object. +@end defivar +@defivar BreakpointEvent inferior_thread +In non-stop mode breakpoints only stop the thread that has hit them. If +@value{GDBN} is running in non-stop mode this attribute will hold a reference +to the thread that has stopped. This object will be of type @code{gdb.InferiorThread} +@xref{Threads In Python}, for details of the @code{gdb.Breakpoint} object. +If all threads are stopped the value of this attribute will be @code{None}. +@end defivar +@item events.ContinueEvent +Indicates that the inferior has been continued. +@item events.ExitedEvent +Indicates that the inferior has exited. +@defivar ExitedEvent exit_code +The exit code that the inferior has returned. +@end defivar +@item events.SignalEvent +@defivar SignalEvent inferior_thread +In non-stop mode this attribute will be set to the specific thread that received the signal +and @code{None} otherwise. +@end defivar +@defivar SignalEvent stop_signal +The signal received by the inferior +@end defivar +@end table + @node Threads In Python @subsubsection Threads In Python @cindex threads in python @@ -23256,10 +23321,31 @@ top of the source tree to the source search path. @value{GDBN} comes with a module to assist writing Python code. @menu +* gdb.events:: Registering observers to listen to inferior events. * gdb.printing:: Building and registering pretty-printers. * gdb.types:: Utilities for working with types. @end menu +@node gdb.events +@subsubsection gdb.events +@cindex gdb.events + +This module provides access to inferior event registries. Add observers +to these registries to receive notification about inferior events. + +The available registries are: + +@table @code +@item events.breakpoint +Notifies listeners of breakpoints being hit. +@item events.cont +Notifies listeners whenever the inferior, or one of its threads, is continued. +@item events.exited +Notifies listeners when the inferior exits. +@item events.signal +Notifies listeners of the inferior, or one of its threads,receiving a signal. +@end table + @node gdb.printing @subsubsection gdb.printing @cindex gdb.printing diff --git a/gdb/python/py-bpevent.c b/gdb/python/py-bpevent.c new file mode 100644 index 0000000..7dbef53 --- /dev/null +++ b/gdb/python/py-bpevent.c @@ -0,0 +1,65 @@ +/* Python interface to inferior breakpoint stop events. + + Copyright (C) 2009, 2010, 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 "py-stopevent.h" + +static PyTypeObject breakpoint_event_object_type; + +static void +breakpoint_evpy_dealloc (PyObject *self) +{ + Py_XDECREF (((breakpoint_event_object *) self)->breakpoint); + stop_evpy_dealloc (self); +} + +/* Create and initialize a BreakpointEvent object. */ + +breakpoint_event_object * +create_breakpoint_event_object (struct bpstats *bs, + PyObject *stopped_thread) +{ + breakpoint_event_object *breakpoint_event_obj = + (breakpoint_event_object *) + create_stop_event_object (&breakpoint_event_object_type, + stopped_thread); + + if (!breakpoint_event_obj) + goto fail; + + breakpoint_event_obj->breakpoint = gdbpy_breakpoint_from_bpstats (bs); + Py_INCREF (breakpoint_event_obj->breakpoint); + + if (evpy_add_attribute ((event_object *) breakpoint_event_obj, + "breakpoint", + breakpoint_event_obj->breakpoint) < 0) + goto fail; + + return breakpoint_event_obj; + + fail: + Py_XDECREF (breakpoint_event_obj); + return NULL; +} + +GDBPY_NEW_EVENT_TYPE (breakpoint, + "gdb.BreakpointEvent", + "BreakpointEvent", + "GDB breakpoint stop event object", + stop_event_object_type, + static); diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c index 88d9930..35203e4 100644 --- a/gdb/python/py-breakpoint.c +++ b/gdb/python/py-breakpoint.c @@ -29,9 +29,6 @@ #include "cli/cli-script.h" #include "ada-lang.h" -/* From breakpoint.c. */ -typedef struct breakpoint_object breakpoint_object; - static PyTypeObject breakpoint_object_type; /* Number of live breakpoints. */ @@ -283,6 +280,15 @@ bppy_set_task (PyObject *self, PyObject *newvalue, void *closure) return 0; } +/* Function to get the corresponding breakpoint object for the given + bpstats. */ + +PyObject * +gdbpy_breakpoint_from_bpstats (struct bpstats *bs) +{ + return (PyObject *) bs->breakpoint_at->py_bp_object; +} + /* Python function which deletes the underlying GDB breakpoint. This triggers the breakpoint_deleted observer which will call gdbpy_breakpoint_deleted; that function cleans up the Python diff --git a/gdb/python/py-continueevent.c b/gdb/python/py-continueevent.c new file mode 100644 index 0000000..320c969 --- /dev/null +++ b/gdb/python/py-continueevent.c @@ -0,0 +1,65 @@ +/* Python interface to inferior continue events. + + Copyright (C) 2009, 2010, 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 "py-event.h" + +static PyTypeObject continue_event_object_type; + +typedef struct +{ + event_object event; +} continue_event_object; + +static void +continue_evpy_dealloc (PyObject *self) +{ + evpy_dealloc (self); +} + +continue_event_object * +create_continue_event_object (void) +{ + return (continue_event_object *) + create_event_object (&continue_event_object_type); +} + +/* Callback function which notifies observers when a continue event occurs. + This function will create a new Python continue event object. + Return -1 if emit fails. */ + +int +emit_continue_event (ptid_t ptid) +{ + event_object *event; + + if (evregpy_no_listeners_p (gdb_py_events.cont)) + return 0; + + event = (event_object *) create_continue_event_object (); + if (event) + return evpy_emit_event (event, gdb_py_events.cont); + return -1; +} + +GDBPY_NEW_EVENT_TYPE (continue, + "gdb.ContinueEvent", + "ContinueEvent", + "GDB continue event object", + event_object_type, + static); diff --git a/gdb/python/py-event.c b/gdb/python/py-event.c new file mode 100644 index 0000000..99dd7bc --- /dev/null +++ b/gdb/python/py-event.c @@ -0,0 +1,178 @@ +/* Python interface to inferior events. + + Copyright (C) 2009, 2010, 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 "py-event.h" + +void +evpy_dealloc (PyObject *self) +{ + Py_XDECREF (((event_object *) self)->dict); + self->ob_type->tp_free (self); +} + +event_object * +create_event_object (PyTypeObject *py_type) +{ + event_object *event_obj; + + event_obj = PyObject_New (event_object, py_type); + if (!event_obj) + goto fail; + + event_obj->dict = PyDict_New (); + if (!event_obj->dict) + goto fail; + + return event_obj; + + fail: + Py_XDECREF (event_obj); + return NULL; +} + +/* Add the attribute ATTR to the event object EVENT. In + python this attribute will be accessible by the name NAME. + returns 0 if the operation succeeds and -1 otherwise. */ + +int +evpy_add_attribute (event_object *event, char *name, PyObject *attr) +{ + return PyObject_SetAttrString ((PyObject *) event, name, attr); +} + +/* Initialize the Python event code. */ + +void +gdbpy_initialize_event (void) +{ + gdbpy_initialize_event_generic (&event_object_type, + "Event"); +} + +/* Initialize the given event type. If BASE is not NULL it will + be set as the types base. + Returns 0 if initialization was successful -1 otherwise. */ + +int +gdbpy_initialize_event_generic (PyTypeObject *type, + char *name) +{ + if (PyType_Ready (type) < 0) + goto fail; + + Py_INCREF (type); + if (PyModule_AddObject (gdb_module, name, (PyObject *) type) < 0) + goto fail; + + return 0; + + fail: + Py_XDECREF (type); + return -1; +} + + +/* Notify the list of listens that the given EVENT has occurred. + returns 0 if emit is successful -1 otherwise. */ + +int +evpy_emit_event (event_object *event, + eventregistry_object *registry) +{ + PyObject *callback_list, *event_obj = (PyObject *) event; + PyObject *callback_list_copy = NULL; + Py_ssize_t i; + + callback_list = (PyObject *) registry->callbacks; + + /* Create a copy of call back list and use that for + notifying listeners to avoid skipping callbacks + in the case of a callback being disconnected during + a notification. */ + callback_list_copy = PySequence_List (callback_list); + if (!callback_list_copy) + goto fail; + + for (i = 0; i < PyList_Size (callback_list_copy); i++) + { + PyObject *func = PyList_GetItem (callback_list_copy, i); + + if (func == NULL) + goto fail; + + if (!PyObject_CallFunctionObjArgs (func, event_obj, NULL)) + { + /* Print the trace here, but keep going -- we want to try to + call all of the callbacks even if one is broken. */ + gdbpy_print_stack (); + } + } + + Py_XDECREF (callback_list_copy); + Py_XDECREF (event_obj); + return 0; + + fail: + gdbpy_print_stack (); + Py_XDECREF (callback_list_copy); + Py_XDECREF (event_obj); + return -1; +} + +PyTypeObject event_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /* ob_size */ + "gdb.Event", /* tp_name */ + sizeof (event_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + evpy_dealloc, /* 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 event object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof (event_object, dict), /* tp_dictoffset */ + 0, /* tp_init */ + 0 /* tp_alloc */ +}; diff --git a/gdb/python/py-event.h b/gdb/python/py-event.h new file mode 100644 index 0000000..8d7af2a --- /dev/null +++ b/gdb/python/py-event.h @@ -0,0 +1,119 @@ +/* Python interface to inferior events. + + Copyright (C) 2009, 2010, 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 . */ + +#ifndef GDB_PY_EVENT_H +#define GDB_PY_EVENT_H + +#include "defs.h" +#include "py-events.h" +#include "command.h" +#include "python-internal.h" +#include "inferior.h" + +/* This macro creates the following functions: + + gdbpy_initialize_{NAME}_event + Used to add the newly created event type to the gdb module. + + and the python type data structure for the event: + + struct PyTypeObject {NAME}_event_object_type + + NAME is the name of the event. + PY_PATH is a string representing the module and python name of + the event. + PY_NAME a string representing what the event should be called in + python. + DOC Python documentation for the new event type + BASE the base event for this event usually just event_object_type. + QUAL qualification for the create event usually 'static' +*/ + +#define GDBPY_NEW_EVENT_TYPE(name, py_path, py_name, doc, base, qual) \ +\ + qual PyTypeObject name##_event_object_type = \ + { \ + PyObject_HEAD_INIT (NULL) \ + 0, /* ob_size */ \ + py_path, /* tp_name */ \ + sizeof (name##_event_object), /* tp_basicsize */ \ + 0, /* tp_itemsize */ \ + name##_evpy_dealloc, /* 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 */ \ + doc, /* tp_doc */ \ + 0, /* tp_traverse */ \ + 0, /* tp_clear */ \ + 0, /* tp_richcompare */ \ + 0, /* tp_weaklistoffset */ \ + 0, /* tp_iter */ \ + 0, /* tp_iternext */ \ + 0, /* tp_methods */ \ + 0, /* tp_members */ \ + 0, /* tp_getset */ \ + &base, /* tp_base */ \ + 0, /* tp_dict */ \ + 0, /* tp_descr_get */ \ + 0, /* tp_descr_set */ \ + 0, /* tp_dictoffset */ \ + 0, /* tp_init */ \ + 0 /* tp_alloc */ \ + }; \ +\ +void \ +gdbpy_initialize_##name##_event (void) \ +{ \ + gdbpy_initialize_event_generic (&name##_event_object_type, \ + py_name); \ +} + +typedef struct +{ + PyObject_HEAD + + PyObject *dict; +} event_object; + +extern int emit_stop_event (struct bpstats *bs, enum target_signal stop_signal); +extern int emit_continue_event (ptid_t ptid); +extern int emit_exited_event (LONGEST *exit_code); + +extern int evpy_emit_event (event_object *event, + eventregistry_object *registry); +extern event_object * create_event_object (PyTypeObject *py_type); +extern void evpy_dealloc (PyObject *self); +extern int evpy_add_attribute (event_object *event, + char *name, PyObject *attr); +int gdbpy_initialize_event_generic (PyTypeObject *type, char *name); + + +#endif /* GDB_PY_EVENT_H */ diff --git a/gdb/python/py-events.h b/gdb/python/py-events.h new file mode 100644 index 0000000..31e5604 --- /dev/null +++ b/gdb/python/py-events.h @@ -0,0 +1,60 @@ +/* Python interface to inferior events. + + Copyright (C) 2009, 2010, 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 . */ + +#ifndef GDB_PY_EVENTS_H +#define GDB_PY_EVENTS_H + +#include "defs.h" +#include "command.h" +#include "python-internal.h" +#include "inferior.h" + + +/* Stores a list of objects to be notified when the event for which this + registry tracks occurs. */ + +typedef struct +{ + PyObject_HEAD + + PyListObject *callbacks; +} eventregistry_object; + +/* Struct holding references to event registries both in python and c. + This is meant to be a singleton. */ + +typedef struct +{ + eventregistry_object *stop; + eventregistry_object *breakpoint; + eventregistry_object *signal; + eventregistry_object *cont; + eventregistry_object *exited; + + PyObject *module; + +} events_object; + +/* Python events singleton. */ +events_object gdb_py_events; + +extern eventregistry_object *create_eventregistry_object (void); +extern int evregpy_no_listeners_p (eventregistry_object *registry); + +#endif /* GDB_PY_EVENTS_H */ diff --git a/gdb/python/py-evtregistry.c b/gdb/python/py-evtregistry.c new file mode 100644 index 0000000..e85e483 --- /dev/null +++ b/gdb/python/py-evtregistry.c @@ -0,0 +1,180 @@ +/* Python interface to inferior thread event registries. + + Copyright (C) 2009, 2010, 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 "command.h" +#include "py-events.h" + +static PyTypeObject eventregistry_object_type; + +/* Implementation of EventRegistry.connect () -> NULL. + Add FUNCTION to the list of listeners. */ + +static PyObject * +evregpy_connect (PyObject *self, PyObject *function) +{ + PyObject *func; + PyObject *callback_list = (PyObject *) + (((eventregistry_object *) self)->callbacks); + + if (!PyArg_ParseTuple (function, "O", &func)) + return NULL; + + if (!PyCallable_Check (func)) + { + PyErr_SetString (PyExc_RuntimeError, "Function is not callable"); + return NULL; + } + + PyList_Append (callback_list, func); + + Py_RETURN_NONE; +} + +/* Implementation of EventRegistry.disconnect () -> NULL. + Remove FUNCTION from the list of listeners. */ + +static PyObject * +evregpy_disconnect (PyObject *self, PyObject *function) +{ + PyObject *func; + int index; + PyObject *callback_list = (PyObject *) + (((eventregistry_object *) self)->callbacks); + + if (!PyArg_ParseTuple (function, "O", &func)) + return NULL; + + if (!PyCallable_Check (func)) + { + PyErr_SetString (PyExc_RuntimeError, "Function is not callable"); + return NULL; + } + + index = PySequence_Index (callback_list, func); + if (index < 0) + { + PyErr_SetString (PyExc_RuntimeError, "Function not found"); + return NULL; + } + + if (PySequence_DelItem (callback_list, index) < 0) + return NULL; + + Py_RETURN_NONE; +} + +/* Create a new event registry. This function uses PyObject_New + and therefore returns a new reference that callers must handle. */ + +eventregistry_object * +create_eventregistry_object (void) +{ + eventregistry_object *eventregistry_obj; + + eventregistry_obj = PyObject_New (eventregistry_object, + &eventregistry_object_type); + + if (!eventregistry_obj) + return NULL; + + eventregistry_obj->callbacks = (PyListObject *) PyList_New (0); + if (!eventregistry_obj->callbacks) + return NULL; + + return eventregistry_obj; +} + +static void +evregpy_dealloc (PyObject *self) +{ + Py_XDECREF (((eventregistry_object *) self)->callbacks); + self->ob_type->tp_free (self); +} + +/* Initialize the Python event registry code. */ + +void +gdbpy_initialize_eventregistry (void) +{ + if (PyType_Ready (&eventregistry_object_type) < 0) + return; + + Py_INCREF (&eventregistry_object_type); + PyModule_AddObject (gdb_module, "EventRegistry", + (PyObject *) &eventregistry_object_type); +} + +/* Retern the number of listeners currently connected to this + registry. */ + +int +evregpy_no_listeners_p (eventregistry_object *registry) +{ + return PyList_Size ((PyObject *) registry->callbacks) == 0; +} + +static PyMethodDef eventregistry_object_methods[] = +{ + { "connect", evregpy_connect, METH_VARARGS, "Add function" }, + { "disconnect", evregpy_disconnect, METH_VARARGS, "Remove function" }, + { NULL } /* Sentinel. */ +}; + +static PyTypeObject eventregistry_object_type = +{ + PyObject_HEAD_INIT (NULL) + 0, /* ob_size */ + "gdb.EventRegistry", /* tp_name */ + sizeof (eventregistry_object), /* tp_basicsize */ + 0, /* tp_itemsize */ + evregpy_dealloc, /* 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, /* tp_flags */ + "GDB event registry object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + eventregistry_object_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0 /* tp_alloc */ +}; diff --git a/gdb/python/py-evts.c b/gdb/python/py-evts.c new file mode 100644 index 0000000..8301b99 --- /dev/null +++ b/gdb/python/py-evts.c @@ -0,0 +1,77 @@ +/* Python interface to inferior events. + + Copyright (C) 2009, 2010, 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 "py-events.h" + +/* Initialize python events. */ + +static int +add_new_registry (eventregistry_object **registryp, char *name) +{ + *registryp = create_eventregistry_object (); + + if (*registryp == NULL) + goto fail; + + if (PyModule_AddObject (gdb_py_events.module, + name, + (PyObject *)(*registryp)) < 0) + goto fail; + + return 0; + + fail: + Py_XDECREF (*registryp); + return -1; +} + +void +gdbpy_initialize_py_events () +{ + gdb_py_events.module = Py_InitModule ("events", NULL); + + if (!gdb_py_events.module) + goto fail; + + if (add_new_registry (&gdb_py_events.stop, "stop") < 0) + goto fail; + + if (add_new_registry (&gdb_py_events.breakpoint, "breakpoint") < 0) + goto fail; + + if (add_new_registry (&gdb_py_events.cont, "cont") < 0) + goto fail; + + if (add_new_registry (&gdb_py_events.exited, "exited") < 0) + goto fail; + + if (add_new_registry (&gdb_py_events.signal, "signal") < 0) + goto fail; + + Py_INCREF (gdb_py_events.module); + if (PyModule_AddObject (gdb_module, + "events", + (PyObject *) gdb_py_events.module) < 0) + goto fail; + + return; + + fail: + gdbpy_print_stack (); +} diff --git a/gdb/python/py-exitedevent.c b/gdb/python/py-exitedevent.c new file mode 100644 index 0000000..357508c --- /dev/null +++ b/gdb/python/py-exitedevent.c @@ -0,0 +1,86 @@ +/* Python interface to inferior exit events. + + Copyright (C) 2009, 2010, 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 "py-event.h" + +static PyTypeObject exited_event_object_type; + +typedef struct +{ + event_object event; + PyObject *exit_code; +} exited_event_object; + +static void +exited_evpy_dealloc (PyObject *self) +{ + Py_XDECREF (((exited_event_object *) self)->exit_code); + evpy_dealloc (self); +} + +exited_event_object * +create_exited_event_object (LONGEST *exit_code) +{ + exited_event_object *exited_event; + + exited_event = (exited_event_object *) + create_event_object (&exited_event_object_type); + + if (!exited_event) + goto fail; + + exited_event->exit_code = PyLong_FromLongLong (*exit_code); + if (evpy_add_attribute ((event_object *) exited_event, + "exit_code", + exited_event->exit_code) < 0) + goto fail; + + return exited_event; + + fail: + Py_XDECREF (exited_event); + return NULL; +} + +/* Callback that is used when an exit event occurs. This function + will create a new Python exited event object. */ + +int +emit_exited_event (LONGEST *exit_code) +{ + event_object *event; + + if (evregpy_no_listeners_p (gdb_py_events.exited)) + return 0; + + event = (event_object *) create_exited_event_object (exit_code); + + if (event) + return evpy_emit_event (event, gdb_py_events.exited); + + return -1; +} + + +GDBPY_NEW_EVENT_TYPE (exited, + "gdb.ExitedEvent", + "ExitedEvent", + "GDB exited event object", + event_object_type, + static); diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c index 6382dab..240c257 100644 --- a/gdb/python/py-inferior.c +++ b/gdb/python/py-inferior.c @@ -26,6 +26,9 @@ #include "python-internal.h" #include "arch-utils.h" #include "language.h" +#include "gdb_signals.h" +#include "py-event.h" +#include "py-stopevent.h" struct threadlist_entry { thread_object *thread_obj; @@ -73,6 +76,57 @@ static PyTypeObject membuf_object_type; } \ } while (0) +static void +python_on_normal_stop (struct bpstats *bs, int print_frame) +{ + struct cleanup *cleanup; + enum target_signal stop_signal; + + if (!find_thread_ptid (inferior_ptid)) + return; + + stop_signal = inferior_thread ()->suspend.stop_signal; + + cleanup = ensure_python_env (get_current_arch (), current_language); + + if (emit_stop_event (bs, stop_signal) < 0) + gdbpy_print_stack (); + + do_cleanups (cleanup); +} + +static void +python_on_resume (ptid_t ptid) +{ + struct cleanup *cleanup; + + cleanup = ensure_python_env (get_current_arch (), current_language); + + if (emit_continue_event (ptid) < 0) + gdbpy_print_stack (); + + do_cleanups (cleanup); +} + +static void +python_inferior_exit (struct inferior *inf) +{ + struct cleanup *cleanup; + LONGEST exitcode_val; + LONGEST *exit_code = NULL; + + cleanup = ensure_python_env (get_current_arch (), current_language); + + if (get_internalvar_integer (lookup_internalvar ("_exitcode"), &exitcode_val)) + exit_code = &exitcode_val; + + if (exit_code + && emit_exited_event (exit_code) < 0) + gdbpy_print_stack (); + + do_cleanups (cleanup); +} + /* Return a borrowed reference to the Python object of type Inferior representing INFERIOR. If the object has already been created, return it, otherwise, create it. Return NULL on failure. */ @@ -108,8 +162,8 @@ inferior_to_inferior_object (struct inferior *inferior) /* Finds the Python Inferior object for the given PID. Returns a borrowed reference, or NULL if PID does not match any inferior - obect. - */ + object. */ + PyObject * find_inferior_object (int pid) { @@ -590,6 +644,9 @@ gdbpy_initialize_inferior (void) observer_attach_new_thread (add_thread_object); observer_attach_thread_exit (delete_thread_object); + observer_attach_normal_stop (python_on_normal_stop); + observer_attach_target_resumed (python_on_resume); + observer_attach_inferior_exit (python_inferior_exit); if (PyType_Ready (&membuf_object_type) < 0) return; diff --git a/gdb/python/py-signalevent.c b/gdb/python/py-signalevent.c new file mode 100644 index 0000000..3bf2fa9 --- /dev/null +++ b/gdb/python/py-signalevent.c @@ -0,0 +1,64 @@ +/* Python interface to inferior signal stop events. + + Copyright (C) 2009, 2010, 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 "py-stopevent.h" + +static PyTypeObject signal_event_object_type; + +static void +signal_evpy_dealloc (PyObject *self) +{ + Py_XDECREF (((signal_event_object *) self)->stop_signal); + stop_evpy_dealloc (self); +} + +signal_event_object * +create_signal_event_object (enum target_signal stop_signal, + PyObject *stopped_thread) +{ + char *signal_name; + signal_event_object *signal_event_obj = + (signal_event_object *) + create_stop_event_object (&signal_event_object_type, + stopped_thread); + + if (!signal_event_obj) + goto fail; + + signal_name = (char *) target_signal_to_name (stop_signal); + signal_event_obj->stop_signal = PyString_FromString (signal_name); + + if (evpy_add_attribute ((event_object *) signal_event_obj, + "stop_signal", + signal_event_obj->stop_signal) < 0) + goto fail; + + return signal_event_obj; + + fail: + Py_XDECREF (signal_event_obj); + return NULL; +} + +GDBPY_NEW_EVENT_TYPE (signal, + "gdb.SignalEvent", + "SignalEvent", + "GDB signal event object", + stop_event_object_type, + static); diff --git a/gdb/python/py-stopevent.c b/gdb/python/py-stopevent.c new file mode 100644 index 0000000..d5a1ec9 --- /dev/null +++ b/gdb/python/py-stopevent.c @@ -0,0 +1,161 @@ +/* Python interface to inferior stop events. + + Copyright (C) 2009, 2010, 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 "py-stopevent.h" + +void +stop_evpy_dealloc (PyObject *self) +{ + Py_XDECREF (((stop_event_object *) self)->inferior_thread); + evpy_dealloc (self); +} + +stop_event_object * +create_stop_event_object (PyTypeObject *py_type, PyObject *thread) +{ + stop_event_object *stop_event_obj = + (stop_event_object *) create_event_object (py_type); + + if (!stop_event_obj) + goto fail; + + stop_event_obj->inferior_thread = thread; + + if (evpy_add_attribute ((event_object *) stop_event_obj, + "inferior_thread", + stop_event_obj->inferior_thread) < 0) + goto fail; + + return stop_event_obj; + + fail: + Py_XDECREF (stop_event_obj); + return NULL; + +} + +/* Stop events can either be thread specific or process wide. If gdb is + running in non-stop mode then the event is thread specific, otherwise + it is process wide. + This function returns the currently stopped thread in non-stop mode and + Py_None otherwise. */ + +PyObject * +get_stopped_thread () +{ + PyObject *stopped_thread = NULL; + + if (non_stop) + stopped_thread = (PyObject *) find_thread_object (inferior_ptid); + else + stopped_thread = Py_None; + + if (!stopped_thread) + return NULL; + + Py_INCREF (stopped_thread); + + return stopped_thread; +} + +/* Callback observers when a stop event occurs. This function will create a new + Python stop event object. If only a specific thread is stopped the thread + object of the event will be set to that thread. Otherwise, if all threads + are stopped thread object will be set to None. + return 0 if the event was created and emitted successfully otherwise + returns -1. */ + +int +emit_stop_event (struct bpstats *bs, enum target_signal stop_signal) +{ + stop_event_object *stop_event_obj = NULL; /* Appease GCC warning. */ + eventregistry_object *registry = NULL; + PyObject* stopped_thread = NULL; + + if (bs && bs->breakpoint_at + && bs->breakpoint_at->type == bp_breakpoint) + { + if (evregpy_no_listeners_p (gdb_py_events.breakpoint)) + return 0; + + stopped_thread = get_stopped_thread(); + if (!stopped_thread) + goto fail; + + stop_event_obj = + (stop_event_object *) + create_breakpoint_event_object (bs, stopped_thread); + if (!stop_event_obj) + goto fail; + + registry = gdb_py_events.breakpoint; + } + + /* Check if the signal is "Signal 0" or "Trace/breakpoint trap". */ + if (stop_signal != TARGET_SIGNAL_0 + && stop_signal != TARGET_SIGNAL_TRAP) + { + if (evregpy_no_listeners_p (gdb_py_events.signal)) + return 0; + + stopped_thread = get_stopped_thread(); + if (!stopped_thread) + goto fail; + + stop_event_obj = + (stop_event_object *) + create_signal_event_object (stop_signal, stopped_thread); + if (!stop_event_obj) + goto fail; + + registry = gdb_py_events.signal; + } + + /* If all fails emit an unknown stop event. All event types should + be known and this should eventually be unused. */ + if (!stop_event_obj) + { + if (evregpy_no_listeners_p (gdb_py_events.stop)) + return 0; + + stopped_thread = get_stopped_thread(); + if (!stopped_thread) + goto fail; + + stop_event_obj = create_stop_event_object (&stop_event_object_type, + stopped_thread); + if (!stop_event_obj) + goto fail; + + registry = gdb_py_events.stop; + } + + return evpy_emit_event ((event_object *) stop_event_obj, registry); + + fail: + Py_XDECREF(stopped_thread); + return -1; +} + +GDBPY_NEW_EVENT_TYPE (stop, + "gdb.StopEvent", + "StopEvent", + "GDB stop event object", + event_object_type, + /*no qual*/); diff --git a/gdb/python/py-stopevent.h b/gdb/python/py-stopevent.h new file mode 100644 index 0000000..bf0b526 --- /dev/null +++ b/gdb/python/py-stopevent.h @@ -0,0 +1,58 @@ +/* Python interface to inferior events. + + Copyright (C) 2009, 2010, 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 . */ + +#ifndef GDB_PY_STOPEVENT_H +#define GDB_PY_STOPEVENT_H + +#include "py-event.h" + +typedef struct +{ + event_object event; + PyObject *inferior_thread; +} stop_event_object; + +typedef struct +{ + stop_event_object stop_event; + PyObject *breakpoint; +} breakpoint_event_object; + +typedef struct +{ + stop_event_object stop_event; + PyObject *stop_signal; +} signal_event_object; + +extern stop_event_object * create_stop_event_object (PyTypeObject *py_type, + PyObject *thread); +extern void stop_evpy_dealloc (PyObject *self); + +extern int emit_stop_event (struct bpstats *bs, + enum target_signal stop_signal); + +extern breakpoint_event_object * +create_breakpoint_event_object (struct bpstats *bs, + PyObject *stopped_thread); + +extern signal_event_object * +create_signal_event_object (enum target_signal stop_signal, + PyObject *stopped_thread); + +#endif /* GDB_PY_STOPEVENT_H */ diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 30d7533..4e141d1 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -79,6 +79,7 @@ typedef int Py_ssize_t; /* Also needed to parse enum var_types. */ #include "command.h" +#include "breakpoint.h" #include "exceptions.h" @@ -86,11 +87,18 @@ struct block; struct value; struct language_defn; struct program_space; +struct bpstats; extern PyObject *gdb_module; extern PyTypeObject value_object_type; extern PyTypeObject block_object_type; extern PyTypeObject symbol_object_type; +extern PyTypeObject event_object_type; +extern PyTypeObject events_object_type; +extern PyTypeObject stop_event_object_type; + +/* Defined in py-breakpoint.c */ +typedef struct breakpoint_object breakpoint_object; typedef struct { @@ -141,6 +149,8 @@ PyObject *objfpy_get_printers (PyObject *, void *); thread_object *create_thread_object (struct thread_info *tp); thread_object *find_thread_object (ptid_t ptid); PyObject *find_inferior_object (int pid); +PyObject *inferior_to_inferior_object (struct inferior *inferior); +PyObject *gdbpy_breakpoint_from_bpstats (struct bpstats *bs); struct block *block_object_to_block (PyObject *obj); struct symbol *symbol_object_to_symbol (PyObject *obj); @@ -167,6 +177,14 @@ void gdbpy_initialize_lazy_string (void); void gdbpy_initialize_parameters (void); void gdbpy_initialize_thread (void); void gdbpy_initialize_inferior (void); +void gdbpy_initialize_eventregistry (void); +void gdbpy_initialize_event (void); +void gdbpy_initialize_py_events (void); +void gdbpy_initialize_stop_event (void); +void gdbpy_initialize_signal_event (void); +void gdbpy_initialize_breakpoint_event (void); +void gdbpy_initialize_continue_event (void); +void gdbpy_initialize_exited_event (void); struct cleanup *make_cleanup_py_decref (PyObject *py); diff --git a/gdb/python/python.c b/gdb/python/python.c index d009be9..04c072a 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1001,6 +1001,15 @@ Enables or disables printing of Python stack traces."), gdbpy_initialize_inferior (); gdbpy_initialize_events (); + gdbpy_initialize_eventregistry (); + gdbpy_initialize_py_events (); + gdbpy_initialize_event (); + gdbpy_initialize_stop_event (); + gdbpy_initialize_signal_event (); + gdbpy_initialize_breakpoint_event (); + gdbpy_initialize_continue_event (); + gdbpy_initialize_exited_event (); + PyRun_SimpleString ("import gdb"); PyRun_SimpleString ("gdb.pretty_printers = []"); diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 0417538..2e9f64b 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2010-12-23 Sami Wagiaalla + + * gdb.python/py-events-threads.c: New file. + * gdb.python/py-events-threads.exp: New file. + * gdb.python/py-events.py: New file. + * gdb.python/py-events.exp: New file. + * gdb.python/py-events.c: New file. + 2010-12-12 Stan Shebs * gdb.trace/tsv.exp: Test print command on trace state variables. diff --git a/gdb/testsuite/gdb.python/py-events.c b/gdb/testsuite/gdb.python/py-events.c new file mode 100644 index 0000000..ceb697e --- /dev/null +++ b/gdb/testsuite/gdb.python/py-events.c @@ -0,0 +1,29 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2010, 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 second(){ + return 12; +} + +int first(){ + return second(); +} + +int main (){ + return first(); +} diff --git a/gdb/testsuite/gdb.python/py-events.exp b/gdb/testsuite/gdb.python/py-events.exp new file mode 100644 index 0000000..e5d6daf --- /dev/null +++ b/gdb/testsuite/gdb.python/py-events.exp @@ -0,0 +1,59 @@ +# Copyright (C) 2010, 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-based +# pretty-printing for the CLI. + +# Skip all tests if Python scripting is not enabled. + +if $tracelevel then { + strace $tracelevel +} + +load_lib gdb-python.exp + +set testfile "py-events" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +set pyfile ${srcdir}/${subdir}/${testfile}.py + +if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } { + return -1 +} + +if { [skip_python_tests] } { continue } + +gdb_test_no_output "python execfile ('${pyfile}')" "" + +if ![runto_main ] then { + fail "Can't run to main" + return -1 +} + +gdb_test "Test_Events" "Event testers registered." + +gdb_breakpoint "first" + +# Test continue event and breakpoint stop event +gdb_test "continue" ".*event type: continue.* +.*event type: stop.* +.*stop reason: breakpoint.* +.*breakpoint number: 2.* +all threads stopped" + +#test exited event. +gdb_test "continue" ".*event type: continue.* +.*event type: exit.* +.*exit code: 12.*" diff --git a/gdb/testsuite/gdb.python/py-events.py b/gdb/testsuite/gdb.python/py-events.py new file mode 100644 index 0000000..57d8842 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-events.py @@ -0,0 +1,62 @@ +# Copyright (C) 2010, 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 pretty +# printers. +import gdb + +def signal_stop_handler (event): + if (isinstance (event, gdb.StopEvent)): + print "event type: stop" + if (isinstance (event, gdb.SignalEvent)): + print "stop reason: signal" + print "stop signal: %s" % (event.stop_signal) + if ( event.inferior_thread is not None) : + print "thread num: %s" % (event.inferior_thread.num); + +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" + +def exit_handler (event): + if (isinstance (event, gdb.ExitedEvent)): + print "event type: exit" + print "exit code: %d" % (event.exit_code) + +def continue_handler (event): + if (isinstance (event, gdb.ContinueEvent)): + print "event type: continue" + +class test_events (gdb.Command): + """Test events.""" + + def __init__ (self): + gdb.Command.__init__ (self, "test_events", gdb.COMMAND_STACK) + + def invoke (self, arg, from_tty): + gdb.events.signal.connect (signal_stop_handler) + gdb.events.breakpoint.connect (breakpoint_stop_handler) + gdb.events.exited.connect (exit_handler) + gdb.events.cont.connect (continue_handler) + print "Event testers registered." + +test_events () diff --git a/gdb/testsuite/gdb.python/py-evthreads.c b/gdb/testsuite/gdb.python/py-evthreads.c new file mode 100644 index 0000000..28124fa --- /dev/null +++ b/gdb/testsuite/gdb.python/py-evthreads.c @@ -0,0 +1,54 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2010, 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 +#include +#include + +pthread_t thread2_id; +pthread_t thread3_id; + +void* thread3 (void* d) +{ + int count3 = 0; + count3++; + + int *bad; + *bad = 1; + + return NULL; +} + +void* thread2 (void* d) +{ + int count2 = 0; + count2++; + return NULL; +} + +int main (){ + + pthread_create (&thread2_id, NULL, thread2, NULL); + pthread_create (&thread3_id, NULL, thread3, NULL); + + int count1 = 0; // stop1 + count1++; + + pthread_join (thread3_id, NULL); + return 12; +} diff --git a/gdb/testsuite/gdb.python/py-evthreads.exp b/gdb/testsuite/gdb.python/py-evthreads.exp new file mode 100644 index 0000000..eb1ecc0 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-evthreads.exp @@ -0,0 +1,89 @@ +# Copyright (C) 2010, 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-based +# pretty-printing for the CLI. + +# Skip all tests if Python scripting is not enabled. + +if $tracelevel then { + strace $tracelevel +} + +load_lib gdb-python.exp + +set testfile "py-evthreads" +set srcfile ${testfile}.c +set binfile ${objdir}/${subdir}/${testfile} +set pyfile ${srcdir}/${subdir}/py-events.py + +gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug nowarnings} +clean_restart $testfile + +if { [skip_python_tests] } { continue } + +gdb_test_no_output "python execfile ('${pyfile}')" "" + +gdb_test "Test_Events" "Event testers registered." +gdb_test_no_output "set non-stop on" +gdb_test_no_output "set target-async on" + +gdb_breakpoint "main" +gdb_breakpoint "thread2" +gdb_breakpoint "thread3" + +send_gdb "run\n" +gdb_expect { + -re "event type: stop.* +.*stop reason: breakpoint.* +.*breakpoint number: 1.* +.*thread num: 1.*" { + pass "reached breakpoint 1" + } + timeout { + fail "did not reach breakpoint 1" + } +} + +send_gdb "next\n" +gdb_expect { + -re "event type: stop.* +.*stop reason: breakpoint.* +.*breakpoint number: 2.* +.*thread num: 2.*" { + pass "reached breakpoint 2" + } + timeout { + fail "did not reach breakpoint 2" + } +} + +send_gdb "next\n" +gdb_expect { + -re "event type: stop.* +.*stop reason: breakpoint.* +.*breakpoint number: 3.* +.*thread num: 3.*" { + pass "reached breakpoint 3" + } + timeout { + fail "did not reach breakpoint 3" + } +} + +gdb_test "continue -a" ".*event type: stop.* +.*stop reason: signal.* +.*stop signal: SIGSEGV.* +.*thread num: 3.*" --------------040502060208050104040703--