[PATCH] Expose signal and syscall catchpoints to Python

Alessandro Di Federico ale+gdb@clearmind.me
Mon Jun 1 22:18:00 GMT 2015


This patch exposes syscall and signal catchpoints to Python. This means
that `gdb.breakpoints()` also returns syscall and signal catchpoints, and
it is possible to create them through `gdb.Breakpoint`.

The patch also implements `gdb.SyscallEvent`, which allows to catch
syscalls through `gdb.events.stop.connect`, and the
`gdb.get_{syscall,signal}_{name,number}` support functions.

Finally, the testsuite has been extended to cover the new features.

2015-06-01  Alessandro Di Federico  <ale+gdb@clearmind.me>

gdb/

	* Makefile.in: add python/py-syscallevent.c.
	* break-catch-sig.c (gdb_signal_type): Move definition in
	breakpoint.h.
	(signal_catchpoint_p): New function.
	(iterate_over_signals): New function.
	(signal_is_catch_all): New function.
	(create_signal_catchpoint): Change linkage to external.
	* break-catch-syscall.c (syscall_catchpoint_p): Change linkage to
	external.
	(iterate_over_syscalls): New function.
	(create_syscall_event_catchpoint): Change linkage to external and
	remove useless parameter.
	* breakpoint.h (gdb_signal_type): Move from break-catch-sig.c.
	(syscall_catchpoint_p, iterate_over_syscalls, signal_catchpoint_p,
	iterate_over_signals, signal_is_catch_all,
	create_syscall_event_catchpoint,
	create_signal_catchpoint): Declare functions.

gdb/common/

	* break-common.h (catchpoint_type): New enumeration.

gdb/python/

	* py-breakpoint.c (pybp_codes): Introduce BP_CATCHPOINT breakpoint
	type.
	(pybp_catchpoint_types): New structure for catchpoint types.
	(bppy_get_last_syscall): New function.
	(bppy_get_last_signal): New function.
	(bppy_get_internal_signals): New function.
	(bppy_get_catchpoint_type): New function.
	(add_syscall_to_list): New function.
	(add_signal_to_list): New function.
	(bppy_get_filter): New function.
	(bppy_init): Extend to support creation of signal and syscall
	catchpoints.
	(gdbpy_initialize_breakpoints): Add initialization of gdb.CT_*
	constants.
	(breakpoint_object_getset): Add properties to support signal and
	syscall catchpoints.
	* py-stopevent.c (emit_stop_event): Implement handling of the
	syscall case.
	* py-stopevent.h (create_syscall_event_object): New declaration.
	* python-internal.h (gdbpy_initialize_syscall_event): New declaration.
	(VEC_py_list_to_vec): Template-like function to convert a Python
	list to a VEC.
	* python.c (gdbpy_get_syscall_name): New function.
	(gdbpy_get_syscall_number): New function.
	(gdbpy_get_signal_name): New function.
	(gdbpy_get_signal_number): New function.
	(_initialize_python): Add initialization of SyscallEvent.
	(python_GdbMethods): Add get_{syscall,signal}_{name,number}
	* py-syscallevent.c: New.

gdb/testsuite/gdb.python/

	* py-breakpoint.exp (test_catchpoints): New test.
	(test_syscall_catchpoint): New test.
	(test_signal_catchpoint): New test.
	* py-events.py: Merge all the handlers of `gdb.events.stop` in a
	single one handling SignalEvent, SyscallEvent and BreakpointEvent.
	* py-evsignal.exp: Fix expectations accordingly to the changes in
	py-events.py.
	* py-evthreads.exp: Likewise.
	* py-evsyscall.exp: New test.
---
 gdb/Makefile.in                            |   6 +
 gdb/break-catch-sig.c                      |  49 +++++-
 gdb/break-catch-syscall.c                  |  39 ++++-
 gdb/breakpoint.h                           |  18 +++
 gdb/common/break-common.h                  |  10 ++
 gdb/python/py-breakpoint.c                 | 229 ++++++++++++++++++++++++++++-
 gdb/python/py-stopevent.c                  |  15 ++
 gdb/python/py-stopevent.h                  |   3 +
 gdb/python/py-syscallevent.c               |  61 ++++++++
 gdb/python/python-internal.h               |  47 ++++++
 gdb/python/python.c                        | 122 +++++++++++++++
 gdb/testsuite/gdb.python/py-breakpoint.exp | 134 +++++++++++++++++
 gdb/testsuite/gdb.python/py-events.py      |  15 +-
 gdb/testsuite/gdb.python/py-evsignal.exp   |   2 +-
 gdb/testsuite/gdb.python/py-evsyscall.exp  |  41 ++++++
 gdb/testsuite/gdb.python/py-evthreads.exp  |   2 +-
 16 files changed, 764 insertions(+), 29 deletions(-)
 create mode 100644 gdb/python/py-syscallevent.c
 create mode 100644 gdb/testsuite/gdb.python/py-evsyscall.exp

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 95104ef..7f3f3c4 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -403,6 +403,7 @@ SUBDIR_PYTHON_OBS = \
 	py-stopevent.o \
 	py-symbol.o \
 	py-symtab.o \
+	py-syscallevent.o \
 	py-threadevent.o \
 	py-type.o \
 	py-unwind.o \
@@ -443,6 +444,7 @@ SUBDIR_PYTHON_SRCS = \
 	python/py-stopevent.c \
 	python/py-symbol.c \
 	python/py-symtab.c \
+	python/py-syscallevent.c \
 	python/py-threadevent.c \
 	python/py-type.c \
 	python/py-unwind.c \
@@ -2634,6 +2636,10 @@ py-symtab.o: $(srcdir)/python/py-symtab.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-symtab.c
 	$(POSTCOMPILE)
 
