diff mbox

[RFC] Allow JIT unwinder provide symbol information

Message ID CAHQ51u4gYscYLvEOXTKSauyeDGcwroM+C9BsKU5VO5UHQbndhg@mail.gmail.com
State Superseded
Headers show

Commit Message

Alexander Smundak March 11, 2014, 1:46 a.m. UTC
I apologize for the delay.

On Mon, Feb 24, 2014 at 5:19 PM, Doug Evans <dje@google.com> wrote:
> Thus I think(!) the only remaining issues are:
> - jit-reader-load version 1 support.
> - update documentation
> - testcase for new functionality
> - testcase to verify version 1 API still works
> We can't break jit readers that have been compiled with the version 1 API.
I started with writing the testcase for this (attached below). The test checks
that a JIT reader can help GDB unwind the stack that could not be unwound
otherwise. That is, the test application contains a function that deliberately
corrupts the address of the outer frame when called (and then restores it).
The test sets the breakpoint at the location where the backtrace is broken
and verifies that the backtrace is shown correctly only in the presence of
a JIT reader (which happens to know where the correct outer frame was
saved).
This test fails with upstream GDB because JIT reader's unwinder is never
called.
Which IMHO is an indication that JIT reader in its current form is unlikely
to be used. And the absence of any tests that involve loading JIT reader
points in the same direction.

Sasha
diff mbox

Patch

