public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v6] Events when inferior is modified
@ 2014-09-17 17:32 Nick Bull
  2014-09-24 14:05 ` Robert O'Callahan
                   ` (2 more replies)
  0 siblings, 3 replies; 21+ messages in thread
From: Nick Bull @ 2014-09-17 17:32 UTC (permalink / raw)
  To: gdb-patches

Hi,

[Resending because the previous discussion petered out without achieving 
signoff. The patch is identical to before except where line numbers have 
changed as the mainline has moved on.]

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>

	* 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 cbec0d2..cdfb889 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 \
@@ -2475,6 +2477,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 343ee49..3fc06f4 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -38,6 +38,16 @@ queue-signal signal-name-or-number
  -D data-directory
    This is an alias for the --data-directory option.

+* 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.
+
  * GDB supports printing and modifying of variable length automatic arrays
    as specified in ISO C99.

diff --git a/gdb/doc/observer.texi b/gdb/doc/observer.texi
index 2757587..2bc006c 100644
--- a/gdb/doc/observer.texi
+++ b/gdb/doc/observer.texi
@@ -281,6 +281,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 9f44948..fb7c682 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -2728,6 +2728,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 events.inferior_call_pre
+Emits @code{gdb.InferiorCallPreEvent} which indicates that a function in
+the inferior is about to be called.
+
+@item events.inferior_call_post
+Emits @code{gdb.InferiorCallPostEvent} which indicates that a function in
+the inferior has returned.
+
+@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..a221e77 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 ();
+
    /* 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 ();
+
    /* 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-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 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..ec9c840 100644
--- a/gdb/testsuite/gdb.python/py-events.exp
+++ b/gdb/testsuite/gdb.python/py-events.exp
@@ -79,6 +79,92 @@ all threads stopped"

  delete_breakpoints

+# Test inferior call events
+gdb_test "call do_nothing()" ".*event type: pre-call.*
+.*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 e1decf0..bc4d838 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -1168,6 +1168,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;

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v6] Events when inferior is modified
  2014-09-17 17:32 [PATCH v6] Events when inferior is modified Nick Bull
@ 2014-09-24 14:05 ` Robert O'Callahan
  2014-09-25 10:14 ` Phil Muldoon
  2014-10-01 17:52 ` Pedro Alves
  2 siblings, 0 replies; 21+ messages in thread
From: Robert O'Callahan @ 2014-09-24 14:05 UTC (permalink / raw)
  To: gdb-patches

This patch would be useful for rr too!

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v6] Events when inferior is modified
  2014-09-17 17:32 [PATCH v6] Events when inferior is modified Nick Bull
  2014-09-24 14:05 ` Robert O'Callahan
@ 2014-09-25 10:14 ` Phil Muldoon
  2014-10-01  9:50   ` Nick Bull
  2014-10-01 17:52 ` Pedro Alves
  2 siblings, 1 reply; 21+ messages in thread
From: Phil Muldoon @ 2014-09-25 10:14 UTC (permalink / raw)
  To: Nick Bull, gdb-patches

On 17/09/14 18:32, Nick Bull wrote:
> Hi,
>
> [Resending because the previous discussion petered out without achieving signoff. The patch is identical to before except where line numbers have changed as the mainline has moved on.]
>
> 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

Can a global maintainer please look at this and give a sign-off? Just a
straight yes or no will do. I have already reviewed it and asked
several times for a sign-off.  Nick has been very patient, and I think
at this point it is becoming somewhat annoying to pester for someone
to let Python patches through.

Cheers,

Phil

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v6] Events when inferior is modified
  2014-09-25 10:14 ` Phil Muldoon
@ 2014-10-01  9:50   ` Nick Bull
  0 siblings, 0 replies; 21+ messages in thread
From: Nick Bull @ 2014-10-01  9:50 UTC (permalink / raw)
  To: Phil Muldoon, gdb-patches



On 25/09/14 11:14, Phil Muldoon wrote:
> On 17/09/14 18:32, Nick Bull wrote:
>> Hi,
>>
>> [Resending because the previous discussion petered out without achieving signoff. The patch is identical to before except where line numbers have changed as the mainline has moved on.]
>>
>> 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
>
> Can a global maintainer please look at this and give a sign-off? Just a
> straight yes or no will do. I have already reviewed it and asked
> several times for a sign-off.  Nick has been very patient, and I think
> at this point it is becoming somewhat annoying to pester for someone
> to let Python patches through.
>
> Cheers,
>
> Phil
>

Thanks Phil (and Robert for support). It would be really good to get it
in this time.

Nick

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v6] Events when inferior is modified
  2014-09-17 17:32 [PATCH v6] Events when inferior is modified Nick Bull
  2014-09-24 14:05 ` Robert O'Callahan
  2014-09-25 10:14 ` Phil Muldoon
@ 2014-10-01 17:52 ` Pedro Alves
  2014-10-17 16:26   ` [PATCH v7] " Nick Bull
  2 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2014-10-01 17:52 UTC (permalink / raw)
  To: Nick Bull, gdb-patches

I see many new functions without intro comments.  Please add an intro comment
to each new function, describing the contract of the function.  If the function
is just implementing some hook, then have the function comment say that
"Inplements method foo for bar", instead of duplicating comments about
parameters and return types.

Please add comments to new enums (inferior_call_kind), and their values, etc. too.

> @@ -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 ();
> +
>     /* 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 ();
> +
>     /* Rethrow an error if we got one trying to run the inferior.  */
>
>     if (e.reason < 0)

I see an issue with these events.  On the call_pre case, the
caller can infer which thread the infcall was being run on
from the currently selected thread.

But, not so from the call_post.  The inferior may have crashed,
exited, or stopped for some breakpoint in a different thread
at that point.  So the user of that event doesn't really know
which function call finished.

GDB only does one function call at a time currently, but
that's subject to change.  It'd be best to design with that
in mind already.

Also, the documentation leaves this issue unspecified:

> +@item events.inferior_call_post
> +Emits @code{gdb.InferiorCallPostEvent} which indicates that a function in
> +the inferior has returned.

I, as I reader, immediately wonder what happens if the function
doesn't get to return, but inferior exits, etc.  E.g., "is the
event called in that scenario as well?".

> +# 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: .*"

Please write explicit \r\n instead.  (or use e.g.,
gdb_test_sequence).


> +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"

Please avoid hard coding breakpoint numbers in tests.
You can instead extract the number with expect_out
when you create the breakpoint, and store it in a
tcl variable.  Alternatively, use $bpnum.

> +gdb_test_multiple {up} $test {
> +    -re "event type: register-changed" {
> +	fail $test
> +    }

Please always match $gdb_prompt $, otherwise you're
leaving the prompt in the expect buffer, which confuses
the next gdb_test_multiple/gdb_test call.

While at it, please make sure the test passes with "make check-read1".

> +    -re "#1.*in first.*\r\n.*do_nothing.*\r\n$gdb_prompt $" {
> +	pass $test
> +    }
> +}

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

Maybe it's just me, but I find this hard to read.  I'd expect some kind
of separator between the event name and its description.  Like e.g.,:

  ** gdb.events.register_changed - A register has been altered.
or:
  ** gdb.events.register_changed: A register has been altered.
or:
  ** gdb.events.register_changed
       A register has been altered.


Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v7] Events when inferior is modified
  2014-10-01 17:52 ` Pedro Alves
@ 2014-10-17 16:26   ` Nick Bull
  2014-10-17 19:49     ` Doug Evans
  0 siblings, 1 reply; 21+ messages in thread
From: Nick Bull @ 2014-10-17 16:26 UTC (permalink / raw)
  To: Pedro Alves, gdb-patches

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
+
 @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
+
+@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
+
+@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;
+
+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)
+{
+  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);
+  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;
+
+  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;
+
+  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;
+
+  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)
         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;

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7] Events when inferior is modified
  2014-10-17 16:26   ` [PATCH v7] " Nick Bull