+py-syscallevent.o: $(srcdir)/python/py-syscallevent.c
+	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-syscallevent.c
+	$(POSTCOMPILE)
+
 py-threadevent.o: $(srcdir)/python/py-threadevent.c
 	$(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-threadevent.c
 	$(POSTCOMPILE)
diff --git a/gdb/break-catch-sig.c b/gdb/break-catch-sig.c
index 419d226..13fcc3d 100644
--- a/gdb/break-catch-sig.c
+++ b/gdb/break-catch-sig.c
@@ -32,10 +32,6 @@
 
 #define INTERNAL_SIGNAL(x) ((x) == GDB_SIGNAL_TRAP || (x) == GDB_SIGNAL_INT)
 
-typedef enum gdb_signal gdb_signal_type;
-
-DEF_VEC_I (gdb_signal_type);
-
 /* An instance of this type is used to represent a signal catchpoint.
    It includes a "struct breakpoint" as a kind of base class; users
    downcast to "struct breakpoint *" when needed.  A breakpoint is
@@ -66,6 +62,49 @@ struct signal_catchpoint
 
 static struct breakpoint_ops signal_catchpoint_ops;
 
+/* Returns non-zero if 'b' is a signal catchpoint.  */
+
+int
+signal_catchpoint_p (struct breakpoint *b)
+{
+  return (b->ops == &signal_catchpoint_ops);
+}
+
+/* Iterates over the signals catched by the catchpoint calling callback */
+
+int
+iterate_over_signals (struct breakpoint *b,
+		       int (*callback) (int, void *),
+		       void *data)
+{
+  struct signal_catchpoint *cp = (struct signal_catchpoint *) b;
+  int i = 0, result = 0;
+  gdb_signal_type iter = 0;
+
+  if (cp->signals_to_be_caught == NULL)
+    return -1;
+
+  for (i = 0;
+       VEC_iterate (gdb_signal_type, cp->signals_to_be_caught, i, iter);
+       i++)
+    {
+      result = (*callback) (iter, data);
+      if (result)
+	return result;
+    }
+
+  return result;
+}
+
+/* Returns non-zero if the signal catchpoint catches also internal signals */
+
+int
+signal_is_catch_all (struct breakpoint *b)
+{
+  struct signal_catchpoint *cp = (struct signal_catchpoint *) b;
+  return cp->catch_all;
+}
+
 /* Count of each signal.  */
 
 static unsigned int *signal_catch_counts;
@@ -365,7 +404,7 @@ signal_catchpoint_explains_signal (struct breakpoint *b, enum gdb_signal sig)
    valid if FILTER is NULL.  If FILTER is NULL and CATCH_ALL is zero,
    then internal signals like SIGTRAP are not caught.  */
 
-static void
+void
 create_signal_catchpoint (int tempflag, VEC (gdb_signal_type) *filter,
 			  int catch_all)
 {
diff --git a/gdb/break-catch-syscall.c b/gdb/break-catch-syscall.c
index 1718f49..97ccc61 100644
--- a/gdb/break-catch-syscall.c
+++ b/gdb/break-catch-syscall.c
@@ -418,21 +418,47 @@ static struct breakpoint_ops catch_syscall_breakpoint_ops;
 
 /* Returns non-zero if 'b' is a syscall catchpoint.  */
 
-static int
+int
 syscall_catchpoint_p (struct breakpoint *b)
 {
   return (b->ops == &catch_syscall_breakpoint_ops);
 }
 
-static void
-create_syscall_event_catchpoint (int tempflag, VEC(int) *filter,
-                                 const struct breakpoint_ops *ops)
+/* Iterates over the syscalls catched by the catchpoint calling callback */
+
+int
+iterate_over_syscalls (struct breakpoint *b,
+		       int (*callback) (int, void *),
+		       void *data)
+{
+  struct syscall_catchpoint *cp = (struct syscall_catchpoint *) b;
+  int i = 0, iter = 0, result = 0;
+
+  if (cp->syscalls_to_be_caught == NULL)
+    return -1;
+
+  for (i = 0;
+       VEC_iterate (int, cp->syscalls_to_be_caught, i, iter);
+       i++)
+    {
+      result = (*callback) (iter, data);
+      if (result)
+	return result;
+    }
+
+  return result;
+}
+
+/* Creates a syscall catchpoint */
+
+void
+create_syscall_event_catchpoint (int tempflag, VEC(int) *filter)
 {
   struct syscall_catchpoint *c;
   struct gdbarch *gdbarch = get_current_arch ();
 
   c = XNEW (struct syscall_catchpoint);
-  init_catchpoint (&c->base, gdbarch, tempflag, NULL, ops);
+  init_catchpoint (&c->base, gdbarch, tempflag, NULL, &catch_syscall_breakpoint_ops);
   c->syscalls_to_be_caught = filter;
 
   install_breakpoint (0, &c->base, 1);
@@ -524,8 +550,7 @@ this architecture yet."));
   else
     filter = NULL;
 
-  create_syscall_event_catchpoint (tempflag, filter,
-				   &catch_syscall_breakpoint_ops);
+  create_syscall_event_catchpoint (tempflag, filter);
 }
 
 
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 562a6b6..dec81b3 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -1240,6 +1240,18 @@ extern struct breakpoint_ops bkpt_breakpoint_ops;
 extern struct breakpoint_ops tracepoint_breakpoint_ops;
 extern struct breakpoint_ops dprintf_breakpoint_ops;
 
+extern int syscall_catchpoint_p (struct breakpoint *);
+extern int iterate_over_syscalls (struct breakpoint *,
+				  int (*) (int, void *), void *);
+extern int signal_catchpoint_p (struct breakpoint *);
+extern int iterate_over_signals (struct breakpoint *, int (*) (int, void *),
+				 void *);
+extern int signal_is_catch_all (struct breakpoint *b);
+
+
+typedef enum gdb_signal gdb_signal_type;
+DEF_VEC_I (gdb_signal_type);
+
 extern void initialize_breakpoint_ops (void);
 
 /* Arguments to pass as context to some catch command handlers.  */
@@ -1305,6 +1317,12 @@ extern int create_breakpoint (struct gdbarch *gdbarch, char *arg,
 			      int enabled,
 			      int internal, unsigned flags);
 
+extern void create_syscall_event_catchpoint (int tempflag, VEC(int) *filter);
+extern void create_signal_catchpoint (int tempflag,
+				      VEC (gdb_signal_type) *filter,
+				      int catch_all);
+
+
 extern void insert_breakpoints (void);
 
 extern int remove_breakpoints (void);
diff --git a/gdb/common/break-common.h b/gdb/common/break-common.h
index e5b55b4..9e2cb11 100644
--- a/gdb/common/break-common.h
+++ b/gdb/common/break-common.h
@@ -19,6 +19,8 @@
 #ifndef BREAK_COMMON_H
 #define BREAK_COMMON_H 1
 
+/* Possible types for hardware breakpoints */
+
 enum target_hw_bp_type
   {
     hw_write   = 0, 		/* Common  HW watchpoint */
@@ -27,4 +29,12 @@ enum target_hw_bp_type
     hw_execute = 3		/* Execute HW breakpoint */
   };
 
+/* Enumeration for the different types of catchpoints */
+
+enum catchpoint_type
+  {
+    ct_syscall = 0,             /* Syscall catchpoint */
+    ct_signal  = 1              /* Signal catchpoint  */
+  };
+
 #endif
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 42a8596..c1667b7 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -30,6 +30,10 @@
 #include "ada-lang.h"
 #include "arch-utils.h"
 #include "language.h"
+#include "common/gdb_signals.h"
+
+DEF_VEC_PY_OP(gdb_signal_type);
+DEF_VEC_PY_OP(int);
 
 /* Number of live breakpoints.  */
 static int bppy_live;
@@ -59,6 +63,7 @@ static struct pybp_code pybp_codes[] =
   { "BP_HARDWARE_WATCHPOINT", bp_hardware_watchpoint},
   { "BP_READ_WATCHPOINT", bp_read_watchpoint},
   { "BP_ACCESS_WATCHPOINT", bp_access_watchpoint},
+  { "BP_CATCHPOINT", bp_catchpoint},
   {NULL} /* Sentinel.  */
 };
 
@@ -71,6 +76,14 @@ static struct pybp_code pybp_watch_types[] =
   {NULL} /* Sentinel.  */
 };
 
