[patch] Add solib_address and decode_line Python functionality

Phil Muldoon
2010-07-19T15:43:25Z

This patch imports the (last) two utility functions from archer (the
other missing section from archer is the event posting code, but that
is another email ...)

These are general purpose utilities to make script hackers life a
little easier.

solib_address -- lookup an address and if it resides in an solib
reports that libs name, or None.

decode_line -- provide decode_line_1 like functionality. Useful for
Python commands

Tested on x8664 with no regressions.

Comments?

Cheers,

Phil

ChangeLog:

2010-07-19 Phil Muldoon <pmuldoon@redhat.com>
Thiago Jung Bauermann <bauerman@br.ibm.com>
Tom Tromey <tromey@redhat.com>

* python/python.c (gdbpy_solib_address): New function.
(gdbpy_decode_line): Likewise.

Documentation ChangeLog:

2010-07-19 Phil Muldoon <pmuldoon@redhat.com>

* gdb.texinfo (Basic Python): Describe solib_address and
decode_line Python APIs

Testsuite ChangeLog:

2010-07-19 Phil Muldoon <pmuldoon@redhat.com>

* gdb.python/python.c: New File.
* gdb.python/python-sl.c: New File.
* gdb.python/python.exp: Test solib_address and decode_line
* functions.

--

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ba5ab75..b75dc23 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -20532,6 +20532,35 @@ Return the name of the current target wide character set
never returned.
@end defun

+@findex gdb.solib_address
+@defun solib_address address
+Return the name of the shared library holding the given @var{address}
+as a string, or @code{None}.
+@end defun
+
+@findex gdb.decode_line
+@defun decode_line @r{[}expression@r{]}
+Decode the optional argument @var{expression} the way that
+@value{GDBN}'s inbuilt 'break' or 'edit' commands do. Return a Python
+tuple holding all the locations that match the expression represented
+as @code{gdb.Symtab_and_line} objects, or @code{None}. If
+@var{expression} is not provided, the current location is returned.
+This function is useful for decoding line, file and function arguments
+for Python commands (@pxref{Commands In Python}). The expected
+format of @var{expression} is:
+
+@table @code
+@item FILE:LINENUM
+A location indicated at that line in that file.
+@item FUNCTION
+A location at the beginning of that function.
+@item FILE:FUNCTION
+A location to distinguish among like-named static functions.
+@item ADDRESS
+A location containing that address.
+@item VARIABLE
+A location containing that variable.
+@end table
+@end defun
+
@node Exception Handling
@subsubsection Exception Handling
@cindex python exceptions
diff --git a/gdb/python/python.c b/gdb/python/python.c
index 6680126..8a9a374 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -42,7 +42,10 @@ static int gdbpy_should_print_stack = 1;
#include "cli/cli-decode.h"
#include "charset.h"
#include "top.h"
+#include "solib.h"
#include "python-internal.h"
+#include "linespec.h"
+#include "source.h"
#include "version.h"
#include "target.h"
#include "gdbthread.h"
@@ -413,6 +416,105 @@ execute_gdb_command (PyObject *self, PyObject *args, PyObject *kw)
Py_RETURN_NONE;
}

