This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH v7] Events when inferior is modified


Hi.  Comments inline.

Nick Bull writes:
 > Pedro,
 > 
 > Thanks for your comments. I believe this version addresses them all.
 > 
 > Nick
 > 
 > 
 > gdb/ChangeLog
 > 
 > 2014-05-15  Nick Bull  <nicholaspbull@gmail.com>
 > 
 > 	* NEWS: Mention new Python events.
 > 	* Makefile.in: add py-infcallevent.c / py-infcallevent.o.
 > 	* doc/observer.texi (inferior_call_pre, inferior_call_post)
 > 	(memory_changed, register_changed): New observers.
 > 	* infcall.c (call_function_by_hand): Notify observer before and
 > 	after inferior call.
 > 	* python/py-event.h (inferior_call_kind): New enum.
 > 	(emit_inferior_call_event): New prototype.
 > 	(emit_register_changed_event): New prototype.
 > 	(emit_memory_changed_event): New prototype.
 > 	* python/py-events.h (events_object): New registries
 > 	inferior_call, memory_changed and register_changed.
 > 	* python/py-evts.c (gdbpy_initialize_py_events): Add the
 > 	inferior_call, memory_changed and register_changed registries.
 > 	* python/py-infcallevent.c: New.
 > 	* python/py-inferior.c (python_on_inferior_call_pre)
 > 	(python_on_inferior_call_post, python_on_register_change)
 > 	(python_on_memory_change): New functions.
 > 	(gdbpy_initialize_inferior): Attach python handler to new
 > 	observers.
 > 	* python/python-internal.h:
 > 	(gdbpy_initialize_inferior_call_pre_event)
 > 	(gdbpy_initialize_inferior_call_post_event)
 > 	(gdbpy_initialize_register_changed_event)
 > 	(gdbpy_initialize_memory_changed_event): New prototypes.
 > 	* python/python.c (_initialize_python): Initialize new events.
 > 	* valops.c (value_assign): Notify register_changed observer.
 > 
 > gdb/doc/ChangeLog
 > 
 > 2014-05-15  Nick Bull  <nicholaspbull@gmail.com>
 > 
 > 	* python.texi (Events In Python): Document new events
 > 	InferiorCallPreEvent, InferiorCallPostEvent, MemoryChangedEvent
 > 	and RegisterChangedEvent.
 > 
 > gdb/testsuite/ChangeLog
 > 
 > 2014-05-15  Nick Bull  <nicholaspbull@gmail.com>
 > 
 > 	* gdb.python/py-events.py (inferior_fn_handler): New.
 > 	(register_changed_handler, memory_changed_handler): New.
 > 	(test_events.invoke): Register new handlers.
 > 	* gdb.python/py-events.exp: Add tests for inferior call,
 > 	memory_changed and register_changed events.
 > 
 > diff --git a/gdb/Makefile.in b/gdb/Makefile.in
 > index 1ffa62a..9a913d8 100644
 > --- a/gdb/Makefile.in
 > +++ b/gdb/Makefile.in
 > @@ -360,6 +360,7 @@ SUBDIR_PYTHON_OBS = \
 >  	py-framefilter.o \
 >  	py-function.o \
 >  	py-gdb-readline.o \
 > +	py-infcallevent.o \
 >  	py-inferior.o \
 >  	py-infthread.o \
 >  	py-lazy-string.o \
 > @@ -398,6 +399,7 @@ SUBDIR_PYTHON_SRCS = \
 >  	python/py-framefilter.c \
 >  	python/py-function.c \
 >  	python/py-gdb-readline.c \
 > +	python/py-infcallevent.c \
 >  	python/py-inferior.c \
 >  	python/py-infthread.c \
 >  	python/py-lazy-string.c \
 > @@ -2477,6 +2479,10 @@ py-gdb-readline.o: $(srcdir)/python/py-gdb-readline.c
 >  	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-gdb-readline.c
 >  	$(POSTCOMPILE)
 >  
 > +py-infcallevent.o: $(srcdir)/python/py-infcallevent.c
 > +	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-infcallevent.c
 > +	$(POSTCOMPILE)
 > +
 >  py-inferior.o: $(srcdir)/python/py-inferior.c
 >  	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-inferior.c
 >  	$(POSTCOMPILE)
 > diff --git a/gdb/NEWS b/gdb/NEWS
 > index b56fe8e..3445634 100644
 > --- a/gdb/NEWS
 > +++ b/gdb/NEWS
 > @@ -1,6 +1,16 @@
 >  		What has changed in GDB?
 >  	     (Organized release by release)
 >  
 > +* Python Scripting
 > +
 > +  New events which are triggered when GDB modifies the state of the 
 > +  inferior.
 > +
 > +  ** gdb.events.inferior_call_pre: Function call is about to be made.
 > +  ** gdb.events.inferior_call_post: Function call has just been made.
 > +  ** gdb.events.memory_changed: A memory location has been altered.
 > +  ** gdb.events.register_changed: A register has been altered.
 > +
 >  *** Changes since GDB 7.8
 >  
 >  * GDB now supports hardware watchpoints on x86 GNU Hurd.
 > diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi
 > index 2757587..88f1d3f 100644
 > --- a/gdb/doc/observer.texi
 > +++ b/gdb/doc/observer.texi
 > @@ -281,6 +281,22 @@ The trace state variable @var{tsv} is deleted.  If @var{tsv} is
 >  The trace state value @var{tsv} is modified.
 >  @end deftypefun
 >  
 > +@deftypefun void inferior_call_pre (ptid_t @var{thread}, CORE_ADDR @var{address})
 > +An inferior function at @var{address} is about to be called in thread
 > +@var{thread}.
 > +@end deftypefun
 > +
 > +@deftypefun void inferior_call_post (ptid_t @var{thread}, CORE_ADDR @var{address})
 > +The inferior function at @var{address} has just been called.  This observer
 > +is called even if the inferior exits during the call.  @var{thread} is the
 > +thread in which the function was called, which may be different from the
 > +current thread.
 > +@end deftypefun
 > +
 > +@deftypefun void register_changed (struct frame_info *@var{frame}, short @var{regnum})
 > +A register in the inferior has been modified.
 > +@end deftypefun