+/* Entries related to catchpoint types. */
+static struct pybp_code pybp_catchpoint_types[] =
+{
+  { "CT_SYSCALL", ct_syscall},
+  { "CT_SIGNAL", ct_signal},
+  {NULL} /* Sentinel.  */
+};
+
 /* Python function which checks the validity of a breakpoint object.  */
 static PyObject *
 bppy_is_valid (PyObject *self, PyObject *args)
@@ -546,6 +559,149 @@ bppy_get_visibility (PyObject *self, void *closure)
   Py_RETURN_TRUE;
 }
 
+/* Python function to get the number of the last catched syscall */
+
+static PyObject *
+bppy_get_last_syscall (PyObject *self, void *closure)
+{
+  gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
+  struct target_waitstatus last;
+  ptid_t ptid;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  if (self_bp->bp->type == bp_catchpoint && syscall_catchpoint_p (self_bp->bp))
+    {
+      get_last_target_status (&ptid, &last);
+      return PyInt_FromLong (last.value.syscall_number);
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to get the number of the last catched signal */
+
+static PyObject *
+bppy_get_last_signal (PyObject *self, void *closure)
+{
+  gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
+  struct target_waitstatus last;
+  ptid_t ptid;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  if (self_bp->bp->type == bp_catchpoint && signal_catchpoint_p (self_bp->bp))
+    {
+      get_last_target_status (&ptid, &last);
+      return PyInt_FromLong (last.value.sig);
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to get the whether the current catchpoint catches internal
+   signals too */
+
+static PyObject *
+bppy_get_internal_signals (PyObject *self, void *closure)
+{
+  gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
+  struct target_waitstatus last;
+  ptid_t ptid;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  if (self_bp->bp->type == bp_catchpoint && signal_catchpoint_p (self_bp->bp))
+    {
+      if (signal_is_catch_all (self_bp->bp))
+	Py_RETURN_TRUE;
+      else
+	Py_RETURN_FALSE;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* Python function to obtain the type of the current catchpoint
+   (e.g. syscall or signal catchpoint) */
+
+static PyObject *
+bppy_get_catchpoint_type (PyObject *self, void *closure)
+{
+  gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  if (self_bp->bp->type == bp_catchpoint)
+    {
+      if (signal_catchpoint_p (self_bp->bp))
+	return PyInt_FromLong (ct_signal);
+      else if (syscall_catchpoint_p (self_bp->bp))
+	return PyInt_FromLong (ct_syscall);
+      else
+	Py_RETURN_NONE;
+    }
+
+  Py_RETURN_NONE;
+}
+
+/* A simple function to append integers to a Python list */
+
+static int
+add_syscall_to_list(int value, void *list) {
+  return PyList_Append(list, PyInt_FromLong (value));
+}
+
+/* A simple function to append integers to a Python list */
+
+static int
+add_signal_to_list(int value, void *list) {
+  int host_signal = gdb_signal_to_host (value);
+  return PyList_Append(list, PyInt_FromLong (host_signal));
+}
+
+/* Python function that returns a list of integers representing the
+   syscalls/signals catched by this breakpoint. */
+
+static PyObject *
+bppy_get_filter (PyObject *self, void *closure)
+{
+  gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
+  int i = 0, iter = 0, iserr = 0;
+  PyObject *list = NULL;
+
+  BPPY_REQUIRE_VALID (self_bp);
+
+  if (self_bp->bp->type == bp_catchpoint)
+    {
+      list = PyList_New(0);
+
+      if (!list)
+	{
+	  PyErr_SetString (PyExc_RuntimeError,
+			   "Couldn't create a list object.");
+	  return NULL;
+	}
+
+      if (syscall_catchpoint_p (self_bp->bp))
+	iserr = iterate_over_syscalls(self_bp->bp, add_syscall_to_list, list);
+      else if (signal_catchpoint_p (self_bp->bp))
+	iserr = iterate_over_signals(self_bp->bp, add_signal_to_list, list);
+
+      if (iserr == -1)
+	{
+	  PyErr_SetString (PyExc_RuntimeError,
+			   "Error while inserting an element in the list.");
+	  Py_DECREF (list);
+	  return NULL;
+	}
+
+      return list;
+    }
+
+  Py_RETURN_NONE;
+}
+
 /* Python function to determine if the breakpoint is a temporary
    breakpoint.  */
 
@@ -628,19 +784,23 @@ bppy_get_ignore_count (PyObject *self, void *closure)
 static int
 bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 {
-  static char *keywords[] = { "spec", "type", "wp_class", "internal",
-			      "temporary", NULL };
+  static char *keywords[] = { "spec", "type", "wp_class", "ct_type", "internal",
+			      "temporary", "filter", "internal_signals", NULL };
   const char *spec;
   int type = bp_breakpoint;
   int access_type = hw_write;
+  int catchpoint_type = ct_syscall;
   PyObject *internal = NULL;
   PyObject *temporary = NULL;
   int internal_bp = 0;
   int temporary_bp = 0;
+  PyObject *filter = NULL;
+  int internal_signals = 0;
 
-  if (! PyArg_ParseTupleAndKeywords (args, kwargs, "s|iiOO", keywords,
+  if (! PyArg_ParseTupleAndKeywords (args, kwargs, "s|iiiOOOi", keywords,
 				     &spec, &type, &access_type,
-				     &internal, &temporary))
+				     &catchpoint_type, &internal, &temporary,
+				     &filter, &internal_signals))
     return -1;
 
   if (internal)
@@ -692,6 +852,40 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
 	      error(_("Cannot understand watchpoint access type."));
 	    break;
 	  }
+	case bp_catchpoint:
+	  {
+	    switch (catchpoint_type)
+	      {
+	      case ct_syscall:
+		{
+		  VEC (int) *syscall_filter = NULL;
+
+		  if (filter != NULL)
+		    syscall_filter = VEC_py_list_to_vec (int, filter, NULL);
+
+		  create_syscall_event_catchpoint (temporary_bp, syscall_filter);
+		  break;
+		}
+	      case ct_signal:
+		{
+		  VEC (gdb_signal_type) *signal_filter = NULL;
+
+		  if (filter != NULL)
+		    signal_filter = VEC_py_list_to_vec (gdb_signal_type,
+							filter,
+							gdb_signal_from_host);
+
+		  create_signal_catchpoint (temporary_bp, signal_filter,
+					    internal_signals);
+		  break;
+		}
+	      default:
+		{
+		  error (_("Catchpoint type not supported."));
+		}
+	      }
+	    break;
+	  }
 	default:
 	  error(_("Do not understand breakpoint type to set."));
 	}
@@ -711,8 +905,6 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
   return 0;
 }
 
-
-
 static int
 build_bp_list (struct breakpoint *b, void *arg)
 {
@@ -864,7 +1056,8 @@ gdbpy_breakpoint_created (struct breakpoint *bp)
       && bp->type != bp_watchpoint
       && bp->type != bp_hardware_watchpoint
       && bp->type != bp_read_watchpoint
-      && bp->type != bp_access_watchpoint)
+      && bp->type != bp_access_watchpoint
+      && bp->type != bp_catchpoint)
     return;
 
   state = PyGILState_Ensure ();
@@ -959,6 +1152,16 @@ gdbpy_initialize_breakpoints (void)
 	return -1;
     }
 
+  /* Add catchpoint types constants.  */
+  for (i = 0; pybp_catchpoint_types[i].name; ++i)
+    {
+      if (PyModule_AddIntConstant (gdb_module,
+				   /* Cast needed for Python 2.4.  */
+				   (char *) pybp_catchpoint_types[i].name,
+				   pybp_catchpoint_types[i].code) < 0)
+	return -1;
+    }
+
   return 0;
 }
 
@@ -1048,6 +1251,18 @@ or None if no condition set."},
     "Whether the breakpoint is visible to the user."},
   { "temporary", bppy_get_temporary, NULL,
     "Whether this breakpoint is a temporary breakpoint."},