+/* Implementation of gdb.solib_address (Long) -> String.
+ Returns the name of the shared library holding a given address, or None. */
+
+static PyObject *
+gdbpy_solib_address (PyObject *self, PyObject *args)
+{
+ unsigned long long pc;
+ char *soname;
+ PyObject *str_obj;
+
+ if (!PyArg_ParseTuple (args, "K", &pc))
+ return NULL;
+
+ soname = solib_name_from_address (current_program_space, pc);
+ if (soname)
+ str_obj = PyString_Decode (soname, strlen (soname), host_charset (), NULL);
+ else
+ {
+ str_obj = Py_None;
+ Py_INCREF (Py_None);
+ }
+
+ return str_obj;
+}
+
+/* A Python function which is a wrapper for decode_line_1. */
+
+static PyObject *
+gdbpy_decode_line (PyObject *self, PyObject *args)
+{
+ struct symtabs_and_lines sals = { NULL, 0 }; /* Initialize to appease gcc. */
+ struct symtab_and_line sal;
+ char *arg = NULL;
+ int free_sals = 0, i;
+ PyObject *result = NULL;
+ volatile struct gdb_exception except;
+
+ if (! PyArg_ParseTuple (args, "|s", &arg))
+ return NULL;
+
+ TRY_CATCH (except, RETURN_MASK_ALL)
+ {
+ if (arg)
+ {
+ char *copy;
+
+ arg = strdup (arg);
+ copy = arg;
+
+ sals = decode_line_1 (&copy, 0, 0, 0, 0, 0);
+ free_sals = 1;
+ }
+ else
+ {
+ set_default_source_symtab_and_line ();
+ sal = get_current_source_symtab_and_line ();
+ sals.sals = &sal;
+ sals.nelts = 1;
+ }
+ }
+ if (arg)
+ xfree (arg);
+
+ if (except.reason < 0)
+ {
+ if (free_sals)
+ xfree (sals.sals);
+ /* We know this will always throw. */
+ GDB_PY_HANDLE_EXCEPTION (except);
+ }
+
+ if (sals.nelts)
+ {
+ result = PyTuple_New (sals.nelts);
+ for (i = 0; i < sals.nelts; ++i)
+ {
+ PyObject *obj;
+ char *str;
+
+ obj = symtab_and_line_to_sal_object (sals.sals[i]);
+ if (! obj)
+ {
+ Py_DECREF (result);
+ result = NULL;
+ break;
+ }
+
+ PyTuple_SetItem (result, i, obj);
+ }
+ }
+
+ if (free_sals)
+ xfree (sals.sals);
+
+ if (result)
+ return result;
+ Py_RETURN_NONE;
+}
+
/* Parse a string and evaluate it as an expression. */
static PyObject *
gdbpy_parse_and_eval (PyObject *self, PyObject *args)
@@ -864,6 +966,14 @@ a boolean indicating if name is a field of the current implied argument\n\
`this' (when the current language is object-oriented)." },
{ "block_for_pc", gdbpy_block_for_pc, METH_VARARGS,
"Return the block containing the given pc value, or None." },
+ { "solib_address", gdbpy_solib_address, METH_VARARGS,
+ "solib_address (Long) -> String.\n\
+Return the name of the shared library holding a given address, or None." },
+ { "decode_line", gdbpy_decode_line, METH_VARARGS,
+ "decode_line (String) -> Tuple. Decode a string argument the way\n\
+that 'break' or 'edit' does. Return a tuple holding all the\n \
+locations that match, represented as gdb.Symtab_and_line objects\n\
+(or None)."},
{ "parse_and_eval", gdbpy_parse_and_eval, METH_VARARGS,
"parse_and_eval (String) -> Value.\n\
Parse String as an expression, evaluate it, and return the result as a Value."
diff --git a/gdb/testsuite/gdb.python/python-sl.c b/gdb/testsuite/gdb.python/python-sl.c
new file mode 100644
index 0000000..579a74e
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python-sl.c
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2010 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/>. */
+
+void func1 ()
+{
+ return;
+}
+
+int func2 ()
+{
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.python/python.c b/gdb/testsuite/gdb.python/python.c
new file mode 100644
index 0000000..750a90a
--- /dev/null
+++ b/gdb/testsuite/gdb.python/python.c
@@ -0,0 +1,28 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2010 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/>. */
+
+/* Shared library function */
+extern void func1 (void);
+extern int func2 (void);
+
+int
+main (int argc, char *argv[])
+{
+ func1 ();
+ func2 ();
+ return 0; /* Break to end. */
+}
diff --git a/gdb/testsuite/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp
index d0e6c63..0d7b4bf 100644
--- a/gdb/testsuite/gdb.python/python.exp
+++ b/gdb/testsuite/gdb.python/python.exp
@@ -20,12 +20,44 @@ if $tracelevel then {
strace $tracelevel
}

-# Start with a fresh gdb.
+set testfile "python"
+set srcfile ${testfile}.c
+set libfile "python-sl"
+set libsrc ${libfile}.c
+set library ${objdir}/${subdir}/${libfile}.sl
+set binfile ${objdir}/${subdir}/${testfile}
+
+if { [gdb_compile_shlib ${srcdir}/${subdir}/${libsrc} ${library} "debug"] != "" } {
+ untested "Could not compile shared library."
+ return -1
+}
+
+set exec_opts [list debug shlib=${library}]

+if { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile} executable $exec_opts] != "" } {
+ untested "Could not compile $binfile."
+ return -1
+}
+
+# Start with a fresh gdb.
gdb_exit
gdb_start
gdb_reinitialize_dir $srcdir/$subdir

+# Skip all tests if Python scripting is not enabled.
+if { [skip_python_tests] } { continue }
+
+# Run a command in GDB, and report a failure if a Python exception is thrown.
+# If report_pass is true, report a pass if no exception is thrown.
+proc gdb_py_test_silent_cmd {cmd name report_pass} {
+ global gdb_prompt
+
+ gdb_test_multiple $cmd $name {
+ -re "Traceback.*$gdb_prompt $" { fail $name }
+ -re "$gdb_prompt $" { if $report_pass { pass $name } }
+ }
+}
+
gdb_test_multiple "python print 23" "verify python support" {
-re "not supported.*$gdb_prompt $" {
unsupported "python support is disabled"
@@ -87,3 +119,46 @@ gdb_test "python import itertools; print 'IMPOR'+'TED'" "IMPORTED" "pythonX.Y/li
gdb_test_no_output \
"python x = gdb.execute('printf \"%d\", 23', to_string = True)"
gdb_test "python print x" "23"
+
+# Start with a fresh gdb.
+clean_restart ${testfile}
+
+# The following tests require execution.
+
+if ![runto_main] then {
+ fail "Can't run to main"
+ return 0
+}
+
+runto [gdb_get_line_number "Break to end."]
+
+# Test gdb.decode_line.
+gdb_test "python gdb.decode_line(\"main.c:43\")" \
+ "RuntimeError: No source file named main.c.*" "test decode_line no source named main"
+
+
+gdb_py_test_silent_cmd "python symtab = gdb.decode_line()" "test decode_line current location" 1
+gdb_test "python print len(symtab)" "1*" "Test decode_line current location"
+gdb_test "python print symtab\[0\].symtab" "gdb/testsuite/gdb.python/python.c.*" "Test decode_line current locationn filename"
+gdb_test "python print symtab\[0\].line" "22" "Test decode_line current location line number"
+
+gdb_py_test_silent_cmd "python symtab = gdb.decode_line(\"python.c:26\")" "test decode_line python.c:26" 1
+gdb_test "python print len(symtab)" "1*" "Test decode_line python.c:26 length"
+gdb_test "python print symtab\[0\].symtab" "gdb/testsuite/gdb.python/python.c.*" "Test decode_line python.c:26 filename"
+gdb_test "python print symtab\[0\].line" "26" "Test decode_line python.c:26 line number"
+
+gdb_test "python gdb.decode_line(\"randomfunc\")" \
+ "RuntimeError: Function \"randomfunc\" not defined.*" "test decode_line randomfunc"
+gdb_py_test_silent_cmd "python symtab = gdb.decode_line(\"func1\")" "test decode_line func1()" 1
+gdb_test "python print len(symtab)" "1*" "Test decode_line func1 length"
+gdb_test "python print symtab\[0\].symtab" "gdb/testsuite/gdb.python/python-sl.c.*" "Test decode_line func1 filename"
+gdb_test "python print symtab\[0\].line" "19" "Test decode_line func1 line number"
+
+# Test gdb.solib_address
+gdb_test "p &func1" "" "func1 address"
+gdb_py_test_silent_cmd "python func1 = gdb.history(0)" "Aquire func1 address" 1
+gdb_test "python print gdb.solib_address(long(func1))" "gdb/testsuite/gdb.python/python-sl.sl" "test func1 solib location"
+
+gdb_test "p &main" "" "main address"
+gdb_py_test_silent_cmd "python main = gdb.history(0)" "Aquire main address" 1
+gdb_test "python print gdb.solib_address(long(main))" "None" "test main solib location"