2009-07-01 Zoltan Varga <vargaz@gmail.com>
authorZoltan Varga <vargaz@gmail.com>
Mon, 2 Nov 2009 15:44:56 +0000 (15:44 -0000)
committerZoltan Varga <vargaz@gmail.com>
Mon, 2 Nov 2009 15:44:56 +0000 (15:44 -0000)
Merge the soft debugger branch.

* debugger-agent.h debugger-agent.c: New files containing the soft
mode debugger module.

* method-to-ir.c (mono_method_to_ir): Generate OP_SEQ_POINT opcodes
at the appropriate locations.

* mini-<ARCH>.c (mono_arch_output_basic_block): Handle OP_SEQ_POINT
opcode.

* mini-<ARCH>.c: Add new arch-specific functions to set/clear breakpoints,
enable/disable single stepping.

* exceptions-<ARCH>.c (mono_arch_find_jit_info_ext): New stack unwinding api
which returns all information in a StackFrameInfo structure, and can handle the
LMF frames added by the debugger.

* mini-<ARCH>.h (MonoLMFExt): New structure containing additional information
about an LMF frame.

* mini-exceptions.c (mono_jit_walk_stack_from_ctx_in_thread): New stack
walker function which works on a specific thread and passes a StackFrameInfo
structure to its callback.

* mini.c (mini_init): Initialize the debugger agent.

* aot-compiler.c aot-runtime.c: Add soft-debug support.

* mini-ops.h: Add OP_SEQ_POINT opcode.

* driver.c (mono_main): Add new '--debugger-agent' option for passing
arguments to the debugger agent.

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

30 files changed:
mono/mini/ChangeLog
mono/mini/Makefile.am
mono/mini/TestDriver.cs
mono/mini/aot-compiler.c
mono/mini/aot-runtime.c
mono/mini/cpu-amd64.md
mono/mini/cpu-arm.md
mono/mini/cpu-x86.md
mono/mini/debug-mini.c
mono/mini/debugger-agent.c [new file with mode: 0644]
mono/mini/debugger-agent.h [new file with mode: 0644]
mono/mini/driver.c
mono/mini/exceptions-amd64.c
mono/mini/exceptions-arm.c
mono/mini/exceptions-x86.c
mono/mini/ir-emit.h
mono/mini/method-to-ir.c
mono/mini/mini-amd64.c
mono/mini/mini-amd64.h
mono/mini/mini-arm.c
mono/mini/mini-arm.h
mono/mini/mini-exceptions.c
mono/mini/mini-ops.h
mono/mini/mini-posix.c
mono/mini/mini-x86.c
mono/mini/mini-x86.h
mono/mini/mini.c
mono/mini/mini.h
mono/mini/optflags-def.h
mono/mini/patch-info.h

index 57011d2db66fbbf24eb4cfd0f2fa53b90a63a9cd..bc0bd721c90fa34c1c883369eac3064bd0a7db0e 100644 (file)
@@ -1,3 +1,39 @@
+2009-07-01  Zoltan Varga  <vargaz@gmail.com>
+
+       Merge the soft debugger branch.
+
+       * debugger-agent.h debugger-agent.c: New files containing the soft
+       mode debugger module.
+
+       * method-to-ir.c (mono_method_to_ir): Generate OP_SEQ_POINT opcodes
+       at the appropriate locations.
+
+       * mini-<ARCH>.c (mono_arch_output_basic_block): Handle OP_SEQ_POINT
+       opcode.
+
+       * mini-<ARCH>.c: Add new arch-specific functions to set/clear breakpoints,
+       enable/disable single stepping.
+
+       * exceptions-<ARCH>.c (mono_arch_find_jit_info_ext): New stack unwinding api
+       which returns all information in a StackFrameInfo structure, and can handle the
+       LMF frames added by the debugger.
+
+       * mini-<ARCH>.h (MonoLMFExt): New structure containing additional information
+       about an LMF frame.
+
+       * mini-exceptions.c (mono_jit_walk_stack_from_ctx_in_thread): New stack
+       walker function which works on a specific thread and passes a StackFrameInfo
+       structure to its callback.
+
+       * mini.c (mini_init): Initialize the debugger agent.
+
+       * aot-compiler.c aot-runtime.c: Add soft-debug support.
+
+       * mini-ops.h: Add OP_SEQ_POINT opcode.
+
+       * driver.c (mono_main): Add new '--debugger-agent' option for passing
+       arguments to the debugger agent.
+
 2009-11-02  Zoltan Varga  <vargaz@gmail.com>
 
        * mini.c (mini_method_compile): Disable llvm for methods with an lmf here to
index cac21a25dd883b68e6c5adc0cb2079694a40fe24..b9d5b5857e43b64a720a98c2055fa994a67edf89 100644 (file)
@@ -284,7 +284,9 @@ common_sources = \
        dwarfwriter.h   \
        dwarfwriter.c   \
        mini-gc.h               \
-       mini-gc.c
+       mini-gc.c               \
+       debugger-agent.h \
+       debugger-agent.c
 
 test_sources =                         \
        basic-calls.cs          \
@@ -598,3 +600,6 @@ version.h: Makefile
 patch-libtool:
        sed -e 's,if (for obj in $$oldobjs,if (for obj in "",g' < ../../libtool > 2; mv 2 ../../libtool
        chmod a+x ../../libtool
+
+tags:
+       etags -o TAGS `find .. -name "*.h" -o -name "*.c"`
index 71f9de906fec8b054dd37072bc98da849d4cc024..7edb0ea825291cd22c11503eface9d631aad84f1 100644 (file)
@@ -53,7 +53,7 @@ public class TestDriver {
                for (int iter = 0; iter < iterations; ++iter) {
                        for (i = 0; i < methods.Length; ++i) {
                                name = methods [i].Name;
-                               if (!name.StartsWith ("test_"))
+                               if (!name.StartsWith ("test_", StringComparison.Ordinal))
                                        continue;
                                if (new_args.Count > 0) {
                                        bool found = false;
index 98bc977f3980a0fc0f03ec06127840d7c217c5d4..527cff080970616fb10b51b6736ac0e80bd10ba3 100644 (file)
@@ -96,6 +96,7 @@ typedef struct MonoAotOptions {
        gboolean asm_only;
        gboolean asm_writer;
        gboolean nodebug;
+       gboolean soft_debug;
        int nthreads;
        int ntrampolines;
        gboolean print_skipped_methods;
@@ -121,6 +122,7 @@ typedef struct MonoAotCompile {
        GHashTable *patch_to_plt_offset;
        GHashTable *plt_offset_to_patch;
        GHashTable *patch_to_got_offset;
+       GHashTable **patch_to_got_offset_by_type;
        GPtrArray *got_patches;
        GHashTable *image_hash;
        GHashTable *method_to_cfg;
@@ -1607,7 +1609,7 @@ get_got_offset (MonoAotCompile *acfg, MonoJumpInfo *ji)
 {
        guint32 got_offset;
 
-       got_offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->patch_to_got_offset, ji));
+       got_offset = GPOINTER_TO_UINT (g_hash_table_lookup (acfg->patch_to_got_offset_by_type [ji->type], ji));
        if (got_offset)
                return got_offset - 1;
 
@@ -1618,6 +1620,7 @@ get_got_offset (MonoAotCompile *acfg, MonoJumpInfo *ji)
        acfg->stats.got_slot_types [ji->type] ++;
 
        g_hash_table_insert (acfg->patch_to_got_offset, ji, GUINT_TO_POINTER (got_offset + 1));
+       g_hash_table_insert (acfg->patch_to_got_offset_by_type [ji->type], ji, GUINT_TO_POINTER (got_offset + 1));
        g_ptr_array_add (acfg->got_patches, ji);
 
        return got_offset;
@@ -2748,6 +2751,7 @@ encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint
        case MONO_PATCH_INFO_GENERIC_CLASS_INIT:
        case MONO_PATCH_INFO_MONITOR_ENTER:
        case MONO_PATCH_INFO_MONITOR_EXIT:
+       case MONO_PATCH_INFO_SEQ_POINT_INFO:
                break;
        default:
                g_warning ("unable to handle jump info %d", patch_info->type);
@@ -2915,7 +2919,7 @@ static void
 emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
 {
        MonoMethod *method;
-       int k, buf_size, method_index;
+       int i, k, buf_size, method_index;
        guint32 debug_info_size;
        guint8 *code;
        char symbol [128];
@@ -2924,6 +2928,7 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
        MonoJitInfo *jinfo = cfg->jit_info;
        guint32 flags;
        gboolean use_unwind_ops = FALSE;
+       GPtrArray *seq_points;
 
        method = cfg->orig_method;
        code = cfg->native_code;
@@ -2941,14 +2946,16 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
                debug_info_size = 0;
        }
 
-       buf_size = header->num_clauses * 256 + debug_info_size + 1024;
+       buf_size = header->num_clauses * 256 + debug_info_size + 1024 + (cfg->seq_points ? (cfg->seq_points->len * 16) : 0);
        p = buf = g_malloc (buf_size);
 
 #ifdef MONO_ARCH_HAVE_XP_UNWIND
        use_unwind_ops = cfg->unwind_ops != NULL;
 #endif
 
-       flags = (jinfo->has_generic_jit_info ? 1 : 0) | (use_unwind_ops ? 2 : 0) | (header->num_clauses ? 4 : 0);
+       seq_points = cfg->seq_points;
+
+       flags = (jinfo->has_generic_jit_info ? 1 : 0) | (use_unwind_ops ? 2 : 0) | (header->num_clauses ? 4 : 0) | (seq_points ? 8 : 0);
 
        encode_value (jinfo->code_size, p, &p);
        encode_value (flags, p, &p);
@@ -3008,6 +3015,22 @@ emit_exception_debug_info (MonoAotCompile *acfg, MonoCompile *cfg)
                encode_method_ref (acfg, jinfo->method, p, &p);
        }
 
+       if (seq_points) {
+               int il_offset, native_offset, last_il_offset, last_native_offset;
+
+               encode_value (seq_points->len, p, &p);
+               last_il_offset = last_native_offset = 0;
+               for (i = 0; i < seq_points->len; i += 2) {
+                       il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i));
+                       native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1));
+                       encode_value (il_offset - last_il_offset, p, &p);
+                       encode_value (native_offset - last_native_offset, p, &p);
+                       last_il_offset = il_offset;
+                       last_native_offset = native_offset;
+               }
+       }
+               
+
        g_assert (debug_info_size < buf_size);
 
        encode_value (debug_info_size, p, &p);
@@ -3506,6 +3529,8 @@ mono_aot_parse_options (const char *aot_options, MonoAotOptions *opts)
                        opts->tool_prefix = g_strdup (arg + strlen ("tool-prefix="));
                } else if (str_begins_with (arg, "autoreg")) {
                        opts->autoreg = TRUE;
+               } else if (str_begins_with (arg, "soft-debug")) {
+                       opts->soft_debug = TRUE;
                } else {
                        fprintf (stderr, "AOT : Unknown argument '%s'.\n", arg);
                        exit (1);
@@ -4164,7 +4189,11 @@ mono_aot_method_hash (MonoMethod *method)
                hashes [0] = mono_aot_str_hash (klass->name);
                hashes [1] = mono_aot_str_hash (klass->name_space);
        }
-       hashes [2] = mono_aot_str_hash (method->name);
+       if (method->wrapper_type == MONO_WRAPPER_STFLD || method->wrapper_type == MONO_WRAPPER_LDFLD || method->wrapper_type == MONO_WRAPPER_LDFLDA)
+               /* The method name includes a stringified pointer */
+               hashes [2] = 0;
+       else
+               hashes [2] = mono_aot_str_hash (method->name);
        hashes [3] = method->wrapper_type;
        hashes [4] = mono_aot_type_hash (sig->ret);
        for (i = 0; i < sig->param_count; i++) {
@@ -5047,6 +5076,21 @@ collect_methods (MonoAotCompile *acfg)
                        method = wrapper;
                }
 
+               /* FIXME: Some mscorlib methods don't have debug info */
+               /*
+               if (acfg->aot_opts.soft_debug && !method->wrapper_type) {
+                       if (!((method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL) ||
+                                 (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) ||
+                                 (method->flags & METHOD_ATTRIBUTE_ABSTRACT) ||
+                                 (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL))) {
+                               if (!mono_debug_lookup_method (method)) {
+                                       fprintf (stderr, "Method %s has no debug info, probably the .mdb file for the assembly is missing.\n", mono_method_full_name (method, TRUE));
+                                       exit (1);
+                               }
+                       }
+               }
+               */
+
                /* Since we add the normal methods first, their index will be equal to their zero based token index */
                add_method_with_index (acfg, method, i, FALSE);
                acfg->method_index ++;
@@ -5240,6 +5284,7 @@ acfg_create (MonoAssembly *ass, guint32 opts)
 {
        MonoImage *image = ass->image;
        MonoAotCompile *acfg;
+       int i;
 
        acfg = g_new0 (MonoAotCompile, 1);
        acfg->methods = g_ptr_array_new ();
@@ -5247,6 +5292,9 @@ acfg_create (MonoAssembly *ass, guint32 opts)
        acfg->plt_offset_to_patch = g_hash_table_new (NULL, NULL);
        acfg->patch_to_plt_offset = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal);
        acfg->patch_to_got_offset = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal);
+       acfg->patch_to_got_offset_by_type = g_new0 (GHashTable*, MONO_PATCH_INFO_NUM);
+       for (i = 0; i < MONO_PATCH_INFO_NUM; ++i)
+               acfg->patch_to_got_offset_by_type [i] = g_hash_table_new (mono_patch_info_hash, mono_patch_info_equal);
        acfg->got_patches = g_ptr_array_new ();
        acfg->method_to_cfg = g_hash_table_new (NULL, NULL);
        acfg->token_info_hash = g_hash_table_new_full (NULL, NULL, NULL, g_free);
@@ -5292,6 +5340,9 @@ acfg_free (MonoAotCompile *acfg)
        g_hash_table_destroy (acfg->image_hash);
        g_hash_table_destroy (acfg->unwind_info_offsets);
        g_hash_table_destroy (acfg->method_label_hash);
+       for (i = 0; i < MONO_PATCH_INFO_NUM; ++i)
+               g_hash_table_destroy (acfg->patch_to_got_offset_by_type [i]);
+       g_free (acfg->patch_to_got_offset_by_type);
        mono_mempool_destroy (acfg->mempool);
        g_free (acfg);
 }
@@ -5331,6 +5382,18 @@ mono_compile_assembly (MonoAssembly *ass, guint32 opts, const char *aot_options)
        if (acfg->aot_opts.static_link)
                acfg->aot_opts.asm_writer = TRUE;
 
+       if (acfg->aot_opts.soft_debug) {
+               MonoDebugOptions *opt = mini_get_debug_options ();
+
+               opt->mdb_optimizations = TRUE;
+               opt->gen_seq_points = TRUE;
+
+               if (mono_debug_format == MONO_DEBUG_FORMAT_NONE) {
+                       fprintf (stderr, "The soft-debug AOT option requires the --debug option.\n");
+                       return 1;
+               }
+       }
+
        load_profile_files (acfg);
 
        acfg->num_trampolines [MONO_AOT_TRAMP_SPECIFIC] = acfg->aot_opts.full_aot ? acfg->aot_opts.ntrampolines : 0;
