public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/4] jit-reader.h: describe interface implemented by the JIT readers.
@ 2011-07-13 20:24 Sanjoy Das
  2011-07-13 20:24 ` [PATCH 4/4] Activate JIT code unwinder for i386 Sanjoy Das
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Sanjoy Das @ 2011-07-13 20:24 UTC (permalink / raw)
  To: gdb-patches; +Cc: Sanjoy Das

Generating ELF files in memory is usually too much work for JIT
compilers, since they usually don't have the related routines (as
opposed to a regular static compiler). One way to simplify this is to
have the JIT vendor themselves provide a shared object which is loaded
and used to parse the debug information. This patch exposes a
simplified interface for the debug info parser.

The interface is _simplified_ in the sense that it does not expose all
of GDB's functionality. The idea is that if the user wishes to use GDB
for full-on debugging of generated code, it will probably be simpler
to generate the full DWARF info.

The interface does not expose more than the minimum amount of GDB
internals; both to prevent versioning problems, and to keep writing
the debug-info reader simple.

This patch adds a header file (`jit-reader.h') which can be included
and implemented in a shared object to build a working JIT
debug-reader.

gdb/ChangeLog:

	* jit-reader.h: Add (new) JIT reader interface.
	* jit.c: Include jit-reader.h in source file
	* Makefile.in: Add jit-reader.h to HFILES_NO_SRCDIR.
---
 gdb/ChangeLog    |    6 ++
 gdb/Makefile.in  |    2 +-
 gdb/jit-reader.h |  189 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/jit.c        |    4 +
 4 files changed, 200 insertions(+), 1 deletions(-)
 create mode 100644 gdb/jit-reader.h

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8ed7eaf..e067f04 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,9 @@
+2011-07-13  Sanjoy Das  <sanjoy@playingwithpointers.com>
+
+	* jit-reader.h: Add (new) JIT reader interface.
+	* jit.c: Include jit-reader.h in source file
+	* Makefile.in: Add jit-reader.h to HFILES_NO_SRCDIR.
+
 2011-07-12  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
 	Code cleanup making also optimized out values lazy.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 494f24f..ca2cbcb 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -816,7 +816,7 @@ osdata.h procfs.h python/py-event.h python/py-events.h python/py-stopevent.h \
 python/python-internal.h python/python.h ravenscar-thread.h record.h \
 solib-darwin.h solib-ia64-hpux.h solib-spu.h windows-nat.h xcoffread.h \
 gnulib/extra/arg-nonnull.h gnulib/extra/c++defs.h gnulib/extra/warn-on-use.h \
-gnulib/stddef.in.h inline-frame.h
+gnulib/stddef.in.h inline-frame.h jit-reader.h
 
 # Header files that already have srcdir in them, or which are in objdir.
 
diff --git a/gdb/jit-reader.h b/gdb/jit-reader.h
new file mode 100644
index 0000000..15980bd
--- /dev/null
+++ b/gdb/jit-reader.h
@@ -0,0 +1,189 @@
+/* Interface for JIT debug-info readers.
+
+   Copyright (C) 2011
+   Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>. */
+
+#ifndef GDB_JIT_READER_H
+#define GDB_JIT_READER_H
+
+/* Versioning: for the SO to be correctly recognized and loaded, put the macro
+   GDB_JIT_DECLARE_API_VERSION in a source file. */
+
+#define GDB_JIT_INTERFACE_VERSION  1
+#define GDB_JIT_DECLARE_API_VERSION             \
+  extern int __gdbjit_so_api_version (void)     \
+  {                                             \
+    return GDB_JIT_INTERFACE_VERSION;           \
+  }
+
+#define GDB_JIT_SUCCESS          1
+#define GDB_JIT_FAIL             0
+#define GDBJIT_MAX_REGISTER_SIZE 64 /* Mirrors the internal GDB definition. */
+
+struct gdbjit_symtab;
+struct gdbjit_block;
+struct gdbjit_symbol;
+struct gdbjit_symtab_callbacks;
+
+#ifndef __GDB__INTERNAL /* Only defined when being compiled in GDB. */
+typedef long long CORE_ADDR;
+#endif
+
+struct gdbjit_line_mapping
+{
+  int line;
+  CORE_ADDR pc;
+};
+
+typedef struct gdbjit_symtab *(gdbjit_symtab_open)
+  (struct gdbjit_symtab_callbacks *callbacks,
+   const char *file_name);
+
+typedef struct gdbjit_block *(gdbjit_new_block)
+  (struct gdbjit_symtab_callbacks *callbacks, struct gdbjit_symtab *symtab,
+   struct gdbjit_block *parent, CORE_ADDR begin_addr, CORE_ADDR end_addr,
+   const char *name);
+
+typedef void (gdbjit_symtab_close) (struct gdbjit_symtab_callbacks *callbacks,
+                                    struct gdbjit_symtab *symtab);
+
+typedef void (gdbjit_symtab_add_line_mapping)
+(struct gdbjit_symtab_callbacks * callbacks, struct gdbjit_symtab *symtab,
+ int nlines, struct gdbjit_line_mapping *lines);
+
+typedef int (gdbjit_target_read) (CORE_ADDR target_mem, void *gdb_buf, int len);
+
+struct gdbjit_symtab_callbacks {
+  /* Create a new symbol table. A symbol table consists of a forest of block
+     hierachies. Blocks are added by a call to new_block. This needs to be
+     paired with a call to symtab_close.
+
+     An optional FILE_NAME can be passed, which will be associated with the
+     symbol table as the source file from which this code was compiled.
+
+     Returns a new struct gdbjit_symtab which can be further passed to
+     new_block and symtab_close. */
+  gdbjit_symtab_open *symtab_open;
+
+  /* Creates a new block. A block is an optionally named contiguous block of
+     code in memory. Creating a new block automatically adds it to the symbol
+     table in which it is created.
+
+     SYMTAB is the symbol table this block is added to. Each block may have an
+     optionally non-NULL PARENT. BEGIN_ADDR and END_ADDR are the first and last
+     addresses spanned by this piece of compiled code. This block may have an
+     optional NAME, in case it corresponds to a function.
+
+     Returns a gdbjit_block instance, which, at this point, is useless. */
+  gdbjit_new_block *new_block;
+
+  /* Adds PC <-> line number mapping for the symbol table SYMTAB. Pass an array
+     of NLINES gdbjit_line_mapping s in LINES. */
+  gdbjit_symtab_add_line_mapping *add_line_mapping;
+
+  /* Reads LEN bytes of memory from the target's address space, beginning from
+     TARGET_MEM into the buffer GDB_BUF points to. */
+  gdbjit_target_read *target_read;
+
+  /* Closes the symbol table SYMTAB, and add it to the internal GDB list. */
+  gdbjit_symtab_close *symtab_close;
+
+  /* For internal use. */
+  void *private;
+};
+
+/* Denotes a register value. */
+struct gdbjit_reg_value
+{
+  /* Set to non-zero if this register has a valid value. Otherwise 0. */
+  int defined;
+  /* The actual value of the register (this is considered valid only if defined
+     is non-zero). */
+  unsigned char value[GDBJIT_MAX_REGISTER_SIZE];
+};
+
+/* Unique frame identifier. This should remain constant throughout the lifetime
+   of the frame concerned. */
+struct gdbjit_frame_id
+{
+  CORE_ADDR code_address;
+  CORE_ADDR stack_address;
+};
+
+struct gdbjit_unwind_callbacks;
+
+typedef struct gdbjit_reg_value (gdbjit_unwind_reg_get)
+  (struct gdbjit_unwind_callbacks *callback, int regnum);
+
+typedef void (gdbjit_unwind_reg_set) (struct gdbjit_unwind_callbacks *callback,
+                                      int regnum, struct gdbjit_reg_value val);
+
+struct gdbjit_unwind_callbacks
+{
+  /* Gets the current value for the register REGNUM (DWARF numbering). */
+  gdbjit_unwind_reg_get *reg_get;
+
+  /* Sets the previous value of the REGNUM register (i.e. its value in the
+     previous frame) to VAL. */
+  gdbjit_unwind_reg_set *reg_set;
+
+  /* Function to read memory off the inferior process being debugged, as
+     documented in gdbjit_symtab_callbacks. */
+  gdbjit_target_read *target_read;
+
+  /* For internal use. */
+  void *private;
+};
+
+/* Called once for each new inferior program. It should also initialize the
+   private pointer (to which a pointer is passed here) if the it needs one.
+
+   return: GDB_SUCCESS on success, or GDB_JIT_FAIL on error,
+   in which this plugin will be discarded (gdbjit_destroy_reader will not be
+   called in such a case). */
+extern int gdbjit_init_reader (void **private);
+
+/* Called to have the reader try to read a block of memory. PRIVATE is the
+   private pointer, (possibly) initialized by gdbjit_init_reader.
+
+   The function is expected to use CALLBACKS (documented above) to build a
+   symbol table for this particular block of memory (pointed to by MEMORY,
+   already copied from the target address space to GDB's address space).
+   MEMORY_SZ is the size of the block of memory.
+
+   Should return GDB_JIT_SUCESS on success and GDB_JIT_FAIL on error. */
+extern int gdbjit_read_debug_info (void *private,
+                                   struct gdbjit_symtab_callbacks *callbacks,
+                                   CORE_ADDR memory, long memory_sz);
+
+/* Called to unwind one frame. PRIVATE is the private pointer. Expected to use
+   CALLBACKS to unwind the registers to the older frame.
+
+   Should return GDB_JIT_SUCESS for success, GDB_JIT_FAIL for fail. */
+extern int gdbjit_unwind_frame (void *private,
+                                struct gdbjit_unwind_callbacks *callbacks);
+
+/* Called to get a unique identifier for the current stack frame (as reported by
+   callbacks). */
+struct gdbjit_frame_id gdbjit_get_frame_id (void *private, struct
+                                            gdbjit_unwind_callbacks *callbacks);
+
+/* Called once the inferior process exits. */
+extern void gdbjit_destroy_reader (void *private);
+
+#endif
diff --git a/gdb/jit.c b/gdb/jit.c
index eb1bcc7..6c07a57 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -19,7 +19,11 @@
 
 #include "defs.h"
 
