From patchwork Wed Oct 22 14:01:25 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Galvan X-Patchwork-Id: 3324 Received: (qmail 8389 invoked by alias); 22 Oct 2014 14:02:05 -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 8366 invoked by uid 89); 22 Oct 2014 14:02:03 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW, SPF_SOFTFAIL autolearn=no version=3.3.2 X-HELO: mail-qa0-f41.google.com Received: from mail-qa0-f41.google.com (HELO mail-qa0-f41.google.com) (209.85.216.41) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Wed, 22 Oct 2014 14:02:02 +0000 Received: by mail-qa0-f41.google.com with SMTP id n8so2405126qaq.0 for ; Wed, 22 Oct 2014 07:01:57 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=ELZ9rQ57x0tRAEOtWoieBlOs9UWlhPW2LTisyB25L6s=; b=CfCQ8v6uZAt0RvW6U5G3E5rjnsDTgpboskeH/FoeRKLcZLKne4UsubyKUkAxY0CJId 5utp/wOVN2kJuafMBT/JPaYQuHHKKxYSfsSzFVowx2homeI/udULUWye9TJVr9eQ6b9q n3RIXLwBojQTL3SkTB5OwLo9ymritOLpVwc5XW+7wRrJSz47Wynfcr5j67HxYpwXGN5f o9lbqcoT/9Mv1TAZ7tefqHE1mbWU086dg0IRpA8I1PR/g3w9tu7gyxRtQwTrF5NCmwfP U2nz4DlK9NoySC4+BbWK65nf4pQR8ezjU5G+pO8JlbTOEwkb3pgOOMQoYEYmlbdk121F +WfA== X-Gm-Message-State: ALoCoQmlMKdlqQeYtpylCggGs+yk21QPCbzv/tGtE/a7yXrdz8GBq1CzHiB6IFLLj1W9k4MN5OH+ X-Received: by 10.140.86.135 with SMTP id p7mr54375988qgd.54.1413986517095; Wed, 22 Oct 2014 07:01:57 -0700 (PDT) Received: from martin-galvan.dominio.tallertechnologies.com ([200.69.202.173]) by mx.google.com with ESMTPSA id i97sm8893098qge.49.2014.10.22.07.01.54 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Wed, 22 Oct 2014 07:01:55 -0700 (PDT) From: Martin Galvan To: gdb-patches@sourceware.org Cc: Martin Galvan Subject: [PATCH] Python API: Add gdb.is_in_prologue and gdb.is_in_epilogue. Date: Wed, 22 Oct 2014 11:01:25 -0300 Message-Id: <1413986485-4673-1-git-send-email-martin.galvan@tallertechnologies.com> Added two new functions to the Python API: gdb.is_in_prologue and gdb.is_in_epilogue. These expose the in_prologue and gdbarch_in_function_epilogue_p functions respectively, which are useful for checking if the values of local variables are valid when single-stepping through machine instructions. Also added tests for gdb.is_in_prologue only. The reason of this is that the tests work by checking the first instruction of a given function, as that's conventionally part of the prologue. Testing gdb.is_in_epilogue seems to be architecture-dependant (for instance, the last instruction of a function is reported as part of an epilogue by gdbarch_in_function_epilogue_p for ARM, but not always for x86_64), so I didn't include a test for it. gdb/ChangeLog 2014-10-22 Martin Galvan * NEWS: Mention new Python functions. * python/python.c: (gdbpy_is_in_prologue) (gdbpy_is_in_epilogue): New functions. gdb/doc/ChangeLog 2014-10-22 Martin Galvan * python.texi (Basic Python): Document new functions gdb.is_in_prologue and gdb.is_in_epilogue. gdb/testsuite/ChangeLog 2014-10-22 Martin Galvan * gdb.python/python.exp: Add tests for gdb.is_in_prologue. --- gdb/NEWS | 3 ++ gdb/doc/python.texi | 45 +++++++++++++++++++++++++++ gdb/python/python.c | 61 +++++++++++++++++++++++++++++++++++++ gdb/testsuite/gdb.python/python.exp | 44 ++++++++++++++++++++++++++ 4 files changed, 153 insertions(+) diff --git a/gdb/NEWS b/gdb/NEWS index 606fd16..31959bb 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -13,6 +13,9 @@ which is the gdb.Progspace object of the containing program space. ** A new event "gdb.clear_objfiles" has been added, triggered when selecting a new file to debug. + ** Two new functions: gdb.is_in_prologue and gdb.is_in_epilogue, + which are wrappers for in_prologue and gdbarch_in_function_epilogue_p + respectively. * New Python-based convenience functions: diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index f1fd841..d87913a 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -440,6 +440,51 @@ such as those used by readline for command input, and annotation related prompts are prohibited from being changed. @end defun +@findex gdb.is_in_prologue +@defun gdb.is_in_prologue (pc, @r{[}functionStart@r{]}) +Returns @code{True} if the given @var{pc} value *might* correspond to +an instruction that executes before the stack frame of its containing +function is completely set up, @code{False} if it definitely doesn't. + +When stepping by machine instructions it's possible that local variables +appear to have wrong values at the beginning of a function. This +happens because it usually takes more than one instruction to set up +a stack frame (including local variable definitions); such instructions +are part of a function's prologue. @value{GDBN} can identify the +addresses where the local variables may show wrong values and inform +you so. @value{GDBN} will usually take a "conservative" approach when +analyzing the prologue, assuming the result will be @code{True} unless +it's completely sure it won't. As such, sometimes a given @var{pc} may +be reported as being before the stack frame is set up when it actually +isn't; however if the result is @code{False} you can be sure +@value{GDBN} is right. + +The optional @var{functionStart} argument is the start address of the +function you want to check if @var{pc} belongs to. If your binary +doesn't have debugging info, @value{GDBN} may need to use this value +to guess if @var{pc} belongs to the prologue. If omitted it defaults +to 0. + +In general you shouldn't worry about passing a @var{functionStart} +argument unless your binary doesn't have debugging info, in which case +ommiting @var{functionStart} may result in @code{True} being returned +when the @var{pc} is not actually inside a prologue. +@end defun + +@findex gdb.is_in_epilogue +@defun gdb.is_in_epilogue (pc) +Returns @code{True} if the given @var{pc} value corresponds to an +instruction that executes after the stack of its containing function +has been destroyed, @code{False} if it doesn't. + +When stepping by machine instructions it's possible that local variables +appear to have wrong values at the end of a function. This happens +because it usually takes more than one instruction to tear down a stack +frame; such instructions are part of a function's epilogue. @value{GDBN} +can identify the addresses where the local variables may show wrong +values and inform you so. +@end defun + @node Exception Handling @subsubsection Exception Handling @cindex python exceptions diff --git a/gdb/python/python.c b/gdb/python/python.c index ca531e2..b1b4422 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -703,6 +703,55 @@ gdbpy_solib_name (PyObject *self, PyObject *args) return str_obj; } +/* A Python wrapper for in_prologue. */ + +static PyObject * +gdbpy_is_in_prologue (PyObject *self, PyObject *args, PyObject *kw) +{ + /* If the user doesn't provide a function start address, assume 0 and hope + we have at least minimal symbols. If we don't, we might be returning + a false positive */ + gdb_py_longest pc; + gdb_py_longest function_start = 0; + const PyObject *result; + char *keywords[] = {"pc", "functionStart", NULL}; + + if (PyArg_ParseTupleAndKeywords (args, kw, + GDB_PY_LLU_ARG "|" GDB_PY_LLU_ARG, + keywords, &pc, &function_start)) + { + result = in_prologue (python_gdbarch, pc, function_start) ? Py_True : Py_False; + Py_INCREF (result); + } + else /* Couldn't parse the given args. */ + { + result = NULL; + } + + return result; +} + +/* A Python wrapper for gdbarch_in_function_epilogue_p. */ + +static PyObject * +gdbpy_is_in_epilogue (PyObject *self, PyObject *args) +{ + gdb_py_longest pc; + const PyObject* result; + + if (PyArg_ParseTuple (args, GDB_PY_LLU_ARG, &pc)) + { + result = gdbarch_in_function_epilogue_p (python_gdbarch, pc) ? Py_True : Py_False; + Py_INCREF (result); + } + else /* Couldn't parse the given args. */ + { + result = NULL; + } + + return result; +} + /* A Python function which is a wrapper for decode_line_1. */ static PyObject * @@ -2000,6 +2049,18 @@ Return the selected inferior object." }, { "inferiors", gdbpy_inferiors, METH_NOARGS, "inferiors () -> (gdb.Inferior, ...).\n\ Return a tuple containing all inferiors." }, + + { "is_in_prologue", gdbpy_is_in_prologue, METH_VARARGS | METH_KEYWORDS, + "is_in_prologue (pc, functionStart) -> Boolean.\n\ +Returns True if the given pc value *might* correspond to an instruction\n\ +that executes before the stack of its containing function is completely set up,\n\ +False if it definitely doesn't."}, + { "is_in_epilogue", gdbpy_is_in_epilogue, METH_VARARGS | METH_KEYWORDS, + "is_in_epilogue (pc) -> Boolean.\n\ +Returns True if the given pc value corresponds to an instruction\n\ +that executes after the stack of its containing function has been destroyed,\n\ +False if it doesn't."}, + {NULL, NULL, 0, NULL} }; diff --git a/gdb/testsuite/gdb.python/python.exp b/gdb/testsuite/gdb.python/python.exp index 3df9347..1611e0b 100644 --- a/gdb/testsuite/gdb.python/python.exp +++ b/gdb/testsuite/gdb.python/python.exp @@ -417,3 +417,47 @@ gdb_py_test_silent_cmd "step" "Step into func2" 1 gdb_py_test_silent_cmd "up" "Step out of func2" 1 gdb_test "python print (gdb.find_pc_line(gdb.selected_frame().pc()).line > line)" "True" "Test find_pc_line with resume address" + +# gdb.is_in_prologue and gdb.is_in_epilogue: + +# Start with a fresh gdb. +clean_restart ${testfile} + +if ![runto_main] then { + fail "Can't run to main" + return 0 +} + +# Go somewhere in the function body. +runto [gdb_get_line_number "Break at func2 call site."] + +# Get the current frame object. +gdb_py_test_silent_cmd "python selectedFrame = gdb.selected_frame()" \ + "Get the current frame object." 0 + +# Get an address inside the function body. +gdb_py_test_silent_cmd "python functionBodyAddress = selectedFrame.pc()" \ + "Get an address inside the function body." 0 + +# Get the start address of the function. +gdb_py_test_silent_cmd "python functionStartAddress = long(selectedFrame.function().value().address)" \ + "Get the start address of the function." 0 + +# Test the function's start address and an address somewhere inside the function body. + +# With functionStartAddress: +gdb_test "python print(gdb.is_in_prologue(functionStartAddress, functionStartAddress))" "True" \ + "Function start is in the prologue" + +gdb_test "python print(gdb.is_in_prologue(functionBodyAddress, functionStartAddress))" "False" \ + "The function body isn't in the prologue" + +# Without functionStartAddress: +gdb_test "python print(gdb.is_in_prologue(functionStartAddress))" "True" \ + "Function start is in the prologue" + +gdb_test "python print(gdb.is_in_prologue(functionBodyAddress))" "False" \ + "The function body isn't in the prologue (requires debug info to pass)" + +gdb_test "python print(gdb.is_in_epilogue(functionBodyAddress))" "False" \ + "The function body isn't in the epilogue (requires debug info to pass)"