[PATCH 3/4] Add an unwinder for JIT compiled code.

Sanjoy Das
2011-07-05T06:27:38Z

Add a (pseudo) unwinder which proxies back everything to the unwinder registered by the JIT debug info reader.

gdb/ChangeLog:

* jit.c: Add JIT frame unwinding code.
* jit-reader.h: Added jit_prepend_unwinder.
---
gdb/ChangeLog | 5 ++
gdb/jit.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/jit.h | 2 +
3 files changed, 158 insertions(+), 0 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 38b83f1..306a0a5 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,10 @@
2011-07-04 Sanjoy Das <sanjoy@playingwithpointers.com>

+ * jit.c: Add JIT frame unwinding code.
+ * jit-reader.h: Added jit_prepend_unwinder.
+
+2011-07-04 Sanjoy Das <sanjoy@playingwithpointers.com>
+
* jit.c: Add symbol handling callbacks for the JIT reader interface.

2011-07-04 Sanjoy Das <sanjoy@playingwithpointers.com>
diff --git a/gdb/jit.c b/gdb/jit.c
index f8810a8..5513dcd 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -25,6 +25,7 @@
#include "breakpoint.h"
#include "command.h"
#include "dictionary.h"
+#include "frame-unwind.h"
#include "gdbcmd.h"
#include "gdbcore.h"
#include "inferior.h"
@@ -775,6 +776,150 @@ jit_breakpoint_re_set_internal (struct gdbarch *gdbarch,
return 0;
}

+struct jit_unwind_private {
+ struct gdbjit_reg_value *registers;
+ struct frame_info *this_frame;
+};
+
+static void
+jit_unwind_reg_set_impl (struct gdbjit_unwind_callbacks *cb, int regnum,
+ struct gdbjit_reg_value value)
+{
+ struct jit_unwind_private *priv = cb->private;
+ int gdb_reg = gdbarch_dwarf2_reg_to_regnum (target_gdbarch, regnum);
+ priv->registers[gdb_reg] = value;
+}
+
+static struct gdbjit_reg_value
+jit_unwind_reg_get_impl (struct gdbjit_unwind_callbacks *cb, int regnum)
+{
+ struct jit_unwind_private *priv = cb->private;
+ struct gdbjit_reg_value val;
+ int gdb_reg = gdbarch_dwarf2_reg_to_regnum (target_gdbarch, regnum);
+ val.defined = frame_register_read (priv->this_frame, gdb_reg, val.value);
+ return val;
+}
+
+static int
+jit_frame_sniffer (const struct frame_unwind *self,
+ struct frame_info *this_frame, void **cache)
+{
+ /* First look up the PC, and check if the code address has been registered or
+ not. */
+ CORE_ADDR pc = get_frame_pc (this_frame);
+ struct jit_inferior_data *inf_data = get_jit_inferior_data ();
+ struct jit_unwind_private *priv_data;
+ struct jit_dbg_reader *iter;
+ struct gdbjit_unwind_callbacks callbacks =
+ {
+ jit_unwind_reg_get_impl,
+ jit_unwind_reg_set_impl,
+ jit_target_read_impl
+ };
+
+ if (inf_data->reader == NULL)
+ return 0;
+
+ /* All the unwinding happens here, and the unwound registers are written to
+ this block of memory, which we then sneakily read back in
+ jit_frame_prev_register. */
+ if (!*cache)
+ {
+ *cache = XZALLOC (struct jit_unwind_private);
+ priv_data = *cache;
+ priv_data->registers = XCALLOC (gdbarch_num_regs (target_gdbarch),
+ struct gdbjit_reg_value);
+ priv_data->this_frame = this_frame;
+ }
+ else
+ {
+ priv_data = *cache;
+ priv_data->this_frame = this_frame;
+ }
+
+ callbacks.private = priv_data;
+
+ /* Try to coax the provided unwinder to unwind the stack, and hope it
+ succeeds. */
+ if (inf_data->reader->unwind (inf_data->reader->private_data, &callbacks)
+ == GDB_JIT_SUCCESS)
+ return 1;
+ xfree (priv_data->registers);
+ xfree (priv_data);
+ *cache = NULL;
+
+ return 0;
+}
+
+static enum unwind_stop_reason
+jit_frame_unwind_stop_reason (struct frame_info *this_frame, void **cache)
+{
+ return UNWIND_NO_REASON;
+}
+
+static void
+jit_frame_this_id (struct frame_info *this_frame, void **cache,
+ struct frame_id *this_id)
+{
+ struct jit_inferior_data *inf_data = get_jit_inferior_data ();
+ struct jit_unwind_private private =
+ {
+ NULL,
+ this_frame
+ };
+ struct gdbjit_frame_id frame_id;
+ struct gdbjit_unwind_callbacks callbacks =
+ {
+ jit_unwind_reg_get_impl,
+ NULL,
+ jit_target_read_impl,
+
+ &private
+ };
+
+ frame_id = inf_data->reader->get_frame_id (inf_data->reader->private_data,
+ &callbacks);
+ *this_id = frame_id_build ((CORE_ADDR) frame_id.stack_address,
+ (CORE_ADDR) frame_id.code_address);
+}
+
+static struct value *
+jit_frame_prev_register (struct frame_info *this_frame, void **cache, int reg)
+{
+ struct jit_unwind_private *priv = *cache;
+ struct gdbjit_reg_value value;
+
+ if (priv == NULL)
+ return frame_unwind_got_optimized (this_frame, reg);
+
+ value = priv->registers[reg];
+ if (value.defined)
+ return frame_unwind_got_bytes (this_frame, reg, value.value);
+ else
+ return frame_unwind_got_optimized (this_frame, reg);
+}
+
+static void
+jit_dealloc_cache (struct frame_info *this_frame, void *cache)
+{
+ (void) this_frame;
+
+ xfree (cache);
+}
+
+/* Simply relays everything back to the unwinder registered by the jit debug
+ info reader.*/
+static const struct frame_unwind jit_frame_unwind =
+{
+ NORMAL_FRAME,
+ jit_frame_unwind_stop_reason,
+ jit_frame_this_id,
+ jit_frame_prev_register,
+ NULL,
+ jit_frame_sniffer,
+ jit_dealloc_cache
+};
+
/* Register any already created translations. */

static void
@@ -954,6 +1099,12 @@ jit_event_handler (struct gdbarch *gdbarch)
}
}

+void
+jit_prepend_unwinder (struct gdbarch *gdbarch)
+{
+ frame_unwind_prepend_unwinder (gdbarch, &jit_frame_unwind);
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */

extern void _initialize_jit (void);
diff --git a/gdb/jit.h b/gdb/jit.h
index 73a1414..9dd8f5a 100644
--- a/gdb/jit.h
+++ b/gdb/jit.h
@@ -80,4 +80,6 @@ extern void jit_breakpoint_re_set (void);

extern void jit_event_handler (struct gdbarch *gdbarch);

+extern void jit_prepend_unwinder (struct gdbarch *gdbarch);
+
#endif /* JIT_H */
--
1.7.5.3