[v5] Events when inferior is modified

Message ID 537A17D3.5070607@gmail.com
State New, archived
Headers

Commit Message

Nick Bull May 19, 2014, 2:40 p.m. UTC
  Hi,

This patch was originally submitted a few months ago; at that time it
was looked at by Phil Muldoon and others, and there were no outstanding
comments.  I've updated it to take account of mainline changes, but
otherwise it is unchanged.

"This patch adds new observers, and corresponding Python events, for
various actions on an inferior: calling a function (by hand),
modifying registers or modifying memory.

A use case for these events is that by flagging these occurrences, it
can be noticed that the execution of the program is potentially
'dirty'.  (Hence why the notification doesn't carry information about
what was modified; for my purposes it's enough to know that /a/
change has happened.)"

Nick


gdb/ChangeLog

2014-05-15  Nick Bull  <nicholaspbull@gmail.com>

	* 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.



  	break;
  

Comments

Eli Zaretskii May 19, 2014, 3:05 p.m. UTC | #1
> Date: Mon, 19 May 2014 15:40:19 +0100
> From: Nick Bull <nicholaspbull@gmail.com>
> 
> This patch was originally submitted a few months ago; at that time it
> was looked at by Phil Muldoon and others, and there were no outstanding
> comments.  I've updated it to take account of mainline changes, but
> otherwise it is unchanged.
> 
> "This patch adds new observers, and corresponding Python events, for
> various actions on an inferior: calling a function (by hand),
> modifying registers or modifying memory.
> 
> A use case for these events is that by flagging these occurrences, it
> can be noticed that the execution of the program is potentially
> 'dirty'.  (Hence why the notification doesn't carry information about
> what was modified; for my purposes it's enough to know that /a/
> change has happened.)"

OK for the documentation part.  Thanks.
  
Nick Bull June 9, 2014, 2:38 p.m. UTC | #2
Ping.