@ 2014-10-17 19:49     ` Doug Evans
  2014-10-17 20:00       ` Doug Evans
  2014-10-24 15:14       ` [PATCH v7] " Pedro Alves
  0 siblings, 2 replies; 21+ messages in thread
From: Doug Evans @ 2014-10-17 19:49 UTC (permalink / raw)
  To: Nick Bull; +Cc: Pedro Alves, gdb-patches

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!

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7] Events when inferior is modified
  2014-10-17 19:49     ` Doug Evans
@ 2014-10-17 20:00       ` Doug Evans
  2014-10-22 12:40         ` [PATCH v8] " Nick Bull
  2014-10-24 15:14       ` [PATCH v7] " Pedro Alves
  1 sibling, 1 reply; 21+ messages in thread
From: Doug Evans @ 2014-10-17 20:00 UTC (permalink / raw)
  To: Nick Bull; +Cc: Pedro Alves, gdb-patches

Doug Evans writes:
 > [...]
 > 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.

Well, more specifically, split it into two and export a routine
that builds a python ptid tuple from a ptid_t.

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

Well, more specifically (bleah) ...

E.g., the addr_obj attribute will have two references when we return
here.  We want it to only have one: when the event goes away
we want addr_obj to go away too.

Obviously we don't want to decref the event object itself.

This applies to my other "Run the cleanups ..." comments below.

 > 
 >  > +  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;
 >  > +}
 >  > +
 > [...]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v8] Events when inferior is modified
  2014-10-17 20:00       ` Doug Evans
@ 2014-10-22 12:40         ` Nick Bull
  2014-11-17 18:13           ` Nick Bull
  0 siblings, 1 reply; 21+ messages in thread
From: Nick Bull @ 2014-10-22 12:40 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches



On 17/10/14 21:00, Doug Evans wrote:
> Doug Evans writes:
>   > [...]


Hi Doug,

Thanks for looking through the patch. I believe this version addresses everything you mentioned.

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/py-infthread.c:
	(gdbpy_create_ptid_object): New.
	(thpy_get_ptid) Use gdbpy_create_ptid_object.
	* python/python-internal.h:
	(gdbpy_create_ptid_object)
	(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..7c4af6b 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}, int @var{regnum})
+A register in the inferior has been modified by the @value{GDBN} user.
+@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 81ec11b..d2376f6 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.ptid
+The thread in which the call will be run.
+@end defvar
+
+@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.ptid
+The thread in which the call was run.
+@end defvar
+
+@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..4a7d58a 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;
+
+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,
+				        int 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..d688b81
--- /dev/null
+++ b/gdb/python/py-infcallevent.c
@@ -0,0 +1,265 @@
+/* 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 (inferior_call_kind flag, ptid_t ptid,
+				   CORE_ADDR addr)
+{
+  int pid;
+  long tid, lwp;
+  PyObject * event;
+  PyObject *ptid_obj = NULL;
+  PyObject *addr_obj = NULL;
+  int failed;
+  struct cleanup *cleanups;
+  struct cleanup *member_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);
+
+  ptid_obj = gdbpy_create_ptid_object (ptid);
+
+  if (!ptid_obj)
+    goto fail;
+
+  member_cleanups = make_cleanup_py_decref (ptid_obj);
+
+  failed = evpy_add_attribute (event, "ptid", ptid_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;
+
+  do_cleanups (member_cleanups);
+  discard_cleanups (cleanups);
+  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, 
+				      int regnum)
+{
+  PyObject * event;
+  PyObject *frame_obj = NULL;
+  PyObject *regnum_obj = NULL;
+  int failed;
+  struct cleanup *cleanups;
+  struct cleanup *member_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;
+  member_cleanups = 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;
+
+  do_cleanups (member_cleanups);
+  discard_cleanups (cleanups);
+  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;
+  struct cleanup *member_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;
+  member_cleanups = 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;
+
+  do_cleanups (member_cleanups);
+  discard_cleanups (cleanups);
+  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, int 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..f22c343 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, int 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/py-infthread.c b/gdb/python/py-infthread.c
index e03cbd9..74570bc 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -138,23 +138,10 @@ thpy_get_ptid (PyObject *self, void *closure)
   int pid;
   long tid, lwp;
   thread_object *thread_obj = (thread_object *) self;
-  PyObject *ret;
 
   THPY_REQUIRE_VALID (thread_obj);
 
-  ret = PyTuple_New (3);
-  if (!ret)
-    return NULL;
-
-  pid = ptid_get_pid (thread_obj->thread->ptid);
-  lwp = ptid_get_lwp (thread_obj->thread->ptid);
-  tid = ptid_get_tid (thread_obj->thread->ptid);
-
-  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
-  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
-  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
-
-  return ret;
+  return gdbpy_create_ptid_object (thread_obj->thread->ptid);
 }
 
 /* Implementation of InferiorThread.switch ().
@@ -236,6 +223,30 @@ thpy_is_valid (PyObject *self, PyObject *args)
   Py_RETURN_TRUE;
 }
 
+/* Return a reference to a new Python object representing a ptid_t.
+   The object is a tuple containing (pid, lwp, tid). */
+PyObject *
+gdbpy_create_ptid_object (ptid_t ptid)
+{
+  int pid;
+  long tid, lwp;
+  PyObject *ret;
+
+  ret = PyTuple_New (3);
+  if (!ret)
+    return NULL;
+
+  pid = ptid_get_pid (ptid);
+  lwp = ptid_get_lwp (ptid);
+  tid = ptid_get_tid (ptid);
+
+  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
+  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
+  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
+ 
+  return ret;
+}
+
 /* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
    Returns the selected thread object.  */
 PyObject *
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 6e7e600..9c7bc53 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -365,6 +365,7 @@ PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
 					   const char *encoding,
 					   struct type *type);
 PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
+PyObject *gdbpy_create_ptid_object (ptid_t ptid);
 PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
 PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args);
 PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
@@ -466,6 +467,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..aa702b7 100644
--- a/gdb/testsuite/gdb.python/py-events.exp
+++ b/gdb/testsuite/gdb.python/py-events.exp
@@ -79,6 +79,123 @@ 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 process_id $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"]
+lappend expected "ptid: \\($process_id, $process_id, 0\\)" "address: $addr"
+lappend expected "event type: post-call"
+lappend expected "ptid: \\($process_id, $process_id, 0\\)" "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..67c1aea 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 ("ptid: %s" % (event.ptid,))
+    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)
         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;

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7] Events when inferior is modified
  2014-10-17 19:49     ` Doug Evans
  2014-10-17 20:00       ` Doug Evans
@ 2014-10-24 15:14       ` Pedro Alves
  2014-11-06 18:19         ` Doug Evans
  1 sibling, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2014-10-24 15:14 UTC (permalink / raw)
  To: Doug Evans, Nick Bull; +Cc: gdb-patches

On 10/17/2014 08:49 PM, Doug Evans wrote:
> 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.