+/* To stop jit-reader.h from re-defining CORE_ADDR */
+#define __GDB__INTERNAL
+
 #include "jit.h"
+#include "jit-reader.h"
 #include "breakpoint.h"
 #include "command.h"
 #include "gdbcmd.h"
-- 
1.7.5.4

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

* [PATCH 4/4] Activate JIT code unwinder for i386.
  2011-07-13 20:24 [PATCH 1/4] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
@ 2011-07-13 20:24 ` Sanjoy Das
  2011-07-14 17:55   ` Tom Tromey
  2011-07-13 20:52 ` [PATCH 2/4] Add symbol-handling callbacks to the jit-reader interface Sanjoy Das
  2011-07-14  1:39 ` [PATCH 3/4] Add an unwinder for JIT compiled code Sanjoy Das
  2 siblings, 1 reply; 8+ messages in thread
From: Sanjoy Das @ 2011-07-13 20:24 UTC (permalink / raw)
  To: gdb-patches; +Cc: Sanjoy Das

Registers the (pseudo) unwinder for JIT code for i386. This can probably be extended to other architectures after some testing and review.

gdb/ChangeLog:
	* i386-tdep.c: Activate JIT unwinder for i386.
---
 gdb/ChangeLog   |    4 ++++
 gdb/i386-tdep.c |    4 ++++
 2 files changed, 8 insertions(+), 0 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 8485b05..51c0727 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,9 @@
 2011-07-13  Sanjoy Das  <sanjoy@playingwithpointers.com>
 
+	* i386-tdep.c: Activate JIT unwinder for i386.
+
+2011-07-13  Sanjoy Das  <sanjoy@playingwithpointers.com>
+
 	* jit.c: Add JIT frame unwinding code.
 	* jit-reader.h: Added jit_prepend_unwinder.
 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 366d0fa..66626ab 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -30,6 +30,7 @@
 #include "frame-base.h"
 #include "frame-unwind.h"
 #include "inferior.h"
+#include "jit.h"
 #include "gdbcmd.h"
 #include "gdbcore.h"
 #include "gdbtypes.h"
@@ -7330,6 +7331,9 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
      CFI info will be used if it is available.  */
   dwarf2_append_unwinders (gdbarch);
 
+  /* JIT reader pseudo-unwinder. */
+  jit_prepend_unwinder (gdbarch);
+
   frame_base_set_default (gdbarch, &i386_frame_base);
 
   /* Pseudo registers may be changed by amd64_init_abi.  */
-- 
1.7.5.4

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

* [PATCH 2/4] Add symbol-handling callbacks to the jit-reader interface.
  2011-07-13 20:24 [PATCH 1/4] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
  2011-07-13 20:24 ` [PATCH 4/4] Activate JIT code unwinder for i386 Sanjoy Das
@ 2011-07-13 20:52 ` Sanjoy Das
  2011-07-14  1:39 ` [PATCH 3/4] Add an unwinder for JIT compiled code Sanjoy Das
  2 siblings, 0 replies; 8+ messages in thread
From: Sanjoy Das @ 2011-07-13 20:52 UTC (permalink / raw)
  To: gdb-patches; +Cc: Sanjoy Das

Implements the callbacks in `struct gdbjit_symtab_callbacks'. This
will allow the JIT debug info reader to be able to parse the debug
info and actually push the symbols into GDB.

gdb/ChangeLog:

	* jit.c: Add symbol handling callbacks for the JIT reader
interface.
---
 gdb/ChangeLog |    4 +
 gdb/jit.c     |  518 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 471 insertions(+), 51 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index e067f04..ac207d1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,9 @@
 2011-07-13  Sanjoy Das  <sanjoy@playingwithpointers.com>
 
+	* jit.c: Add symbol handling callbacks for the JIT reader interface.
+
+2011-07-13  Sanjoy Das  <sanjoy@playingwithpointers.com>
+
 	* jit-reader.h: Add (new) JIT reader interface.
 	* jit.c: Include jit-reader.h in source file
 	* Makefile.in: Add jit-reader.h to HFILES_NO_SRCDIR.
diff --git a/gdb/jit.c b/gdb/jit.c
index 6c07a57..2899469 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -24,8 +24,10 @@
 
 #include "jit.h"
 #include "jit-reader.h"
+#include "block.h"
 #include "breakpoint.h"
 #include "command.h"
+#include "dictionary.h"
 #include "gdbcmd.h"
 #include "gdbcore.h"
 #include "inferior.h"
@@ -34,8 +36,11 @@
 #include "symfile.h"
 #include "symtab.h"
 #include "target.h"
+#include "gdb_dirent.h"
 #include "gdb_stat.h"
 
+#include <dlfcn.h>
+
 static const struct objfile_data *jit_objfile_data;
 
 static const char *const jit_break_name = "__jit_debug_register_code";
@@ -50,6 +55,115 @@ static void jit_inferior_init (struct gdbarch *gdbarch);
 
 static int jit_debug = 0;
 
