Debugging with GDB: JIT Interface

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 29. JIT Compilation Interface

This chapter documents GDB's just-in-time (JIT) compilation interface. A JIT compiler is a program or library that generates native executable code at runtime and executes it, usually in order to achieve good performance while maintaining platform independence.

Programs that use JIT compilation are normally difficult to debug because portions of their code are generated at runtime, instead of being loaded from object files, which is where GDB normally finds the program's symbols and debug information. In order to debug programs that use JIT compilation, GDB has an interface that allows the program to register in-memory symbol files with GDB at runtime.

If you are using GDB to debug a program that uses this interface, then it should work transparently so long as you have not stripped the binary. If you are developing a JIT compiler, then the interface is documented in the rest of this chapter. At this time, the only known client of this interface is the LLVM JIT.

Broadly speaking, the JIT interface mirrors the dynamic loader interface. The JIT compiler communicates with GDB by writing data into a global variable and calling a fuction at a well-known symbol. When GDB attaches, it reads a linked list of symbol files from the global variable to find existing code, and puts a breakpoint in the function so that it can find out about additional code.

> 29.1 JIT Declarations |  | Relevant C struct declarations
> 29.2 Registering Code |  | Steps to register code
> 29.3 Unregistering Code |  | Steps to unregister code
> 29.4 Custom Debug Info |  | Emit debug information in a custom format

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 29.1 JIT Declarations

These are the relevant struct declarations that a C program should include to implement the interface:

    typedef enum
    {
      JIT_NOACTION = 0,
      JIT_REGISTER_FN,
      JIT_UNREGISTER_FN
    } jit_actions_t;

    struct jit_code_entry
    {
      struct jit_code_entry *next_entry;
      struct jit_code_entry *prev_entry;
      const char *symfile_addr;
      uint64_t symfile_size;
    };

    struct jit_descriptor
    {
      uint32_t version;
      /* This type should be jit_actions_t, but we use uint32_t
         to be explicit about the bitwidth.  */
      uint32_t action_flag;
      struct jit_code_entry *relevant_entry;
      struct jit_code_entry *first_entry;
    };

    /* GDB puts a breakpoint in this function.  */
    void __attribute__((noinline)) __jit_debug_register_code() { };

    /* Make sure to specify the version statically, because the
       debugger may check the version before we can set it.  */
    struct jit_descriptor __jit_debug_descriptor = { 1, 0, 0, 0 };

If the JIT is multi-threaded, then it is important that the JIT synchronize any modifications to this global data properly, which can easily be done by putting a global mutex around modifications to these structures.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 29.2 Registering Code

To register code with GDB, the JIT should follow this protocol:

- Generate an object file in memory with symbols and other desired debug information. The file must include the virtual addresses of the sections.

- Create a code entry for the file, which gives the start and size of the symbol file.

- Add it to the linked list in the JIT descriptor.

- Point the relevant_entry field of the descriptor at the entry.

- Set `action_flag` to `JIT_REGISTER` and call `__jit_debug_register_code`.

When GDB is attached and the breakpoint fires, GDB uses the `relevant_entry` pointer so it doesn't have to walk the list looking for new code. However, the linked list must still be maintained in order to allow GDB to attach to a running process and still find the symbol files.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 29.3 Unregistering Code

If code is freed, then the JIT should use the following protocol:

- Remove the code entry corresponding to the code from the linked list.

- Point the `relevant_entry` field of the descriptor at the code entry.

- Set `action_flag` to `JIT_UNREGISTER` and call `__jit_debug_register_code`.

If the JIT frees or recompiles code without unregistering it, then GDB and the JIT will leak the memory used for the associated symbol files.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 29.4 Custom Debug Info

Generating debug information in platform-native file formats (like ELF or COFF) may be an overkill for JIT compilers; especially if all the debug info is used for is displaying a meaningful backtrace. The issue can be resolved by having the JIT writers decide on a debug info format and also provide a reader that parses the debug info generated by the JIT compiler. This section gives a brief overview on writing such a parser. More specific details can be found in the source file ``gdb/jit-reader.in'`, which is also installed as a header at ``includedir/gdb/jit-reader.h'` for easy inclusion.

The reader is implemented as a shared object (so this functionality is not available on platforms which don't allow loading shared objects at runtime). Two GDB commands, `jit-reader-load` and `jit-reader-unload` are provided, to be used to load and unload the readers from a preconfigured directory. Once loaded, the shared object is used the parse the debug information emitted by the JIT compiler.

> 29.4.1 Using JIT Debug Info Readers |  | How to use supplied readers correctly
> 29.4.2 Writing JIT Debug Info Readers |  | Creating a debug-info reader

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 29.4.1 Using JIT Debug Info Readers

Readers can be loaded and unloaded using the `jit-reader-load` and `jit-reader-unload` commands.

`jit-reader-load reader`
  Load the JIT reader named reader. reader is a shared object specified as either an absolute or a relative file name. In the latter case, GDB will try to load the reader from a pre-configured directory, usually ``libdir/gdb/'` on a UNIX system (here libdir is the system library directory, often ``/usr/local/lib'`).

  Only one reader can be active at a time; trying to load a second reader when one is already loaded will result in GDB reporting an error. A new JIT reader can be loaded by first unloading the current one using `jit-reader-unload` and then invoking `jit-reader-load`.

`jit-reader-unload`
  Unload the currently loaded JIT reader.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 29.4.2 Writing JIT Debug Info Readers

As mentioned, a reader is essentially a shared object conforming to a certain ABI. This ABI is described in ``jit-reader.h'`.

``jit-reader.h'` defines the structures, macros and functions required to write a reader. It is installed (along with GDB), in ``includedir/gdb'` where includedir is the system include directory.

Readers need to be released under a GPL compatible license. A reader can be declared as released under such a license by placing the macro `GDB_DECLARE_GPL_COMPATIBLE_READER` in a source file.

The entry point for readers is the symbol `gdb_init_reader`, which is expected to be a function with the prototype

    extern struct gdb_reader_funcs *gdb_init_reader (void);

`struct gdb_reader_funcs` contains a set of pointers to callback functions. These functions are executed to read the debug info generated by the JIT compiler (`read`), to unwind stack frames (`unwind`) and to create canonical frame IDs (`get_Frame_id`). It also has a callback that is called when the reader is being unloaded (`destroy`). The struct looks like this

    struct gdb_reader_funcs
    {
      /* Must be set to GDB_READER_INTERFACE_VERSION.  */
      int reader_version;

      /* For use by the reader.  */
      void *priv_data;

      gdb_read_debug_info *read;
      gdb_unwind_frame *unwind;
      gdb_get_frame_id *get_frame_id;
      gdb_destroy_reader *destroy;
    };

The callbacks are provided with another set of callbacks by GDB to do their job. For `read`, these callbacks are passed in a `struct gdb_symbol_callbacks` and for `unwind` and `get_frame_id`, in a `struct gdb_unwind_callbacks`. `struct gdb_symbol_callbacks` has callbacks to create new object files and new symbol tables inside those object files. `struct gdb_unwind_callbacks` has callbacks to read registers off the current frame and to write out the values of the registers in the previous frame. Both have a callback (`target_read`) to read bytes off the target's address space.

[ << ] | [ >> ] |  |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

This document was generated by GNAT Mailserver on April, 17 2014 using texi2html