How about using GDB's own unique thread number instead of
the ptid?  Doesn't seem to be any reason to expose
target-side details or identifiers here?

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7] Events when inferior is modified
  2014-10-24 15:14       ` [PATCH v7] " Pedro Alves
@ 2014-11-06 18:19         ` Doug Evans
  2014-11-07 12:21           ` Pedro Alves
  0 siblings, 1 reply; 21+ messages in thread
From: Doug Evans @ 2014-11-06 18:19 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Nick Bull, gdb-patches

On Fri, Oct 24, 2014 at 8:13 AM, Pedro Alves <palves@redhat.com> wrote:
> On 10/17/2014 08:49 PM, Doug Evans wrote:
>> 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.
>
> How about using GDB's own unique thread number instead of
> the ptid?  Doesn't seem to be any reason to expose
> target-side details or identifiers here?

Yeah, I thought of that.
We already expose ptids.
Plus one can look at them as just an id: something you receive, pass
around, and print.
But I don't have a strong preference, other than consistency.
Whatever we pick we need to use it for everything (barring compelling
reasons to do otherwise).

Setting aside concerns of exposing target details,
are there other technical reasons to prefer one over the other?
Here's a question that comes to mind.
Internally we use ptids and not thread numbers.
Do any of the reasons for doing so carry over to the Python API?

[While IWBN if internal and external APIs used the same id everywhere,
I'm more concerned with more technical details of picking one over the
other.  E.g., Thread IDs are more transient, they get recycled more
often, but technically ptids can get recycled too.]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7] Events when inferior is modified
  2014-11-06 18:19         ` Doug Evans
@ 2014-11-07 12:21           ` Pedro Alves
  2014-11-07 17:04             ` Doug Evans
  0 siblings, 1 reply; 21+ messages in thread
From: Pedro Alves @ 2014-11-07 12:21 UTC (permalink / raw)
  To: Doug Evans; +Cc: Nick Bull, gdb-patches

On 11/06/2014 06:19 PM, Doug Evans wrote:
> On Fri, Oct 24, 2014 at 8:13 AM, Pedro Alves <palves@redhat.com> wrote:
>> On 10/17/2014 08:49 PM, Doug Evans wrote:
>>> 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.
>>
>> How about using GDB's own unique thread number instead of
>> the ptid?  Doesn't seem to be any reason to expose
>> target-side details or identifiers here?
> 
> Yeah, I thought of that.
> We already expose ptids.

OOC, is that just py-infthread.c:thpy_get_ptid, or elsewhere
too?

> Plus one can look at them as just an id: something you receive, pass
> around, and print.

Yes, as long as we pass the whole ptid, that works.  Let's go with that.

> But I don't have a strong preference, other than consistency.
> Whatever we pick we need to use it for everything (barring compelling
> reasons to do otherwise).
> 
> Setting aside concerns of exposing target details,
> are there other technical reasons to prefer one over the other?

I'm been trying to come up with some rule, but it's very hard
to say in general.  I was thinking that given that we're
specifically referring to a user-visible thread, we should
prefer the GDB number, like we generally expose GDB
thread numbers to MI.  But maybe we'll find a case in the future
where we do an infcall on some execution object that isn't mapped
to a visible GDB thread, and so a ptid would work better.

> Here's a question that comes to mind.
> Internally we use ptids and not thread numbers.

GDB didn't use to model non-threaded inferiors as single-threaded.
Until pthreads or similar was detected as loaded in the inferior,
"info threads" would came out empty.  So there's that historical part.

(Related, I sometimes wonder about whether we should expose execution
objects finer than "standard" threads, like fibers / lightweight execution
agents (c++ N3874) / coroutines to the user as first class
"GDB threads", if the runtime has those, thus think of "GDB threads"
as the finer execution object type the user can interact with.
Or maybe that's not the best model, and exposing "fibers"
as first class citizens distinct from "threads" would be better.)

On the target/backend/core run control side, we need to work
with ptids, as we're interfacing with the lower debug APIs, which of
course now nothing about GDB's thread ids, and sometimes need to
interface with execution objects even if there's no GDB thread
mapped to it, yet, or ever.

> Do any of the reasons for doing so carry over to the Python API?

I guess it depends on which level we're positioning the Python API.
If at the same level as CLI/MI, and/or directly exposing
the user-visible objects/concepts, then GDB ids seems preferable.
Otherwise, if working at lower levels, a ptid may be better.

Anyway.  To reiterate, I agree.  If we're just looking at
the (whole) ptid as just an id: something you receive, pass around,
and print, then it works for me.

> 
> [While IWBN if internal and external APIs used the same id everywhere,
> I'm more concerned with more technical details of picking one over the
> other.  E.g., Thread IDs are more transient, they get recycled more
> often, but technically ptids can get recycled too.]

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v7] Events when inferior is modified
  2014-11-07 12:21           ` Pedro Alves
@ 2014-11-07 17:04             ` Doug Evans
  0 siblings, 0 replies; 21+ messages in thread
From: Doug Evans @ 2014-11-07 17:04 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Nick Bull, gdb-patches

On Fri, Nov 7, 2014 at 4:21 AM, Pedro Alves <palves@redhat.com> wrote:
> On 11/06/2014 06:19 PM, Doug Evans wrote:
>> On Fri, Oct 24, 2014 at 8:13 AM, Pedro Alves <palves@redhat.com> wrote:
>>> On 10/17/2014 08:49 PM, Doug Evans wrote:
>>>> 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.
>>>
>>> How about using GDB's own unique thread number instead of
>>> the ptid?  Doesn't seem to be any reason to expose
>>> target-side details or identifiers here?
>>
>> Yeah, I thought of that.
>> We already expose ptids.
>
> OOC, is that just py-infthread.c:thpy_get_ptid, or elsewhere
> too?
>
>> Plus one can look at them as just an id: something you receive, pass
>> around, and print.
>
> Yes, as long as we pass the whole ptid, that works.  Let's go with that.
>
>> But I don't have a strong preference, other than consistency.
>> Whatever we pick we need to use it for everything (barring compelling
>> reasons to do otherwise).
>>
>> Setting aside concerns of exposing target details,
>> are there other technical reasons to prefer one over the other?
>
> I'm been trying to come up with some rule, but it's very hard
> to say in general.  I was thinking that given that we're
> specifically referring to a user-visible thread, we should
> prefer the GDB number, like we generally expose GDB
> thread numbers to MI.  But maybe we'll find a case in the future
> where we do an infcall on some execution object that isn't mapped
> to a visible GDB thread, and so a ptid would work better.
>
>> Here's a question that comes to mind.
>> Internally we use ptids and not thread numbers.
>
> GDB didn't use to model non-threaded inferiors as single-threaded.
> Until pthreads or similar was detected as loaded in the inferior,
> "info threads" would came out empty.  So there's that historical part.
>
> (Related, I sometimes wonder about whether we should expose execution
> objects finer than "standard" threads, like fibers / lightweight execution
> agents (c++ N3874) / coroutines to the user as first class
> "GDB threads", if the runtime has those, thus think of "GDB threads"
> as the finer execution object type the user can interact with.
> Or maybe that's not the best model, and exposing "fibers"
> as first class citizens distinct from "threads" would be better.)
>
> On the target/backend/core run control side, we need to work
> with ptids, as we're interfacing with the lower debug APIs, which of
> course now nothing about GDB's thread ids, and sometimes need to
> interface with execution objects even if there's no GDB thread
> mapped to it, yet, or ever.
>
>> Do any of the reasons for doing so carry over to the Python API?
>
> I guess it depends on which level we're positioning the Python API.
> If at the same level as CLI/MI, and/or directly exposing
> the user-visible objects/concepts, then GDB ids seems preferable.
> Otherwise, if working at lower levels, a ptid may be better.
>
> Anyway.  To reiterate, I agree.  If we're just looking at
> the (whole) ptid as just an id: something you receive, pass around,
> and print, then it works for me.
>
>>
>> [While IWBN if internal and external APIs used the same id everywhere,
>> I'm more concerned with more technical details of picking one over the
>> other.  E.g., Thread IDs are more transient, they get recycled more
>> often, but technically ptids can get recycled too.]

