From patchwork Tue Dec 9 02:39:36 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Evans X-Patchwork-Id: 4118 Received: (qmail 22741 invoked by alias); 9 Dec 2014 02:39:42 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 22724 invoked by uid 89); 9 Dec 2014 02:39:41 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.8 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mail-pa0-f74.google.com Received: from mail-pa0-f74.google.com (HELO mail-pa0-f74.google.com) (209.85.220.74) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Tue, 09 Dec 2014 02:39:39 +0000 Received: by mail-pa0-f74.google.com with SMTP id kq14so851408pab.3 for ; Mon, 08 Dec 2014 18:39:38 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:subject:date:message-id:mime-version :content-type; bh=MT1l5gQNtA6qlxf9wbSMQ8c9v9mKRWC7dnxRxrlDyfo=; b=Aeg6tk1KvSHY9bX9gnZfc5JwSy861dKZOVM1ccqs6t8/lgrgtF2p/D3zEtI/eJwNa3 DDD3LFA+biIajVu7xxqUFxAbFTT7mKJrL4S5x6JzF/MHmRJ5UHpwLLAyzlweiTG6Zle8 G4aieBiKpFf6PZOpgSeLTRg/rjBbUPJymrbTHjxfm6HmXsLEsMyiyhu11FZ3ORagKcfZ QR18oB07jmjokn/YX4KJh5JXLmDbhzo+onCfrv6N/0HdF50MOx58Vp5m6pSW4NsAUpNl 4EXDISmyGrSux8Fg6qmJ9+zrelEkTJIbb5ie4RT1yF3fFLt7tD19ar47oPFBfHUIRYwP EtXg== X-Gm-Message-State: ALoCoQnbQSPWsVvLgEqLOImSH5I32j7fd5mpPWRYiyhSAXAPs0gejO1QvW8T17HTv3ELVuu9lmeb X-Received: by 10.67.22.7 with SMTP id ho7mr31552534pad.7.1418092777963; Mon, 08 Dec 2014 18:39:37 -0800 (PST) Received: from corpmail-nozzle1-1.hot.corp.google.com ([100.108.1.104]) by gmr-mx.google.com with ESMTPS id t24si1719844yht.2.2014.12.08.18.39.37 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 08 Dec 2014 18:39:37 -0800 (PST) Received: from ruffy.mtv.corp.google.com ([172.17.128.44]) by corpmail-nozzle1-1.hot.corp.google.com with ESMTPS id a3Pvtvjl.1; Mon, 08 Dec 2014 18:39:37 -0800 From: Doug Evans To: gdb-patches@sourceware.org, eliz@gnu.org Subject: [PATCH] New python function gdb.lookup_objfile. Date: Mon, 08 Dec 2014 18:39:36 -0800 Message-ID: MIME-Version: 1.0 X-IsSubscribed: yes Hi. This patch implements a new python function gdb.lookup_objfile. It provides the ability to look up an objfile given a file name or build id. While one could call gdb.objfiles() and then scan over that, it requires construction of the list, and with *lots* of objfiles it is a bit cumbersome. Regression tested on amd64-linux. 2014-12-08 Doug Evans * NEWS: Mention gdb.lookup_objfile. * python/python.c (GdbMethods): Add lookup_objfile. * python/python-internal.h (gdbpy_lookup_objfile): Declare. * python/py-objfile.c: #include "symtab.h". (objfpy_build_id_ok, objfpy_build_id_matches): New functions. (objfpy_lookup_objfile_by_name): New function. (objfpy_lookup_objfile_by_build_id): New function. (gdbpy_lookup_objfile): New function. doc/ * python.texi (Objfiles In Python): Document gdb.lookup_objfile. testsuite/ * lib/gdb-python.exp (get_python_valueof): New function. * gdb.python/py-objfile.exp: Add tests for gdb.lookup_objfile. diff --git a/gdb/NEWS b/gdb/NEWS index 6a2cb9b..93e06d4 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -28,6 +28,7 @@ ** A new event "gdb.clear_objfiles" has been added, triggered when selecting a new file to debug. ** You can now add attributes to gdb.Objfile and gdb.Progspace objects. + ** New function gdb.lookup_objfile. * New Python-based convenience functions: diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index efd258d..b54d341 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -3488,6 +3488,22 @@ Return a sequence of all the objfiles current known to @value{GDBN}. @xref{Objfiles In Python}. @end defun +@findex gdb.lookup_objfile +@defun gdb.lookup_objfile (name @r{[}, by_build_id{]}) +Look up @var{name}, which is a file name, in the list of objfiles +for the current program space (@pxref{Progspaces In Python}). +If the objfile is not found then a Python @code{ValueError} exception +is thrown. + +If @var{by_build_id} is provided and is @code{True} then @var{name} +is the build ID of the objfile. +This is supported only on some operating systems, notably those which use +the ELF format for binary files and the @sc{gnu} Binutils. For more details +about this feature, see the description of the @option{--build-id} +command-line option in @ref{Options, , Command Line Options, ld.info, +The GNU Linker}. +@end defun + Each objfile is represented by an instance of the @code{gdb.Objfile} class. diff --git a/gdb/python/py-objfile.c b/gdb/python/py-objfile.c index 51cf47c..e78ceba 100644 --- a/gdb/python/py-objfile.c +++ b/gdb/python/py-objfile.c @@ -24,6 +24,7 @@ #include "language.h" #include "build-id.h" #include "elf-bfd.h" +#include "symtab.h" typedef struct { @@ -380,6 +385,147 @@ objfpy_add_separate_debug_file (PyObject *self, PyObject *args, PyObject *kw) Py_RETURN_NONE; } +/* Subroutine of gdbpy_lookup_objfile_by_build_id to simplify it. + Return non-zero if STRING is a potentially valid build id. */ + +static int +objfpy_build_id_ok (const char *string) +{ + size_t i, n = strlen (string); + + if (n % 2 != 0) + return 0; + for (i = 0; i < n; ++i) + { + if (!isxdigit (string[i])) + return 0; + } + return 1; +} + +/* Subroutine of gdbpy_lookup_objfile_by_build_id to simplify it. + Returns non-zero if BUILD_ID matches STRING. + It is assumed that objfpy_build_id_ok (string) returns TRUE. */ + +static int +objfpy_build_id_matches (const struct elf_build_id *build_id, + const char *string) +{ + size_t i; + + if (strlen (string) != 2 * build_id->size) + return 0; + + for (i = 0; i < build_id->size; ++i) + { + char c1 = string[i * 2], c2 = string[i * 2 + 1]; + int byte = (host_hex_value (c1) << 4) | host_hex_value (c2); + + if (byte != build_id->data[i]) + return 0; + } + + return 1; +} + +/* Subroutine of gdbpy_lookup_objfile to simplify it. + Look up an objfile by its file name. */ + +static struct objfile * +objfpy_lookup_objfile_by_name (const char *name) +{ + struct objfile *objfile; + + ALL_OBJFILES (objfile) + { + if ((objfile->flags & OBJF_NOT_FILENAME) != 0) + continue; + /* Don't return separate debug files. */ + if (objfile->separate_debug_objfile_backlink != NULL) + continue; + if (compare_filenames_for_search (objfile_name (objfile), name)) + return objfile; + } + + return NULL; +} + +/* Subroutine of gdbpy_lookup_objfile to simplify it. + Look up an objfile by its build id. */ + +static struct objfile * +objfpy_lookup_objfile_by_build_id (const char *build_id) +{ + struct objfile *objfile; + + ALL_OBJFILES (objfile) + { + const struct elf_build_id *obfd_build_id; + + if (objfile->obfd == NULL) + continue; + /* Don't return separate debug files. */ + if (objfile->separate_debug_objfile_backlink != NULL) + continue; + obfd_build_id = build_id_bfd_get (objfile->obfd); + if (obfd_build_id == NULL) + continue; + if (objfpy_build_id_matches (obfd_build_id, build_id)) + return objfile; + } + + return NULL; +} + +/* Implementation of gdb.lookup_objfile. */ + +PyObject * +gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw) +{ + static char *keywords[] = { "name", "by_build_id", NULL }; + const char *name; + PyObject *by_build_id_obj = NULL; + int by_build_id; + struct objfile *objfile; + + if (! PyArg_ParseTupleAndKeywords (args, kw, "s|O!", keywords, + &name, &PyBool_Type, &by_build_id_obj)) + return NULL; + + by_build_id = 0; + if (by_build_id_obj != NULL) + { + int cmp = PyObject_IsTrue (by_build_id_obj); + + if (cmp < 0) + return NULL; + by_build_id = cmp; + } + + if (by_build_id) + { + if (!objfpy_build_id_ok (name)) + { + PyErr_SetString (PyExc_TypeError, _("Not a valid build id.")); + return NULL; + } + objfile = objfpy_lookup_objfile_by_build_id (name); + } + else + objfile = objfpy_lookup_objfile_by_name (name); + + if (objfile != NULL) + { + PyObject *result = objfile_to_objfile_object (objfile); + + Py_XINCREF (result); + return result; + } + + PyErr_SetString (PyExc_ValueError, _("Objfile not found.")); + return NULL; +} + /* Clear the OBJFILE pointer in an Objfile object and remove the diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h index 716c0de..544fe93 100644 --- a/gdb/python/python-internal.h +++ b/gdb/python/python-internal.h @@ -393,6 +393,7 @@ PyObject *objfile_to_objfile_object (struct objfile *) PyObject *objfpy_get_printers (PyObject *, void *); PyObject *objfpy_get_frame_filters (PyObject *, void *); PyObject *objfpy_get_xmethods (PyObject *, void *); +PyObject *gdbpy_lookup_objfile (PyObject *self, PyObject *args, PyObject *kw); PyObject *gdbarch_to_arch_object (struct gdbarch *gdbarch); diff --git a/gdb/python/python.c b/gdb/python/python.c index 1362bd2..b1d8283 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -1956,6 +1956,14 @@ a boolean indicating if name is a field of the current implied argument\n\ METH_VARARGS | METH_KEYWORDS, "lookup_global_symbol (name [, domain]) -> symbol\n\ Return the symbol corresponding to the given name (or None)." }, + + { "lookup_objfile", (PyCFunction) gdbpy_lookup_objfile, + METH_VARARGS | METH_KEYWORDS, + "lookup_objfile (name, [by_build_id]) -> objfile\n\ +Look up the specified objfile.\n\ +If by_build_id is True, the objfile is looked up by using name\n\ +as its build id." }, + { "block_for_pc", gdbpy_block_for_pc, METH_VARARGS, "Return the block containing the given pc value, or None." }, { "solib_name", gdbpy_solib_name, METH_VARARGS, diff --git a/gdb/testsuite/gdb.python/py-objfile.exp b/gdb/testsuite/gdb.python/py-objfile.exp index f3a8a6c..a56bc95 100644 --- a/gdb/testsuite/gdb.python/py-objfile.exp +++ b/gdb/testsuite/gdb.python/py-objfile.exp @@ -32,23 +32,39 @@ if ![runto_main] then { return 0 } +set python_error_text "Error while executing Python code\\." + gdb_py_test_silent_cmd "python sym = gdb.lookup_symbol(\"some_var\")" \ "Find a symbol in objfile" 1 gdb_py_test_silent_cmd "python objfile = sym\[0\].symtab.objfile" \ "Get backing object file" 1 -gdb_test "python print (objfile.filename)" ".*py-objfile.*" \ +gdb_test "python print (objfile.filename)" ".*${testfile}" \ "Get objfile file name" +gdb_test "python print (gdb.lookup_objfile (\"${testfile}\").filename)" \ + "${testfile}" +gdb_test "python print (gdb.lookup_objfile (\"junk\"))" \ + "Objfile not found\\.\r\n${python_error_text}" + set binfile_build_id [get_build_id $binfile] if [string compare $binfile_build_id ""] { verbose -log "binfile_build_id = $binfile_build_id" gdb_test "python print (objfile.build_id)" "$binfile_build_id" \ "Get objfile build id" + gdb_test "python print (gdb.lookup_objfile (\"$binfile_build_id\", by_build_id=True).filename)" \ + "${testfile}" } else { unsupported "build-id is not supported by the compiler" } +# Other lookup_objfile_by_build_id tests we can do, even if compiler doesn't +# support them. +gdb_test "python print (gdb.lookup_objfile (\"foo\", by_build_id=True))" \ + "Not a valid build id\\.\r\n${python_error_text}" +gdb_test "python print (gdb.lookup_objfile (\"1234abcdef\", by_build_id=True))" \ + "Objfile not found\\.\r\n${python_error_text}" + gdb_test "python print (objfile.progspace)" "" \ "Get objfile program space" gdb_test "python print (objfile.is_valid())" "True" \ @@ -93,3 +109,9 @@ gdb_test "python print (sep_objfile.owner.filename)" "${testfile}2" \ gdb_test "p main" "= {int \\(\\)} $hex
" \ "print main with debug info" + +# Separate debug files are not findable. +if { [get_python_valueof "sep_objfile.build_id" "None"] != "None" } { + gdb_test "python print (gdb.lookup_objfile (sep_objfile.build_id, by_build_id=True))" \ + "Objfile not found\\.\r\n${python_error_text}" +} diff --git a/gdb/testsuite/lib/gdb-python.exp b/gdb/testsuite/lib/gdb-python.exp index d5e7928..eefff73 100644 --- a/gdb/testsuite/lib/gdb-python.exp +++ b/gdb/testsuite/lib/gdb-python.exp @@ -45,3 +45,24 @@ proc gdb_py_test_multiple { name args } { } return 0 } + +# Return the result of python expression EXPR. +# DEFAULT is returned if there's an error. +# This is modelled after get_integer_valueof. + +proc get_python_valueof { exp default } { + global gdb_prompt + + set test "get python valueof \"${exp}\"" + set val ${default} + gdb_test_multiple "python print (\"valueof: %s\" % (${exp}))" "$test" { + -re "valueof: (\[^\r\n\]*)\[\r\n\]*$gdb_prompt $" { + set val $expect_out(1,string) + pass "$test ($val)" + } + timeout { + fail "$test (timeout)" + } + } + return ${val} +}