This may be excessively nitpicky, but when I read this I'm left wondering if it also applies to register changes made during program execution.
E.g., should this get called even after a "stepi"?
Plus registers get modified to perform, e.g., inferior function calls and I'm guessing we don't want to call this,
e.g., for every register restored after the inferior function call completes.

"has been modified" leaves these questions unanswered.
I think all that's needed is to improve the wording here.
I see the docs for the python register_changed event say
"by the GDB user". How about saying that here too?

Also, please use "int" instead of "short" for the regnum,
throughout the patch.

 > +
 >  @deftypefun void test_notification (int @var{somearg})
 >  This observer is used for internal testing.  Do not use.  
 >  See testsuite/gdb.gdb/observer.exp.
 > diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
 > index 81ec11b..9af78e8 100644
 > --- a/gdb/doc/python.texi
 > +++ b/gdb/doc/python.texi
 > @@ -2728,6 +2728,55 @@ A reference to the object file (@code{gdb.Objfile}) which has been loaded.
 >  @xref{Objfiles In Python}, for details of the @code{gdb.Objfile} object.
 >  @end defvar
 >  
 > +@item events.inferior_call_pre
 > +Emits @code{gdb.InferiorCallPreEvent} which indicates that a function in
 > +the inferior is about to be called.
 > +
 > +@defvar InferiorCallPreEvent.tid
 > +The thread in which the call will be run.
 > +@end defvar

As a user I'd like to know what "tid" is without first having to write an example and find out for myself.
There are several kinds: pthread_t, lwpid, and so on, and
"tid" is ambiguous.
Obviously, not every target has pthreads or lwps,
but the text here should say at least something.
We export threads as gdb.InferiorThread objects and these objects
have "num" and "ptid" attributes.  Intuitively I would expect "tid"
to be one of these (or better yet the gdb.InferiorThread object itself,
though that's problematic so I understand why we're not passing
that there).

Let's use the same name as is used in gdb.InferiorThread, "ptid".
[See more below.]

 > +
 > +@defvar InferiorCallPreEvent.address
 > +The location of the function to be called.
 > +@end defvar
 > +
 > +@item events.inferior_call_post
 > +Emits @code{gdb.InferiorCallPostEvent} which indicates that a function in
 > +the inferior has returned.
 > +
 > +@defvar InferiorCallPostEvent.tid
 > +The thread in which the call was run.
 > +@end defvar