One thing that occurs to me is that gdb can inform users when thread
ids get recycled, not so with ptids.  [An unlikely thing to need to
worry about in general, but I'm all for being robust where we can.]

Plus we can provide routines to map one to the other.
[Hmmm, if we give out thread ids, do we need to attach a "generation
number" or some such to them?]

So at the moment I guess I'm undecided.

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8] Events when inferior is modified
  2014-10-22 12:40         ` [PATCH v8] " Nick Bull
@ 2014-11-17 18:13           ` Nick Bull
  2014-11-17 21:25             ` Doug Evans
  0 siblings, 1 reply; 21+ messages in thread
From: Nick Bull @ 2014-11-17 18:13 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches

Ping.

The discussion on exposing ptids vs GDB thread numbers looks to have ended without a clear
conclusion. This patch uses ptids - is it OK?

Thanks

Nick

On 22/10/14 13:40, Nick Bull wrote:
> 
> 
> On 17/10/14 21:00, Doug Evans wrote:
>> Doug Evans writes:
>>    > [...]
> 
> 
> Hi Doug,
> 
> Thanks for looking through the patch. I believe this version addresses everything you mentioned.
> 
> 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/py-infthread.c:
> 	(gdbpy_create_ptid_object): New.
> 	(thpy_get_ptid) Use gdbpy_create_ptid_object.
> 	* python/python-internal.h:
> 	(gdbpy_create_ptid_object)
> 	(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..7c4af6b 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}, int @var{regnum})
> +A register in the inferior has been modified by the @value{GDBN} user.
> +@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 81ec11b..d2376f6 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.ptid
> +The thread in which the call will be run.
> +@end defvar
> +
> +@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.ptid
> +The thread in which the call was run.
> +@end defvar
> +
> +@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..4a7d58a 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;
> +
> +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,
> +				        int 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..d688b81
> --- /dev/null
> +++ b/gdb/python/py-infcallevent.c
> @@ -0,0 +1,265 @@
> +/* 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 (inferior_call_kind flag, ptid_t ptid,
> +				   CORE_ADDR addr)
> +{
> +  int pid;
> +  long tid, lwp;
> +  PyObject * event;
> +  PyObject *ptid_obj = NULL;
> +  PyObject *addr_obj = NULL;
> +  int failed;
> +  struct cleanup *cleanups;
> +  struct cleanup *member_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);
> +
> +  ptid_obj = gdbpy_create_ptid_object (ptid);
> +
> +  if (!ptid_obj)
> +    goto fail;
> +
> +  member_cleanups = make_cleanup_py_decref (ptid_obj);
> +
> +  failed = evpy_add_attribute (event, "ptid", ptid_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;
> +
> +  do_cleanups (member_cleanups);
> +  discard_cleanups (cleanups);
> +  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,
> +				      int regnum)
> +{
> +  PyObject * event;
> +  PyObject *frame_obj = NULL;
> +  PyObject *regnum_obj = NULL;
> +  int failed;
> +  struct cleanup *cleanups;
> +  struct cleanup *member_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;
> +  member_cleanups = 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;
> +
> +  do_cleanups (member_cleanups);
> +  discard_cleanups (cleanups);
> +  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;
> +  struct cleanup *member_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;
> +  member_cleanups = 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;
> +
> +  do_cleanups (member_cleanups);
> +  discard_cleanups (cleanups);
> +  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, int 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..f22c343 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, int 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/py-infthread.c b/gdb/python/py-infthread.c
> index e03cbd9..74570bc 100644
> --- a/gdb/python/py-infthread.c
> +++ b/gdb/python/py-infthread.c
> @@ -138,23 +138,10 @@ thpy_get_ptid (PyObject *self, void *closure)
>     int pid;
>     long tid, lwp;
>     thread_object *thread_obj = (thread_object *) self;
> -  PyObject *ret;
>   
>     THPY_REQUIRE_VALID (thread_obj);
>   
> -  ret = PyTuple_New (3);
> -  if (!ret)
> -    return NULL;
> -
> -  pid = ptid_get_pid (thread_obj->thread->ptid);
> -  lwp = ptid_get_lwp (thread_obj->thread->ptid);
> -  tid = ptid_get_tid (thread_obj->thread->ptid);
> -
> -  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
> -  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
> -  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
> -
> -  return ret;
> +  return gdbpy_create_ptid_object (thread_obj->thread->ptid);
>   }
>   
>   /* Implementation of InferiorThread.switch ().
> @@ -236,6 +223,30 @@ thpy_is_valid (PyObject *self, PyObject *args)
>     Py_RETURN_TRUE;
>   }
>   
> +/* Return a reference to a new Python object representing a ptid_t.
> +   The object is a tuple containing (pid, lwp, tid). */
> +PyObject *
> +gdbpy_create_ptid_object (ptid_t ptid)
> +{
> +  int pid;
> +  long tid, lwp;
> +  PyObject *ret;
> +
> +  ret = PyTuple_New (3);
> +  if (!ret)
> +    return NULL;
> +
> +  pid = ptid_get_pid (ptid);
> +  lwp = ptid_get_lwp (ptid);
> +  tid = ptid_get_tid (ptid);
> +
> +  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
> +  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
> +  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
> +
> +  return ret;
> +}
> +
>   /* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
>      Returns the selected thread object.  */
>   PyObject *
> diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
> index 6e7e600..9c7bc53 100644
> --- a/gdb/python/python-internal.h
> +++ b/gdb/python/python-internal.h
> @@ -365,6 +365,7 @@ PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
>   					   const char *encoding,
>   					   struct type *type);
>   PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
> +PyObject *gdbpy_create_ptid_object (ptid_t ptid);
>   PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
>   PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args);
>   PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
> @@ -466,6 +467,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..aa702b7 100644
> --- a/gdb/testsuite/gdb.python/py-events.exp
> +++ b/gdb/testsuite/gdb.python/py-events.exp
> @@ -79,6 +79,123 @@ 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 process_id $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"]
> +lappend expected "ptid: \\($process_id, $process_id, 0\\)" "address: $addr"
> +lappend expected "event type: post-call"
> +lappend expected "ptid: \\($process_id, $process_id, 0\\)" "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..67c1aea 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 ("ptid: %s" % (event.ptid,))
> +    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)
>           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;
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8] Events when inferior is modified
  2014-11-17 18:13           ` Nick Bull
@ 2014-11-17 21:25             ` Doug Evans
  2014-11-18 16:37               ` Nick Bull
  0 siblings, 1 reply; 21+ messages in thread
From: Doug Evans @ 2014-11-17 21:25 UTC (permalink / raw)
  To: Nick Bull; +Cc: gdb-patches, Pedro Alves

Hi.

Let's go with ptids.

On Mon, Nov 17, 2014 at 10:13 AM, Nick Bull <nicholaspbull@gmail.com> wrote:
> Ping.
>
> The discussion on exposing ptids vs GDB thread numbers looks to have ended without a clear
> conclusion. This patch uses ptids - is it OK?
>
> Thanks
>
> Nick
>
> On 22/10/14 13:40, Nick Bull wrote:
>>
>>
>> On 17/10/14 21:00, Doug Evans wrote:
>>> Doug Evans writes:
>>>    > [...]
>>
>>
>> Hi Doug,
>>
>> Thanks for looking through the patch. I believe this version addresses everything you mentioned.
>>
>> 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/py-infthread.c:
>>       (gdbpy_create_ptid_object): New.
>>       (thpy_get_ptid) Use gdbpy_create_ptid_object.
>>       * python/python-internal.h:
>>       (gdbpy_create_ptid_object)
>>       (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..7c4af6b 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}, int @var{regnum})
>> +A register in the inferior has been modified by the @value{GDBN} user.
>> +@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 81ec11b..d2376f6 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.ptid
>> +The thread in which the call will be run.
>> +@end defvar
>> +
>> +@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.ptid
>> +The thread in which the call was run.
>> +@end defvar
>> +
>> +@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..4a7d58a 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;
>> +
>> +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,
>> +                                     int 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..d688b81
>> --- /dev/null
>> +++ b/gdb/python/py-infcallevent.c
>> @@ -0,0 +1,265 @@
>> +/* 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 (inferior_call_kind flag, ptid_t ptid,
>> +                                CORE_ADDR addr)
>> +{
>> +  int pid;
>> +  long tid, lwp;
>> +  PyObject * event;
>> +  PyObject *ptid_obj = NULL;
>> +  PyObject *addr_obj = NULL;
>> +  int failed;
>> +  struct cleanup *cleanups;
>> +  struct cleanup *member_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);
>> +
>> +  ptid_obj = gdbpy_create_ptid_object (ptid);
>> +
>> +  if (!ptid_obj)
>> +    goto fail;
>> +
>> +  member_cleanups = make_cleanup_py_decref (ptid_obj);
>> +
>> +  failed = evpy_add_attribute (event, "ptid", ptid_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;
>> +
>> +  do_cleanups (member_cleanups);
>> +  discard_cleanups (cleanups);
>> +  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,
>> +                                   int regnum)
>> +{
>> +  PyObject * event;
>> +  PyObject *frame_obj = NULL;
>> +  PyObject *regnum_obj = NULL;
>> +  int failed;
>> +  struct cleanup *cleanups;
>> +  struct cleanup *member_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;
>> +  member_cleanups = 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;
>> +
>> +  do_cleanups (member_cleanups);
>> +  discard_cleanups (cleanups);
>> +  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;
>> +  struct cleanup *member_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;
>> +  member_cleanups = 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;
>> +
>> +  do_cleanups (member_cleanups);
>> +  discard_cleanups (cleanups);
>> +  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, int 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..f22c343 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, int 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/py-infthread.c b/gdb/python/py-infthread.c
>> index e03cbd9..74570bc 100644
>> --- a/gdb/python/py-infthread.c
>> +++ b/gdb/python/py-infthread.c
>> @@ -138,23 +138,10 @@ thpy_get_ptid (PyObject *self, void *closure)
>>     int pid;
>>     long tid, lwp;
>>     thread_object *thread_obj = (thread_object *) self;
>> -  PyObject *ret;
>>
>>     THPY_REQUIRE_VALID (thread_obj);
>>
>> -  ret = PyTuple_New (3);
>> -  if (!ret)
>> -    return NULL;
>> -
>> -  pid = ptid_get_pid (thread_obj->thread->ptid);
>> -  lwp = ptid_get_lwp (thread_obj->thread->ptid);
>> -  tid = ptid_get_tid (thread_obj->thread->ptid);
>> -
>> -  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
>> -  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
>> -  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
>> -
>> -  return ret;
>> +  return gdbpy_create_ptid_object (thread_obj->thread->ptid);
>>   }
>>
>>   /* Implementation of InferiorThread.switch ().
>> @@ -236,6 +223,30 @@ thpy_is_valid (PyObject *self, PyObject *args)
>>     Py_RETURN_TRUE;
>>   }
>>
>> +/* Return a reference to a new Python object representing a ptid_t.
>> +   The object is a tuple containing (pid, lwp, tid). */
>> +PyObject *
>> +gdbpy_create_ptid_object (ptid_t ptid)
>> +{
>> +  int pid;
>> +  long tid, lwp;
>> +  PyObject *ret;
>> +
>> +  ret = PyTuple_New (3);
>> +  if (!ret)
>> +    return NULL;
>> +
>> +  pid = ptid_get_pid (ptid);
>> +  lwp = ptid_get_lwp (ptid);
>> +  tid = ptid_get_tid (ptid);
>> +
>> +  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
>> +  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
>> +  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
>> +
>> +  return ret;
>> +}
>> +
>>   /* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
>>      Returns the selected thread object.  */
>>   PyObject *
>> diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
>> index 6e7e600..9c7bc53 100644
>> --- a/gdb/python/python-internal.h
>> +++ b/gdb/python/python-internal.h
>> @@ -365,6 +365,7 @@ PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
>>                                          const char *encoding,
>>                                          struct type *type);
>>   PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
>> +PyObject *gdbpy_create_ptid_object (ptid_t ptid);
>>   PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
>>   PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args);
>>   PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
>> @@ -466,6 +467,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..aa702b7 100644
>> --- a/gdb/testsuite/gdb.python/py-events.exp
>> +++ b/gdb/testsuite/gdb.python/py-events.exp
>> @@ -79,6 +79,123 @@ 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 process_id $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"]
>> +lappend expected "ptid: \\($process_id, $process_id, 0\\)" "address: $addr"
>> +lappend expected "event type: post-call"
>> +lappend expected "ptid: \\($process_id, $process_id, 0\\)" "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..67c1aea 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 ("ptid: %s" % (event.ptid,))
>> +    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)
>>           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;
>>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8] Events when inferior is modified
  2014-11-17 21:25             ` Doug Evans
@ 2014-11-18 16:37               ` Nick Bull
  2014-11-20  0:21                 ` Doug Evans
  0 siblings, 1 reply; 21+ messages in thread
From: Nick Bull @ 2014-11-18 16:37 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches, Pedro Alves

On 17/11/14 21:24, Doug Evans wrote:
> Hi.
> 
> Let's go with ptids.

Doug,

Thanks. So is this version of the patch now good to apply?  Note that I don't have
push access so will need someone to commit it on my behalf.

Thanks

Nick



> 
> On Mon, Nov 17, 2014 at 10:13 AM, Nick Bull <nicholaspbull@gmail.com> wrote:
>> Ping.
>>
>> The discussion on exposing ptids vs GDB thread numbers looks to have ended without a clear
>> conclusion. This patch uses ptids - is it OK?
>>
>> Thanks
>>
>> Nick
>>
>> On 22/10/14 13:40, Nick Bull wrote:
>>>
>>>
>>> On 17/10/14 21:00, Doug Evans wrote:
>>>> Doug Evans writes:
>>>>     > [...]
>>>
>>>
>>> Hi Doug,
>>>
>>> Thanks for looking through the patch. I believe this version addresses everything you mentioned.
>>>
>>> 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/py-infthread.c:
>>>        (gdbpy_create_ptid_object): New.
>>>        (thpy_get_ptid) Use gdbpy_create_ptid_object.
>>>        * python/python-internal.h:
>>>        (gdbpy_create_ptid_object)
>>>        (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..7c4af6b 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}, int @var{regnum})
>>> +A register in the inferior has been modified by the @value{GDBN} user.
>>> +@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 81ec11b..d2376f6 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.ptid
>>> +The thread in which the call will be run.
>>> +@end defvar
>>> +
>>> +@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.ptid
>>> +The thread in which the call was run.
>>> +@end defvar
>>> +
>>> +@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..4a7d58a 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;
>>> +
>>> +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,
>>> +                                     int 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..d688b81
>>> --- /dev/null
>>> +++ b/gdb/python/py-infcallevent.c
>>> @@ -0,0 +1,265 @@
>>> +/* 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 (inferior_call_kind flag, ptid_t ptid,
>>> +                                CORE_ADDR addr)
>>> +{
>>> +  int pid;
>>> +  long tid, lwp;
>>> +  PyObject * event;
>>> +  PyObject *ptid_obj = NULL;
>>> +  PyObject *addr_obj = NULL;
>>> +  int failed;
>>> +  struct cleanup *cleanups;
>>> +  struct cleanup *member_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);
>>> +
>>> +  ptid_obj = gdbpy_create_ptid_object (ptid);
>>> +
>>> +  if (!ptid_obj)
>>> +    goto fail;
>>> +
>>> +  member_cleanups = make_cleanup_py_decref (ptid_obj);
>>> +
>>> +  failed = evpy_add_attribute (event, "ptid", ptid_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;
>>> +
>>> +  do_cleanups (member_cleanups);
>>> +  discard_cleanups (cleanups);
>>> +  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,
>>> +                                   int regnum)
>>> +{
>>> +  PyObject * event;
>>> +  PyObject *frame_obj = NULL;
>>> +  PyObject *regnum_obj = NULL;
>>> +  int failed;
>>> +  struct cleanup *cleanups;
>>> +  struct cleanup *member_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;
>>> +  member_cleanups = 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;
>>> +
>>> +  do_cleanups (member_cleanups);
>>> +  discard_cleanups (cleanups);
>>> +  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;
>>> +  struct cleanup *member_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;
>>> +  member_cleanups = 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;
>>> +
>>> +  do_cleanups (member_cleanups);
>>> +  discard_cleanups (cleanups);
>>> +  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, int 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..f22c343 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, int 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/py-infthread.c b/gdb/python/py-infthread.c
>>> index e03cbd9..74570bc 100644
>>> --- a/gdb/python/py-infthread.c
>>> +++ b/gdb/python/py-infthread.c
>>> @@ -138,23 +138,10 @@ thpy_get_ptid (PyObject *self, void *closure)
>>>      int pid;
>>>      long tid, lwp;
>>>      thread_object *thread_obj = (thread_object *) self;
>>> -  PyObject *ret;
>>>
>>>      THPY_REQUIRE_VALID (thread_obj);
>>>
>>> -  ret = PyTuple_New (3);
>>> -  if (!ret)
>>> -    return NULL;
>>> -
>>> -  pid = ptid_get_pid (thread_obj->thread->ptid);
>>> -  lwp = ptid_get_lwp (thread_obj->thread->ptid);
>>> -  tid = ptid_get_tid (thread_obj->thread->ptid);
>>> -
>>> -  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
>>> -  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
>>> -  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
>>> -
>>> -  return ret;
>>> +  return gdbpy_create_ptid_object (thread_obj->thread->ptid);
>>>    }
>>>
>>>    /* Implementation of InferiorThread.switch ().
>>> @@ -236,6 +223,30 @@ thpy_is_valid (PyObject *self, PyObject *args)
>>>      Py_RETURN_TRUE;
>>>    }
>>>
>>> +/* Return a reference to a new Python object representing a ptid_t.
>>> +   The object is a tuple containing (pid, lwp, tid). */
>>> +PyObject *
>>> +gdbpy_create_ptid_object (ptid_t ptid)
>>> +{
>>> +  int pid;
>>> +  long tid, lwp;
>>> +  PyObject *ret;
>>> +
>>> +  ret = PyTuple_New (3);
>>> +  if (!ret)
>>> +    return NULL;
>>> +
>>> +  pid = ptid_get_pid (ptid);
>>> +  lwp = ptid_get_lwp (ptid);
>>> +  tid = ptid_get_tid (ptid);
>>> +
>>> +  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
>>> +  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
>>> +  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
>>> +
>>> +  return ret;
>>> +}
>>> +
>>>    /* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
>>>       Returns the selected thread object.  */
>>>    PyObject *
>>> diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
>>> index 6e7e600..9c7bc53 100644
>>> --- a/gdb/python/python-internal.h
>>> +++ b/gdb/python/python-internal.h
>>> @@ -365,6 +365,7 @@ PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
>>>                                           const char *encoding,
>>>                                           struct type *type);
>>>    PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
>>> +PyObject *gdbpy_create_ptid_object (ptid_t ptid);
>>>    PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
>>>    PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args);
>>>    PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
>>> @@ -466,6 +467,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..aa702b7 100644
>>> --- a/gdb/testsuite/gdb.python/py-events.exp
>>> +++ b/gdb/testsuite/gdb.python/py-events.exp
>>> @@ -79,6 +79,123 @@ 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 process_id $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"]
>>> +lappend expected "ptid: \\($process_id, $process_id, 0\\)" "address: $addr"
>>> +lappend expected "event type: post-call"
>>> +lappend expected "ptid: \\($process_id, $process_id, 0\\)" "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..67c1aea 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 ("ptid: %s" % (event.ptid,))
>>> +    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)
>>>            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;
>>>

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8] Events when inferior is modified
  2014-11-18 16:37               ` Nick Bull