+/* The environment variable we poke at to get the file name of the shared object
+   to load. */
+
+static const char *jit_dbg_reader_so_name_env = "JIT_DEBUG_READER";
+
+/* Symbols (and the corresponding types) to be loaded from the shared object
+   supposed to do the debug-info parsing. */
+typedef int (jit_init_reader_fn) (void **);
+static const char *jit_init_reader_sym = "gdbjit_init_reader";
+
+typedef int (jit_read_debug_info_fn) (void *, struct gdbjit_symtab_callbacks *,
+                                      void *mem, long sz);
+static const char *jit_read_debug_info_sym = "gdbjit_read_debug_info";
+
+typedef int (jit_unwind_frame_fn) (void *, struct gdbjit_unwind_callbacks *);
+static const char *jit_unwind_frame_sym = "gdbjit_unwind_frame";
+
+typedef void (jit_destroy_reader_fn) (void *);
+static const char *jit_destroy_reader_sym = "gdbjit_destroy_reader";
+
+typedef struct gdbjit_frame_id (jit_get_frame_id_fn) (void *, struct
+                                                     gdbjit_unwind_callbacks *);
+static const char *jit_get_frame_id_sym = "gdbjit_get_frame_id";
+
+static const char *jit_so_api_version_sym = "__gdbjit_so_api_version";
+
+struct jit_dbg_reader
+{
+  jit_init_reader_fn *init;
+  jit_read_debug_info_fn *read;
+  jit_unwind_frame_fn *unwind;
+  jit_get_frame_id_fn *get_frame_id;
+  jit_destroy_reader_fn *destroy;
+
+  void *handle, *private_data;
+};
+
+static struct jit_dbg_reader *
+jit_dbg_reader_load (void)
+{
+  /* Check if a reader has been provided. */
+  const char *file_name = getenv (jit_dbg_reader_so_name_env);
+  void *handle = NULL;
+  int (*jit_reader_so_api_version) (void) = NULL;
+  struct jit_dbg_reader *reader = NULL;
+  
+  if (file_name == NULL)
+    return NULL;
+
+  handle = dlopen (file_name, RTLD_NOW);
+  if (!handle)
+    goto cleanup;
+
+  reader = XZALLOC (struct jit_dbg_reader);
+  reader->init = dlsym (handle, jit_init_reader_sym);
+  reader->handle = handle;
+
+  if (!reader->init)
+    goto cleanup;
+
+  reader->read = dlsym (handle, jit_read_debug_info_sym);
+  if (!reader->read)
+    goto cleanup;
+
+  reader->unwind = dlsym (handle, jit_unwind_frame_sym);
+  if (!reader->unwind)
+    goto cleanup;
+
+  reader->get_frame_id = dlsym (handle, jit_get_frame_id_sym);
+  if (!reader->get_frame_id)
+    goto cleanup;
+
+  reader->destroy = dlsym (handle, jit_destroy_reader_sym);
+  if (!reader->destroy)
+    goto cleanup;
+
+  jit_reader_so_api_version = dlsym (handle, jit_so_api_version_sym);
+  if (!jit_reader_so_api_version ||
+      jit_reader_so_api_version () != GDB_JIT_INTERFACE_VERSION)
+    {
+      goto cleanup;
+    }
+
+  if (reader->init (&reader->private_data) != GDB_JIT_SUCCESS)
+    goto cleanup;
+
+  return reader;
+
+ cleanup:
+  xfree (reader);
+  if (handle)
+    {
+      dlclose (handle);
+      fprintf (stderr, "Could not load reader object %s\n", file_name);
+    }
+  else /* also report why the shared object could not be loaded */
+    fprintf (stderr, "Could not load reader object %s (%s)\n", file_name,
+             dlerror());
+  return NULL;
+}
+
+static void
+jit_dbg_reader_destroy (struct jit_dbg_reader *reader)
+{
+  if (reader)
+    reader->destroy (reader->private_data);
+  xfree (reader);
+}
+
 static void
 show_jit_debug (struct ui_file *file, int from_tty,
 		struct cmd_list_element *c, const char *value)
