http://sourceware.org/gdb/wiki/ProjectArcher http://sourceware.org/gdb/wiki/ArcherBranchManagement GIT snapshot: commit cfee64ee869a6a6e4ab0b64af149d4cf59517d80 tromey/python diff --git a/README.archer b/README.archer new file mode 100644 index 0000000..173b8ea --- /dev/null +++ b/README.archer @@ -0,0 +1,2 @@ +This branch originally held the Python code for gdb. It still exists +because a small amount of code here has not yet been merged upstream. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 5af6103..9c69cb0 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1553,6 +1553,12 @@ stamp-h: $(srcdir)/config.in config.status CONFIG_LINKS= \ $(SHELL) config.status +.gdbinit: $(srcdir)/gdbinit.in config.status + CONFIG_FILES=".gdbinit:gdbinit.in" \ + CONFIG_COMMANDS= \ + CONFIG_HEADERS= \ + $(SHELL) config.status + config.status: $(srcdir)/configure configure.tgt configure.host ../bfd/development.sh $(SHELL) config.status --recheck diff --git a/gdb/data-directory/Makefile.in b/gdb/data-directory/Makefile.in index 3f23516..b5d6390 100644 --- a/gdb/data-directory/Makefile.in +++ b/gdb/data-directory/Makefile.in @@ -66,7 +66,10 @@ PYTHON_FILE_LIST = \ gdb/unwinder.py \ gdb/prompt.py \ gdb/xmethod.py \ + gdb/types.py \ gdb/command/__init__.py \ + gdb/command/ignore_errors.py \ + gdb/command/pahole.py \ gdb/command/xmethods.py \ gdb/command/frame_filters.py \ gdb/command/unwinders.py \ @@ -78,6 +81,8 @@ PYTHON_FILE_LIST = \ gdb/function/as_string.py \ gdb/function/caller_is.py \ gdb/function/strfns.py \ + gdb/function/caller_is.py \ + gdb/function/in_scope.py \ gdb/printer/__init__.py \ gdb/printer/bound_registers.py diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index f5dde61..1d92bd6 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -1225,6 +1225,16 @@ for remote debugging. Run using @var{device} for your program's standard input and output. @c FIXME: kingdon thinks there is more to -tty. Investigate. +@item -P +@cindex @code{-P} +@itemx --python +@cindex @code{--python} +Change interpretation of command line so that the argument immediately +following this switch is taken to be the name of a Python script file. +This option stops option processing; subsequent options are passed to +Python as @code{sys.argv}. This option is only available if Python +scripting support was enabled when @value{GDBN} was configured. + @c resolve the situation of these eventually @item -tui @cindex @code{--tui} diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index a17e37d..c56068b 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -88,8 +88,6 @@ containing @code{end}. For example: @smallexample (@value{GDBP}) python -Type python script -End with a line saying just "end". >print 23 >end 23 diff --git a/gdb/gdb-gdb.gdb.in b/gdb/gdb-gdb.gdb.in index 05a38b2..9801fdf 100644 --- a/gdb/gdb-gdb.gdb.in +++ b/gdb/gdb-gdb.gdb.in @@ -1,5 +1,15 @@ echo Setting up the environment for debugging gdb.\n +# Set up the Python library and "require" command. +python +from os.path import abspath +gdb.datadir = abspath ('@srcdir@/python/lib') +gdb.pythonlibdir = gdb.datadir +gdb.__path__ = [gdb.datadir + '/gdb'] +sys.path.insert(0, gdb.datadir) +end +source @srcdir@/python/lib/gdb/__init__.py + if !$gdb_init_done set variable $gdb_init_done = 1 diff --git a/gdb/main.c b/gdb/main.c index 5477379..b7e413f 100644 --- a/gdb/main.c +++ b/gdb/main.c @@ -33,6 +33,7 @@ #include "interps.h" #include "main.h" +#include "python/python.h" #include "source.h" #include "cli/cli-cmds.h" #include "objfiles.h" @@ -459,6 +460,8 @@ captured_main (void *data) char *cdarg = NULL; char *ttyarg = NULL; + int python_script = 0; + /* These are static so that we can take their address in an initializer. */ static int print_help; @@ -663,10 +666,14 @@ captured_main (void *data) {"args", no_argument, &set_args, 1}, {"l", required_argument, 0, 'l'}, {"return-child-result", no_argument, &return_child_result, 1}, +#if HAVE_PYTHON + {"python", no_argument, 0, 'P'}, + {"P", no_argument, 0, 'P'}, +#endif {0, no_argument, 0, 0} }; - while (1) + while (!python_script) { int option_index; @@ -684,6 +691,9 @@ captured_main (void *data) case 0: /* Long option that just sets a flag. */ break; + case 'P': + python_script = 1; + break; case OPT_SE: symarg = optarg; execarg = optarg; @@ -864,7 +874,31 @@ captured_main (void *data) /* Now that gdb_init has created the initial inferior, we're in position to set args for that inferior. */ - if (set_args) + if (python_script) + { + /* The first argument is a python script to evaluate, and + subsequent arguments are passed to the script for + processing there. */ + if (optind >= argc) + { + fprintf_unfiltered (gdb_stderr, + _("%s: Python script file name required\n"), + argv[0]); + exit (1); + } + + /* FIXME: should handle inferior I/O intelligently here. + E.g., should be possible to run gdb in pipeline and have + Python (and gdb) output go to stderr or file; and if a + prompt is needed, open the tty. */ + quiet = 1; + /* FIXME: should read .gdbinit if, and only if, a prompt is + requested by the script. Though... maybe this is not + ideal? */ + /* FIXME: likewise, reading in history. */ + inhibit_gdbinit = 1; + } + else if (set_args) { /* The remaining options are the command-line options for the inferior. The first one is the sym/exec file, and the rest @@ -1127,7 +1161,8 @@ captured_main (void *data) /* Read in the old history after all the command files have been read. */ - init_history (); + if (!python_script) + init_history (); if (batch_flag) { @@ -1138,13 +1173,25 @@ captured_main (void *data) /* Show time and/or space usage. */ do_cleanups (pre_stat_chain); - /* NOTE: cagney/1999-11-07: There is probably no reason for not - moving this loop and the code found in captured_command_loop() - into the command_loop() proper. The main thing holding back that - change - SET_TOP_LEVEL() - has been eliminated. */ - while (1) +#if HAVE_PYTHON + if (python_script) { - catch_errors (captured_command_loop, 0, "", RETURN_MASK_ALL); + extern int pagination_enabled; + pagination_enabled = 0; + run_python_script (argc - optind, &argv[optind]); + return 1; + } + else +#endif + { + /* NOTE: cagney/1999-11-07: There is probably no reason for not + moving this loop and the code found in captured_command_loop() + into the command_loop() proper. The main thing holding back that + change - SET_TOP_LEVEL() - has been eliminated. */ + while (1) + { + catch_errors (captured_command_loop, 0, "", RETURN_MASK_ALL); + } } /* No exit -- exit is through quit_command. */ } @@ -1187,6 +1234,12 @@ print_gdb_help (struct ui_file *stream) fputs_unfiltered (_("\ This is the GNU debugger. Usage:\n\n\ gdb [options] [executable-file [core-file or process-id]]\n\ + gdb [options] --args executable-file [inferior-arguments ...]\n"), stream); +#if HAVE_PYTHON + fputs_unfiltered (_("\ + gdb [options] [--python|-P] script-file [script-arguments ...]\n"), stream); +#endif + fputs_unfiltered (_("\n\ gdb [options] --args executable-file [inferior-arguments ...]\n\n\ "), stream); fputs_unfiltered (_("\ @@ -1231,6 +1284,13 @@ Output and user interface control:\n\n\ #endif fputs_unfiltered (_("\ --dbx DBX compatibility mode.\n\ +"), stream); +#if HAVE_PYTHON + fputs_unfiltered (_("\ + --python, -P Following argument is Python script file; remaining\n\ + arguments are passed to script.\n"), stream); +#endif + fputs_unfiltered (_("\ -q, --quiet, --silent\n\ Do not print version number on startup.\n\n\ "), stream); diff --git a/gdb/python/lib/gdb/command/ignore_errors.py b/gdb/python/lib/gdb/command/ignore_errors.py new file mode 100644 index 0000000..6fa48ff --- /dev/null +++ b/gdb/python/lib/gdb/command/ignore_errors.py @@ -0,0 +1,37 @@ +# Ignore errors in user commands. + +# Copyright (C) 2008 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 . + +import gdb + +class IgnoreErrorsCommand (gdb.Command): + """Execute a single command, ignoring all errors. +Only one-line commands are supported. +This is primarily useful in scripts.""" + + def __init__ (self): + super (IgnoreErrorsCommand, self).__init__ ("ignore-errors", + gdb.COMMAND_OBSCURE, + # FIXME... + gdb.COMPLETE_COMMAND) + + def invoke (self, arg, from_tty): + try: + gdb.execute (arg, from_tty) + except: + pass + +IgnoreErrorsCommand () diff --git a/gdb/python/lib/gdb/command/pahole.py b/gdb/python/lib/gdb/command/pahole.py new file mode 100644 index 0000000..e08eaf5 --- /dev/null +++ b/gdb/python/lib/gdb/command/pahole.py @@ -0,0 +1,81 @@ +# pahole command for gdb + +# Copyright (C) 2008, 2009, 2012 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 . + +import gdb + +class Pahole (gdb.Command): + """Show the holes in a structure. +This command takes a single argument, a type name. +It prints the type and displays comments showing where holes are.""" + + def __init__ (self): + super (Pahole, self).__init__ ("pahole", gdb.COMMAND_NONE, + gdb.COMPLETE_SYMBOL) + + def maybe_print_hole(self, bitpos, field_bitpos): + if bitpos != field_bitpos: + hole = field_bitpos - bitpos + print (' /* XXX %d bit hole, try to pack */' % hole) + + def pahole (self, type, level, name): + if name is None: + name = '' + tag = type.tag + if tag is None: + tag = '' + print ('%sstruct %s {' % (' ' * (2 * level), tag)) + bitpos = 0 + for field in type.fields (): + # Skip static fields. + if not hasattr (field, ('bitpos')): + continue + + ftype = field.type.strip_typedefs() + + self.maybe_print_hole(bitpos, field.bitpos) + bitpos = field.bitpos + if field.bitsize > 0: + fieldsize = field.bitsize + else: + # TARGET_CHAR_BIT here... + fieldsize = 8 * ftype.sizeof + + # TARGET_CHAR_BIT + print (' /* %3d %3d */' % (int (bitpos / 8), int (fieldsize / 8)), end = "") + bitpos = bitpos + fieldsize + + if ftype.code == gdb.TYPE_CODE_STRUCT: + self.pahole (ftype, level + 1, field.name) + else: + print (' ' * (2 + 2 * level), end = "") + print ('%s %s' % (str (ftype), field.name)) + + if level == 0: + self.maybe_print_hole(bitpos, 8 * type.sizeof) + + print (' ' * (14 + 2 * level), end = "") + print ('} %s' % name) + + def invoke (self, arg, from_tty): + type = gdb.lookup_type (arg) + type = type.strip_typedefs () + if type.code != gdb.TYPE_CODE_STRUCT: + raise (TypeError, '%s is not a struct type' % arg) + print (' ' * 14, end = "") + self.pahole (type, 0, '') + +Pahole() diff --git a/gdb/python/lib/gdb/function/in_scope.py b/gdb/python/lib/gdb/function/in_scope.py new file mode 100644 index 0000000..8742680 --- /dev/null +++ b/gdb/python/lib/gdb/function/in_scope.py @@ -0,0 +1,47 @@ +# In-scope function. + +# Copyright (C) 2008 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 . + +import gdb + +class InScope (gdb.Function): + """Return True if all the given variables or macros are in scope. +Takes one argument for each variable name to be checked.""" + + def __init__ (self): + super (InScope, self).__init__ ("in_scope") + + def invoke (self, *vars): + if len (vars) == 0: + raise (TypeError, "in_scope takes at least one argument") + + # gdb.Value isn't hashable so it can't be put in a map. + # Convert to string first. + wanted = set (map (lambda x: x.string (), vars)) + found = set () + block = gdb.selected_frame ().block () + while block: + for sym in block: + if (sym.is_argument or sym.is_constant + or sym.is_function or sym.is_variable): + if sym.name in wanted: + found.add (sym.name) + + block = block.superblock + + return wanted == found + +InScope () diff --git a/gdb/python/python.c b/gdb/python/python.c index 621e201..703a1a0 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -95,6 +95,8 @@ const struct extension_language_defn extension_language_python = #include "linespec.h" #include "source.h" #include "version.h" +#include "inferior.h" +#include "gdbthread.h" #include "target.h" #include "gdbthread.h" #include "interps.h" @@ -1263,6 +1265,92 @@ gdbpy_print_stack (void) /* Return the current Progspace. There always is one. */ +/* True if 'gdb -P' was used, false otherwise. */ +static int running_python_script; + +/* True if we are currently in a call to 'gdb.cli', false otherwise. */ +static int in_cli; + +/* Enter the command loop. */ + +static PyObject * +gdbpy_cli (PyObject *unused1, PyObject *unused2) +{ + if (! running_python_script || in_cli) + return PyErr_Format (PyExc_RuntimeError, "cannot invoke CLI recursively"); + + if (ui_out_is_mi_like_p (current_uiout)) + return PyErr_Format (PyExc_RuntimeError, _("Cannot invoke CLI from MI.")); + + in_cli = 1; + /* See captured_command_loop. */ + + /* Give the interpreter a chance to print a prompt. */ + interp_pre_command_loop (top_level_interpreter ()); + + /* Now it's time to start the event loop. */ + start_event_loop (); + + in_cli = 0; + + Py_RETURN_NONE; +} + +/* Set up the Python argument vector and evaluate a script. This is + used to implement 'gdb -P'. */ + +void +run_python_script (int argc, char **argv) +{ + FILE *input; + + /* We never free this, since we plan to exit at the end. */ + ensure_python_env (get_current_arch (), current_language); + + running_python_script = 1; + +#if PYTHON_ABI_VERSION < 3 + PySys_SetArgv (argc - 1, argv + 1); +#else + { + wchar_t **wargv = (wchar_t **) alloca (sizeof (*wargv) * (argc + 1)); + int i; + + for (i = 1; i < argc; i++) + { + size_t len = mbstowcs (NULL, argv[i], 0); + /* Python-related GDB sources are built with -DNDEBUG + https://sourceware.org/bugzilla/show_bug.cgi?id=20445 */ + size_t len2 ATTRIBUTE_UNUSED; + + if (len == (size_t) -1) + { + fprintf (stderr, "Invalid multibyte argument #%d \"%s\"\n", + i, argv[i]); + exit (1); + } + wargv[i] = (wchar_t *) alloca (sizeof (**wargv) * (len + 1)); + len2 = mbstowcs (wargv[i], argv[i], len + 1); + assert (len2 == len); + } + wargv[argc] = NULL; + PySys_SetArgv (argc - 1, wargv + 1); + } +#endif + + input = fopen (argv[0], "r"); + if (! input) + { + fprintf (stderr, "could not open %s: %s\n", argv[0], strerror (errno)); + exit (1); + } + PyRun_SimpleFile (input, argv[0]); + fclose (input); + exit (0); +} + + + static PyObject * gdbpy_get_current_progspace (PyObject *unused1, PyObject *unused2) @@ -1981,6 +2069,8 @@ PyMethodDef python_GdbMethods[] = Evaluate command, a string, as a gdb CLI command. Optionally returns\n\ a Python String containing the output of the command if to_string is\n\ set to True." }, + { "cli", gdbpy_cli, METH_NOARGS, + "Enter the gdb CLI" }, { "parameter", gdbpy_parameter, METH_VARARGS, "Return a gdb parameter's value" }, diff --git a/gdb/python/python.h b/gdb/python/python.h index b810187..a3620ab 100644 --- a/gdb/python/python.h +++ b/gdb/python/python.h @@ -25,4 +25,6 @@ /* This is all that python exports to gdb. */ extern const struct extension_language_defn extension_language_python; +extern void run_python_script (int argc, char **argv); + #endif /* GDB_PYTHON_H */ diff --git a/gdb/testsuite/gdb.gdb/selftest.exp b/gdb/testsuite/gdb.gdb/selftest.exp index 2fdd9e3..391900d 100644 --- a/gdb/testsuite/gdb.gdb/selftest.exp +++ b/gdb/testsuite/gdb.gdb/selftest.exp @@ -87,6 +87,10 @@ proc do_steps_and_nexts {} { set description "step over cmdarg_vec initialization" set command "step" } + -re ".*python_script = 0.*$gdb_prompt $" { + set description "step over python_script initialization" + set command "step" + } -re ".*pre_stat_chain = make_command_stats_cleanup.*$gdb_prompt $" { set description "next over make_command_stats_cleanup and everything it calls" set command "next" diff --git a/gdb/testsuite/gdb.python/py-frame.exp b/gdb/testsuite/gdb.python/py-frame.exp index b5a4682..d5451a6 100644 --- a/gdb/testsuite/gdb.python/py-frame.exp +++ b/gdb/testsuite/gdb.python/py-frame.exp @@ -95,6 +95,8 @@ gdb_test "python print ('result = %s' % f0.read_var ('a'))" " = 1" "test Frame.r gdb_test "python print ('result = %s' % (gdb.selected_frame () == f1))" " = True" "test gdb.selected_frame" +gdb_test "python print ('result = %s' % (f0.block ()))" "" "test Frame.block" + # Can read SP register. gdb_test "python print ('result = %s' % (gdb.selected_frame ().read_register ('sp') == gdb.parse_and_eval ('\$sp')))" \ " = True" \ diff --git a/gdb/testsuite/gdb.python/py-value.exp b/gdb/testsuite/gdb.python/py-value.exp index 57a9ba1..d90d85a 100644 --- a/gdb/testsuite/gdb.python/py-value.exp +++ b/gdb/testsuite/gdb.python/py-value.exp @@ -397,6 +397,15 @@ proc test_value_after_death {} { "print value's type" } +# Regression test for a cast failure. The bug was that if we cast a +# value to its own type, gdb could crash. This happened because we +# could end up double-freeing a struct value. +proc test_cast_regression {} { + gdb_test "python v = gdb.Value(5)" "" "create value for cast test" + gdb_test "python v = v.cast(v.type)" "" "cast value for cast test" + gdb_test "python print(v)" "5" "print value for cast test" +} + # Regression test for invalid subscript operations. The bug was that # the type of the value was not being checked before allowing a # subscript operation to proceed. @@ -526,6 +535,7 @@ test_value_in_inferior test_inferior_function_call test_lazy_strings test_value_after_death +test_cast_regression # Test either C or C++ values.