+  { "filter", bppy_get_filter, NULL,
+    "List of catched syscalls/signals. If empty, all the syscalls/signals are\n\
+being catched."},
+  { "internal_signals", bppy_get_internal_signals, NULL,
+    "Boolean indicating whether the breakpoint catches internal signals too or\n\
+not."},
+  { "last_syscall", bppy_get_last_syscall, NULL,
+    "Last catched syscall."},
+  { "last_signal", bppy_get_last_signal, NULL,
+    "Last catched signal."},
+  { "ct_type", bppy_get_catchpoint_type, NULL,
+    "Get catchpoint type."},
   { NULL }  /* Sentinel.  */
 };
 
diff --git a/gdb/python/py-stopevent.c b/gdb/python/py-stopevent.c
index 684edff..e6da332 100644
--- a/gdb/python/py-stopevent.c
+++ b/gdb/python/py-stopevent.c
@@ -19,6 +19,7 @@
 
 #include "defs.h"
 #include "py-stopevent.h"
+#include "infrun.h"
 
 PyObject *
 create_stop_event_object (PyTypeObject *py_type)
@@ -49,6 +50,8 @@ emit_stop_event (struct bpstats *bs, enum gdb_signal stop_signal)
   PyObject *list = NULL;
   PyObject *first_bp = NULL;
   struct bpstats *current_bs;