@@ -140,6 +254,9 @@ struct jit_inferior_data
 {
   CORE_ADDR breakpoint_addr;  /* &__jit_debug_register_code()  */
   CORE_ADDR descriptor_addr;  /* &__jit_debug_descriptor  */
+  struct jit_dbg_reader *reader; /* If a debug info reader was found. */
+  struct objfile *jit_objfile;   /* The object file where all the JIT symbols
+                                    will be added. */
 };
 
 /* Return jit_inferior_data for current inferior.  Allocate if not already
@@ -238,6 +355,282 @@ jit_read_code_entry (struct gdbarch *gdbarch,
       extract_unsigned_integer (&entry_buf[3 * ptr_size], 8, byte_order);
 }
 
+struct gdbjit_block
+{
+  struct gdbjit_block *next, *parent;
+  struct block *real_block;
+
+  /* The first and last code address corresponding to this block */
+  CORE_ADDR begin, end;
+  /* The name of this block (if any). If this is non-NULL, the FUNCTION symbol
+     symbol is set to this value. */
+  const char *name;
+};
+
+struct gdbjit_symtab
+{
+  struct gdbjit_block *blocks;
+  struct linetable *linetable;
+  int nblocks;
+
+  const char *file_name;
+};
+
+struct jit_dbg_reader_data
+{
+  struct objfile **objf;
+};
+
+static int
+jit_target_read_impl (CORE_ADDR target_mem, void *gdb_buf, int len)
+{
+  return target_read_memory (target_mem, gdb_buf, len);
+}
+
+static struct gdbjit_symtab *
+jit_symtab_open_impl (struct gdbjit_symtab_callbacks *cb, const char *file_name)
+{
+  struct gdbjit_symtab *ret = XZALLOC (struct gdbjit_symtab);
+  ret->file_name = xstrdup (file_name);
+  return ret;
+}
+
+/* Returns true if the block corrensponding to old should be placed before the
+   block corrensponding to new in the final blockvector. */
+static int
+compare_block (struct gdbjit_block *old, struct gdbjit_block *new)
+{
+  if (old == NULL)
+    return 1;
+  if (old->begin < new->begin) 
+    return 1;
+  else if (old->begin == new->begin)
+    {
+      if (old->end > new->end)
+        return 1;
+      else
+        return 0;
+    }
+  else
+    return 0;
+}
+
+static struct gdbjit_block *
+jit_new_block_impl (struct gdbjit_symtab_callbacks *cb,
+                    struct gdbjit_symtab *symtab,
+                    struct gdbjit_block *parent,
+                    CORE_ADDR begin, CORE_ADDR end, const char *name)
+{
+  struct gdbjit_block *block = XZALLOC (struct gdbjit_block);
+
+  (void) cb;
+
+  block->next = symtab->blocks;
+  block->begin = begin;
+  block->end = end;
+  block->name = name ? xstrdup (name) : NULL;
+  block->parent = parent;
+
+  /* Ensure that the blocks are inserted in the correct (reverse of the order
+     expected by blockvector). */
+  if (compare_block (symtab->blocks, block))
+    {
+      symtab->blocks = block;
+    }
+  else
+    {
+      struct gdbjit_block *i = symtab->blocks;
+
+      for (;; i = i->next)
+        {
+          /* Guranteed to terminate, since compare_block (NULL, _) returns 1 */
+          if (compare_block (i->next, block))
+            {
+              block->next = i->next;
+              i->next = block;
+              break;
+            }
+        }
+    }
+  symtab->nblocks++;
+
+  return block;
+}
+
+static void
+jit_symtab_add_line_mapping_impl (struct gdbjit_symtab_callbacks *cb,
+                                  struct gdbjit_symtab *stab,
+                                  int nlines,
+                                  struct gdbjit_line_mapping *map)
+{
+  int i;
+  /* Must copy */
+  if (!nlines)
+    return;
+
+  stab->linetable = xmalloc (sizeof (struct linetable) + (nlines - 1) *
+                             sizeof (struct linetable_entry));
+  stab->linetable->nitems = nlines;
+  for (i = 0; i < nlines; i++)
+    {
+      stab->linetable->item [i].pc = map[i].pc;
+      stab->linetable->item [i].line = map[i].line;
+    }
+}
+
+static void
+jit_symtab_close_impl (struct gdbjit_symtab_callbacks *cb,
+                       struct gdbjit_symtab *stab)
+{
+  struct jit_dbg_reader_data *dat = cb->private;
+  struct symtab *symtab;
+  struct gdbjit_block *blck_iter, *blck_iter_tmp;
+  struct block *block_iter;
+  struct objfile *objfile;
+  int actual_nblocks = FIRST_LOCAL_BLOCK + stab->nblocks, i;
+  CORE_ADDR begin, end;
+
+  if (*dat->objf == NULL)
+    {
+       objfile = allocate_objfile (NULL, 0);
+      *dat->objf = objfile;
+      objfile->gdbarch = target_gdbarch;
+      objfile->msymbols = obstack_alloc (&objfile->objfile_obstack,
+			                                   sizeof (struct minimal_symbol));
+      objfile->msymbols[0].ginfo.name = NULL;
+      objfile->msymbols[0].ginfo.value.address = 0;
+      objfile->name = xstrdup ("JIT");
+    }
+  else
+    objfile = *dat->objf;
+  
+  symtab = allocate_symtab (stab->file_name, objfile);
+  symtab->dirname = NULL; /* JIT compilers compile in memory */
+
+  if (stab->linetable) {
+    int size = (stab->linetable->nitems - 1) * sizeof (struct linetable_entry) +
+      sizeof (struct linetable);
+    LINETABLE (symtab) = obstack_alloc (&objfile->objfile_obstack, size);
+    memcpy (LINETABLE (symtab), stab->linetable, size);
+  } else {
+    LINETABLE (symtab) = NULL;
+  }
+
+  symtab->blockvector = obstack_alloc (&objfile->objfile_obstack,
+                                       (sizeof (struct blockvector)
+                                        + (actual_nblocks - 1) *
+                                        sizeof (struct block *)));
+
+  /* (begin, end) will contain the PC range this entire blockvector spans. */
+  symtab->primary = 1;
+  BLOCKVECTOR_MAP (symtab->blockvector) = NULL;
+  begin = stab->blocks->begin;
+  end = stab->blocks->end;
+  BLOCKVECTOR_NBLOCKS (symtab->blockvector) = actual_nblocks;
+
+  for (i = (actual_nblocks - 1), blck_iter = stab->blocks;
+       i >= FIRST_LOCAL_BLOCK; i--)
+    {
+      struct block *bl = allocate_block (&objfile->objfile_obstack);
+      struct symbol *block_name = obstack_alloc (&objfile->objfile_obstack,
+                                                 sizeof (struct symbol));
+
+      BLOCK_DICT (bl) = dict_create_linear (&objfile->objfile_obstack, NULL);
+      BLOCK_START (bl) = blck_iter->begin;
+      BLOCK_END (bl) = blck_iter->end;
+      BLOCK_FUNCTION (bl) = block_name;
+
+      memset (block_name, 0, sizeof (struct symbol));
+      SYMBOL_DOMAIN (block_name) = VAR_DOMAIN;
+      SYMBOL_CLASS (block_name) = LOC_BLOCK;
+      block_name->symtab = symtab;
+      SYMBOL_BLOCK_VALUE (block_name) = bl;
+
+      block_name->ginfo.name = obstack_alloc (&objfile->objfile_obstack,
+			                                        1 + strlen (blck_iter->name));
+      strcpy (block_name->ginfo.name, blck_iter->name);
+      BLOCK_FUNCTION (bl) = block_name;
+ 
+      BLOCKVECTOR_BLOCK (symtab->blockvector, i) = bl;
+      if (begin < (long) blck_iter->begin)
+        begin = (long) blck_iter->begin;
+      if (end > (long) blck_iter->end)
+        end = (long) blck_iter->end;
+    }
+
+  blck_iter = blck_iter->next;
+
+  /* Now add the special blocks. */
+  block_iter = NULL;
+  for (i = 0; i < FIRST_LOCAL_BLOCK; i++)
+    {
+      struct block *bl = allocate_block (&objfile->objfile_obstack);
+      BLOCK_DICT (bl) = dict_create_linear (&objfile->objfile_obstack, NULL);
+      BLOCK_SUPERBLOCK (bl) = block_iter;
+      block_iter = bl;
+
+      BLOCK_START (bl) = begin;
+      BLOCK_END (bl) = end;
+
+      BLOCKVECTOR_BLOCK (symtab->blockvector, i) = bl;
+    }
+
+  /* Fill up the superblock fields. */
+  for (blck_iter = stab->blocks; blck_iter; blck_iter = blck_iter->next)
+    {
+      if (blck_iter->parent != NULL)
+        BLOCK_SUPERBLOCK (blck_iter->real_block) =
+          blck_iter->parent->real_block;
+    }
+
+  /* Free memory. */
+  blck_iter = stab->blocks;
+  for (blck_iter = stab->blocks, blck_iter_tmp = blck_iter->next;
+       blck_iter; blck_iter = blck_iter_tmp)
+    {
+      xfree (blck_iter);
+    }
+}
+
+static int
+jit_try_read_symtab (struct jit_inferior_data *inf_data,
+                     CORE_ADDR target_mem, long sz)
+{
+  void *gdb_mem;
+  int status = 0;
+  struct jit_dbg_reader *i;
+  struct jit_dbg_reader_data priv_data;
+  struct gdbjit_symtab_callbacks callbacks =
+    {
+      jit_symtab_open_impl,
+      jit_new_block_impl,
+      jit_symtab_add_line_mapping_impl,
+      jit_target_read_impl,
+      jit_symtab_close_impl,
+
+      &priv_data
+    };
+
+  if (!inf_data->reader)
+    return 0;
+
+  priv_data.objf = &inf_data->jit_objfile;
+
+  gdb_mem = xmalloc (sz);
+  if (target_read_memory (target_mem, gdb_mem, sz))
+    {
+      status = 0;
+      goto cleanup;
+    }
+
+  status = inf_data->reader->read (inf_data->reader->private_data, &callbacks,
+                                   gdb_mem, sz) == GDB_JIT_SUCCESS;
+
+ cleanup:
+  xfree (gdb_mem);
+  return status;
+}
+
 /* This function registers code associated with a JIT code entry.  It uses the
    pointer and size pair in the entry to read the symbol file from the remote
    and then calls symbol_file_add_from_local_memory to add it as though it were
@@ -247,14 +640,9 @@ static void
 jit_register_code (struct gdbarch *gdbarch,
 		   CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
 {
-  bfd *nbfd;
-  struct section_addr_info *sai;
-  struct bfd_section *sec;
   struct objfile *objfile;
-  struct cleanup *old_cleanups, *my_cleanups;
-  int i;
-  const struct bfd_arch_info *b;
-  CORE_ADDR *entry_addr_ptr;
+  int i, success;
+  struct jit_inferior_data *inf_data = get_jit_inferior_data ();
 
   if (jit_debug)
     fprintf_unfiltered (gdb_stdlog,
@@ -263,53 +651,68 @@ jit_register_code (struct gdbarch *gdbarch,
 			paddress (gdbarch, code_entry->symfile_addr),
 			pulongest (code_entry->symfile_size));
 
-  nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
-                                      code_entry->symfile_size, gnutarget);
-  old_cleanups = make_cleanup_bfd_close (nbfd);
-
-  /* Check the format.  NOTE: This initializes important data that GDB uses!
-     We would segfault later without this line.  */
-  if (!bfd_check_format (nbfd, bfd_object))
+  if (jit_try_read_symtab (inf_data, code_entry->symfile_addr,
+                           code_entry->symfile_size))
     {
-      printf_unfiltered (_("\
+      objfile = inf_data->jit_objfile;
+    }
+  else
+    {
+      struct cleanup *old_cleanups;
+      bfd *nbfd;
+      struct section_addr_info *sai;
+      struct bfd_section *sec;
+      const struct bfd_arch_info *b;
+      CORE_ADDR *entry_addr_ptr;
+      
+      nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
+                                          code_entry->symfile_size, gnutarget);
+      old_cleanups = make_cleanup_bfd_close (nbfd);
+
+      /* Check the format.  NOTE: This initializes important data that GDB uses!
+         We would segfault later without this line.  */
+      if (!bfd_check_format (nbfd, bfd_object))
+        {
+          printf_unfiltered (_("\
 JITed symbol file is not an object file, ignoring it.\n"));
-      do_cleanups (old_cleanups);
-      return;
+          do_cleanups (old_cleanups);
+          return;
+        }
+
+      /* Check bfd arch.  */
+      b = gdbarch_bfd_arch_info (gdbarch);
+      if (b->compatible (b, bfd_get_arch_info (nbfd)) != b)
+        warning (_("JITed object file architecture %s is not compatible "
+                   "with target architecture %s."), bfd_get_arch_info
+                 (nbfd)->printable_name, b->printable_name);
+
+      /* Read the section address information out of the symbol file.  Since the
+         file is generated by the JIT at runtime, it should all of the absolute
+         addresses that we care about.  */
+      sai = alloc_section_addr_info (bfd_count_sections (nbfd));
+      make_cleanup_free_section_addr_info (sai);
+      i = 0;
+      for (sec = nbfd->sections; sec != NULL; sec = sec->next)
+        if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
+          {
+            /* We assume that these virtual addresses are absolute, and do not
+               treat them as offsets.  */
+            sai->other[i].addr = bfd_get_section_vma (nbfd, sec);
+            sai->other[i].name = xstrdup (bfd_get_section_name (nbfd, sec));
+            sai->other[i].sectindex = sec->index;
+            ++i;
+          }
+
+      /* This call takes ownership of sai.  */
+      objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED, NULL);
+
+      /* Remember a mapping from entry_addr to objfile.  */
+      entry_addr_ptr = xmalloc (sizeof (CORE_ADDR));
+      *entry_addr_ptr = entry_addr;
+      set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr);
+
+      discard_cleanups (old_cleanups);
     }