index a605338ba291d1e91b5e887857f2ef89126956c5..646ec9881a7b838632af701bf30a10979b7cf9c6 100644 (file)
@@ -1382,7 +1382,7 @@ decode_exception_debug_info (MonoAotModule *amodule, MonoDomain *domain,
        int i, buf_len;
        MonoJitInfo *jinfo;
        guint code_len, used_int_regs, flags;
-       gboolean has_generic_jit_info, has_dwarf_unwind_info, has_clauses;
+       gboolean has_generic_jit_info, has_dwarf_unwind_info, has_clauses, has_seq_points;
        guint8 *p;
        int generic_info_size;
 
@@ -1394,6 +1394,7 @@ decode_exception_debug_info (MonoAotModule *amodule, MonoDomain *domain,
        has_generic_jit_info = (flags & 1) != 0;
        has_dwarf_unwind_info = (flags & 2) != 0;
        has_clauses = (flags & 4) != 0;
+       has_seq_points = (flags & 8) != 0;
        if (has_dwarf_unwind_info) {
                guint32 offset;
 
@@ -1463,6 +1464,30 @@ decode_exception_debug_info (MonoAotModule *amodule, MonoDomain *domain,
                jinfo->method = decode_method_ref_2 (amodule, p, &p);
        }
 
+       if (has_seq_points) {
+               GPtrArray *seq_points;
+               int il_offset, native_offset, last_il_offset, last_native_offset;
+
+               int len = decode_value (p, &p);
+
+               seq_points = g_ptr_array_new ();
+               last_il_offset = last_native_offset = 0;
+               for (i = 0; i < len; i += 2) {
+                       il_offset = last_il_offset + decode_value (p, &p);
+                       native_offset = last_native_offset + decode_value (p, &p);
+
+                       g_ptr_array_add (seq_points, GINT_TO_POINTER (il_offset));
+                       g_ptr_array_add (seq_points, GINT_TO_POINTER (native_offset));
+
+                       last_il_offset = il_offset;
+                       last_native_offset = native_offset;
+               }
+
+               mono_domain_lock (domain);
+               g_hash_table_insert (domain_jit_info (domain)->seq_points, method, seq_points);
+               mono_domain_unlock (domain);
+       }
+
        /* Load debug info */
        buf_len = decode_value (p, &p);
        mono_debug_add_aot_method (domain, method, code, p, buf_len);
@@ -1829,6 +1854,8 @@ decode_patch (MonoAotModule *aot_module, MonoMemPool *mp, MonoJumpInfo *ji, guin
                ji->data.rgctx_entry = entry;
                break;
        }
+       case MONO_PATCH_INFO_SEQ_POINT_INFO:
+               break;
        default:
                g_warning ("unhandled type %d", ji->type);
                g_assert_not_reached ();
@@ -2064,6 +2091,19 @@ load_method (MonoDomain *domain, MonoAotModule *amodule, MonoImage *image, MonoM
 
        mono_aot_unlock ();
 
+       if (mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION) {
+               MonoJitInfo *jinfo;
+
+               if (!method) {
+                       method = mono_get_method (image, token, NULL);
+                       g_assert (method);
+               }
+               mono_profiler_method_jit (method);
+               jinfo = mono_jit_info_table_find (domain, (char*)code);
+               g_assert (jinfo);
+               mono_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK);
+       }
+
        if (from_plt && klass && !klass->generic_container)
                mono_runtime_class_init (mono_class_vtable (domain, klass));
 
index c32611eea3f87267a8c3b623c970ffa4007202d3..c69f6bd8301d015d909df482a4b5f8a0bd005272 100644 (file)
@@ -58,6 +58,7 @@ jmp: len:120
 tailcall: len:120 clob:c
 br: len:6
 label: len:0
+seq_point: len:16
 
 long_add: dest:i src1:i src2:i len:3 clob:1
 long_sub: dest:i src1:i src2:i len:3 clob:1
index 902017c395f517ed987cd6e4e2d1c0d5919a16f7..b0f8c97fa04f2e56b4d79babe2c0451d9e6dc891 100644 (file)
@@ -53,6 +53,7 @@ break: len:4
 jmp: len:92
 br: len:4
 switch: src1:i len:8
+seq_point: len:38
 
 throw: src1:i len:24
 rethrow: src1:i len:20
index 2f2043f968d05b96f16729fe6e0498f1d9ad3019..38c2f96162e8530f2b5341f433045f41b7701619 100644 (file)
@@ -61,6 +61,8 @@ break: len:1
 jmp: len:32
 call: dest:a clob:c len:17
 br: len:5
+seq_point: len:16
+
 int_beq: len:6
 int_bge: len:6
 int_bgt: len:6
index 8c51e3d2ce079f44b953a521c48ca9f21ea977dc..6df93fe2541bd60190e2031c77163f9b3abc9a55 100644 (file)
@@ -306,7 +306,7 @@ mono_debug_close_method (MonoCompile *cfg)
        for (i = 0; i < jit->num_line_numbers; i++)
                jit->line_numbers [i] = g_array_index (info->line_numbers, MonoDebugLineNumberEntry, i);
 
-       debug_info = mono_debug_add_method (method, jit, cfg->domain);
+       debug_info = mono_debug_add_method (cfg->method_to_register, jit, cfg->domain);
 
        mono_debug_add_vg_method (method, jit);
 
diff --git a/mono/mini/debugger-agent.c b/mono/mini/debugger-agent.c
new file mode 100644 (file)
index 0000000..f239ed3
--- /dev/null
@@ -0,0 +1,5216 @@
+/*
+ * debugger-agent.c: Debugger back-end module
+ *
+ * Author:
+ *   Zoltan Varga (vargaz@gmail.com)
+ *
+ * (C) 2009 Novell, Inc.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <glib.h>
+
+#include <pthread.h>
+
+#include <mono/metadata/mono-debug.h>
+#include <mono/metadata/mono-debug-debugger.h>
+#include <mono/metadata/debug-mono-symfile.h>
+#include <mono/metadata/gc-internal.h>
+#include <mono/metadata/threads-types.h>
+#include <mono/utils/mono-semaphore.h>
+#include "debugger-agent.h"
+#include "mini.h"
+
+#ifndef DISABLE_DEBUGGER_AGENT
+
+typedef struct {
+       gboolean enabled;
+       char *transport;
+       char *address;
+       int log_level;
+       char *log_file;
+} AgentConfig;
+
+typedef struct
+{
+       int id;
+       guint32 il_offset;
+       MonoDomain *domain;
+       MonoMethod *method;
+       MonoContext ctx;
+       MonoDebugMethodJitInfo *jit;
+       int flags;
+} StackFrame;
+
+typedef struct
+{
+       int id;
+       guint8 *p;
+       guint8 *endp;
+       /* This is the context which needs to be restored after the invoke */
+       MonoContext ctx;
+       gboolean has_ctx;
+} InvokeData;
+
+typedef struct {
+       MonoContext ctx;
+       MonoLMF *lmf;
+       MonoDomain *domain;
+       gboolean has_context;
+       gpointer resume_event;
+       /* This is computed on demand when it is requested using the wire protocol */
+       /* It is freed up when the thread is resumed */
+       int frame_count;
+       StackFrame **frames;
+       /* 
+        * Whenever the frame info is up-to-date. If not, compute_frame_info () will need to
+        * re-compute it.
+        */
+       gboolean frames_up_to_date;
+       /* 
+        * Points to data about a pending invoke which needs to be executed after the thread
+        * resumes.
+        */
+       InvokeData *invoke;
+       /*
+        * Set to TRUE if this thread is suspended in suspend_current () or it is executing
+        * native code.
+        */
+       gboolean suspended;
+       /* Used to pass the context to the breakpoint/single step handler */
+       MonoContext handler_ctx;
+       /* Whenever thread_stop () was called for this thread */
+       gboolean terminated;
+
+       /* Number of thread interruptions not yet processed */
+       gint32 interrupt_count;
+} DebuggerTlsData;
+
+/* 
+ * Wire Protocol definitions
+ */
+
+#define HEADER_LENGTH 11
+
+#define MAJOR_VERSION 0
+#define MINOR_VERSION 2
+
+typedef enum {
+       CMD_SET_VM = 1,
+       CMD_SET_OBJECT_REF = 9,
+       CMD_SET_STRING_REF = 10,
+       CMD_SET_THREAD = 11,
+       CMD_SET_ARRAY_REF = 13,
+       CMD_SET_EVENT_REQUEST = 15,
+       CMD_SET_STACK_FRAME = 16,
+       CMD_SET_APPDOMAIN = 20,
+       CMD_SET_ASSEMBLY = 21,
+       CMD_SET_METHOD = 22,
+       CMD_SET_TYPE = 23,
+       CMD_SET_MODULE = 24,
+       CMD_SET_EVENT = 64
+} CommandSet;
+
+typedef enum {
+       EVENT_KIND_VM_START = 0,
+       EVENT_KIND_VM_DEATH = 1,
+       EVENT_KIND_THREAD_START = 2,
+       EVENT_KIND_THREAD_DEATH = 3,
+       EVENT_KIND_APPDOMAIN_CREATE = 4,
+       EVENT_KIND_APPDOMAIN_UNLOAD = 5,
+       EVENT_KIND_METHOD_ENTRY = 6,
+       EVENT_KIND_METHOD_EXIT = 7,
+       EVENT_KIND_ASSEMBLY_LOAD = 8,
+       EVENT_KIND_ASSEMBLY_UNLOAD = 9,
+       EVENT_KIND_BREAKPOINT = 10,
+       EVENT_KIND_STEP = 11,
+       EVENT_KIND_TYPE_LOAD = 12,
+       EVENT_KIND_EXCEPTION = 13
+} EventKind;
+
+typedef enum {
+       SUSPEND_POLICY_NONE = 0,
+       SUSPEND_POLICY_EVENT_THREAD = 1,
+       SUSPEND_POLICY_ALL = 2
+} SuspendPolicy;
+
+typedef enum {
+       ERR_NONE = 0,
+       ERR_INVALID_OBJECT = 20,
+       ERR_INVALID_FIELDID = 25,
+       ERR_INVALID_FRAMEID = 30,
+       ERR_NOT_IMPLEMENTED = 100,
+       ERR_NOT_SUSPENDED = 101,
+       ERR_INVALID_ARGUMENT = 102,
+       ERR_UNLOADED = 103
+} ErrorCode;
+
+typedef enum {
+       MOD_KIND_COUNT = 1,
+       MOD_KIND_THREAD_ONLY = 3,
+       MOD_KIND_LOCATION_ONLY = 7,
+       MOD_KIND_EXCEPTION_ONLY = 8,
+       MOD_KIND_STEP = 10
+} ModifierKind;
+
+typedef enum {
+       STEP_DEPTH_INTO = 0,
+       STEP_DEPTH_OVER = 1,
+       STEP_DEPTH_OUT = 2
+} StepDepth;
+
+typedef enum {
+       STEP_SIZE_MIN = 0,
+       STEP_SIZE_LINE = 1
+} StepSize;
+
+typedef enum {
+       TOKEN_TYPE_STRING = 0,
+       TOKEN_TYPE_TYPE = 1,
+       TOKEN_TYPE_FIELD = 2,
+       TOKEN_TYPE_METHOD = 3,
+       TOKEN_TYPE_UNKNOWN = 4
+} TokenType;
+
+typedef enum {
+       VALUE_TYPE_ID_NULL = 0xf0,
+       VALUE_TYPE_ID_TYPE = 0xf1
+} ValueTypeId;
+
+typedef enum {
+       FRAME_FLAG_DEBUGGER_INVOKE = 1
+} StackFrameFlags;
+
+typedef enum {
+       CMD_VM_VERSION = 1,
+       CMD_VM_ALL_THREADS = 2,
+       CMD_VM_SUSPEND = 3,
+       CMD_VM_RESUME = 4,
+       CMD_VM_EXIT = 5,
+       CMD_VM_DISPOSE = 6,
+       CMD_VM_INVOKE_METHOD = 7
+} CmdVM;
+
+typedef enum {
+       CMD_THREAD_GET_FRAME_INFO = 1,
+       CMD_THREAD_GET_NAME = 2,
+       CMD_THREAD_GET_STATE = 3,
+       CMD_THREAD_GET_INFO = 4
+} CmdThread;
+
+typedef enum {
+       CMD_EVENT_REQUEST_SET = 1,
+       CMD_EVENT_REQUEST_CLEAR = 2,
+       CMD_EVENT_REQUEST_CLEAR_ALL_BREAKPOINTS = 3
+} CmdEvent;
+
+typedef enum {
+       CMD_COMPOSITE = 100
+} CmdComposite;
+
+typedef enum {
+       CMD_APPDOMAIN_GET_ROOT_DOMAIN = 1,
+       CMD_APPDOMAIN_GET_FRIENDLY_NAME = 2,
+       CMD_APPDOMAIN_GET_ASSEMBLIES = 3,
+       CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY = 4,
+       CMD_APPDOMAIN_CREATE_STRING = 5,
+       CMD_APPDOMAIN_GET_CORLIB = 6
+} CmdAppDomain;
+
+typedef enum {
+       CMD_ASSEMBLY_GET_LOCATION = 1,
+       CMD_ASSEMBLY_GET_ENTRY_POINT = 2,
+       CMD_ASSEMBLY_GET_MANIFEST_MODULE = 3,
+       CMD_ASSEMBLY_GET_OBJECT = 4,
+       CMD_ASSEMBLY_GET_TYPE = 5,
+} CmdAssembly;
+
+typedef enum {
+       CMD_MODULE_GET_INFO = 1,
+} CmdModule;
+
+typedef enum {
+       CMD_METHOD_GET_NAME = 1,
+       CMD_METHOD_GET_DECLARING_TYPE = 2,
+       CMD_METHOD_GET_DEBUG_INFO = 3,
+       CMD_METHOD_GET_PARAM_INFO = 4,
+       CMD_METHOD_GET_LOCALS_INFO = 5,
+       CMD_METHOD_GET_INFO = 6,
+       CMD_METHOD_GET_BODY = 7,
+       CMD_METHOD_RESOLVE_TOKEN = 8,
+} CmdMethod;
+
+typedef enum {
+       CMD_TYPE_GET_INFO = 1,
+       CMD_TYPE_GET_METHODS = 2,
+       CMD_TYPE_GET_FIELDS = 3,
+       CMD_TYPE_GET_VALUES = 4,
+       CMD_TYPE_GET_OBJECT = 5,
+       CMD_TYPE_GET_SOURCE_FILES = 6,
+       CMD_TYPE_SET_VALUES = 7,
+       CMD_TYPE_IS_ASSIGNABLE_FROM = 8,
+       CMD_TYPE_GET_PROPERTIES = 9,
+       CMD_TYPE_GET_CATTRS = 10,
+       CMD_TYPE_GET_FIELD_CATTRS = 11,
+       CMD_TYPE_GET_PROPERTY_CATTRS = 12
+} CmdType;
+
+typedef enum {
+       CMD_STACK_FRAME_GET_VALUES = 1,
+       CMD_STACK_FRAME_GET_THIS = 2,
+       CMD_STACK_FRAME_SET_VALUES = 3
+} CmdStackFrame;
+
+typedef enum {
+       CMD_ARRAY_REF_GET_LENGTH = 1,
+       CMD_ARRAY_REF_GET_VALUES = 2,
+       CMD_ARRAY_REF_SET_VALUES = 3,
+} CmdArray;
+
+typedef enum {
+       CMD_STRING_REF_GET_VALUE = 1,
+} CmdString;
+
+typedef enum {
+       CMD_OBJECT_REF_GET_TYPE = 1,
+       CMD_OBJECT_REF_GET_VALUES = 2,
+       CMD_OBJECT_REF_IS_COLLECTED = 3,
+       CMD_OBJECT_REF_GET_ADDRESS = 4,
+       CMD_OBJECT_REF_GET_DOMAIN = 5,
+       CMD_OBJECT_REF_SET_VALUES = 6
+} CmdObject;
+
+typedef struct {
+       ModifierKind kind;
+       union {
+               int count; /* For kind == MOD_KIND_COUNT */
+               MonoInternalThread *thread; /* For kind == MOD_KIND_THREAD_ONLY */
+               MonoClass *exc_class; /* For kind == MONO_KIND_EXCEPTION_ONLY */
+       } data;
+} Modifier;
+
+typedef struct{
+       int id;
+       int event_kind;
+       int suspend_policy;
+       int nmodifiers;
+       gpointer info;
+       Modifier modifiers [MONO_ZERO_LEN_ARRAY];
+} EventRequest;
+
+/*
+ * Describes a single step request.
+ */
+typedef struct {
+       EventRequest *req;
+       MonoInternalThread *thread;
+       StepDepth depth;
+       StepSize size;
+       gpointer last_sp;
+       gpointer start_sp;
+       MonoMethod *last_method;
+       int last_line;
+} MonoSingleStepReq;
+
+/* Dummy structure used for the profiler callbacks */
+typedef struct {
+} DebuggerProfiler;
+
+#define DEBUG(level,s) do { if (G_UNLIKELY ((level) <= log_level)) { s; fflush (log_file); } } while (0)
+
+/*
+ * Globals
+ */
+
+static AgentConfig agent_config;
+
+static gboolean inited;
+
+static int conn_fd;
+
+static int packet_id = 0;
+
+static int objref_id = 0;
+
+static int event_request_id = 0;
+
+static int frame_id = 0;
+
+static GPtrArray *event_requests;
+
+static guint32 debugger_tls_id;
+
+static gboolean vm_start_event_sent, vm_death_event_sent, disconnected;
+
+/* Maps MonoInternalThread -> DebuggerTlsData */
+static MonoGHashTable *thread_to_tls;
+
+/* Maps tid -> MonoInternalThread */
+static MonoGHashTable *tid_to_thread;
+
+/* Maps tid -> MonoThread (not MonoInternalThread) */
+static MonoGHashTable *tid_to_thread_obj;
+
+static gsize debugger_thread_id;
+
+static HANDLE debugger_thread_handle;
+
+static int log_level;
+
+static FILE *log_file;
+
+/* Classes whose class load event has been sent */
+static GHashTable *loaded_classes;
+
+/* Assemblies whose assembly load event has no been sent yet */
+static GPtrArray *pending_assembly_loads;
+
+/* Whenever the debugger thread has exited */
+static gboolean debugger_thread_exited;
+
+/* Cond variable used to wait for debugger_thread_exited becoming true */
+static pthread_cond_t debugger_thread_exited_cond = PTHREAD_COND_INITIALIZER;
+
+/* Mutex for the cond var above */
+static pthread_mutex_t debugger_thread_exited_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static DebuggerProfiler debugger_profiler;
+
+/* The single step request instance */
+static MonoSingleStepReq *ss_req = NULL;
+
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+/* Number of single stepping operations in progress */
+static int ss_count;
+#endif
+
+static void transport_connect (char *host, int port);
+
+static guint32 WINAPI debugger_thread (void *arg);
+
+static void runtime_initialized (MonoProfiler *prof);
+
+static void runtime_shutdown (MonoProfiler *prof);
+
+static void thread_startup (MonoProfiler *prof, gsize tid);
+
+static void thread_end (MonoProfiler *prof, gsize tid);
+
+static void appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result);
+
+static void appdomain_unload (MonoProfiler *prof, MonoDomain *domain);
+
+static void assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result);
+
+static void assembly_unload (MonoProfiler *prof, MonoAssembly *assembly);
+
+static void start_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
+
+static void end_runtime_invoke (MonoProfiler *prof, MonoMethod *method);
+
+static void jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result);
+
+static void add_pending_breakpoints (MonoMethod *method, MonoJitInfo *jinfo);
+
+static void start_single_stepping (void);
+
+static void stop_single_stepping (void);
+
+static void suspend_current (void);
+
+/* Submodule init/cleanup */
+static void breakpoints_init (void);
+static void breakpoints_cleanup (void);
+
+static void objrefs_init (void);
+static void objrefs_cleanup (void);
+
+static void ids_init (void);
+static void ids_cleanup (void);
+
+static void suspend_init (void);
+
+static ErrorCode ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req);
+static void ss_stop (EventRequest *req);
+
+static int
+parse_address (char *address, char **host, int *port)
+{
+       char *pos = strchr (address, ':');
+
+       if (pos == NULL || pos == address)
+               return 1;
+
+       *host = g_malloc (pos - address + 1);
+       strncpy (*host, address, pos - address);
+       (*host) [pos - address] = '\0';
+
+       *port = atoi (pos + 1);
+
+       return 0;
+}
+
+static void
+print_usage (void)
+{
+       fprintf (stderr, "Usage: mono --debugger-agent=[<option>=<value>,...] ...\n");
+       fprintf (stderr, "Available options:\n");
+       fprintf (stderr, "  transport=<transport>\t\tTransport to use for connecting to the debugger (mandatory, possible values: 'dt_socket')\n");
+       fprintf (stderr, "  address=<hostname>:<port>\tAddress to connect to (mandatory)\n");
+       fprintf (stderr, "  loglevel=<n>\tLog level (defaults to 0)\n");
+       fprintf (stderr, "  logfile=<file>\tFile to log to (defaults to stdout)\n");
+}
+
+void
+mono_debugger_agent_parse_options (char *options)
+{
+       char **args, **ptr;
+       char *host;
+       int port;
+
+#ifndef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       fprintf (stderr, "--debugger-agent is not supported on this platform.\n");
+       exit (1);
+#endif
+
+       agent_config.enabled = TRUE;
+
+       args = g_strsplit (options, ",", -1);
+       for (ptr = args; ptr && *ptr; ptr ++) {
+               char *arg = *ptr;
+
+               if (strncmp (arg, "transport=", 10) == 0) {
+                       agent_config.transport = g_strdup (arg + 10);
+               } else if (strncmp (arg, "address=", 8) == 0) {
+                       agent_config.address = g_strdup (arg + 8);
+               } else if (strncmp (arg, "loglevel=", 9) == 0) {
+                       agent_config.log_level = atoi (arg + 9);
+               } else if (strncmp (arg, "logfile=", 8) == 0) {
+                       agent_config.log_file = g_strdup (arg + 8);
+               } else {
+                       print_usage ();
+                       exit (1);
+               }
+       }
+
+       if (agent_config.transport == NULL) {
+               fprintf (stderr, "debugger-agent: The 'transport' option is mandatory.\n");
+               exit (1);
+       }
+       if (strcmp (agent_config.transport, "dt_socket") != 0) {
+               fprintf (stderr, "debugger-agent: The only supported value for the 'transport' option is 'dt_socket'.\n");
+               exit (1);
+       }
+
+       if (agent_config.address == NULL) {
+               fprintf (stderr, "debugger-agent: The 'address' option is mandatory.\n");
+               exit (1);
+       }
+
+       if (parse_address (agent_config.address, &host, &port)) {
+               fprintf (stderr, "debugger-agent: The format of the 'address' options is '<host>:<port>'\n");
+               exit (1);
+       }
+}
+
+void
+mono_debugger_agent_init (void)
+{
+       char *host;
+       int port;
+       int res;
+
+       if (!agent_config.enabled)
+               return;
+
+       /* Need to know whenever a thread has acquired the loader mutex */
+       mono_loader_lock_track_ownership (TRUE);
+
+       event_requests = g_ptr_array_new ();
+
+       mono_profiler_install ((MonoProfiler*)&debugger_profiler, runtime_shutdown);
+       mono_profiler_set_events (MONO_PROFILE_APPDOMAIN_EVENTS | MONO_PROFILE_THREADS | MONO_PROFILE_ASSEMBLY_EVENTS | MONO_PROFILE_JIT_COMPILATION | MONO_PROFILE_METHOD_EVENTS);
+       mono_profiler_install_runtime_initialized (runtime_initialized);
+       mono_profiler_install_appdomain (NULL, appdomain_load, appdomain_unload, NULL);
+       mono_profiler_install_thread (thread_startup, thread_end);
+       mono_profiler_install_assembly (NULL, assembly_load, assembly_unload, NULL);
+       mono_profiler_install_jit_end (jit_end);
+       mono_profiler_install_method_invoke (start_runtime_invoke, end_runtime_invoke);
+
+       debugger_tls_id = TlsAlloc ();
+
+       thread_to_tls = mono_g_hash_table_new (NULL, NULL);
+       MONO_GC_REGISTER_ROOT (thread_to_tls);
+
+       tid_to_thread = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
+       MONO_GC_REGISTER_ROOT (tid_to_thread);
+
+       tid_to_thread_obj = mono_g_hash_table_new_type (NULL, NULL, MONO_HASH_VALUE_GC);
+       MONO_GC_REGISTER_ROOT (tid_to_thread_obj);
+
+       loaded_classes = g_hash_table_new (mono_aligned_addr_hash, NULL);
+       pending_assembly_loads = g_ptr_array_new ();
+
+       res = parse_address (agent_config.address, &host, &port);
+       g_assert (res == 0);
+
+       log_level = agent_config.log_level;
+
+       if (agent_config.log_file) {
+               log_file = fopen (agent_config.log_file, "w+");
+               if (!log_file) {
+                       fprintf (stderr, "Unable to create log file '%s': %s.\n", agent_config.log_file, strerror (errno));
+                       exit (1);
+               }
+       } else {
+               log_file = stdout;
+       }
+
+       ids_init ();
+       objrefs_init ();
+       breakpoints_init ();
+       suspend_init ();
+
+       mini_get_debug_options ()->gen_seq_points = TRUE;
+       /* 
+        * This is needed because currently we don't handle liveness info.
+        */
+       mini_get_debug_options ()->mdb_optimizations = TRUE;
+
+       /* This is needed because we can't set local variables in registers yet */
+       mono_disable_optimizations (MONO_OPT_LINEARS);
+
+       inited = TRUE;
+
+       transport_connect (host, port);
+}
+
+static void
+mono_debugger_agent_cleanup (void)
+{
+       if (!agent_config.enabled)
+               return;
+
+       /* This will interrupt the agent thread */
+       /* Close the read part only so it can still send back replies */
+       shutdown (conn_fd, SHUT_RD);
+
+       /* 
+        * Wait for the thread to exit.
+        *
+        * If we continue with the shutdown without waiting for it, then the client might
+        * not receive an answer to its last command like a resume.
+        * The WaitForSingleObject infrastructure doesn't seem to work during shutdown, so
+        * use pthreads.
+        */
+       //WaitForSingleObject (debugger_thread_handle, INFINITE);
+       if (GetCurrentThreadId () != debugger_thread_id) {
+               pthread_mutex_lock (&debugger_thread_exited_mutex);
+               if (!debugger_thread_exited)
+                       pthread_cond_wait (&debugger_thread_exited_cond, &debugger_thread_exited_mutex);
+               pthread_mutex_unlock (&debugger_thread_exited_mutex);
+       }
+
+       breakpoints_cleanup ();
+       objrefs_cleanup ();
+       ids_cleanup ();
+}
+
+static void
+transport_connect (char *host, int port)
+{
+       struct addrinfo hints;
+       struct addrinfo *result, *rp;
+       int sfd, s, res;
+       char port_string [128];
+       char handshake_msg [128];
+       guint8 buf [128];
+
+       sprintf (port_string, "%d", port);
+
+       /* Obtain address(es) matching host/port */
+
+       memset (&hints, 0, sizeof (struct addrinfo));
+       hints.ai_family = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
+       hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
+       hints.ai_flags = 0;
+       hints.ai_protocol = 0;          /* Any protocol */
+
+       s = getaddrinfo (host, port_string, &hints, &result);
+       if (s != 0) {
+               fprintf (stderr, "debugger-agent: Unable to connect to %s:%d: %s\n", host, port, gai_strerror (s));
+               exit (1);
+       }
+
+       /* getaddrinfo() returns a list of address structures.
+          Try each address until we successfully connect().
+          If socket() (or connect()) fails, we (close the socket
+          and) try the next address. */
+
+       for (rp = result; rp != NULL; rp = rp->ai_next) {
+               sfd = socket (rp->ai_family, rp->ai_socktype,
+                                         rp->ai_protocol);
+               if (sfd == -1)
+                       continue;
+
+               if (connect (sfd, rp->ai_addr, rp->ai_addrlen) != -1)
+                       break;       /* Success */
+
+               close (sfd);
+       }
+       
+       freeaddrinfo (result);
+
+       if (rp == 0) {
+               fprintf (stderr, "debugger-agent: Unable to connect to %s:%d\n", host, port);
+               exit (1);
+       }
+       
+       /* Write handshake message */
+       sprintf (handshake_msg, "DWP-Handshake");
+       res = write (sfd, handshake_msg, strlen (handshake_msg));
+       g_assert (res != -1);
+
+       /* Read answer */
+       res = read (sfd, buf, strlen (handshake_msg));
+       if ((res != strlen (handshake_msg)) || (memcmp (buf, handshake_msg, strlen (handshake_msg) != 0))) {
+               fprintf (stderr, "debugger-agent: DWP handshake failed.\n");
+               exit (1);
+       }
+
+       /* 
+        * Set TCP_NODELAY on the socket so the client receives events/command
+        * results immediately.
+        */
+       {
+               int flag = 1;
+               int result = setsockopt(sfd,
+                                 IPPROTO_TCP,
+                                 TCP_NODELAY,
+                                 (char *) &flag,
+                                 sizeof(int));
+               g_assert (result >= 0);
+       }
+
+       conn_fd = sfd;
+}
+
+static gboolean
+transport_send (guint8 *data, int len)
+{
+       int res;
+
+       res = write (conn_fd, data, len);
+       if (res != len)
+               return FALSE;
+       else
+               return TRUE;
+}
+
+static void
+start_debugger_thread (void)
+{
+       gsize tid;
+
+       debugger_thread_handle = mono_create_thread (NULL, 0, debugger_thread, NULL, 0, &tid);
+       g_assert (debugger_thread_handle);
+}
+
+/*
+ * Functions to decode protocol data
+ */
+
+static inline int
+decode_byte (guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       *endbuf = buf + 1;
+       g_assert (*endbuf <= limit);
+       return buf [0];
+}
+
+static inline int
+decode_int (guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       *endbuf = buf + 4;
+       g_assert (*endbuf <= limit);
+
+       return (((int)buf [0]) << 24) | (((int)buf [1]) << 16) | (((int)buf [2]) << 8) | (((int)buf [3]) << 0);
+}
+
+static inline gint64
+decode_long (guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       guint32 high = decode_int (buf, &buf, limit);
+       guint32 low = decode_int (buf, &buf, limit);
+
+       *endbuf = buf;
+
+       return ((((guint64)high) << 32) | ((guint64)low));
+}
+
+static inline int
+decode_id (guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       return decode_int (buf, endbuf, limit);
+}
+
+static inline char*
+decode_string (guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       int len = decode_int (buf, &buf, limit);
+       char *s;
+
+       s = g_malloc (len + 1);
+       g_assert (s);
+
+       memcpy (s, buf, len);
+       s [len] = '\0';
+       buf += len;
+       *endbuf = buf;
+
+       return s;
+}
+
+/*
+ * Functions to encode protocol data
+ */
+
+typedef struct {
+       guint8 *buf, *p, *end;
+} Buffer;
+
+static inline void
+buffer_init (Buffer *buf, int size)
+{
+       buf->buf = g_malloc (size);
+       buf->p = buf->buf;
+       buf->end = buf->buf + size;
+}
+
+static inline void
+buffer_make_room (Buffer *buf, int size)
+{
+       if (buf->end - buf->p < size) {
+               int new_size = buf->end - buf->buf + size + 32;
+               guint8 *p = g_realloc (buf->buf, new_size);
+               size = buf->p - buf->buf;
+               buf->buf = p;
+               buf->p = p + size;
+               buf->end = buf->buf + new_size;
+       }
+}
+
+static inline void
+buffer_add_byte (Buffer *buf, guint8 val)
+{
+       buffer_make_room (buf, 1);
+       buf->p [0] = val;
+       buf->p++;
+}
+
+static inline void
+buffer_add_int (Buffer *buf, guint32 val)
+{
+       buffer_make_room (buf, 4);
+       buf->p [0] = (val >> 24) & 0xff;
+       buf->p [1] = (val >> 16) & 0xff;
+       buf->p [2] = (val >> 8) & 0xff;
+       buf->p [3] = (val >> 0) & 0xff;
+       buf->p += 4;
+}
+
+static inline void
+buffer_add_long (Buffer *buf, guint64 l)
+{
+       buffer_add_int (buf, (l >> 32) & 0xffffffff);
+       buffer_add_int (buf, (l >> 0) & 0xffffffff);
+}
+
+static inline void
+buffer_add_id (Buffer *buf, int id)
+{
+       buffer_add_int (buf, (guint64)id);
+}
+
+static inline void
+buffer_add_data (Buffer *buf, guint8 *data, int len)
+{
+       buffer_make_room (buf, len);
+       memcpy (buf->p, data, len);
+       buf->p += len;
+}
+
+static inline void
+buffer_add_string (Buffer *buf, const char *str)
+{
+       int len;
+
+       if (str == NULL) {
+               buffer_add_int (buf, 0);
+       } else {
+               len = strlen (str);
+               buffer_add_int (buf, len);
+               buffer_add_data (buf, (guint8*)str, len);
+       }
+}
+
+static inline void
+buffer_free (Buffer *buf)
+{
+       g_free (buf->buf);
+}
+
+static gboolean
+send_packet (int command_set, int command, Buffer *data)
+{
+       Buffer buf;
+       int len, id;
+       gboolean res;
+
+       id = InterlockedIncrement (&packet_id);
+
+       len = data->p - data->buf + 11;
+       buffer_init (&buf, len);
+       buffer_add_int (&buf, len);
+       buffer_add_int (&buf, id);
+       buffer_add_byte (&buf, 0); /* flags */
+       buffer_add_byte (&buf, command_set);
+       buffer_add_byte (&buf, command);
+       memcpy (buf.buf + 11, data->buf, data->p - data->buf);
+
+       res = transport_send (buf.buf, len);
+
+       buffer_free (&buf);
+
+       return res;
+}
+
+static gboolean
+send_reply_packet (int id, int error, Buffer *data)
+{
+       Buffer buf;
+       int len;
+       gboolean res;
+       
+       len = data->p - data->buf + 11;
+       buffer_init (&buf, len);
+       buffer_add_int (&buf, len);
+       buffer_add_int (&buf, id);
+       buffer_add_byte (&buf, 0x80); /* flags */
+       buffer_add_byte (&buf, (error >> 8) & 0xff);
+       buffer_add_byte (&buf, error);
+       memcpy (buf.buf + 11, data->buf, data->p - data->buf);
+
+       res = transport_send (buf.buf, len);
+
+       buffer_free (&buf);
+
+       return res;
+}
+
+/*
+ * OBJECT IDS
+ */
+
+/*
+ * Represents an object accessible by the debugger client.
+ */
+typedef struct {
+       /* Unique id used in the wire protocol to refer to objects */
+       int id;
+       /*
+        * A weakref gc handle pointing to the object. The gc handle is used to 
+        * detect if the object was garbage collected.
+        */
+       guint32 handle;
+} ObjRef;
+
+/* Maps objid -> ObjRef */
+static GHashTable *objrefs;
+
+static void
+free_objref (gpointer value)
+{
+       ObjRef *o = value;
+
+       mono_gchandle_free (o->handle);
+
+       g_free (o);
+}
+
+static void
+objrefs_init (void)
+{
+       objrefs = g_hash_table_new_full (NULL, NULL, NULL, free_objref);
+}
+
+static void
+objrefs_cleanup (void)
+{
+       g_hash_table_destroy (objrefs);
+       objrefs = NULL;
+}
+
+static GHashTable *obj_to_objref;
+
+/*
+ * Return an ObjRef for OBJ.
+ */
+static ObjRef*
+get_objref (MonoObject *obj)
+{
+       ObjRef *ref;
+
+       if (obj == NULL)
+               return 0;
+
+#ifdef HAVE_SGEN_GC
+       NOT_IMPLEMENTED;
+#endif
+
+       /* Use a hash table with masked pointers to internalize object references */
+       /* FIXME: This can grow indefinitely */
+       mono_loader_lock ();
+
+       if (!obj_to_objref)
+               obj_to_objref = g_hash_table_new (NULL, NULL);
+
+       ref = g_hash_table_lookup (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)));
+       /* ref might refer to a different object with the same addr which was GCd */
+       if (ref && mono_gchandle_get_target (ref->handle) == obj) {
+               mono_loader_unlock ();
+               return ref;
+       }
+
+       ref = g_new0 (ObjRef, 1);
+       ref->id = InterlockedIncrement (&objref_id);
+       ref->handle = mono_gchandle_new_weakref (obj, FALSE);
+
+       g_hash_table_insert (objrefs, GINT_TO_POINTER (ref->id), ref);
+       g_hash_table_insert (obj_to_objref, GINT_TO_POINTER (~((gsize)obj)), ref);
+
+       mono_loader_unlock ();
+
+       return ref;
+}
+
+static inline int
+get_objid (MonoObject *obj)
+{
+       return get_objref (obj)->id;
+}
+
+/*
+ * Set OBJ to the object identified by OBJID.
+ * Returns 0 or an error code if OBJID is invalid or the object has been garbage
+ * collected.
+ */
+static ErrorCode
+get_object_allow_null (int objid, MonoObject **obj)
+{
+       ObjRef *ref;
+
+       if (objid == 0) {
+               *obj = NULL;
+               return 0;
+       }
+
+       if (!objrefs)
+               return ERR_INVALID_OBJECT;
+
+       mono_loader_lock ();
+
+       ref = g_hash_table_lookup (objrefs, GINT_TO_POINTER (objid));
+
+       if (ref) {
+               *obj = mono_gchandle_get_target (ref->handle);
+               mono_loader_unlock ();
+               if (!(*obj))
+                       return ERR_INVALID_OBJECT;
+               return 0;
+       } else {
+               mono_loader_unlock ();
+               return ERR_INVALID_OBJECT;
+       }
+}
+
+static ErrorCode
+get_object (int objid, MonoObject **obj)
+{
+       int err = get_object_allow_null (objid, obj);
+
+       if (err)
+               return err;
+       if (!(*obj))
+               return ERR_INVALID_OBJECT;
+       return 0;
+}
+
+static inline int
+decode_objid (guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       return decode_id (buf, endbuf, limit);
+}
+
+static inline void
+buffer_add_objid (Buffer *buf, MonoObject *o)
+{
+       buffer_add_id (buf, get_objid (o));
+}
+
+/*
+ * IDS
+ */
+
+typedef enum {
+       ID_ASSEMBLY = 0,
+       ID_MODULE = 1,
+       ID_TYPE = 2,
+       ID_METHOD = 3,
+       ID_FIELD = 4,
+       ID_DOMAIN = 5,
+       ID_PROPERTY = 6,
+       ID_NUM
+} IdType;
+
+/*
+ * Represents a runtime structure accessible to the debugger client
+ */
+typedef struct {
+       /* Unique id used in the wire protocol */
+       int id;
+       /* Domain of the runtime structure, NULL if the domain was unloaded */
+       MonoDomain *domain;
+       union {
+               gpointer val;
+               MonoClass *klass;
+               MonoMethod *method;
+               MonoImage *image;
+               MonoAssembly *assembly;
+               MonoClassField *field;
+               MonoDomain *domain;
+               MonoProperty *property;
+       } data;
+} Id;
+
+typedef struct {
+       /* Maps runtime structure -> Id */
+       GHashTable *val_to_id [ID_NUM];
+} AgentDomainInfo;
+
+/* Maps id -> Id */
+static GPtrArray *ids [ID_NUM];
+
+static void
+ids_init (void)
+{
+       int i;
+
+       for (i = 0; i < ID_NUM; ++i)
+               ids [i] = g_ptr_array_new ();
+}
+
+static void
+ids_cleanup (void)
+{
+       int i, j;
+
+       for (i = 0; i < ID_NUM; ++i) {
+               if (ids [i]) {
+                       for (j = 0; j < ids [i]->len; ++j)
+                               g_free (g_ptr_array_index (ids [i], j));
+                       g_ptr_array_free (ids [i], TRUE);
+               }
+               ids [i] = NULL;
+       }
+}
+
+void
+mono_debugger_agent_free_domain_info (MonoDomain *domain)
+{
+       AgentDomainInfo *info = domain_jit_info (domain)->agent_info;
+       int i, j;
+
+       if (info) {
+               for (i = 0; i < ID_NUM; ++i)
+                       if (info->val_to_id [i])
+                               g_hash_table_destroy (info->val_to_id [i]);
+               g_free (info);
+       }
+
+       domain_jit_info (domain)->agent_info = NULL;
+
+       /* Clear ids referencing structures in the domain */
+       for (i = 0; i < ID_NUM; ++i) {
+               if (ids [i]) {
+                       for (j = 0; j < ids [i]->len; ++j) {
+                               Id *id = g_ptr_array_index (ids [i], j);
+                               if (id->domain == domain)
+                                       id->domain = NULL;
+                       }
+               }
+       }
+}
+
+static int
+get_id (MonoDomain *domain, IdType type, gpointer val)
+{
+       Id *id;
+       AgentDomainInfo *info;
+
+       if (val == NULL)
+               return 0;
+
+       mono_loader_lock ();
+
+       mono_domain_lock (domain);
+
+       if (!domain_jit_info (domain)->agent_info)
+               domain_jit_info (domain)->agent_info = g_new0 (AgentDomainInfo, 1);
+       info = domain_jit_info (domain)->agent_info;
+       if (info->val_to_id [type] == NULL)
+               info->val_to_id [type] = g_hash_table_new (mono_aligned_addr_hash, NULL);
+
+       id = g_hash_table_lookup (info->val_to_id [type], val);
+       if (id) {
+               mono_domain_unlock (domain);
+               mono_loader_unlock ();
+               return id->id;
+       }
+
+       id = g_new0 (Id, 1);
+       /* Reserve id 0 */
+       id->id = ids [type]->len + 1;
+       id->domain = domain;
+       id->data.val = val;
+
+       g_hash_table_insert (info->val_to_id [type], val, id);
+
+       mono_domain_unlock (domain);
+
+       g_ptr_array_add (ids [type], id);
+
+       mono_loader_unlock ();
+
+       return id->id;
+}
+
+static inline gpointer
+decode_ptr_id (guint8 *buf, guint8 **endbuf, guint8 *limit, IdType type, MonoDomain **domain, int *err)
+{
+       Id *res;
+
+       int id = decode_id (buf, endbuf, limit);
+
+       *err = 0;
+       if (domain)
+               *domain = NULL;
+
+       if (id == 0)
+               return NULL;
+
+       // FIXME: error handling
+       mono_loader_lock ();
+       g_assert (id > 0 && id <= ids [type]->len);
+
+       res = g_ptr_array_index (ids [type], GPOINTER_TO_INT (id - 1));
+       mono_loader_unlock ();
+
+       if (res->domain == NULL) {
+               *err = ERR_UNLOADED;
+               return NULL;
+       }
+
+       if (domain)
+               *domain = res->domain;
+
+       return res->data.val;
+}
+
+static inline void
+buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer val)
+{
+       buffer_add_id (buf, get_id (domain, type, val));
+}
+
+static inline MonoClass*
+decode_typeid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
+{
+       return decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err);
+}
+
+static inline MonoAssembly*
+decode_assemblyid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
+{
+       return decode_ptr_id (buf, endbuf, limit, ID_ASSEMBLY, domain, err);
+}
+
+static inline MonoImage*
+decode_moduleid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
+{
+       return decode_ptr_id (buf, endbuf, limit, ID_MODULE, domain, err);
+}
+
+static inline MonoMethod*
+decode_methodid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
+{
+       return decode_ptr_id (buf, endbuf, limit, ID_METHOD, domain, err);
+}
+
+static inline MonoClassField*
+decode_fieldid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
+{
+       return decode_ptr_id (buf, endbuf, limit, ID_FIELD, domain, err);
+}
+
+static inline MonoDomain*
+decode_domainid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
+{
+       return decode_ptr_id (buf, endbuf, limit, ID_DOMAIN, domain, err);
+}
+
+static inline MonoProperty*
+decode_propertyid (guint8 *buf, guint8 **endbuf, guint8 *limit, MonoDomain **domain, int *err)
+{
+       return decode_ptr_id (buf, endbuf, limit, ID_PROPERTY, domain, err);
+}
+
+static inline void
+buffer_add_typeid (Buffer *buf, MonoDomain *domain, MonoClass *klass)
+{
+       buffer_add_ptr_id (buf, domain, ID_TYPE, klass);
+}
+
+static inline void
+buffer_add_methodid (Buffer *buf, MonoDomain *domain, MonoMethod *method)
+{
+       buffer_add_ptr_id (buf, domain, ID_METHOD, method);
+}
+
+static inline void
+buffer_add_assemblyid (Buffer *buf, MonoDomain *domain, MonoAssembly *assembly)
+{
+       buffer_add_ptr_id (buf, domain, ID_ASSEMBLY, assembly);
+}
+
+static inline void
+buffer_add_moduleid (Buffer *buf, MonoDomain *domain, MonoImage *image)
+{
+       buffer_add_ptr_id (buf, domain, ID_MODULE, image);
+}
+
+static inline void
+buffer_add_fieldid (Buffer *buf, MonoDomain *domain, MonoClassField *field)
+{
+       buffer_add_ptr_id (buf, domain, ID_FIELD, field);
+}
+
+static inline void
+buffer_add_propertyid (Buffer *buf, MonoDomain *domain, MonoProperty *property)
+{
+       buffer_add_ptr_id (buf, domain, ID_PROPERTY, property);
+}
+
+static inline void
+buffer_add_domainid (Buffer *buf, MonoDomain *domain)
+{
+       buffer_add_ptr_id (buf, domain, ID_DOMAIN, domain);
+}
+
+static void invoke_method (void);
+
+/*
+ * SUSPEND/RESUME
+ */
+
+static void
+save_thread_context (MonoContext *ctx)
+{
+       DebuggerTlsData *tls;
+
+       tls = TlsGetValue (debugger_tls_id);
+       g_assert (tls);
+
+       if (ctx) {
+               memcpy (&tls->ctx, ctx, sizeof (MonoContext));
+       } else {
+#ifdef MONO_INIT_CONTEXT_FROM_CURRENT
+               MONO_INIT_CONTEXT_FROM_CURRENT (&tls->ctx);
+#else
+               MONO_INIT_CONTEXT_FROM_FUNC (&tls->ctx, save_thread_context);
+#endif
+       }
+
+       tls->lmf = mono_get_lmf ();
+       tls->domain = mono_domain_get ();
+       tls->has_context = TRUE;
+}
+
+/* The number of times the runtime is suspended */
+static gint32 suspend_count;
+
+/* Number of threads suspended */
+/* 
+ * If this is equal to the size of thread_to_tls, the runtime is considered
+ * suspended.
+ */
+static gint32 threads_suspend_count;
+
+static pthread_mutex_t suspend_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* Cond variable used to wait for suspend_count becoming 0 */
+static pthread_cond_t suspend_cond = PTHREAD_COND_INITIALIZER;
+
+/* Semaphore used to wait for a thread becoming suspended */
+static MonoSemType suspend_sem;
+
+static void
+suspend_init (void)
+{
+       MONO_SEM_INIT (&suspend_sem, 0);
+}
+
+/*
+ * mono_debugger_agent_thread_interrupt:
+ *
+ *   Called by the abort signal handler.
+ */
+gboolean
+mono_debugger_agent_thread_interrupt (MonoJitInfo *ji)
+{
+       DebuggerTlsData *tls;
+
+       tls = TlsGetValue (debugger_tls_id);
+       if (!tls)
+               return FALSE;
+
+       /*
+        * We use interrupt_count to determine whenever this interrupt should be processed
+        * by us or the normal interrupt processing code in the signal handler.
+        * There is no race here with notify_thread (), since the signal is sent after
+        * incrementing interrupt_count.
+        */
+       if (tls->interrupt_count == 0)
+               return FALSE;
+
+       InterlockedDecrement (&tls->interrupt_count);
+
+       // FIXME: Races when the thread leaves managed code before hitting a single step
+       // event.
+
+       if (ji) {
+               /* Running managed code, will be suspended by the single step code */
+               //printf ("S1: %p\n", GetCurrentThreadId ());
+               return TRUE;
+       } else {
+               /* 
+                * Running native code, will be suspended when it returns to/enters 
+                * managed code. Treat it as already suspended.
+                */
+               //printf ("S2: %p\n", GetCurrentThreadId ());
+               if (!tls->suspended) {
+                       tls->suspended = TRUE;
+                       MONO_SEM_POST (&suspend_sem);
+               }
+               return TRUE;
+       }
+}
+
+/*
+ * notify_thread:
+ *
+ *   Notify a thread that it needs to suspend.
+ */
+static void
+notify_thread (gpointer key, gpointer value, gpointer user_data)
+{
+       MonoInternalThread *thread = key;
+       DebuggerTlsData *tls = value;
+       gsize tid = thread->tid;
+
+       if (GetCurrentThreadId () != tid) {
+               DEBUG(1, fprintf (log_file, "[%p] Interrupting %p...\n", (gpointer)GetCurrentThreadId (), (gpointer)tid));
+               /*
+                * Maybe we could use the normal interrupt infrastructure, but that does a lot
+                * of things like breaking waits etc. which we don't want.
+                */
+               InterlockedIncrement (&tls->interrupt_count);
+               pthread_kill ((pthread_t) tid, mono_thread_get_abort_signal ());
+       }
+}
+
+/*
+ * suspend_vm:
+ *
+ * Increase the suspend count of the VM. While the suspend count is greater 
+ * than 0, runtime threads are suspended at certain points during execution.
+ */
+static void
+suspend_vm (void)
+{
+       mono_loader_lock ();
+
+       pthread_mutex_lock (&suspend_mutex);
+
+       suspend_count ++;
+
+       DEBUG(1, fprintf (log_file, "[%p] Suspending vm...\n", (gpointer)GetCurrentThreadId ()));
+
+       if (suspend_count == 1) {
+               // FIXME: Is it safe to call this inside the lock ?
+               start_single_stepping ();
+               mono_g_hash_table_foreach (thread_to_tls, notify_thread, NULL);
+       }
+
+       pthread_mutex_unlock (&suspend_mutex);
+
+       mono_loader_unlock ();
+}
+
+/*
+ * resume_vm:
+ *
+ * Decrease the suspend count of the VM. If the count reaches 0, runtime threads
+ * are resumed.
+ */
+static void
+resume_vm (void)
+{
+       int err;
+
+       g_assert (debugger_thread_id == GetCurrentThreadId ());
+
+       mono_loader_lock ();
+
+       pthread_mutex_lock (&suspend_mutex);
+
+       g_assert (suspend_count > 0);
+       suspend_count --;
+
+       DEBUG(1, fprintf (log_file, "[%p] Resuming vm...\n", (gpointer)GetCurrentThreadId ()));
+
+       if (suspend_count == 0) {
+               // FIXME: Is it safe to call this inside the lock ?
+               stop_single_stepping ();
+               err = pthread_cond_broadcast (&suspend_cond);
+               g_assert (err == 0);
+       }
+
+       err = pthread_mutex_unlock (&suspend_mutex);
+       g_assert (err == 0);
+
+       mono_loader_unlock ();
+}
+
+static void
+invalidate_frames (DebuggerTlsData *tls)
+{
+       int i;
+
+       if (!tls)
+               tls = TlsGetValue (debugger_tls_id);
+       g_assert (tls);
+
+       for (i = 0; i < tls->frame_count; ++i) {
+               if (tls->frames [i]->jit)
+                       mono_debug_free_method_jit_info (tls->frames [i]->jit);
+               g_free (tls->frames [i]);
+       }
+       g_free (tls->frames);
+       tls->frame_count = 0;
+       tls->frames = NULL;
+}
+
+/*
+ * suspend_current:
+ *
+ *   Suspend the current thread until the runtime is resumed. If the thread has a 
+ * pending invoke, then the invoke is executed before this function returns. 
+ */
+static void
+suspend_current (void)
+{
+       int err;
+       DebuggerTlsData *tls;
+
+       g_assert (debugger_thread_id != GetCurrentThreadId ());
+
+       if (mono_loader_lock_is_owned_by_self ()) {
+               /*
+                * If we own the loader mutex, can't suspend until we release it, since the
+                * whole runtime can deadlock otherwise.
+                */
+               return;
+       }
+
+       tls = TlsGetValue (debugger_tls_id);
+       g_assert (tls);
+
+       pthread_mutex_lock (&suspend_mutex);
+
+       if (!tls->suspended) {
+               tls->suspended = TRUE;
+               MONO_SEM_POST (&suspend_sem);
+       }
+
+       DEBUG(1, fprintf (log_file, "[%p] Suspended.\n", (gpointer)GetCurrentThreadId ()));
+
+       while (suspend_count > 0) {
+               err = pthread_cond_wait (&suspend_cond, &suspend_mutex);
+               g_assert (err == 0);
+       }
+
+       tls->suspended = FALSE;
+
+       threads_suspend_count --;
+
+       pthread_mutex_unlock (&suspend_mutex);
+
+       DEBUG(1, fprintf (log_file, "[%p] Resumed.\n", (gpointer)GetCurrentThreadId ()));
+
+       if (tls->invoke) {
+               /* Save the original context */
+               tls->invoke->has_ctx = TRUE;
+               memcpy (&tls->invoke->ctx, &tls->ctx, sizeof (MonoContext));
+
+               invoke_method ();
+       }
+
+       /* The frame info becomes invalid after a resume */
+       tls->has_context = FALSE;
+       invalidate_frames (NULL);
+}
+
+static void
+count_thread (gpointer key, gpointer value, gpointer user_data)
+{
+       DebuggerTlsData *tls = value;
+
+       if (!tls->suspended && !tls->terminated)
+               *(int*)user_data = *(int*)user_data + 1;
+}
+
+static int
+count_threads_to_wait_for (void)
+{
+       int count = 0;
+
+       mono_loader_lock ();
+       mono_g_hash_table_foreach (thread_to_tls, count_thread, &count);
+       mono_loader_unlock ();
+
+       return count;
+}      
+
+/*
+ * wait_for_suspend:
+ *
+ *   Wait until the runtime is completely suspended.
+ */
+static void
+wait_for_suspend (void)
+{
+       int nthreads, nwait, err;
+       gboolean waited = FALSE;
+
+       // FIXME: Threads starting/stopping ?
+       mono_loader_lock ();
+       nthreads = mono_g_hash_table_size (thread_to_tls);
+       mono_loader_unlock ();
+
+       while (TRUE) {
+               nwait = count_threads_to_wait_for ();
+               if (nwait) {
+                       DEBUG(1, fprintf (log_file, "Waiting for %d(%d) threads to suspend...\n", nwait, nthreads));
+                       err = MONO_SEM_WAIT (&suspend_sem);
+                       g_assert (err == 0);
+                       waited = TRUE;
+               } else {
+                       break;
+               }
+       }
+
+       if (waited)
+               DEBUG(1, fprintf (log_file, "%d threads suspended.\n", nthreads));
+}
+
+/*
+ * is_suspended:
+ *
+ *   Return whenever the runtime is suspended.
+ */
+static gboolean
+is_suspended (void)
+{
+       return count_threads_to_wait_for () == 0;
+}
+
+/*
+ * compute_il_offset:
+ *
+ *    Compute the IL offset corresponding to NATIVE_OFFSET, which should be
+ * a location of a sequence point.
+ * We use this function instead of mono_debug_il_offset_from_address () etc,
+ * which doesn't seem to work in a lot of cases.
+ */
+static gint32
+compute_il_offset (MonoDomain *domain, MonoMethod *method, gint32 native_offset)
+{
+       GPtrArray *seq_points;
+       int i, last_il_offset, seq_il_offset, seq_native_offset;
+
+       mono_domain_lock (domain);
+       seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
+       mono_domain_unlock (domain);
+       g_assert (seq_points);
+
+       last_il_offset = -1;
+
+       /* Find the sequence point */
+       for (i = 0; i < seq_points->len; i += 2) {
+               seq_il_offset = GPOINTER_TO_UINT (g_ptr_array_index (seq_points, i));
+               seq_native_offset = GPOINTER_TO_UINT (g_ptr_array_index (seq_points, i + 1));
+
+               if (seq_native_offset > native_offset)
+                       break;
+               last_il_offset = seq_il_offset;
+       }
+
+       return last_il_offset;
+}
+
+typedef struct {
+       DebuggerTlsData *tls;
+       GSList *frames;
+} ComputeFramesUserData;
+
+static gboolean
+process_frame (StackFrameInfo *info, MonoContext *ctx, gpointer user_data)
+{
+       ComputeFramesUserData *ud = user_data;
+       StackFrame *frame;
+       MonoMethod *method;
+
+       if (info->type != FRAME_TYPE_MANAGED) {
+               if (info->type == FRAME_TYPE_DEBUGGER_INVOKE) {
+                       /* Mark the last frame as an invoke frame */
+                       if (ud->frames)
+                               ((StackFrame*)ud->frames->data)->flags |= FRAME_FLAG_DEBUGGER_INVOKE;
+               }
+               return FALSE;
+       }
+
+       if (info->ji)
+               method = info->ji->method;
+       else
+               method = info->method;
+
+       if (!method || (method->wrapper_type && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD))
+               return FALSE;
+
+       if (info->il_offset == -1) {
+               /* Can't use compute_il_offset () since ip doesn't point precisely at at a seq point */
+               info->il_offset = mono_debug_il_offset_from_address (method, info->domain, info->native_offset);
+       }
+
+       DEBUG (1, fprintf (stderr, "\tFrame: %s %d %d %d\n", mono_method_full_name (method, TRUE), info->native_offset, info->il_offset, info->managed));
+
+       if (!info->managed && method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD) {
+               /*
+                * mono_arch_find_jit_info () returns the context stored in the LMF for 
+                * native frames, but it should unwind once. This is why we have duplicate
+                * frames on the stack sometimes.
+                * !managed also seems to be set for dynamic methods.
+                */
+               return FALSE;
+       }
+
+       frame = g_new0 (StackFrame, 1);
+       frame->method = method;
+       frame->il_offset = info->il_offset;
+       frame->ctx = *ctx;
+       frame->domain = info->domain;
+
+       ud->frames = g_slist_append (ud->frames, frame);
+
+       return FALSE;
+}
+
+static void
+compute_frame_info (MonoInternalThread *thread, DebuggerTlsData *tls)
+{
+       ComputeFramesUserData user_data;
+       GSList *tmp;
+       int i, findex, new_frame_count;
+       StackFrame **new_frames, *f;
+
+       // FIXME: Locking on tls
+       if (tls->frames && tls->frames_up_to_date)
+               return;
+
+       DEBUG(1, fprintf (log_file, "Frames for %p(tid=%lx):\n", thread, (glong)thread->tid));
+
+       user_data.tls = tls;
+       user_data.frames = NULL;
+       if (tls->has_context) {
+               mono_jit_walk_stack_from_ctx_in_thread (process_frame, tls->domain, &tls->ctx, FALSE, thread, tls->lmf, &user_data);
+       } else {
+               // FIXME:
+               tls->frame_count = 0;
+               return;
+       }
+
+       new_frame_count = g_slist_length (user_data.frames);
+       new_frames = g_new0 (StackFrame*, new_frame_count);
+       findex = 0;
+       for (tmp = user_data.frames; tmp; tmp = tmp->next) {
+               f = tmp->data;
+
+               /* 
+                * Reuse the id for already existing stack frames, so invokes don't invalidate
+                * the still valid stack frames.
+                */
+               for (i = 0; i < tls->frame_count; ++i) {
+                       if (MONO_CONTEXT_GET_SP (&tls->frames [i]->ctx) == MONO_CONTEXT_GET_SP (&f->ctx)) {
+                               f->id = tls->frames [i]->id;
+                               break;
+                       }
+               }
+
+               if (i >= tls->frame_count)
+                       f->id = InterlockedIncrement (&frame_id);
+
+               new_frames [findex ++] = f;
+       }
+
+       g_slist_free (user_data.frames);
+
+       invalidate_frames (tls);
+
+       tls->frames = new_frames;
+       tls->frame_count = new_frame_count;
+       tls->frames_up_to_date = TRUE;
+}
+
+/*
+ * EVENT HANDLING
+ */
+
+/*
+ * create_event_list:
+ *
+ *   Return a list of event request ids matching EVENT, starting from REQS, which
+ * can be NULL to include all event requests. Set SUSPEND_POLICY to the suspend
+ * policy.
+ * We return request ids, instead of requests, to simplify threading, since 
+ * requests could be deleted anytime when the loader lock is not held.
+ * LOCKING: Assumes the loader lock is held.
+ */
+static GSList*
+create_event_list (EventKind event, GPtrArray *reqs, MonoException *exc, int *suspend_policy)
+{
+       int i, j;
+       GSList *events = NULL;
+
+       *suspend_policy = SUSPEND_POLICY_NONE;
+
+       if (!reqs)
+               reqs = event_requests;
+
+       if (!reqs)
+               return NULL;
+
+       for (i = 0; i < reqs->len; ++i) {
+               EventRequest *req = g_ptr_array_index (reqs, i);
+               if (req->event_kind == event) {
+                       gboolean filtered = FALSE;
+
+                       /* Apply filters */
+                       for (j = 0; j < req->nmodifiers; ++j) {
+                               if (req->modifiers [j].kind == MOD_KIND_COUNT) {
+                                       filtered = TRUE;
+                                       if (req->modifiers [j].data.count > 0) {
+                                               if (req->modifiers [j].data.count > 0) {
+                                                       req->modifiers [j].data.count --;
+                                                       if (req->modifiers [j].data.count == 0)
+                                                               filtered = FALSE;
+                                               }
+                                       }
+                               } else if (req->modifiers [j].kind == MOD_KIND_THREAD_ONLY) {
+                                       if (req->modifiers [j].data.thread != mono_thread_internal_current ())
+                                               filtered = TRUE;
+                               } else if (req->modifiers [j].kind == MOD_KIND_EXCEPTION_ONLY && exc) {
+                                       if (req->modifiers [j].data.exc_class && !mono_class_is_assignable_from (req->modifiers [j].data.exc_class, exc->object.vtable->klass))
+                                               filtered = TRUE;
+                               }
+                       }
+
+                       if (!filtered) {
+                               *suspend_policy = MAX (*suspend_policy, req->suspend_policy);
+                               events = g_slist_append (events, GINT_TO_POINTER (req->id));
+                       }
+               }
+       }
+
+       /* Send a VM START/DEATH event by default */
+       if (event == EVENT_KIND_VM_START)
+               events = g_slist_append (events, GINT_TO_POINTER (0));
+       if (event == EVENT_KIND_VM_DEATH)
+               events = g_slist_append (events, GINT_TO_POINTER (0));
+
+       return events;
+}
+
+static G_GNUC_UNUSED const char*
+event_to_string (EventKind event)
+{
+       switch (event) {
+       case EVENT_KIND_VM_START: return "VM_START";
+       case EVENT_KIND_VM_DEATH: return "VM_DEATH";
+       case EVENT_KIND_THREAD_START: return "THREAD_START";
+       case EVENT_KIND_THREAD_DEATH: return "THREAD_DEATH";
+       case EVENT_KIND_APPDOMAIN_CREATE: return "APPDOMAIN_CREATE";
+       case EVENT_KIND_APPDOMAIN_UNLOAD: return "APPDOMAIN_UNLOAD";
+       case EVENT_KIND_METHOD_ENTRY: return "METHOD_ENTRY";
+       case EVENT_KIND_METHOD_EXIT: return "METHOD_EXIT";
+       case EVENT_KIND_ASSEMBLY_LOAD: return "ASSEMBLY_LOAD";
+       case EVENT_KIND_ASSEMBLY_UNLOAD: return "ASSEMBLY_UNLOAD";
+       case EVENT_KIND_BREAKPOINT: return "BREAKPOINT";
+       case EVENT_KIND_STEP: return "STEP";
+       case EVENT_KIND_TYPE_LOAD: return "TYPE_LOAD";
+       case EVENT_KIND_EXCEPTION: return "EXCEPTION";
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+/*
+ * process_event:
+ *
+ *   Send an event to the client, suspending the vm if needed.
+ * LOCKING: Since this can suspend the calling thread, no locks should be held
+ * by the caller.
+ * The EVENTS list is freed by this function.
+ */
+static void
+process_event (EventKind event, gpointer arg, gint32 il_offset, MonoContext *ctx, GSList *events, int suspend_policy)
+{
+       Buffer buf;
+       GSList *l;
+       MonoDomain *domain = mono_domain_get ();
+       MonoThread *thread;
+
+       if (!agent_config.enabled)
+               return;
+
+       if (!vm_start_event_sent && event != EVENT_KIND_VM_START)
+               // FIXME: We miss those events
+               return;
+
+       if (vm_death_event_sent)
+               return;
+
+       if (mono_runtime_is_shutting_down () && event != EVENT_KIND_VM_DEATH)
+               return;
+
+       if (disconnected)
+               return;
+
+       if (events == NULL)
+               return;
+
+       if (debugger_thread_id == GetCurrentThreadId () && event != EVENT_KIND_VM_DEATH)
+               // FIXME: Send these with a NULL thread, don't suspend the current thread
+               return;
+
+       buffer_init (&buf, 128);
+       buffer_add_byte (&buf, suspend_policy);
+       buffer_add_int (&buf, g_slist_length (events)); // n of events
+
+       for (l = events; l; l = l->next) {
+               buffer_add_byte (&buf, event); // event kind
+               buffer_add_int (&buf, GPOINTER_TO_INT (l->data)); // request id
+
+               thread = mono_thread_current ();
+
+               if (event == EVENT_KIND_VM_START)
+                       thread = arg;
+               else if (event == EVENT_KIND_THREAD_START)
+                       g_assert (mono_thread_internal_current () == arg);
+
+               buffer_add_objid (&buf, (MonoObject*)thread); // thread
+
+               switch (event) {
+               case EVENT_KIND_THREAD_START:
+               case EVENT_KIND_THREAD_DEATH:
+                       break;
+               case EVENT_KIND_APPDOMAIN_CREATE:
+               case EVENT_KIND_APPDOMAIN_UNLOAD:
+                       buffer_add_domainid (&buf, arg);
+                       break;
+               case EVENT_KIND_METHOD_ENTRY:
+               case EVENT_KIND_METHOD_EXIT:
+                       buffer_add_methodid (&buf, domain, arg);
+                       break;
+               case EVENT_KIND_ASSEMBLY_LOAD:
+               case EVENT_KIND_ASSEMBLY_UNLOAD:
+                       buffer_add_assemblyid (&buf, domain, arg);
+                       break;
+               case EVENT_KIND_TYPE_LOAD:
+                       buffer_add_typeid (&buf, domain, arg);
+                       break;
+               case EVENT_KIND_BREAKPOINT:
+               case EVENT_KIND_STEP:
+                       buffer_add_methodid (&buf, domain, arg);
+                       buffer_add_long (&buf, il_offset);
+                       break;
+               case EVENT_KIND_VM_START:
+                       buffer_add_domainid (&buf, mono_get_root_domain ());
+                       break;
+               case EVENT_KIND_VM_DEATH:
+                       break;
+               case EVENT_KIND_EXCEPTION:
+                       buffer_add_objid (&buf, (MonoObject*)arg);
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
+       }
+
+       if (event == EVENT_KIND_VM_START) {
+               suspend_policy = SUSPEND_POLICY_ALL;
+               start_debugger_thread ();
+       }
+   
+       if (event == EVENT_KIND_VM_DEATH) {
+               vm_death_event_sent = TRUE;
+
+               suspend_policy = SUSPEND_POLICY_NONE;
+       }
+
+       if (mono_runtime_is_shutting_down ())
+               suspend_policy = SUSPEND_POLICY_NONE;
+
+       if (suspend_policy != SUSPEND_POLICY_NONE) {
+               /* 
+                * Save the thread context and start suspending before sending the packet,
+                * since we could be receiving the resume request before send_packet ()
+                * returns.
+                */
+               save_thread_context (ctx);
+               suspend_vm ();
+       }
+
+       send_packet (CMD_SET_EVENT, CMD_COMPOSITE, &buf);
+
+       g_slist_free (events);
+       events = NULL;
+
+       if (event == EVENT_KIND_VM_START)
+               vm_start_event_sent = TRUE;
+
+       DEBUG (1, fprintf (log_file, "[%p] Sent event %s, suspend=%d.\n", (gpointer)GetCurrentThreadId (), event_to_string (event), suspend_policy));
+
+       buffer_free (&buf);
+
+       switch (suspend_policy) {
+       case SUSPEND_POLICY_NONE:
+               break;
+       case SUSPEND_POLICY_ALL:
+               suspend_current ();
+               break;
+       case SUSPEND_POLICY_EVENT_THREAD:
+               NOT_IMPLEMENTED;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+static void
+process_profiler_event (EventKind event, gpointer arg)
+{
+       int suspend_policy;
+       GSList *events;
+
+       mono_loader_lock ();
+       events = create_event_list (event, NULL, NULL, &suspend_policy);
+       mono_loader_unlock ();
+
+       process_event (event, arg, 0, NULL, events, suspend_policy);
+}
+
+static void
+runtime_initialized (MonoProfiler *prof)
+{
+       process_profiler_event (EVENT_KIND_VM_START, mono_thread_current ());
+}      
+
+static void
+runtime_shutdown (MonoProfiler *prof)
+{
+       process_profiler_event (EVENT_KIND_VM_DEATH, mono_thread_current ());
+
+       mono_debugger_agent_cleanup ();
+}
+
+static void
+thread_startup (MonoProfiler *prof, gsize tid)
+{
+       MonoInternalThread *thread = mono_thread_internal_current ();
+       MonoInternalThread *old_thread;
+       DebuggerTlsData *tls;
+
+       if (tid == debugger_thread_id)
+               return;
+
+       g_assert (thread->tid == tid);
+
+       mono_loader_lock ();
+       old_thread = mono_g_hash_table_lookup (tid_to_thread, (gpointer)tid);
+       mono_loader_unlock ();
+       if (old_thread) {
+               if (thread == old_thread) {
+                       /* 
+                        * For some reason, thread_startup () might be called for the same thread
+                        * multiple times (attach ?).
+                        */
+                       DEBUG (1, fprintf (log_file, "[%p] thread_start () called multiple times for %p, ignored.\n", (gpointer)tid, (gpointer)tid));
+                       return;
+               } else {
+                       /*
+                        * thread_end () might not be called for some threads, and the tid could
+                        * get reused.
+                        */
+                       DEBUG (1, fprintf (log_file, "[%p] Removing stale data for tid %p.\n", (gpointer)tid, (gpointer)tid));
+                       mono_loader_lock ();
+                       mono_g_hash_table_remove (thread_to_tls, old_thread);
+                       mono_g_hash_table_remove (tid_to_thread, (gpointer)tid);
+                       mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid);
+                       mono_loader_unlock ();
+               }
+       }
+
+       tls = TlsGetValue (debugger_tls_id);
+       g_assert (!tls);
+       // FIXME: Free this somewhere
+       tls = g_new0 (DebuggerTlsData, 1);
+       tls->resume_event = CreateEvent (NULL, FALSE, FALSE, NULL);
+       TlsSetValue (debugger_tls_id, tls);
+
+       DEBUG (1, fprintf (log_file, "[%p] Thread started, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls));
+
+       mono_loader_lock ();
+       mono_g_hash_table_insert (thread_to_tls, thread, tls);
+       mono_g_hash_table_insert (tid_to_thread, (gpointer)tid, thread);
+       mono_g_hash_table_insert (tid_to_thread_obj, (gpointer)tid, mono_thread_current ());
+       mono_loader_unlock ();
+
+       process_profiler_event (EVENT_KIND_THREAD_START, thread);
+
+       /* 
+        * suspend_vm () could have missed this thread, so wait for a resume.
+        */
+       suspend_current ();
+}
+
+static void
+thread_end (MonoProfiler *prof, gsize tid)
+{
+       MonoInternalThread *thread;
+       DebuggerTlsData *tls = NULL;
+
+       mono_loader_lock ();
+       thread = mono_g_hash_table_lookup (tid_to_thread, (gpointer)tid);
+       if (thread) {
+               tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+               /* FIXME: Maybe we need to free this instead, but some code can't handle that */
+               tls->terminated = TRUE;
+               mono_g_hash_table_remove (tid_to_thread_obj, (gpointer)tid);
+               /* Can't remove from tid_to_thread, as that would defeat the check in thread_start () */
+       }
+       mono_loader_unlock ();
+
+       /* We might be called for threads started before we registered the start callback */
+       if (thread) {
+               DEBUG (1, fprintf (log_file, "[%p] Thread terminated, obj=%p, tls=%p.\n", (gpointer)tid, thread, tls));
+               process_profiler_event (EVENT_KIND_THREAD_DEATH, thread);
+       }
+}
+
+static void
+appdomain_load (MonoProfiler *prof, MonoDomain *domain, int result)
+{
+       process_profiler_event (EVENT_KIND_APPDOMAIN_CREATE, domain);
+}
+
+static void
+appdomain_unload (MonoProfiler *prof, MonoDomain *domain)
+{
+       process_profiler_event (EVENT_KIND_APPDOMAIN_UNLOAD, domain);
+}
+
+static void
+assembly_load (MonoProfiler *prof, MonoAssembly *assembly, int result)
+{
+       /* Sent later in jit_end () */
+       mono_loader_lock ();
+       g_ptr_array_add (pending_assembly_loads, assembly);
+       mono_loader_unlock ();
+}
+
+static void
+assembly_unload (MonoProfiler *prof, MonoAssembly *assembly)
+{
+       process_profiler_event (EVENT_KIND_ASSEMBLY_UNLOAD, assembly);
+}
+
+static void
+start_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
+{
+}
+
+static void
+end_runtime_invoke (MonoProfiler *prof, MonoMethod *method)
+{
+       int i;
+
+       if (ss_req == NULL || ss_req->start_sp > __builtin_frame_address (0) || ss_req->thread != mono_thread_internal_current ())
+               return;
+
+       /*
+        * We need to stop single stepping when exiting a runtime invoke, since if it is
+        * a step out, it may return to native code, and thus never end.
+        */
+       mono_loader_lock ();
+       for (i = 0; i < event_requests->len; ++i) {
+               EventRequest *req = g_ptr_array_index (event_requests, i);
+
+               if (req->event_kind == EVENT_KIND_STEP) {
+                       ss_stop (req);
+                        g_ptr_array_remove_index_fast (event_requests, i);
+                        g_free (req);
+                       break;
+               }
+       }
+       mono_loader_unlock ();
+
+}
+
+static void
+jit_end (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo, int result)
+{
+       /*
+        * We emit type load events when the first method of the type is JITted,
+        * since the class load profiler callbacks might be called with the
+        * loader lock held. They could also occur in the debugger thread.
+        * Same for assembly load events.
+        */
+       gboolean type_load = FALSE;
+
+       while (TRUE) {
+               MonoAssembly *assembly = NULL;
+
+               // FIXME: Maybe store this in TLS so the thread of the event is correct ?
+               mono_loader_lock ();
+               if (pending_assembly_loads->len > 0) {
+                       assembly = g_ptr_array_index (pending_assembly_loads, 0);
+                       g_ptr_array_remove_index (pending_assembly_loads, 0);
+               }
+               mono_loader_unlock ();
+
+               if (assembly)
+                       process_profiler_event (EVENT_KIND_ASSEMBLY_LOAD, assembly);
+               else
+                       break;
+       }
+
+       mono_loader_lock ();
+       if (!g_hash_table_lookup (loaded_classes, method->klass)) {
+               type_load = TRUE;
+               g_hash_table_insert (loaded_classes, method->klass, method->klass);
+       }
+       mono_loader_unlock ();
+       if (type_load)
+               process_profiler_event (EVENT_KIND_TYPE_LOAD, method->klass);
+
+       if (!result)
+               add_pending_breakpoints (method, jinfo);
+}
+
+/*
+ * BREAKPOINTS/SINGLE STEPPING
+ */
+
+/* 
+ * Contains information about an inserted breakpoint.
+ */
+typedef struct {
+       long il_offset, native_offset;
+       guint8 *ip;
+       gboolean pending, entry;
+       MonoJitInfo *ji;
+} BreakpointInstance;
+
+/*
+ * Contains generic information about a breakpoint.
+ */
+typedef struct {
+       /* 
+        * The method where the breakpoint is placed. Can be NULL in which case it 
+        * is inserted into every method. This is used to implement method entry/
+        * exit events. Can be a generic method definition, in which case the
+        * breakpoint is inserted into every instance.
+        */
+       MonoMethod *method;
+       long il_offset;
+       gboolean pending, entry;
+       EventRequest *req;
+       /* 
+        * A list of BreakpointInstance structures describing where the breakpoint
+        * was inserted. There could be more than one because of 
+        * generics/appdomains/method entry/exit.
+        */
+       GPtrArray *children;
+} MonoBreakpoint;
+
+/* List of breakpoints */
+static GPtrArray *breakpoints;
+/* Maps breakpoint locations to the number of breakpoints at that location */
+static GHashTable *bp_locs;
+
+static void
+breakpoints_init (void)
+{
+       breakpoints = g_ptr_array_new ();
+       bp_locs = g_hash_table_new (NULL, NULL);
+}      
+
+static void
+breakpoints_cleanup (void)
+{
+       int i;
+
+       mono_loader_lock ();
+
+       for (i = 0; i < breakpoints->len; ++i)
+               g_free (g_ptr_array_index (breakpoints, i));
+
+       g_ptr_array_free (breakpoints, TRUE);
+       g_hash_table_destroy (bp_locs);
+
+       mono_loader_unlock ();
+}
+
+/*
+ * insert_breakpoint:
+ *
+ *   Insert the breakpoint described by BP into the method described by
+ * JI.
+ */
+static void
+insert_breakpoint (GPtrArray *seq_points, MonoJitInfo *ji, MonoBreakpoint *bp)
+{
+       int i, count;
+       gint32 il_offset, native_offset;
+       BreakpointInstance *inst;
+
+       native_offset = 0;
+       for (i = 0; i < seq_points->len; i += 2) {
+               il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i));
+               native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1));
+
+               if (il_offset == bp->il_offset)
+                       break;
+       }
+
+       if (i == seq_points->len)
+               /* Have to handle this somehow */
+               NOT_IMPLEMENTED;
+
+       inst = g_new0 (BreakpointInstance, 1);
+       inst->native_offset = native_offset;
+       inst->ip = (guint8*)ji->code_start + native_offset;
+       inst->ji = ji;
+
+       mono_loader_lock ();
+
+       g_ptr_array_add (bp->children, inst);
+
+       count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, inst->ip));
+       g_hash_table_insert (bp_locs, inst->ip, GINT_TO_POINTER (count + 1));
+       mono_loader_unlock ();
+
+       if (count == 0) {
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+               mono_arch_set_breakpoint (ji, inst->ip);
+#else
+               NOT_IMPLEMENTED;
+#endif
+       }
+}
+
+static void
+remove_breakpoint (BreakpointInstance *inst)
+{
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       int count;
+       MonoJitInfo *ji = inst->ji;
+       guint8 *ip = inst->ip;
+
+       mono_loader_lock ();
+       count = GPOINTER_TO_INT (g_hash_table_lookup (bp_locs, ip));
+       g_hash_table_insert (bp_locs, ip, GINT_TO_POINTER (count - 1));
+       mono_loader_unlock ();
+
+       g_assert (count > 0);
+
+       if (count == 1) {
+               mono_arch_clear_breakpoint (ji, ip);
+       }
+#else
+       NOT_IMPLEMENTED;
+#endif
+}      
+
+/*
+ * add_pending_breakpoints:
+ *
+ *   Insert pending breakpoints into the newly JITted method METHOD.
+ */
+static void
+add_pending_breakpoints (MonoMethod *method, MonoJitInfo *ji)
+{
+       int i;
+       GPtrArray *seq_points;
+       MonoDomain *domain;
+
+       if (!breakpoints)
+               return;
+
+       domain = mono_domain_get ();
+
+       mono_loader_lock ();
+
+       for (i = 0; i < breakpoints->len; ++i) {
+               MonoBreakpoint *bp = g_ptr_array_index (breakpoints, i);
+
+               if (bp->pending && (bp->method == method || !bp->method || (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method))) {
+                       mono_domain_lock (domain);
+                       seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method);
+                       mono_domain_unlock (domain);
+                       if (!seq_points)
+                               /* Could be AOT code */
+                               continue;
+                       g_assert (seq_points);
+
+                       insert_breakpoint (seq_points, ji, bp);
+               }
+       }
+
+       mono_loader_unlock ();
+}
+
+static void
+set_bp_in_method (MonoDomain *domain, MonoMethod *method, GPtrArray *seq_points, MonoBreakpoint *bp)
+{
+       gpointer code;
+       MonoJitInfo *ji;
+
+       code = mono_jit_find_compiled_method_with_jit_info (domain, method, &ji);
+       if (!code) {
+               /* Might be AOTed code */
+               code = mono_aot_get_method (domain, method);
+               g_assert (code);
+               ji = mono_jit_info_table_find (domain, code);
+               g_assert (ji);
+       }
+       g_assert (code);
+
+       insert_breakpoint (seq_points, ji, bp);
+}
+
+static void
+set_bp_in_method_cb (gpointer key, gpointer value, gpointer user_data)
+{
+       MonoMethod *method = key;
+       GPtrArray *seq_points = value;
+       MonoBreakpoint *bp = user_data;
+       MonoDomain *domain = mono_domain_get ();
+
+       if (bp->method) {
+               if (method->is_inflated && ((MonoMethodInflated*)method)->declaring == bp->method) {
+                       /* Generic instance */
+                       set_bp_in_method (domain, method, seq_points, bp);
+               }
+       } else {
+               /* Method entry/exit */
+               set_bp_in_method (domain, method, seq_points, bp);
+       }
+}
+
+/*
+ * set_breakpoint:
+ *
+ *   Set a breakpoint at IL_OFFSET in METHOD.
+ * METHOD can be NULL, in which case a breakpoint is placed in all methods.
+ * METHOD can also be a generic method definition, in which case a breakpoint
+ * is placed in all instances of the method.
+ */
+static MonoBreakpoint*
+set_breakpoint (MonoMethod *method, long il_offset, EventRequest *req)
+{
+       GPtrArray *seq_points;
+       MonoDomain *domain;
+       MonoBreakpoint *bp;
+
+       // FIXME: 
+       // - suspend/resume the vm to prevent code patching problems
+       // - appdomains
+       // - multiple breakpoints on the same location
+       // - dynamic methods
+       // - races
+
+       bp = g_new0 (MonoBreakpoint, 1);
+       bp->method = method;
+       bp->il_offset = il_offset;
+       bp->req = req;
+       bp->children = g_ptr_array_new ();
+
+       domain = mono_domain_get ();
+       mono_domain_lock (domain);
+       if (method) {
+               seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, method);
+               if (seq_points) {
+                       set_bp_in_method (domain, method, seq_points, bp);
+               } else {
+                       if (method->is_generic)
+                               /* There might be already JITted instances */
+                               g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, bp);
+
+                       /* Not yet JITted */
+                       bp->pending = TRUE;
+               }
+       } else {
+               g_hash_table_foreach (domain_jit_info (domain)->seq_points, set_bp_in_method_cb, bp);
+               bp->pending = TRUE;
+       }
+       mono_domain_unlock (domain);
+
+       mono_loader_lock ();
+       g_ptr_array_add (breakpoints, bp);
+       mono_loader_unlock ();
+
+       return bp;
+}
+
+static void
+clear_breakpoint (MonoBreakpoint *bp)
+{
+       int i;
+
+       // FIXME: locking, races
+       for (i = 0; i < bp->children->len; ++i) {
+               BreakpointInstance *inst = g_ptr_array_index (bp->children, i);
+
+               remove_breakpoint (inst);
+
+               g_free (inst);
+       }
+
+       mono_loader_lock ();
+       g_ptr_array_remove (breakpoints, bp);
+       mono_loader_unlock ();
+
+       g_ptr_array_free (bp->children, TRUE);
+       g_free (bp);
+}
+
+static void
+process_breakpoint_inner (MonoContext *ctx)
+{
+       MonoJitInfo *ji;
+       guint8 *orig_ip, *ip;
+       int i, j, suspend_policy;
+       guint32 native_offset;
+       MonoBreakpoint *bp;
+       BreakpointInstance *inst;
+       GPtrArray *reqs;
+       GSList *events = NULL;
+       EventKind kind = EVENT_KIND_BREAKPOINT;
+
+       // FIXME: Speed this up
+
+       orig_ip = ip = MONO_CONTEXT_GET_IP (ctx);
+       ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+       g_assert (ji);
+       g_assert (ji->method);
+
+       /* Compute the native offset of the breakpoint from the ip */
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       ip = mono_arch_get_ip_for_breakpoint (ji, ctx);
+       native_offset = ip - (guint8*)ji->code_start;   
+#else
+       NOT_IMPLEMENTED;
+#endif
+
+       /* 
+        * Skip the instruction causing the breakpoint signal.
+        */
+       mono_arch_skip_breakpoint (ctx);
+
+       if (ji->method->wrapper_type)
+               return;
+
+       reqs = g_ptr_array_new ();
+
+       DEBUG(1, fprintf (log_file, "[%p] Breakpoint hit, method=%s, offset=0x%x.\n", (gpointer)GetCurrentThreadId (), ji->method->name, native_offset));
+
+       mono_loader_lock ();
+
+       bp = NULL;
+       for (i = 0; i < breakpoints->len; ++i) {
+               bp = g_ptr_array_index (breakpoints, i);
+
+               if (!bp->method)
+                       continue;
+
+               for (j = 0; j < bp->children->len; ++j) {
+                       inst = g_ptr_array_index (bp->children, j);
+                       if (inst->ji == ji && inst->native_offset == native_offset)
+                               g_ptr_array_add (reqs, bp->req);
+               }
+       }
+       if (reqs->len == 0) {
+               GPtrArray *seq_points;
+               int seq_il_offset, seq_native_offset;
+               MonoDomain *domain = mono_domain_get ();
+
+               /* Maybe a method entry/exit event */
+               mono_domain_lock (domain);
+               seq_points = g_hash_table_lookup (domain_jit_info (domain)->seq_points, ji->method);
+               mono_domain_unlock (domain);
+               if (!seq_points) {
+                       // FIXME: Generic sharing */
+                       mono_loader_unlock ();
+                       return;
+               }
+               g_assert (seq_points);
+
+               for (i = 0; i < seq_points->len; i += 2) {
+                       seq_il_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i));
+                       seq_native_offset = GPOINTER_TO_INT (g_ptr_array_index (seq_points, i + 1));
+
+                       if (native_offset == seq_native_offset) {
+                               if (seq_il_offset == METHOD_ENTRY_IL_OFFSET)
+                                       kind = EVENT_KIND_METHOD_ENTRY;
+                               else if (seq_il_offset == METHOD_EXIT_IL_OFFSET)
+                                       kind = EVENT_KIND_METHOD_EXIT;
+                               break;
+                       }
+               }
+       }
+       
+       if (reqs->len > 0)
+               events = create_event_list (EVENT_KIND_BREAKPOINT, reqs, NULL, &suspend_policy);
+       else if (kind != EVENT_KIND_BREAKPOINT)
+               events = create_event_list (kind, NULL, NULL, &suspend_policy);
+
+       g_ptr_array_free (reqs, TRUE);
+
+       mono_loader_unlock ();
+
+       if (events)
+               process_event (kind, ji->method, 0, ctx, events, suspend_policy);
+}
+
+static void
+process_breakpoint (void)
+{
+       DebuggerTlsData *tls;
+       MonoContext ctx;
+       static void (*restore_context) (void *);
+
+       if (!restore_context)
+               restore_context = mono_get_restore_context ();
+
+       tls = TlsGetValue (debugger_tls_id);
+       memcpy (&ctx, &tls->handler_ctx, sizeof (MonoContext));
+
+       process_breakpoint_inner (&ctx);
+
+       /* This is called when resuming from a signal handler, so it shouldn't return */
+       restore_context (&ctx);
+       g_assert_not_reached ();
+}
+
+void
+mono_debugger_agent_breakpoint_hit (void *sigctx)
+{
+       DebuggerTlsData *tls;
+       MonoContext ctx;
+
+       /*
+        * We are called from a signal handler, and running code there causes all kinds of
+        * problems, like the original signal is disabled, libgc can't handle altstack, etc.
+        * So set up the signal context to return to the real breakpoint handler function.
+        */
+
+       // FIXME: This might not work on an altstack ?
+       tls = TlsGetValue (debugger_tls_id);
+       g_assert (tls);
+
+       mono_arch_sigctx_to_monoctx (sigctx, &ctx);
+       memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
+       
+       MONO_CONTEXT_SET_IP (&ctx, process_breakpoint);
+
+       mono_arch_monoctx_to_sigctx (&ctx, sigctx);
+}
+
+static void
+process_single_step_inner (MonoContext *ctx)
+{
+       MonoJitInfo *ji;
+       guint8 *ip;
+       GPtrArray *reqs;
+       int il_offset, suspend_policy;
+       MonoDomain *domain = mono_domain_get ();
+       GSList *events;
+
+       // FIXME: Speed this up
+
+       ip = MONO_CONTEXT_GET_IP (ctx);
+
+       /* Skip the instruction causing the single step */
+       mono_arch_skip_single_step (ctx);
+
+       if (suspend_count > 0) {
+               if (debugger_thread_id == GetCurrentThreadId ())
+                       return;
+
+               DEBUG(1, fprintf (log_file, "[%p] Received single step event for suspending.\n", (gpointer)GetCurrentThreadId ()));
+
+               ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+
+               /* See the comment below */
+               if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
+                       return;
+
+               save_thread_context (ctx);
+               suspend_current ();
+               return;
+       }
+
+       if (!ss_req)
+               // FIXME: A suspend race
+               return;
+
+       if (mono_thread_internal_current () != ss_req->thread)
+               return;
+
+       if (log_level > 0) {
+               const char *depth = NULL;
+
+               ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+
+               switch (ss_req->depth) {
+               case STEP_DEPTH_OVER:
+                       depth = "over";
+                       break;
+               case STEP_DEPTH_OUT:
+                       depth = "out";
+                       break;
+               case STEP_DEPTH_INTO:
+                       depth = "into";
+                       break;
+               default:
+                       g_assert_not_reached ();
+               }
+                       
+               DEBUG (1, fprintf (log_file, "[%p] Single step event (depth=%s) at %s (%p), sp %p, last sp %p\n", (gpointer)GetCurrentThreadId (), ss_req->depth == STEP_DEPTH_OVER ? "over" : "out", mono_method_full_name (ji->method, TRUE), MONO_CONTEXT_GET_IP (ctx), MONO_CONTEXT_GET_SP (ctx), ss_req->last_sp));
+       }
+
+       /*
+        * We implement step over/out by single stepping until we reach the same 
+        * frame/parent frame.
+        * FIXME:
+        * - this is slow
+        * - stack growing upward
+        * - localloc
+        * - exceptions
+        */
+       if (ss_req->depth != STEP_DEPTH_INTO) {
+               if (ss_req->depth == STEP_DEPTH_OVER && MONO_CONTEXT_GET_SP (ctx) < ss_req->last_sp)
+                       return;
+               if (ss_req->depth == STEP_DEPTH_OUT && MONO_CONTEXT_GET_SP (ctx) <= ss_req->last_sp)
+                       return;
+
+               ss_req->last_sp = MONO_CONTEXT_GET_SP (ctx);
+       }
+
+       ji = mono_jit_info_table_find (mono_domain_get (), (char*)ip);
+       g_assert (ji);
+       g_assert (ji->method);
+
+       if (ji->method->wrapper_type && ji->method->wrapper_type != MONO_WRAPPER_DYNAMIC_METHOD)
+               return;
+
+       /* 
+        * FIXME: 
+        * Stopping in memset makes half-initialized vtypes visible.
+        * Stopping in memcpy makes half-copied vtypes visible.
+        */
+       if (ji->method->klass == mono_defaults.string_class && (!strcmp (ji->method->name, "memset") || strstr (ji->method->name, "memcpy")))
+               return;
+
+       /* 
+        * The ip points to the instruction causing the single step event, convert it
+        * to the offset stored in seq_points.
+        */
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       ip = mono_arch_get_ip_for_single_step (ji, ctx);
+#else
+       g_assert_not_reached ();
+#endif
+
+       /* 
+        * mono_debug_lookup_source_location () doesn't work for IL offset 0 for 
+        * example, so do things by hand.
+        */
+       il_offset = compute_il_offset (domain, ji->method, (guint8*)ip - (guint8*)ji->code_start);
+
+       if (il_offset == -1)
+               return;
+
+       if (ss_req->size == STEP_SIZE_LINE) {
+               /* Step until a different source line is reached */
+               MonoDebugMethodInfo *minfo;
+
+               minfo = mono_debug_lookup_method (ji->method);
+
+               if (minfo) {
+                       MonoDebugSourceLocation *loc = mono_debug_symfile_lookup_location (minfo, il_offset);
+
+                       if (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line) {
+                               mono_debug_free_source_location (loc);
+                               return;
+                       }
+                       if (!loc)
+                               /*
+                                * Step until we reach a location with line number info, 
+                                * otherwise the client can't show a location.
+                                * This can happen for example with statics initialized inline
+                                * outside of a cctor.
+                                */
+                               return;
+
+                       if (loc) {
+                               ss_req->last_method = ji->method;
+                               ss_req->last_line = loc->row;
+                               mono_debug_free_source_location (loc);
+                       }
+               }
+       }
+
+       // FIXME: Has to lock earlier
+
+       reqs = g_ptr_array_new ();
+
+       mono_loader_lock ();
+
+       g_ptr_array_add (reqs, ss_req->req);
+
+       events = create_event_list (EVENT_KIND_STEP, reqs, NULL, &suspend_policy);
+
+       g_ptr_array_free (reqs, TRUE);
+
+       mono_loader_unlock ();
+
+       process_event (EVENT_KIND_STEP, ji->method, il_offset, ctx, events, suspend_policy);
+}
+
+static void
+process_single_step (void)
+{
+       DebuggerTlsData *tls;
+       MonoContext ctx;
+       static void (*restore_context) (void *);
+
+       if (!restore_context)
+               restore_context = mono_get_restore_context ();
+
+       tls = TlsGetValue (debugger_tls_id);
+       memcpy (&ctx, &tls->handler_ctx, sizeof (MonoContext));
+
+       process_single_step_inner (&ctx);
+
+       /* This is called when resuming from a signal handler, so it shouldn't return */
+       restore_context (&ctx);
+       g_assert_not_reached ();
+}
+
+/*
+ * mono_debugger_agent_single_step_event:
+ *
+ *   Called from a signal handler to handle a single step event.
+ */
+void
+mono_debugger_agent_single_step_event (void *sigctx)
+{
+       DebuggerTlsData *tls;
+       MonoContext ctx;
+
+       /* Resume to process_single_step through the signal context */
+
+       // FIXME: Since step out/over is implemented using step in, the step in case should
+       // be as fast as possible. Move the relevant code from process_single_step_inner ()
+       // here
+
+       /* Save the original context in TLS */
+       // FIXME: This might not work on an altstack ?
+       tls = TlsGetValue (debugger_tls_id);
+       g_assert (tls);
+       mono_arch_sigctx_to_monoctx (sigctx, &ctx);
+       memcpy (&tls->handler_ctx, &ctx, sizeof (MonoContext));
+       
+       MONO_CONTEXT_SET_IP (&ctx, process_single_step);
+
+       mono_arch_monoctx_to_sigctx (&ctx, sigctx);
+}
+
+/*
+ * start_single_stepping:
+ *
+ *   Turn on single stepping. Can be called multiple times, for example,
+ * by a single step event request + a suspend.
+ */
+static void
+start_single_stepping (void)
+{
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       int val = InterlockedIncrement (&ss_count);
+
+       if (val == 1)
+               mono_arch_start_single_stepping ();
+#else
+       g_assert_not_reached ();
+#endif
+}
+
+static void
+stop_single_stepping (void)
+{
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       int val = InterlockedDecrement (&ss_count);
+
+       if (val == 0)
+               mono_arch_stop_single_stepping ();
+#else
+       g_assert_not_reached ();
+#endif
+}
+
+/*
+ * Start single stepping of thread THREAD
+ */
+static ErrorCode
+ss_start (MonoInternalThread *thread, StepSize size, StepDepth depth, EventRequest *req)
+{
+       DebuggerTlsData *tls;
+
+       if (suspend_count == 0)
+               return ERR_NOT_SUSPENDED;
+
+       wait_for_suspend ();
+
+       // FIXME: Multiple requests
+       if (ss_req)
+               return ERR_NOT_IMPLEMENTED;
+
+       ss_req = g_new0 (MonoSingleStepReq, 1);
+       ss_req->req = req;
+       ss_req->thread = thread;
+       ss_req->size = size;
+       ss_req->depth = depth;
+       req->info = ss_req;
+
+       mono_loader_lock ();
+       tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+       mono_loader_unlock ();
+       g_assert (tls);
+       g_assert (tls->has_context);
+       ss_req->start_sp = ss_req->last_sp = MONO_CONTEXT_GET_SP (&tls->ctx);
+
+       if (ss_req->size == STEP_SIZE_LINE) {
+               StackFrame *frame;
+               MonoDebugMethodInfo *minfo;
+
+               /* Compute the initial line info */
+               compute_frame_info (thread, tls);
+
+               g_assert (tls->frame_count);
+               frame = tls->frames [0];
+
+               ss_req->last_method = frame->method;
+               ss_req->last_line = -1;
+
+               minfo = mono_debug_lookup_method (frame->method);
+               if (minfo && frame->il_offset != -1) {
+                       MonoDebugSourceLocation *loc = mono_debug_symfile_lookup_location (minfo, frame->il_offset);
+
+                       if (loc) {
+                               ss_req->last_line = loc->row;
+                               g_free (loc);
+                       }
+               }
+       }
+
+       start_single_stepping ();
+
+       return 0;
+}
+
+static void
+ss_stop (EventRequest *req)
+{
+       // FIXME: Locking
+       g_assert (ss_req);
+       g_assert (ss_req->req == req);
+
+       g_free (ss_req);
+       ss_req = NULL;
+
+       stop_single_stepping ();
+}
+
+void
+mono_debugger_agent_handle_exception (MonoException *exc, MonoContext *ctx)
+{
+       int suspend_policy;
+       GSList *events;
+
+       if (!agent_config.enabled)
+               return;
+
+       mono_loader_lock ();
+       events = create_event_list (EVENT_KIND_EXCEPTION, NULL, exc, &suspend_policy);
+       mono_loader_unlock ();
+
+       process_event (EVENT_KIND_EXCEPTION, exc, 0, ctx, events, suspend_policy);
+}
+
+/*
+ * buffer_add_value:
+ *
+ *   Add the encoding of the value at ADDR described by T to the buffer.
+ */
+static void
+buffer_add_value (Buffer *buf, MonoType *t, void *addr, MonoDomain *domain)
+{
+       MonoObject *obj;
+
+       if (t->byref) {
+               g_assert (*(void**)addr);
+               addr = *(void**)addr;
+       }
+
+       switch (t->type) {
+       case MONO_TYPE_VOID:
+               buffer_add_byte (buf, t->type);
+               break;
+       case MONO_TYPE_BOOLEAN:
+       case MONO_TYPE_I1:
+       case MONO_TYPE_U1:
+               buffer_add_byte (buf, t->type);
+               buffer_add_int (buf, *(gint8*)addr);
+               break;
+       case MONO_TYPE_CHAR:
+       case MONO_TYPE_I2:
+       case MONO_TYPE_U2:
+               buffer_add_byte (buf, t->type);
+               buffer_add_int (buf, *(gint16*)addr);
+               break;
+       case MONO_TYPE_I4:
+       case MONO_TYPE_U4:
+       case MONO_TYPE_R4:
+               buffer_add_byte (buf, t->type);
+               buffer_add_int (buf, *(gint32*)addr);
+               break;
+       case MONO_TYPE_I8:
+       case MONO_TYPE_U8:
+       case MONO_TYPE_R8:
+               buffer_add_byte (buf, t->type);
+               buffer_add_long (buf, *(gint64*)addr);
+               break;
+       case MONO_TYPE_I:
+       case MONO_TYPE_U:
+       case MONO_TYPE_PTR: {
+               gssize val = *(gssize*)addr;
+               
+               buffer_add_byte (buf, t->type);
+               buffer_add_long (buf, val);
+               break;
+       }
+       handle_ref:
+       case MONO_TYPE_STRING:
+       case MONO_TYPE_SZARRAY:
+       case MONO_TYPE_OBJECT:
+       case MONO_TYPE_CLASS:
+       case MONO_TYPE_ARRAY:
+               obj = *(MonoObject**)addr;
+
+               if (!obj) {
+                       buffer_add_byte (buf, VALUE_TYPE_ID_NULL);
+               } else {
+                       if (obj->vtable->klass->valuetype) {
+                               t = &obj->vtable->klass->byval_arg;
+                               addr = mono_object_unbox (obj);
+                               goto handle_vtype;
+                       } else if (obj->vtable->klass->rank) {
+                               buffer_add_byte (buf, obj->vtable->klass->byval_arg.type);
+                       } else if (obj->vtable->klass->byval_arg.type == MONO_TYPE_GENERICINST) {
+                               buffer_add_byte (buf, MONO_TYPE_CLASS);
+                       } else {
+                               buffer_add_byte (buf, obj->vtable->klass->byval_arg.type);
+                       }
+                       buffer_add_objid (buf, obj);
+               }
+               break;
+       handle_vtype:
+       case MONO_TYPE_VALUETYPE: {
+               int nfields;
+               gpointer iter;
+               MonoClassField *f;
+               MonoClass *klass = mono_class_from_mono_type (t);
+
+               buffer_add_byte (buf, MONO_TYPE_VALUETYPE);
+               buffer_add_byte (buf, klass->enumtype);
+               buffer_add_typeid (buf, domain, klass);
+
+               nfields = 0;
+               iter = NULL;
+               while ((f = mono_class_get_fields (klass, &iter))) {
+                       if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
+                               continue;
+                       if (mono_field_is_deleted (f))
+                               continue;
+                       nfields ++;
+               }
+               buffer_add_int (buf, nfields);
+
+               iter = NULL;
+               while ((f = mono_class_get_fields (klass, &iter))) {
+                       if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
+                               continue;
+                       if (mono_field_is_deleted (f))
+                               continue;
+                       buffer_add_value (buf, f->type, (guint8*)addr + f->offset - sizeof (MonoObject), domain);
+               }
+               break;
+       }
+       case MONO_TYPE_GENERICINST:
+               if (mono_type_generic_inst_is_valuetype (t)) {
+                       goto handle_vtype;
+               } else {
+                       goto handle_ref;
+               }
+               break;
+       default:
+               NOT_IMPLEMENTED;
+       }
+}
+
+static ErrorCode
+decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       int err;
+       int type = decode_byte (buf, &buf, limit);
+
+       if (type != t->type && !MONO_TYPE_IS_REFERENCE (t)) {
+               DEBUG(1, fprintf (log_file, "Expected value of type %d, got %d.\n", t->type, type));
+               return ERR_INVALID_ARGUMENT;
+       }
+
+       switch (t->type) {
+       case MONO_TYPE_BOOLEAN:
+               *(guint8*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_CHAR:
+               *(gunichar2*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_I1:
+               *(gint8*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_U1:
+               *(guint8*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_I2:
+               *(gint16*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_U2:
+               *(guint16*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_I4:
+               *(gint32*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_U4:
+               *(guint32*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_I8:
+               *(gint64*)addr = decode_long (buf, &buf, limit);
+               break;
+       case MONO_TYPE_U8:
+               *(guint64*)addr = decode_long (buf, &buf, limit);
+               break;
+       case MONO_TYPE_R4:
+               *(guint32*)addr = decode_int (buf, &buf, limit);
+               break;
+       case MONO_TYPE_R8:
+               *(guint64*)addr = decode_long (buf, &buf, limit);
+               break;
+       case MONO_TYPE_VALUETYPE: {
+               gboolean is_enum = decode_byte (buf, &buf, limit);
+               MonoClass *klass;
+               MonoClassField *f;
+               int nfields;
+               gpointer iter = NULL;
+               MonoDomain *d;
+
+               /* Enums are sent as a normal vtype */
+               if (is_enum)
+                       return ERR_NOT_IMPLEMENTED;
+               klass = decode_typeid (buf, &buf, limit, &d, &err);
+               if (err)
+                       return err;
+
+               if (klass != mono_class_from_mono_type (t))
+                       return ERR_INVALID_ARGUMENT;
+
+               nfields = decode_int (buf, &buf, limit);
+               while ((f = mono_class_get_fields (klass, &iter))) {
+                       if (f->type->attrs & FIELD_ATTRIBUTE_STATIC)
+                               continue;
+                       if (mono_field_is_deleted (f))
+                               continue;
+                       err = decode_value (f->type, domain, (guint8*)addr + f->offset - sizeof (MonoObject), buf, &buf, limit);
+                       if (err)
+                               return err;
+                       nfields --;
+               }
+               g_assert (nfields == 0);
+               break;
+       }
+       default:
+               if (MONO_TYPE_IS_REFERENCE (t)) {
+                       if (type == MONO_TYPE_OBJECT) {
+                               int objid = decode_objid (buf, &buf, limit);
+                               int err;
+                               MonoObject *obj;
+
+                               err = get_object (objid, (MonoObject**)&obj);
+                               if (err)
+                                       return err;
+
+                               if (obj && !mono_class_is_assignable_from (mono_class_from_mono_type (t), obj->vtable->klass))
+                                       return ERR_INVALID_ARGUMENT;
+                               if (obj && obj->vtable->domain != domain)
+                                       return ERR_INVALID_ARGUMENT;
+
+                               mono_gc_wbarrier_generic_store (addr, obj);
+                       } else if (type == VALUE_TYPE_ID_NULL) {
+                               *(MonoObject**)addr = NULL;
+                       } else {
+                               return ERR_INVALID_ARGUMENT;
+                       }
+               } else {
+                       NOT_IMPLEMENTED;
+               }
+               break;
+       }
+
+       *endbuf = buf;
+
+       return 0;
+}
+
+static void
+add_var (Buffer *buf, MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain)
+{
+       guint32 flags;
+       int reg;
+       guint8 *addr;
+       gpointer reg_val;
+
+       flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+       reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+
+       switch (flags) {
+       case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
+               reg_val = mono_arch_context_get_int_reg (ctx, reg);
+
+               buffer_add_value (buf, t, &reg_val, domain);
+               break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
+               addr = mono_arch_context_get_int_reg (ctx, reg);
+               addr += (gint32)var->offset;
+
+               //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
+
+               buffer_add_value (buf, t, addr, domain);
+               break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
+               NOT_IMPLEMENTED;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+static void
+set_var (MonoType *t, MonoDebugVarInfo *var, MonoContext *ctx, MonoDomain *domain, guint8 *val)
+{
+       guint32 flags;
+       int reg, size;
+       guint8 *addr;
+
+       flags = var->index & MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+       reg = var->index & ~MONO_DEBUG_VAR_ADDRESS_MODE_FLAGS;
+
+       if (MONO_TYPE_IS_REFERENCE (t))
+               size = sizeof (gpointer);
+       else
+               size = mono_class_value_size (mono_class_from_mono_type (t), NULL);
+
+       switch (flags) {
+       case MONO_DEBUG_VAR_ADDRESS_MODE_REGISTER:
+               // FIXME: Can't set registers, so we disable linears
+               NOT_IMPLEMENTED;
+               break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_REGOFFSET:
+               addr = mono_arch_context_get_int_reg (ctx, reg);
+               addr += (gint32)var->offset;
+
+               //printf ("[R%d+%d] = %p\n", reg, var->offset, addr);
+
+               // FIXME: Write barriers
+               memcpy (addr, val, size);
+               break;
+       case MONO_DEBUG_VAR_ADDRESS_MODE_DEAD:
+               NOT_IMPLEMENTED;
+               break;
+       default:
+               g_assert_not_reached ();
+       }
+}
+
+static void
+clear_event_request (int req_id, int etype)
+{
+       int i;
+
+       mono_loader_lock ();
+       for (i = 0; i < event_requests->len; ++i) {
+               EventRequest *req = g_ptr_array_index (event_requests, i);
+
+               if (req->id == req_id && req->event_kind == etype) {
+                       if (req->event_kind == EVENT_KIND_BREAKPOINT)
+                               clear_breakpoint (req->info);
+                       if (req->event_kind == EVENT_KIND_STEP)
+                               ss_stop (req);
+                       g_ptr_array_remove_index_fast (event_requests, i);
+                       g_free (req);
+                       break;
+               }
+       }
+       mono_loader_unlock ();
+}
+
+static void
+add_thread (gpointer key, gpointer value, gpointer user_data)
+{
+       MonoInternalThread *thread = value;
+       Buffer *buf = user_data;
+
+       buffer_add_objid (buf, (MonoObject*)thread);
+}
+
+static ErrorCode
+do_invoke_method (Buffer *buf, InvokeData *invoke)
+{
+       guint8 *p = invoke->p;
+       guint8 *end = invoke->endp;
+       MonoMethod *m;
+       int i, err, nargs;
+       MonoMethodSignature *sig;
+       guint8 **arg_buf;
+       void **args;
+       MonoObject *this, *res, *exc;
+       MonoDomain *domain;
+       guint8 *this_buf;
+#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+       MonoLMFExt ext;
+#endif
+
+       m = decode_methodid (p, &p, end, &domain, &err);
+       if (err)
+               return err;
+       sig = mono_method_signature (m);
+
+       if (m->klass->valuetype)
+               this_buf = g_alloca (mono_class_instance_size (m->klass));
+       else
+               this_buf = g_alloca (sizeof (MonoObject*));
+       err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
+       if (err)
+               return err;
+
+       if (!m->klass->valuetype)
+               this = *(MonoObject**)this_buf;
+       else
+               this = NULL;
+
+       DEBUG (1, printf ("[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (m, TRUE), this ? this->vtable->klass->name : "<null>"));
+
+       if (this && this->vtable->domain != domain)
+               NOT_IMPLEMENTED;
+
+       if (!m->klass->valuetype && !(m->flags & METHOD_ATTRIBUTE_STATIC) && !this) {
+               if (!strcmp (m->name, ".ctor")) {
+                       if (m->klass->flags & TYPE_ATTRIBUTE_ABSTRACT)
+                               return ERR_INVALID_ARGUMENT;
+                       else
+                               this = mono_object_new (domain, m->klass);
+               } else {
+                       return ERR_INVALID_ARGUMENT;
+               }
+       }
+
+       if (this && !mono_class_is_assignable_from (m->klass, this->vtable->klass))
+               return ERR_INVALID_ARGUMENT;
+
+       nargs = decode_int (p, &p, end);
+       if (nargs != sig->param_count)
+               return ERR_INVALID_ARGUMENT;
+       /* Use alloca to get gc tracking */
+       arg_buf = g_alloca (nargs * sizeof (gpointer));
+       memset (arg_buf, 0, nargs * sizeof (gpointer));
+       args = g_alloca (nargs * sizeof (gpointer));
+       for (i = 0; i < nargs; ++i) {
+               if (MONO_TYPE_IS_REFERENCE (sig->params [i])) {
+                       err = decode_value (sig->params [i], domain, (guint8*)&args [i], p, &p, end);
+                       if (err)
+                               break;
+
+                       if (args [i] && ((MonoObject*)args [i])->vtable->domain != domain)
+                               NOT_IMPLEMENTED;
+               } else {
+                       arg_buf [i] = g_alloca (mono_class_instance_size (mono_class_from_mono_type (sig->params [i])));
+                       err = decode_value (sig->params [i], domain, arg_buf [i], p, &p, end);
+                       if (err)
+                               break;
+                       args [i] = arg_buf [i];
+               }
+       }
+
+       if (i < nargs)
+               return err;
+
+       /* 
+        * Add an LMF frame to link the stack frames on the invoke method with our caller.
+        */
+       /* FIXME: Move this to arch specific code */
+#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+       if (invoke->has_ctx) {
+               MonoLMF **lmf_addr;
+
+               lmf_addr = mono_get_lmf_addr ();
+
+               /* Setup our lmf */
+               memset (&ext, 0, sizeof (ext));
+#ifdef TARGET_AMD64
+               ext.lmf.previous_lmf = *(lmf_addr);
+               /* Mark that this is a MonoLMFExt */
+               ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
+               ext.lmf.rsp = (gssize)&ext;
+#elif defined(TARGET_X86)
+               ext.lmf.previous_lmf = (gsize)*(lmf_addr);
+               /* Mark that this is a MonoLMFExt */
+               ext.lmf.previous_lmf = (gsize)(gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
+               ext.lmf.ebp = (gssize)&ext;
+#elif defined(TARGET_ARM)
+               ext.lmf.previous_lmf = *(lmf_addr);
+               /* Mark that this is a MonoLMFExt */
+               ext.lmf.previous_lmf = (gpointer)(((gssize)ext.lmf.previous_lmf) | 2);
+               ext.lmf.ebp = (gssize)&ext;
+#else
+               g_assert_not_reached ();
+#endif
+
+               ext.debugger_invoke = TRUE;
+               memcpy (&ext.ctx, &invoke->ctx, sizeof (MonoContext));
+
+               mono_set_lmf ((MonoLMF*)&ext);
+       }
+#endif
+
+       if (m->klass->valuetype)
+               res = mono_runtime_invoke (m, this_buf, args, &exc);
+       else
+               res = mono_runtime_invoke (m, this, args, &exc);
+       if (exc) {
+               buffer_add_byte (buf, 0);
+               buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &exc, domain);
+       } else {
+               buffer_add_byte (buf, 1);
+               if (sig->ret->type == MONO_TYPE_VOID) {
+                       if (!strcmp (m->name, ".ctor") && !m->klass->valuetype) {
+                               buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &this, domain);
+                       }
+                       else
+                               buffer_add_value (buf, &mono_defaults.void_class->byval_arg, NULL, domain);
+               } else if (MONO_TYPE_IS_REFERENCE (sig->ret)) {
+                       buffer_add_value (buf, sig->ret, &res, domain);
+               } else if (mono_class_from_mono_type (sig->ret)->valuetype) {
+                       g_assert (res);
+                       buffer_add_value (buf, sig->ret, mono_object_unbox (res), domain);
+               } else {
+                       NOT_IMPLEMENTED;
+               }
+       }
+
+#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+       if (invoke->has_ctx)
+               mono_set_lmf ((gpointer)(((gssize)ext.lmf.previous_lmf) & ~3));
+#endif
+
+       // FIXME: byref arguments
+       // FIXME: varargs
+       return ERR_NONE;
+}
+
+/*
+ * invoke_method:
+ *
+ *   Invoke the method given by tls->invoke in the current thread.
+ */
+static void
+invoke_method (void)
+{
+       DebuggerTlsData *tls;
+       InvokeData *invoke;
+       int id;
+       int err;
+       Buffer buf;
+       static void (*restore_context) (void *);
+       MonoContext restore_ctx;
+
+       if (!restore_context)
+               restore_context = mono_get_restore_context ();
+
+       tls = TlsGetValue (debugger_tls_id);
+       g_assert (tls);
+
+       invoke = tls->invoke;
+       g_assert (invoke);
+       tls->invoke = NULL;
+
+       tls->frames_up_to_date = FALSE;
+
+       id = invoke->id;
+
+       buffer_init (&buf, 128);
+
+       err = do_invoke_method (&buf, invoke);
+
+       /* Start suspending before sending the reply */
+       suspend_vm ();
+
+       send_reply_packet (id, err, &buf);
+       
+       buffer_free (&buf);
+
+       memcpy (&restore_ctx, &invoke->ctx, sizeof (MonoContext));
+
+       if (invoke->has_ctx)
+               save_thread_context (&restore_ctx);
+
+       g_free (invoke->p);
+       g_free (invoke);
+
+       suspend_current ();
+}
+
+static ErrorCode
+vm_commands (int command, int id, guint8 *p, guint8 *end, Buffer *buf)
+{
+       switch (command) {
+       case CMD_VM_VERSION: {
+               char *build_info, *version;
+
+               build_info = mono_get_runtime_build_info ();
+               version = g_strdup_printf ("mono %s", build_info);
+
+               buffer_add_string (buf, version); /* vm version */
+               buffer_add_int (buf, MAJOR_VERSION);
+               buffer_add_int (buf, MINOR_VERSION);
+               g_free (build_info);
+               g_free (version);
+               break;
+       }
+       case CMD_VM_ALL_THREADS: {
+               // FIXME: Domains
+               mono_loader_lock ();
+               buffer_add_int (buf, mono_g_hash_table_size (tid_to_thread_obj));
+               mono_g_hash_table_foreach (tid_to_thread_obj, add_thread, buf);
+               mono_loader_unlock ();
+               break;
+       }
+       case CMD_VM_SUSPEND:
+               suspend_vm ();
+               wait_for_suspend ();
+               break;
+       case CMD_VM_RESUME:
+               if (suspend_count == 0)
+                       return ERR_NOT_SUSPENDED;
+               resume_vm ();
+               break;
+       case CMD_VM_DISPOSE:
+               /* Clear all event requests */
+               mono_loader_lock ();
+               while (event_requests->len > 0) {
+                       EventRequest *req = g_ptr_array_index (event_requests, 0);
+
+                       clear_event_request (req->id, req->event_kind);
+               }
+               mono_loader_unlock ();
+
+               // FIXME: Count resumes
+               resume_vm ();
+               disconnected = TRUE;
+               break;
+       case CMD_VM_EXIT: {
+               int exit_code = decode_int (p, &p, end);
+
+               // FIXME: What if there is a VM_DEATH event request with SUSPEND_ALL ?
+
+               /* Have to send a reply before exiting */
+               send_reply_packet (id, 0, buf);
+
+               /* Clear all event requests */
+               mono_loader_lock ();
+               while (event_requests->len > 0) {
+                       EventRequest *req = g_ptr_array_index (event_requests, 0);
+
+                       clear_event_request (req->id, req->event_kind);
+               }
+               mono_loader_unlock ();
+
+               /* FIXME: Races with normal shutdown */
+               while (suspend_count > 0)
+                       resume_vm ();
+
+               /*
+                * The JDWP documentation says that the shutdown is not orderly. It doesn't
+                * specify whenever a VM_DEATH event is sent. We currently do an orderly
+                * shutdown similar to Environment.Exit ().
+                */
+               mono_runtime_set_shutting_down ();
+
+               mono_threads_set_shutting_down ();
+
+               /* Suspend all managed threads since the runtime is going away */
+               DEBUG(1, fprintf (log_file, "Suspending all threads...\n"));
+               mono_thread_suspend_all_other_threads ();
+               DEBUG(1, fprintf (log_file, "Shutting down the runtime...\n"));
+               mono_runtime_quit ();
+               shutdown (conn_fd, SHUT_RDWR);
+               DEBUG(1, fprintf (log_file, "Exiting...\n"));
+
+               exit (exit_code);
+       }               
+       case CMD_VM_INVOKE_METHOD: {
+               int objid = decode_objid (p, &p, end);
+               MonoThread *thread;
+               DebuggerTlsData *tls;
+               int err;
+
+               err = get_object (objid, (MonoObject**)&thread);
+               if (err)
+                       return err;
+
+               // Wait for suspending if it already started
+               if (suspend_count)
+                       wait_for_suspend ();
+               if (!is_suspended ())
+                       return ERR_NOT_SUSPENDED;
+
+               mono_loader_lock ();
+               tls = mono_g_hash_table_lookup (thread_to_tls, thread->internal_thread);
+               mono_loader_unlock ();
+               g_assert (tls);
+
+               /* 
+                * Store the invoke data into tls, the thread will execute it after it is
+                * resumed.
+                */
+               if (tls->invoke)
+                       NOT_IMPLEMENTED;
+               tls->invoke = g_new0 (InvokeData, 1);
+               tls->invoke->id = id;
+               tls->invoke->p = g_malloc (end - p);
+               memcpy (tls->invoke->p, p, end - p);
+               tls->invoke->endp = tls->invoke->p + (end - p);
+
+               resume_vm ();
+               break;
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+event_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int err;
+
+       switch (command) {
+       case CMD_EVENT_REQUEST_SET: {
+               EventRequest *req;
+               int i, event_kind, suspend_policy, nmodifiers, mod;
+               MonoMethod *method;
+               long location;
+               MonoThread *step_thread;
+               int size, depth, step_thread_id;
+               MonoDomain *domain;
+
+               event_kind = decode_byte (p, &p, end);
+               suspend_policy = decode_byte (p, &p, end);
+               nmodifiers = decode_byte (p, &p, end);
+
+               req = g_malloc0 (sizeof (EventRequest) + (nmodifiers * sizeof (Modifier)));
+               req->id = InterlockedIncrement (&event_request_id);
+               req->event_kind = event_kind;
+               req->suspend_policy = suspend_policy;
+               req->nmodifiers = nmodifiers;
+
+               method = NULL;
+               for (i = 0; i < nmodifiers; ++i) {
+                       mod = decode_byte (p, &p, end);
+
+                       req->modifiers [i].kind = mod;
+                       if (mod == MOD_KIND_COUNT) {
+                               req->modifiers [i].data.count = decode_int (p, &p, end);
+                       } else if (mod == MOD_KIND_LOCATION_ONLY) {
+                               method = decode_methodid (p, &p, end, &domain, &err);
+                               if (err)
+                                       return err;
+                               location = decode_long (p, &p, end);
+                       } else if (mod == MOD_KIND_STEP) {
+                               step_thread_id = decode_id (p, &p, end);
+                               size = decode_int (p, &p, end);
+                               depth = decode_int (p, &p, end);
+                       } else if (mod == MOD_KIND_THREAD_ONLY) {
+                               int id = decode_id (p, &p, end);
+
+                               err = get_object (id, (MonoObject**)&req->modifiers [i].data.thread);
+                               if (err) {
+                                       g_free (req);
+                                       return err;
+                               }
+                       } else if (mod == MOD_KIND_EXCEPTION_ONLY) {
+                               MonoClass *exc_class = decode_typeid (p, &p, end, &domain, &err);
+
+                               if (err)
+                                       return err;
+                               if (exc_class) {
+                                       req->modifiers [i].data.exc_class = exc_class;
+
+                                       if (!mono_class_is_assignable_from (mono_defaults.exception_class, exc_class)) {
+                                               g_free (req);
+                                               return ERR_INVALID_ARGUMENT;
+                                       }
+                               }
+                       } else {
+                               g_free (req);
+                               return ERR_NOT_IMPLEMENTED;
+                       }
+               }
+
+               if (req->event_kind == EVENT_KIND_BREAKPOINT) {
+                       g_assert (method);
+
+                       req->info = set_breakpoint (method, location, req);
+               } else if (req->event_kind == EVENT_KIND_STEP) {
+                       g_assert (step_thread_id);
+
+                       err = get_object (step_thread_id, (MonoObject**)&step_thread);
+                       if (err) {
+                               g_free (req);
+                               return err;
+                       }
+
+                       err = ss_start (step_thread->internal_thread, size, depth, req);
+                       if (err) {
+                               g_free (req);
+                               return err;
+                       }
+               } else if (req->event_kind == EVENT_KIND_METHOD_ENTRY) {
+                       req->info = set_breakpoint (NULL, METHOD_ENTRY_IL_OFFSET, req);
+               } else if (req->event_kind == EVENT_KIND_METHOD_EXIT) {
+                       req->info = set_breakpoint (NULL, METHOD_EXIT_IL_OFFSET, req);
+               } else if (req->event_kind == EVENT_KIND_EXCEPTION) {
+               } else {
+                       if (req->nmodifiers) {
+                               g_free (req);
+                               return ERR_NOT_IMPLEMENTED;
+                       }
+               }
+
+               mono_loader_lock ();
+               g_ptr_array_add (event_requests, req);
+               mono_loader_unlock ();
+
+               buffer_add_int (buf, req->id);
+               break;
+       }
+       case CMD_EVENT_REQUEST_CLEAR: {
+               int etype = decode_byte (p, &p, end);
+               int req_id = decode_int (p, &p, end);
+
+               // FIXME: Make a faster mapping from req_id to request
+               mono_loader_lock ();
+               clear_event_request (req_id, etype);
+               mono_loader_unlock ();
+               break;
+       }
+       case CMD_EVENT_REQUEST_CLEAR_ALL_BREAKPOINTS: {
+               int i;
+
+               mono_loader_lock ();
+               i = 0;
+               while (i < event_requests->len) {
+                       EventRequest *req = g_ptr_array_index (event_requests, i);
+
+                       if (req->event_kind == EVENT_KIND_BREAKPOINT) {
+                               clear_breakpoint (req->info);
+
+                               g_ptr_array_remove_index_fast (event_requests, i);
+                               g_free (req);
+                       } else {
+                               i ++;
+                       }
+               }
+               mono_loader_unlock ();
+               break;
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+domain_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int err;
+       MonoDomain *domain;
+
+       switch (command) {
+       case CMD_APPDOMAIN_GET_ROOT_DOMAIN: {
+               buffer_add_domainid (buf, mono_get_root_domain ());
+               break;
+       }
+       case CMD_APPDOMAIN_GET_FRIENDLY_NAME: {
+               domain = decode_domainid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+               buffer_add_string (buf, domain->friendly_name);
+               break;
+       }
+       case CMD_APPDOMAIN_GET_ASSEMBLIES: {
+               GSList *tmp;
+               MonoAssembly *ass;
+               int count;
+
+               domain = decode_domainid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+               mono_loader_lock ();
+               count = 0;
+               for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
+                       count ++;
+               }
+               buffer_add_int (buf, count);
+               for (tmp = domain->domain_assemblies; tmp; tmp = tmp->next) {
+                       ass = tmp->data;
+                       buffer_add_assemblyid (buf, domain, ass);
+               }
+               mono_loader_unlock ();
+               break;
+       }
+       case CMD_APPDOMAIN_GET_ENTRY_ASSEMBLY: {
+               domain = decode_domainid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+
+               buffer_add_assemblyid (buf, domain, domain->entry_assembly);
+               break;
+       }
+       case CMD_APPDOMAIN_GET_CORLIB: {
+               domain = decode_domainid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+
+               buffer_add_assemblyid (buf, domain, domain->domain->mbr.obj.vtable->klass->image->assembly);
+               break;
+       }
+       case CMD_APPDOMAIN_CREATE_STRING: {
+               char *s;
+               MonoString *o;
+
+               domain = decode_domainid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+               s = decode_string (p, &p, end);
+
+               o = mono_string_new (domain, s);
+               buffer_add_objid (buf, (MonoObject*)o);
+               break;
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+assembly_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int err;
+       MonoAssembly *ass;
+       MonoDomain *domain;
+
+       ass = decode_assemblyid (p, &p, end, &domain, &err);
+       if (err)
+               return err;
+
+       switch (command) {
+       case CMD_ASSEMBLY_GET_LOCATION: {
+               buffer_add_string (buf, mono_image_get_filename (ass->image));
+               break;                  
+       }
+       case CMD_ASSEMBLY_GET_ENTRY_POINT: {
+               guint32 token;
+               MonoMethod *m;
+
+               if (ass->image->dynamic) {
+                       buffer_add_id (buf, 0);
+               } else {
+                       token = mono_image_get_entry_point (ass->image);
+                       if (token == 0) {
+                               buffer_add_id (buf, 0);
+                       } else {
+                               m = mono_get_method (ass->image, token, NULL);
+                               buffer_add_methodid (buf, domain, m);
+                       }
+               }
+               break;                  
+       }
+       case CMD_ASSEMBLY_GET_MANIFEST_MODULE: {
+               buffer_add_moduleid (buf, domain, ass->image);
+               break;
+       }
+       case CMD_ASSEMBLY_GET_OBJECT: {
+               MonoObject *o = (MonoObject*)mono_assembly_get_object (mono_domain_get (), ass);
+               buffer_add_objid (buf, o);
+               break;
+       }
+       case CMD_ASSEMBLY_GET_TYPE: {
+               char *s = decode_string (p, &p, end);
+               gboolean ignorecase = decode_byte (p, &p, end);
+               MonoTypeNameParse info;
+               MonoType *t;
+               gboolean type_resolve;
+
+               if (!mono_reflection_parse_type (s, &info)) {
+                       t = NULL;
+               } else {
+                       if (info.assembly.name)
+                               NOT_IMPLEMENTED;
+                       t = mono_reflection_get_type (ass->image, &info, ignorecase, &type_resolve);
+               }
+               buffer_add_typeid (buf, domain, t ? mono_class_from_mono_type (t) : NULL);
+               mono_reflection_free_type_info (&info);
+               g_free (s);
+
+               break;
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+module_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int err;
+       MonoDomain *domain;
+
+       switch (command) {
+       case CMD_MODULE_GET_INFO: {
+               MonoImage *image = decode_moduleid (p, &p, end, &domain, &err);
+               char *basename;
+
+               basename = g_path_get_basename (image->name);
+               buffer_add_string (buf, basename); // name
+               buffer_add_string (buf, image->module_name); // scopename
+               buffer_add_string (buf, image->name); // fqname
+               buffer_add_string (buf, mono_image_get_guid (image)); // guid
+               buffer_add_assemblyid (buf, domain, image->assembly); // assembly
+               g_free (basename);
+               break;                  
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static void
+buffer_add_cattr_arg (Buffer *buf, MonoType *t, MonoDomain *domain, MonoObject *val)
+{
+       if (val && val->vtable->klass == mono_defaults.monotype_class) {
+               /* Special case these so the client doesn't have to handle Type objects */
+               
+               buffer_add_byte (buf, VALUE_TYPE_ID_TYPE);
+               buffer_add_typeid (buf, domain, mono_class_from_mono_type (((MonoReflectionType*)val)->type));
+       } else if (MONO_TYPE_IS_REFERENCE (t))
+               buffer_add_value (buf, t, &val, domain);
+       else
+               buffer_add_value (buf, t, mono_object_unbox (val), domain);
+}
+
+static void
+buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass *attr_klass, MonoCustomAttrInfo *cinfo)
+{
+       int i, j;
+       int nattrs = 0;
+
+       if (!cinfo) {
+               buffer_add_int (buf, 0);
+               return;
+       }
+
+       for (i = 0; i < cinfo->num_attrs; ++i) {
+               if (!attr_klass || mono_class_has_parent (cinfo->attrs [i].ctor->klass, attr_klass))
+                       nattrs ++;
+       }
+       buffer_add_int (buf, nattrs);
+
+       for (i = 0; i < cinfo->num_attrs; ++i) {
+               MonoCustomAttrEntry *attr = &cinfo->attrs [i];
+               if (!attr_klass || mono_class_has_parent (attr->ctor->klass, attr_klass)) {
+                       MonoArray *typed_args, *named_args;
+                       MonoType *t;
+                       CattrNamedArg *arginfo;
+
+                       mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo);
+
+                       buffer_add_methodid (buf, domain, attr->ctor);
+
+                       /* Ctor args */
+                       if (typed_args) {
+                               buffer_add_int (buf, mono_array_length (typed_args));
+                               for (j = 0; j < mono_array_length (typed_args); ++j) {
+                                       MonoObject *val = mono_array_get (typed_args, MonoObject*, j);
+
+                                       t = mono_method_signature (attr->ctor)->params [j];
+
+                                       buffer_add_cattr_arg (buf, t, domain, val);
+                               }
+                       } else {
+                               buffer_add_int (buf, 0);
+                       }
+
+                       /* Named args */
+                       if (named_args) {
+                               buffer_add_int (buf, mono_array_length (named_args));
+
+                               for (j = 0; j < mono_array_length (named_args); ++j) {
+                                       MonoObject *val = mono_array_get (named_args, MonoObject*, j);
+
+                                       if (arginfo [j].prop) {
+                                               buffer_add_byte (buf, 0x54);
+                                               buffer_add_propertyid (buf, domain, arginfo [j].prop);
+                                       } else if (arginfo [j].field) {
+                                               buffer_add_byte (buf, 0x53);
+                                       } else {
+                                               g_assert_not_reached ();
+                                       }
+
+                                       buffer_add_cattr_arg (buf, t, domain, val);
+                               }
+                       } else {
+                               buffer_add_int (buf, 0);
+                       }
+               }
+       }
+}
+
+static ErrorCode
+type_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       MonoClass *klass;
+       MonoDomain *domain;
+       MonoClass *nested;
+       MonoType *type;
+       gpointer iter;
+       guint8 b;
+       int err, nnested;
+       char *name;
+
+       klass = decode_typeid (p, &p, end, &domain, &err);
+       if (err)
+               return err;
+
+       switch (command) {
+       case CMD_TYPE_GET_INFO: {
+               buffer_add_string (buf, klass->name_space);
+               buffer_add_string (buf, klass->name);
+               // FIXME: byref
+               name = mono_type_get_name_full (&klass->byval_arg, MONO_TYPE_NAME_FORMAT_FULL_NAME);
+               buffer_add_string (buf, name);
+               g_free (name);
+               buffer_add_assemblyid (buf, domain, klass->image->assembly);
+               buffer_add_moduleid (buf, domain, klass->image);
+               buffer_add_typeid (buf, domain, klass->parent);
+               if (klass->rank || klass->byval_arg.type == MONO_TYPE_PTR)
+                       buffer_add_typeid (buf, domain, klass->element_class);
+               else
+                       buffer_add_id (buf, 0);
+               buffer_add_int (buf, klass->type_token);
+               buffer_add_byte (buf, klass->rank);
+               buffer_add_int (buf, klass->flags);
+               b = 0;
+               type = &klass->byval_arg;
+               // FIXME: Can't decide whenever a class represents a byref type
+               if (FALSE)
+                       b |= (1 << 0);
+               if (type->type == MONO_TYPE_PTR)
+                       b |= (1 << 1);
+               if (!type->byref && (((type->type >= MONO_TYPE_BOOLEAN) && (type->type <= MONO_TYPE_R8)) || (type->type == MONO_TYPE_I) || (type->type == MONO_TYPE_U)))
+                       b |= (1 << 2);
+               if (type->type == MONO_TYPE_VALUETYPE)
+                       b |= (1 << 3);
+               buffer_add_byte (buf, b);
+               nnested = 0;
+               iter = NULL;
+               while ((nested = mono_class_get_nested_types (klass, &iter)))
+                       nnested ++;
+               buffer_add_int (buf, nnested);
+               iter = NULL;
+               while ((nested = mono_class_get_nested_types (klass, &iter)))
+                       buffer_add_typeid (buf, domain, nested);
+               break;
+       }
+       case CMD_TYPE_GET_METHODS: {
+               int nmethods;
+               int i = 0;
+               gpointer iter = NULL;
+               MonoMethod *m;
+
+               nmethods = mono_class_num_methods (klass);
+
+               buffer_add_int (buf, nmethods);
+
+               while ((m = mono_class_get_methods (klass, &iter))) {
+                       buffer_add_methodid (buf, domain, m);
+                       i ++;
+               }
+               g_assert (i == nmethods);
+               break;
+       }
+       case CMD_TYPE_GET_FIELDS: {
+               int nfields;
+               int i = 0;
+               gpointer iter = NULL;
+               MonoClassField *f;
+
+               nfields = mono_class_num_fields (klass);
+
+               buffer_add_int (buf, nfields);
+
+               while ((f = mono_class_get_fields (klass, &iter))) {
+                       buffer_add_fieldid (buf, domain, f);
+                       buffer_add_string (buf, f->name);
+                       buffer_add_typeid (buf, domain, mono_class_from_mono_type (f->type));
+                       buffer_add_int (buf, f->type->attrs);
+                       i ++;
+               }
+               g_assert (i == nfields);
+               break;
+       }
+       case CMD_TYPE_GET_PROPERTIES: {
+               int nprops;
+               int i = 0;
+               gpointer iter = NULL;
+               MonoProperty *p;
+
+               nprops = mono_class_num_properties (klass);
+
+               buffer_add_int (buf, nprops);
+
+               while ((p = mono_class_get_properties (klass, &iter))) {
+                       buffer_add_propertyid (buf, domain, p);
+                       buffer_add_string (buf, p->name);
+                       buffer_add_methodid (buf, domain, p->get);
+                       buffer_add_methodid (buf, domain, p->set);
+                       buffer_add_int (buf, p->attrs);
+                       i ++;
+               }
+               g_assert (i == nprops);
+               break;
+       }
+       case CMD_TYPE_GET_CATTRS: {
+               MonoClass *attr_klass = decode_typeid (p, &p, end, NULL, &err);
+               MonoCustomAttrInfo *cinfo;
+
+               cinfo = mono_custom_attrs_from_class (klass);
+
+               buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
+               break;
+       }
+       case CMD_TYPE_GET_FIELD_CATTRS: {
+               MonoClass *attr_klass;
+               MonoCustomAttrInfo *cinfo;
+               MonoClassField *field;
+
+               field = decode_fieldid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+               attr_klass = decode_typeid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+
+               cinfo = mono_custom_attrs_from_field (klass, field);
+
+               buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
+               break;
+       }
+       case CMD_TYPE_GET_PROPERTY_CATTRS: {
+               MonoClass *attr_klass;
+               MonoCustomAttrInfo *cinfo;
+               MonoProperty *prop;
+
+               prop = decode_propertyid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+               attr_klass = decode_typeid (p, &p, end, NULL, &err);
+               if (err)
+                       return err;
+
+               cinfo = mono_custom_attrs_from_property (klass, prop);
+
+               buffer_add_cattrs (buf, domain, klass->image, attr_klass, cinfo);
+               break;
+       }
+       case CMD_TYPE_GET_VALUES: {
+               guint8 *val;
+               MonoClassField *f;
+               MonoVTable *vtable;
+               MonoClass *k;
+               int len, i;
+               gboolean found;
+
+               len = decode_int (p, &p, end);
+               for (i = 0; i < len; ++i) {
+                       f = decode_fieldid (p, &p, end, NULL, &err);
+                       if (err)
+                               return err;
+
+                       if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC))
+                               return ERR_INVALID_FIELDID;
+                       if (mono_class_field_is_special_static (f))
+                               return ERR_INVALID_FIELDID;
+
+                       /* Check that the field belongs to the object */
+                       found = FALSE;
+                       for (k = klass; k; k = k->parent) {
+                               if (k == f->parent) {
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+                       if (!found)
+                               return ERR_INVALID_FIELDID;
+
+                       vtable = mono_class_vtable (domain, f->parent);
+                       val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
+                       mono_field_static_get_value (vtable, f, val);
+                       buffer_add_value (buf, f->type, val, domain);
+                       g_free (val);
+               }
+               break;
+       }
+       case CMD_TYPE_SET_VALUES: {
+               guint8 *val;
+               MonoClassField *f;
+               MonoVTable *vtable;
+               MonoClass *k;
+               int len, i;
+               gboolean found;
+
+               len = decode_int (p, &p, end);
+               for (i = 0; i < len; ++i) {
+                       f = decode_fieldid (p, &p, end, NULL, &err);
+                       if (err)
+                               return err;
+
+                       if (!(f->type->attrs & FIELD_ATTRIBUTE_STATIC))
+                               return ERR_INVALID_FIELDID;
+                       if (mono_class_field_is_special_static (f))
+                               return ERR_INVALID_FIELDID;
+
+                       /* Check that the field belongs to the object */
+                       found = FALSE;
+                       for (k = klass; k; k = k->parent) {
+                               if (k == f->parent) {
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+                       if (!found)
+                               return ERR_INVALID_FIELDID;
+
+                       // FIXME: Check for literal/const
+
+                       vtable = mono_class_vtable (domain, f->parent);
+                       val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
+                       err = decode_value (f->type, domain, val, p, &p, end);
+                       if (err) {
+                               g_free (val);
+                               return err;
+                       }
+                       mono_field_static_set_value (vtable, f, val);
+                       g_free (val);
+               }
+               break;
+       }
+       case CMD_TYPE_GET_OBJECT: {
+               MonoObject *o = (MonoObject*)mono_type_get_object (mono_domain_get (), &klass->byval_arg);
+               buffer_add_objid (buf, o);
+               break;
+       }
+       case CMD_TYPE_GET_SOURCE_FILES: {
+               gpointer iter = NULL;
+               MonoMethod *method;
+               char *source_file, *base;
+               GPtrArray *files;
+               int i;
+
+               files = g_ptr_array_new ();
+
+               while ((method = mono_class_get_methods (klass, &iter))) {
+                       MonoDebugMethodInfo *minfo = mono_debug_lookup_method (method);
+
+                       if (minfo) {
+                               mono_debug_symfile_get_line_numbers (minfo, &source_file, NULL, NULL, NULL);
+
+                               for (i = 0; i < files->len; ++i)
+                                       if (!strcmp (g_ptr_array_index (files, i), source_file))
+                                               break;
+                               if (i == files->len)
+                                       g_ptr_array_add (files, g_strdup (source_file));
+                               g_free (source_file);
+                       }
+               }
+
+               buffer_add_int (buf, files->len);
+               for (i = 0; i < files->len; ++i) {
+                       source_file = g_ptr_array_index (files, i);
+                       base = g_path_get_basename (source_file);
+                       buffer_add_string (buf, base);
+                       g_free (base);
+                       g_free (source_file);
+               }
+               g_ptr_array_free (files, TRUE);
+               break;
+       }
+       case CMD_TYPE_IS_ASSIGNABLE_FROM: {
+               MonoClass *oklass = decode_typeid (p, &p, end, NULL, &err);
+
+               if (err)
+                       return err;
+               if (mono_class_is_assignable_from (klass, oklass))
+                       buffer_add_byte (buf, 1);
+               else
+                       buffer_add_byte (buf, 0);
+               break;
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+method_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int err;
+       MonoDomain *domain;
+       MonoMethod *method;
+       MonoMethodHeader *header;
+
+       method = decode_methodid (p, &p, end, &domain, &err);
+       if (err)
+               return err;
+
+       switch (command) {
+       case CMD_METHOD_GET_NAME: {
+               buffer_add_string (buf, method->name);
+               break;                  
+       }
+       case CMD_METHOD_GET_DECLARING_TYPE: {
+               buffer_add_typeid (buf, domain, method->klass);
+               break;
+       }
+       case CMD_METHOD_GET_DEBUG_INFO: {
+               MonoDebugMethodInfo *minfo;
+               char *source_file;
+               int i, n_il_offsets;
+               int *il_offsets;
+               int *line_numbers;
+
+               header = mono_method_get_header (method);
+               if (!header) {
+                       buffer_add_int (buf, 0);
+                       buffer_add_string (buf, "");
+                       buffer_add_int (buf, 0);
+                       break;
+               }
+
+               minfo = mono_debug_lookup_method (method);
+               if (!minfo) {
+                       buffer_add_int (buf, header->code_size);
+                       buffer_add_string (buf, "");
+                       buffer_add_int (buf, 0);
+                       break;
+               }
+
+               mono_debug_symfile_get_line_numbers (minfo, &source_file, &n_il_offsets, &il_offsets, &line_numbers);
+               buffer_add_int (buf, header->code_size);
+               buffer_add_string (buf, source_file);
+               buffer_add_int (buf, n_il_offsets);
+               //printf ("Line number table for method %s:\n", mono_method_full_name (method,  TRUE));
+               for (i = 0; i < n_il_offsets; ++i) {
+                       //printf ("IL%d -> %d\n", il_offsets [i], line_numbers [i]);
+                       buffer_add_int (buf, il_offsets [i]);
+                       buffer_add_int (buf, line_numbers [i]);
+               }
+               g_free (source_file);
+               g_free (il_offsets);
+               g_free (line_numbers);
+               break;
+       }
+       case CMD_METHOD_GET_PARAM_INFO: {
+               MonoMethodSignature *sig = mono_method_signature (method);
+               guint32 i;
+               char **names;
+
+               /* FIXME: mono_class_from_mono_type () and byrefs */
+
+               /* FIXME: Use a smaller encoding */
+               buffer_add_int (buf, sig->call_convention);
+               buffer_add_int (buf, sig->param_count);
+               buffer_add_int (buf, sig->generic_param_count);
+               buffer_add_typeid (buf, domain, mono_class_from_mono_type (sig->ret));
+               for (i = 0; i < sig->param_count; ++i) {
+                       /* FIXME: vararg */
+                       buffer_add_typeid (buf, domain, mono_class_from_mono_type (sig->params [i]));
+               }
+
+               /* Emit parameter names */
+               names = g_new (char *, sig->param_count);
+               mono_method_get_param_names (method, (const char **) names);
+               for (i = 0; i < sig->param_count; ++i)
+                       buffer_add_string (buf, names [i]);
+               g_free (names);
+
+               break;
+       }
+       case CMD_METHOD_GET_LOCALS_INFO: {
+               int i, j, num_locals;
+               char **local_names;
+               int *local_indexes;
+
+               header = mono_method_get_header (method);
+               g_assert (header);
+
+               buffer_add_int (buf, header->num_locals);
+
+               /* Types */
+               for (i = 0; i < header->num_locals; ++i)
+                       buffer_add_typeid (buf, domain, mono_class_from_mono_type (header->locals [i]));
+
+               /* Names */
+               num_locals = mono_debug_lookup_locals (method, &local_names, &local_indexes);
+               for (i = 0; i < header->num_locals; ++i) {
+                       for (j = 0; j < num_locals; ++j)
+                               if (local_indexes [j] == i)
+                                       break;
+                       if (j < num_locals)
+                               buffer_add_string (buf, local_names [j]);
+                       else
+                               buffer_add_string (buf, "");
+               }
+               g_free (local_names);
+               g_free (local_indexes);
+
+               /* Live ranges */
+               /* FIXME: This works because we set debug_options.mdb_optimizations */
+               for (i = 0; i < header->num_locals; ++i) {
+                       buffer_add_int (buf, 0);
+                       buffer_add_int (buf, header->code_size);
+               }
+
+               break;
+       }
+       case CMD_METHOD_GET_INFO:
+               buffer_add_int (buf, method->flags);
+               buffer_add_int (buf, method->iflags);
+               buffer_add_int (buf, method->token);
+               break;
+       case CMD_METHOD_GET_BODY: {
+               int i;
+
+               header = mono_method_get_header (method);
+               if (!header) {
+                       buffer_add_int (buf, 0);
+               } else {
+                       buffer_add_int (buf, header->code_size);
+                       for (i = 0; i < header->code_size; ++i)
+                               buffer_add_byte (buf, header->code [i]);
+               }
+               break;
+       }
+       case CMD_METHOD_RESOLVE_TOKEN: {
+               guint32 token = decode_int (p, &p, end);
+
+               // FIXME: Generics
+               switch (mono_metadata_token_code (token)) {
+               case MONO_TOKEN_STRING: {
+                       MonoString *s;
+                       char *s2;
+
+                       s = mono_ldstr (domain, method->klass->image, mono_metadata_token_index (token));
+                       g_assert (s);
+
+                       s2 = mono_string_to_utf8 (s);
+
+                       buffer_add_byte (buf, TOKEN_TYPE_STRING);
+                       buffer_add_string (buf, s2);
+                       g_free (s2);
+                       break;
+               }
+               default: {
+                       gpointer val;
+                       MonoClass *handle_class;
+
+                       if (method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD) {
+                               val = mono_method_get_wrapper_data (method, token);
+                               handle_class = mono_method_get_wrapper_data (method, token + 1);
+
+                               if (handle_class == NULL) {
+                                       // Can't figure out the token type
+                                       buffer_add_byte (buf, TOKEN_TYPE_UNKNOWN);
+                                       break;
+                               }
+                       } else {
+                               val = mono_ldtoken (method->klass->image, token, &handle_class, NULL);
+                               g_assert (val);
+                       }
+
+                       if (handle_class == mono_defaults.typehandle_class) {
+                               buffer_add_byte (buf, TOKEN_TYPE_TYPE);
+                               buffer_add_typeid (buf, domain, mono_class_from_mono_type ((MonoType*)val));
+                       } else if (handle_class == mono_defaults.fieldhandle_class) {
+                               buffer_add_byte (buf, TOKEN_TYPE_FIELD);
+                               buffer_add_fieldid (buf, domain, val);
+                       } else if (handle_class == mono_defaults.methodhandle_class) {
+                               buffer_add_byte (buf, TOKEN_TYPE_METHOD);
+                               buffer_add_methodid (buf, domain, val);
+                       } else if (handle_class == mono_defaults.string_class) {
+                               char *s;
+
+                               s = mono_string_to_utf8 (val);
+                               buffer_add_byte (buf, TOKEN_TYPE_STRING);
+                               buffer_add_string (buf, s);
+                               g_free (s);
+                       } else {
+                               g_assert_not_reached ();
+                       }
+                       break;
+               }
+               }
+               break;
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+thread_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int objid = decode_objid (p, &p, end);
+       int err;
+       MonoThread *thread_obj;
+       MonoInternalThread *thread;
+
+       err = get_object (objid, (MonoObject**)&thread_obj);
+       if (err)
+               return err;
+
+       thread = thread_obj->internal_thread;
+          
+       switch (command) {
+       case CMD_THREAD_GET_NAME: {
+               guint32 name_len;
+               gunichar2 *s = mono_thread_get_name (thread, &name_len);
+
+               if (!s) {
+                       buffer_add_int (buf, 0);
+               } else {
+                       char *name;
+                       glong len;
+
+                       name = g_utf16_to_utf8 (s, name_len, NULL, &len, NULL);
+                       g_assert (name);
+                       buffer_add_int (buf, len);
+                       buffer_add_data (buf, (guint8*)name, len);
+                       g_free (s);
+               }
+               break;
+       }
+       case CMD_THREAD_GET_FRAME_INFO: {
+               DebuggerTlsData *tls;
+               int i, start_frame, length;
+
+               // Wait for suspending if it already started
+               if (suspend_count)
+                       wait_for_suspend ();
+               if (!is_suspended ())
+                       return ERR_NOT_SUSPENDED;
+
+               start_frame = decode_int (p, &p, end);
+               length = decode_int (p, &p, end);
+
+               if (start_frame != 0 || length != -1)
+                       return ERR_NOT_IMPLEMENTED;
+
+               mono_loader_lock ();
+               tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+               mono_loader_unlock ();
+               g_assert (tls);
+
+               compute_frame_info (thread, tls);
+
+               buffer_add_int (buf, tls->frame_count);
+               for (i = 0; i < tls->frame_count; ++i) {
+                       buffer_add_int (buf, tls->frames [i]->id);
+                       buffer_add_methodid (buf, tls->frames [i]->domain, tls->frames [i]->method);
+                       buffer_add_int (buf, tls->frames [i]->il_offset);
+                       /*
+                        * Instead of passing the frame type directly to the client, we associate
+                        * it with the previous frame using a set of flags. This avoids lots of
+                        * conditional code in the client, since a frame whose type isn't 
+                        * FRAME_TYPE_MANAGED has no method, location, etc.
+                        */
+                       buffer_add_byte (buf, tls->frames [i]->flags);
+               }
+
+               break;
+       }
+       case CMD_THREAD_GET_STATE:
+               buffer_add_int (buf, thread->state);
+               break;
+       case CMD_THREAD_GET_INFO:
+               buffer_add_byte (buf, thread->threadpool_thread);
+               break;
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+frame_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int objid;
+       int err;
+       MonoThread *thread_obj;
+       MonoInternalThread *thread;
+       int pos, i, len;
+       DebuggerTlsData *tls;
+       StackFrame *frame;
+       MonoDebugMethodJitInfo *jit;
+       MonoDebugVarInfo *var;
+       MonoMethodSignature *sig;
+       gssize id;
+       MonoMethodHeader *header;
+
+       objid = decode_objid (p, &p, end);
+       err = get_object (objid, (MonoObject**)&thread_obj);
+       if (err)
+               return err;
+
+       thread = thread_obj->internal_thread;
+
+       id = decode_id (p, &p, end);
+
+       mono_loader_lock ();
+       tls = mono_g_hash_table_lookup (thread_to_tls, thread);
+       mono_loader_unlock ();
+       g_assert (tls);
+
+       for (i = 0; i < tls->frame_count; ++i) {
+               if (tls->frames [i]->id == id)
+                       break;
+       }
+       if (i == tls->frame_count)
+               return ERR_INVALID_FRAMEID;
+
+       frame = tls->frames [i];
+
+       if (!frame->jit) {
+               frame->jit = mono_debug_find_method (frame->method, frame->domain);
+               g_assert (frame->jit);
+       }
+       jit = frame->jit;
+
+       sig = mono_method_signature (frame->method);
+
+       switch (command) {
+       case CMD_STACK_FRAME_GET_VALUES: {
+               len = decode_int (p, &p, end);
+               header = mono_method_get_header (frame->method);
+
+               for (i = 0; i < len; ++i) {
+                       pos = decode_int (p, &p, end);
+
+                       if (pos < 0) {
+                               pos = - pos - 1;
+
+                               g_assert (pos >= 0 && pos < jit->num_params);
+
+                               var = &jit->params [pos];
+
+                               add_var (buf, sig->params [pos], &jit->params [pos], &frame->ctx, frame->domain);
+                       } else {
+                               g_assert (pos >= 0 && pos < jit->num_locals);
+
+                               var = &jit->locals [pos];
+                               
+                               add_var (buf, header->locals [pos], &jit->locals [pos], &frame->ctx, frame->domain);
+                       }
+               }
+               break;
+       }
+       case CMD_STACK_FRAME_GET_THIS: {
+               if (frame->method->klass->valuetype) {
+                       if (!sig->hasthis) {
+                               MonoObject *p = NULL;
+                               buffer_add_value (buf, &mono_defaults.object_class->byval_arg, &p, frame->domain);
+                       } else {
+                               add_var (buf, &frame->method->klass->this_arg, jit->this_var, &frame->ctx, frame->domain);
+                       }
+               } else {
+                       if (!sig->hasthis) {
+                               MonoObject *p = NULL;
+                               buffer_add_value (buf, &frame->method->klass->byval_arg, &p, frame->domain);
+                       } else {
+                               add_var (buf, &frame->method->klass->byval_arg, jit->this_var, &frame->ctx, frame->domain);
+                       }
+               }
+               break;
+       }
+       case CMD_STACK_FRAME_SET_VALUES: {
+               guint8 *val_buf;
+               MonoType *t;
+               MonoDebugVarInfo *var;
+
+               len = decode_int (p, &p, end);
+               header = mono_method_get_header (frame->method);
+
+               for (i = 0; i < len; ++i) {
+                       pos = decode_int (p, &p, end);
+
+                       if (pos < 0) {
+                               pos = - pos - 1;
+
+                               g_assert (pos >= 0 && pos < jit->num_params);
+
+                               t = sig->params [pos];
+                               var = &jit->params [pos];
+                       } else {
+                               g_assert (pos >= 0 && pos < jit->num_locals);
+
+                               t = header->locals [pos];
+                               var = &jit->locals [pos];
+                       }
+
+                       if (MONO_TYPE_IS_REFERENCE (t))
+                               val_buf = g_alloca (sizeof (MonoObject*));
+                       else
+                               val_buf = g_alloca (mono_class_instance_size (mono_class_from_mono_type (t)));
+                       err = decode_value (t, frame->domain, val_buf, p, &p, end);
+                       if (err)
+                               return err;
+
+                       set_var (t, var, &frame->ctx, frame->domain, val_buf);
+               }
+               break;
+       }
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+array_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       MonoArray *arr;
+       int objid, err, index, len, i, esize;
+       gpointer elem;
+
+       objid = decode_objid (p, &p, end);
+       err = get_object (objid, (MonoObject**)&arr);
+       if (err)
+               return err;
+
+       switch (command) {
+       case CMD_ARRAY_REF_GET_LENGTH:
+               buffer_add_int (buf, arr->obj.vtable->klass->rank);
+               if (!arr->bounds) {
+                       buffer_add_int (buf, arr->max_length);
+                       buffer_add_int (buf, 0);
+               } else {
+                       for (i = 0; i < arr->obj.vtable->klass->rank; ++i) {
+                               buffer_add_int (buf, arr->bounds [i].length);
+                               buffer_add_int (buf, arr->bounds [i].lower_bound);
+                       }
+               }
+               break;
+       case CMD_ARRAY_REF_GET_VALUES:
+               index = decode_int (p, &p, end);
+               len = decode_int (p, &p, end);
+
+               g_assert (index >= 0 && len >= 0);
+               // Reordered to avoid integer overflow
+               g_assert (!(index > arr->max_length - len));
+
+               esize = mono_array_element_size (arr->obj.vtable->klass);
+               for (i = index; i < index + len; ++i) {
+                       elem = (gpointer*)((char*)arr->vector + (i * esize));
+                       buffer_add_value (buf, &arr->obj.vtable->klass->element_class->byval_arg, elem, arr->obj.vtable->domain);
+               }
+               break;
+       case CMD_ARRAY_REF_SET_VALUES:
+               index = decode_int (p, &p, end);
+               len = decode_int (p, &p, end);
+
+               g_assert (index >= 0 && len >= 0);
+               // Reordered to avoid integer overflow
+               g_assert (!(index > arr->max_length - len));
+
+               esize = mono_array_element_size (arr->obj.vtable->klass);
+               for (i = index; i < index + len; ++i) {
+                       elem = (gpointer*)((char*)arr->vector + (i * esize));
+
+                       decode_value (&arr->obj.vtable->klass->element_class->byval_arg, arr->obj.vtable->domain, elem, p, &p, end);
+               }
+               break;
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+string_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int objid, err;
+       MonoString *str;
+       char *s;
+
+       objid = decode_objid (p, &p, end);
+       err = get_object (objid, (MonoObject**)&str);
+       if (err)
+               return err;
+
+       switch (command) {
+       case CMD_STRING_REF_GET_VALUE:
+               s = mono_string_to_utf8 (str);
+               buffer_add_string (buf, s);
+               g_free (s);
+               break;
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static ErrorCode
+object_commands (int command, guint8 *p, guint8 *end, Buffer *buf)
+{
+       int objid, err;
+       MonoObject *obj;
+       int len, i;
+       MonoClassField *f;
+       MonoClass *k;
+       gboolean found;
+
+       if (command == CMD_OBJECT_REF_IS_COLLECTED) {
+               objid = decode_objid (p, &p, end);
+               err = get_object (objid, &obj);
+               if (err)
+                       buffer_add_int (buf, 1);
+               else
+                       buffer_add_int (buf, 0);
+               return 0;
+       }
+
+       objid = decode_objid (p, &p, end);
+       err = get_object (objid, &obj);
+       if (err)
+               return err;
+
+       switch (command) {
+       case CMD_OBJECT_REF_GET_TYPE:
+               buffer_add_typeid (buf, obj->vtable->domain, obj->vtable->klass);
+               break;
+       case CMD_OBJECT_REF_GET_VALUES:
+               len = decode_int (p, &p, end);
+
+               for (i = 0; i < len; ++i) {
+                       MonoClassField *f = decode_fieldid (p, &p, end, NULL, &err);
+                       if (err)
+                               return err;
+
+                       /* Check that the field belongs to the object */
+                       found = FALSE;
+                       for (k = obj->vtable->klass; k; k = k->parent) {
+                               if (k == f->parent) {
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+                       if (!found)
+                               return ERR_INVALID_FIELDID;
+
+                       if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) {
+                               guint8 *val;
+                               MonoVTable *vtable;
+
+                               if (mono_class_field_is_special_static (f))
+                                       return ERR_INVALID_FIELDID;
+
+                               g_assert (f->type->attrs & FIELD_ATTRIBUTE_STATIC);
+                               vtable = mono_class_vtable (obj->vtable->domain, f->parent);
+                               val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
+                               mono_field_static_get_value (vtable, f, val);
+                               buffer_add_value (buf, f->type, val, obj->vtable->domain);
+                               g_free (val);
+                       } else {
+                               buffer_add_value (buf, f->type, (guint8*)obj + f->offset, obj->vtable->domain);
+                       }
+               }
+               break;
+       case CMD_OBJECT_REF_SET_VALUES:
+               len = decode_int (p, &p, end);
+
+               for (i = 0; i < len; ++i) {
+                       f = decode_fieldid (p, &p, end, NULL, &err);
+                       if (err)
+                               return err;
+
+                       /* Check that the field belongs to the object */
+                       found = FALSE;
+                       for (k = obj->vtable->klass; k; k = k->parent) {
+                               if (k == f->parent) {
+                                       found = TRUE;
+                                       break;
+                               }
+                       }
+                       if (!found)
+                               return ERR_INVALID_FIELDID;
+
+                       if (f->type->attrs & FIELD_ATTRIBUTE_STATIC) {
+                               guint8 *val;
+                               MonoVTable *vtable;
+
+                               if (mono_class_field_is_special_static (f))
+                                       return ERR_INVALID_FIELDID;
+
+                               g_assert (f->type->attrs & FIELD_ATTRIBUTE_STATIC);
+                               vtable = mono_class_vtable (obj->vtable->domain, f->parent);
+
+                               val = g_malloc (mono_class_instance_size (mono_class_from_mono_type (f->type)));
+                               err = decode_value (f->type, obj->vtable->domain, val, p, &p, end);
+                               if (err) {
+                                       g_free (val);
+                                       return err;
+                               }
+                               mono_field_static_set_value (vtable, f, val);
+                               g_free (val);
+                       } else {
+                               err = decode_value (f->type, obj->vtable->domain, (guint8*)obj + f->offset, p, &p, end);
+                               if (err)
+                                       return err;
+                       }
+               }
+               break;
+       case CMD_OBJECT_REF_GET_ADDRESS:
+               buffer_add_long (buf, (gssize)obj);
+               break;
+       case CMD_OBJECT_REF_GET_DOMAIN:
+               buffer_add_domainid (buf, obj->vtable->domain);
+               break;
+       default:
+               return ERR_NOT_IMPLEMENTED;
+       }
+
+       return ERR_NONE;
+}
+
+static const char*
+command_set_to_string (CommandSet command_set)
+{
+       switch (command_set) {
+       case CMD_SET_VM:
+               return "VM";
+       case CMD_SET_OBJECT_REF:
+               return "OBJECT_REF";
+       case CMD_SET_STRING_REF:
+               return "STRING_REF"; 
+       case CMD_SET_THREAD:
+               return "THREAD"; 
+       case CMD_SET_ARRAY_REF:
+               return "ARRAY_REF"; 
+       case CMD_SET_EVENT_REQUEST:
+               return "EVENT_REQUEST"; 
+       case CMD_SET_STACK_FRAME:
+               return "STACK_FRAME"; 
+       case CMD_SET_APPDOMAIN:
+               return "APPDOMAIN"; 
+       case CMD_SET_ASSEMBLY:
+               return "ASSEMBLY"; 
+       case CMD_SET_METHOD:
+               return "METHOD"; 
+       case CMD_SET_TYPE:
+               return "TYPE"; 
+       case CMD_SET_MODULE:
+               return "MODULE"; 
+       case CMD_SET_EVENT:
+               return "EVENT"; 
+       default:
+               return "";
+       }
+}
+
+/*
+ * debugger_thread:
+ *
+ *   This thread handles communication with the debugger client using a JDWP
+ * like protocol.
+ */
+static guint32 WINAPI
+debugger_thread (void *arg)
+{
+       int res, len, id, flags, command_set, command;
+       guint8 header [HEADER_LENGTH];
+       guint8 *data, *p, *end;
+       Buffer buf;
+       ErrorCode err;
+       gboolean no_reply;
+
+       DEBUG (1, fprintf (log_file, "[dbg] Agent thread started, pid=%p\n", (gpointer)GetCurrentThreadId ()));
+
+       debugger_thread_id = GetCurrentThreadId ();
+
+       mono_jit_thread_attach (mono_get_root_domain ());
+
+       mono_thread_internal_current ()->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
+
+       while (TRUE) {
+               res = read (conn_fd, header, HEADER_LENGTH);
+
+               /* This will break if the socket is closed during shutdown too */
+               if (res != HEADER_LENGTH)
+                       break;
+
+               p = header;
+               end = header + HEADER_LENGTH;
+
+               len = decode_int (p, &p, end);
+               id = decode_int (p, &p, end);
+               flags = decode_byte (p, &p, end);
+               command_set = decode_byte (p, &p, end);
+               command = decode_byte (p, &p, end);
+
+               g_assert (flags == 0);
+
+               DEBUG (1, fprintf (log_file, "[dbg] Received command %s(%d), id=%d.\n", command_set_to_string (command_set), command, id));
+
+               data = g_malloc (len - HEADER_LENGTH);
+               res = read (conn_fd, data, len - HEADER_LENGTH);
+               if (res != len - HEADER_LENGTH)
+                       break;
+
+               p = data;
+               end = data + (len - HEADER_LENGTH);
+
+               buffer_init (&buf, 128);
+
+               err = ERR_NONE;
+               no_reply = FALSE;
+
+               /* Process the request */
+               switch (command_set) {
+               case CMD_SET_VM:
+                       err = vm_commands (command, id, p, end, &buf);
+                       if (!err && command == CMD_VM_INVOKE_METHOD)
+                               /* Sent after the invoke is complete */
+                               no_reply = TRUE;
+                       break;
+               case CMD_SET_EVENT_REQUEST:
+                       err = event_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_APPDOMAIN:
+                       err = domain_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_ASSEMBLY:
+                       err = assembly_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_MODULE:
+                       err = module_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_TYPE:
+                       err = type_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_METHOD:
+                       err = method_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_THREAD:
+                       err = thread_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_STACK_FRAME:
+                       err = frame_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_ARRAY_REF:
+                       err = array_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_STRING_REF:
+                       err = string_commands (command, p, end, &buf);
+                       break;
+               case CMD_SET_OBJECT_REF:
+                       err = object_commands (command, p, end, &buf);
+                       break;
+               default:
+                       err = ERR_NOT_IMPLEMENTED;
+               }               
+
+               if (!no_reply)
+                       send_reply_packet (id, err, &buf);
+
+               g_free (data);
+               buffer_free (&buf);
+
+               if (command_set == CMD_SET_VM && command == CMD_VM_DISPOSE)
+                       break;
+       }
+
+       pthread_mutex_lock (&debugger_thread_exited_mutex);
+       debugger_thread_exited = TRUE;
+       pthread_cond_signal (&debugger_thread_exited_cond);
+       pthread_mutex_unlock (&debugger_thread_exited_mutex);   
+
+       shutdown (conn_fd, SHUT_RDWR);
+
+       return 0;
+}
+
+#else /* DISABLE_DEBUGGER_AGENT */
+
+void
+mono_debugger_agent_parse_options (char *options)
+{
+       g_error ("This runtime is configure with the debugger agent disabled.");
+}
+
+void
+mono_debugger_agent_init (void)
+{
+}
+
+void
+mono_debugger_agent_cleanup (void)
+{
+}
+
+void
+mono_debugger_agent_event (MonoDebuggerAgentEvent event, gpointer arg)
+{
+}
+
+void
+mono_debugger_agent_breakpoint_hit (void *sigctx)
+{
+}
+
+void
+mono_debugger_agent_single_step_event (void *sigctx)
+{
+}
+
+void
+mono_debugger_agent_free_domain_info (MonoDomain *domain)
+{
+}
+
+gboolean mono_debugger_agent_thread_interrupt (MonoJitInfo *ji)
+{
+}
+
+void
+mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *ctx)
+{
+}
+
+#endif
+
diff --git a/mono/mini/debugger-agent.h b/mono/mini/debugger-agent.h
new file mode 100644 (file)
index 0000000..fe8a17f
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef __MONO_DEBUGGER_AGENT_H__
+#define __MONO_DEBUGGER_AGENT_H__
+
+#include "mini.h"
+
+/* IL offsets used to mark the sequence points belonging to method entry/exit events */
+#define METHOD_ENTRY_IL_OFFSET -1
+#define METHOD_EXIT_IL_OFFSET 0xffffff
+
+void
+mono_debugger_agent_parse_options (char *options) MONO_INTERNAL;
+
+void
+mono_debugger_agent_init (void) MONO_INTERNAL;
+
+void
+mono_debugger_agent_breakpoint_hit (void *sigctx) MONO_INTERNAL;
+
+void
+mono_debugger_agent_single_step_event (void *sigctx) MONO_INTERNAL;
+
+void
+mono_debugger_agent_free_domain_info (MonoDomain *domain) MONO_INTERNAL;
+
+gboolean mono_debugger_agent_thread_interrupt (MonoJitInfo *ji) MONO_INTERNAL;
+
+void
+mono_debugger_agent_handle_exception (MonoException *ext, MonoContext *ctx) MONO_INTERNAL;
+
+#endif
index d20a8cde4ab1d787ba22708d95e2b9a27113b760..7f2a70415d90235ad481ad21ba4fd28d97af1c29 100644 (file)
@@ -55,6 +55,7 @@
 #include <ctype.h>
 #include <locale.h>
 #include "version.h"
+#include "debugger-agent.h"
 
 static FILE *mini_stats_fd = NULL;
 
@@ -1105,6 +1106,7 @@ mini_usage (void)
                "Development:\n"
                "    --aot                  Compiles the assembly to native code\n"
                "    --debug[=<options>]    Enable debugging support, use --help-debug for details\n"
+               "    --debugger-agent=options Enable the debugger agent\n"
                "    --profile[=profiler]   Runs in profiling mode with the specified profiler module\n"
                "    --trace[=EXPR]         Enable tracing, use --help-trace for details\n"
                "    --help-devel           Shows more options available to developers\n"
@@ -1441,6 +1443,12 @@ mono_main (int argc, char* argv[])
                        enable_debugging = TRUE;
                        if (!parse_debug_options (argv [i] + 8))
                                return 1;
+               } else if (strncmp (argv [i], "--debugger-agent=", 17) == 0) {
+                       MonoDebugOptions *opt = mini_get_debug_options ();
+
+                       mono_debugger_agent_parse_options (argv [i] + 17);
+                       opt->mdb_optimizations = TRUE;
+                       enable_debugging = TRUE;
                } else if (strcmp (argv [i], "--security") == 0) {
                        mono_verifier_set_mode (MONO_VERIFIER_MODE_VERIFIABLE);
                        mono_security_set_mode (MONO_SECURITY_MODE_CAS);
index 126b0aaa7a95e94a8987127edcb5089b436643df..2964d80252cfd8efbde2a441037d6fd6124406e1 100644 (file)
@@ -528,30 +528,27 @@ mono_arch_get_throw_corlib_exception_full (guint32 *code_size, MonoJumpInfo **ji
        return start;
 }
 
-/* mono_arch_find_jit_info:
+/*
+ * mono_arch_find_jit_info_ext:
  *
- * This function is used to gather information from @ctx. It return the 
- * MonoJitInfo of the corresponding function, unwinds one stack frame and
- * stores the resulting context into @new_ctx. It also stores a string 
- * describing the stack location into @trace (if not NULL), and modifies
- * the @lmf if necessary. @native_offset return the IP offset from the 
- * start of the function or -1 if that info is not available.
+ * This function is used to gather information from @ctx, and store it in @frame_info.
+ * It unwinds one stack frame, and stores the resulting context into @new_ctx. @lmf
+ * is modified if needed.
+ * Returns TRUE on success, FALSE otherwise.
+ * This function is a version of mono_arch_find_jit_info () where all the results are
+ * returned in a StackFrameInfo structure.
  */
-MonoJitInfo *
-mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
-                        MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
+gboolean
+mono_arch_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
+                                                        MonoJitInfo *ji, MonoContext *ctx, 
+                                                        MonoContext *new_ctx, MonoLMF **lmf, 
+                                                        StackFrameInfo *frame)
 {
-       MonoJitInfo *ji;
        gpointer ip = MONO_CONTEXT_GET_IP (ctx);
 
-       /* Avoid costly table lookup during stack overflow */
-       if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
-               ji = prev_ji;
-       else
-               ji = mini_jit_info_table_find (domain, ip, NULL);
-
-       if (managed)
-               *managed = FALSE;
+       memset (frame, 0, sizeof (StackFrameInfo));
+       frame->ji = ji;
+       frame->managed = FALSE;
 
        *new_ctx = *ctx;
 
@@ -561,9 +558,10 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                guint32 unwind_info_len;
                guint8 *unwind_info;
 
-               if (managed)
-                       if (!ji->method->wrapper_type)
-                               *managed = TRUE;
+               frame->type = FRAME_TYPE_MANAGED;
+
+               if (!ji->method->wrapper_type || ji->method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
+                       frame->managed = TRUE;
 
                if (ji->from_aot)
                        unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
@@ -610,7 +608,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
 
                if (*lmf && ((*lmf) != jit_tls->first_lmf) && (MONO_CONTEXT_GET_SP (ctx) >= (gpointer)(*lmf)->rsp)) {
                        /* remove any unused lmf */
-                       *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~1);
+                       *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~3);
                }
 
 #ifndef MONO_AMD64_NO_PUSHES
@@ -623,16 +621,34 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                }
 #endif
 
-               return ji;
+               return TRUE;
        } else if (*lmf) {
                guint64 rip;
 
+               if (((guint64)(*lmf)->previous_lmf) & 2) {
+                       /* 
+                        * This LMF entry is created by the soft debug code to mark transitions to
+                        * managed code done during invokes.
+                        */
+                       MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
+
+                       g_assert (ext->debugger_invoke);
+
+                       memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+
+                       *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~3);
+
+                       frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
+
+                       return TRUE;
+               }
+
                if (((guint64)(*lmf)->previous_lmf) & 1) {
                        /* This LMF has the rip field set */
                        rip = (*lmf)->rip;
                } else if ((*lmf)->rsp == 0) {
                        /* Top LMF entry */
-                       return (gpointer)-1;
+                       return FALSE;
                } else {
                        /* 
                         * The rsp field is set just before the call which transitioned to native 
@@ -644,9 +660,12 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                ji = mini_jit_info_table_find (domain, (gpointer)rip, NULL);
                if (!ji) {
                        // FIXME: This can happen with multiple appdomains (bug #444383)
-                       return (gpointer)-1;
+                       return FALSE;
                }
 
+               frame->ji = ji;
+               frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
+
                new_ctx->rip = rip;
                new_ctx->rbp = (*lmf)->rbp;
                new_ctx->rsp = (*lmf)->rsp;
@@ -661,12 +680,12 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                new_ctx->rsi = (*lmf)->rsi;
 #endif
 
-               *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~1);
+               *lmf = (gpointer)(((guint64)(*lmf)->previous_lmf) & ~3);
 
-               return ji ? ji : res;
+               return TRUE;
        }
 
-       return NULL;
+       return FALSE;
 }
 
 /**
index ec42f1d655254b2a884e3801e8f04846b86617e0..f2c3e628230a1b7ae5afeedd8ba0b4492cca28b9 100644 (file)
@@ -351,30 +351,24 @@ mono_arch_get_throw_corlib_exception_full (guint32 *code_size, MonoJumpInfo **ji
        return mono_arch_get_throw_exception_generic (168, TRUE, FALSE, code_size, ji, aot);
 }      
 
-/* mono_arch_find_jit_info:
+/* 
+ * mono_arch_find_jit_info_ext:
  *
- * This function is used to gather information from @ctx. It return the 
- * MonoJitInfo of the corresponding function, unwinds one stack frame and
- * stores the resulting context into @new_ctx. It also stores a string 
- * describing the stack location into @trace (if not NULL), and modifies
- * the @lmf if necessary. @native_offset return the IP offset from the 
- * start of the function or -1 if that info is not available.
+ * See exceptions-amd64.c for docs;
  */
-MonoJitInfo *
-mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji,
-                        MonoContext *ctx, MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
+gboolean
+mono_arch_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
+                                                        MonoJitInfo *ji, MonoContext *ctx, 
+                                                        MonoContext *new_ctx, MonoLMF **lmf, 
+                                                        StackFrameInfo *frame)
 {
-       MonoJitInfo *ji;
        gpointer ip = MONO_CONTEXT_GET_IP (ctx);
 
-       /* Avoid costly table lookup during stack overflow */
-       if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
-               ji = prev_ji;
-       else
-               ji = mini_jit_info_table_find (domain, ip, NULL);
+       memset (frame, 0, sizeof (StackFrameInfo));
+       frame->ji = ji;
+       frame->managed = FALSE;
 
-       if (managed)
-               *managed = FALSE;
+       *new_ctx = *ctx;
 
        if (ji != NULL) {
                int i;
@@ -383,11 +377,10 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                guint32 unwind_info_len;
                guint8 *unwind_info;
 
-               *new_ctx = *ctx;
+               frame->type = FRAME_TYPE_MANAGED;
 
-               if (managed)
-                       if (!ji->method->wrapper_type)
-                               *managed = TRUE;
+               if (!ji->method->wrapper_type || ji->method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
+                       frame->managed = TRUE;
 
                if (ji->from_aot)
                        unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
@@ -419,23 +412,41 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
 
                if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
                        /* remove any unused lmf */
-                       *lmf = (*lmf)->previous_lmf;
+                       *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
                }
 
                /* we substract 1, so that the IP points into the call instruction */
                new_ctx->eip--;
 
-               return ji;
+               return TRUE;
        } else if (*lmf) {
-               
-               *new_ctx = *ctx;
 
+               if (((gsize)(*lmf)->previous_lmf) & 2) {
+                       /* 
+                        * This LMF entry is created by the soft debug code to mark transitions to
+                        * managed code done during invokes.
+                        */
+                       MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
+
+                       g_assert (ext->debugger_invoke);
+
+                       memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+
+                       *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
+
+                       frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
+
+                       return TRUE;
+               }
+
+               frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
+               
                if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) {
+                       frame->ji = ji;
                } else {
                        if (!(*lmf)->method)
-                               return (gpointer)-1;
-                       memset (res, 0, MONO_SIZEOF_JIT_INFO);
-                       res->method = (*lmf)->method;
+                               return FALSE;
+                       frame->method = (*lmf)->method;
                }
 
                memcpy (&new_ctx->regs [0], &(*lmf)->iregs [4], sizeof (gulong) * MONO_SAVED_GREGS);
@@ -444,12 +455,12 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                new_ctx->eip = (*lmf)->iregs [13];
                new_ctx->ebp = new_ctx->esp;
 
-               *lmf = (*lmf)->previous_lmf;
+               *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
 
-               return ji ? ji : res;
+               return TRUE;
        }
 
-       return NULL;
+       return FALSE;
 }
 
 void
@@ -459,6 +470,7 @@ mono_arch_sigctx_to_monoctx (void *sigctx, MonoContext *mctx)
        struct ucontext *uc = sigctx;
 
        mctx->eip = uc->uc_mcontext.gregs [ARMREG_PC];
+       mctx->ebp = uc->uc_mcontext.gregs [ARMREG_SP];
        mctx->esp = uc->uc_mcontext.gregs [ARMREG_SP];
        memcpy (&mctx->regs, &uc->uc_mcontext.gregs [ARMREG_R4], sizeof (gulong) * 8);
        /* memcpy (&mctx->fregs, &uc->uc_mcontext.uc_regs->fpregs.fpregs [14], sizeof (double) * MONO_SAVED_FREGS);*/
index b3bfd26b60d85965affd0c26c8908481c5a54d92..b54d3a86f8d52cea2aa53f3d05da79addb8a0939 100644 (file)
@@ -582,30 +582,22 @@ mono_arch_get_throw_corlib_exception (void)
        return start;
 }
 
-/* mono_arch_find_jit_info:
+/*
+ * mono_arch_find_jit_info_ext:
  *
- * This function is used to gather information from @ctx. It return the 
- * MonoJitInfo of the corresponding function, unwinds one stack frame and
- * stores the resulting context into @new_ctx. It also stores a string 
- * describing the stack location into @trace (if not NULL), and modifies
- * the @lmf if necessary. @native_offset return the IP offset from the 
- * start of the function or -1 if that info is not available.
+ * See exceptions-amd64.c for docs.
  */
-MonoJitInfo *
-mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
-                        MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
+gboolean
+mono_arch_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
+                                                        MonoJitInfo *ji, MonoContext *ctx, 
+                                                        MonoContext *new_ctx, MonoLMF **lmf, 
+                                                        StackFrameInfo *frame)
 {
-       MonoJitInfo *ji;
        gpointer ip = MONO_CONTEXT_GET_IP (ctx);
 
-       /* Avoid costly table lookup during stack overflow */
-       if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
-               ji = prev_ji;
-       else
-               ji = mini_jit_info_table_find (domain, ip, NULL);
-
-       if (managed)
-               *managed = FALSE;
+       memset (frame, 0, sizeof (StackFrameInfo));
+       frame->ji = ji;
+       frame->managed = FALSE;
 
        *new_ctx = *ctx;
 
@@ -615,9 +607,10 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                guint32 unwind_info_len;
                guint8 *unwind_info;
 
-               if (managed)
-                       if (!ji->method->wrapper_type)
-                               *managed = TRUE;
+               frame->type = FRAME_TYPE_MANAGED;
+
+               if (!ji->method->wrapper_type || ji->method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD)
+                       frame->managed = TRUE;
 
                if (ji->from_aot)
                        unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
@@ -656,7 +649,7 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
 
                if (*lmf && (MONO_CONTEXT_GET_BP (ctx) >= (gpointer)(*lmf)->ebp)) {
                        /* remove any unused lmf */
-                       *lmf = (gpointer)(((guint32)(*lmf)->previous_lmf) & ~1);
+                       *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
                }
 
                /* Pop arguments off the stack */
@@ -667,19 +660,34 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                        new_ctx->esp += stack_to_pop;
                }
 
-               return ji;
+               return TRUE;
        } else if (*lmf) {
-               
-               *new_ctx = *ctx;
 
+               if (((guint64)(*lmf)->previous_lmf) & 2) {
+                       /* 
+                        * This LMF entry is created by the soft debug code to mark transitions to
+                        * managed code done during invokes.
+                        */
+                       MonoLMFExt *ext = (MonoLMFExt*)(*lmf);
+
+                       g_assert (ext->debugger_invoke);
+
+                       memcpy (new_ctx, &ext->ctx, sizeof (MonoContext));
+
+                       *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
+
+                       frame->type = FRAME_TYPE_DEBUGGER_INVOKE;
+
+                       return TRUE;
+               }
+               
                if ((ji = mini_jit_info_table_find (domain, (gpointer)(*lmf)->eip, NULL))) {
                } else {
                        if (!((guint32)((*lmf)->previous_lmf) & 1))
                                /* Top LMF entry */
-                               return (gpointer)-1;
+                               return FALSE;
                        /* Trampoline lmf frame */
-                       memset (res, 0, MONO_SIZEOF_JIT_INFO);
-                       res->method = (*lmf)->method;
+                       frame->method = (*lmf)->method;
                }
 
                new_ctx->esi = (*lmf)->esi;
@@ -688,6 +696,9 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                new_ctx->ebp = (*lmf)->ebp;
                new_ctx->eip = (*lmf)->eip;
 
+               frame->ji = ji;
+               frame->type = FRAME_TYPE_MANAGED_TO_NATIVE;
+
                /* Check if we are in a trampoline LMF frame */
                if ((guint32)((*lmf)->previous_lmf) & 1) {
                        /* lmf->esp is set by the trampoline code */
@@ -709,12 +720,12 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInf
                         * expression points to a stack location which can be used as ESP */
                        new_ctx->esp = (unsigned long)&((*lmf)->eip);
 
-               *lmf = (gpointer)(((guint32)(*lmf)->previous_lmf) & ~1);
+               *lmf = (gpointer)(((gsize)(*lmf)->previous_lmf) & ~3);
 
-               return ji ? ji : res;
+               return TRUE;
        }
 
-       return NULL;
+       return FALSE;
 }
 
 #ifdef __sun
index 7d853057dd2f8146109a6d5cf5b170386cd45693..a5fb5bb0e9e8f8ea533414f06dcf74a94f2fd165 100644 (file)
@@ -359,6 +359,12 @@ alloc_dreg (MonoCompile *cfg, MonoStackType stack_type)
         (dest)->klass = mono_class_from_mono_type (ltype); \
        } while (0)
 
+#define NEW_SEQ_POINT(cfg,dest,il_offset,ss_loc) do {   \
+       MONO_INST_NEW ((cfg), (dest), OP_SEQ_POINT); \
+       (dest)->inst_imm = (il_offset); \
+       (dest)->flags = ss_loc ? MONO_INST_SINGLE_STEP_LOC : 0; \
+       } while (0)
+
 /*
  * Variants which do an emit as well.
  */
index 8d1e2a1c6c4aad0c9e5ab641ff0f93c38ab7d35d..7a90f8d880830fcfa69fc6d674843350d58d6b47 100644 (file)
@@ -57,6 +57,7 @@
 #include "ir-emit.h"
 
 #include "jit-icalls.h"
+#include "debugger-agent.h"
 
 #define BRANCH_COST 100
 #define INLINE_LENGTH_LIMIT 20
@@ -5345,7 +5346,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        GSList *class_inits = NULL;
        gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
        int context_used;
-       gboolean init_locals;
+       gboolean init_locals, seq_points;
 
        /* serialization and xdomain stuff may need access to private fields and methods */
        dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
@@ -5373,6 +5374,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        mono_jit_stats.cil_code_size += header->code_size;
        init_locals = header->init_locals;
 
+       seq_points = cfg->gen_seq_points && cfg->method == method;
+
        /* 
         * Methods without init_locals set could cause asserts in various passes
         * (#497220).
@@ -5783,6 +5786,16 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        }
                }
 
+               /*
+                * Sequence points are points where the debugger can place a breakpoint.
+                * Currently, we generate these automatically at points where the IL
+                * stack is empty.
+                */
+               if (seq_points && sp == stack_start) {
+                       NEW_SEQ_POINT (cfg, ins, ip - header->code, TRUE);
+                       MONO_ADD_INS (cfg->cbb, ins);
+               }
+
                bblock->real_offset = cfg->real_offset;
 
                if ((cfg->method == method) && cfg->coverage_info) {
@@ -8021,7 +8034,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                                    field->offset);
                                        iargs [4] = sp [1];
 
-                                       if (cfg->opt & MONO_OPT_INLINE) {
+                                       if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
                                                costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper), 
                                                                       iargs, ip, cfg->real_offset, dont_inline, TRUE);
                                                g_assert (costs > 0);
@@ -9847,6 +9860,14 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                }
        }
 
+       /* Add a sequence point for method entry/exit events */
+       if (seq_points) {
+               NEW_SEQ_POINT (cfg, ins, METHOD_ENTRY_IL_OFFSET, FALSE);
+               MONO_ADD_INS (init_localsbb, ins);
+               NEW_SEQ_POINT (cfg, ins, METHOD_EXIT_IL_OFFSET, FALSE);
+               MONO_ADD_INS (cfg->bb_exit, ins);
+       }
+
        cfg->ip = NULL;
 
        if (cfg->method == method) {
index 4cab6ec61e429dc4ec666a53dac65ea3cd9d4608..fc097000f02d8241a2240347c4e6ef6146dda09b 100644 (file)
 #include <mono/metadata/profiler-private.h>
 #include <mono/metadata/mono-debug.h>
 #include <mono/utils/mono-math.h>
+#include <mono/utils/mono-mmap.h>
 
 #include "trace.h"
 #include "ir-emit.h"
 #include "mini-amd64.h"
 #include "cpu-amd64.h"
+#include "debugger-agent.h"
 
 /* 
  * Can't define this in mini-amd64.h cause that would turn on the generic code in
@@ -59,6 +61,9 @@ static gboolean optimize_for_xen = TRUE;
 #define CALLCONV_IS_STDCALL(call_conv) ((call_conv) == MONO_CALL_STDCALL)
 #endif
 
+/* amd64_mov_reg_imm () */
+#define BREAKPOINT_SIZE 8
+
 /* This mutex protects architecture specific caches */
 #define mono_mini_arch_lock() EnterCriticalSection (&mini_arch_mutex)
 #define mono_mini_arch_unlock() LeaveCriticalSection (&mini_arch_mutex)
@@ -67,6 +72,15 @@ static CRITICAL_SECTION mini_arch_mutex;
 MonoBreakpointInfo
 mono_breakpoint_info [MONO_BREAKPOINT_ARRAY_SIZE];
 
+/*
+ * The code generated for sequence points reads from this location, which is
+ * made read-only when single stepping is enabled.
+ */
+static gpointer ss_trigger_page;
+
+/* Enabled breakpoints read from this trigger page */
+static gpointer bp_trigger_page;
+
 #ifdef PLATFORM_WIN32
 /* On Win64 always reserve first 32 bytes for first four arguments */
 #define ARGS_OFFSET 48
@@ -896,6 +910,10 @@ void
 mono_arch_init (void)
 {
        InitializeCriticalSection (&mini_arch_mutex);
+
+       ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
+       bp_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
+       mono_mprotect (bp_trigger_page, mono_pagesize (), 0);
 }
 
 /*
@@ -3475,6 +3493,34 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_NOT_REACHED:
                case OP_NOT_NULL:
                        break;
+               case OP_SEQ_POINT: {
+                       int i, il_offset;
+
+                       /* 
+                        * Read from the single stepping trigger page. This will cause a
+                        * SIGSEGV when single stepping is enabled.
+                        * We do this _before_ the breakpoint, so single stepping after
+                        * a breakpoint is hit will step to the next IL offset.
+                        */
+                       g_assert (((guint64)ss_trigger_page >> 32) == 0);
+
+                       if (ins->flags & MONO_INST_SINGLE_STEP_LOC)
+                               amd64_mov_reg_mem (code, AMD64_R11, (guint64)ss_trigger_page, 4);
+
+                       il_offset = ins->inst_imm;
+
+                       if (!cfg->seq_points)
+                               cfg->seq_points = g_ptr_array_new ();
+                       g_ptr_array_add (cfg->seq_points, GUINT_TO_POINTER (il_offset));
+                       g_ptr_array_add (cfg->seq_points, GUINT_TO_POINTER (code - cfg->native_code));
+                       /* 
+                        * A placeholder for a possible breakpoint inserted by
+                        * mono_arch_set_breakpoint ().
+                        */
+                       for (i = 0; i < BREAKPOINT_SIZE; ++i)
+                               x86_nop (code);
+                       break;
+               }
                case OP_ADDCC:
                case OP_LADD:
                        amd64_alu_reg_reg (code, X86_ADD, ins->sreg1, ins->sreg2);
@@ -7322,3 +7368,160 @@ mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
                        g_assert_not_reached ();
        }
 }
+
+/*
+ * mono_arch_set_breakpoint:
+ *
+ *   Set a breakpoint at the native code corresponding to JI at NATIVE_OFFSET.
+ * The location should contain code emitted by OP_SEQ_POINT.
+ */
+void
+mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+       guint8 *code = ip;
+       guint8 *orig_code = code;
+
+       /* 
+        * In production, we will use int3 (has to fix the size in the md 
+        * file). But that could confuse gdb, so during development, we emit a SIGSEGV
+        * instead.
+        */
+       g_assert (code [0] == 0x90);
+
+       g_assert (((guint64)bp_trigger_page >> 32) == 0);
+
+       amd64_mov_reg_mem (code, AMD64_R11, (guint64)bp_trigger_page, 4);
+       g_assert (code - orig_code == BREAKPOINT_SIZE);
+}
+
+/*
+ * mono_arch_clear_breakpoint:
+ *
+ *   Clear the breakpoint at IP.
+ */
+void
+mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+       guint8 *code = ip;
+       int i;
+
+       for (i = 0; i < BREAKPOINT_SIZE; ++i)
+               x86_nop (code);
+}
+       
+/*
+ * mono_arch_start_single_stepping:
+ *
+ *   Start single stepping.
+ */
+void
+mono_arch_start_single_stepping (void)
+{
+       mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
+}
+       
+/*
+ * mono_arch_stop_single_stepping:
+ *
+ *   Stop single stepping.
+ */
+void
+mono_arch_stop_single_stepping (void)
+{
+       mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
+}
+
+/*
+ * mono_arch_is_single_step_event:
+ *
+ *   Return whenever the machine state in SIGCTX corresponds to a single
+ * step event.
+ */
+gboolean
+mono_arch_is_single_step_event (siginfo_t *info, void *sigctx)
+{
+       /* Sometimes the address is off by 4 */
+       if (info->si_addr >= ss_trigger_page && (guint8*)info->si_addr <= (guint8*)ss_trigger_page + 128)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+gboolean
+mono_arch_is_breakpoint_event (siginfo_t *info, void *sigctx)
+{
+       /* Sometimes the address is off by 4 */
+       if (info->si_addr >= bp_trigger_page && (guint8*)info->si_addr <= (guint8*)bp_trigger_page + 128)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+/*
+ * mono_arch_get_ip_for_breakpoint:
+ *
+ *   Convert the ip in CTX to the address where a breakpoint was placed.
+ */
+guint8*
+mono_arch_get_ip_for_breakpoint (MonoJitInfo *ji, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+
+       /* size of xor r11, r11 */
+       ip -= 0;
+
+       return ip;
+}
+
+/*
+ * mono_arch_get_ip_for_single_step:
+ *
+ *   Convert the ip in CTX to the address stored in seq_points.
+ */
+guint8*
+mono_arch_get_ip_for_single_step (MonoJitInfo *ji, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+
+       /* Size of amd64_mov_reg_mem (r11) */
+       ip += 8;
+
+       return ip;
+}
+
+/*
+ * mono_arch_skip_breakpoint:
+ *
+ *   Modify CTX so the ip is placed after the breakpoint instruction, so when
+ * we resume, the instruction is not executed again.
+ */
+void
+mono_arch_skip_breakpoint (MonoContext *ctx)
+{
+       MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + BREAKPOINT_SIZE);
+}
+
+/*
+ * mono_arch_skip_single_step:
+ *
+ *   Modify CTX so the ip is placed after the single step trigger instruction,
+ * we resume, the instruction is not executed again.
+ */
+void
+mono_arch_skip_single_step (MonoContext *ctx)
+{
+       MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 8);
+}
+
+/*
+ * mono_arch_create_seq_point_info:
+ *
+ *   Return a pointer to a data structure which is used by the sequence
+ * point implementation in AOTed code.
+ */
+gpointer
+mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
+{
+       NOT_IMPLEMENTED;
+       return NULL;
+}
index 90a0f61e0b9b81d6b9d3400a4599d789acdb8a4d..986912796fc6fb80085f8a638570a16a9ce36f4a 100644 (file)
@@ -136,6 +136,8 @@ struct MonoLMF {
         * If the lowest bit is set to 1, then this LMF has the rip field set. Otherwise,
         * the rip field is not set, and the rsp field points to the stack location where
         * the caller ip is saved.
+        * If the second lowest bit is set to 1, then this is a MonoLMFExt structure, and
+        * the other fields are not valid.
         */
        gpointer    previous_lmf;
        gpointer    lmf_addr;
@@ -223,6 +225,15 @@ typedef struct {
 
 #endif
 
+/*
+ * This structure is an extension of MonoLMF and contains extra information.
+ */
+typedef struct {
+       struct MonoLMF lmf;
+       gboolean debugger_invoke;
+       MonoContext ctx; /* if debugger_invoke is TRUE */
+} MonoLMFExt;
+
 /*
  * some icalls like mono_array_new_va needs to be called using a different 
  * calling convention.
@@ -343,6 +354,8 @@ typedef struct {
 #define MONO_ARCH_HAVE_STATIC_RGCTX_TRAMPOLINE 1
 
 #define MONO_ARCH_AOT_SUPPORTED 1
+#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
+#define MONO_ARCH_HAVE_FIND_JIT_INFO_EXT 1
 
 #if !defined(PLATFORM_WIN32) || defined(__sun)
 #define MONO_ARCH_ENABLE_MONITOR_IL_FASTPATH 1
index b7e6e8ba18054a984d88b2ee184a51defa6041af..912caa3ae35302949892eaa89b2336600d628e62 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <mono/metadata/appdomain.h>
 #include <mono/metadata/debug-helpers.h>
+#include <mono/utils/mono-mmap.h>
 
 #include "mini-arm.h"
 #include "cpu-arm.h"
@@ -39,6 +40,22 @@ static int v5_supported = 0;
 static int v7_supported = 0;
 static int thumb_supported = 0;
 
+/*
+ * The code generated for sequence points reads from this location, which is
+ * made read-only when single stepping is enabled.
+ */
+static gpointer ss_trigger_page;
+
+/* Enabled breakpoints read from this trigger page */
+static gpointer bp_trigger_page;
+
+/* Structure used by the sequence points in AOTed code */
+typedef struct {
+       gpointer ss_trigger_page;
+       gpointer bp_trigger_page;
+       guint8* bp_addrs [MONO_ZERO_LEN_ARRAY];
+} SeqPointInfo;
+
 /*
  * TODO:
  * floating point support: on ARM it is a mess, there are at least 3
@@ -489,7 +506,11 @@ mono_arch_cpu_init (void)
 void
 mono_arch_init (void)
 {
-       InitializeCriticalSection (&mini_arch_mutex);   
+       InitializeCriticalSection (&mini_arch_mutex);
+
+       ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
+       bp_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
+       mono_mprotect (bp_trigger_page, mono_pagesize (), 0);
 }
 
 /*
@@ -1171,6 +1192,17 @@ mono_arch_create_vars (MonoCompile *cfg)
                        mono_print_ins (cfg->vret_addr);
                }
        }
+
+       if (cfg->gen_seq_points && cfg->compile_aot) {
+           MonoInst *ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+               ins->flags |= MONO_INST_VOLATILE;
+               cfg->arch.seq_point_info_var = ins;
+
+               /* Allocate a separate variable for this to save 1 load per seq point */
+           ins = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+               ins->flags |= MONO_INST_VOLATILE;
+               cfg->arch.ss_trigger_page_var = ins;
+       }
 }
 
 void
@@ -3032,6 +3064,95 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_NOT_REACHED:
                case OP_NOT_NULL:
                        break;
+               case OP_SEQ_POINT: {
+                       int i, il_offset;
+                       MonoInst *info_var = cfg->arch.seq_point_info_var;
+                       MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var;
+                       MonoInst *var;
+                       int dreg = ARMREG_LR;
+
+                       /*
+                        * For AOT, we use one got slot per method, which will point to a
+                        * SeqPointInfo structure, containing all the information required
+                        * by the code below.
+                        */
+                       if (cfg->compile_aot) {
+                               g_assert (info_var);
+                               g_assert (info_var->opcode == OP_REGOFFSET);
+                               g_assert (arm_is_imm12 (info_var->inst_offset));
+                       }
+
+                       /* 
+                        * Read from the single stepping trigger page. This will cause a
+                        * SIGSEGV when single stepping is enabled.
+                        * We do this _before_ the breakpoint, so single stepping after
+                        * a breakpoint is hit will step to the next IL offset.
+                        */
+                       g_assert (((guint64)(gsize)ss_trigger_page >> 32) == 0);
+
+                       if (ins->flags & MONO_INST_SINGLE_STEP_LOC) {
+                               if (cfg->compile_aot) {
+                                       /* Load the trigger page addr from the variable initialized in the prolog */
+                                       var = ss_trigger_page_var;
+                                       g_assert (var);
+                                       g_assert (var->opcode == OP_REGOFFSET);
+                                       g_assert (arm_is_imm12 (var->inst_offset));
+                                       ARM_LDR_IMM (code, dreg, var->inst_basereg, var->inst_offset);
+                               } else {
+                                       ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
+                                       ARM_B (code, 0);
+                                       *(int*)code = (int)ss_trigger_page;
+                                       code += 4;
+                               }
+                               ARM_LDR_IMM (code, dreg, dreg, 0);
+                       }
+
+                       il_offset = ins->inst_imm;
+
+                       if (!cfg->seq_points)
+                               cfg->seq_points = g_ptr_array_new ();
+                       g_ptr_array_add (cfg->seq_points, GUINT_TO_POINTER (il_offset));
+                       g_ptr_array_add (cfg->seq_points, GUINT_TO_POINTER (code - cfg->native_code));
+
+                       if (cfg->compile_aot) {
+                               guint32 offset = code - cfg->native_code;
+                               guint32 val;
+
+                               ARM_LDR_IMM (code, dreg, info_var->inst_basereg, info_var->inst_offset);
+                               /* Add the offset */
+                               val = ((offset / 4) * sizeof (guint8*)) + G_STRUCT_OFFSET (SeqPointInfo, bp_addrs);
+                               ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF), 0);
+                               /* 
+                                * Have to emit nops to keep the difference between the offset
+                                * stored in seq_points and breakpoint instruction constant,
+                                * mono_arch_get_ip_for_breakpoint () depends on this.
+                                */
+                               if (val & 0xFF00)
+                                       ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF00) >> 8, 24);
+                               else
+                                       ARM_NOP (code);
+                               if (val & 0xFF0000)
+                                       ARM_ADD_REG_IMM (code, dreg, dreg, (val & 0xFF0000) >> 16, 16);
+                               else
+                                       ARM_NOP (code);
+                               g_assert (!(val & 0xFF000000));
+                               /* Load the info->bp_addrs [offset], which is either 0 or the address of a trigger page */
+                               ARM_LDR_IMM (code, dreg, dreg, 0);
+
+                               /* What is faster, a branch or a load ? */
+                               ARM_CMP_REG_IMM (code, dreg, 0, 0);
+                               /* The breakpoint instruction */
+                               ARM_LDR_IMM_COND (code, dreg, dreg, 0, ARMCOND_NE);
+                       } else {
+                               /* 
+                                * A placeholder for a possible breakpoint inserted by
+                                * mono_arch_set_breakpoint ().
+                                */
+                               for (i = 0; i < 4; ++i)
+                                       ARM_NOP (code);
+                       }
+                       break;
+               }
                case OP_ADDCC:
                case OP_IADDCC:
                        ARM_ADDS_REG_REG (code, ins->dreg, ins->sreg1, ins->sreg2);
@@ -4466,6 +4587,44 @@ mono_arch_emit_prolog (MonoCompile *cfg)
        if (tracing)
                code = mono_arch_instrument_prolog (cfg, mono_trace_enter_method, code, TRUE);
 
+       if (cfg->arch.seq_point_info_var) {
+               MonoInst *ins = cfg->arch.seq_point_info_var;
+
+               /* Initialize the variable from a GOT slot */
+               mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_SEQ_POINT_INFO, cfg->method);
+               ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
+               ARM_B (code, 0);
+               *(gpointer*)code = NULL;
+               code += 4;
+               ARM_LDR_REG_REG (code, ARMREG_R0, ARMREG_PC, ARMREG_R0);
+
+               g_assert (ins->opcode == OP_REGOFFSET);
+
+               if (arm_is_imm12 (ins->inst_offset)) {
+                       ARM_STR_IMM (code, ARMREG_R0, ins->inst_basereg, ins->inst_offset);
+               } else {
+                       code = mono_arm_emit_load_imm (code, ARMREG_LR, ins->inst_offset);
+                       ARM_STR_REG_REG (code, ARMREG_R0, ins->inst_basereg, ARMREG_LR);
+               }
+       }
+
+       /* Initialize ss_trigger_page_var */
+       {
+               MonoInst *info_var = cfg->arch.seq_point_info_var;
+               MonoInst *ss_trigger_page_var = cfg->arch.ss_trigger_page_var;
+               int dreg = ARMREG_LR;
+
+               if (info_var) {
+                       g_assert (info_var->opcode == OP_REGOFFSET);
+                       g_assert (arm_is_imm12 (info_var->inst_offset));
+
+                       ARM_LDR_IMM (code, dreg, info_var->inst_basereg, info_var->inst_offset);
+                       /* Load the trigger page addr */
+                       ARM_LDR_IMM (code, dreg, dreg, G_STRUCT_OFFSET (SeqPointInfo, ss_trigger_page));
+                       ARM_STR_IMM (code, dreg, ss_trigger_page_var->inst_basereg, ss_trigger_page_var->inst_offset);
+               }
+       }
+
        cfg->code_len = code - cfg->native_code;
        g_assert (cfg->code_len < cfg->code_size);
        g_free (cinfo);
@@ -4980,3 +5139,214 @@ mono_arch_context_get_int_reg (MonoContext *ctx, int reg)
                return NULL;
        }
 }
+
+/*
+ * mono_arch_set_breakpoint:
+ *
+ *   Set a breakpoint at the native code corresponding to JI at NATIVE_OFFSET.
+ * The location should contain code emitted by OP_SEQ_POINT.
+ */
+void
+mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+       guint8 *code = ip;
+       guint32 native_offset = ip - (guint8*)ji->code_start;
+
+       if (ji->from_aot) {
+               SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
+
+               g_assert (native_offset % 4 == 0);
+               g_assert (info->bp_addrs [native_offset / 4] == 0);
+               info->bp_addrs [native_offset / 4] = bp_trigger_page;
+       } else {
+               int dreg = ARMREG_LR;
+
+               /* Read from another trigger page */
+               ARM_LDR_IMM (code, dreg, ARMREG_PC, 0);
+               ARM_B (code, 0);
+               *(int*)code = (int)bp_trigger_page;
+               code += 4;
+               ARM_LDR_IMM (code, dreg, dreg, 0);
+
+               mono_arch_flush_icache (code - 16, 16);
+
+#if 0
+               /* This is currently implemented by emitting an SWI instruction, which 
+                * qemu/linux seems to convert to a SIGILL.
+                */
+               *(int*)code = (0xef << 24) | 8;
+               code += 4;
+               mono_arch_flush_icache (code - 4, 4);
+#endif
+       }
+}
+
+/*
+ * mono_arch_clear_breakpoint:
+ *
+ *   Clear the breakpoint at IP.
+ */
+void
+mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+       guint8 *code = ip;
+       int i;
+
+       if (ji->from_aot) {
+               guint32 native_offset = ip - (guint8*)ji->code_start;
+               SeqPointInfo *info = mono_arch_get_seq_point_info (mono_domain_get (), ji->code_start);
+
+               g_assert (native_offset % 4 == 0);
+               g_assert (info->bp_addrs [native_offset / 4] == bp_trigger_page);
+               info->bp_addrs [native_offset / 4] = 0;
+       } else {
+               for (i = 0; i < 4; ++i)
+                       ARM_NOP (code);
+
+               mono_arch_flush_icache (ip, code - ip);
+       }
+}
+       
+/*
+ * mono_arch_start_single_stepping:
+ *
+ *   Start single stepping.
+ */
+void
+mono_arch_start_single_stepping (void)
+{
+       mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
+}
+       
+/*
+ * mono_arch_stop_single_stepping:
+ *
+ *   Stop single stepping.
+ */
+void
+mono_arch_stop_single_stepping (void)
+{
+       mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
+}
+
+#if __APPLE__
+#define DBG_SIGNAL SIGBUS
+#else
+#define DBG_SIGNAL SIGSEGV
+#endif
+
+/*
+ * mono_arch_is_single_step_event:
+ *
+ *   Return whenever the machine state in SIGCTX corresponds to a single
+ * step event.
+ */
+gboolean
+mono_arch_is_single_step_event (siginfo_t *info, void *sigctx)
+{
+       /* Sometimes the address is off by 4 */
+       if (info->si_addr >= ss_trigger_page && (guint8*)info->si_addr <= (guint8*)ss_trigger_page + 128)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+/*
+ * mono_arch_is_breakpoint_event:
+ *
+ *   Return whenever the machine state in SIGCTX corresponds to a breakpoint event.
+ */
+gboolean
+mono_arch_is_breakpoint_event (siginfo_t *info, void *sigctx)
+{
+       if (info->si_signo == DBG_SIGNAL) {
+               /* Sometimes the address is off by 4 */
+               if (info->si_addr >= bp_trigger_page && (guint8*)info->si_addr <= (guint8*)bp_trigger_page + 128)
+                       return TRUE;
+               else
+                       return FALSE;
+       } else {
+               return FALSE;
+       }
+}
+
+guint8*
+mono_arch_get_ip_for_breakpoint (MonoJitInfo *ji, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+
+       if (ji->from_aot)
+               ip -= 6 * 4;
+       else
+               ip -= 12;
+
+       return ip;
+}
+
+guint8*
+mono_arch_get_ip_for_single_step (MonoJitInfo *ji, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+
+       ip += 4;
+
+       return ip;
+}
+
+/*
+ * mono_arch_skip_breakpoint:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_skip_breakpoint (MonoContext *ctx)
+{
+       MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 4);
+}
+
+/*
+ * mono_arch_skip_single_step:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_skip_single_step (MonoContext *ctx)
+{
+       MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 4);
+}
+
+/*
+ * mono_arch_get_seq_point_info:
+ *
+ *   See mini-amd64.c for docs.
+ */
+gpointer
+mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
+{
+       SeqPointInfo *info;
+       MonoJitInfo *ji;
+
+       // FIXME: Add a free function
+
+       mono_domain_lock (domain);
+       info = g_hash_table_lookup (domain_jit_info (domain)->arch_seq_points, 
+                                                               code);
+       mono_domain_unlock (domain);
+
+       if (!info) {
+               ji = mono_jit_info_table_find (domain, (char*)code);
+               g_assert (ji);
+
+               info = g_malloc0 (sizeof (SeqPointInfo) + ji->code_size);
+
+               info->ss_trigger_page = ss_trigger_page;
+               info->bp_trigger_page = bp_trigger_page;
+
+               mono_domain_lock (domain);
+               g_hash_table_insert (domain_jit_info (domain)->arch_seq_points,
+                                                        code, info);
+               mono_domain_unlock (domain);
+       }
+
+       return info;
+}
index 7eca9ea4af650cf10aca2bef318fe3163ed02e8e..53c3f23113b10e84ddaebcae2eb0d0c2ee9b7d90 100644 (file)
@@ -93,6 +93,10 @@ mono_arm_throw_exception_by_token (guint32 type_token, unsigned long eip, unsign
 
 /* keep the size of the structure a multiple of 8 */
 struct MonoLMF {
+       /* 
+        * If the second lowest bit is set to 1, then this is a MonoLMFExt structure, and
+        * the other fields are not valid.
+        */
        gpointer    previous_lmf;
        gpointer    lmf_addr;
        MonoMethod *method;
@@ -122,6 +126,7 @@ typedef struct {
 
 typedef struct MonoCompileArch {
        int dummy;
+       gpointer seq_point_info_var, ss_trigger_page_var;
 } MonoCompileArch;
 
 #define MONO_ARCH_EMULATE_FCONV_TO_I8 1
@@ -158,6 +163,9 @@ typedef struct MonoCompileArch {
 #define MONO_ARCH_DYN_CALL_SUPPORTED 1
 #define MONO_ARCH_DYN_CALL_PARAM_AREA 24
 
+#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
+#define MONO_ARCH_HAVE_FIND_JIT_INFO_EXT 1
+
 /* ARM doesn't have too many registers, so we have to use a callee saved one */
 #define MONO_ARCH_RGCTX_REG ARMREG_V5
 /* First argument reg */
@@ -188,6 +196,15 @@ typedef struct MonoCompileArch {
        #define UCONTEXT_REG_R4(ctx) ((ctx)->sig_ctx.arm_r4)
 #endif
 
+/*
+ * This structure is an extension of MonoLMF and contains extra information.
+ */
+typedef struct {
+       struct MonoLMF lmf;
+       gboolean debugger_invoke;
+       MonoContext ctx; /* if debugger_invoke is TRUE */
+} MonoLMFExt;
+
 void
 mono_arm_throw_exception (MonoObject *exc, unsigned long eip, unsigned long esp, gulong *int_regs, gdouble *fp_regs);
 
index 3f6f884fbabb9c2f77a0bdee800a78be5d0032ef..8648936e2d7e618ce08fa731a764da90fbea9995 100644 (file)
@@ -48,6 +48,7 @@
 #include "mini.h"
 #include "debug-mini.h"
 #include "trace.h"
+#include "debugger-agent.h"
 
 #ifndef MONO_ARCH_CONTEXT_DEF
 #define MONO_ARCH_CONTEXT_DEF
@@ -187,6 +188,69 @@ mono_get_throw_corlib_exception (void)
        return throw_corlib_exception_func;
 }
 
+#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+
+/*
+ * find_jit_info_no_ext:
+ *
+ * If the target has the find_jit_info_ext version of this function, define the old
+ * version here which translates between the old and new APIs.
+ */
+static MonoJitInfo *
+find_jit_info_no_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, 
+                          MonoContext *new_ctx, MonoLMF **lmf, gboolean *managed)
+{
+       StackFrameInfo frame;
+       MonoJitInfo *ji;
+       gboolean err;
+       gpointer ip = MONO_CONTEXT_GET_IP (ctx);
+
+       /* Avoid costly table lookup during stack overflow */
+       if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
+               ji = prev_ji;
+       else
+               ji = mini_jit_info_table_find (domain, ip, NULL);
+
+       if (managed)
+               *managed = FALSE;
+
+       err = mono_arch_find_jit_info_ext (domain, jit_tls, ji, ctx, new_ctx, lmf, &frame);
+       if (!err)
+               return (gpointer)-1;
+
+       /* Convert between the new and the old APIs */
+       switch (frame.type) {
+       case FRAME_TYPE_MANAGED:
+               if (managed)
+                       *managed = TRUE;
+               return ji;
+       case FRAME_TYPE_MANAGED_TO_NATIVE:
+               if (frame.ji)
+                       return frame.ji;
+               else {
+                       memset (res, 0, sizeof (MonoJitInfo));
+                       res->method = frame.method;
+                       return res;
+               }
+       case FRAME_TYPE_DEBUGGER_INVOKE: {
+               MonoContext tmp_ctx;
+
+               /*
+                * The normal exception handling code can't handle this frame, so just
+                * skip it.
+                */
+               ji = find_jit_info_no_ext (domain, jit_tls, res, NULL, new_ctx, &tmp_ctx, lmf, managed);
+               memcpy (new_ctx, &tmp_ctx, sizeof (MonoContext));
+               return ji;
+       }
+       default:
+               g_assert_not_reached ();
+               return NULL;
+       }
+}
+
+#endif
+
 /* mono_find_jit_info:
  *
  * This function is used to gather information from @ctx. It return the 
@@ -214,7 +278,11 @@ mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *re
        if (managed)
                *managed = FALSE;
 
+#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+       ji = find_jit_info_no_ext (domain, jit_tls, res, prev_ji, ctx, new_ctx, lmf, &managed2);
+#else
        ji = mono_arch_find_jit_info (domain, jit_tls, res, prev_ji, ctx, new_ctx, lmf, &managed2);
+#endif
 
        if (ji == (gpointer)-1)
                return ji;
@@ -255,6 +323,76 @@ mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *re
        return ji;
 }
 
+#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+
+/*
+ * mono_find_jit_info_ext:
+ *
+ *   A version of mono_find_jit_info which returns all data in the StackFrameInfo
+ * structure.
+ */
+static gboolean
+mono_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
+                                               MonoJitInfo *prev_ji, MonoContext *ctx,
+                                               MonoContext *new_ctx, char **trace, MonoLMF **lmf,
+                                               StackFrameInfo *frame)
+{
+       gboolean err;
+       gpointer ip = MONO_CONTEXT_GET_IP (ctx);
+       MonoJitInfo *ji;
+       MonoDomain *target_domain;
+
+       if (trace)
+               *trace = NULL;
+
+       /* Avoid costly table lookup during stack overflow */
+       if (prev_ji && (ip > prev_ji->code_start && ((guint8*)ip < ((guint8*)prev_ji->code_start) + prev_ji->code_size)))
+               ji = prev_ji;
+       else
+               ji = mini_jit_info_table_find (domain, ip, &target_domain);
+
+       if (!target_domain)
+               target_domain = domain;
+
+       err = mono_arch_find_jit_info_ext (target_domain, jit_tls, ji, ctx, new_ctx, lmf, frame);
+       if (!err)
+               return FALSE;
+
+       frame->native_offset = -1;
+       frame->domain = target_domain;
+
+       ji = frame->ji;
+
+       if (ji && (frame->managed || ji->method->wrapper_type)) {
+               const char *real_ip, *start;
+
+               start = (const char *)ji->code_start;
+               if (!frame->managed)
+                       /* ctx->ip points into native code */
+                       real_ip = (const char*)MONO_CONTEXT_GET_IP (new_ctx);
+               else
+                       real_ip = (const char*)ip;
+
+               if ((real_ip >= start) && (real_ip <= start + ji->code_size))
+                       frame->native_offset = real_ip - start;
+               else
+                       frame->native_offset = -1;
+
+               if (trace)
+                       *trace = mono_debug_print_stack_frame (ji->method, frame->native_offset, domain);
+       } else {
+               if (trace && frame->method) {
+                       char *fname = mono_method_full_name (frame->method, TRUE);
+                       *trace = g_strdup_printf ("in (unmanaged) %s", fname);
+                       g_free (fname);
+               }
+       }
+
+       return TRUE;
+}
+
+#endif /* MONO_ARCH_HAVE_FIND_JIT_INFO_EXT */
+
 static gpointer
 get_generic_info_from_stack_frame (MonoJitInfo *ji, MonoContext *ctx)
 {
@@ -562,6 +700,72 @@ mono_jit_walk_stack (MonoStackWalk func, gboolean do_il_offset, gpointer user_da
        mono_jit_walk_stack_from_ctx (func, NULL, do_il_offset, user_data);
 }
 
+void
+mono_jit_walk_stack_from_ctx_in_thread (MonoJitStackWalk func, MonoDomain *domain, MonoContext *start_ctx, gboolean do_il_offset, MonoInternalThread *thread, MonoLMF *lmf, gpointer user_data)
+{
+       MonoJitTlsData *jit_tls = thread->jit_data;
+       gint il_offset;
+       MonoContext ctx, new_ctx;
+       StackFrameInfo frame;
+#ifndef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+       gint native_offset;
+       gboolean managed;
+       MonoJitInfo *ji, rji;
+#else
+       gboolean res;
+#endif
+       
+       MONO_ARCH_CONTEXT_DEF
+
+       mono_arch_flush_register_windows ();
+
+       if (start_ctx) {
+               memcpy (&ctx, start_ctx, sizeof (MonoContext));
+       } else {
+#ifdef MONO_INIT_CONTEXT_FROM_CURRENT
+               MONO_INIT_CONTEXT_FROM_CURRENT (&ctx);
+#else
+               MONO_INIT_CONTEXT_FROM_FUNC (&ctx, mono_jit_walk_stack_from_ctx);
+#endif
+               g_assert (thread == mono_thread_internal_current ());
+       }
+
+       while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
+#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+               res = mono_find_jit_info_ext (domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, &frame);
+               if (!res)
+                       return;
+#else
+               ji = mono_find_jit_info (domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, &native_offset, &managed);
+               g_assert (ji);
+               frame.type = FRAME_TYPE_MANAGED;
+               frame.ji = ji;
+               frame.managed = managed;
+               frame.native_offset = native_offset;
+
+               if (ji == (gpointer)-1)
+                       return;
+#endif
+
+               if (do_il_offset && frame.ji) {
+                       MonoDebugSourceLocation *source;
+
+                       source = mono_debug_lookup_source_location (frame.ji->method, frame.native_offset, domain);
+                       il_offset = source ? source->il_offset : -1;
+                       mono_debug_free_source_location (source);
+               } else
+                       il_offset = -1;
+
+               frame.il_offset = il_offset;
+
+               if (func (&frame, &ctx, user_data))
+                       return;
+               
+               ctx = new_ctx;
+       }
+}
+
+
 MonoBoolean
 ves_icall_get_frame_info (gint32 skip, MonoBoolean need_file_info, 
                          MonoReflectionMethod **method, 
@@ -922,6 +1126,9 @@ mono_handle_exception_internal (MonoContext *ctx, gpointer obj, gpointer origina
                obj = (MonoObject *)ex;
        } 
 
+       if (!test_only)
+               mono_debugger_agent_handle_exception (obj, ctx);
+
        /*
         * Allocate a new exception object instead of the preconstructed ones.
         */
@@ -1282,6 +1489,7 @@ mono_handle_exception (MonoContext *ctx, gpointer obj, gpointer original_ip, gbo
 {
        if (!test_only)
                mono_perfcounters->exceptions_thrown++;
+
        return mono_handle_exception_internal (ctx, obj, original_ip, test_only, NULL, NULL);
 }
 
index b23d8bf1d2be77997fbd6be7707f877f535b4808..b5b0ebb142e8cff97c4d47f17d3f7b3765fd40a0 100644 (file)
@@ -26,6 +26,7 @@ MINI_OP(OP_SETLRET,   "setlret", NONE, IREG, IREG)
 MINI_OP(OP_LOCALLOC, "localloc", IREG, IREG, NONE)
 MINI_OP(OP_LOCALLOC_IMM, "localloc_imm", IREG, NONE, NONE)
 MINI_OP(OP_CHECK_THIS, "checkthis", NONE, IREG, NONE)
+MINI_OP(OP_SEQ_POINT, "seq_point", NONE, NONE, NONE)
 
 MINI_OP(OP_VOIDCALL,   "voidcall", NONE, NONE, NONE)
 MINI_OP(OP_VOIDCALLVIRT,       "voidcallvirt", NONE, NONE, NONE)
index a5fba4104f6143168fefcc62184418529b125e2f..b9839cb0797445ec6d8bc46ed399049e32ae6ff1 100644 (file)
@@ -59,6 +59,7 @@
 #include <ctype.h>
 #include "trace.h"
 #include "version.h"
+#include "debugger-agent.h"
 
 #include "jit-icalls.h"
 
@@ -184,6 +185,9 @@ SIG_HANDLER_SIGNATURE (sigusr1_signal_handler)
         */
        ji = mono_jit_info_table_find (mono_domain_get (), mono_arch_ip_from_context(ctx));
        running_managed = ji != NULL;
+
+       if (mono_debugger_agent_thread_interrupt (ji))
+               return;
        
        exc = mono_thread_request_interruption (running_managed); 
        if (!exc)
@@ -359,6 +363,15 @@ add_signal_handler (int signo, gpointer handler)
                        sigaddset (&sa.sa_mask, mono_gc_get_suspend_signal ());
        }
 #endif
+       if (signo == SIGSEGV) {
+               /* 
+                * Delay abort signals while handling SIGSEGVs since they could go unnoticed.
+                */
+               sigset_t block_mask;
+     
+               sigemptyset (&block_mask);
+               sigaddset (&sa.sa_mask, mono_thread_get_abort_signal ());
+       }
 #else
        sa.sa_handler = handler;
        sigemptyset (&sa.sa_mask);
index 3116bd69d8f98dff995a3469ae81ee9ef20b97d9..e5ac990f7a9aa4152bcd0bc0adc82eefc7ddb559 100644 (file)
@@ -22,6 +22,7 @@
 #include <mono/metadata/mono-debug.h>
 #include <mono/utils/mono-math.h>
 #include <mono/utils/mono-counters.h>
+#include <mono/utils/mono-mmap.h>
 
 #include "trace.h"
 #include "mini-x86.h"
@@ -64,6 +65,15 @@ static CRITICAL_SECTION mini_arch_mutex;
 MonoBreakpointInfo
 mono_breakpoint_info [MONO_BREAKPOINT_ARRAY_SIZE];
 
+/*
+ * The code generated for sequence points reads from this location, which is
+ * made read-only when single stepping is enabled.
+ */
+static gpointer ss_trigger_page;
+
+/* Enabled breakpoints read from this trigger page */
+static gpointer bp_trigger_page;
+
 const char*
 mono_arch_regname (int reg)
 {
@@ -648,6 +658,10 @@ void
 mono_arch_init (void)
 {
        InitializeCriticalSection (&mini_arch_mutex);
+
+       ss_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ);
+       bp_trigger_page = mono_valloc (NULL, mono_pagesize (), MONO_MMAP_READ|MONO_MMAP_32BIT);
+       mono_mprotect (bp_trigger_page, mono_pagesize (), 0);
 }
 
 /*
@@ -2340,6 +2354,32 @@ mono_arch_output_basic_block (MonoCompile *cfg, MonoBasicBlock *bb)
                case OP_NOT_REACHED:
                case OP_NOT_NULL:
                        break;
+               case OP_SEQ_POINT: {
+                       int i, il_offset;
+
+                       /* 
+                        * Read from the single stepping trigger page. This will cause a
+                        * SIGSEGV when single stepping is enabled.
+                        * We do this _before_ the breakpoint, so single stepping after
+                        * a breakpoint is hit will step to the next IL offset.
+                        */
+                       if (ins->flags & MONO_INST_SINGLE_STEP_LOC)
+                               x86_alu_reg_mem (code, X86_CMP, X86_EAX, (guint32)ss_trigger_page);
+
+                       il_offset = ins->inst_imm;
+
+                       if (!cfg->seq_points)
+                               cfg->seq_points = g_ptr_array_new ();
+                       g_ptr_array_add (cfg->seq_points, GUINT_TO_POINTER (il_offset));
+                       g_ptr_array_add (cfg->seq_points, GUINT_TO_POINTER (code - cfg->native_code));
+                       /* 
+                        * A placeholder for a possible breakpoint inserted by
+                        * mono_arch_set_breakpoint ().
+                        */
+                       for (i = 0; i < 6; ++i)
+                               x86_nop (code);
+                       break;
+               }
                case OP_ADDCC:
                case OP_IADDCC:
                case OP_IADD:
@@ -5704,3 +5744,156 @@ mono_arch_decompose_long_opts (MonoCompile *cfg, MonoInst *long_ins)
 #endif /* MONO_ARCH_SIMD_INTRINSICS */
 }
 
+#if __APPLE__
+#define DBG_SIGNAL SIGBUS
+#else
+#define DBG_SIGNAL SIGSEGV
+#endif
+
+/*
+ * mono_arch_set_breakpoint:
+ *
+ *   Set a breakpoint at the native code corresponding to JI at NATIVE_OFFSET.
+ * The location should contain code emitted by OP_SEQ_POINT.
+ */
+void
+mono_arch_set_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+       guint8 *code = ip;
+
+       /* 
+        * In production, we will use int3 (has to fix the size in the md 
+        * file). But that could confuse gdb, so during development, we emit a SIGSEGV
+        * instead.
+        */
+       g_assert (code [0] == 0x90);
+       x86_alu_reg_mem (code, X86_CMP, X86_EAX, (guint32)bp_trigger_page);
+}
+
+/*
+ * mono_arch_clear_breakpoint:
+ *
+ *   Clear the breakpoint at IP.
+ */
+void
+mono_arch_clear_breakpoint (MonoJitInfo *ji, guint8 *ip)
+{
+       guint8 *code = ip;
+       int i;
+
+       for (i = 0; i < 6; ++i)
+               x86_nop (code);
+}
+       
+/*
+ * mono_arch_start_single_stepping:
+ *
+ *   Start single stepping.
+ */
+void
+mono_arch_start_single_stepping (void)
+{
+       mono_mprotect (ss_trigger_page, mono_pagesize (), 0);
+}
+       
+/*
+ * mono_arch_stop_single_stepping:
+ *
+ *   Stop single stepping.
+ */
+void
+mono_arch_stop_single_stepping (void)
+{
+       mono_mprotect (ss_trigger_page, mono_pagesize (), MONO_MMAP_READ);
+}
+
+/*
+ * mono_arch_is_single_step_event:
+ *
+ *   Return whenever the machine state in SIGCTX corresponds to a single
+ * step event.
+ */
+gboolean
+mono_arch_is_single_step_event (siginfo_t *info, void *sigctx)
+{
+       /* Sometimes the address is off by 4 */
+       if (info->si_signo == DBG_SIGNAL && (info->si_addr >= ss_trigger_page && (guint8*)info->si_addr <= (guint8*)ss_trigger_page + 128))
+               return TRUE;
+       else
+               return FALSE;
+}
+
+gboolean
+mono_arch_is_breakpoint_event (siginfo_t *info, void *sigctx)
+{
+       /* Sometimes the address is off by 4 */
+       if (info->si_signo == DBG_SIGNAL && (info->si_addr >= bp_trigger_page && (guint8*)info->si_addr <= (guint8*)bp_trigger_page + 128))
+               return TRUE;
+       else
+               return FALSE;
+}
+
+/*
+ * mono_arch_get_ip_for_breakpoint:
+ *
+ *   See mini-amd64.c for docs.
+ */
+guint8*
+mono_arch_get_ip_for_breakpoint (MonoJitInfo *ji, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+
+       return ip;
+}
+
+#define BREAKPOINT_SIZE 6
+
+/*
+ * mono_arch_get_ip_for_single_step:
+ *
+ *   See mini-amd64.c for docs.
+ */
+guint8*
+mono_arch_get_ip_for_single_step (MonoJitInfo *ji, MonoContext *ctx)
+{
+       guint8 *ip = MONO_CONTEXT_GET_IP (ctx);
+
+       /* Size of x86_alu_reg_imm */
+       ip += 6;
+
+       return ip;
+}
+
+/*
+ * mono_arch_skip_breakpoint:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_skip_breakpoint (MonoContext *ctx)
+{
+       MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + BREAKPOINT_SIZE);
+}
+
+/*
+ * mono_arch_skip_single_step:
+ *
+ *   See mini-amd64.c for docs.
+ */
+void
+mono_arch_skip_single_step (MonoContext *ctx)
+{
+       MONO_CONTEXT_SET_IP (ctx, (guint8*)MONO_CONTEXT_GET_IP (ctx) + 6);
+}
+
+/*
+ * mono_arch_get_seq_point_info:
+ *
+ *   See mini-amd64.c for docs.
+ */
+gpointer
+mono_arch_get_seq_point_info (MonoDomain *domain, guint8 *code)
+{
+       NOT_IMPLEMENTED;
+       return NULL;
+}
index 9c5b1ec6b051200e63e31d12f20b80cdc3382100..feaade49a92a7abda7b4bf3f89ef0bc837bd45cb 100644 (file)
@@ -124,7 +124,11 @@ LONG CALLBACK seh_handler(EXCEPTION_POINTERS* ep);
 #define MONO_ARCH_RETREG2 X86_EDX
 
 struct MonoLMF {
-       /* Offset by 1 if this is a trampoline LMF frame */
+       /* 
+        * If the lowest bit is set to 1, then this is a trampoline LMF frame.
+        * If the second lowest bit is set to 1, then this is a MonoLMFExt structure, and
+        * the other fields are not valid.
+        */
        guint32    previous_lmf;
        gpointer    lmf_addr;
        /* Only set in trampoline LMF frames */
@@ -209,6 +213,15 @@ typedef struct {
 
 #endif
 
+/*
+ * This structure is an extension of MonoLMF and contains extra information.
+ */
+typedef struct {
+       struct MonoLMF lmf;
+       gboolean debugger_invoke;
+       MonoContext ctx; /* if debugger_invoke is TRUE */
+} MonoLMFExt;
+
 /* Enables OP_LSHL, OP_LSHL_IMM, OP_LSHR, OP_LSHR_IMM, OP_LSHR_UN, OP_LSHR_UN_IMM */
 #define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
 
@@ -255,6 +268,8 @@ typedef struct {
 
 #define MONO_ARCH_GSHARED_SUPPORTED 1
 #define MONO_ARCH_HAVE_LLVM_IMT_TRAMPOLINE 1
+#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
+#define MONO_ARCH_HAVE_FIND_JIT_INFO_EXT 1
 
 /* Used for optimization, not complete */
 #define MONO_ARCH_IS_OP_MEMBASE(opcode) ((opcode) == OP_X86_PUSH_MEMBASE)
index 2ad9acb1a5f96dc480003ee88c02c7be3413d88c..2affcbb8600e29116ce3dae4bf9c383388401360 100644 (file)
@@ -65,6 +65,7 @@
 
 #include "debug-mini.h"
 #include "mini-gc.h"
+#include "debugger-agent.h"
 
 static gpointer mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, MonoException **ex);
 
@@ -2193,6 +2194,16 @@ mono_get_lmf_addr (void)
 #endif
 }
 
+void
+mono_set_lmf (MonoLMF *lmf)
+{
+#if defined(HAVE_KW_THREAD) && defined(MONO_ARCH_ENABLE_MONO_LMF_VAR)
+       mono_lmf = lmf;
+#endif
+
+       (*mono_get_lmf_addr ()) = lmf;
+}
+
 /* Called by native->managed wrappers */
 void
 mono_jit_thread_attach (MonoDomain *domain)
@@ -2489,6 +2500,7 @@ mono_patch_info_hash (gconstpointer data)
        case MONO_PATCH_INFO_JIT_ICALL_ADDR:
        case MONO_PATCH_INFO_FIELD:
        case MONO_PATCH_INFO_SFLDA:
+       case MONO_PATCH_INFO_SEQ_POINT_INFO:
                return (ji->type << 8) | (gssize)ji->data.target;
        default:
                return (ji->type << 8);
@@ -2533,7 +2545,6 @@ mono_patch_info_equal (gconstpointer ka, gconstpointer kb)
 
                return e1->method == e2->method && e1->in_mrgctx == e2->in_mrgctx && e1->info_type == e2->info_type && mono_patch_info_equal (e1->data, e2->data);
        }
-
        default:
                if (ji1->data.target != ji2->data.target)
                        return 0;
@@ -2767,6 +2778,15 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code,
        case MONO_PATCH_INFO_MONITOR_EXIT:
                target = mono_create_monitor_exit_trampoline ();
                break;
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       case MONO_PATCH_INFO_SEQ_POINT_INFO:
+               if (!run_cctors)
+                       /* AOT, not needed */
+                       target = NULL;
+               else
+                       target = mono_arch_get_seq_point_info (domain, code);
+               break;
+#endif
        default:
                g_assert_not_reached ();
        }
@@ -3189,6 +3209,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        cfg->compile_aot = compile_aot;
        cfg->skip_visibility = method->skip_visibility;
        cfg->orig_method = method;
+       cfg->gen_seq_points = debug_options.gen_seq_points;
        if (try_generic_shared)
                cfg->generic_sharing_context = (MonoGenericSharingContext*)&cfg->generic_sharing_context;
        cfg->compile_llvm = try_llvm;
@@ -3199,6 +3220,16 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
                return cfg;
        }
 
+       if (cfg->generic_sharing_context) {
+               MonoGenericContext object_context = mono_method_construct_object_context (method_to_compile);
+
+               method_to_register = mono_class_inflate_generic_method (method_to_compile, &object_context);
+       } else {
+               g_assert (method == method_to_compile);
+               method_to_register = method;
+       }
+       cfg->method_to_register = method_to_register;
+
        /* No way to obtain the location info for 'this' */
        if (try_generic_shared) {
                cfg->exception_message = g_strdup ("gshared");
@@ -3220,6 +3251,8 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
                 */
                cfg->disable_initlocals_opt = TRUE;
 
+               cfg->extend_live_ranges = TRUE;
+
                /* Temporarily disable this when running in the debugger until we have support
                 * for this in the debugger. */
                cfg->disable_omit_fp = TRUE;
@@ -3266,8 +3299,6 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
                }
                if (MONO_PROBE_METHOD_COMPILE_END_ENABLED ())
                        MONO_PROBE_METHOD_COMPILE_END (method, FALSE);
-               if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
-                       mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED);
                return cfg;
        }
 