Ditto.  ptid.

 > +
 > +@defvar InferiorCallPostEvent.address
 > +The location of the function that was called.
 > +@end defvar
 > +
 > +@item events.memory_changed
 > +Emits @code{gdb.MemoryChangedEvent} which indicates that the memory of the
 > +inferior has been modified by the @value{GDBN} user, for instance via a
 > +command like @w{@code{set *addr = value}}.  The event has the following
 > +attributes:
 > +
 > +@defvar MemoryChangedEvent.address
 > +The start address of the changed region.
 > +@end defvar
 > +
 > +@defvar MemoryChangedEvent.length
 > +Length in bytes of the changed region.
 > +@end defvar
 > +
 > +@item events.register_changed
 > +Emits @code{gdb.RegisterChangedEvent} which indicates that a register in the
 > +inferior has been modified by the @value{GDBN} user.
 > +
 > +@defvar RegisterChangedEvent.frame
 > +A gdb.Frame object representing the frame in which the register was modified.
 > +@end defvar
 > +@defvar RegisterChangedEvent.regnum
 > +Denotes which register was modified.
 > +@end defvar
 > +
 >  @end table
 >  
 >  @node Threads In Python
 > diff --git a/gdb/infcall.c b/gdb/infcall.c
 > index e60d1d4..d390fbe 100644
 > --- a/gdb/infcall.c
 > +++ b/gdb/infcall.c
 > @@ -36,6 +36,7 @@
 >  #include "gdbthread.h"
 >  #include "exceptions.h"
 >  #include "event-top.h"
 > +#include "observer.h"
 >  
 >  /* If we can't find a function's name from its address,
 >     we print this instead.  */
 > @@ -627,6 +628,8 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
 >        target_values_type = values_type;
 >      }
 >  
 > +  observer_notify_inferior_call_pre (inferior_ptid, funaddr);
 > +
 >    /* Determine the location of the breakpoint (and possibly other
 >       stuff) that the called function will return to.  The SPARC, for a
 >       function returning a structure or union, needs to make space for
 > @@ -860,6 +863,8 @@ call_function_by_hand (struct value *function, int nargs, struct value **args)
 >      e = run_inferior_call (tp, real_pc);
 >    }
 >  
 > +  observer_notify_inferior_call_post (call_thread_ptid, funaddr);
 > +
 >    /* Rethrow an error if we got one trying to run the inferior.  */
 >  
 >    if (e.reason < 0)
 > diff --git a/gdb/python/py-event.h b/gdb/python/py-event.h
 > index 5a0f29b..5c3cc4b 100644
 > --- a/gdb/python/py-event.h
 > +++ b/gdb/python/py-event.h
 > @@ -105,6 +105,22 @@ typedef struct
 >  extern int emit_continue_event (ptid_t ptid);
 >  extern int emit_exited_event (const LONGEST *exit_code, struct inferior *inf);
 >  
 > +/* For inferior function call events, discriminate whether event is
 > +   before or after the call. */
 > +
 > +typedef enum
 > +{
 > +  /* Before the call */
 > +  inferior_call_pre,
 > +  /* After the call */
 > +  inferior_call_post,
 > +} inferior_call_kind;

I know this isn't a universal convention, but I really like it.
Symbols that are all lowercase, with no indication that they're
in fact just a constant, makes reading code using them measurably harder.