+  ptid_t ptidp;
+  struct target_waitstatus status;
 
   if (evregpy_no_listeners_p (gdb_py_events.stop))
     return 0;
@@ -95,6 +98,18 @@ emit_stop_event (struct bpstats *bs, enum gdb_signal stop_signal)
 	goto fail;
     }
 
+  if (stop_signal == GDB_SIGNAL_0)
+    {
+      get_last_target_status(&ptidp, &status);
+      if (status.kind == TARGET_WAITKIND_SYSCALL_ENTRY)
+	{
+	  stop_event_obj =
+	    create_syscall_event_object (status.value.syscall_number, bs);
+	  if (!stop_event_obj)
+	    goto fail;
+	}
+    }
+
   /* If all fails emit an unknown stop event.  All event types should
      be known and this should eventually be unused.  */
   if (!stop_event_obj)
diff --git a/gdb/python/py-stopevent.h b/gdb/python/py-stopevent.h
index f219070..663da7c 100644
--- a/gdb/python/py-stopevent.h
+++ b/gdb/python/py-stopevent.h
@@ -33,4 +33,7 @@ extern PyObject *create_breakpoint_event_object (PyObject *breakpoint_list,
 
 extern PyObject *create_signal_event_object (enum gdb_signal stop_signal);
 
+extern PyObject *create_syscall_event_object (int syscall_number,
+					      struct bpstats *bs);
+
 #endif /* GDB_PY_STOPEVENT_H */
diff --git a/gdb/python/py-syscallevent.c b/gdb/python/py-syscallevent.c
new file mode 100644
index 0000000..c844be8
--- /dev/null
+++ b/gdb/python/py-syscallevent.c
@@ -0,0 +1,61 @@
+/* Python interface to inferior syscall events.
+
+   Copyright (C) 2009-2015 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-stopevent.h"
+#include "xml-syscall.h"
+
+extern PyTypeObject syscall_event_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("event_object");
+
+PyObject *
+create_syscall_event_object (int syscall_number, struct bpstats *bs)
+{
+  PyObject *syscall_number_obj = NULL;
+  PyObject *syscall_event_obj =
+    create_stop_event_object (&syscall_event_object_type);
+  struct syscall s;
+  struct gdbarch *gdbarch = bs->bp_location_at->gdbarch;
+
+  if (!syscall_event_obj)
+    goto fail;
+
+  get_syscall_by_number (gdbarch, syscall_number, &s);
+
+  syscall_number_obj = PyLong_FromLongLong (syscall_number);
+  if (syscall_number_obj == NULL)
+    goto fail;
+
+  if (evpy_add_attribute (syscall_event_obj,
+                          "stop_syscall",
+                          syscall_number_obj) < 0)
+    goto fail;
+
+  return syscall_event_obj;
+
+ fail:
+  Py_XDECREF (syscall_event_obj);
+  return NULL;
+}
+
+GDBPY_NEW_EVENT_TYPE (syscall,
+                      "gdb.SyscallEvent",
+                      "SyscallEvent",
+                      "GDB syscall event object",
+                      stop_event_object_type);
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index f881559..139c88b 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -474,6 +474,8 @@ int gdbpy_initialize_stop_event (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_signal_event (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
+int gdbpy_initialize_syscall_event (void)
+  CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_breakpoint_event (void)
   CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
 int gdbpy_initialize_continue_event (void)
@@ -596,4 +598,49 @@ struct varobj;
 struct varobj_iter *py_varobj_get_iterator (struct varobj *var,
 					    PyObject *printer);
 
+/* Define a "template" macro to create a Python list from a VEC of integer-like
+   values. See vec.h. */
+
+#define VEC_py_list_to_vec(T,V,C) (VEC_OP(T,py_list_to_vec)((V), (C) VEC_ASSERT_INFO))
+
+#define DEF_VEC_PY_OP(T)						\
+static inline VEC(T)*							\
+VEC_OP(T, py_list_to_vec) (PyObject *list, T (*converter)(int) VEC_ASSERT_DECL) \
+{									\
+  VEC(T)* result = NULL;						\
+  struct cleanup *cleanup = make_cleanup (VEC_cleanup (T), &result);	\
+  Py_ssize_t i = 0;							\
+  									\
+  if (!PyList_Check (list))						\
+    {									\
+      PyErr_SetString (PyExc_RuntimeError,				\
+		       _("Input object not a list."));			\
+      return NULL;							\
+    }									\
+  									\
+  for (i = 0; i < PyList_Size(list); i++)				\
+    {									\
+      long value;							\
+									\
+      if (!gdb_py_int_as_long(PyList_GetItem (list, i), &value))	\
+	{								\
+	  PyErr_SetString (PyExc_RuntimeError,				\
+			   _("Couldn't convert list element to long.")); \
+	  do_cleanups (cleanup);					\
+	  return NULL;							\
+	}								\
+      									\
+      if (converter)							\
+	value = converter(value);					\
+      									\
+      VEC_safe_push (T, result, value);					\
+    }									\
+  									\
+  discard_cleanups (cleanup);						\
+  return result;							\
+  									\
+}									\
+struct vec_swallow_trailing_semi
+
+
 #endif /* GDB_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 4f88b0e..046443c 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -33,6 +33,8 @@
 #include "python.h"
 #include "extension-priv.h"
 #include "cli/cli-utils.h"
+#include "xml-syscall.h"
+#include "common/gdb_signals.h"
 #include <ctype.h>
 
 /* Declared constants and enum for python stack printing.  */
@@ -715,6 +717,113 @@ gdbpy_solib_name (PyObject *self, PyObject *args)
   return str_obj;
 }
 
