[sdb] Fix round tripping of vtypes with boxed fields. Fixes #12354.
[mono.git] / mono / mini / debugger-agent.c
index 42c415e0e1064e2243c5dce4fb2140f30c8c6ae7..ebf7735578575cfda26e0b514b6960322c38e463 100644 (file)
@@ -2207,7 +2207,17 @@ buffer_add_ptr_id (Buffer *buf, MonoDomain *domain, IdType type, gpointer 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);
+       MonoClass *klass;
+
+       klass = decode_ptr_id (buf, endbuf, limit, ID_TYPE, domain, err);
+       if (G_UNLIKELY (log_level >= 2) && klass) {
+               char *s;
+
+               s = mono_type_full_name (&klass->byval_arg);
+               DEBUG(2, fprintf (log_file, "[dbg]   recv class [%s]\n", s));
+               g_free (s);
+       }
+       return klass;
 }
 
 static inline MonoAssembly*
@@ -2250,12 +2260,29 @@ static inline void
 buffer_add_typeid (Buffer *buf, MonoDomain *domain, MonoClass *klass)
 {
        buffer_add_ptr_id (buf, domain, ID_TYPE, klass);
+       if (G_UNLIKELY (log_level >= 2) && klass) {
+               char *s;
+
+               s = mono_type_full_name (&klass->byval_arg);
+               if (GetCurrentThreadId () == debugger_thread_id)
+                       DEBUG(2, fprintf (log_file, "[dbg]   send class [%s]\n", s));
+               else
+                       DEBUG(2, fprintf (log_file, "[%p]   send class [%s]\n", (gpointer)GetCurrentThreadId (), s));
+               g_free (s);
+       }
 }
 
 static inline void
 buffer_add_methodid (Buffer *buf, MonoDomain *domain, MonoMethod *method)
 {
        buffer_add_ptr_id (buf, domain, ID_METHOD, method);
+       if (G_UNLIKELY (log_level >= 2) && method) {
+               char *s;
+
+               s = mono_method_full_name (method, 1);
+               DEBUG(2, fprintf (log_file, "[dbg]   send method [%s]\n", s));
+               g_free (s);
+       }
 }
 
 static inline void
@@ -4383,6 +4410,42 @@ clear_breakpoints_for_domain (MonoDomain *domain)
        mono_loader_unlock ();
 }
 