@@ -3379,8 +3410,6 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
 
                if (MONO_PROBE_METHOD_COMPILE_END_ENABLED ())
                        MONO_PROBE_METHOD_COMPILE_END (method, FALSE);
-               if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
-                       mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED);
                /* cfg contains the details of the failure, so let the caller cleanup */
                return cfg;
        }
@@ -3785,15 +3814,6 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
                                generic_info_size);
        }
 
-       if (cfg->generic_sharing_context) {
-               MonoGenericContext object_context = mono_method_construct_object_context (method_to_compile);
-
-               method_to_register = mono_class_inflate_generic_method (method_to_compile, &object_context);
-       } else {
-               g_assert (method == method_to_compile);
-               method_to_register = method;
-       }
-
        jinfo->method = method_to_register;
        jinfo->code_start = cfg->native_code;
        jinfo->code_size = cfg->code_len;
@@ -3916,6 +3936,13 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        mono_save_xdebug_info (cfg);
 
        mini_gc_create_gc_map (cfg);
+       if (cfg->seq_points) {
+               // FIXME: dynamic methods
+               mono_domain_lock (domain);
+               g_hash_table_insert (domain_jit_info (domain)->seq_points, method_to_register, cfg->seq_points);
+               mono_domain_unlock (domain);
+       }
 
        if (!cfg->compile_aot) {
                mono_domain_lock (cfg->domain);
@@ -3946,8 +3973,6 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
 
        if (MONO_PROBE_METHOD_COMPILE_END_ENABLED ())
                MONO_PROBE_METHOD_COMPILE_END (method, TRUE);
-       if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
-               mono_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK);
 
        return cfg;
 }