On 19/05/14 15:40, Nick Bull wrote:
> Hi,
>
> This patch was originally submitted a few months ago; at that time it
> was looked at by Phil Muldoon and others, and there were no outstanding
> comments.  I've updated it to take account of mainline changes, but
> otherwise it is unchanged.
>
> "This patch adds new observers, and corresponding Python events, for
> various actions on an inferior: calling a function (by hand),
> modifying registers or modifying memory.
>
> A use case for these events is that by flagging these occurrences, it
> can be noticed that the execution of the program is potentially
> 'dirty'.  (Hence why the notification doesn't carry information about
> what was modified; for my purposes it's enough to know that /a/
> change has happened.)"
>
> Nick
>
>
> gdb/ChangeLog
>
> 2014-05-15  Nick Bull  <nicholaspbull@gmail.com>
>
>      * 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 3efedc8..d9d3906 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -353,6 +353,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 \
> @@ -389,6 +390,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 \
> @@ -2414,6 +2416,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/doc/observer.texi b/gdb/doc/observer.texi
> index 61acbb2..adaa734 100644
> --- a/gdb/doc/observer.texi
> +++ b/gdb/doc/observer.texi
> @@ -253,6 +253,18 @@ 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 (void)
> +An inferior function is about to be called.
> +@end deftypefun
> +
> +@deftypefun void inferior_call_post (void)
> +An inferior function has just been called.
> +@end deftypefun
> +
> +@deftypefun void register_changed (struct frame_info *@var{frame},
> short @var{regnum})
> +A register in the inferior has been modified.
> +@end deftypefun
> +
>   @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 90b7074..bd54537 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -2403,6 +2403,39 @@ 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 inferior_call_pre
> +Emits @code{gdb.InferiorCallPreEvent} which indicates that a function in
> +the inferior is about to be called.
> +
> +@item inferior_call_post
> +Emits @code{gdb.InferiorCallPostEvent} which indicates that a function in
> +the inferior has returned.
> +
> +@item 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 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 9907263..57679ea 100644
> --- a/gdb/infcall.c
> +++ b/gdb/infcall.c
> @@ -37,6 +37,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.  */
> @@ -621,6 +622,8 @@ call_function_by_hand (struct value *function, int
> nargs, struct value **args)
>         target_values_type = values_type;
>       }
>
> +  observer_notify_inferior_call_pre ();
> +
>     /* 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
> @@ -854,6 +857,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 ();
> +
>     /* 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..39aee99 100644
> --- a/gdb/python/py-event.h
> +++ b/gdb/python/py-event.h
> @@ -105,6 +105,16 @@ typedef struct
>   extern int emit_continue_event (ptid_t ptid);
>   extern int emit_exited_event (const LONGEST *exit_code, struct
> inferior *inf);
>
> +typedef enum
> +{
> +  inferior_call_pre,
> +  inferior_call_post,
> +} inferior_call_kind;
> +
> +extern int emit_inferior_call_event (inferior_call_kind kind);
> +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..d0a68ea
> --- /dev/null
> +++ b/gdb/python/py-infcallevent.c
> @@ -0,0 +1,213 @@
> +/* 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");
> +
> +static PyObject *
> +create_inferior_call_event_object (int flag)
> +{
> +  PyObject * event;
> +
> +  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;
> +    }
> +
> +  return event;
> +}
> +
> +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;
> +
> +  return event;
> +
> + fail:
> +  do_cleanups (cleanups);
> +  return NULL;
> +}
> +
> +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;
> +
> +  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)
> +{
> +  PyObject *event;
> +
> +  if (evregpy_no_listeners_p (gdb_py_events.inferior_call))
> +    return 0;
> +
> +  event = create_inferior_call_event_object (flag);
> +  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..27d2ffc 100644
> --- a/gdb/python/py-inferior.c
> +++ b/gdb/python/py-inferior.c
> @@ -118,6 +118,58 @@ python_on_resume (ptid_t ptid)
>   }
>
>   static void
> +python_on_inferior_call_pre (void)
> +{
> +  struct cleanup *cleanup;
> +
> +  cleanup = ensure_python_env (target_gdbarch (), current_language);
> +
> +  if (emit_inferior_call_event (inferior_call_pre) < 0)
> +    gdbpy_print_stack ();
> +
> +  do_cleanups (cleanup);
> +}
> +
> +static void
> +python_on_inferior_call_post (void)
> +{
> +  struct cleanup *cleanup;
> +
> +  cleanup = ensure_python_env (target_gdbarch (), current_language);
> +
> +  if (emit_inferior_call_event (inferior_call_post) < 0)
> +    gdbpy_print_stack ();
> +
> +  do_cleanups (cleanup);
> +}
> +
> +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);
> +}
> +
> +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)
>   {
>     struct cleanup *cleanup;
> @@ -794,6 +846,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 07650f7..7d165cc 100644
> --- a/gdb/python/python-internal.h
> +++ b/gdb/python/python-internal.h
> @@ -422,6 +422,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 cbfa73a..2d979bb 100644
> --- a/gdb/python/python.c
> +++ b/gdb/python/python.c
> @@ -1749,6 +1749,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..e235303 100644
> --- a/gdb/testsuite/gdb.python/py-events.exp
> +++ b/gdb/testsuite/gdb.python/py-events.exp
> @@ -79,6 +79,93 @@ all threads stopped"
>
>   delete_breakpoints
>
> +# Test inferior call events
> +gdb_test "call do_nothing()" ".*event type: pre-call.*
> +.*event type: continue.*
> +.*event type: post-call.*"
> +
> +# Test register changed event
> +gdb_test_no_output {set $old_sp = $sp}
> +gdb_test {set $sp = 0} ".*event type: register-changed.*
> +.*frame: .*
> +.*num: .*"
> +gdb_test {set $sp = 1} ".*event type: register-changed.*
> +.*frame: .*
> +.*num: .*"
> +gdb_test {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" {
> +    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" {
> +    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" {
> +    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 {set *(int*) $sp = 0} ".*event type: memory-changed.*
> +.*address: .*
> +.*length: .*"
> +gdb_test {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 5 at .*\r\n$gdb_prompt $" {
> +    pass $test
> +    }
> +
> +}
> +
> +set test "continue to breakpoint 5"
> +gdb_test_multiple "continue" $test {
> +     -re "event type: memory-changed" {
> +    fail $test
> +    }
> +    -re ".*event type: continue.*
> +.*event type: stop.*
> +.*stop reason: breakpoint.*
> +.*all threads stopped.*$gdb_prompt $" {
> +    pass $test
> +    }
> +}
> +
> +gdb_test_no_output "delete 5"
> +
>   #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..94f91ac 100644
> --- a/gdb/testsuite/gdb.python/py-events.py
> +++ b/gdb/testsuite/gdb.python/py-events.py
> @@ -57,6 +57,28 @@ 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
> +
> +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 +90,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)
>           print ("Event testers registered.")
>
>   test_events ()
> diff --git a/gdb/valops.c b/gdb/valops.c
> index ff25f1a..4550b95 100644
> --- a/gdb/valops.c
> +++ b/gdb/valops.c
> @@ -1169,6 +1169,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;
  
Nick Bull June 16, 2014, 5:10 p.m. UTC | #3
Hi,

This patch has been submitted several times. The code and the 
documentation have been reviewed on this list, it creates no regressions 
in the testsuite, and the copyright assignment is on file.

Is there anything else I need to do before this can be applied?

Thanks

Nick

On 19/05/14 15:40, Nick Bull wrote:
> Hi,
>
> This patch was originally submitted a few months ago; at that time it
> was looked at by Phil Muldoon and others, and there were no outstanding
> comments.  I've updated it to take account of mainline changes, but
> otherwise it is unchanged.
>
> "This patch adds new observers, and corresponding Python events, for
> various actions on an inferior: calling a function (by hand),
> modifying registers or modifying memory.
>
> A use case for these events is that by flagging these occurrences, it
> can be noticed that the execution of the program is potentially
> 'dirty'.  (Hence why the notification doesn't carry information about
> what was modified; for my purposes it's enough to know that /a/
> change has happened.)"
>
> Nick
>
>
> gdb/ChangeLog
>
> 2014-05-15  Nick Bull  <nicholaspbull@gmail.com>
>
>      * 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 3efedc8..d9d3906 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -353,6 +353,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 \
> @@ -389,6 +390,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 \
> @@ -2414,6 +2416,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/doc/observer.texi b/gdb/doc/observer.texi
> index 61acbb2..adaa734 100644
> --- a/gdb/doc/observer.texi
> +++ b/gdb/doc/observer.texi
> @@ -253,6 +253,18 @@ 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 (void)
> +An inferior function is about to be called.
> +@end deftypefun
> +
> +@deftypefun void inferior_call_post (void)
> +An inferior function has just been called.
> +@end deftypefun
> +
> +@deftypefun void register_changed (struct frame_info *@var{frame},
> short @var{regnum})
> +A register in the inferior has been modified.
> +@end deftypefun
> +
>   @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 90b7074..bd54537 100644
> --- a/gdb/doc/python.texi
> +++ b/gdb/doc/python.texi
> @@ -2403,6 +2403,39 @@ 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 inferior_call_pre
> +Emits @code{gdb.InferiorCallPreEvent} which indicates that a function in
> +the inferior is about to be called.
> +
> +@item inferior_call_post
> +Emits @code{gdb.InferiorCallPostEvent} which indicates that a function in
> +the inferior has returned.
> +
> +@item 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 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 9907263..57679ea 100644
> --- a/gdb/infcall.c
> +++ b/gdb/infcall.c
> @@ -37,6 +37,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.  */
> @@ -621,6 +622,8 @@ call_function_by_hand (struct value *function, int
> nargs, struct value **args)
>         target_values_type = values_type;
>       }
>
> +  observer_notify_inferior_call_pre ();
> +
>     /* 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
> @@ -854,6 +857,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 ();
> +
>     /* 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..39aee99 100644
> --- a/gdb/python/py-event.h
> +++ b/gdb/python/py-event.h
> @@ -105,6 +105,16 @@ typedef struct
>   extern int emit_continue_event (ptid_t ptid);
>   extern int emit_exited_event (const LONGEST *exit_code, struct
> inferior *inf);
>
> +typedef enum
> +{
> +  inferior_call_pre,
> +  inferior_call_post,
> +} inferior_call_kind;
> +
> +extern int emit_inferior_call_event (inferior_call_kind kind);
> +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..d0a68ea
> --- /dev/null
> +++ b/gdb/python/py-infcallevent.c
> @@ -0,0 +1,213 @@
> +/* 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");
> +
> +static PyObject *
> +create_inferior_call_event_object (int flag)
> +{
> +  PyObject * event;
> +
> +  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;
> +    }
> +
> +  return event;
> +}
> +
> +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;
> +
> +  return event;
> +
> + fail:
> +  do_cleanups (cleanups);
> +  return NULL;
> +}
> +
> +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;
> +
> +  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)
> +{
> +  PyObject *event;
> +
> +  if (evregpy_no_listeners_p (gdb_py_events.inferior_call))
> +    return 0;
> +
> +  event = create_inferior_call_event_object (flag);
> +  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..27d2ffc 100644
> --- a/gdb/python/py-inferior.c
> +++ b/gdb/python/py-inferior.c
> @@ -118,6 +118,58 @@ python_on_resume (ptid_t ptid)
>   }
>
>   static void
> +python_on_inferior_call_pre (void)
> +{
> +  struct cleanup *cleanup;
> +
> +  cleanup = ensure_python_env (target_gdbarch (), current_language);
> +
> +  if (emit_inferior_call_event (inferior_call_pre) < 0)
> +    gdbpy_print_stack ();
> +
> +  do_cleanups (cleanup);
> +}
> +
> +static void
> +python_on_inferior_call_post (void)
> +{
> +  struct cleanup *cleanup;
> +
> +  cleanup = ensure_python_env (target_gdbarch (), current_language);
> +
> +  if (emit_inferior_call_event (inferior_call_post) < 0)
> +    gdbpy_print_stack ();
> +
> +  do_cleanups (cleanup);
> +}
> +
> +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);
> +}
> +
> +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)
>   {
>     struct cleanup *cleanup;
> @@ -794,6 +846,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 07650f7..7d165cc 100644
> --- a/gdb/python/python-internal.h
> +++ b/gdb/python/python-internal.h
> @@ -422,6 +422,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 cbfa73a..2d979bb 100644
> --- a/gdb/python/python.c
> +++ b/gdb/python/python.c
> @@ -1749,6 +1749,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..e235303 100644
> --- a/gdb/testsuite/gdb.python/py-events.exp
> +++ b/gdb/testsuite/gdb.python/py-events.exp
> @@ -79,6 +79,93 @@ all threads stopped"
>
>   delete_breakpoints
>
> +# Test inferior call events
> +gdb_test "call do_nothing()" ".*event type: pre-call.*
> +.*event type: continue.*
> +.*event type: post-call.*"
> +
> +# Test register changed event
> +gdb_test_no_output {set $old_sp = $sp}
> +gdb_test {set $sp = 0} ".*event type: register-changed.*
> +.*frame: .*
> +.*num: .*"
> +gdb_test {set $sp = 1} ".*event type: register-changed.*
> +.*frame: .*
> +.*num: .*"
> +gdb_test {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" {
> +    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" {
> +    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" {
> +    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 {set *(int*) $sp = 0} ".*event type: memory-changed.*
> +.*address: .*
> +.*length: .*"
> +gdb_test {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 5 at .*\r\n$gdb_prompt $" {
> +    pass $test
> +    }
> +
> +}
> +
> +set test "continue to breakpoint 5"
> +gdb_test_multiple "continue" $test {
> +     -re "event type: memory-changed" {
> +    fail $test
> +    }
> +    -re ".*event type: continue.*
> +.*event type: stop.*
> +.*stop reason: breakpoint.*
> +.*all threads stopped.*$gdb_prompt $" {
> +    pass $test
> +    }
> +}
> +
> +gdb_test_no_output "delete 5"
> +
>   #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..94f91ac 100644
> --- a/gdb/testsuite/gdb.python/py-events.py
> +++ b/gdb/testsuite/gdb.python/py-events.py
> @@ -57,6 +57,28 @@ 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
> +
> +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 +90,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)
>           print ("Event testers registered.")
>
>   test_events ()
> diff --git a/gdb/valops.c b/gdb/valops.c
> index ff25f1a..4550b95 100644
> --- a/gdb/valops.c
> +++ b/gdb/valops.c
> @@ -1169,6 +1169,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;
  
Phil Muldoon June 16, 2014, 7:10 p.m. UTC | #4
On 16/06/14 18:10, Nick Bull wrote:
> Hi,
>
> This patch has been submitted several times. The code and the documentation have been reviewed on this list, it creates no regressions in the testsuite, and the copyright assignment is on file.
>
> Is there anything else I need to do before this can be applied?

It still needs a maintainer sign-off.  Do you remember if you received one? I can't find one but I might have missed it.  I cc'd Tom so it comes to his attention also.

It probably needs a NEWS entry also.

Cheers,

Phil
  
Oguz Kayral June 17, 2014, 7:31 p.m. UTC | #5
> +@item inferior_call_pre
..
> +@item inferior_call_post
..
> +@item memory_changed
..
> +@item register_changed


Probably a nitpick but these should begin with "events."

https://sourceware.org/gdb/current/onlinedocs/gdb/Events-In-Python.html
  

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 3efedc8..d9d3906 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -353,6 +353,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 \
@@ -389,6 +390,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 \
@@ -2414,6 +2416,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/doc/observer.texi b/gdb/doc/observer.texi
index 61acbb2..adaa734 100644
--- a/gdb/doc/observer.texi
+++ b/gdb/doc/observer.texi
@@ -253,6 +253,18 @@  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 (void)
+An inferior function is about to be called.
+@end deftypefun
+
+@deftypefun void inferior_call_post (void)
+An inferior function has just been called.
+@end deftypefun
+
+@deftypefun void register_changed (struct frame_info *@var{frame}, 
short @var{regnum})
+A register in the inferior has been modified.
+@end deftypefun
+
  @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 90b7074..bd54537 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -2403,6 +2403,39 @@  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 inferior_call_pre
+Emits @code{gdb.InferiorCallPreEvent} which indicates that a function in
+the inferior is about to be called.
+
+@item inferior_call_post
+Emits @code{gdb.InferiorCallPostEvent} which indicates that a function in
+the inferior has returned.
+
+@item 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 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 9907263..57679ea 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -37,6 +37,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.  */
@@ -621,6 +622,8 @@  call_function_by_hand (struct value *function, int 
nargs, struct value **args)
        target_values_type = values_type;
      }

