public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Python API: Add gdb.is_in_prologue and gdb.is_in_epilogue.
@ 2014-10-22 14:02 Martin Galvan
  2014-10-22 15:10 ` Eli Zaretskii
                   ` (2 more replies)
  0 siblings, 3 replies; 30+ messages in thread
From: Martin Galvan @ 2014-10-22 14:02 UTC (permalink / raw)
  To: gdb-patches; +Cc: Martin Galvan

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  <martin.galvan@tallertechnologies.com>

    * 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  <martin.galvan@tallertechnologies.com>

    * python.texi (Basic Python): Document new functions gdb.is_in_prologue and
    gdb.is_in_epilogue.

gdb/testsuite/ChangeLog

2014-10-22  Martin Galvan  <martin.galvan@tallertechnologies.com>

    * 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)"

-- 
1.9.1

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2014-11-07 14:45 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-10-22 14:02 [PATCH] Python API: Add gdb.is_in_prologue and gdb.is_in_epilogue Martin Galvan
2014-10-22 15:10 ` Eli Zaretskii
2014-10-22 15:14   ` Martin Galvan
2014-10-22 17:33   ` Martin Galvan
2014-10-22 17:47     ` Eli Zaretskii
2014-10-22 18:06       ` Martin Galvan
2014-10-22 18:07       ` Eli Zaretskii
2014-10-22 18:32         ` Martin Galvan
2014-10-22 18:37           ` Eli Zaretskii
2014-10-22 19:23 ` Doug Evans
2014-10-22 21:34 ` Pedro Alves
2014-10-22 21:59   ` Pedro Alves
2014-10-23 17:36     ` Martin Galvan
2014-10-23 17:57       ` Ulrich Weigand
2014-10-23 18:09         ` Martin Galvan
2014-10-23 18:14           ` Daniel Gutson
2014-10-24  2:42             ` Doug Evans
2014-10-24 14:58         ` Pedro Alves
2014-10-24  4:57       ` Doug Evans
2014-10-24 15:02         ` Pedro Alves
2014-10-24 15:34           ` Ulrich Weigand
2014-10-24 15:47             ` Doug Evans
2014-10-24 14:57       ` Pedro Alves
2014-10-24 15:13         ` Ulrich Weigand
2014-11-07 14:45           ` [push] Revert old nexti prologue check and eliminate in_prologue Pedro Alves
2014-10-24 19:49         ` [PATCH] Python API: Add gdb.is_in_prologue and gdb.is_in_epilogue Martin Galvan
2014-10-24 20:09           ` Pedro Alves
2014-10-24 21:11             ` Martin Galvan
2014-10-24 22:34               ` Pedro Alves
2014-10-27 16:40                 ` Martin Galvan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).