From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 950 invoked by alias); 10 Jun 2010 20:27:22 -0000 Received: (qmail 934 invoked by uid 22791); 10 Jun 2010 20:27:20 -0000 X-SWARE-Spam-Status: No, hits=-5.8 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Thu, 10 Jun 2010 20:27:12 +0000 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o5AKRAMG000374 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 10 Jun 2010 16:27:10 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o5AKR9mK022433; Thu, 10 Jun 2010 16:27:10 -0400 Received: from opsy.redhat.com (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id o5AKR9Ye002999; Thu, 10 Jun 2010 16:27:09 -0400 Received: by opsy.redhat.com (Postfix, from userid 500) id B2C163782DC; Thu, 10 Jun 2010 14:27:03 -0600 (MDT) From: Tom Tromey To: gdb-patches@sourceware.org Subject: RFA: add to_string argument to gdb.execute Reply-To: Tom Tromey Date: Thu, 10 Jun 2010 20:27:00 -0000 Message-ID: MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii 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 X-SW-Source: 2010-06/txt/msg00251.txt.bz2 I plan to check this in. It needs a doc review. This adds a to_string argument to gdb.execute, so you can capture command output in a python string. I've gone back and forth on how I wanted to implement this idea, but in the end I settled on this approach as very simple and useful. This patch also adds keyword arguments to gdb.execute. I think our rule should be that any function with 2 or more arguments should take keyword arguments; we violate this in a couple of places. There is still room for a bigger change, involving structured output from gdb. My plan there is to expose MI commands to Python, and make a new kind of ui_out that creates Python objects. I think this approach has several nice points: we know it will be fairly complete (because MI is), and we already have documentation. Built and regtested on x86-64 (compile farm). Tom 2010-06-10 Tom Tromey PR python/10808: * python/python.c (execute_gdb_command): Add keywords. Accept "to_string" argument. (struct restore_ui_file_closure): New. (restore_ui_file): New function. (make_cleanup_restore_ui_file): Likewise. (GdbMethods) : Update. 2010-06-10 Tom Tromey PR python/10808: * gdb.texinfo (Basic Python): Document new gdb.execute argument. 2010-06-10 Tom Tromey PR python/10808: * gdb.python/python.exp: Add new tests. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index fa7a0ec..e7af24a 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -20045,15 +20045,20 @@ methods and classes added by @value{GDBN} are placed in this module. use in all scripts evaluated by the @code{python} command. @findex gdb.execute -@defun execute command [from_tty] +@defun execute command [from_tty] [to_string] Evaluate @var{command}, a string, as a @value{GDBN} CLI command. If a GDB exception happens while @var{command} runs, it is translated as described in @ref{Exception Handling,,Exception Handling}. -If no exceptions occur, this function returns @code{None}. @var{from_tty} specifies whether @value{GDBN} ought to consider this command as having originated from the user invoking it interactively. It must be a boolean value. If omitted, it defaults to @code{False}. + +By default, any output produced by @var{command} is sent to +@value{GDBN}'s standard output. If the @var{to_string} parameter is +@code{True}, then output will be collected by @code{gdb.execute} and +returned as a string. The default is @code{False}, in which case the +return value is @code{None}. @end defun @findex gdb.breakpoints diff --git a/gdb/python/python.c b/gdb/python/python.c index 31880c1..c4d4a55 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -309,36 +309,94 @@ gdbpy_target_wide_charset (PyObject *self, PyObject *args) return PyUnicode_Decode (cset, strlen (cset), host_charset (), NULL); } +struct restore_ui_file_closure +{ + struct ui_file **variable; + struct ui_file *value; +}; + +static void +restore_ui_file (void *p) +{ + struct restore_ui_file_closure *closure = p; + + *(closure->variable) = closure->value; +} + +/* Remember the current value of *VARIABLE and make it restored when + the cleanup is run. */ +struct cleanup * +make_cleanup_restore_ui_file (struct ui_file **variable) +{ + struct restore_ui_file_closure *c = XNEW (struct restore_ui_file_closure); + + c->variable = variable; + c->value = *variable; + + return make_cleanup_dtor (restore_ui_file, (void *) c, xfree); +} + /* A Python function which evaluates a string using the gdb CLI. */ static PyObject * -execute_gdb_command (PyObject *self, PyObject *args) +execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw) { char *arg; - PyObject *from_tty_obj = NULL; - int from_tty; - int cmp; + PyObject *from_tty_obj = NULL, *to_string_obj = NULL; + int from_tty, to_string; volatile struct gdb_exception except; + static char *keywords[] = {"command", "from_tty", "to_string", NULL }; + char *result = NULL; - if (! PyArg_ParseTuple (args, "s|O!", &arg, &PyBool_Type, &from_tty_obj)) + if (! PyArg_ParseTupleAndKeywords (args, kw, "s|O!O!", keywords, &arg, + &PyBool_Type, &from_tty_obj, + &PyBool_Type, &to_string_obj)) return NULL; from_tty = 0; if (from_tty_obj) { - cmp = PyObject_IsTrue (from_tty_obj); + int cmp = PyObject_IsTrue (from_tty_obj); if (cmp < 0) - return NULL; + return NULL; from_tty = cmp; } + to_string = 0; + if (to_string_obj) + { + int cmp = PyObject_IsTrue (to_string_obj); + if (cmp < 0) + return NULL; + to_string = cmp; + } + TRY_CATCH (except, RETURN_MASK_ALL) { /* Copy the argument text in case the command modifies it. */ char *copy = xstrdup (arg); struct cleanup *cleanup = make_cleanup (xfree, copy); + struct ui_file *str_file = NULL; + + if (to_string) + { + str_file = mem_fileopen (); + + make_cleanup_restore_ui_file (&gdb_stdout); + make_cleanup_restore_ui_file (&gdb_stderr); + make_cleanup_ui_file_delete (str_file); + + gdb_stdout = str_file; + gdb_stderr = str_file; + } execute_command (copy, from_tty); + + if (str_file) + result = ui_file_xstrdup (str_file, NULL); + else + result = NULL; + do_cleanups (cleanup); } GDB_PY_HANDLE_EXCEPTION (except); @@ -346,6 +404,12 @@ execute_gdb_command (PyObject *self, PyObject *args) /* Do any commands attached to breakpoint we stopped at. */ bpstat_do_actions (); + if (result) + { + PyObject *r = PyString_FromString (result); + xfree (result); + return r; + } Py_RETURN_NONE; } @@ -737,7 +801,7 @@ static PyMethodDef GdbMethods[] = { { "history", gdbpy_history, METH_VARARGS, "Get a value from history" }, - { "execute", execute_gdb_command, METH_VARARGS, + { "execute", (PyCFunction) execute_gdb_command, METH_VARARGS | METH_KEYWORDS, "Execute a gdb command" }, { "parameter", gdbpy_parameter, METH_VARARGS, "Return a gdb parameter's value" }, diff --git a/gdb/testsuite/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp index b345ad2..f7f11cc 100644 --- a/gdb/testsuite/gdb.python/python.exp +++ b/gdb/testsuite/gdb.python/python.exp @@ -80,3 +80,7 @@ gdb_test "source $srcdir/$subdir/source2.py" "yes" gdb_test "python print gdb.current_objfile()" "None" gdb_test "python print gdb.objfiles()" "\\\[\\\]" + +gdb_test_no_output \ + "python x = gdb.execute('printf \"%d\", 23', to_string = True)" +gdb_test "python print x" "23"