+/* Python function to obtain the name of a syscall given its number */
+
+static PyObject *
+gdbpy_get_syscall_name (PyObject *self, PyObject *arg)
+{
+  long syscall_number;
+  struct gdbarch *gdbarch = get_current_arch ();
+  struct syscall s;
+
+  if (PyInt_Check (arg) && gdb_py_int_as_long (arg, &syscall_number))
+    {
+      get_syscall_by_number (gdbarch, syscall_number, &s);
+
+      if (!s.name || strlen (s.name) == 0)
+	Py_RETURN_NONE;
+
+      return PyString_Decode (s.name, strlen (s.name), host_charset (), NULL);
+    }
+
+  PyErr_SetString (PyExc_RuntimeError, "Argument is not a number.");
+  return NULL;
+}
+
+/* Python function to obtain the number of a syscall given its name */
+
+static PyObject *
+gdbpy_get_syscall_number (PyObject *self, PyObject *arg)
+{
+  const char *syscall_name = NULL;
+  struct gdbarch *gdbarch = get_current_arch ();
+  struct syscall s;
+
+  if (!PyString_Check (arg))
+    {
+      PyErr_SetString (PyExc_RuntimeError, "Argument is not a string.");
+      return NULL;
+    }
+
+  syscall_name = PyBytes_AsString (arg);
+
+  if (syscall_name == NULL)
+    Py_RETURN_NONE;
+
+  get_syscall_by_name (gdbarch, syscall_name, &s);
+
+  if (s.number == UNKNOWN_SYSCALL)
+    Py_RETURN_NONE;
+
+  return PyInt_FromLong (s.number);
+
+}
+
+
+/* Python function to obtain the name of a signal given its number */
+
+static PyObject *
+gdbpy_get_signal_name (PyObject *self, PyObject *arg)
+{
+  long signal_number;
+  struct gdbarch *gdbarch = get_current_arch ();
+  const char *s = NULL;
+  enum gdb_signal signal = 0;
+
+  if (PyInt_Check (arg) && gdb_py_int_as_long (arg, &signal_number))
+    {
+      signal = gdb_signal_from_host (signal_number);
+      s = gdb_signal_to_name (signal);
+
+      if (!s || strlen (s) == 0)
+	Py_RETURN_NONE;
+
+      return PyString_Decode (s, strlen (s), host_charset (), NULL);
+    }
+
+  PyErr_SetString (PyExc_RuntimeError, "Argument is not a number.");
+  return NULL;
+}
+
+/* Python function to obtain the number of a signal given its name */
+
+static PyObject *
+gdbpy_get_signal_number (PyObject *self, PyObject *arg)
+{
+  const char *signal_name = NULL;
+  int signal_number = 0;
+  enum gdb_signal signal = 0;
+
+  if (!PyString_Check (arg))
+    {
+      PyErr_SetString (PyExc_RuntimeError, "Argument is not a string.");
+      return NULL;
+    }
+
+  signal_name = PyBytes_AsString (arg);
+
+  if (signal_name == NULL)
+    Py_RETURN_NONE;
+
+  signal = gdb_signal_from_name (signal_name);
+  signal_number = gdb_signal_to_host (signal);
+
+  if (signal_number == 0)
+    Py_RETURN_NONE;
+
+  return PyInt_FromLong (signal_number);
+}
+
 /* A Python function which is a wrapper for decode_line_1.  */
 
 static PyObject *
@@ -1819,6 +1928,7 @@ message == an error message without a stack will be printed."),
       || gdbpy_initialize_event () < 0
       || gdbpy_initialize_stop_event () < 0
       || gdbpy_initialize_signal_event () < 0
+      || gdbpy_initialize_syscall_event () < 0
       || gdbpy_initialize_breakpoint_event () < 0
       || gdbpy_initialize_continue_event () < 0
       || gdbpy_initialize_inferior_call_pre_event () < 0
@@ -2081,6 +2191,18 @@ Return the selected inferior object." },
   { "inferiors", gdbpy_inferiors, METH_NOARGS,
     "inferiors () -> (gdb.Inferior, ...).\n\
 Return a tuple containing all inferiors." },
+  { "get_syscall_name", gdbpy_get_syscall_name, METH_O,
+    "get_syscall_name (number) -> string.\n\
+Return the name of the syscall." },
+  { "get_syscall_number", gdbpy_get_syscall_number, METH_O,
+    "get_syscall_number (string) -> number.\n\
+Return the syscall number corresponding to the specified name." },
+  { "get_signal_name", gdbpy_get_signal_name, METH_O,
+    "get_signal_name (number) -> string.\n\
+Return the name of the signal." },
+  { "get_signal_number", gdbpy_get_signal_number, METH_O,
+    "get_signal_number (string) -> number.\n\
+Return the signal number corresponding to the specified name." },
   {NULL, NULL, 0, NULL}
 };
 
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index 0dfc845..772c162 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -462,6 +462,137 @@ proc test_bkpt_temporary { } {
     }
 }
 