@@ -4009,12 +4034,13 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in
 {
        MonoCompile *cfg;
        gpointer code = NULL;
-       MonoJitInfo *info;
+       MonoJitInfo *jinfo, *info;
        MonoVTable *vtable;
        MonoException *ex = NULL;
+       guint32 prof_options;
 
 #ifdef MONO_USE_AOT_COMPILER
-       if ((opt & MONO_OPT_AOT) && !(mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION)) {
+       if (opt & MONO_OPT_AOT) {
                MonoDomain *domain = mono_domain_get ();
 
                mono_class_init (method->klass);
@@ -4023,6 +4049,7 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in
                        vtable = mono_class_vtable (domain, method->klass);
                        g_assert (vtable);
                        mono_runtime_class_init (vtable);
+
                        return code;
                }
        }
@@ -4172,6 +4199,10 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in
        if (ex) {
                mono_destroy_compile (cfg);
                *jit_ex = ex;
+
+               if (cfg->prof_options & MONO_PROFILE_JIT_COMPILATION)
+                       mono_profiler_method_end_jit (method, NULL, MONO_PROFILE_FAILED);
+
                return NULL;
        }
 
@@ -4203,6 +4234,10 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in
                mono_domain_jit_code_hash_unlock (target_domain);
        }
 
+       jinfo = cfg->jit_info;
+
+       prof_options = cfg->prof_options;
+
        mono_destroy_compile (cfg);
 
        if (domain_jit_info (target_domain)->jump_target_hash) {
@@ -4231,6 +4266,10 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in
                g_assert (exc);
                mono_raise_exception (exc);
        }
