From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 52176 invoked by alias); 1 Dec 2016 00:15:37 -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 52084 invoked by uid 89); 1 Dec 2016 00:15:29 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-4.5 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_NONE,RP_MATCHES_RCVD,SPF_PASS autolearn=ham version=3.3.2 spammy=1606, inferiors, *arg1, beneath X-HELO: mail-io0-f201.google.com Received: from mail-io0-f201.google.com (HELO mail-io0-f201.google.com) (209.85.223.201) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 01 Dec 2016 00:15:19 +0000 Received: by mail-io0-f201.google.com with SMTP id c21so11854365ioj.1 for ; Wed, 30 Nov 2016 16:15:17 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:message-id:date:subject:from:to:cc; bh=zIM4f82S1fkC8TpRnWATQT5fZuDWmZMZaCf55g6n5mM=; b=L9Cp8r7l7fuokyJ7eYRo/ZoILc4mGO51YH4+ZJj6ByCwLDs+5kyF1q1Wg6Fth9pSYS F6g5h3hmLjrHLl7s3PVt3PFjKvIB/pbY+U86xBzoiNldU6y21bvLp2VcsJzug9qL/i2W eMg/L96zRbGcorEcXbp3LNIL8KOjhokSwYcOGcgJyWsj/IjPF0v7lfj/VyCgOD3ArK+4 0yxY6+6dSXPby9BvOU2wL/NKbK9/oZrtm76PVk1NvCfl33ILSYfKIgYHI/xqtVGy4oMd 1Z8+Hi0CXHPL6LUFCYTB2UsxpdON0UBUjaBLZxwkrviG1zfqdF5n+qAhgIygeLrav8vM R6cw== X-Gm-Message-State: AKaTC03fDsVhkPLFf9iT4SzmpGdRKoBKfmXkfz9/DsmHwWd+zfs0xwoVdjKNeVTeF5wQRLiphw== MIME-Version: 1.0 X-Received: by 10.157.24.45 with SMTP id b42mr8525753ote.134.1480551315849; Wed, 30 Nov 2016 16:15:15 -0800 (PST) Message-ID: <001a113fadee41258e05428db71d@google.com> Date: Thu, 01 Dec 2016 00:15:00 -0000 Subject: Re: [PATCH v3 5/8] python: Create Python bindings for record history. From: Doug Evans To: Tim Wiederhake Cc: gdb-patches@sourceware.org, palves@redhat.com, markus.t.metzger@intel.com Content-Type: text/plain; charset=UTF-8; format=flowed; delsp=yes X-IsSubscribed: yes X-SW-Source: 2016-12/txt/msg00000.txt.bz2 Tim Wiederhake writes: > 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-11-21 Tim Wiederhake > > gdb/ChangeLog > > * Makefile.in (SUBDIR_PYTHON_OBS): Add python/py-record.o. > (SUBDIR_PYTHON_SRCS): Add python/py-record.c. > * 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: Regenerate. > * 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): Add to_record_python_interface function. > > > --- > gdb/Makefile.in | 2 + > gdb/python/py-record.c | 257 +++++++++++++++++++++++++++++++++++++++++++ > gdb/python/py-record.h | 57 ++++++++++ > gdb/python/python-internal.h | 5 + > gdb/python/python.c | 13 +++ > gdb/target-debug.h | 2 + > gdb/target-delegates.c | 33 ++++++ > gdb/target.c | 7 ++ > gdb/target.h | 10 ++ > 9 files changed, 386 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 b68cf58..3340b5e 100644 > --- a/gdb/Makefile.in > +++ b/gdb/Makefile.in > @@ -403,6 +403,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 \ > @@ -443,6 +444,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 \ > diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c > new file mode 100644 > index 0000000..e25f6ca > --- /dev/null > +++ b/gdb/python/py-record.c > @@ -0,0 +1,257 @@ > +/* 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.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); > +} > + > +/* Implementation of gdb.start_recording (method) -> gdb.Record. */ > + > +PyObject * > +gdbpy_start_recording (PyObject *self, PyObject *args) > +{ > + const char *method = NULL; > + const char *format = NULL; > + > + if (!PyArg_ParseTuple (args, "|ss", &method, &format)) > + return NULL; > + > + TRY > + { > + record_start (method, format, 0); I suppose we're leaving it to gdb to deal with issues like "what happens if I start a recording on one inferior, switch inferiors, and start another recording?" Library APIs based on global state suck, but that's not your fault. Still, I wonder if the user can get into any kind of trouble doing such things. > + return gdbpy_current_recording (self, args); > + } > + CATCH (except, RETURN_MASK_ALL) > + { > + gdbpy_convert_exception (except); > + } > + END_CATCH > + > + return NULL; > +} > + > +/* 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) > +{ > + TRY > + { > + execute_command_to_string ("record stop", 0); > + Py_RETURN_NONE; > + } > + CATCH (except, RETURN_MASK_ALL) > + { > + gdbpy_convert_exception (except); > + } > + END_CATCH > + > + return NULL; > +} > 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 5f0abf8..e2bb9a8 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 321479b..7f031ae 100644 > --- a/gdb/python/python.c > +++ b/gdb/python/python.c > @@ -1786,6 +1786,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 > @@ -1998,6 +1999,18 @@ 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] [, format]) -> gdb.Record.\n\ > +Start recording with the given method. If no method is given, will fall back\n\ > +to the system default method. If no format is given, will fall back to the\n\ > +default format for the given method."}, > + { "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 73e45dd..66e137b 100644 > --- a/gdb/target-delegates.c > +++ b/gdb/target-delegates.c > @@ -3995,6 +3995,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); > +} Access to gdb's extension languages must go through the extension language interface. [Any time you see the word "python" (or "scheme") in a core gdb object think "bug!"] See extension.[ch], extension-priv.h. > + > +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; > @@ -4418,6 +4447,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) > @@ -4581,6 +4612,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; > @@ -4739,6 +4771,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 246d292..416a6ab 100644 > --- a/gdb/target.c > +++ b/gdb/target.c > @@ -3812,6 +3812,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 a54b3db..70f10fb 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. */ > @@ -1232,6 +1233,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 *) > @@ -2528,6 +2535,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 > -- /dje