diff --git a/gdb/testsuite/gdb.arch/amd64-jit-reader.c
b/gdb/testsuite/gdb.arch/amd64-jit-reader.c
new file mode 100644
index 0000000..2887abd
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-jit-reader.c
@@ -0,0 +1,117 @@ 
+#include <stdlib.h>
+#include <stdio.h>
+#include "jit-reader.h"
+
+GDB_DECLARE_GPL_COMPATIBLE_READER
+
+extern struct gdb_reader_funcs *gdb_init_reader (void);
+
+static enum gdb_status debug_info_provider (struct gdb_reader_funcs *self,
+                                            struct gdb_symbol_callbacks *cb,
+                                            void *memory, long memory_sz);
+static enum gdb_status frame_unwinder (struct gdb_reader_funcs *self,
+                                       struct gdb_unwind_callbacks *cb);
+static struct gdb_frame_id frame_id_provider(struct gdb_reader_funcs *self,
+                                             struct gdb_unwind_callbacks *cb);
+static void destructor (struct gdb_reader_funcs *self);
+
+#define DATA_COOKIE 0xdeaffead
+static int jit_data = DATA_COOKIE;
+
+static struct gdb_reader_funcs jit_reader_funcs =
+{
+  1,  /* version */
+  (void *)&jit_data,
+  debug_info_provider,
+  frame_unwinder,
+  frame_id_provider,
+  destructor
+};
+
+struct gdb_reader_funcs *
+gdb_init_reader (void)
+{
+  fprintf (stderr, "Test JIT reader loaded.\n");
+  return &jit_reader_funcs;
+}
+
+enum gdb_status
+debug_info_provider (struct gdb_reader_funcs *self,
+                     struct gdb_symbol_callbacks *cb,
+                     void *memory, long memory_sz)
+{
+  if (*(int *)(self->priv_data) != DATA_COOKIE)
+    return GDB_FAIL;
+  fprintf (stderr, "Debug info provider called for %p..%p.\n",
+           memory, (void *)(memory_sz + (char *)memory));
+  return GDB_SUCCESS;
+}
+
+void
+destructor (struct gdb_reader_funcs *self)
+{
+  fprintf (stderr, "Test JIT reader unloaded.\n");
+  return;
+}
+
+static GDB_CORE_ADDR
+_get_frame_register (struct gdb_unwind_callbacks *cb, int reg_no)
+{
+  struct gdb_reg_value *reg = cb->reg_get (cb, reg_no);
+  GDB_CORE_ADDR reg_value = *(GDB_CORE_ADDR *)reg->value;
+  reg->free (reg);
+  return reg_value;
+}
+
+static void
+_set_frame_register (struct gdb_unwind_callbacks *cb, int reg_no,
+                    GDB_CORE_ADDR value)
+{
+  struct gdb_reg_value *reg = cb->reg_get (cb, reg_no);
+  *(GDB_CORE_ADDR *)(reg->value) = value;
+  reg->defined = 1;
+  (cb->reg_set)(cb, reg_no, reg);
+}
+
+#define REG_BP 6
+#define REG_PC 16
+#define REG_SP 7
+enum gdb_status
+frame_unwinder (struct gdb_reader_funcs *self,
+                struct gdb_unwind_callbacks *cb)
+{
+  GDB_CORE_ADDR bp = _get_frame_register (cb, REG_BP);
+  GDB_CORE_ADDR pc = _get_frame_register (cb, REG_PC);
+  GDB_CORE_ADDR sp = _get_frame_register (cb, REG_SP);
+  GDB_CORE_ADDR word;
+  cb->target_read(bp, &word, 8);
+  if (word != bp)
+    return GDB_FAIL;
+
+  /* We are at the frame that xxx-jit-unwind program altered for us.
+     We have:
+      BP-8   +--------------+
+             | Previous BP  |
+      BP     +--------------+
+             | BP (wrong!)  |
+      BP+8   +--------------+
+             | Previous PC  |
+             +--------------+
+    Save outer frame's BP, PC and SP.  */
+  cb->target_read (bp - 8, &word, 8);
+  _set_frame_register (cb, REG_BP, word);
+  cb->target_read (bp + 8, &word, 8);
+  _set_frame_register (cb, REG_PC, word);
+  _set_frame_register (cb, REG_SP, bp + 16);
+  return GDB_SUCCESS;
+}
+
+struct gdb_frame_id frame_id_provider (struct gdb_reader_funcs *self,
+                                       struct gdb_unwind_callbacks *cb)
+{
+  struct gdb_frame_id frame_id =
+      { _get_frame_register (cb, REG_PC),
+        _get_frame_register (cb, REG_SP) };
+  fprintf (stderr, "frame_id_provider called.\n");
+  return frame_id;
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-jit-reader.exp
b/gdb/testsuite/gdb.arch/amd64-jit-reader.exp
new file mode 100644
index 0000000..513e8b0
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-jit-reader.exp
@@ -0,0 +1,60 @@ 
+if {[skip_shlib_tests]} {
+    untested "Shared libraries are not supported"
+    return -1
+}
+
+if {[get_compiler_info]} {
+    return -1
+}
+
+if { ![istarget x86_64-*-* ] || ![is_lp64_target] } {
+    verbose "Skipping amd64 JIT reader tests."
+    return
+}
+
+set testfile "amd64-jit-unwind"
+set srcfile ${testfile}.c
+set binfile [standard_output_file ${testfile}]
+if  { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}"
executable [list debug]] != "" } {
+    untested "could not compile test program"
+    return -1
+}
+
+set jit_reader "amd64-jit-reader"
+set jit_reader_source "${srcdir}/${subdir}/${jit_reader}.c"
+set jit_reader_binfile [standard_output_file ${jit_reader}.so]
+
+if { [gdb_compile_shlib ${jit_reader_source} ${jit_reader_binfile}
{additional_flags="-I.."}] != "" } {
+    untested "Cannot compile amd-jit-reader"
+    return  -1
+}
+
+# Check that GDB is unable to show the backtrace for jit-unwind without
+# JIT reader.
+clean_restart $testfile
+if { ![runto_main] } {
+    fail "Can't run to main"
+    return
+}
+gdb_breakpoint [gdb_get_line_number "break backtrace-broken" ]
+gdb_continue_to_breakpoint "break backtrace-broken"
+gdb_test "where" "Backtrace stopped: frame did not save the .*"
+gdb_test "continue" "Continuing\..*$inferior_exited_re.*"
+
+# Check that GDB shows backtrace with JIT reader present
+# and that JIT reader can be unloaded.
+clean_restart $testfile
+if { ![runto_main] } {
+    fail "Can't run to main"
+    return
+}
+gdb_test "jit-reader-load ${jit_reader_binfile}" "Test JIT reader loaded."
+gdb_breakpoint [gdb_get_line_number "break backtrace-broken" ]
+gdb_continue_to_breakpoint "break backtrace-broken"
+gdb_test_sequence "where"  "Bad backtrace" {
+    "\[\r\n\]+#0 .* break_unwind_chain \\(\\) at "
+    "\[\r\n\]+#1 .* break_unwind_chain_1 \\(\\) at "
+    "\[\r\n\]+#2 .* main \\(.*\\) at"
+}
+gdb_test "continue"  "Continuing\..*$inferior_exited_re.*"
+gdb_test "jit-reader-unload" "Test JIT reader unloaded."
diff --git a/gdb/testsuite/gdb.arch/amd64-jit-unwind.c
b/gdb/testsuite/gdb.arch/amd64-jit-unwind.c
new file mode 100644
index 0000000..c2ae3de
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-jit-unwind.c
@@ -0,0 +1,43 @@ 
+/* This test program is part of GDB, the GNU debugger.
+
+   Copyright 2011-2014 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/>.  */
+
+/* This program is used to verify that a JIT handler can unwind custom
+   stack frames.  */
+
+#include <stdint.h>
+#include <stdio.h>
+static void break_unwind_chain()
+{
+  /* Save outer frame address, then corrupt the frame chain by setting
+     the outer frame address in it to self.  */
+  void *outer_fp = *(void **)__builtin_frame_address(0);
+  *(void  **)__builtin_frame_address(0) = __builtin_frame_address(0);
+  /* Now restore it so that we can return.  The test sets the
+     breakpoint just before this happens, and GDB will not be able to
+     show the backtrace without JIT reader.  */
+  *(void **)__builtin_frame_address(0) = outer_fp; /* break backtrace-broken */
+}
+
+static void break_unwind_chain_1()
+{
+  break_unwind_chain();
+}
+
+int main(int argc, char *argv[])
+{
+  break_unwind_chain_1();
+}