[RFA] Re: [RFC][patch 1/9] initial Python support

Thiago Jung Bauermann
2008-07-15T07:38:01Z

Hi,

This is the latest version of the patch adding initial Python support.
AFAIK it fixes all concernsï»¿ that were raised regarding it, and has
documentation and a testcase. The only caveat is...

On Sun, 2008-06-15 at 18:18 -0400, thiagoju@linux.ibm.com wrote:
> Zitat von Daniel Jacobowitz <drow@false.org>:
> > > +static PyObject *
> > > +execute_gdb_command (PyObject *self, PyObject *args)
> > > +{
> > > + struct cmd_list_element *alias, *prefix, *cmd;
> > > + char *arg, *newarg;
> > > + volatile struct gdb_exception except;
> > > +
> > > + if (! PyArg_ParseTuple (args, "s", &arg))
> > > + return NULL;
> > > +
> > > + TRY_CATCH (except, RETURN_MASK_ALL)
> > > + {
> > > + execute_command (arg, 0);
> > > + }
> > > + GDB_PY_HANDLE_EXCEPTION (except);
> > > +
> > > + /* gdbtk does bpstat_do_actions here... */
> >
> > Please figure out if we need to or not. It looks like we do; running
> > the command "continue" to a breakpoint with actions will test it.
>
> I don't know much about bpstat_do_actions, and didn't do the experiment
> you mention (not sure I understood it), so I made a crude try at adding
> a call to bpstat_do_actions just to get this patch out.

... the fix I implemented for this. I still would like someone to check
it for sanity.
--
[]'s
Thiago Jung Bauermann
Software Engineer
IBM Linux Technology Center

gdb/
2008-07-15 Vladimir Prus <vladimir@codesourcery.com>
Tom Tromey <tromey@redhat.com>
Thiago Jung Bauermann <bauerman@br.ibm.com>
Doug Evans <dje@google.com>

* Makefile.in (SUBDIR_PYTHON_OBS, SUBDIR_PYTHON_SRCS,
SUBDIR_PYTHON_DEPS, SUBDIR_PYTHON_LDFLAGS, SUBDIR_PYTHON_CFLAGS,
PYTHON_CFLAGS): New.
(python_h, python_internal_h): New.
(COMMON_OBS): Add python.o.
(INIT_FILES): Add python/python.c.
(cli-script.o): Depend on python.h
(python.o, python-utils.o): New.
* cli/cli-script.c (print_command_lines): Handle python_control.
(execute_control_command): Handle python_control.
(execute_control_command_suppressed): New function.
(while_command): Call execute_control_command_suppressed.
(if_command): Likewise.
(get_command_line): Remove static attribute.
(read_next_line): Handle "python".
(recurse_read_control_structure): Handle python_control.
(read_command_lines): Handle python_control.
Include python.h.
* cli/cli-script.h (get_command_line): Add prototype.
(execute_control_command_suppressed): Likewise.
* configure.ac: Add --with-python.
* defs.h (enum command_control_type) <python_control>: New
constant.
* python/python-internal.h: New file.
* python/python.c: New file.
* python/python.h: New file.
* python/utils.c: New file.

gdb/doc/
2008-07-15 Tom Tromey <tromey@redhat.com>

* gdb.texinfo (Python): New chapter.

gdb/testsuite/
2008-07-15 Tom Tromey <tromey@redhat.com>

* gdb.python/python.exp: New file.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9304bc2..9007bc8 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -254,6 +254,18 @@ SUBDIR_TUI_LDFLAGS=
SUBDIR_TUI_CFLAGS= \
-DTUI=1

+#
+# python sub directory definitons
+#
+SUBDIR_PYTHON_OBS = \
+ python-utils.o
+SUBDIR_PYTHON_SRCS = \
+ python/python.c \
+ python/utils.c
+SUBDIR_PYTHON_DEPS =
+SUBDIR_PYTHON_LDFLAGS=
+SUBDIR_PYTHON_CFLAGS=
+

# Opcodes currently live in one of two places. Either they are in the
# opcode library, typically ../opcodes, or they are in a header file
@@ -963,6 +975,13 @@ tui_wingeneral_h = $(srcdir)/tui/tui-wingeneral.h
tui_win_h = $(srcdir)/tui/tui-win.h $(tui_data_h)
tui_winsource_h = $(srcdir)/tui/tui-winsource.h $(tui_data_h)

+#
+# gdb/python/ headers
+#
+
+python_h = $(srcdir)/python/python.h $(value_h)
+python_internal_h = $(srcdir)/python/python-internal.h
+
# gdb/features preparsed descriptions
features_headers = $(defs_h) $(gdbtypes_h) $(target_descriptions_h)
arm_with_iwmmxt_c = $(srcdir)/features/arm-with-iwmmxt.c $(features_headers)
@@ -1081,7 +1100,8 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \
tramp-frame.o \
solib.o solib-null.o \
prologue-value.o memory-map.o xml-support.o \
- target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o
+ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \
+ python.o

TSOBS = inflow.o

@@ -1275,7 +1295,11 @@ test-cp-name-parser$(EXEEXT): test-cp-name-parser.o $(LIBIBERTY)
# duplicates. Files in the gdb/ directory can end up appearing in
# COMMON_OBS (as a .o file) and CONFIG_SRCS (as a .c file).

-INIT_FILES = $(COMMON_OBS) $(TSOBS) $(CONFIG_SRCS)
+# NOTE: bauermann/2008-06-15: python.c needs to be explicitly included
+# because the generation of init.c does not work for .c files which
+# result in differently named objects (i.e., python/python -> python.o).
+
+INIT_FILES = $(COMMON_OBS) $(TSOBS) $(CONFIG_SRCS) python/python.c
init.c: $(INIT_FILES)
@echo Making init.c
@rm -f init.c-tmp init.l-tmp
@@ -3051,7 +3075,8 @@ cli-logging.o: $(srcdir)/cli/cli-logging.c $(defs_h) $(gdbcmd_h) $(ui_out_h) \
$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/cli/cli-logging.c
cli-script.o: $(srcdir)/cli/cli-script.c $(defs_h) $(value_h) $(language_h) \
$(ui_out_h) $(gdb_string_h) $(exceptions_h) $(top_h) $(cli_cmds_h) \
- $(cli_decode_h) $(cli_script_h) $(gdb_assert_h) $(breakpoint_h)
+ $(cli_decode_h) $(cli_script_h) $(gdb_assert_h) $(breakpoint_h) \
+ $(python_h)
$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/cli/cli-script.c
cli-setshow.o: $(srcdir)/cli/cli-setshow.c $(defs_h) $(readline_tilde_h) \
$(value_h) $(gdb_string_h) $(ui_out_h) $(cli_decode_h) $(cli_cmds_h) \
@@ -3375,4 +3400,22 @@ tui-winsource.o: $(srcdir)/tui/tui-winsource.c $(defs_h) $(symtab_h) \
$(gdb_curses_h) $(gdb_assert_h)
$(CC) -c $(INTERNAL_CFLAGS) $(srcdir)/tui/tui-winsource.c

+#
+# gdb/python/ dependencies
+#
+# Need to explicitly specify the compile rule as make will do nothing
+# or try to compile the object file into the sub-directory.
+
+# Flags needed to compile Python code (taken from python-config --cflags)
+PYTHON_CFLAGS=-fno-strict-aliasing -DNDEBUG -fwrapv
+
+python.o: $(srcdir)/python/python.c $(defs_h) $(python_h) \
+ $(command_h) $(libiberty_h) $(cli_decode_h) $(charset_h) $(top_h) \
+ $(exceptions_h) $(python_internal_h) $(version_h) $(cli_script_h) \
+ $(ui_out_h)
+ $(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) $(srcdir)/python/python.c
+python-utils.o: $(srcdir)/python/utils.c $(defs_h) $(python_internal_h)
+ $(CC) -c $(INTERNAL_CFLAGS) $(PYTHON_CFLAGS) \
+ $(srcdir)/python/utils.c -o python-utils.o
+
### end of the gdb Makefile.in.
diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c
index e6fbe3f..69fd3c7 100644
--- a/gdb/cli/cli-script.c
+++ b/gdb/cli/cli-script.c
@@ -34,6 +34,8 @@
#include "cli/cli-script.h"
#include "gdb_assert.h"

+#include "python/python.h"
+
/* Prototypes for local functions */

static enum command_control_type
@@ -102,7 +104,7 @@ build_command_line (enum command_control_type type, char *args)
/* Build and return a new command structure for the control commands
such as "if" and "while". */

-static struct command_line *
+struct command_line *
get_command_line (enum command_control_type type, char *arg)
{
struct command_line *cmd;
@@ -225,6 +227,18 @@ print_command_lines (struct ui_out *uiout, struct command_line *cmd,
continue;
}

+ if (list->control_type == python_control)
+ {
+ ui_out_field_string (uiout, NULL, "python");
+ print_command_lines (uiout, *list->body_list, depth + 1);
+ if (depth)
+ ui_out_spaces (uiout, 2 * depth);
+ ui_out_field_string (uiout, NULL, "end");
+ ui_out_text (uiout, "\n");
+ list = list->next;
+ continue;
+ }
+
/* ignore illegal command type and try next */
list = list->next;
} /* while (list) */
@@ -527,6 +541,12 @@ execute_control_command (struct command_line *cmd)
ret = commands_from_control_command (new_line, cmd);
break;
}
+ case python_control:
+ {
+ eval_python_from_control_command (cmd);
+ ret = simple_control;
+ break;
+ }