+
+       if (prof_options & MONO_PROFILE_JIT_COMPILATION)
+               mono_profiler_method_end_jit (method, jinfo, MONO_PROFILE_OK);
+
        mono_runtime_class_init (vtable);
        return code;
 }
@@ -4681,6 +4720,16 @@ SIG_HANDLER_SIGNATURE (mono_sigsegv_signal_handler)
 
        GET_CONTEXT;
 
+#ifdef MONO_ARCH_SOFT_DEBUG_SUPPORTED
+       if (mono_arch_is_single_step_event (info, ctx)) {
+               mono_debugger_agent_single_step_event (ctx);
+               return;
+       } else if (mono_arch_is_breakpoint_event (info, ctx)) {
+               mono_debugger_agent_breakpoint_hit (ctx);
+               return;
+       }
+#endif
+
        /* The thread might no be registered with the runtime */
        if (!mono_domain_get () || !jit_tls) {
                if (mono_chain_signal (SIG_HANDLER_PARAMS))
@@ -4887,6 +4936,8 @@ mini_create_jit_domain_info (MonoDomain *domain)
        info->static_rgctx_trampoline_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
        info->llvm_vcall_trampoline_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
        info->runtime_invoke_hash = g_hash_table_new_full (mono_aligned_addr_hash, NULL, NULL, runtime_invoke_info_free);
+       info->seq_points = g_hash_table_new (mono_aligned_addr_hash, NULL);
+       info->arch_seq_points = g_hash_table_new (mono_aligned_addr_hash, NULL);
 
        domain->runtime_info = info;
 }
