This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PING] [PATCH] Expose signal and syscall catchpoints to Python
- From: Alessandro Di Federico <ale+gdb at clearmind dot me>
- To: gdb-patches at sourceware dot org
- Cc: dje at google dot com
- Date: Fri, 26 Jun 2015 11:55:36 +0200
- Subject: [PING] [PATCH] Expose signal and syscall catchpoints to Python
- Authentication-results: sourceware.org; auth=none
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