+proc test_catchpoints { } {
+    global testfile hex
+
+    with_test_prefix test_catchpoints {
+	# Start with a fresh gdb.
+	clean_restart ${testfile}
+
+	if ![runto_main] then {
+	    fail "Cannot run to main."
+	    return 0
+	}
+
+	# Check get_{signal,syscall}_{name,number} given sensible results
+	gdb_test "python print gdb.get_syscall_name(gdb.get_syscall_number(\"write\")) == \"write\"" \
+	    "True" "Round-trip check for syscall"
+	gdb_test "python print gdb.get_signal_name(gdb.get_signal_number(\"SIGUSR2\")) == \"SIGUSR2\"" \
+	    "True" "Round-trip check for signal"
+
+	# Create a catchpoint on the write syscall
+	gdb_test "catch syscall write" \
+	    "Catchpoint.*\(write\).*" "catch syscall write"
+
+	# Check that the catchpoint exists and has the expected properties
+	gdb_py_test_silent_cmd "python blist = gdb.breakpoints()" \
+	    "Get Breakpoint List" 0
+	gdb_test "python print (len(blist))" \
+	    "2" "Check for two breakpoints"
+	gdb_test "python print (blist\[1\])" \
+	    "<gdb.Breakpoint object at $hex>" "Check obj exist"
+	gdb_test "python print (blist\[1\].type == gdb.BP_CATCHPOINT)" \
+	    "True" "Check the breakpoint is a catchpoint"
+	gdb_test "python print (blist\[1\].ct_type == gdb.CT_SYSCALL)" \
+	    "True" "Check the breakpoint is a syscall catchpoint"
+	gdb_test "python print (map(gdb.get_syscall_name, blist\[1\].filter))" \
+	    "\\\['write'\\\]" "Check correctness of the filter"
+
+	# Create a catchpoint for the SIGUSR1 signal
+	gdb_test "catch signal SIGUSR1" "Catchpoint.*\(signal SIGUSR1\).*" "catch signal SIGUSR1"
+
+	# Check that the catchpoint exists and has the expected properties
+	gdb_py_test_silent_cmd "python blist = gdb.breakpoints()" \
+	    "Get Breakpoint List" 0
+	gdb_test "python print (len(blist))" \
+	    "3" "Check for three breakpoints"
+	gdb_test "python print (blist\[2\])" \
+	    "<gdb.Breakpoint object at $hex>" "Check obj exist"
+	gdb_test "python print (blist\[2\].type == gdb.BP_CATCHPOINT)" \
+	    "True" "Check the breakpoint is a catchpoint"
+	gdb_test "python print (blist\[2\].ct_type == gdb.CT_SIGNAL)" \
+	    "True" "Check the breakpoint is a signal catchpoint"
+	gdb_test "python print (map(gdb.get_signal_name, blist\[2\].filter))" \
+	    "\\\['SIGUSR1'\\\]" "Check correctness of the filter"
+	gdb_test "python print (blist\[2\].internal_signals)" \
+	    "False" "Check correctness of internal_signals"
+
+	# Create a catchpoint for all the signals, internal ones included
+	gdb_test "catch signal all" "Catchpoint.*\(any signal\).*" "catch signal all"
+
+	# Check that the catchpoint exists and has the expected properties
+	gdb_py_test_silent_cmd "python blist = gdb.breakpoints()" \
+	    "Get Breakpoint List" 0
+	gdb_test "python print (len(blist))" \
+	    "4" "Check for three breakpoints"
+	gdb_test "python print (blist\[3\])" \
+	    "<gdb.Breakpoint object at $hex>" "Check obj exist"
+	gdb_test "python print (blist\[3\].ct_type == gdb.CT_SIGNAL)" \
+	    "True" "Check the breakpoint is a signal catchpoint"
+	gdb_test "python print (blist\[3\].type == gdb.BP_CATCHPOINT)" \
+	    "True" "Check the breakpoint is a catchpoint"
+	gdb_test "python print (blist\[3\].internal_signals)" \
+	    "True" "Check correctness of internal_signals"
+    }
+}
+
+proc test_syscall_catchpoint { } {
+    global testfile decimal
+
+    with_test_prefix test_signal_catchpoint {
+	# Start with a fresh gdb.
+	clean_restart ${testfile}
+
+	if ![runto_main] then {
+	    fail "Cannot run to main."
+	    return 0
+	}
+
+	# Create a syscall catchpoint from Python for write
+	gdb_py_test_silent_cmd "python cp1 = gdb.Breakpoint(\"\", type=gdb.BP_CATCHPOINT, ct_type=gdb.CT_SYSCALL, filter=\[gdb.get_syscall_number(\"write\")\])" \
+	    "Create catchpoint for a specific signal" 0
+	gdb_test "python print (cp1 in gdb.breakpoints())" "True" \
+	    "Check the catchpoint has been inserted"
+	gdb_test "python print (cp1.ct_type == gdb.CT_SYSCALL)" \
+	    "True" "Check the breakpoint is a syscall catchpoint"
+	gdb_test "python print (map(gdb.get_syscall_name, cp1.filter))" \
+	    "\\\['write'\\\]" "Check correctness of the filter"
+    }
+}
+
+proc test_signal_catchpoint { } {
+    global testfile decimal
+
+    with_test_prefix test_signal_catchpoint {
+	# Start with a fresh gdb.
+	clean_restart ${testfile}
+
+	if ![runto_main] then {
+	    fail "Cannot run to main."
+	    return 0
+	}
+
+	# Create a signal catchpoint from Python for all signals (internal
+	# ones included)
+	gdb_py_test_silent_cmd "python cp1 = gdb.Breakpoint(\"\", type=gdb.BP_CATCHPOINT, ct_type=gdb.CT_SIGNAL, internal_signals=True)" \
+	    "Create catchpoint for all signals" 0
+	gdb_test "python print (cp1 in gdb.breakpoints())" "True" \
+	    "Check the catchpoint has been inserted"
+	gdb_test "python print cp1.internal_signals" "True" \
+	    "Check internal_signals value"
+
+	# Create a signal catchpoint from Python for SIGUSR1
+	gdb_py_test_silent_cmd "python cp2 = gdb.Breakpoint(\"\", type=gdb.BP_CATCHPOINT, ct_type=gdb.CT_SIGNAL, filter=\[gdb.get_signal_number(\"SIGUSR1\")\])" \
+	    "Create catchpoint for a specific signal" 0
+	gdb_test "python print (cp2 in gdb.breakpoints())" "True" \
+	    "Check the catchpoint has been inserted"
+	gdb_test "python print cp2.internal_signals" "False" \
+	    "Check internal_signals value"
+	gdb_test "python print (map(gdb.get_signal_name, cp2.filter))" \
+	    "\\\['SIGUSR1'\\\]" "Check correctness of the filter"
+    }
+}
+
 test_bkpt_basic
 test_bkpt_deletion
 test_bkpt_cond_and_cmds