default:
warning (_("Invalid control type in canned commands structure."));
@@ -538,6 +558,17 @@ execute_control_command (struct command_line *cmd)
return ret;
}

+/* Like execute_control_command, but first set
+ suppress_next_print_command_trace. */
+
+enum command_control_type
+execute_control_command_suppressed (struct command_line *cmd)
+{
+ suppress_next_print_command_trace = 1;
+ return execute_control_command (cmd);
+}
+
+
/* "while" command support. Executes a body of statements while the
loop condition is nonzero. */

@@ -552,8 +583,7 @@ while_command (char *arg, int from_tty)
if (command == NULL)
return;

- suppress_next_print_command_trace = 1;
- execute_control_command (command);
+ execute_control_command_suppressed (command);
free_command_lines (&command);
}

@@ -571,8 +601,7 @@ if_command (char *arg, int from_tty)
if (command == NULL)
return;

- suppress_next_print_command_trace = 1;
- execute_control_command (command);
+ execute_control_command_suppressed (command);
free_command_lines (&command);
}

@@ -886,6 +915,12 @@ read_next_line (struct command_line **command)
first_arg++;
*command = build_command_line (commands_control, first_arg);
}
+ else if (p1 - p == 6 && !strncmp (p, "python", 6))
+ {
+ /* Note that we ignore the inline "python command" form
+ here. */
+ *command = build_command_line (python_control, "");
+ }
else if (p1 - p == 10 && !strncmp (p, "loop_break", 10))
{
*command = (struct command_line *)
@@ -962,6 +997,7 @@ recurse_read_control_structure (struct command_line *current_cmd)
{
if (current_cmd->control_type == while_control
|| current_cmd->control_type == if_control
+ || current_cmd->control_type == python_control
|| current_cmd->control_type == commands_control)
{
/* Success reading an entire canned sequence of commands. */
@@ -1013,6 +1049,7 @@ recurse_read_control_structure (struct command_line *current_cmd)
on it. */
if (next->control_type == while_control
|| next->control_type == if_control
+ || next->control_type == python_control
|| next->control_type == commands_control)
{
control_level++;
@@ -1086,6 +1123,7 @@ read_command_lines (char *prompt_arg, int from_tty)

if (next->control_type == while_control
|| next->control_type == if_control
+ || next->control_type == python_control
|| next->control_type == commands_control)
{
control_level++;
diff --git a/gdb/cli/cli-script.h b/gdb/cli/cli-script.h
index 42663dd..7416e1c 100644
--- a/gdb/cli/cli-script.h
+++ b/gdb/cli/cli-script.h
@@ -40,6 +40,12 @@ extern void show_user_1 (struct cmd_list_element *c, struct ui_file *stream);
extern enum command_control_type
execute_control_command (struct command_line *cmd);

+extern enum command_control_type
+ execute_control_command_suppressed (struct command_line *cmd);
+
+extern struct command_line *get_command_line (enum command_control_type,
+ char *);
+
extern void print_command_lines (struct ui_out *,
struct command_line *, unsigned int);

diff --git a/gdb/configure.ac b/gdb/configure.ac
index 101dedf..0adc9fd 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -485,6 +485,99 @@ else
fi
fi

+dnl Utility to simplify finding libpython.
+AC_DEFUN([AC_TRY_LIBPYTHON],
+[
+ version=$1
+ define([have_libpython_var],$2)
+ define([VERSION],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-],
+ [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])])
+ [HAVE_LIB]VERSION=no
+ AC_MSG_CHECKING([for ${version}])
+ save_LIBS=$LIBS
+ LIBS="$LIBS -l${version}"
+ AC_LINK_IFELSE(AC_LANG_PROGRAM([[#include "${version}/Python.h"]],
+ [[Py_Initialize ();]]),
+ [[HAVE_LIB]VERSION=yes
+ have_libpython_var=yes],
+ [LIBS=$save_LIBS])
+ AC_MSG_RESULT([$[HAVE_LIB]VERSION])
+])
+
+AC_ARG_WITH(python,
+ AS_HELP_STRING([--with-python], [include python support (auto/yes/no/<path>)]),
+ [], [with_python=auto])
+AC_MSG_CHECKING([whether to use python])
+AC_MSG_RESULT([$with_python])
+
+if test "${with_python}" = no; then
+ AC_MSG_WARN([python support disabled; some features may be unavailable.])
+ have_libpython=no
+else
+ case "${with_python}" in
+ yes | auto)
+ # Leave as empty, use defaults.
+ python_includes=
+ python_libs=
+ ;;
+ /*)
+ python_includes="-I${with_python}/include"
+ python_libs="-L${with_python}/lib"
+ ;;
+ *)
+ AC_ERROR(invalid value for --with-python)
+ ;;
+ esac
+
+ save_CPPFLAGS=$CPPFLAGS
+ CPPFLAGS="$CPPFLAGS ${python_includes}"
+ save_LIBS=$LIBS
+ LIBS="$LIBS ${python_libs}"
+ have_libpython=no
+ if test "${have_libpython}" = no; then
+ AC_TRY_LIBPYTHON(python2.6, have_libpython)
+ if test "${HAVE_LIBPYTHON2_6}" = yes; then
+ AC_DEFINE(HAVE_LIBPYTHON2_6, 1, [Define if Python 2.6 is being used.])
+ fi
+ fi
+ if test ${have_libpython} = no; then
+ AC_TRY_LIBPYTHON(python2.5, have_libpython)
+ if test "${HAVE_LIBPYTHON2_5}" = yes; then
+ AC_DEFINE(HAVE_LIBPYTHON2_5, 1, [Define if Python 2.5 is being used.])
+ fi
+ fi
+ if test ${have_libpython} = no; then
+ AC_TRY_LIBPYTHON(python2.4, have_libpython)
+ if test "${HAVE_LIBPYTHON2_4}" = yes; then
+ AC_DEFINE(HAVE_LIBPYTHON2_4, 1, [Define if Python 2.4 is being used.])
+ fi
+ fi
+ if test ${have_libpython} = no; then
+ case "${with_python}" in
+ yes)
+ AC_MSG_ERROR([python is missing or unusable])
+ ;;
+ auto)
+ AC_MSG_WARN([python is missing or unusable; some features may be unavailable.])
+ ;;
+ *)
+ AC_MSG_ERROR([no usable python found at ${with_python}])
+ ;;
+ esac
+ CPPFLAGS=$save_CPPFLAGS
+ LIBS=$save_LIBS
+ fi
+fi
+
+if test "${have_libpython}" = yes; then
+ AC_DEFINE(HAVE_PYTHON, 1, [Define if Python interpreter is being linked in.])
+ CONFIG_OBS="$CONFIG_OBS \$(SUBDIR_PYTHON_OBS)"
+ CONFIG_DEPS="$CONFIG_DEPS \$(SUBDIR_PYTHON_DEPS)"
+ CONFIG_SRCS="$CONFIG_SRCS \$(SUBDIR_PYTHON_SRCS)"
+ CONFIG_INITS="$CONFIG_INITS \$(SUBDIR_PYTHON_INITS)"
+ ENABLE_CFLAGS="$ENABLE_CFLAGS \$(SUBDIR_PYTHON_CFLAGS)"
+fi
+
# ------------------------- #
# Checks for header files. #
# ------------------------- #
diff --git a/gdb/defs.h b/gdb/defs.h
index 2af40ab..98d3f7c 100644
--- a/gdb/defs.h
+++ b/gdb/defs.h
@@ -642,6 +642,7 @@ enum command_control_type
while_control,
if_control,
commands_control,
+ python_control,
invalid_control
};

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 46d5904..ef7985a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -1,6 +1,6 @@
\input texinfo @c -*-texinfo-*-
@c Copyright (C) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
-@c 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+@c 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008
@c Free Software Foundation, Inc.
@c
@c %**start of header
@@ -155,6 +155,7 @@ software in general. We will miss him.
* Emacs:: Using @value{GDBN} under @sc{gnu} Emacs
* GDB/MI:: @value{GDBN}'s Machine Interface.
* Annotations:: @value{GDBN}'s annotation interface.
+* Python:: Scripting @value{GDBN} using Python.

* GDB Bugs:: Reporting bugs in @value{GDBN}

@@ -22406,6 +22407,41 @@ source which is being displayed. @var{addr} is in the form @samp{0x}
followed by one or more lowercase hex digits (note that this does not
depend on the language).

+@node Python
+@chapter Scripting @value{GDBN} using Python
+@cindex Python
+
+You can script @value{GDBN} using the @url{http://www.python.org/,
+Python} programming language. This feature is available only if
+@value{GDBN} was configured using @code{--with-python}.
+
+@kindex python
+The @code{python} command can be used to evaluate Python code.
+
+If given an argument, the @code{python} command will evaluate the
+argument as a Python command. For example:
+
+@smallexample
+(@value{GDBP}) python print 23
+23
+@end smallexample
+
+If you do not provide an argument to @code{python}, it will act as a
+multi-line command, like @code{define}. In this case, the Python
+script is made up of subsequent command lines, given after the
+@code{python} command. This command list is terminated using a line
+containing @code{end}. For example:
+
+@smallexample
+(@value{GDBP}) python
+Type python script
+End with a line saying just "end".
+>print 23
+>end
+23
+@end smallexample
+
+
@node GDB Bugs
@chapter Reporting Bugs in @value{GDBN}
@cindex bugs in @value{GDBN}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
new file mode 100644
index 0000000..da2e7e1
--- /dev/null
+++ b/gdb/python/python-internal.h
@@ -0,0 +1,73 @@
+/* Gdb/Python header for private use by Python module.
+
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_PYTHON_INTERNAL_H
+#define GDB_PYTHON_INTERNAL_H
+
+/* Python 2.4 doesn't include stdint.h soon enough to get {u,}intptr_t
+ needed by pyport.h. */
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+/* /usr/include/features.h on linux systems will define _POSIX_C_SOURCE
+ if it sees _GNU_SOURCE (which config.h will define).
+ pyconfig.h defines _POSIX_C_SOURCE to a different value than
+ /usr/include/features.h does causing compilation to fail.
+ To work around this, undef _POSIX_C_SOURCE before we include Python.h. */
+#undef _POSIX_C_SOURCE
+
+#if HAVE_LIBPYTHON2_4
+#include "python2.4/Python.h"
+/* Py_ssize_t is not defined until 2.5. */
+typedef Py_intptr_t Py_ssize_t;
+#elif HAVE_LIBPYTHON2_5
+#include "python2.5/Python.h"
+#elif HAVE_LIBPYTHON2_6
+#include "python2.6/Python.h"
+#else
+#error "Unable to find usable Python.h"
+#endif
+
+struct block;
+struct symbol;
+struct symtab_and_line;
+
+extern PyObject *gdb_module;
+
+struct cleanup *make_cleanup_py_decref (PyObject *py);
+
+/* Use this after a TRY_EXCEPT to throw the appropriate Python
+ exception. */
+#define GDB_PY_HANDLE_EXCEPTION(Exception) \
+ do { \
+ if (Exception.reason < 0) \
+ return PyErr_Format (Exception.reason == RETURN_QUIT \
+ ? PyExc_KeyboardInterrupt : PyExc_RuntimeError, \
+ "%s", Exception.message); \
+ } while (0)
+
+
+void gdbpy_print_stack (void);
+
+PyObject *python_string_to_unicode (PyObject *obj);
+char *unicode_to_target_string (PyObject *unicode_str);
+char *python_string_to_target_string (PyObject *obj);
+
+#endif /* GDB_PYTHON_INTERNAL_H */
diff --git a/gdb/python/python.c b/gdb/python/python.c
new file mode 100644
index 0000000..a492bd3
--- /dev/null
+++ b/gdb/python/python.c
@@ -0,0 +1,431 @@
+/* General python/gdb code
+
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "command.h"
+#include "ui-out.h"
+#include "cli/cli-script.h"
+
+#include <ctype.h>
+
+#ifdef HAVE_PYTHON
+
+#include "python.h"
+#include "libiberty.h"
+#include "cli/cli-decode.h"
+#include "charset.h"
+#include "top.h"
+#include "exceptions.h"
+#include "python-internal.h"
+#include "version.h"
+#include "gdbcmd.h"
+
+
+PyObject *gdb_module;
+
+static PyObject *get_show_variable (PyObject *, PyObject *);
+static PyObject *execute_gdb_command (PyObject *, PyObject *);
+static PyObject *gdbpy_write (PyObject *, PyObject *);
+static PyObject *gdbpy_flush (PyObject *, PyObject *);
+
+static PyMethodDef GdbMethods[] =
+{
+ { "execute", execute_gdb_command, METH_VARARGS,
+ "Execute a gdb command" },
+ { "show", get_show_variable, METH_VARARGS,
+ "Return a gdb setting's value" },
+
+ { "write", gdbpy_write, METH_VARARGS,
+ "Write a string using gdb's filtered stream." },
+ { "flush", gdbpy_flush, METH_NOARGS,
+ "Flush gdb's filtered stdout stream." },
+
+ {NULL, NULL, 0, NULL}
+};
+
+/* Given a command_line, return a command string suitable for passing
+ to Python. Lines in the string are separated by newlines. The
+ return value is allocated using xmalloc and the caller is
+ responsible for freeing it. */
+
+static char *
+compute_python_string (struct command_line *l)
+{
+ struct command_line *iter;
+ char *script = NULL;
+ int size = 0;
+ int here;
+
+ for (iter = l; iter; iter = iter->next)
+ size += strlen (iter->line) + 1;
+
+ script = xmalloc (size + 1);
+ here = 0;
+ for (iter = l; iter; iter = iter->next)
+ {
+ int len = strlen (iter->line);
+ strcpy (&script[here], iter->line);
+ here += len;
+ script[here++] = '\n';
+ }
+ script[here] = '\0';
+ return script;
+}
+
+void
+eval_python_from_control_command (struct command_line *cmd)
+{
+ char *script;
+
+ if (cmd->body_count != 1)
+ error (_("Invalid \"python\" block structure."));
+
+ script = compute_python_string (cmd->body_list[0]);
+ PyRun_SimpleString (script);
+ xfree (script);
+ if (PyErr_Occurred ())
+ {
+ gdbpy_print_stack ();
+ error (_("error while executing Python code"));
+ }
+}
+
+/* Implementation of the gdb "python" command. */
+
+static void
+python_command (char *arg, int from_tty)
+{
+ while (arg && *arg && isspace (*arg))
+ ++arg;
+ if (arg && *arg)
+ {
+ PyRun_SimpleString (arg);
+ if (PyErr_Occurred ())
+ {
+ gdbpy_print_stack ();
+ error (_("error while executing Python code"));
+ }
+ }
+ else
+ {
+ char *tmpbuf = xstrprintf ("Type python script");
+ struct cleanup *cleanups = make_cleanup (xfree, tmpbuf);
+ struct command_line *l = get_command_line (python_control, "");
+ cleanups = make_cleanup_free_command_lines (&l);
+ execute_control_command_suppressed (l);
+ do_cleanups (cleanups);
+ }
+}
+
+\f
+
+/* Transform a gdb variable's value into a Python value. May return
+ NULL (and set a Python exception) on error. Helper function for
+ get_show_variable. */
+
+static PyObject *
+variable_to_python (struct cmd_list_element *cmd)
+{
+ switch (cmd->var_type)
+ {
+ case var_string:
+ case var_string_noescape:
+ case var_optional_filename:
+ case var_filename:
+ case var_enum:
+ {
+ char *str = * (char **) cmd->var;
+ if (! str)
+ str = "";
+ return PyString_Decode (str, strlen (str), host_charset (), NULL);
+ }
+
+ case var_boolean:
+ {
+ if (* (int *) cmd->var)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+ }
+
+ case var_auto_boolean:
+ {
+ enum auto_boolean ab = * (enum auto_boolean *) cmd->var;
+ if (ab == AUTO_BOOLEAN_TRUE)
+ Py_RETURN_TRUE;
+ else if (ab == AUTO_BOOLEAN_FALSE)
+ Py_RETURN_FALSE;
+ else
+ Py_RETURN_NONE;
+ }
+
+ case var_integer:
+ if ((* (int *) cmd->var) == INT_MAX)
+ Py_RETURN_NONE;
+ /* Fall through. */
+ case var_zinteger:
+ return PyLong_FromLong (* (int *) cmd->var);
+
+ case var_uinteger:
+ {
+ unsigned int val = * (unsigned int *) cmd->var;
+ if (val == UINT_MAX)
+ Py_RETURN_NONE;
+ return PyLong_FromUnsignedLong (val);
+ }
+ }
+
+ return PyErr_Format (PyExc_RuntimeError, "programmer error: unhandled type");
+}
+
+/* A Python function which returns a gdb variable's value as a Python
+ value. */
+
+static PyObject *
+get_show_variable (PyObject *self, PyObject *args)
+{
+ struct cmd_list_element *alias, *prefix, *cmd;
+ char *arg, *newarg;
+ volatile struct gdb_exception except;
+
+ if (! PyArg_ParseTuple (args, "s", &arg))
+ return NULL;
+
+ newarg = concat ("show ", arg, (char *) NULL);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (! lookup_cmd_composition (newarg, &alias, &prefix, &cmd))
+ {
+ xfree (newarg);
+ return PyErr_Format (PyExc_RuntimeError,
+ "could not find variable `%s'", arg);
+ }
+ }
+ xfree (newarg);
+ GDB_PY_HANDLE_EXCEPTION (except);
+
+ if (! cmd->var)
+ return PyErr_Format (PyExc_RuntimeError, "`%s' is not a variable", arg);
+ return variable_to_python (cmd);
+}
+
+/* A Python function which evaluates a string using the gdb CLI. */
+
+static PyObject *
+execute_gdb_command (PyObject *self, PyObject *args)
+{
+ struct cmd_list_element *alias, *prefix, *cmd;
+ char *arg, *newarg;
+ volatile struct gdb_exception except;
+ struct cleanup *old_chain;
+
+ if (! PyArg_ParseTuple (args, "s", &arg))
+ return NULL;
+
+ old_chain = make_cleanup (null_cleanup, 0);
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ execute_command (arg, 0);
+ }
+ GDB_PY_HANDLE_EXCEPTION (except);
+
+ /* Do any commands attached to breakpoint we stopped at. Only if we
+ are always running synchronously. Or if we have just executed a
+ command that doesn't start the target. */
+ if (!target_can_async_p () || !target_executing)
+ {
+ bpstat_do_actions (&stop_bpstat);
+ do_cleanups (old_chain);
+ }
+
+ Py_RETURN_NONE;
+}
+
+/* This is a cleanup function which decrements the refcount on a
+ Python object. */
+
+static void
+py_decref (void *p)
+{
+ PyObject *py = p;
+ /* Note that we need the extra braces in this 'if' to avoid a
+ warning from gcc. */
+ if (py)
+ {
+ Py_DECREF (py);
+ }
+}
+
+/* Return a new cleanup which will decrement the Python object's
+ refcount when run. */
+
+struct cleanup *
+make_cleanup_py_decref (PyObject *py)
+{
+ return make_cleanup (py_decref, (void *) py);
+}
+
+\f
+
+/* Printing. */
+
+/* A python function to write a single string using gdb's filtered
+ output stream. */
+static PyObject *
+gdbpy_write (PyObject *self, PyObject *args)
+{
+ char *arg;
+ if (! PyArg_ParseTuple (args, "s", &arg))
+ return NULL;
+ printf_filtered ("%s", arg);
+ Py_RETURN_NONE;
+}
+
+/* A python function to flush gdb's filtered output stream. */
+static PyObject *
+gdbpy_flush (PyObject *self, PyObject *args)
+{
+ gdb_flush (gdb_stdout);
+ Py_RETURN_NONE;
+}
+
+/* True if we should print the stack when catching a Python error,
+ false otherwise. */
+static int gdbpy_should_print_stack = 1;
+
+/* Print a python exception trace, or print nothing and clear the
+ python exception, depending on gdbpy_should_print_stack. Only call
+ this if a python exception is set. */
+void
+gdbpy_print_stack (void)
+{
+ if (gdbpy_should_print_stack)
+ PyErr_Print ();
+ else
+ PyErr_Clear ();
+}
+
+\f
+
+/* Initialize the Python code. */
+
+void
+_initialize_python (void)
+{
+ add_com ("python", class_obscure, python_command, _("\
+Evaluate a Python command.\n\
+\n\
+The command can be given as an argument, for instance:\n\
+\n\
+ python print 23\n\
+\n\
+If no argument is given, the following lines are read and used\n\
+as the Python commands. Type a line containing \"end\" to indicate\n\
+the end of the command."));
+
+ add_setshow_boolean_cmd ("python-stack", class_maintenance,
+ &gdbpy_should_print_stack, _("\
+Set Python stack printing."), _("\
+Show Python stack printing."), _("\
+Enables printing of Python stack traces."),
+ NULL, NULL,
+ &maintenance_set_cmdlist,
+ &maintenance_show_cmdlist);
+
+ Py_Initialize ();
+
+ gdb_module = Py_InitModule ("gdb", GdbMethods);
+
+ /* The casts to (char*) are for python 2.4. */
+ PyModule_AddStringConstant (gdb_module, "VERSION", (char*) version);
+ PyModule_AddStringConstant (gdb_module, "HOST_CONFIG", (char*) host_name);
+ PyModule_AddStringConstant (gdb_module, "TARGET_CONFIG", (char*) target_name);
+
+ PyRun_SimpleString ("import gdb");
+
+ /* Create a couple objects which are used for Python's stdout and
+ stderr. */
+ PyRun_SimpleString ("\
+import sys\n\
+class GdbOutputFile:\n\
+ def close(self):\n\
+ # Do nothing.\n\
+ return None\n\
+\n\
+ def isatty(self):\n\
+ return False\n\
+\n\
+ def write(self, s):\n\
+ gdb.write(s)\n\
+\n\
+ def writelines(self, iterable):\n\
+ for line in iterable:\n\
+ self.write(line)\n\
+\n\
+ def flush(self):\n\
+ gdb.flush()\n\
+\n\
+sys.stderr = GdbOutputFile()\n\
+sys.stdout = GdbOutputFile()\n\
+");
+}
+
+#else /* HAVE_PYTHON */
+
+/* Dummy implementation of the gdb "python" command. */
+
+static void
+python_command (char *arg, int from_tty)
+{
+ while (arg && *arg && isspace (*arg))
+ ++arg;
+ if (arg && *arg)
+ error (_("Python scripting is not supported in this copy of GDB."));
+ else
+ {
+ char *tmpbuf = xstrprintf ("Type python script");
+ struct cleanup *cleanups = make_cleanup (xfree, tmpbuf);
+ struct command_line *l = get_command_line (python_control, "");
+ cleanups = make_cleanup_free_command_lines (&l);
+ execute_control_command_suppressed (l);
+ do_cleanups (cleanups);
+ }
+}
+
+void
+eval_python_from_control_command (struct command_line *cmd)
+{
+ error (_("Python scripting is not supported in this copy of GDB."));
+}
+
+/* Initialize dummy Python code. */
+
+void
+_initialize_python (void)
+{
+ add_com ("python", class_obscure, python_command, _("\
+Evaluate a Python command.\n\
+\n\
+Python scripting is not supported in this copy of GDB.\n\
+This command is only a placeholder."));
+}
+
+#endif /* HAVE_PYTHON */
diff --git a/gdb/python/python.h b/gdb/python/python.h
new file mode 100644
index 0000000..00ff159
--- /dev/null
+++ b/gdb/python/python.h
@@ -0,0 +1,27 @@
+/* Python/gdb header for generic use in gdb
+
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_PYTHON_H
+#define GDB_PYTHON_H
+
+#include "value.h"
+
+void eval_python_from_control_command (struct command_line *);
+
+#endif /* GDB_PYTHON_H */
diff --git a/gdb/python/utils.c b/gdb/python/utils.c
new file mode 100644
index 0000000..9883cc5
--- /dev/null
+++ b/gdb/python/utils.c
@@ -0,0 +1,117 @@
+/* General utility routines for GDB/Python.
+
+ Copyright (C) 2008 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "charset.h"
+#include "python-internal.h"
+
+
+/* This is a cleanup function which decrements the refcount on a
+ Python object. */
+
+static void
+py_decref (void *p)
+{
+ PyObject *py = p;
+ /* Note that we need the extra braces in this 'if' to avoid a
+ warning from gcc. */
+ if (py)
+ {
+ Py_DECREF (py);
+ }
+}
+
+/* Return a new cleanup which will decrement the Python object's
+ refcount when run. */
+
+struct cleanup *
+make_cleanup_py_decref (PyObject *py)
+{
+ return make_cleanup (py_decref, (void *) py);
+}
+
+/* Converts a Python 8-bit string to a unicode string object. Assumes the
+ 8-bit string is in the host charset. If an error occurs during conversion,
+ returns NULL with a python exception set.
+
+ As an added bonus, the functions accepts a unicode string and returns it
+ right away, so callers don't need to check which kind of string they've
+ got.
+
+ If the given object is not one of the mentioned string types, NULL is
+ returned, with the TypeError python exception set. */
+PyObject *
+python_string_to_unicode (PyObject *obj)
+{
+ PyObject *unicode_str;
+
+ /* If obj is already a unicode string, just return it.
+ I wish life was always that simple... */
+ if (PyUnicode_Check (obj))
+ unicode_str = obj;
+ else if (PyString_Check (obj))
+ unicode_str = PyUnicode_FromEncodedObject (obj, host_charset (), NULL);
+ else
+ {
+ PyErr_SetString (PyExc_TypeError,
+ _("Expected a string or unicode object."));
+ unicode_str = NULL;
+ }
+
+ return unicode_str;
+}
+
+/* Returns a newly allocated string with the contents of the given unicode
+ string object converted to the target's charset. If an error occurs during
+ the conversion, NULL will be returned and a python exception will be set.
+
+ The caller is responsible for xfree'ing the string. */
+char *
+unicode_to_target_string (PyObject *unicode_str)
+{
+ char *target_string;
+ PyObject *string;
+
+ /* Translate string to target's charset. */
+ string = PyUnicode_AsEncodedString (unicode_str, target_charset (), NULL);
+ if (string == NULL)
+ return NULL;
+
+ target_string = xstrdup (PyString_AsString (string));
+
+ Py_DECREF (string);
+
+ return target_string;
+}
+
+/* Converts a python string (8-bit or unicode) to a target string in
+ the target's charset. Returns NULL on error, with a python exception set.
+
+ The caller is responsible for xfree'ing the string. */
+char *
+python_string_to_target_string (PyObject *obj)
+{
+ PyObject *str;
+
+ str = python_string_to_unicode (obj);
+ if (str == NULL)
+ return NULL;
+
+ return unicode_to_target_string (str);
+}
diff --git a/gdb/testsuite/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp
new file mode 100644
index 0000000..adc79f3
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python.exp
@@ -0,0 +1,40 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite. It tests the mechanism
+# exposing values to Python.
+
+if $tracelevel then {
+ strace $tracelevel
+}
+
+# Start with a fresh gdb.
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+gdb_test_multiple "python print 23" "verify python support" {
+ -re "not supported.*$gdb_prompt $" {
+ unsupported "python support is disabled"
+ return -1
+ }
+ -re "$gdb_prompt $" {}
+}
+
+gdb_test_multiple "python\nprint 23\nend\n" "multi-line python command" {
+ -re "23.*$gdb_prompt $" { pass "multi-line python command" }
+ -re "$gdb_prompt $" { fail "multi-line python command" }
+}