From patchwork Mon Jun 1 22:19:03 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessandro Di Federico X-Patchwork-Id: 7002 Received: (qmail 8276 invoked by alias); 1 Jun 2015 22:18:54 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 8267 invoked by uid 89); 1 Jun 2015 22:18:54 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.9 required=5.0 tests=AWL, BAYES_50, KAM_INFOUSMEBIZ, SPF_HELO_PASS, SPF_PASS, T_RP_MATCHES_RCVD autolearn=no version=3.3.2 X-HELO: clearmind.me Received: from clearmind.me (HELO clearmind.me) (178.32.49.9) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Mon, 01 Jun 2015 22:18:49 +0000 From: Alessandro Di Federico To: gdb-patches@sourceware.org Subject: [PATCH] Expose signal and syscall catchpoints to Python Date: Tue, 2 Jun 2015 00:19:03 +0200 Message-Id: <1433197143-5402-1-git-send-email-ale+gdb@clearmind.me> X-IsSubscribed: yes 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 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 . */ + +#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 /* 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\])" \ + "" "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\])" \ + "" "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\])" \ + "" "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 . + +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 } }