-
-  /* Check bfd arch.  */
-  b = gdbarch_bfd_arch_info (gdbarch);
-  if (b->compatible (b, bfd_get_arch_info (nbfd)) != b)
-    warning (_("JITed object file architecture %s is not compatible "
-               "with target architecture %s."), bfd_get_arch_info
-             (nbfd)->printable_name, b->printable_name);
-
-  /* Read the section address information out of the symbol file.  Since the
-     file is generated by the JIT at runtime, it should all of the absolute
-     addresses that we care about.  */
-  sai = alloc_section_addr_info (bfd_count_sections (nbfd));
-  make_cleanup_free_section_addr_info (sai);
-  i = 0;
-  for (sec = nbfd->sections; sec != NULL; sec = sec->next)
-    if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
-      {
-        /* We assume that these virtual addresses are absolute, and do not
-           treat them as offsets.  */
-        sai->other[i].addr = bfd_get_section_vma (nbfd, sec);
-        sai->other[i].name = xstrdup (bfd_get_section_name (nbfd, sec));
-        sai->other[i].sectindex = sec->index;
-        ++i;
-      }
-
-  /* This call takes ownership of sai.  */
-  objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED, NULL);
-
-  /* Remember a mapping from entry_addr to objfile.  */
-  entry_addr_ptr = xmalloc (sizeof (CORE_ADDR));
-  *entry_addr_ptr = entry_addr;
-  set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr);
-
-  discard_cleanups (old_cleanups);
 }
 
 /* This function unregisters JITed code and frees the corresponding
@@ -395,6 +798,8 @@ jit_inferior_init (struct gdbarch *gdbarch)
   if (jit_breakpoint_re_set_internal (gdbarch, inf_data) != 0)
     return;
 
+  inf_data->reader = jit_dbg_reader_load ();
+
   if (inf_data->descriptor_addr == 0)
     {
       struct minimal_symbol *desc_symbol;
@@ -474,6 +879,11 @@ jit_reset_inferior_data_and_breakpoints (void)
   /* Remove any existing JIT breakpoint(s).  */
   remove_jit_event_breakpoints ();
 
+  /* Destroy JIT reader objects. */
+  jit_dbg_reader_destroy (inf_data->reader);
+  if (inf_data->jit_objfile)
+    free_objfile (inf_data->jit_objfile);
+
   jit_inferior_init (target_gdbarch);
 }
 