Can I ask that these be uppercase?
INFERIOR_CALL_PRE and INFERIOR_CALL_POST.
That's the existing convention we use, at least somewhat.
I'd like to advocate using it more.
I'm open to a different convention, but that's the one we have.
foo_bar is just painful.

 > +
 > +extern int emit_inferior_call_event (inferior_call_kind kind,
 > +				     ptid_t thread, CORE_ADDR addr);
 > +extern int emit_register_changed_event (struct frame_info *frame,
 > +					short regnum);
 > +extern int emit_memory_changed_event (CORE_ADDR addr, ssize_t len);
 >  extern int evpy_emit_event (PyObject *event,
 >                              eventregistry_object *registry)
 >    CPYCHECKER_STEALS_REFERENCE_TO_ARG (1);
 > diff --git a/gdb/python/py-events.h b/gdb/python/py-events.h
 > index 3431612..e219305 100644
 > --- a/gdb/python/py-events.h
 > +++ b/gdb/python/py-events.h
 > @@ -46,6 +46,9 @@ typedef struct
 >    eventregistry_object *cont;
 >    eventregistry_object *exited;
 >    eventregistry_object *new_objfile;
 > +  eventregistry_object *inferior_call;
 > +  eventregistry_object *memory_changed;
 > +  eventregistry_object *register_changed;
 >  
 >    PyObject *module;
 >  
 > diff --git a/gdb/python/py-evts.c b/gdb/python/py-evts.c
 > index a7daf8a..1825464 100644
 > --- a/gdb/python/py-evts.c
 > +++ b/gdb/python/py-evts.c
 > @@ -73,6 +73,18 @@ gdbpy_initialize_py_events (void)
 >    if (add_new_registry (&gdb_py_events.exited, "exited") < 0)
 >      return -1;
 >  
 > +  if (add_new_registry (&gdb_py_events.inferior_call,
 > +			"inferior_call") < 0)
 > +    return -1;
 > +
 > +  if (add_new_registry (&gdb_py_events.memory_changed,
 > +			"memory_changed") < 0)
 > +    return -1;
 > +
 > +  if (add_new_registry (&gdb_py_events.register_changed,
 > +			"register_changed") < 0)
 > +    return -1;
 > +
 >    if (add_new_registry (&gdb_py_events.new_objfile, "new_objfile") < 0)
 >      return -1;
 >  
 > diff --git a/gdb/python/py-infcallevent.c b/gdb/python/py-infcallevent.c
 > new file mode 100644
 > index 0000000..3d19c6d
 > --- /dev/null
 > +++ b/gdb/python/py-infcallevent.c
 > @@ -0,0 +1,251 @@
 > +/* Python interface to inferior function events.
 > +
 > +   Copyright (C) 2013 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 <http://www.gnu.org/licenses/>.  */
 > +
 > +#include "defs.h"
 > +#include "py-event.h"
 > +
 > +static PyTypeObject inferior_call_pre_event_object_type
 > +    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object");
 > +static PyTypeObject inferior_call_post_event_object_type
 > +    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object");
 > +static PyTypeObject register_changed_event_object_type
 > +    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object");
 > +static PyTypeObject memory_changed_event_object_type
 > +    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object");
 > +
 > +/* Construct either a gdb.InferiorCallPreEvent or a
 > +   gdb.InferiorCallPostEvent. */
 > +
 > +static PyObject *
 > +create_inferior_call_event_object (int flag, ptid_t ptid, CORE_ADDR addr)

