2009-10-09 Zoltan Varga <vargaz@gmail.com>
authorZoltan Varga <vargaz@gmail.com>
Fri, 9 Oct 2009 14:48:04 +0000 (14:48 -0000)
committerZoltan Varga <vargaz@gmail.com>
Fri, 9 Oct 2009 14:48:04 +0000 (14:48 -0000)
* image-writer.c: Add support for emitting the image into a memory buffer.

* dwarfwriter.c: Add support for sharing one IL file between multiple images.

* aot-compiler.c: Add support for registering debug info with GDB using the
new JIT debugging interface in GDB 7.0. It can be turned on by setting
MONO_XDEBUG to 'gdb'.

svn path=/trunk/mono/; revision=143873

mono/mini/ChangeLog
mono/mini/aot-compiler.c
mono/mini/dwarfwriter.c
mono/mini/dwarfwriter.h
mono/mini/image-writer.c
mono/mini/image-writer.h
mono/mini/mini.c
mono/mini/mini.h

index 3c774a321c3a0cd2769d84938c6775f37f03bd04..074334cf7ebf555520081ad80c68fe159ddb3190 100644 (file)
@@ -1,3 +1,13 @@
+2009-10-09  Zoltan Varga  <vargaz@gmail.com>
+
+       * image-writer.c: Add support for emitting the image into a memory buffer.
+
+       * dwarfwriter.c: Add support for sharing one IL file between multiple images.
+
+       * aot-compiler.c: Add support for registering debug info with GDB using the
+       new JIT debugging interface in GDB 7.0. It can be turned on by setting
+       MONO_XDEBUG to 'gdb'.
+
 2009-10-06  Zoltan Varga  <vargaz@gmail.com>
 
        * aot-compiler.c (add_generic_class): Add an instance of GenericComparer<T> for
index f6ce0fcfe0532a9083bf14527949ebafc8a3930d..2355fc7f748a9ddfea6306276e9ebf5a87f7e922 100644 (file)
@@ -5415,7 +5415,7 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options)
        acfg->temp_prefix = img_writer_get_temp_label_prefix (acfg->w);
 
        if (!acfg->aot_opts.nodebug)
-               acfg->dwarf = mono_dwarf_writer_create (acfg->w, NULL, FALSE);
+               acfg->dwarf = mono_dwarf_writer_create (acfg->w, NULL, 0, FALSE);
 
        img_writer_emit_start (acfg->w);
 
@@ -5529,8 +5529,11 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options)
  * into the runtime to emit the shared library, which would cause all kinds of
  * complications, like threading issues, and the fact that the ELF writer's
  * emit_writeout () function cannot be called more than once.
+ * GDB 7.0 and later has a JIT interface.
  */
 
+#define USE_GDB_JIT_INTERFACE
+
 /* The recommended gdb macro is: */
 /*
   define xdb
@@ -5539,26 +5542,80 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options)
   end
 */
 
+/*
+ * GDB JIT interface definitions.
+ *
+ *     http://sources.redhat.com/gdb/onlinedocs/gdb_30.html
+ */
+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(void);
+
+void __attribute__((noinline)) __jit_debug_register_code(void) { };
+
+/* 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 };
+
 static MonoDwarfWriter *xdebug_writer;
-static FILE *xdebug_fp;
+static FILE *xdebug_fp, *il_file;
+static gboolean use_gdb_interface, save_symfiles;
+static int il_file_line_index;
 
 void
-mono_xdebug_init (void)
+mono_xdebug_init (char *options)
 {
-       FILE *il_file;
        MonoImageWriter *w;
+       char **args, **ptr;
+
+       args = g_strsplit (options, ",", -1);
+       for (ptr = args; ptr && *ptr; ptr ++) {
+               char *arg = *ptr;
+
+               if (!strcmp (arg, "gdb"))
+                       use_gdb_interface = TRUE;
+               if (!strcmp (arg, "save-symfiles"))
+                       save_symfiles = TRUE;
+       }
+
+       /* This file will contain the IL code for methods which don't have debug info */
+       il_file = fopen ("xdb.il", "w");
+
+       if (use_gdb_interface)
+               return;
 
        unlink ("xdb.s");
        xdebug_fp = fopen ("xdb.s", "w");