@@ -470,3 +601,6 @@ test_watchpoints
 test_bkpt_internal
 test_bkpt_eval_funcs
 test_bkpt_temporary
+test_catchpoints
+test_syscall_catchpoint
+test_signal_catchpoint
diff --git a/gdb/testsuite/gdb.python/py-events.py b/gdb/testsuite/gdb.python/py-events.py
index d7fefe0..c166d19 100644
--- a/gdb/testsuite/gdb.python/py-events.py
+++ b/gdb/testsuite/gdb.python/py-events.py
@@ -17,19 +17,19 @@
 # printers.
 import gdb
 
-def signal_stop_handler (event):
+def stop_handler (event):
     if (isinstance (event, gdb.StopEvent)):
         print ("event type: stop")
+
     if (isinstance (event, gdb.SignalEvent)):
         print ("stop reason: signal")
         print ("stop signal: %s" % (event.stop_signal))
         if ( event.inferior_thread is not None) :
             print ("thread num: %s" % (event.inferior_thread.num))
-
-def breakpoint_stop_handler (event):
-    if (isinstance (event, gdb.StopEvent)):
-        print ("event type: stop")
-    if (isinstance (event, gdb.BreakpointEvent)):
+    elif (isinstance (event, gdb.SyscallEvent)):
+        print ("stop reason: syscall")
+        print ("stop syscall: %d" % (event.stop_syscall))
+    elif (isinstance (event, gdb.BreakpointEvent)):
         print ("stop reason: breakpoint")
         print ("first breakpoint number: %s" % (event.breakpoint.number))
         for bp in event.breakpoints:
@@ -93,8 +93,7 @@ class test_events (gdb.Command):
         gdb.Command.__init__ (self, "test-events", gdb.COMMAND_STACK)
 
     def invoke (self, arg, from_tty):
-        gdb.events.stop.connect (signal_stop_handler)
-        gdb.events.stop.connect (breakpoint_stop_handler)
+        gdb.events.stop.connect (stop_handler)
         gdb.events.exited.connect (exit_handler)
         gdb.events.cont.connect (continue_handler)
         gdb.events.inferior_call.connect (inferior_call_handler)
diff --git a/gdb/testsuite/gdb.python/py-evsignal.exp b/gdb/testsuite/gdb.python/py-evsignal.exp
index c90be8a..d8571d3 100644
--- a/gdb/testsuite/gdb.python/py-evsignal.exp
+++ b/gdb/testsuite/gdb.python/py-evsignal.exp
@@ -38,7 +38,7 @@ gdb_test_no_output "set non-stop on"
 
 gdb_run_cmd
 gdb_test_multiple "" "Signal Thread 3"  {
-    -re "event type: stop\r\nstop reason: signal\r\nstop signal: SIGUSR1\r\nthread num: 3\r\nevent type: stop\r\n$gdb_prompt $" {
+    -re "event type: stop\r\nstop reason: signal\r\nstop signal: SIGUSR1\r\nthread num: 3\r\n$gdb_prompt $" {
         pass "thread 3 was signaled"
     }
     -re "The target does not support running in non-stop mode"  {
diff --git a/gdb/testsuite/gdb.python/py-evsyscall.exp b/gdb/testsuite/gdb.python/py-evsyscall.exp
new file mode 100644
index 0000000..5040089
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-evsyscall.exp
@@ -0,0 +1,41 @@
+# Copyright (C) 2010-2015 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+load_lib gdb-python.exp
+
+standard_testfile py-value.c
+set pyfile ${srcdir}/${subdir}/py-events.py
+
+if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+    return -1
+}
+clean_restart $testfile
+
+if { [skip_python_tests] } { continue }
+
+
+gdb_test "catch syscall write" "Catchpoint.*\(write\).*" "catch syscall write"
+
+gdb_test_no_output "python exec (open ('${pyfile}').read ())" ""
+
+gdb_test "test-events" "Event testers registered."
+
+gdb_run_cmd
+
+gdb_test_multiple "" "write syscall"  {
+    -re "event type: stop\r\nstop reason: syscall\r\nstop syscall: 1\r\n$gdb_prompt $" {
+        pass "write syscall performed"
+    }
+}
diff --git a/gdb/testsuite/gdb.python/py-evthreads.exp b/gdb/testsuite/gdb.python/py-evthreads.exp
index 4c9a53a..a1179eb 100644
--- a/gdb/testsuite/gdb.python/py-evthreads.exp
+++ b/gdb/testsuite/gdb.python/py-evthreads.exp
@@ -75,7 +75,7 @@ gdb_test_multiple "continue&" $test {
 
 set test "thread 3 was signaled"
 gdb_test_multiple "" $test {
-    -re "event type: stop\r\nstop reason: signal\r\nstop signal: SIGUSR1\r\nthread num: 3\r\nevent type: stop\r\n$" {
+    -re "event type: stop\r\nstop reason: signal\r\nstop signal: SIGUSR1\r\nthread num: 3\r\n$" {
         pass $test
     }
 }
-- 
2.3.6



More information about the Gdb-patches mailing list