+/*
+ * ss_update:
+ *
+ * Return FALSE if single stepping needs to continue because we are at the same line.
+ */
+static gboolean
+ss_update (SingleStepReq *req, MonoJitInfo *ji, SeqPoint *sp)
+{
+       MonoDebugMethodInfo *minfo;
+       MonoDebugSourceLocation *loc = NULL;
+       gboolean hit = TRUE;
+
+       if (req->size != STEP_SIZE_LINE)
+               return TRUE;
+
+       /* Have to check whenever a different source line was reached */
+       minfo = mono_debug_lookup_method (ji->method);
+
+       if (minfo)
+               loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
+
+       if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line)) {
+               /* Have to continue single stepping */
+               DEBUG(1, fprintf (log_file, "[%p] Same source line, continuing single stepping.\n", (gpointer)GetCurrentThreadId ()));
+               hit = FALSE;
+       }
+                               
+       if (loc) {
+               ss_req->last_method = ji->method;
+               ss_req->last_line = loc->row;
+               mono_debug_free_source_location (loc);
+       }
+
+       return hit;
+}
+
 static gboolean
 breakpoint_matches_assembly (MonoBreakpoint *bp, MonoAssembly *assembly)
 {
@@ -4470,31 +4533,9 @@ process_breakpoint_inner (DebuggerTlsData *tls)
        for (i = 0; i < ss_reqs_orig->len; ++i) {
                EventRequest *req = g_ptr_array_index (ss_reqs_orig, i);
                SingleStepReq *ss_req = req->info;
-               gboolean hit = TRUE;
-
-               if (ss_req->size == STEP_SIZE_LINE) {
-                       /* Have to check whenever a different source line was reached */
-                       MonoDebugMethodInfo *minfo;
-                       MonoDebugSourceLocation *loc = NULL;
-
-                       minfo = mono_debug_lookup_method (ji->method);
-
-                       if (minfo)
-                               loc = mono_debug_symfile_lookup_location (minfo, sp->il_offset);
-
-                       if (!loc || (loc && ji->method == ss_req->last_method && loc->row == ss_req->last_line)) {
-                               /* Have to continue single stepping */
-                               DEBUG(1, fprintf (log_file, "[%p] Same source line, continuing single stepping.\n", (gpointer)GetCurrentThreadId ()));
-                               hit = FALSE;
-                       }
-                               
-                       if (loc) {
-                               ss_req->last_method = ji->method;
-                               ss_req->last_line = loc->row;
-                               mono_debug_free_source_location (loc);
-                       }
-               }
+               gboolean hit;
 
+               hit = ss_update (ss_req, ji, sp);
                if (hit)
                        g_ptr_array_add (ss_reqs, req);
 
@@ -4713,41 +4754,8 @@ process_single_step_inner (DebuggerTlsData *tls)
                return;
        il_offset = sp->il_offset;
 
-       // FIXME: No tests fail if this is disabled
-#if 0
-       if (ss_req->size == STEP_SIZE_LINE) {
-               // FIXME:
-               NOT_IMPLEMENTED;
-
-               /* 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);
-                       }
-               }
-       }
-#endif
+       if (!ss_update (ss_req, ji, sp))
+               return;
 
        /* Start single stepping again from the current sequence point */
        ss_start (ss_req, ji->method, sp, info, ctx, tls, FALSE);
@@ -5581,6 +5589,49 @@ obj_is_of_type (MonoObject *obj, MonoType *t)
 static ErrorCode
 decode_value (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit);
 
+static ErrorCode
+decode_vtype (MonoType *t, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
+{
+       gboolean is_enum;
+       MonoClass *klass;
+       MonoClassField *f;
+       int nfields;
+       gpointer iter = NULL;
+       MonoDomain *d;
+       int err;
+
+       is_enum = decode_byte (buf, &buf, limit);
+       /* 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 (t && klass != mono_class_from_mono_type (t)) {
+               char *name = mono_type_full_name (t);
+               char *name2 = mono_type_full_name (&klass->byval_arg);
+               DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got %s.\n", (gpointer)GetCurrentThreadId (), name, name2));
+               g_free (name);
+               g_free (name2);
+               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);
+       return 0;
+}
+
 static ErrorCode
 decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr, guint8 *buf, guint8 **endbuf, guint8 *limit)
 {
@@ -5653,38 +5704,11 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr,
                g_assert (type == MONO_TYPE_VALUETYPE);
                /* Fall through */
                handle_vtype:
-       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);
+       case MONO_TYPE_VALUETYPE:
+               err = decode_vtype (t, domain, addr,buf, &buf, limit);
                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;
-       }
        handle_ref:
        default:
                if (MONO_TYPE_IS_REFERENCE (t)) {
@@ -5709,7 +5733,44 @@ decode_value_internal (MonoType *t, int type, MonoDomain *domain, guint8 *addr,
                                mono_gc_wbarrier_generic_store (addr, obj);
                        } else if (type == VALUE_TYPE_ID_NULL) {
                                *(MonoObject**)addr = NULL;
+                       } else if (type == MONO_TYPE_VALUETYPE) {
+                               guint8 *buf2;
+                               gboolean is_enum;
+                               MonoClass *klass;
+                               MonoDomain *d;
+                               guint8 *vtype_buf;
+                               int vtype_buf_size;
+
+                               /* This can happen when round-tripping boxed vtypes */
+                               /*
+                                * Obtain vtype class.
+                                * Same as the beginning of the handle_vtype case above.
+                                */
+                               buf2 = buf;
+                               is_enum = decode_byte (buf, &buf, limit);
+                               if (is_enum)
+                                       return ERR_NOT_IMPLEMENTED;
+                               klass = decode_typeid (buf, &buf, limit, &d, &err);
+                               if (err)
+                                       return err;
+
+                               /* Decode the vtype into a temporary buffer, then box it. */
+                               vtype_buf_size = mono_class_value_size (klass, NULL);
+                               vtype_buf = g_malloc0 (vtype_buf_size);
+                               g_assert (vtype_buf);
+
+                               buf = buf2;
+                               err = decode_vtype (NULL, domain, vtype_buf, buf, &buf, limit);
+                               if (err) {
+                                       g_free (vtype_buf);
+                                       return err;
+                               }
+                               *(MonoObject**)addr = mono_value_box (d, klass, vtype_buf);
+                               g_free (vtype_buf);
                        } else {
+                               char *name = mono_type_full_name (t);
+                               DEBUG(1, fprintf (log_file, "[%p] Expected value of type %s, got 0x%0x.\n", (gpointer)GetCurrentThreadId (), name, type));
+                               g_free (name);
                                return ERR_INVALID_ARGUMENT;
                        }
                } else {
@@ -6101,8 +6162,10 @@ do_invoke_method (DebuggerTlsData *tls, Buffer *buf, InvokeData *invoke, guint8
        if (m->klass->valuetype && (m->flags & METHOD_ATTRIBUTE_STATIC)) {
                /* Should be null */
                int type = decode_byte (p, &p, end);
-               if (type != VALUE_TYPE_ID_NULL)
+               if (type != VALUE_TYPE_ID_NULL) {
+                       DEBUG (1, fprintf (log_file, "[%p] Error: Static vtype method invoked with this argument.\n", (gpointer)GetCurrentThreadId ()));
                        return ERR_INVALID_ARGUMENT;
+               }
                memset (this_buf, 0, mono_class_instance_size (m->klass));
        } else {
                err = decode_value (&m->klass->byval_arg, domain, this_buf, p, &p, end);
@@ -6346,7 +6409,7 @@ invoke_method (void)
                tls->resume_count -= invoke->suspend_count;
        }
 
-       DEBUG (1, fprintf (log_file, "[%p] Invoke finished, resume_count = %d.\n", (gpointer)GetCurrentThreadId (), tls->resume_count));
+       DEBUG (1, fprintf (log_file, "[%p] Invoke finished (%d), resume_count = %d.\n", (gpointer)GetCurrentThreadId (), err, tls->resume_count));
 
        /*
         * Take the loader lock to avoid race conditions with CMD_VM_ABORT_INVOKE:
@@ -7305,8 +7368,10 @@ buffer_add_cattrs (Buffer *buf, MonoDomain *domain, MonoImage *image, MonoClass
                        MonoArray *typed_args, *named_args;
                        MonoType *t;
                        CattrNamedArg *arginfo;
+                       MonoError error;
 
-                       mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo);
+                       mono_reflection_create_custom_attr_data_args (image, attr->ctor, attr->data, attr->data_size, &typed_args, &named_args, &arginfo, &error);
+                       g_assert (mono_error_ok (&error));
 
                        buffer_add_methodid (buf, domain, attr->ctor);
 
@@ -8744,41 +8809,188 @@ command_set_to_string (CommandSet command_set)
        }
 }
 
+static const char* vm_cmds_str [] = {
+       "VERSION",
+       "ALL_THREADS",
+       "SUSPEND",
+       "RESUME",
+       "EXIT",
+       "DISPOSE",
+       "INVOKE_METHOD",
+       "SET_PROTOCOL_VERSION",
+       "ABORT_INVOKE",
+       "SET_KEEPALIVE"
+       "GET_TYPES_FOR_SOURCE_FILE",
+       "GET_TYPES",
+       "INVOKE_METHODS"
+};
+
+static const char* thread_cmds_str[] = {
+       "GET_FRAME_INFO",
+       "GET_NAME",
+       "GET_STATE",
+       "GET_INFO",
+       "GET_ID",
+       "GET_TID"
+};
+
+static const char* event_cmds_str[] = {
+       "REQUEST_SET",
+       "REQUEST_CLEAR",
+       "REQUEST_CLEAR_ALL_BREAKPOINTS"
+};
+
+static const char* appdomain_cmds_str[] = {
+       "GET_ROOT_DOMAIN",
+       "GET_FRIENDLY_NAME",
+       "GET_ASSEMBLIES",
+       "GET_ENTRY_ASSEMBLY",
+       "CREATE_STRING",
+       "GET_CORLIB",
+       "CREATE_BOXED_VALUE"
+};
+
+static const char* assembly_cmds_str[] = {
+       "GET_LOCATION",
+       "GET_ENTRY_POINT",
+       "GET_MANIFEST_MODULE",
+       "GET_OBJECT",
+       "GET_TYPE",
+       "GET_NAME"
+};
+
+static const char* module_cmds_str[] = {
+       "GET_INFO",
+};
+
+static const char* method_cmds_str[] = {
+       "GET_NAME",
+       "GET_DECLARING_TYPE",
+       "GET_DEBUG_INFO",
+       "GET_PARAM_INFO",
+       "GET_LOCALS_INFO",
+       "GET_INFO",
+       "GET_BODY",
+       "RESOLVE_TOKEN",
+       "GET_CATTRS ",
+       "MAKE_GENERIC_METHOD"
+};
+
+static const char* type_cmds_str[] = {
+       "GET_INFO",
+       "GET_METHODS",
+       "GET_FIELDS",
+       "GET_VALUES",
+       "GET_OBJECT",
+       "GET_SOURCE_FILES",
+       "SET_VALUES",
+       "IS_ASSIGNABLE_FROM",
+       "GET_PROPERTIES ",
+       "GET_CATTRS",
+       "GET_FIELD_CATTRS",
+       "GET_PROPERTY_CATTRS",
+       "GET_SOURCE_FILES_2",
+       "GET_VALUES_2",
+       "GET_METHODS_BY_NAME_FLAGS",
+       "GET_INTERFACES",
+       "GET_INTERFACE_MAP",
+       "IS_INITIALIZED"
+};
+
+static const char* stack_frame_cmds_str[] = {
+       "GET_VALUES",
+       "GET_THIS",
+       "SET_VALUES"
+};
+
+static const char* array_cmds_str[] = {
+       "GET_LENGTH",
+       "GET_VALUES",
+       "SET_VALUES",
+};
+
+static const char* string_cmds_str[] = {
+       "GET_VALUE",
+       "GET_LENGTH",
+       "GET_CHARS"
+};
+
+static const char* object_cmds_str[] = {
+       "GET_TYPE",
+       "GET_VALUES",
+       "IS_COLLECTED",
+       "GET_ADDRESS",
+       "GET_DOMAIN",
+       "SET_VALUES",
+       "GET_INFO",
+};
+
 static const char*
 cmd_to_string (CommandSet set, int command)
 {
+       const char **cmds;
+       int cmds_len = 0;
+
        switch (set) {
-       case CMD_SET_VM: {
-               switch (command) {
-               case CMD_VM_VERSION:
-                       return "VERSION";
-               case CMD_VM_ALL_THREADS:
-                       return "ALL_THREADS";
-               case CMD_VM_SUSPEND:
-                       return "SUSPEND";
-               case CMD_VM_RESUME:
-                       return "RESUME";
-               case CMD_VM_EXIT:
-                       return "EXIT";
-               case CMD_VM_DISPOSE:
-                       return "DISPOSE";
-               case CMD_VM_INVOKE_METHOD:
-                       return "INVOKE_METHOD";
-               case CMD_VM_SET_PROTOCOL_VERSION:
-                       return "SET_PROTOCOL_VERSION";
-               case CMD_VM_ABORT_INVOKE:
-                       return "ABORT_INVOKE";
-               case CMD_VM_SET_KEEPALIVE:
-                       return "SET_KEEPALIVE";
-               default:
-                       break;
-               }
+       case CMD_SET_VM:
+               cmds = vm_cmds_str;
+               cmds_len = G_N_ELEMENTS (vm_cmds_str);
+               break;
+       case CMD_SET_OBJECT_REF:
+               cmds = object_cmds_str;
+               cmds_len = G_N_ELEMENTS (object_cmds_str);
+               break;
+       case CMD_SET_STRING_REF:
+               cmds = string_cmds_str;
+               cmds_len = G_N_ELEMENTS (string_cmds_str);
+               break;
+       case CMD_SET_THREAD:
+               cmds = thread_cmds_str;
+               cmds_len = G_N_ELEMENTS (thread_cmds_str);
+               break;
+       case CMD_SET_ARRAY_REF:
+               cmds = array_cmds_str;
+               cmds_len = G_N_ELEMENTS (array_cmds_str);
+               break;
+       case CMD_SET_EVENT_REQUEST:
+               cmds = event_cmds_str;
+               cmds_len = G_N_ELEMENTS (event_cmds_str);
+               break;
+       case CMD_SET_STACK_FRAME:
+               cmds = stack_frame_cmds_str;
+               cmds_len = G_N_ELEMENTS (stack_frame_cmds_str);
+               break;
+       case CMD_SET_APPDOMAIN:
+               cmds = appdomain_cmds_str;
+               cmds_len = G_N_ELEMENTS (appdomain_cmds_str);
+               break;
+       case CMD_SET_ASSEMBLY:
+               cmds = assembly_cmds_str;
+               cmds_len = G_N_ELEMENTS (assembly_cmds_str);
+               break;
+       case CMD_SET_METHOD:
+               cmds = method_cmds_str;
+               cmds_len = G_N_ELEMENTS (method_cmds_str);
+               break;
+       case CMD_SET_TYPE:
+               cmds = type_cmds_str;
+               cmds_len = G_N_ELEMENTS (type_cmds_str);
+               break;
+       case CMD_SET_MODULE:
+               cmds = module_cmds_str;
+               cmds_len = G_N_ELEMENTS (module_cmds_str);
+               break;
+       case CMD_SET_EVENT:
+               cmds = event_cmds_str;
+               cmds_len = G_N_ELEMENTS (event_cmds_str);
                break;
-       }
        default:
                break;
        }
-       return NULL;
+       if (command > 0 && command <= cmds_len)
+               return cmds [command - 1];
+       else
+               return NULL;
 }
 
 static gboolean
@@ -8878,7 +9090,7 @@ debugger_thread (void *arg)
                                cmd_str = cmd_num;
                        }
                        
-                       DEBUG (1, fprintf (log_file, "[dbg] Received command %s(%s), id=%d.\n", command_set_to_string (command_set), cmd_str, id));
+                       DEBUG (1, fprintf (log_file, "[dbg] Command %s(%s) [%d].\n", command_set_to_string (command_set), cmd_str, id));
                }
 
                data = g_malloc (len - HEADER_LENGTH);