-
+       
        w = img_writer_create (xdebug_fp, FALSE);
 
        img_writer_emit_start (w);
 
-       /* This file will contain the IL code for methods which don't have debug info */
-       il_file = fopen ("xdb.il", "w");
-
-       xdebug_writer = mono_dwarf_writer_create (w, il_file, TRUE);
+       xdebug_writer = mono_dwarf_writer_create (w, il_file, 0, TRUE);
 
        /* Emit something so the file has a text segment */
        img_writer_emit_section_change (w, ".text", 0);
@@ -5577,13 +5634,83 @@ mono_xdebug_init (void)
 void
 mono_save_xdebug_info (MonoCompile *cfg)
 {
-       if (!xdebug_writer)
-               return;
+       if (use_gdb_interface) {
+               MonoImageWriter *w;
+               MonoDwarfWriter *dw;
+               guint8 *img;
+               guint32 img_size;
+               struct jit_code_entry *entry;
 
-       mono_loader_lock ();
-       mono_dwarf_writer_emit_method (xdebug_writer, cfg, cfg->jit_info->method, NULL, NULL, cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->method, mono_domain_get ()));
-       fflush (xdebug_fp);
-       mono_loader_unlock ();
+               w = img_writer_create (NULL, TRUE);
+
+               img_writer_emit_start (w);
+
+               /* This file will contain the IL code for methods which don't have debug info */
+               if (!il_file)
+                       il_file = fopen ("xdb.il", "w");
+
+               mono_loader_lock ();
+
+               dw = mono_dwarf_writer_create (w, il_file, il_file_line_index, FALSE);
+
+               /* Emit something so the file has a text segment */
+               img_writer_emit_section_change (w, ".text", 0);
+               img_writer_emit_string (w, "");
+
+               mono_dwarf_writer_emit_base_info (dw, arch_get_cie_program ());
+               mono_dwarf_writer_emit_method (dw, cfg, cfg->jit_info->method, NULL, NULL, cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->method, mono_domain_get ()));
+               il_file_line_index = mono_dwarf_writer_get_il_file_line_index (dw);
+               mono_dwarf_writer_close (dw);
+
+               img_writer_emit_writeout (w);
+
+               img = img_writer_get_output (w, &img_size);
+
+               img_writer_destroy (w);
+
+               if (save_symfiles) {
+                       /* Save the symbol files to help debugging */
+                       FILE *fp;
+                       char *file_name;
+                       static int file_counter;
+
+                       file_counter ++;
+                       file_name = g_strdup_printf ("xdb-%d.o", file_counter);
+                       printf ("%s -> %s\n", mono_method_full_name (cfg->method, TRUE), file_name);
+
+                       fp = fopen (file_name, "w");
+                       fwrite (img, img_size, 1, fp);
+                       fclose (fp);
+                       g_free (file_name);
+               }
+
+               /* Register the image with GDB */
+
+               entry = g_malloc (sizeof (struct jit_code_entry));
+
+               entry->symfile_addr = (const char*)img;
+               entry->symfile_size = img_size;
+
+               entry->next_entry = __jit_debug_descriptor.first_entry;
+               if (__jit_debug_descriptor.first_entry)
+                       __jit_debug_descriptor.first_entry->prev_entry = entry;
+               __jit_debug_descriptor.first_entry = entry;
+
+               __jit_debug_descriptor.relevant_entry = entry;
+               __jit_debug_descriptor.action_flag = JIT_REGISTER_FN;
+
+               __jit_debug_register_code ();
+               
+               mono_loader_unlock ();
+       } else {
+               if (!xdebug_writer)
+                       return;
+
+               mono_loader_lock ();
+               mono_dwarf_writer_emit_method (xdebug_writer, cfg, cfg->jit_info->method, NULL, NULL, cfg->jit_info->code_start, cfg->jit_info->code_size, cfg->args, cfg->locals, cfg->unwind_ops, mono_debug_find_method (cfg->jit_info->method, mono_domain_get ()));
+               fflush (xdebug_fp);
+               mono_loader_unlock ();
+       }
 }
 
 /*
index 66bed76adfc73cfec9d0f9a6467d7f8433e9e117..e3de95a697d192b2bb77e0cafe075f16ec0d9370 100644 (file)
@@ -57,7 +57,7 @@ struct _MonoDwarfWriter
  * debug information.
  */
 MonoDwarfWriter*
-mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, gboolean appending)
+mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, int il_file_start_line, gboolean appending)
 {
        MonoDwarfWriter *w = g_new0 (MonoDwarfWriter, 1);
        
@@ -72,6 +72,7 @@ mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, gboolean appen
 
        w->w = writer;
        w->il_file = il_file;
+       w->il_file_line_index = il_file_start_line;
        w->appending = appending;
 
        if (appending)
@@ -89,6 +90,12 @@ mono_dwarf_writer_destroy (MonoDwarfWriter *w)
        g_free (w);
 }
 
+int
+mono_dwarf_writer_get_il_file_line_index (MonoDwarfWriter *w)
+{
+       return w->il_file_line_index;
+}
+
 /* Wrappers around the image writer functions */
 
 static inline void
index 319cc38acc639834c034fbe01f41065fced3ed07..a6b30ec25138f0e987c6dbb9c7105318271b4bbf 100644 (file)
@@ -20,7 +20,7 @@
 
 typedef struct _MonoDwarfWriter MonoDwarfWriter;
 
-MonoDwarfWriter* mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, gboolean appending) MONO_INTERNAL;
+MonoDwarfWriter* mono_dwarf_writer_create (MonoImageWriter *writer, FILE *il_file, int il_file_start_line, gboolean appending) MONO_INTERNAL;
 
 void mono_dwarf_writer_destroy (MonoDwarfWriter *w) MONO_INTERNAL;
 