flag is an enum.

 > +{
 > +  PyObject * event;
 > +  PyObject *tid_obj = NULL;
 > +  PyObject *addr_obj = NULL;
 > +  int failed;
 > +  struct cleanup *cleanups;
 > +
 > +  switch (flag)
 > +    {
 > +    case inferior_call_pre:
 > +      event = create_event_object (&inferior_call_pre_event_object_type);
 > +      break;
 > +    case inferior_call_post:
 > +      event = create_event_object (&inferior_call_post_event_object_type);
 > +      break;
 > +    default:
 > +      return NULL;
 > +    }
 > +
 > +  cleanups = make_cleanup_py_decref (event);
 > +
 > +  tid_obj = PyLong_FromLong (ptid.lwp);

Alas our use of thread ids is a bit, umm, confusing
(in more ways than one! :-().
Here, it's not guaranteed that ptid.lwp has something useful,
and it may be that the target uses ptid.tid instead.

See python/py-infthread.c:thpy_get_ptid.
I think we should make that non-static and use that here.
IOW, pass the whole ptid_t to the event.

 > +  if (tid_obj == NULL)
 > +    goto fail;
 > +  make_cleanup_py_decref (tid_obj);
 > +
 > +  failed = evpy_add_attribute (event, "tid", tid_obj) < 0;
 > +  if (failed)
 > +    goto fail;
 > +
 > +  addr_obj = PyLong_FromLongLong (addr);
 > +  if (addr_obj == NULL)
 > +    goto fail;
 > +  make_cleanup_py_decref (addr_obj);
 > +
 > +  failed = evpy_add_attribute (event, "address", addr_obj) < 0;
 > +  if (failed)
 > +    goto fail;
 > +

Run the cleanups here as well as for the fail case.

 > +  return event;
 > +
 > + fail:
 > +  do_cleanups (cleanups);
 > +  return NULL;
 > +}
 > +
 > +/* Construct a gdb.RegisterChangedEvent containing the affected
 > +   register number. */
 > +
 > +static PyObject *
 > +create_register_changed_event_object (struct frame_info *frame, 
 > +				      short regnum)
 > +{
 > +  PyObject * event;
 > +  PyObject *frame_obj = NULL;
 > +  PyObject *regnum_obj = NULL;
 > +  int failed;
 > +  struct cleanup *cleanups;
 > +
 > +  event = create_event_object (&register_changed_event_object_type);
 > +  if (event == NULL)
 > +    return NULL;
 > +
 > +  cleanups = make_cleanup_py_decref (event);
 > +
 > +  frame_obj = frame_info_to_frame_object (frame);
 > +  if (frame_obj == NULL)
 > +    goto fail;
 > +  make_cleanup_py_decref (frame_obj);
 > +
 > +  failed = evpy_add_attribute (event, "frame", frame_obj) < 0;
 > +  if (failed)
 > +    goto fail;
 > +
 > +  regnum_obj = PyLong_FromLongLong (regnum);
 > +  if (regnum_obj == NULL)
 > +    goto fail;
 > +  make_cleanup_py_decref (regnum_obj);
 > +
 > +  failed = evpy_add_attribute (event, "regnum", regnum_obj) < 0;
 > +  if (failed)
 > +    goto fail;
 > +

Run the cleanups here as well as for the fail case.

 > +  return event;
 > +
 > + fail:
 > +  do_cleanups (cleanups);
 > +  return NULL;
 > +}
 > +
 > +/* Construct a gdb.MemoryChangedEvent describing the extent of the
 > +   affected memory. */
 > +
 > +static PyObject *
 > +create_memory_changed_event_object (CORE_ADDR addr, ssize_t len)
 > +{
 > +  PyObject * event;
 > +  PyObject *addr_obj = NULL;
 > +  PyObject *len_obj = NULL;
 > +  int failed;
 > +  struct cleanup *cleanups;
 > +
 > +  event = create_event_object (&memory_changed_event_object_type);
 > +
 > +  if (event == NULL)
 > +    return NULL;
 > +  cleanups = make_cleanup_py_decref (event);
 > +
 > +  addr_obj = PyLong_FromLongLong (addr);
 > +  if (addr_obj == NULL)
 > +    goto fail;
 > +  make_cleanup_py_decref (addr_obj);
 > +
 > +  failed = evpy_add_attribute (event, "address", addr_obj) < 0;
 > +  if (failed)
 > +    goto fail;
 > +
 > +  len_obj = PyLong_FromLong (len);
 > +  if (len_obj == NULL)
 > +    goto fail;
 > +  make_cleanup_py_decref (len_obj);
 > +
 > +  failed = evpy_add_attribute (event, "length", len_obj) < 0;
 > +  if (failed)
 > +    goto fail;
 > +

Run the cleanups here as well as for the fail case.

 > +  return event;
 > +
 > + fail:
 > +  do_cleanups (cleanups);
 > +  return NULL;
 > +}
 > +
 > +/* Callback function which notifies observers when an event occurs which
 > +   calls a function in the inferior.
 > +   This function will create a new Python inferior-call event object.
 > +   Return -1 if emit fails.  */
 > +
 > +int
 > +emit_inferior_call_event (inferior_call_kind flag, ptid_t thread,
 > +			  CORE_ADDR addr)
 > +{
 > +  PyObject *event;
 > +
 > +  if (evregpy_no_listeners_p (gdb_py_events.inferior_call))
 > +    return 0;
 > +
 > +  event = create_inferior_call_event_object (flag, thread, addr);
 > +  if (event != NULL)
 > +    return evpy_emit_event (event, gdb_py_events.inferior_call);
 > +  return -1;
 > +}
 > +
 > +/* Callback when memory is modified by the user.  This function will
 > +   create a new Python memory changed event object. */
 > +
 > +int
 > +emit_memory_changed_event (CORE_ADDR addr, ssize_t len)
 > +{
 > +  PyObject *event;
 > +
 > +  if (evregpy_no_listeners_p (gdb_py_events.memory_changed))
 > +    return 0;
 > +
 > +  event = create_memory_changed_event_object (addr, len);
 > +  if (event != NULL)
 > +    return evpy_emit_event (event, gdb_py_events.memory_changed);
 > +  return -1;
 > +}
 > +
 > +/* Callback when a register is modified by the user.  This function
 > +   will create a new Python register changed event object. */
 > +
 > +int
 > +emit_register_changed_event (struct frame_info* frame, short regnum)
 > +{
 > +  PyObject *event;
 > +
 > +  if (evregpy_no_listeners_p (gdb_py_events.register_changed))
 > +    return 0;
 > +
 > +  event = create_register_changed_event_object (frame, regnum);
 > +  if (event != NULL)
 > +    return evpy_emit_event (event, gdb_py_events.register_changed);
 > +  return -1;
 > +}
 > +
 > +
 > +GDBPY_NEW_EVENT_TYPE (inferior_call_pre,
 > +		      "gdb.InferiorCallPreEvent",
 > +		      "InferiorCallPreEvent",
 > +		      "GDB inferior function pre-call event object",
 > +		      event_object_type,
 > +		      static);
 > +
 > +GDBPY_NEW_EVENT_TYPE (inferior_call_post,
 > +		      "gdb.InferiorCallPostEvent",
 > +		      "InferiorCallPostEvent",
 > +		      "GDB inferior function post-call event object",
 > +		      event_object_type,
 > +		      static);
 > +
 > +GDBPY_NEW_EVENT_TYPE (register_changed,
 > +		      "gdb.RegisterChangedEvent",
 > +		      "RegisterChangedEvent",
 > +		      "GDB register change event object",
 > +		      event_object_type,
 > +		      static);
 > +
 > +GDBPY_NEW_EVENT_TYPE (memory_changed,
 > +		      "gdb.MemoryChangedEvent",
 > +		      "MemoryChangedEvent",
 > +		      "GDB memory change event object",
 > +		      event_object_type,
 > +		      static);
 > diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
 > index 9b8b8f5..e359665 100644
 > --- a/gdb/python/py-inferior.c
 > +++ b/gdb/python/py-inferior.c
 > @@ -117,6 +117,72 @@ python_on_resume (ptid_t ptid)
 >    do_cleanups (cleanup);
 >  }
 >  
 > +/* Callback, registered as an observer, that notifies Python listeners
 > +   when an inferior function call is about to be made. */
 > +
 > +static void
 > +python_on_inferior_call_pre (ptid_t thread, CORE_ADDR address)
 > +{
 > +  struct cleanup *cleanup;
 > +
 > +  cleanup = ensure_python_env (target_gdbarch (), current_language);
 > +
 > +  if (emit_inferior_call_event (inferior_call_pre, thread, address) < 0)
 > +    gdbpy_print_stack ();
 > +
 > +  do_cleanups (cleanup);
 > +}
 > +
 > +/* Callback, registered as an observer, that notifies Python listeners
 > +   when an inferior function call has completed. */
 > +
 > +static void
 > +python_on_inferior_call_post (ptid_t thread, CORE_ADDR address)
 > +{
 > +  struct cleanup *cleanup;
 > +
 > +  cleanup = ensure_python_env (target_gdbarch (), current_language);
 > +
 > +  if (emit_inferior_call_event (inferior_call_post, thread, address) < 0)
 > +    gdbpy_print_stack ();
 > +
 > +  do_cleanups (cleanup);
 > +}
 > +
 > +/* Callback, registered as an observer, that notifies Python listeners
 > +   when a part of memory has been modified by user action (eg via a
 > +   'set' command). */
 > +
 > +static void
 > +python_on_memory_change (struct inferior *inferior, CORE_ADDR addr, ssize_t len, const bfd_byte *data)
 > +{
 > +  struct cleanup *cleanup;
 > +
 > +  cleanup = ensure_python_env (target_gdbarch (), current_language);
 > +
 > +  if (emit_memory_changed_event (addr, len) < 0)
 > +    gdbpy_print_stack ();
 > +
 > +  do_cleanups (cleanup);
 > +}
 > +
 > +/* Callback, registered as an observer, that notifies Python listeners
 > +   when a register has been modified by user action (eg via a 'set'
 > +   command). */
 > +
 > +static void
 > +python_on_register_change (struct frame_info *frame, short regnum)
 > +{
 > +  struct cleanup *cleanup;
 > +
 > +  cleanup = ensure_python_env (target_gdbarch (), current_language);
 > +
 > +  if (emit_register_changed_event (frame, regnum) < 0)
 > +    gdbpy_print_stack ();
 > +
 > +  do_cleanups (cleanup);
 > +}
 > +
 >  static void
 >  python_inferior_exit (struct inferior *inf)
 >  {
 > @@ -794,6 +860,10 @@ gdbpy_initialize_inferior (void)
 >    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_call_pre (python_on_inferior_call_pre);
 > +  observer_attach_inferior_call_post (python_on_inferior_call_post);
 > +  observer_attach_memory_changed (python_on_memory_change);
 > +  observer_attach_register_changed (python_on_register_change);
 >    observer_attach_inferior_exit (python_inferior_exit);
 >    observer_attach_new_objfile (python_new_objfile);
 >  
 > diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
 > index 6e7e600..822eb6e 100644
 > --- a/gdb/python/python-internal.h
 > +++ b/gdb/python/python-internal.h
 > @@ -466,6 +466,14 @@ int gdbpy_initialize_breakpoint_event (void)
 >    CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 >  int gdbpy_initialize_continue_event (void)
 >    CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 > +int gdbpy_initialize_inferior_call_pre_event (void)
 > +  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 > +int gdbpy_initialize_inferior_call_post_event (void)
 > +  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 > +int gdbpy_initialize_register_changed_event (void)
 > +  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 > +int gdbpy_initialize_memory_changed_event (void)
 > +  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 >  int gdbpy_initialize_exited_event (void)
 >    CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 >  int gdbpy_initialize_thread_event (void)
 > diff --git a/gdb/python/python.c b/gdb/python/python.c
 > index 40c4ec9..68e3af8 100644
 > --- a/gdb/python/python.c
 > +++ b/gdb/python/python.c
 > @@ -1754,6 +1754,10 @@ message == an error message without a stack will be printed."),
 >        || gdbpy_initialize_signal_event () < 0
 >        || gdbpy_initialize_breakpoint_event () < 0
 >        || gdbpy_initialize_continue_event () < 0
 > +      || gdbpy_initialize_inferior_call_pre_event () < 0
 > +      || gdbpy_initialize_inferior_call_post_event () < 0
 > +      || gdbpy_initialize_register_changed_event () < 0
 > +      || gdbpy_initialize_memory_changed_event () < 0
 >        || gdbpy_initialize_exited_event () < 0
 >        || gdbpy_initialize_thread_event () < 0
 >        || gdbpy_initialize_new_objfile_event ()  < 0
 > diff --git a/gdb/testsuite/gdb.python/py-events.exp b/gdb/testsuite/gdb.python/py-events.exp
 > index 92de550..f819945 100644
 > --- a/gdb/testsuite/gdb.python/py-events.exp
 > +++ b/gdb/testsuite/gdb.python/py-events.exp
 > @@ -79,6 +79,121 @@ all threads stopped"
 >  
 >  delete_breakpoints
 >  
 > +# Test inferior call events
 > +
 > +gdb_test_multiple "info threads" "get current thread" {
 > +    -re "\[^\n\r\]*process (\[0-9\]+)\[^\n\r\]*do_nothing.*$gdb_prompt $" {
 > +	set tid $expect_out(1,string)
 > +	pass "get current thread"
 > +    }
 > +}
 > +
 > +gdb_test_multiple "print do_nothing" "get address of do_nothing" {
 > +    -re "\[^\n\r\]*(0x\[0-9a-f\]+) \<do_nothing\>.*$gdb_prompt $" {
 > +	set addr $expect_out(1,string)
 > +	pass "get address of do_nothing"
 > +    }
 > +}
 > +
 > +set expected [list "event type: pre-call" "tid: $tid" "address: $addr"]
 > +lappend expected "event type: post-call" "tid: $tid" "address: $addr"
 > +gdb_test_sequence "call do_nothing()" "" $expected
 > +
 > +# Test register changed event
 > +gdb_test_no_output {set $old_sp = $sp}
 > +gdb_test_sequence {set $sp = 0} "" {
 > +    "event type: register-changed"
 > +    "frame: "
 > +    "num: "
 > +}
 > +gdb_test_sequence {set $sp = 1} "" {
 > +    "event type: register-changed"
 > +    "frame: "
 > +    "num: "
 > +}
 > +gdb_test_sequence {set $sp = $old_sp} "" {
 > +    "event type: register-changed"
 > +    "frame: "
 > +    "num: "
 > +}
 > +
 > +# Test that no register_changed event is generated on "non-user"
 > +# modifications
 > +set test "up"
 > +gdb_test_multiple {up} $test {
 > +    -re "event type: register-changed.*\r\n$gdb_prompt $" {
 > +	fail $test
 > +    }
 > +    -re "#1.*in first.*\r\n.*do_nothing.*\r\n$gdb_prompt $" {
 > +	pass $test
 > +    }
 > +}
 > +
 > +set test "down"
 > +gdb_test_multiple {down} $test {
 > +    -re "event type: register-changed.*\r\n$gdb_prompt $" {
 > +	fail $test
 > +    }
 > +    -re "#0.*do_nothing.*at.*\r\n.*void do_nothing.*\r\n$gdb_prompt $" {
 > +	pass $test
 > +    }
 > +}
 > +
 > +set test "step"
 > +gdb_test_multiple {step} $test {
 > +    -re "event type: register-changed.*\r\n$gdb_prompt $" {
 > +	fail $test
 > +    }
 > +    -re "first.*at.*\r\n.*28.*for.*\r\n$gdb_prompt $" {
 > +	pass $test
 > +    }
 > +}
 > +
 > +
 > +# Test memory changed event
 > +gdb_test_no_output {set $saved = *(int*) $sp}
 > +gdb_test_sequence {set *(int*) $sp = 0} "" {
 > +    "event type: memory-changed"
 > +    "address: "
 > +    "length: "
 > +}
 > +gdb_test_sequence {set *(int*) $sp = $saved} "" {
 > +    "event type: memory-changed"
 > +    "address: "
 > +    "length: "
 > +}
 > +
 > +# Test that no memory_changed event is generated on breakpoint
 > +# activity
 > +set test "break second"
 > +gdb_test_multiple "break second" $test {
 > +    -re "event type: memory-changed" {
 > +	fail $test
 > +    }
 > +    -re "Breakpoint (\[0-9\]+) at .*\r\n$gdb_prompt $" {
 > +	set second_breakpoint $expect_out(1,string)
 > +	pass $test
 > +    }
 > +
 > +}
 > +
 > +set test "continue to breakpoint $second_breakpoint"
 > +set expected ".*event type: continue.*\r\n"
 > +append expected ".*event type: stop.*\r\n"
 > +append expected ".*stop reason: breakpoint.*\r\n"
 > +append expected ".*all threads stopped.*\r\n$gdb_prompt $"
 > +
 > +gdb_test_multiple "continue" $test {
 > +    -re "event type: memory-changed.*\r\n$gdb_prompt $" {
 > +	fail $test
 > +    }
 > +    -re $expected {
 > +	pass $test
 > +    }
 > +}
 > +
 > +gdb_test_no_output "delete $second_breakpoint"
 > +
 >  #test exited event.
 >  gdb_test "continue" ".*event type: continue.*
 >  .*event type: exit.*
 > diff --git a/gdb/testsuite/gdb.python/py-events.py b/gdb/testsuite/gdb.python/py-events.py
 > index 1f0012a..9753d10 100644
 > --- a/gdb/testsuite/gdb.python/py-events.py
 > +++ b/gdb/testsuite/gdb.python/py-events.py
 > @@ -57,6 +57,30 @@ def new_objfile_handler (event):
 >      print ("event type: new_objfile")
 >      print ("new objfile name: %s" % (event.new_objfile.filename))
 >  
 > +def inferior_fn_handler (event):
 > +    if (isinstance (event, gdb.InferiorCallPreEvent)):
 > +        print ("event type: pre-call")
 > +    elif (isinstance (event, gdb.InferiorCallPostEvent)):
 > +        print ("event type: post-call")
 > +    else:
 > +        assert False
 > +    print ("tid: %s" % (event.tid))
 > +    print ("address: 0x%x" % (event.address))
 > +
 > +def register_changed_handler (event):
 > +    assert (isinstance (event, gdb.RegisterChangedEvent))
 > +    print ("event type: register-changed")
 > +    assert (isinstance (event.frame, gdb.Frame))
 > +    print ("frame: %s" % (event.frame))
 > +    print ("num: %s" % (event.regnum))
 > +
 > +def memory_changed_handler (event):
 > +    assert (isinstance (event, gdb.MemoryChangedEvent))
 > +    print ("event type: memory-changed")
 > +    print ("address: %s" % (event.address))
 > +    print ("length: %s" % (event.length))
 > +
 > +
 >  class test_events (gdb.Command):
 >      """Test events."""
 >  
 > @@ -68,6 +92,9 @@ class test_events (gdb.Command):
 >          gdb.events.stop.connect (breakpoint_stop_handler)
 >          gdb.events.exited.connect (exit_handler)
 >          gdb.events.cont.connect (continue_handler)
 > +        gdb.events.inferior_call.connect(inferior_fn_handler)
 > +        gdb.events.memory_changed.connect(memory_changed_handler)
 > +        gdb.events.register_changed.connect(register_changed_handler)

While I realize we don't always follow pep008 (Python Style Guide),
"Consistency Is Good" wins, so please add a space before ( here.

 >          print ("Event testers registered.")
 >  
 >  test_events ()
 > diff --git a/gdb/valops.c b/gdb/valops.c
 > index f177907..d900e50 100644
 > --- a/gdb/valops.c
 > +++ b/gdb/valops.c
 > @@ -1170,6 +1170,7 @@ value_assign (struct value *toval, struct value *fromval)
 >  	      }
 >  	  }
 >  
 > +	observer_notify_register_changed (frame, value_reg);
 >  	if (deprecated_register_changed_hook)
 >  	  deprecated_register_changed_hook (-1);
 >  	break;

Thanks!


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]