@ 2014-11-20  0:21                 ` Doug Evans
  2014-11-20 10:36                   ` Nick Bull
  0 siblings, 1 reply; 21+ messages in thread
From: Doug Evans @ 2014-11-20  0:21 UTC (permalink / raw)
  To: Nick Bull; +Cc: gdb-patches, Pedro Alves

On Tue, Nov 18, 2014 at 8:37 AM, Nick Bull <nicholaspbull@gmail.com> wrote:
> On 17/11/14 21:24, Doug Evans wrote:
>> Hi.
>>
>> Let's go with ptids.
>
> Doug,
>
> Thanks. So is this version of the patch now good to apply?  Note that I don't have
> push access so will need someone to commit it on my behalf.

Hi.

I gave the patch one last look-over.
It's almost good to apply but what remains is whitespace and a file name change.

I wouldn't expect to find the register/memory change events in py-infcallevent.c
so how about naming the file py-infevents.c?

I'll make those changes and commit.
[Saves you a round trip.]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8] Events when inferior is modified
  2014-11-20  0:21                 ` Doug Evans
@ 2014-11-20 10:36                   ` Nick Bull
  2014-12-02 19:20                     ` Doug Evans
  0 siblings, 1 reply; 21+ messages in thread
From: Nick Bull @ 2014-11-20 10:36 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches, Pedro Alves



On 20/11/14 00:21, Doug Evans wrote:
> On Tue, Nov 18, 2014 at 8:37 AM, Nick Bull <nicholaspbull@gmail.com> wrote:
>> On 17/11/14 21:24, Doug Evans wrote:
>>> Hi.
>>>
>>> Let's go with ptids.
>>
>> Doug,
>>
>> Thanks. So is this version of the patch now good to apply?  Note that I don't have
>> push access so will need someone to commit it on my behalf.
> 
> Hi.
> 
> I gave the patch one last look-over.
> It's almost good to apply but what remains is whitespace and a file name change.
> 
> I wouldn't expect to find the register/memory change events in py-infcallevent.c
> so how about naming the file py-infevents.c?
> 
> I'll make those changes and commit.
> [Saves you a round trip.]

Excellent. Thanks to you and everyone else for your help.

Nick
 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8] Events when inferior is modified
  2014-11-20 10:36                   ` Nick Bull