@@ -4944,6 +4995,9 @@ mini_free_jit_domain_info (MonoDomain *domain)
        g_hash_table_destroy (info->llvm_vcall_trampoline_hash);
        g_hash_table_destroy (info->runtime_invoke_hash);
 
+       if (info->agent_info)
+               mono_debugger_agent_free_domain_info (domain);
+
        g_free (domain->runtime_info);
        domain->runtime_info = NULL;
 }
@@ -5031,6 +5085,8 @@ mini_init (const char *filename, const char *runtime_version)
        if (default_opt & MONO_OPT_AOT)
                mono_aot_init ();
 
+       mono_debugger_agent_init ();
+
 #ifdef MONO_ARCH_GSHARED_SUPPORTED
        mono_set_generic_sharing_supported (TRUE);
 #endif
@@ -5496,6 +5552,12 @@ mono_set_defaults (int verbose_level, guint32 opts)
        default_opt_set = TRUE;
 }
 
+void
+mono_disable_optimizations (guint32 opts)
+{
+       default_opt &= ~opts;
+}
+
 /*
  * mono_get_runtime_build_info:
  *
index 6cf372822bafd974593f914c114daccb774cf7e7..e3f06bdb1f3e1d66890fe77217b9b33fc3a3d785 100644 (file)
@@ -149,6 +149,12 @@ typedef struct
        GHashTable *method_code_hash;
        /* Maps methods to a RuntimeInvokeInfo structure */
        GHashTable *runtime_invoke_hash;