@@ -494,6 +904,12 @@ jit_inferior_exit_hook (struct inferior *inf)
 {
   struct objfile *objf;
   struct objfile *temp;
+  struct jit_inferior_data *inf_data;
+
+ inf_data = inferior_data (inf, jit_inferior_data);
+ jit_dbg_reader_destroy (inf_data->reader);
+ if (inf_data->jit_objfile)
+   free_objfile (inf_data->jit_objfile);
 
   ALL_OBJFILES_SAFE (objf, temp)
     if (objfile_data (objf, jit_objfile_data) != NULL)
-- 
1.7.5.4

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

* [PATCH 3/4] Add an unwinder for JIT compiled code.
  2011-07-13 20:24 [PATCH 1/4] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
  2011-07-13 20:24 ` [PATCH 4/4] Activate JIT code unwinder for i386 Sanjoy Das
  2011-07-13 20:52 ` [PATCH 2/4] Add symbol-handling callbacks to the jit-reader interface Sanjoy Das
@ 2011-07-14  1:39 ` Sanjoy Das
  2011-07-14 21:05   ` Tom Tromey
  2 siblings, 1 reply; 8+ messages in thread
From: Sanjoy Das @ 2011-07-14  1:39 UTC (permalink / raw)
  To: gdb-patches; +Cc: Sanjoy Das

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     |  150 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/jit.h     |    2 +
 3 files changed, 157 insertions(+), 0 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index ac207d1..8485b05 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,10 @@
 2011-07-13  Sanjoy Das  <sanjoy@playingwithpointers.com>
 
+	* jit.c: Add JIT frame unwinding code.
+	* jit-reader.h: Added jit_prepend_unwinder.
+
+2011-07-13  Sanjoy Das  <sanjoy@playingwithpointers.com>
+
 	* jit.c: Add symbol handling callbacks for the JIT reader interface.
 
 2011-07-13  Sanjoy Das  <sanjoy@playingwithpointers.com>
diff --git a/gdb/jit.c b/gdb/jit.c
index 2899469..03a3d0b 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -28,6 +28,7 @@
 #include "breakpoint.h"
 #include "command.h"
 #include "dictionary.h"
+#include "frame-unwind.h"
 #include "gdbcmd.h"
 #include "gdbcore.h"
 #include "inferior.h"
@@ -781,6 +782,149 @@ 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 (frame_id.stack_address, 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
@@ -960,6 +1104,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.4

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

* Re: [PATCH 4/4] Activate JIT code unwinder for i386.
  2011-07-13 20:24 ` [PATCH 4/4] Activate JIT code unwinder for i386 Sanjoy Das
@ 2011-07-14 17:55   ` Tom Tromey
  2011-07-14 17:58     ` Sanjoy Das
  0 siblings, 1 reply; 8+ messages in thread
From: Tom Tromey @ 2011-07-14 17:55 UTC (permalink / raw)
  To: Sanjoy Das; +Cc: gdb-patches

>>>>> "Sanjoy" == Sanjoy Das <sanjoy@playingwithpointers.com> writes:

Sanjoy> Registers the (pseudo) unwinder for JIT code for i386. This can
Sanjoy> probably be extended to other architectures after some testing
Sanjoy> and review.

This doesn't seem to be different from the earlier version of this
patch; which Mark K. said should probably be changed to put the JIT
unwinders after the DWARF ones.

Tom

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

* Re: [PATCH 4/4] Activate JIT code unwinder for i386.
  2011-07-14 17:55   ` Tom Tromey
@ 2011-07-14 17:58     ` Sanjoy Das
  0 siblings, 0 replies; 8+ messages in thread
From: Sanjoy Das @ 2011-07-14 17:58 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

Hi!

Thanks for the feedback; I'll get started on fixing the problems.

And no, I have not done anything regarding copyright assignment.

-- 
Sanjoy Das
http://playingwithpointers.com

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

* Re: [PATCH 3/4] Add an unwinder for JIT compiled code.
  2011-07-14  1:39 ` [PATCH 3/4] Add an unwinder for JIT compiled code Sanjoy Das
@ 2011-07-14 21:05   ` Tom Tromey
  0 siblings, 0 replies; 8+ messages in thread
From: Tom Tromey @ 2011-07-14 21:05 UTC (permalink / raw)
  To: Sanjoy Das; +Cc: gdb-patches

>>>>> "Sanjoy" == Sanjoy Das <sanjoy@playingwithpointers.com> writes:

Sanjoy> 	* jit-reader.h: Added jit_prepend_unwinder.

This mentions the wrong file.

Otherwise this one seems pretty good, though again, I would appreciate
further reviews by others.

Tom

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

* [PATCH 2/4] Add symbol-handling callbacks to the jit-reader interface.
  2011-07-05  6:27 [PATCH 1/4] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
@ 2011-07-05  6:27 ` Sanjoy Das
  0 siblings, 0 replies; 8+ messages in thread
From: Sanjoy Das @ 2011-07-05  6:27 UTC (permalink / raw)
  To: gdb-patches; +Cc: Sanjoy Das

Implements the callbacks in `struct gdbjit_symtab_callbacks'. This
will allow the JIT debug info reader to be able to parse the debug
info and actually push the symbols into GDB.

gdb/ChangeLog:

	* jit.c: Add symbol handling callbacks for the JIT reader
interface.
---
 gdb/ChangeLog |    4 +
 gdb/jit.c     |  522 +++++++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 475 insertions(+), 51 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 3ad44e8..38b83f1 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,9 @@
 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>
+
 	* jit-reader.h: Add (new) JIT reader interface.
 	* jit.c: Include jit-reader.h in source file
 	* Makefile.in: Add jit-reader.h to HFILES_NO_SRCDIR.
diff --git a/gdb/jit.c b/gdb/jit.c
index 4ce5814..f8810a8 100644
--- a/gdb/jit.c
+++ b/gdb/jit.c
@@ -21,8 +21,10 @@
 
 #include "jit.h"
 #include "jit-reader.h"
+#include "block.h"
 #include "breakpoint.h"
 #include "command.h"
+#include "dictionary.h"
 #include "gdbcmd.h"
 #include "gdbcore.h"
 #include "inferior.h"
@@ -31,8 +33,11 @@
 #include "symfile.h"
 #include "symtab.h"
 #include "target.h"
+#include "gdb_dirent.h"
 #include "gdb_stat.h"
 
+#include <dlfcn.h>
+
 static const struct objfile_data *jit_objfile_data;
 
 static const char *const jit_break_name = "__jit_debug_register_code";
@@ -45,6 +50,115 @@ static const struct inferior_data *jit_inferior_data = NULL;
 
 static int jit_debug = 0;
 
+/* The environment variable we poke at to get the file name of the shared object
+   to load. */
+
+static const char *jit_dbg_reader_so_name_env = "JIT_DEBUG_READER";
+
+/* Symbols (and the corresponding types) to be loaded from the shared object
+   supposed to do the debug-info parsing. */
+typedef int (jit_init_reader_fn) (void **);
+static const char *jit_init_reader_sym = "gdbjit_init_reader";
+
+typedef int (jit_read_debug_info_fn) (void *, struct gdbjit_symtab_callbacks *,
+                                      void *mem, long sz);
+static const char *jit_read_debug_info_sym = "gdbjit_read_debug_info";
+
+typedef int (jit_unwind_frame_fn) (void *, struct gdbjit_unwind_callbacks *);
+static const char *jit_unwind_frame_sym = "gdbjit_unwind_frame";
+
+typedef void (jit_destroy_reader_fn) (void *);
+static const char *jit_destroy_reader_sym = "gdbjit_destroy_reader";
+
+typedef struct gdbjit_frame_id (jit_get_frame_id_fn) (void *, struct
+                                                     gdbjit_unwind_callbacks *);
+static const char *jit_get_frame_id_sym = "gdbjit_get_frame_id";
+
+static const char *jit_so_api_version_sym = "__gdbjit_so_api_version";
+
+struct jit_dbg_reader
+{
+  jit_init_reader_fn *init;
+  jit_read_debug_info_fn *read;
+  jit_unwind_frame_fn *unwind;
+  jit_get_frame_id_fn *get_frame_id;
+  jit_destroy_reader_fn *destroy;
+
+  void *handle, *private_data;
+};
+
+static struct jit_dbg_reader *
+jit_dbg_reader_load (void)
+{
+  /* Check if a reader has been provided. */
+  const char *file_name = getenv (jit_dbg_reader_so_name_env);
+  void *handle = NULL;
+  int (*jit_reader_so_api_version) (void) = NULL;
+  struct jit_dbg_reader *reader = NULL;
+  
+  if (file_name == NULL)
+    return NULL;
+
+  handle = dlopen (file_name, RTLD_NOW);
+  if (!handle)
+    goto cleanup;
+
+  reader = XZALLOC (struct jit_dbg_reader);
+  reader->init = dlsym (handle, jit_init_reader_sym);
+  reader->handle = handle;
+
+  if (!reader->init)
+    goto cleanup;
+
+  reader->read = dlsym (handle, jit_read_debug_info_sym);
+  if (!reader->read)
+    goto cleanup;
+
+  reader->unwind = dlsym (handle, jit_unwind_frame_sym);
+  if (!reader->unwind)
+    goto cleanup;
+
+  reader->get_frame_id = dlsym (handle, jit_get_frame_id_sym);
+  if (!reader->get_frame_id)
+    goto cleanup;
+
+  reader->destroy = dlsym (handle, jit_destroy_reader_sym);
+  if (!reader->destroy)
+    goto cleanup;
+
+  jit_reader_so_api_version = dlsym (handle, jit_so_api_version_sym);
+  if (!jit_reader_so_api_version ||
+      jit_reader_so_api_version () != GDB_JIT_INTERFACE_VERSION)
+    {
+      goto cleanup;
+    }
+
+  if (reader->init (&reader->private_data) != GDB_JIT_SUCCESS)
+    goto cleanup;
+
+  return reader;
+
+ cleanup:
+  xfree (reader);
+  if (handle)
+    {
+      dlclose (handle);
+      fprintf (stderr, "Could not load reader object %s\n", file_name);
+    }
+  else /* also report why the shared object could not be loaded */
+    fprintf (stderr, "Could not load reader object %s (%s)\n", file_name,
+             dlerror());
+  return NULL;
+}
+
+static void
+jit_dbg_reader_destroy (struct jit_dbg_reader *reader)
+{
+  if (reader)
+    reader->destroy (reader->private_data);
+  xfree (reader);
+}
+
 static void
 show_jit_debug (struct ui_file *file, int from_tty,
 		struct cmd_list_element *c, const char *value)
@@ -135,6 +249,9 @@ struct jit_inferior_data
 {
   CORE_ADDR breakpoint_addr;  /* &__jit_debug_register_code()  */
   CORE_ADDR descriptor_addr;  /* &__jit_debug_descriptor  */
+  struct jit_dbg_reader *reader; /* If a debug info reader was found. */
+  struct objfile *jit_objfile;   /* The object file where all the JIT symbols
+                                    will be added. */
 };
 
 /* Return jit_inferior_data for current inferior.  Allocate if not already
@@ -233,6 +350,286 @@ jit_read_code_entry (struct gdbarch *gdbarch,
       extract_unsigned_integer (&entry_buf[3 * ptr_size], 8, byte_order);
 }
 
+struct gdbjit_block
+{
+  struct gdbjit_block *next, *parent;
+  struct block *real_block;
+
+  /* The first and last code address corresponding to this block */
+  CORE_ADDR begin, end;
+  /* The name of this block (if any). If this is non-NULL, the FUNCTION symbol
+     symbol is set to this value. */
+  const char *name;
+};
+
+struct gdbjit_symtab
+{
+  struct gdbjit_block *blocks;
+  struct linetable *linetable;
+  int nblocks;
+
+  const char *file_name;
+};
+
+struct jit_dbg_reader_data
+{
+  struct objfile **objf;
+};
+
+static int
+jit_target_read_impl (void *target_mem, void *gdb_buf, int len)
+{
+  return target_read_memory ((CORE_ADDR) target_mem, gdb_buf, len);
+}
+
+static struct gdbjit_symtab *
+jit_symtab_open_impl (struct gdbjit_symtab_callbacks *cb, const char *file_name)
+{
+  struct gdbjit_symtab *ret = XZALLOC (struct gdbjit_symtab);
+  ret->file_name = xstrdup (file_name);
+  return ret;
+}
+
+/* Returns true if the block corrensponding to old should be placed before the
+   block corrensponding to new in the final blockvector. */
+static int
+compare_block (struct gdbjit_block *old, struct gdbjit_block *new)
+{
+  if (old == NULL)
+    return 1;
+  if (old->begin < new->begin) 
+    return 1;
+  else if (old->begin == new->begin)
+    {
+      if (old->end > new->end)
+        return 1;
+      else
+        return 0;
+    }
+  else
+    return 0;
+}
+
+static struct gdbjit_block *
+jit_new_block_impl (struct gdbjit_symtab_callbacks *cb,
+                    struct gdbjit_symtab *symtab,
+                    struct gdbjit_block *parent,
+                    void *begin, void *end, const char *name)
+{
+  struct gdbjit_block *block = XZALLOC (struct gdbjit_block);
+
+  (void) cb;
+
+  block->next = symtab->blocks;
+  block->begin = (CORE_ADDR) begin;
+  block->end = (CORE_ADDR) end;
+  block->name = name ? xstrdup (name) : NULL;
+  block->parent = parent;
+
+  /* Ensure that the blocks are inserted in the correct (reverse of the order
+     expected by blockvector). */
+  if (compare_block (symtab->blocks, block))
+    {
+      symtab->blocks = block;
+    }
+  else
+    {
+      struct gdbjit_block *i = symtab->blocks;
+
+      for (;; i = i->next)
+        {
+          /* Guranteed to terminate, since compare_block (NULL, _) returns 1 */
+          if (compare_block (i->next, block))
+            {
+              block->next = i->next;
+              i->next = block;
+              break;
+            }
+        }
+    }
+  symtab->nblocks++;
+
+  return block;
+}
+
+static void
+jit_symtab_add_line_mapping_impl (struct gdbjit_symtab_callbacks *cb,
+                                  struct gdbjit_symtab *stab,
+                                  int nlines,
+                                  struct gdbjit_line_mapping *map)
+{
+  int i;
+  /* Must copy */
+  if (!nlines)
+    return;
+
+  stab->linetable = xmalloc (sizeof (struct linetable) + (nlines - 1) *
+                             sizeof (struct linetable_entry));
+  stab->linetable->nitems = nlines;
+  for (i = 0; i < nlines; i++)
+    {
+      stab->linetable->item [i].pc = (CORE_ADDR) map[i].pc;
+      stab->linetable->item [i].line = map[i].line;
+    }
+}
+
+static void
+jit_symtab_close_impl (struct gdbjit_symtab_callbacks *cb,
+                       struct gdbjit_symtab *stab)
+{
+  struct jit_dbg_reader_data *dat = cb->private;
+  struct symtab *symtab;
+  struct gdbjit_block *blck_iter, *blck_iter_tmp;
+  struct block *block_iter;
+  struct objfile *objfile;
+  int actual_nblocks = FIRST_LOCAL_BLOCK + stab->nblocks, i;
+  CORE_ADDR begin, end;
+
+  if (*dat->objf == NULL)
+    {
+       objfile = allocate_objfile (NULL, 0);
+      *dat->objf = objfile;
+      objfile->gdbarch = target_gdbarch;
+      objfile->msymbols = obstack_alloc (&objfile->objfile_obstack,
+			                                   sizeof (struct minimal_symbol));
+      objfile->msymbols[0].ginfo.name = NULL;
+      objfile->msymbols[0].ginfo.value.address = 0;
+      objfile->name = xstrdup ("JIT");
+    }
+  else
+    objfile = *dat->objf;
+  
+  symtab = allocate_symtab (stab->file_name, objfile);
+  symtab->dirname = NULL; /* JIT compilers compile in memory */
+
+  if (stab->linetable) {
+    int size = (stab->linetable->nitems - 1) * sizeof (struct linetable_entry) +
+      sizeof (struct linetable);
+    LINETABLE (symtab) = obstack_alloc (&objfile->objfile_obstack, size);
+    memcpy (LINETABLE (symtab), stab->linetable, size);
+  } else {
+    LINETABLE (symtab) = NULL;
+  }
+
+  symtab->blockvector = obstack_alloc (&objfile->objfile_obstack,
+                                       (sizeof (struct blockvector)
+                                        + (actual_nblocks - 1) *
+                                        sizeof (struct block *)));
+
+  /* (begin, end) will contain the PC range this entire blockvector spans. */
+  symtab->primary = 1;
+  BLOCKVECTOR_MAP (symtab->blockvector) = NULL;
+  begin = stab->blocks->begin;
+  end = stab->blocks->end;
+  BLOCKVECTOR_NBLOCKS (symtab->blockvector) = actual_nblocks;
+
+  for (i = (actual_nblocks - 1), blck_iter = stab->blocks;
+       i >= FIRST_LOCAL_BLOCK; i--)
+    {
+      struct block *bl = allocate_block (&objfile->objfile_obstack);
+      struct symbol *block_name = obstack_alloc (&objfile->objfile_obstack,
+                                                 sizeof (struct symbol));
+
+      BLOCK_DICT (bl) = dict_create_linear (&objfile->objfile_obstack, NULL);
+      BLOCK_START (bl) = (CORE_ADDR) blck_iter->begin;
+      BLOCK_END (bl) = (CORE_ADDR) blck_iter->end;
+      BLOCK_FUNCTION (bl) = block_name;
+
+      memset (block_name, 0, sizeof (struct symbol));
+      SYMBOL_DOMAIN (block_name) = VAR_DOMAIN;
+      SYMBOL_CLASS (block_name) = LOC_BLOCK;
+      block_name->symtab = symtab;
+      SYMBOL_BLOCK_VALUE (block_name) = bl;
+
+      block_name->ginfo.name = obstack_alloc (&objfile->objfile_obstack,
+			                                        1 + strlen (blck_iter->name));
+      strcpy (block_name->ginfo.name, blck_iter->name);
+      BLOCK_FUNCTION (bl) = block_name;
+ 
+      BLOCKVECTOR_BLOCK (symtab->blockvector, i) = bl;
+      if (begin < (long) blck_iter->begin)
+        begin = (long) blck_iter->begin;
+      if (end > (long) blck_iter->end)
+        end = (long) blck_iter->end;
+    }
+
+  blck_iter = blck_iter->next;
+
+  /* Now add the special blocks. */
+  block_iter = NULL;
+  for (i = 0; i < FIRST_LOCAL_BLOCK; i++)
+    {
+      struct block *bl = allocate_block (&objfile->objfile_obstack);
+      BLOCK_DICT (bl) = dict_create_linear (&objfile->objfile_obstack, NULL);
+      BLOCK_SUPERBLOCK (bl) = block_iter;
+      block_iter = bl;
+
+      BLOCK_START (bl) = (CORE_ADDR) begin;
+      BLOCK_END (bl) = (CORE_ADDR) end;
+
+      BLOCKVECTOR_BLOCK (symtab->blockvector, i) = bl;
+    }
+
+  /* Fill up the superblock fields. */
+  for (blck_iter = stab->blocks; blck_iter; blck_iter = blck_iter->next)
+    {
+      if (blck_iter->parent != NULL)
+        BLOCK_SUPERBLOCK (blck_iter->real_block) =
+          blck_iter->parent->real_block;
+    }
+
+  /* Free memory. */
+  blck_iter = stab->blocks;
+  for (blck_iter = stab->blocks, blck_iter_tmp = blck_iter->next;
+       blck_iter; blck_iter = blck_iter_tmp)
+    {
+      xfree (blck_iter);
+    }
+}
+
+static int
+jit_try_read_symtab (struct jit_inferior_data *inf_data,
+                     CORE_ADDR target_mem, long sz)
+{
+  void *gdb_mem;
+  int status = 0;
+  struct jit_dbg_reader *i;
+  struct jit_dbg_reader_data priv_data;
+  struct gdbjit_symtab_callbacks callbacks =
+    {
+      jit_symtab_open_impl,
+      jit_new_block_impl,
+      jit_symtab_add_line_mapping_impl,
+      jit_target_read_impl,
+      jit_symtab_close_impl,
+
+      &priv_data
+    };
+
+  if (!inf_data->reader)
+    return 0;
+
+  priv_data.objf = &inf_data->jit_objfile;
+
+  gdb_mem = xmalloc (sz);
+  if (target_read_memory (target_mem, gdb_mem, sz))
+    {
+      status = 0;
+      goto cleanup;
+    }
+
+  if (inf_data->reader->read (inf_data->reader->private_data, &callbacks,
+                              gdb_mem, sz) == GDB_JIT_SUCCESS)
+    {
+      status = 1;
+      goto cleanup;
+    }
+
+ cleanup:
+  xfree (gdb_mem);
+  return status;
+}
+
 /* This function registers code associated with a JIT code entry.  It uses the
    pointer and size pair in the entry to read the symbol file from the remote
    and then calls symbol_file_add_from_local_memory to add it as though it were
@@ -242,14 +639,9 @@ static void
 jit_register_code (struct gdbarch *gdbarch,
 		   CORE_ADDR entry_addr, struct jit_code_entry *code_entry)
 {
-  bfd *nbfd;
-  struct section_addr_info *sai;
-  struct bfd_section *sec;
   struct objfile *objfile;
-  struct cleanup *old_cleanups, *my_cleanups;
-  int i;
-  const struct bfd_arch_info *b;
-  CORE_ADDR *entry_addr_ptr;
+  int i, success;
+  struct jit_inferior_data *inf_data = get_jit_inferior_data ();
 
   if (jit_debug)
     fprintf_unfiltered (gdb_stdlog,
@@ -258,53 +650,68 @@ jit_register_code (struct gdbarch *gdbarch,
 			paddress (gdbarch, code_entry->symfile_addr),
 			pulongest (code_entry->symfile_size));
 
-  nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
-                                      code_entry->symfile_size, gnutarget);
-  old_cleanups = make_cleanup_bfd_close (nbfd);
-
-  /* Check the format.  NOTE: This initializes important data that GDB uses!
-     We would segfault later without this line.  */
-  if (!bfd_check_format (nbfd, bfd_object))
+  if (jit_try_read_symtab (inf_data, code_entry->symfile_addr,
+                           code_entry->symfile_size))
     {
-      printf_unfiltered (_("\
+      objfile = inf_data->jit_objfile;
+    }
+  else
+    {
+      struct cleanup *old_cleanups;
+      bfd *nbfd;
+      struct section_addr_info *sai;
+      struct bfd_section *sec;
+      const struct bfd_arch_info *b;
+      CORE_ADDR *entry_addr_ptr;
+      
+      nbfd = bfd_open_from_target_memory (code_entry->symfile_addr,
+                                          code_entry->symfile_size, gnutarget);
+      old_cleanups = make_cleanup_bfd_close (nbfd);
+
+      /* Check the format.  NOTE: This initializes important data that GDB uses!
+         We would segfault later without this line.  */
+      if (!bfd_check_format (nbfd, bfd_object))
+        {
+          printf_unfiltered (_("\
 JITed symbol file is not an object file, ignoring it.\n"));
-      do_cleanups (old_cleanups);
-      return;
+          do_cleanups (old_cleanups);
+          return;
+        }
+
+      /* Check bfd arch.  */
+      b = gdbarch_bfd_arch_info (gdbarch);
+      if (b->compatible (b, bfd_get_arch_info (nbfd)) != b)
+        warning (_("JITed object file architecture %s is not compatible "
+                   "with target architecture %s."), bfd_get_arch_info
+                 (nbfd)->printable_name, b->printable_name);
+
+      /* Read the section address information out of the symbol file.  Since the
+         file is generated by the JIT at runtime, it should all of the absolute
+         addresses that we care about.  */
+      sai = alloc_section_addr_info (bfd_count_sections (nbfd));
+      make_cleanup_free_section_addr_info (sai);
+      i = 0;
+      for (sec = nbfd->sections; sec != NULL; sec = sec->next)
+        if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
+          {
+            /* We assume that these virtual addresses are absolute, and do not
+               treat them as offsets.  */
+            sai->other[i].addr = bfd_get_section_vma (nbfd, sec);
+            sai->other[i].name = xstrdup (bfd_get_section_name (nbfd, sec));
+            sai->other[i].sectindex = sec->index;
+            ++i;
+          }
+
+      /* This call takes ownership of sai.  */
+      objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED, NULL);
+
+      /* Remember a mapping from entry_addr to objfile.  */
+      entry_addr_ptr = xmalloc (sizeof (CORE_ADDR));
+      *entry_addr_ptr = entry_addr;
+      set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr);
+
+      discard_cleanups (old_cleanups);
     }
-
-  /* Check bfd arch.  */
-  b = gdbarch_bfd_arch_info (gdbarch);
-  if (b->compatible (b, bfd_get_arch_info (nbfd)) != b)
-    warning (_("JITed object file architecture %s is not compatible "
-               "with target architecture %s."), bfd_get_arch_info
-             (nbfd)->printable_name, b->printable_name);
-
-  /* Read the section address information out of the symbol file.  Since the
-     file is generated by the JIT at runtime, it should all of the absolute
-     addresses that we care about.  */
-  sai = alloc_section_addr_info (bfd_count_sections (nbfd));
-  make_cleanup_free_section_addr_info (sai);
-  i = 0;
-  for (sec = nbfd->sections; sec != NULL; sec = sec->next)
-    if ((bfd_get_section_flags (nbfd, sec) & (SEC_ALLOC|SEC_LOAD)) != 0)
-      {
-        /* We assume that these virtual addresses are absolute, and do not
-           treat them as offsets.  */
-        sai->other[i].addr = bfd_get_section_vma (nbfd, sec);
-        sai->other[i].name = xstrdup (bfd_get_section_name (nbfd, sec));
-        sai->other[i].sectindex = sec->index;
-        ++i;
-      }
-
-  /* This call takes ownership of sai.  */
-  objfile = symbol_file_add_from_bfd (nbfd, 0, sai, OBJF_SHARED, NULL);
-
-  /* Remember a mapping from entry_addr to objfile.  */
-  entry_addr_ptr = xmalloc (sizeof (CORE_ADDR));
-  *entry_addr_ptr = entry_addr;
-  set_objfile_data (objfile, jit_objfile_data, entry_addr_ptr);
-
-  discard_cleanups (old_cleanups);
 }
 
 /* This function unregisters JITed code and frees the corresponding
@@ -385,6 +792,8 @@ jit_inferior_init (struct gdbarch *gdbarch)
   if (jit_breakpoint_re_set_internal (gdbarch, inf_data) != 0)
     return;
 
+  inf_data->reader = jit_dbg_reader_load ();
+
   if (inf_data->descriptor_addr == 0)
     {
       struct minimal_symbol *desc_symbol;
@@ -464,6 +873,11 @@ jit_reset_inferior_data_and_breakpoints (void)
   /* Remove any existing JIT breakpoint(s).  */
   remove_jit_event_breakpoints ();
 
+  /* Destroy JIT reader objects. */
+  jit_dbg_reader_destroy (inf_data->reader);
+  if (inf_data->jit_objfile)
+    free_objfile (inf_data->jit_objfile);
+
   jit_inferior_init (target_gdbarch);
 }
 
@@ -484,6 +898,12 @@ jit_inferior_exit_hook (struct inferior *inf)
 {
   struct objfile *objf;
   struct objfile *temp;
+  struct jit_inferior_data *inf_data;
+
+ inf_data = inferior_data (inf, jit_inferior_data);
+ jit_dbg_reader_destroy (inf_data->reader);
+ if (inf_data->jit_objfile)
+   free_objfile (inf_data->jit_objfile);
 
   ALL_OBJFILES_SAFE (objf, temp)
     if (objfile_data (objf, jit_objfile_data) != NULL)
-- 
1.7.5.3

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

end of thread, other threads:[~2011-07-14 17:55 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-13 20:24 [PATCH 1/4] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
2011-07-13 20:24 ` [PATCH 4/4] Activate JIT code unwinder for i386 Sanjoy Das
2011-07-14 17:55   ` Tom Tromey
2011-07-14 17:58     ` Sanjoy Das
2011-07-13 20:52 ` [PATCH 2/4] Add symbol-handling callbacks to the jit-reader interface Sanjoy Das
2011-07-14  1:39 ` [PATCH 3/4] Add an unwinder for JIT compiled code Sanjoy Das
2011-07-14 21:05   ` Tom Tromey
  -- strict thread matches above, loose matches on Subject: below --
2011-07-05  6:27 [PATCH 1/4] jit-reader.h: describe interface implemented by the JIT readers Sanjoy Das
2011-07-05  6:27 ` [PATCH 2/4] Add symbol-handling callbacks to the jit-reader interface Sanjoy Das

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).