@ 2014-12-02 19:20                     ` Doug Evans
  2014-12-04 10:45                       ` Nick Bull
  2014-12-15  8:33                       ` Yao Qi
  0 siblings, 2 replies; 21+ messages in thread
From: Doug Evans @ 2014-12-02 19:20 UTC (permalink / raw)
  To: Nick Bull; +Cc: gdb-patches, Pedro Alves

Nick Bull writes:
 > 
 > 
 > On 20/11/14 00:21, Doug Evans wrote:
 > > On Tue, Nov 18, 2014 at 8:37 AM, Nick Bull <nicholaspbull@gmail.com> wrote:
 > >> On 17/11/14 21:24, Doug Evans wrote:
 > >>> Hi.
 > >>>
 > >>> Let's go with ptids.
 > >>
 > >> Doug,
 > >>
 > >> Thanks. So is this version of the patch now good to apply?  Note that I don't have
 > >> push access so will need someone to commit it on my behalf.
 > > 
 > > Hi.
 > > 
 > > I gave the patch one last look-over.
 > > It's almost good to apply but what remains is whitespace and a file name change.
 > > 
 > > I wouldn't expect to find the register/memory change events in py-infcallevent.c
 > > so how about naming the file py-infevents.c?
 > > 
 > > I'll make those changes and commit.
 > > [Saves you a round trip.]
 > 
 > Excellent. Thanks to you and everyone else for your help.

Here is what I committed.

I tweaked a few things:

- updated copyright year in one file
- hardcoded line number in py-events.exp
- ChangeLog
- inferior_fn_handle -> inferior_call_handler
- whitespace
- py-infcallevent.c -> py-infevents.c

I also forgot to set the git Author before pushing it upstream,
so I reverted and recommitted with the correct Author.
[I know one can change the author of the most recent
commit with git commit --amend, but I had already pushed it
upstream, and I wasn't sure of a simple way of fixing things
without reverting/recommiting.]

2014-12-02  Nick Bull  <nicholaspbull@gmail.com>

	* NEWS: Mention new Python events.
	* Makefile.in (SUBDIR_PYTHON_OBS): Add py-infevents.o.
	(SUBDIR_PYTHON_SRCS): Add py-infevents.c.
	(py-infevents.o): New rule.
	* 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-infevents.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/py-infthread.c(gdbpy_create_ptid_object): New.
	(thpy_get_ptid) Use gdbpy_create_ptid_object.
	* python/python-internal.h:
	(gdbpy_create_ptid_object)
	(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-12-02  Nick Bull  <nicholaspbull@gmail.com>

	* python.texi (Events In Python): Document new events
	InferiorCallPreEvent, InferiorCallPostEvent, MemoryChangedEvent
	and RegisterChangedEvent.

gdb/testsuite/ChangeLog

2014-12-02  Nick Bull  <nicholaspbull@gmail.com>

	* gdb.python/py-events.py (inferior_call_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 1da8af6..81c8c94 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -361,6 +361,7 @@ SUBDIR_PYTHON_OBS = \
 	py-function.o \
 	py-gdb-readline.o \
 	py-inferior.o \
+	py-infevents.o \
 	py-infthread.o \
 	py-lazy-string.o \
 	py-linetable.o \
@@ -399,6 +400,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-function.c \
 	python/py-gdb-readline.c \
 	python/py-inferior.c \
+	python/py-infevents.c \
 	python/py-infthread.c \
 	python/py-lazy-string.c \
 	python/py-linetable.c \
@@ -2478,6 +2480,10 @@ py-inferior.o: $(srcdir)/python/py-inferior.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-inferior.c
 	$(POSTCOMPILE)
 
+py-infevents.o: $(srcdir)/python/py-infevents.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-infevents.c
+	$(POSTCOMPILE)
+
 py-infthread.o: $(srcdir)/python/py-infthread.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-infthread.c
 	$(POSTCOMPILE)
diff --git a/gdb/NEWS b/gdb/NEWS
index d38266f..7262502 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..7c4af6b 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}, int @var{regnum})
+A register in the inferior has been modified by the @value{GDBN} user.
+@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 5b35306..63db2b2 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -2738,6 +2738,55 @@ A reference to the program space (@code{gdb.Progspace}) whose objfile list has
 been cleared.  @xref{Progspaces In Python}.
 @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.ptid
+The thread in which the call will be run.
+@end defvar
+
+@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.ptid
+The thread in which the call was run.
+@end defvar
+
+@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 718393c..25a7af4 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -35,6 +35,7 @@
 #include "ada-lang.h"
 #include "gdbthread.h"
 #include "event-top.h"
+#include "observer.h"
 
 /* If we can't find a function's name from its address,
    we print this instead.  */
@@ -654,6 +655,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
@@ -899,6 +902,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 c0bd9a6..6df92ea 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;
+
+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,
+				        int 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 0b5656b..4d5b895 100644
--- a/gdb/python/py-events.h
+++ b/gdb/python/py-events.h
@@ -47,6 +47,9 @@ typedef struct
   eventregistry_object *exited;
   eventregistry_object *new_objfile;
   eventregistry_object *clear_objfiles;
+  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 d08b14b..e006763 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-inferior.c b/gdb/python/py-inferior.c
index 1b38d55..9af8173 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -116,6 +116,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, int 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)
 {
@@ -802,6 +868,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/py-infevents.c b/gdb/python/py-infevents.c
new file mode 100644
index 0000000..beb0598
--- /dev/null
+++ b/gdb/python/py-infevents.c
@@ -0,0 +1,263 @@
+/* Python interface to inferior function events.
+
+   Copyright (C) 2013, 2014 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 (inferior_call_kind flag, ptid_t ptid,
+				   CORE_ADDR addr)
+{
+  int pid;
+  long tid, lwp;
+  PyObject *event;
+  PyObject *ptid_obj = NULL;
+  PyObject *addr_obj = NULL;
+  int failed;
+  struct cleanup *cleanups;
+  struct cleanup *member_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);
+
+  ptid_obj = gdbpy_create_ptid_object (ptid);
+  if (ptid_obj == NULL)
+    goto fail;
+  member_cleanups = make_cleanup_py_decref (ptid_obj);
+
+  failed = evpy_add_attribute (event, "ptid", ptid_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;
+
+  do_cleanups (member_cleanups);
+  discard_cleanups (cleanups);
+  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, 
+				      int regnum)
+{
+  PyObject *event;
+  PyObject *frame_obj = NULL;
+  PyObject *regnum_obj = NULL;
+  int failed;
+  struct cleanup *cleanups;
+  struct cleanup *member_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;
+  member_cleanups = 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;
+
+  do_cleanups (member_cleanups);
+  discard_cleanups (cleanups);
+  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;
+  struct cleanup *member_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;
+  member_cleanups = 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;
+
+  do_cleanups (member_cleanups);
+  discard_cleanups (cleanups);
+  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, int 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-infthread.c b/gdb/python/py-infthread.c
index 1399322..17d5a2a 100644
--- a/gdb/python/py-infthread.c
+++ b/gdb/python/py-infthread.c
@@ -137,23 +137,10 @@ thpy_get_ptid (PyObject *self, void *closure)
   int pid;
   long tid, lwp;
   thread_object *thread_obj = (thread_object *) self;
-  PyObject *ret;
 
   THPY_REQUIRE_VALID (thread_obj);
 
-  ret = PyTuple_New (3);
-  if (!ret)
-    return NULL;
-
-  pid = ptid_get_pid (thread_obj->thread->ptid);
-  lwp = ptid_get_lwp (thread_obj->thread->ptid);
-  tid = ptid_get_tid (thread_obj->thread->ptid);
-
-  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
-  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
-  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
-
-  return ret;
+  return gdbpy_create_ptid_object (thread_obj->thread->ptid);
 }
 
 /* Implementation of InferiorThread.switch ().
@@ -235,6 +222,30 @@ thpy_is_valid (PyObject *self, PyObject *args)
   Py_RETURN_TRUE;
 }
 
+/* Return a reference to a new Python object representing a ptid_t.
+   The object is a tuple containing (pid, lwp, tid). */
+PyObject *
+gdbpy_create_ptid_object (ptid_t ptid)
+{
+  int pid;
+  long tid, lwp;
+  PyObject *ret;
+
+  ret = PyTuple_New (3);
+  if (!ret)
+    return NULL;
+
+  pid = ptid_get_pid (ptid);
+  lwp = ptid_get_lwp (ptid);
+  tid = ptid_get_tid (ptid);
+
+  PyTuple_SET_ITEM (ret, 0, PyInt_FromLong (pid));
+  PyTuple_SET_ITEM (ret, 1, PyInt_FromLong (lwp));
+  PyTuple_SET_ITEM (ret, 2, PyInt_FromLong (tid));
+ 
+  return ret;
+}
+
 /* Implementation of gdb.selected_thread () -> gdb.InferiorThread.
    Returns the selected thread object.  */
 PyObject *
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 54eebeb..716c0de 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -363,6 +363,7 @@ PyObject *gdbpy_create_lazy_string_object (CORE_ADDR address, long length,
 					   const char *encoding,
 					   struct type *type);
 PyObject *gdbpy_inferiors (PyObject *unused, PyObject *unused2);
+PyObject *gdbpy_create_ptid_object (ptid_t ptid);
 PyObject *gdbpy_selected_thread (PyObject *self, PyObject *args);
 PyObject *gdbpy_selected_inferior (PyObject *self, PyObject *args);
 PyObject *gdbpy_string_to_argv (PyObject *self, PyObject *args);
@@ -464,6 +465,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 ca531e2..1362bd2 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1753,6 +1753,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 c4331fe..1372382 100644
--- a/gdb/testsuite/gdb.python/py-events.exp
+++ b/gdb/testsuite/gdb.python/py-events.exp
@@ -79,6 +79,123 @@ 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 process_id $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"]
+lappend expected "ptid: \\($process_id, $process_id, 0\\)" "address: $addr"
+lappend expected "event type: post-call"
+lappend expected "ptid: \\($process_id, $process_id, 0\\)" "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"
+# Note: This will step the inferior out of do_nothing and back into first.
+gdb_test_multiple {step} $test {
+    -re "event type: register-changed.*\r\n$gdb_prompt $" {
+	fail $test
+    }
+    -re "first.* at .*\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.*
 .*clear_objfiles\[\r\n\]*progspace: .*py-events.*
diff --git a/gdb/testsuite/gdb.python/py-events.py b/gdb/testsuite/gdb.python/py-events.py
index 9af768b..3bc5ff4 100644
--- a/gdb/testsuite/gdb.python/py-events.py
+++ b/gdb/testsuite/gdb.python/py-events.py
@@ -62,6 +62,30 @@ def clear_objfiles_handler (event):
     print ("event type: clear_objfiles")
     print ("progspace: %s" % (event.progspace.filename))
 
+def inferior_call_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 ("ptid: %s" % (event.ptid,))
+    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."""
 
@@ -73,6 +97,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_call_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 83a74b2..1dac738 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;

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8] Events when inferior is modified
  2014-12-02 19:20                     ` Doug Evans
