From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 75514 invoked by alias); 27 Oct 2016 06:29:35 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 75337 invoked by uid 89); 27 Oct 2016 06:29:34 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.3 required=5.0 tests=AWL,BAYES_00,KAM_LAZY_DOMAIN_SECURITY,RP_MATCHES_RCVD autolearn=ham version=3.3.2 spammy=4026, 4437, 443,7, closure X-HELO: mga06.intel.com Received: from mga06.intel.com (HELO mga06.intel.com) (134.134.136.31) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 27 Oct 2016 06:29:23 +0000 Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by orsmga104.jf.intel.com with ESMTP; 26 Oct 2016 23:29:21 -0700 X-ExtLoop1: 1 Received: from irvmail001.ir.intel.com ([163.33.26.43]) by fmsmga002.fm.intel.com with ESMTP; 26 Oct 2016 23:29:20 -0700 Received: from ulvlx001.iul.intel.com (ulvlx001.iul.intel.com [172.28.207.17]) by irvmail001.ir.intel.com (8.14.3/8.13.6/MailSET/Hub) with ESMTP id u9R6TJuL013786; Thu, 27 Oct 2016 07:29:20 +0100 Received: from ulvlx001.iul.intel.com (localhost [127.0.0.1]) by ulvlx001.iul.intel.com with ESMTP id u9R6TJNG003350; Thu, 27 Oct 2016 08:29:19 +0200 Received: (from twiederh@localhost) by ulvlx001.iul.intel.com with œ id u9R6TJ5B003347; Thu, 27 Oct 2016 08:29:19 +0200 From: Tim Wiederhake To: gdb-patches@sourceware.org Cc: palves@redhat.com, markus.t.metzger@intel.com Subject: [PATCH 4/7] python: Create Python bindings for record history. Date: Thu, 27 Oct 2016 06:29:00 -0000 Message-Id: <1477549711-2603-5-git-send-email-tim.wiederhake@intel.com> In-Reply-To: <1477549711-2603-1-git-send-email-tim.wiederhake@intel.com> References: <1477549711-2603-1-git-send-email-tim.wiederhake@intel.com> X-IsSubscribed: yes X-SW-Source: 2016-10/txt/msg00738.txt.bz2 This patch adds three new functions to the gdb module in Python: - start_recording - stop_recording - current_recording start_recording and current_recording return an object of the new type gdb.Record, which can be used to access the recorded data. 2016-10-26 Tim Wiederhake gdb/ChangeLog * Makefile.in (SUBDIR_PYTHON_OBS): Add python/py-record.o. (SUBDIR_PYTHON_SRCS): Add python/py-record.c. (py-record.o): New rule. * python/py-record.c: New file. * python/py-record.h: New file. * python/python-internal.h (gdbpy_start_recording, gdbpy_current_recording, gdpy_stop_recording, gdbpy_initialize_record): New export. * python/python.c (_initialize_python): Add gdbpy_initialize_record. (python_GdbMethods): Add gdbpy_start_recording, gdbpy_current_recording and gdbpy_stop_recording. * target-debug.h (target_debug_print_struct_record_python_interface): New define. * target-delegates.c: Regenerated. * target.c (target_record_python_interface): New function. * target.h: Added struct record_python_interface forward declaration. Export target_record_python_interface. (struct target_ops): Added to_record_python_interface function. --- gdb/Makefile.in | 6 + gdb/python/py-record.c | 291 +++++++++++++++++++++++++++++++++++++++++++ gdb/python/py-record.h | 57 +++++++++ gdb/python/python-internal.h | 5 + gdb/python/python.c | 12 ++ gdb/target-debug.h | 2 + gdb/target-delegates.c | 33 +++++ gdb/target.c | 7 ++ gdb/target.h | 10 ++ 9 files changed, 423 insertions(+) create mode 100644 gdb/python/py-record.c create mode 100644 gdb/python/py-record.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 2c88434..844a648 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -402,6 +402,7 @@ SUBDIR_PYTHON_OBS = \ py-param.o \ py-prettyprint.o \ py-progspace.o \ + py-record.o \ py-signalevent.o \ py-stopevent.o \ py-symbol.o \ @@ -442,6 +443,7 @@ SUBDIR_PYTHON_SRCS = \ python/py-param.c \ python/py-prettyprint.c \ python/py-progspace.c \ + python/py-record.c \ python/py-signalevent.c \ python/py-stopevent.c \ python/py-symbol.c \ @@ -2690,6 +2692,10 @@ py-progspace.o: $(srcdir)/python/py-progspace.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-progspace.c $(POSTCOMPILE) +py-record.o: $(srcdir)/python/py-record.c + $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-record.c + $(POSTCOMPILE) + py-signalevent.o: $(srcdir)/python/py-signalevent.c $(COMPILE) $(PYTHON_CFLAGS) $(srcdir)/python/py-signalevent.c $(POSTCOMPILE) diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c new file mode 100644 index 0000000..2fe6e13 --- /dev/null +++ b/gdb/python/py-record.c @@ -0,0 +1,291 @@ +/* Python interface to record targets. + + Copyright 2016 Free Software Foundation, Inc. + + Contributed by Intel Corp. + + 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 "gdbcmd.h" +#include "record-btrace.h" +#include "record-full.h" +#include "py-record.h" +#include "target.h" + +/* Python Record object. */ + +typedef struct +{ + PyObject_HEAD + + struct record_python_interface interface; +} recpy_record_object; + +/* Python Record type. */ + +static PyTypeObject recpy_record_type = { + PyVarObject_HEAD_INIT (NULL, 0) +}; + +/* Implementation of record.method. */ + +static PyObject * +recpy_method (PyObject *self, void* closure) +{ + recpy_record_object *obj = (recpy_record_object *) self; + + if (obj->interface.method == NULL) + return PyString_FromString (_("unknown")); + + return PyString_FromString (obj->interface.method); +} + +/* Implementation of record.format. */ + +static PyObject * +recpy_format (PyObject *self, void* closure) +{ + recpy_record_object *obj = (recpy_record_object *) self; + + if (obj->interface.format == NULL) + return PyString_FromString (_("unknown")); + + return PyString_FromString (obj->interface.format); +} + +/* Implementation of record.goto (instruction) -> None. */ + +static PyObject * +recpy_goto (PyObject *self, PyObject *value) +{ + recpy_record_object *obj = (recpy_record_object *) self; + + if (obj->interface.goto_position == NULL) + return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); + + return obj->interface.goto_position (self, value); +} + +/* Implementation of record.replay_position [instruction] */ + +static PyObject * +recpy_replay_position (PyObject *self, void *closure) +{ + recpy_record_object *obj = (recpy_record_object *) self; + + if (obj->interface.replay_position == NULL) + return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); + + return obj->interface.replay_position (self, closure); +} + +/* Implementation of record.instruction_history [list]. */ + +static PyObject * +recpy_instruction_history (PyObject *self, void* closure) +{ + recpy_record_object *obj = (recpy_record_object *) self; + + if (obj->interface.instruction_history == NULL) + return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); + + return obj->interface.instruction_history (self, closure); +} + +/* Implementation of record.function_call_history [list]. */ + +static PyObject * +recpy_function_call_history (PyObject *self, void* closure) +{ + recpy_record_object *obj = (recpy_record_object *) self; + + if (obj->interface.function_call_history == NULL) + return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); + + return obj->interface.function_call_history (self, closure); +} + +/* Implementation of record.begin [instruction]. */ + +static PyObject * +recpy_begin (PyObject *self, void* closure) +{ + recpy_record_object *obj = (recpy_record_object *) self; + + if (obj->interface.begin == NULL) + return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); + + return obj->interface.begin (self, closure); +} + +/* Implementation of record.end [instruction]. */ + +static PyObject * +recpy_end (PyObject *self, void* closure) +{ + recpy_record_object *obj = (recpy_record_object *) self; + + if (obj->interface.end == NULL) + return PyErr_Format (PyExc_NotImplementedError, _("Not implemented.")); + + return obj->interface.end (self, closure); +} + +/* Record method list. */ + +static PyMethodDef recpy_record_methods[] = { + { "goto", recpy_goto, METH_VARARGS, + "goto (instruction|function_call) -> None.\n\ +Rewind to given location."}, + { NULL } +}; + +/* Record member list. */ + +static PyGetSetDef recpy_record_getset[] = { + { "method", recpy_method, NULL, "Current recording method.", NULL }, + { "format", recpy_format, NULL, "Current recording format.", NULL }, + { "replay_position", recpy_replay_position, NULL, "Current replay position.", + NULL }, + { "instruction_history", recpy_instruction_history, NULL, + "List of instructions in current recording.", NULL }, + { "function_call_history", recpy_function_call_history, NULL, + "List of function calls in current recording.", NULL }, + { "begin", recpy_begin, NULL, + "First instruction in current recording.", NULL }, + { "end", recpy_end, NULL, + "One past the last instruction in current recording. This is typically the \ +current instruction and is used for e.g. record.goto(record.end).", NULL }, + { NULL } +}; + +/* Sets up the record API in the gdb module. */ + +int +gdbpy_initialize_record (void) +{ + recpy_record_type.tp_new = PyType_GenericNew; + recpy_record_type.tp_flags = Py_TPFLAGS_DEFAULT; + recpy_record_type.tp_basicsize = sizeof (recpy_record_object); + recpy_record_type.tp_name = "gdb.Record"; + recpy_record_type.tp_doc = "GDB record object"; + recpy_record_type.tp_methods = recpy_record_methods; + recpy_record_type.tp_getset = recpy_record_getset; + + return PyType_Ready (&recpy_record_type); +} + +/* Executes a command silently. Returns non-zero on success; returns zero and + sets Python exception on failure. */ + +static int +gdbpy_execute_silenty (char *command) +{ + char *output = NULL; + int success = 1; + + TRY + { + output = execute_command_to_string (command, 0); + } + CATCH (except, RETURN_MASK_ALL) + { + if (except.reason < 0) + gdbpy_convert_exception (except); + + success = 0; + } + END_CATCH + + xfree (output); + return success; +} + +/* Implementation of gdb.start_recording (method) -> gdb.Record. */ + +PyObject * +gdbpy_start_recording (PyObject *self, PyObject *args) +{ + char *method = "full"; + + if (!PyArg_ParseTuple (args, "|s", &method)) + return NULL; + + if (strncmp (method, "full", sizeof ("full")) == 0) + { + if (!gdbpy_execute_silenty ("record full")) + return NULL; + } + else if (strncmp (method, "btrace", sizeof ("btrace")) == 0) + { + if (!gdbpy_execute_silenty ("record btrace pt")) + { + PyErr_Clear (); + if (!gdbpy_execute_silenty ("record btrace bts")) + return NULL; + } + } + else if ((strncmp (method, "pt", sizeof ("pt")) == 0) + || (strncmp (method, "btrace pt", sizeof ("btrace pt")) == 0)) + { + if (!gdbpy_execute_silenty ("record btrace pt")) + return NULL; + } + else if ((strncmp (method, "bts", sizeof ("bts")) == 0) + || (strncmp (method, "btrace bts", sizeof ("btrace bts")) == 0)) + { + if (!gdbpy_execute_silenty ("record btrace bts")) + return NULL; + } + else + return PyErr_Format (PyExc_TypeError, _("Unknown recording method.")); + + return gdbpy_current_recording (self, args); +} + +/* Implementation of gdb.current_recording (self) -> gdb.Record. */ + +PyObject * +gdbpy_current_recording (PyObject *self, PyObject *args) +{ + recpy_record_object* obj; + + obj = PyObject_New (recpy_record_object, &recpy_record_type); + if (obj == NULL) + return NULL; + + memset (&obj->interface, 0, sizeof (struct record_python_interface)); + + if (!target_record_python_interface (&obj->interface)) + { + Py_DecRef ((PyObject *) obj); + Py_RETURN_NONE; + } + + return (PyObject *) obj; +} + +/* Implementation of gdb.stop_recording (self) -> None. */ + +PyObject * +gdbpy_stop_recording (PyObject *self, PyObject *args) +{ + if (!gdbpy_execute_silenty ("record stop")) + return NULL; + + Py_RETURN_NONE; +} diff --git a/gdb/python/py-record.h b/gdb/python/py-record.h new file mode 100644 index 0000000..fb56182 --- /dev/null +++ b/gdb/python/py-record.h @@ -0,0 +1,57 @@ +/* Python interface to record targets. + + Copyright 2016 Free Software Foundation, Inc. + + Contributed by Intel Corp. + + 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 . */ + +#ifndef GDB_PY_RECORD_H +#define GDB_PY_RECORD_H + +#include "python-internal.h" + +/* Record interface for Python bindings. Any field may be NULL if unknown + or not supported. */ + +struct record_python_interface +{ + /* Recording method, e.g. "full" or "btrace". */ + const char *method; + + /* Recording format, e.g. "bts" or "pt". */ + const char *format; + + /* Implementation of gdb.Record.goto (instruction) -> None. */ + binaryfunc goto_position; + + /* Implementation of gdb.Record.replay_position [instruction] */ + getter replay_position; + + /* Implementation of gdb.Record.instruction_history [list]. */ + getter instruction_history; + + /* Implementation of gdb.Record.function_call_history [list]. */ + getter function_call_history; + + /* Implementation of gdb.Record.begin [instruction]. */ + getter begin; + + /* Implementation of gdb.Record.end [instruction]. */ + getter end; +}; + +#endif /* GDB_PY_RECORD_H */ diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 8545c7b..3828b4c 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -362,6 +362,9 @@ PyObject *gdbpy_frame_stop_reason_string (PyObject *, PyObject *); PyObject *gdbpy_lookup_symbol (PyObject *self, PyObject *args, PyObject *kw); PyObject *gdbpy_lookup_global_symbol (PyObject *self, PyObject *args, PyObject *kw); +PyObject *gdbpy_start_recording (PyObject *self, PyObject *args); +PyObject *gdbpy_current_recording (PyObject *self, PyObject *args); +PyObject *gdbpy_stop_recording (PyObject *self, PyObject *args); PyObject *gdbpy_newest_frame (PyObject *self, PyObject *args); PyObject *gdbpy_selected_frame (PyObject *self, PyObject *args); PyObject *gdbpy_block_for_pc (PyObject *self, PyObject *args); @@ -429,6 +432,8 @@ int gdbpy_initialize_values (void) CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; int gdbpy_initialize_frames (void) CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; +int gdbpy_initialize_record (void) + CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; int gdbpy_initialize_symtabs (void) CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; int gdbpy_initialize_commands (void) diff --git a/gdb/python/python.c b/gdb/python/python.c index 7e34d26..39e411d 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1799,6 +1799,7 @@ message == an error message without a stack will be printed."), || gdbpy_initialize_values () < 0 || gdbpy_initialize_frames () < 0 || gdbpy_initialize_commands () < 0 + || gdbpy_initialize_record () < 0 || gdbpy_initialize_symbols () < 0 || gdbpy_initialize_symtabs () < 0 || gdbpy_initialize_blocks () < 0 @@ -2011,6 +2012,17 @@ Return the selected frame object." }, "stop_reason_string (Integer) -> String.\n\ Return a string explaining unwind stop reason." }, + { "start_recording", gdbpy_start_recording, METH_VARARGS, + "start_recording ([method]) -> gdb.Record.\n\ +Start recording with the given method. If no method is given, will fall back\n\ +to the system default metdhod."}, + { "current_recording", gdbpy_current_recording, METH_NOARGS, + "current_recording () -> gdb.Record.\n\ +Return current recording object." }, + { "stop_recording", gdbpy_stop_recording, METH_NOARGS, + "stop_recording () -> None.\n\ +Stop current recording." }, + { "lookup_type", (PyCFunction) gdbpy_lookup_type, METH_VARARGS | METH_KEYWORDS, "lookup_type (name [, block]) -> type\n\ diff --git a/gdb/target-debug.h b/gdb/target-debug.h index ef7e14d..8790016 100644 --- a/gdb/target-debug.h +++ b/gdb/target-debug.h @@ -160,6 +160,8 @@ target_debug_do_print (host_address_to_string (X)) #define target_debug_print_enum_remove_bp_reason(X) \ target_debug_do_print (plongest (X)) +#define target_debug_print_struct_record_python_interface_p(X) \ + target_debug_do_print (host_address_to_string (X)) static void target_debug_print_struct_target_waitstatus_p (struct target_waitstatus *status) diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c index 57e7939..d7edfdb 100644 --- a/gdb/target-delegates.c +++ b/gdb/target-delegates.c @@ -3973,6 +3973,35 @@ debug_call_history_range (struct target_ops *self, ULONGEST arg1, ULONGEST arg2, } static int +delegate_record_python_interface (struct target_ops *self, struct record_python_interface *arg1) +{ + self = self->beneath; + return self->to_record_python_interface (self, arg1); +} + +static int +tdefault_record_python_interface (struct target_ops *self, struct record_python_interface *arg1) +{ + return 0; +} + +static int +debug_record_python_interface (struct target_ops *self, struct record_python_interface *arg1) +{ + int result; + fprintf_unfiltered (gdb_stdlog, "-> %s->to_record_python_interface (...)\n", debug_target.to_shortname); + result = debug_target.to_record_python_interface (&debug_target, arg1); + fprintf_unfiltered (gdb_stdlog, "<- %s->to_record_python_interface (", debug_target.to_shortname); + target_debug_print_struct_target_ops_p (&debug_target); + fputs_unfiltered (", ", gdb_stdlog); + target_debug_print_struct_record_python_interface_p (arg1); + fputs_unfiltered (") = ", gdb_stdlog); + target_debug_print_int (result); + fputs_unfiltered ("\n", gdb_stdlog); + return result; +} + +static int delegate_augmented_libraries_svr4_read (struct target_ops *self) { self = self->beneath; @@ -4394,6 +4423,8 @@ install_delegators (struct target_ops *ops) ops->to_call_history_from = delegate_call_history_from; if (ops->to_call_history_range == NULL) ops->to_call_history_range = delegate_call_history_range; + if (ops->to_record_python_interface == NULL) + ops->to_record_python_interface = delegate_record_python_interface; if (ops->to_augmented_libraries_svr4_read == NULL) ops->to_augmented_libraries_svr4_read = delegate_augmented_libraries_svr4_read; if (ops->to_get_unwinder == NULL) @@ -4556,6 +4587,7 @@ install_dummy_methods (struct target_ops *ops) ops->to_call_history = tdefault_call_history; ops->to_call_history_from = tdefault_call_history_from; ops->to_call_history_range = tdefault_call_history_range; + ops->to_record_python_interface = tdefault_record_python_interface; ops->to_augmented_libraries_svr4_read = tdefault_augmented_libraries_svr4_read; ops->to_get_unwinder = tdefault_get_unwinder; ops->to_get_tailcall_unwinder = tdefault_get_tailcall_unwinder; @@ -4713,6 +4745,7 @@ init_debug_target (struct target_ops *ops) ops->to_call_history = debug_call_history; ops->to_call_history_from = debug_call_history_from; ops->to_call_history_range = debug_call_history_range; + ops->to_record_python_interface = debug_record_python_interface; ops->to_augmented_libraries_svr4_read = debug_augmented_libraries_svr4_read; ops->to_get_unwinder = debug_get_unwinder; ops->to_get_tailcall_unwinder = debug_get_tailcall_unwinder; diff --git a/gdb/target.c b/gdb/target.c index cb89e75..50a192b 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -3784,6 +3784,13 @@ target_record_stop_replaying (void) } /* See target.h. */ +int +target_record_python_interface (struct record_python_interface *iface) +{ + return current_target.to_record_python_interface (¤t_target, iface); +} + +/* See target.h. */ void target_goto_record_begin (void) diff --git a/gdb/target.h b/gdb/target.h index 176f332..faaed0e 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -39,6 +39,7 @@ struct traceframe_info; struct expression; struct dcache_struct; struct inferior; +struct record_python_interface; #include "infrun.h" /* For enum exec_direction_kind. */ #include "breakpoint.h" /* For enum bptype. */ @@ -1230,6 +1231,12 @@ struct target_ops ULONGEST begin, ULONGEST end, int flags) TARGET_DEFAULT_NORETURN (tcomplain ()); + /* Fill in the record python interface object and return non-zero. + Return zero on failure or if no recording is active. */ + int (*to_record_python_interface) (struct target_ops *, + struct record_python_interface *) + TARGET_DEFAULT_RETURN (0); + /* Nonzero if TARGET_OBJECT_LIBRARIES_SVR4 may be read with a non-empty annex. */ int (*to_augmented_libraries_svr4_read) (struct target_ops *) @@ -2503,6 +2510,9 @@ extern void target_call_history_from (ULONGEST begin, int size, int flags); /* See to_call_history_range. */ extern void target_call_history_range (ULONGEST begin, ULONGEST end, int flags); +/* See to_record_python_interface. */ +extern int target_record_python_interface (struct record_python_interface *); + /* See to_prepare_to_generate_core. */ extern void target_prepare_to_generate_core (void); -- 2.7.4