+       /* Maps MonoMethod to a GPtrArray containing sequence point locations */
+       GHashTable *seq_points;
+       /* Debugger agent data */
+       gpointer agent_info;
+       /* Maps MonoMethod to an arch-specific structure */
+       GHashTable *arch_seq_points;
 } MonoJitDomainInfo;
 
 typedef struct {
@@ -163,6 +169,42 @@ typedef struct {
        int dummy;
 } MonoDynCallInfo;
 
+/*
+ * Possible frame types returned by the stack walker.
+ */
+typedef enum {
+       /* Normal managed frames */
+       FRAME_TYPE_MANAGED = 0,
+       /* Pseudo frame marking the start of a method invocation done by the soft debugger */
+       FRAME_TYPE_DEBUGGER_INVOKE = 1,
+       /* Frame for transitioning to native code */
+       FRAME_TYPE_MANAGED_TO_NATIVE = 2,
+       FRAME_TYPE_SENTINEL = 3
+} StackFrameType;
+
+/*
+ * Information about a stack frame
+ */
+typedef struct {
+       StackFrameType type;
+       /* 
+        * For FRAME_TYPE_MANAGED.
+        * For FRAME_TYPE_MANAGED_TO_NATIVE, the ji for the method which transitioned to
+        * native code, if there is one, else NULL.
+        */
+       MonoJitInfo *ji;
+       /*
+        * For FRAME_TYPE_MANAGED_TO_NATIVE, it is either the method which transitioned 
+        * to native code, or the method which was JITted.
+        */
+       MonoMethod *method;
+       /* The domain containing the code executed by this frame */
+       MonoDomain *domain;
+       gboolean managed;
+       int native_offset;
+       int il_offset;
+} StackFrameInfo;
+
 #if 0
 #define mono_bitset_foreach_bit(set,b,n) \
        for (b = 0; b < n; b++)\
@@ -617,6 +659,7 @@ enum {
        /* temp local created by a DUP: used only within a BB */
        MONO_INST_IS_TEMP    = 1,
        MONO_INST_INIT       = 1, /* in localloc */
+       MONO_INST_SINGLE_STEP_LOC = 1, /* in SEQ_POINT */
        MONO_INST_IS_DEAD    = 2,
        MONO_INST_TAILCALL   = 4,
        MONO_INST_VOLATILE   = 4,
@@ -627,7 +670,7 @@ enum {
        MONO_INST_DEFINITION_HAS_SIDE_EFFECTS = 8,
        /* the address of the variable has been taken */
        MONO_INST_INDIRECT   = 16,
-       MONO_INST_NORANGECHECK   = 16
+       MONO_INST_NORANGECHECK   = 16,
 };
 
 #define inst_c0 data.op[0].const_val
@@ -902,6 +945,7 @@ typedef struct {
        MonoInst        **args;
        MonoType        **arg_types;
        MonoMethod      *current_method; /* The method currently processed by method_to_ir () */
+       MonoMethod      *method_to_register; /* The method to register in JIT info tables */
        MonoGenericContext *generic_context;
 
        /* 
@@ -980,6 +1024,7 @@ typedef struct {
        guint            uses_vtable_reg : 1;
        guint            uses_simd_intrinsics : 1;
        guint            keep_cil_nops : 1;
+       guint            gen_seq_points : 1;
        gpointer         debug_info;
        guint32          lmf_offset;
     guint16          *intvars;
@@ -1036,6 +1081,14 @@ typedef struct {
        /* Used to implement dyn_call */
        MonoInst *dyn_call_var;
 
+       /*
+        * List of sequence points represented as IL offset+native offset pairs.
+        * Allocated using glib.
+        * IL offset can be -1 or 0xffffff to refer to the sequence points
+        * inside the prolog and epilog used to implement method entry/exit events.
+        */
+       GPtrArray *seq_points;
+
        /* Used by AOT */
        guint32 got_offset;
 } MonoCompile;
@@ -1241,6 +1294,7 @@ typedef struct {
        gboolean suspend_on_sigsegv;
        gboolean dyn_runtime_invoke;
        gboolean gdb;
+       gboolean gen_seq_points;
 } MonoDebugOptions;
 
 enum {
@@ -1308,6 +1362,7 @@ MonoDebugOptions *mini_get_debug_options   (void) MONO_INTERNAL;
 char*       mono_get_runtime_build_info    (void) MONO_INTERNAL;
 
 /* helper methods */
+void      mono_disable_optimizations       (guint32 opts) MONO_INTERNAL;
 MonoJumpInfoToken* mono_jump_info_token_new (MonoMemPool *mp, MonoImage *image, guint32 token) MONO_INTERNAL;
 MonoJumpInfoToken* mono_jump_info_token_new2 (MonoMemPool *mp, MonoImage *image, guint32 token, MonoGenericContext *context) MONO_INTERNAL;
 MonoInst* mono_find_spvar_for_region        (MonoCompile *cfg, int region) MONO_INTERNAL;
@@ -1371,6 +1426,7 @@ gpointer  mono_jit_find_compiled_method     (MonoDomain *domain, MonoMethod *met
 gpointer  mono_jit_compile_method           (MonoMethod *method) MONO_INTERNAL;
 MonoLMF * mono_get_lmf                      (void) MONO_INTERNAL;
 MonoLMF** mono_get_lmf_addr                 (void) MONO_INTERNAL;
+void      mono_set_lmf                      (MonoLMF *lmf) MONO_INTERNAL;
 void      mono_jit_thread_attach            (MonoDomain *domain);
 guint32   mono_get_jit_tls_key              (void) MONO_INTERNAL;
 gint32    mono_get_jit_tls_offset           (void) MONO_INTERNAL;
@@ -1604,6 +1660,19 @@ LLVMCallInfo* mono_arch_get_llvm_call_info      (MonoCompile *cfg, MonoMethodSig
 guint8*   mono_arch_emit_load_got_addr          (guint8 *start, guint8 *code, MonoCompile *cfg, MonoJumpInfo **ji) MONO_INTERNAL;
 guint8*   mono_arch_emit_load_aotconst (guint8 *start, guint8 *code, MonoJumpInfo **ji, int tramp_type, gconstpointer target) MONO_INTERNAL;
 
+/* Soft Debug support */
+void      mono_arch_set_breakpoint              (MonoJitInfo *ji, guint8 *ip) MONO_INTERNAL;
+void      mono_arch_clear_breakpoint            (MonoJitInfo *ji, guint8 *ip) MONO_INTERNAL;
+void      mono_arch_start_single_stepping       (void) MONO_INTERNAL;
+void      mono_arch_stop_single_stepping        (void) MONO_INTERNAL;
+gboolean  mono_arch_is_single_step_event        (siginfo_t *info, void *sigctx) MONO_INTERNAL;
+gboolean  mono_arch_is_breakpoint_event (siginfo_t *info, void *sigctx) MONO_INTERNAL;
+guint8*   mono_arch_get_ip_for_single_step      (MonoJitInfo *ji, MonoContext *ctx) MONO_INTERNAL;
+guint8*   mono_arch_get_ip_for_breakpoint       (MonoJitInfo *ji, MonoContext *ctx) MONO_INTERNAL;
+void     mono_arch_skip_breakpoint              (MonoContext *ctx) MONO_INTERNAL;
+void     mono_arch_skip_single_step             (MonoContext *ctx) MONO_INTERNAL;
+gpointer mono_arch_get_seq_point_info           (MonoDomain *domain, guint8 *code) MONO_INTERNAL;
+
 MonoJitInfo *mono_arch_find_jit_info            (MonoDomain *domain, 
                                                 MonoJitTlsData *jit_tls, 
                                                 MonoJitInfo *res, 
@@ -1612,6 +1681,11 @@ MonoJitInfo *mono_arch_find_jit_info            (MonoDomain *domain,
                                                 MonoContext *new_ctx, 
                                                 MonoLMF **lmf, 
                                                 gboolean *managed) MONO_INTERNAL;
+gboolean
+mono_arch_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
+                                                        MonoJitInfo *ji, MonoContext *ctx, 
+                                                        MonoContext *new_ctx, MonoLMF **lmf, 
+                                                        StackFrameInfo *frame_info) MONO_INTERNAL;
 gpointer mono_arch_get_call_filter              (void) MONO_INTERNAL;
 gpointer mono_arch_get_restore_context          (void) MONO_INTERNAL;
 gpointer mono_arch_get_call_filter_full         (guint32 *code_size, MonoJumpInfo **ji, gboolean aot) MONO_INTERNAL;
@@ -1658,6 +1732,10 @@ gpointer    mono_arch_build_imt_thunk           (MonoVTable *vtable, MonoDomain
 void    mono_arch_notify_pending_exc            (void) MONO_INTERNAL;
 
 /* Exception handling */
+
+/* Same as MonoStackWalk, but pass the context/frame type as well */
+typedef gboolean (*MonoJitStackWalk)            (StackFrameInfo *frame, MonoContext *ctx, gpointer data);
+
 void     mono_exceptions_init                   (void) MONO_INTERNAL;
 gboolean mono_handle_exception                  (MonoContext *ctx, gpointer obj,
                                                 gpointer original_ip, gboolean test_only) MONO_INTERNAL;
@@ -1665,10 +1743,11 @@ void     mono_handle_native_sigsegv             (int signal, void *sigctx) MONO_
 void     mono_print_thread_dump                 (void *sigctx);
 void     mono_jit_walk_stack                    (MonoStackWalk func, gboolean do_il_offset, gpointer user_data) MONO_INTERNAL;
 void     mono_jit_walk_stack_from_ctx           (MonoStackWalk func, MonoContext *ctx, gboolean do_il_offset, gpointer user_data) MONO_INTERNAL;
+void     mono_jit_walk_stack_from_ctx_in_thread (MonoJitStackWalk func, MonoDomain *domain, MonoContext *start_ctx, gboolean do_il_offset, MonoInternalThread *thread, MonoLMF *lmf, gpointer user_data) MONO_INTERNAL;
 void     mono_setup_altstack                    (MonoJitTlsData *tls) MONO_INTERNAL;
 void     mono_free_altstack                     (MonoJitTlsData *tls) MONO_INTERNAL;
 gpointer mono_altstack_restore_prot             (mgreg_t *regs, guint8 *code, gpointer *tramp_data, guint8* tramp) MONO_INTERNAL;
-MonoJitInfo* mini_jit_info_table_find (MonoDomain *domain, char *addr, MonoDomain **out_domain) MONO_INTERNAL;
+MonoJitInfo* mini_jit_info_table_find           (MonoDomain *domain, char *addr, MonoDomain **out_domain) MONO_INTERNAL;
 
 MonoJitInfo * mono_find_jit_info                (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, MonoJitInfo *prev_ji, MonoContext *ctx, MonoContext *new_ctx, char **trace, MonoLMF **lmf, int *native_offset, gboolean *managed) MONO_INTERNAL;
 
index 88a9a2bb9186db9aedf15bfdfd1595d1343433a4..8e5de76f70790e4af774dfed2d1470c492a01b08 100644 (file)
@@ -24,4 +24,3 @@ OPTFLAG(TREEPROP ,22, "treeprop",   "Tree propagation")
 OPTFLAG(SSE2     ,23, "sse2",       "SSE2 instructions on x86")
 OPTFLAG(GSHARED  ,24, "gshared",    "Share generics")
 OPTFLAG(SIMD    ,25, "simd",       "Simd intrinsics")
-
index 390bee4a6aa9a39c48b4087f9036fa5ecea8ff34..0493efc8e8e8ad83d4d09d6225d17e06c0d8572a 100644 (file)
@@ -38,4 +38,5 @@ PATCH_INFO(GENERIC_CLASS_INIT, "generic_class_init")
 PATCH_INFO(MONITOR_ENTER, "monitor_enter")
 PATCH_INFO(MONITOR_EXIT, "monitor_exit")
 PATCH_INFO(MSCORLIB_GOT_ADDR, "mscorlib_got_addr")
+PATCH_INFO(SEQ_POINT_INFO, "seq_point_info")
 PATCH_INFO(NONE, "none")