@ 2014-12-04 10:45                       ` Nick Bull
  2014-12-15  8:33                       ` Yao Qi
  1 sibling, 0 replies; 21+ messages in thread
From: Nick Bull @ 2014-12-04 10:45 UTC (permalink / raw)
  To: Doug Evans; +Cc: gdb-patches, Pedro Alves



On 02/12/14 19:19, Doug Evans wrote:
> Nick Bull writes:
>   >
>   >
>   > On 20/11/14 00:21, Doug Evans wrote:
>   > > On Tue, Nov 18, 2014 at 8:37 AM, Nick Bull <nicholaspbull@gmail.com> wrote:
>   > >> On 17/11/14 21:24, Doug Evans wrote:
>   > >>> Hi.
>   > >>>
>   > >>> Let's go with ptids.
>   > >>
>   > >> Doug,
>   > >>
>   > >> Thanks. So is this version of the patch now good to apply?  Note that I don't have
>   > >> push access so will need someone to commit it on my behalf.
>   > >
>   > > Hi.
>   > >
>   > > I gave the patch one last look-over.
>   > > It's almost good to apply but what remains is whitespace and a file name change.
>   > >
>   > > I wouldn't expect to find the register/memory change events in py-infcallevent.c
>   > > so how about naming the file py-infevents.c?
>   > >
>   > > I'll make those changes and commit.
>   > > [Saves you a round trip.]
>   >
>   > Excellent. Thanks to you and everyone else for your help.
> 
> Here is what I committed.
> 
> I tweaked a few things:
> 
> - updated copyright year in one file
> - hardcoded line number in py-events.exp
> - ChangeLog
> - inferior_fn_handle -> inferior_call_handler
> - whitespace
> - py-infcallevent.c -> py-infevents.c