+  observer_notify_inferior_call_pre ();
+
    /* 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
@@ -854,6 +857,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 ();
+
    /* 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..39aee99 100644
--- a/gdb/python/py-event.h
+++ b/gdb/python/py-event.h
@@ -105,6 +105,16 @@  typedef struct
  extern int emit_continue_event (ptid_t ptid);
  extern int emit_exited_event (const LONGEST *exit_code, struct 
inferior *inf);

+typedef enum
+{
+  inferior_call_pre,
+  inferior_call_post,
+} inferior_call_kind;
+
+extern int emit_inferior_call_event (inferior_call_kind kind);
+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..d0a68ea
--- /dev/null
+++ b/gdb/python/py-infcallevent.c
@@ -0,0 +1,213 @@ 
+/* 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");
+
+static PyObject *
+create_inferior_call_event_object (int flag)
+{
+  PyObject * event;
+
+  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;
+    }
+
+  return event;
+}
+
+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;
+
+  return event;
+
+ fail:
+  do_cleanups (cleanups);
+  return NULL;
+}
+
+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;
+
+  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)
+{
+  PyObject *event;
+
+  if (evregpy_no_listeners_p (gdb_py_events.inferior_call))
+    return 0;
+
+  event = create_inferior_call_event_object (flag);
+  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..27d2ffc 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -118,6 +118,58 @@  python_on_resume (ptid_t ptid)
  }

  static void
+python_on_inferior_call_pre (void)
+{
+  struct cleanup *cleanup;
+
+  cleanup = ensure_python_env (target_gdbarch (), current_language);
+
+  if (emit_inferior_call_event (inferior_call_pre) < 0)
+    gdbpy_print_stack ();
+
+  do_cleanups (cleanup);
+}
+
+static void
+python_on_inferior_call_post (void)
+{
+  struct cleanup *cleanup;
+
+  cleanup = ensure_python_env (target_gdbarch (), current_language);
+
+  if (emit_inferior_call_event (inferior_call_post) < 0)
+    gdbpy_print_stack ();
+
+  do_cleanups (cleanup);
+}
+
+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);
+}
+
+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)
  {
    struct cleanup *cleanup;
@@ -794,6 +846,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 07650f7..7d165cc 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -422,6 +422,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 cbfa73a..2d979bb 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1749,6 +1749,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..e235303 100644
--- a/gdb/testsuite/gdb.python/py-events.exp
+++ b/gdb/testsuite/gdb.python/py-events.exp
@@ -79,6 +79,93 @@  all threads stopped"

  delete_breakpoints

+# Test inferior call events
+gdb_test "call do_nothing()" ".*event type: pre-call.*
+.*event type: continue.*
+.*event type: post-call.*"
+
+# Test register changed event
+gdb_test_no_output {set $old_sp = $sp}
+gdb_test {set $sp = 0} ".*event type: register-changed.*
+.*frame: .*
+.*num: .*"
+gdb_test {set $sp = 1} ".*event type: register-changed.*
+.*frame: .*
+.*num: .*"
+gdb_test {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" {
+	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" {
+	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" {
+	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 {set *(int*) $sp = 0} ".*event type: memory-changed.*
+.*address: .*
+.*length: .*"
+gdb_test {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 5 at .*\r\n$gdb_prompt $" {
+	pass $test
+    }
+
+}
+
+set test "continue to breakpoint 5"
+gdb_test_multiple "continue" $test {
+     -re "event type: memory-changed" {
+	fail $test
+    }
+    -re ".*event type: continue.*
+.*event type: stop.*
+.*stop reason: breakpoint.*
+.*all threads stopped.*$gdb_prompt $" {
+	pass $test
+    }
+}
+
+gdb_test_no_output "delete 5"
+
  #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..94f91ac 100644
--- a/gdb/testsuite/gdb.python/py-events.py
+++ b/gdb/testsuite/gdb.python/py-events.py
@@ -57,6 +57,28 @@  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
+
+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 +90,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)
          print ("Event testers registered.")

  test_events ()
diff --git a/gdb/valops.c b/gdb/valops.c
index ff25f1a..4550b95 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1169,6 +1169,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);