@@ -28,6 +28,8 @@ void mono_dwarf_writer_emit_base_info (MonoDwarfWriter *w, GSList *base_unwind_p
 
 void mono_dwarf_writer_close (MonoDwarfWriter *w) MONO_INTERNAL;
 
+int mono_dwarf_writer_get_il_file_line_index (MonoDwarfWriter *w) MONO_INTERNAL;
+
 void mono_dwarf_writer_emit_trampoline (MonoDwarfWriter *w, const char *tramp_name, char *start_symbol, char *end_symbol, guint8 *code, guint32 code_size, GSList *unwind_info) MONO_INTERNAL;
 
 void
index 9fcbd03b6f3912dde0156b01b0a04fe6de72ed98..5b294676e6a997fce7107f5bd0d0bcb0fe206498 100644 (file)
@@ -152,6 +152,8 @@ struct _MonoImageWriter {
        BinReloc *relocations;
        GHashTable *labels;
        int num_relocs;
+       guint8 *out_buf;
+       int out_buf_size, out_buf_pos;
 #endif
        /* Asm writer */
        char *tmpfname;
@@ -990,6 +992,29 @@ resolve_relocations (MonoImageWriter *acfg)
 
 #endif /* USE_ELF_RELA */
 
+static void
+bin_writer_fwrite (MonoImageWriter *acfg, void *val, size_t size, size_t nmemb)
+{
+       if (acfg->fp)
+               fwrite (val, size, nmemb, acfg->fp);
+       else {
+               g_assert (acfg->out_buf_pos + (size * nmemb) <= acfg->out_buf_size);
+               memcpy (acfg->out_buf + acfg->out_buf_pos, val, size * nmemb);
+               acfg->out_buf_pos += (size * nmemb);
+       }
+}
+
+static void
+bin_writer_fseek (MonoImageWriter *acfg, int offset)
+{
+       if (acfg->fp)
+               fseek (acfg->fp, offset, SEEK_SET);
+       else
+               acfg->out_buf_pos = offset;
+}
+
+static int normal_sections [] = { SECT_DATA, SECT_DEBUG_FRAME, SECT_DEBUG_INFO, SECT_DEBUG_ABBREV, SECT_DEBUG_LINE, SECT_DEBUG_LOC };
+
 static int
 bin_writer_emit_writeout (MonoImageWriter *acfg)
 {
@@ -1324,63 +1349,58 @@ bin_writer_emit_writeout (MonoImageWriter *acfg)
        reloc_symbols (acfg, symtab, secth, &str_table, FALSE);
        relocs = resolve_relocations (acfg);
 
-       fwrite (&header, sizeof (header), 1, file);
-       fwrite (&progh, sizeof (progh), 1, file);
-       fwrite (hash, sizeof (int) * (hash [0] + hash [1] + 2), 1, file);
-       fwrite (dynsym, sizeof (ElfSymbol) * hash [1], 1, file);
-       fwrite (dyn_str_table.data->str, dyn_str_table.data->len, 1, file);
+       if (!acfg->fp) {
+               acfg->out_buf_size = file_offset + sizeof (secth);
+               acfg->out_buf = g_malloc (acfg->out_buf_size);
+       }
+
+       bin_writer_fwrite (acfg, &header, sizeof (header), 1);
+       bin_writer_fwrite (acfg, &progh, sizeof (progh), 1);
+       bin_writer_fwrite (acfg, hash, sizeof (int) * (hash [0] + hash [1] + 2), 1);
+       bin_writer_fwrite (acfg, dynsym, sizeof (ElfSymbol) * hash [1], 1);
+       bin_writer_fwrite (acfg, dyn_str_table.data->str, dyn_str_table.data->len, 1);
        /* .rel.dyn */
-       fseek (file, secth [SECT_REL_DYN].sh_offset, SEEK_SET);
-       fwrite (relocs, sizeof (ElfReloc), acfg->num_relocs, file);
+       bin_writer_fseek (acfg, secth [SECT_REL_DYN].sh_offset);
+       bin_writer_fwrite (acfg, relocs, sizeof (ElfReloc), acfg->num_relocs);
 
        /* .rela.dyn */
-       fseek (file, secth [SECT_RELA_DYN].sh_offset, SEEK_SET);
-       fwrite (relocs, secth [SECT_RELA_DYN].sh_size, 1, file);
+       bin_writer_fseek (acfg, secth [SECT_RELA_DYN].sh_offset);
+       bin_writer_fwrite (acfg, relocs, secth [SECT_RELA_DYN].sh_size, 1);
 
        /* .text */
        if (sections [SECT_TEXT]) {
-               fseek (file, secth [SECT_TEXT].sh_offset, SEEK_SET);
-               fwrite (sections [SECT_TEXT]->data, sections [SECT_TEXT]->cur_offset, 1, file);
+               bin_writer_fseek (acfg, secth [SECT_TEXT].sh_offset);
+               bin_writer_fwrite (acfg, sections [SECT_TEXT]->data, sections [SECT_TEXT]->cur_offset, 1);
        }
        /* .dynamic */
-       fwrite (dynamic, sizeof (dynamic), 1, file);
+       bin_writer_fwrite (acfg, dynamic, sizeof (dynamic), 1);
 
        /* .got.plt */
        size = secth [SECT_DYNAMIC].sh_addr;
-       fwrite (&size, sizeof (size), 1, file);
-
-       /* .data */
-       if (sections [SECT_DATA]) {
-               fseek (file, secth [SECT_DATA].sh_offset, SEEK_SET);
-               fwrite (sections [SECT_DATA]->data, sections [SECT_DATA]->cur_offset, 1, file);
+       bin_writer_fwrite (acfg, &size, sizeof (size), 1);
+
+       /* normal sections */
+       for (i = 0; i < sizeof (normal_sections) / sizeof (normal_sections [0]); ++i) {
+               int sect = normal_sections [i];
+               if (sections [sect]) {
+                       bin_writer_fseek (acfg, secth [sect].sh_offset);
+                       bin_writer_fwrite (acfg, sections [sect]->data, sections [sect]->cur_offset, 1);
+               }
        }
 
-       fseek (file, secth [SECT_DEBUG_FRAME].sh_offset, SEEK_SET);
-       if (sections [SECT_DEBUG_FRAME])
-               fwrite (sections [SECT_DEBUG_FRAME]->data, sections [SECT_DEBUG_FRAME]->cur_offset, 1, file);
-       fseek (file, secth [SECT_DEBUG_INFO].sh_offset, SEEK_SET);
-       if (sections [SECT_DEBUG_INFO])
-               fwrite (sections [SECT_DEBUG_INFO]->data, sections [SECT_DEBUG_INFO]->cur_offset, 1, file);
-       fseek (file, secth [SECT_DEBUG_ABBREV].sh_offset, SEEK_SET);
-       if (sections [SECT_DEBUG_ABBREV])
-               fwrite (sections [SECT_DEBUG_ABBREV]->data, sections [SECT_DEBUG_ABBREV]->cur_offset, 1, file);
-       fseek (file, secth [SECT_DEBUG_LINE].sh_offset, SEEK_SET);
-       if (sections [SECT_DEBUG_LINE])
-               fwrite (sections [SECT_DEBUG_LINE]->data, sections [SECT_DEBUG_LINE]->cur_offset, 1, file);
-       fseek (file, secth [SECT_DEBUG_LINE].sh_offset, SEEK_SET);
-       if (sections [SECT_DEBUG_LOC])
-               fwrite (sections [SECT_DEBUG_LOC]->data, sections [SECT_DEBUG_LOC]->cur_offset, 1, file);
-       fseek (file, secth [SECT_SHSTRTAB].sh_offset, SEEK_SET);
-       fwrite (sh_str_table.data->str, sh_str_table.data->len, 1, file);
-       fseek (file, secth [SECT_SYMTAB].sh_offset, SEEK_SET);
-       fwrite (symtab, sizeof (ElfSymbol) * num_local_syms, 1, file);
-       fseek (file, secth [SECT_STRTAB].sh_offset, SEEK_SET);
-       fwrite (str_table.data->str, str_table.data->len, 1, file);
+       bin_writer_fseek (acfg, secth [SECT_SHSTRTAB].sh_offset);
+       bin_writer_fwrite (acfg, sh_str_table.data->str, sh_str_table.data->len, 1);
+       bin_writer_fseek (acfg, secth [SECT_SYMTAB].sh_offset);
+       bin_writer_fwrite (acfg, symtab, sizeof (ElfSymbol) * num_local_syms, 1);
+       bin_writer_fseek (acfg, secth [SECT_STRTAB].sh_offset);
+       bin_writer_fwrite (acfg, str_table.data->str, str_table.data->len, 1);
        /*g_print ("file_offset %d vs %d\n", file_offset, ftell (file));*/
        /*g_assert (file_offset >= ftell (file));*/
-       fseek (file, file_offset, SEEK_SET);
-       fwrite (&secth, sizeof (secth), 1, file);
-       fclose (file);
+       bin_writer_fseek (acfg, file_offset);
+       bin_writer_fwrite (acfg, &secth, sizeof (secth), 1);
+
+       if (acfg->fp)
+               fclose (acfg->fp);
 
        return 0;
 }
@@ -1957,6 +1977,30 @@ img_writer_emit_unset_mode (MonoImageWriter *acfg)
                asm_writer_emit_unset_mode (acfg);
 }
 
+/*
+ * img_writer_get_output:
+ *
+ *   Return the output buffer of a binary writer emitting to memory. The returned memory
+ * is from malloc, and it is owned by the caller.
+ */
+guint8*
+img_writer_get_output (MonoImageWriter *acfg, guint32 *size)
+{
+#ifdef USE_BIN_WRITER
+       guint8 *buf;
+
+       g_assert (acfg->use_bin_writer);
+
+       buf = acfg->out_buf;
+       *size = acfg->out_buf_size;
+       acfg->out_buf = NULL;
+       return buf;
+#else
+       g_assert_not_reached ();
+       return NULL;
+#endif
+}
+
 /*
  * Return whenever the binary writer is supported on this platform.
  */
@@ -1970,6 +2014,13 @@ bin_writer_supported (void)
 #endif
 }
 
+/*
+ * img_writer_create:
+ *
+ *   Create an image writer writing to FP. If USE_BIN_WRITER is TRUE, FP can be NULL,
+ * in this case the image writer will write to a memory buffer obtainable by calling
+ * img_writer_get_output ().
+ */
 MonoImageWriter*
 img_writer_create (FILE *fp, gboolean use_bin_writer)
 {
@@ -1979,6 +2030,9 @@ img_writer_create (FILE *fp, gboolean use_bin_writer)
        g_assert (!use_bin_writer);
 #endif
 
+       if (!use_bin_writer)
+               g_assert (fp);
+
        w->fp = fp;
        w->use_bin_writer = use_bin_writer;
        w->mempool = mono_mempool_new ();
index 01a2d1853dd737c64e350bee0f0621e4e3460168..1b12ac4c2d60abaeb7ab0975c7cb58a0a9a605d4 100644 (file)
@@ -36,6 +36,8 @@ void img_writer_emit_start (MonoImageWriter *w) MONO_INTERNAL;
 
 int img_writer_emit_writeout (MonoImageWriter *w) MONO_INTERNAL;
 
+guint8* img_writer_get_output (MonoImageWriter *acfg, guint32 *size) MONO_INTERNAL;
+
 void img_writer_emit_section_change (MonoImageWriter *w, const char *section_name, int subsection_index) MONO_INTERNAL;
 
 void img_writer_emit_push_section (MonoImageWriter *w, const char *section_name, int subsection) MONO_INTERNAL;
index 4bf6f6ce471e49888dfcb6bc7d115b52f0d14a2a..7773f9ac57a2f3d091364dbc37d82dec3e354d09 100644 (file)
@@ -4981,7 +4981,8 @@ mini_init (const char *filename, const char *runtime_version)
        mini_gc_init ();
 
        if (getenv ("MONO_XDEBUG")) {
-               mono_xdebug_init ();
+               char *xdebug_opts = getenv ("MONO_XDEBUG");
+               mono_xdebug_init (xdebug_opts);
                /* So methods for multiple domains don't have the same address */
                mono_dont_free_domains = TRUE;
                mono_using_xdebug = TRUE;
index e32eb1a3fe4d1684626a1fc572ce978f8800a2fd..99d20a14e2684018e60f4a90b21415058074a091 100644 (file)
@@ -1436,7 +1436,7 @@ void     mono_aot_register_globals          (gpointer *globals);
 /* This too */
 void     mono_aot_register_module           (gpointer *aot_info);
 
-void     mono_xdebug_init                   (void) MONO_INTERNAL;
+void     mono_xdebug_init                   (char *xdebug_opts) MONO_INTERNAL;
 void     mono_save_xdebug_info              (MonoCompile *cfg) MONO_INTERNAL;
 void     mono_save_trampoline_xdebug_info   (const char *tramp_name, guint8 *code, guint32 code_size, GSList *unwind_info) MONO_INTERNAL;
 /* This is an exported function */