Many thanks Doug.

Nick

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v8] Events when inferior is modified
  2014-12-02 19:20                     ` Doug Evans
  2014-12-04 10:45                       ` Nick Bull
@ 2014-12-15  8:33                       ` Yao Qi
  1 sibling, 0 replies; 21+ messages in thread
From: Yao Qi @ 2014-12-15  8:33 UTC (permalink / raw)
  To: Doug Evans; +Cc: Nick Bull, gdb-patches, Pedro Alves

Doug Evans <dje@google.com> writes:

> diff --git a/gdb/NEWS b/gdb/NEWS
> index d38266f..7262502 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

These entries should go to "Changes since GDB 7.8" section.  This patch
moves them there.

-- 
Yao (齐尧)

From: Yao Qi <yao@codesourcery.com>
Date: Mon, 15 Dec 2014 16:29:21 +0800
Subject: [PATCH] Move NEWS entries to the right section

gdb:

2014-12-15  Yao Qi  <yao@codesourcery.com>

	* NEWS: Move some entries to "Changes since GDB 7.8" section.

diff --git a/gdb/NEWS b/gdb/NEWS
index 168e810..75a05c3 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -1,16 +1,6 @@
 		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.
@@ -30,6 +20,14 @@
   ** You can now add attributes to gdb.Objfile and gdb.Progspace objects.
   ** New function gdb.lookup_objfile.
 
+  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.
+
 * New Python-based convenience functions:
 
   ** $_caller_is(name [, number_of_frames])

^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2014-12-15  8:33 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-09-17 17:32 [PATCH v6] Events when inferior is modified Nick Bull
2014-09-24 14:05 ` Robert O'Callahan
2014-09-25 10:14 ` Phil Muldoon
2014-10-01  9:50   ` Nick Bull
2014-10-01 17:52 ` Pedro Alves
2014-10-17 16:26   ` [PATCH v7] " Nick Bull
2014-10-17 19:49     ` Doug Evans
2014-10-17 20:00       ` Doug Evans
2014-10-22 12:40         ` [PATCH v8] " Nick Bull
2014-11-17 18:13           ` Nick Bull
2014-11-17 21:25             ` Doug Evans
2014-11-18 16:37               ` Nick Bull
2014-11-20  0:21                 ` Doug Evans
2014-11-20 10:36                   ` Nick Bull
2014-12-02 19:20                     ` Doug Evans
2014-12-04 10:45                       ` Nick Bull
2014-12-15  8:33                       ` Yao Qi
2014-10-24 15:14       ` [PATCH v7] " Pedro Alves
2014-11-06 18:19         ` Doug Evans
2014-11-07 12:21           ` Pedro Alves
2014-11-07 17:04             ` Doug Evans

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).