/*监控需要hook的函数*/ static void profile_jit_result (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result) { if (result == MONO_PROFILE_FAILED) return; if (mono_method_get_token (method) == 0) return; /*一般是动态生成的marshall*/ uint32_t iflags; int flag = mono_method_get_flags (method, &iflags); if (iflags != 0) return; /*iflags非0 一般是一些native和特殊的method实现*/ if (mono_jit_info_get_code_size (jinfo) < 4) return; /*代码段太小, 无法hook*/ void *p = mono_jit_info_get_code_start (jinfo); if (p == 0) { LOGD ("function code size is null"); return; } /*测试jit函数是否是mov r12, sp*/ if (*(uint32_t*)p != 0xE1A0C00D) LOGD ("exception func : %s , %p", mono_method_get_name (method), p); /*TODO : 增加可配置的image和函数列表*/ if (strcmp (get_method_image_name (method), "Assembly-CSharp") != 0) return; if (strcmp (mono_method_get_name (method), ".ctor") == 0) return; if (strcmp (mono_method_get_name (method), ".cctor") == 0) return; if (strcmp (mono_method_get_name (method), "set") == 0) return; if (strcmp (mono_method_get_name (method), "get") == 0) return; if (strcmp (mono_method_get_name (method), "Update") == 0) return; if (strcmp (mono_method_get_name (method), "LateUpdate") == 0) return; if (strcmp (mono_method_get_name (method), "OnGUI") == 0) return; /*TODO : 需要一个容器来存储还未编译, 但又想hook的函数*/ bool donthook = false; pthread_mutex_lock (&replace_mutex); if (replace_method_dict.find (method) != replace_method_dict.end ()) donthook = true; pthread_mutex_unlock (&replace_mutex); if (donthook) return; char *hook = specific_hook (p, method, (void*)func_trace); if (hook == 0) { /*将失败的hook也放到一个表里面*/ LOGD ("hook err : %s", mono_method_get_name (method)); return; } pthread_mutex_lock (&hooked_mutex); hooked_method_dict[method] = new HookInfo(jinfo, hook); pthread_mutex_unlock (&hooked_mutex); LOGD ("hook func : %s , %p", mono_method_get_name (method), p); return; }
GDMonoClassMember::Visibility GDMonoMethod::get_visibility() { switch (mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { case MONO_METHOD_ATTR_PRIVATE: return GDMonoClassMember::PRIVATE; case MONO_METHOD_ATTR_FAM_AND_ASSEM: return GDMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_METHOD_ATTR_ASSEM: return GDMonoClassMember::INTERNAL; case MONO_METHOD_ATTR_FAMILY: return GDMonoClassMember::PROTECTED; case MONO_METHOD_ATTR_PUBLIC: return GDMonoClassMember::PUBLIC; default: ERR_FAIL_V(GDMonoClassMember::PRIVATE); } }
MonoMemberVisibility MonoMethod::getVisibility() { uint32_t flags = mono_method_get_flags(mMethod, nullptr) & MONO_METHOD_ATTR_ACCESS_MASK; if (flags == MONO_METHOD_ATTR_PRIVATE) return MonoMemberVisibility::Private; else if (flags == MONO_METHOD_ATTR_FAM_AND_ASSEM) return MonoMemberVisibility::ProtectedInternal; else if (flags == MONO_METHOD_ATTR_ASSEM) return MonoMemberVisibility::Internal; else if (flags == MONO_METHOD_ATTR_FAMILY) return MonoMemberVisibility::Protected; else if (flags == MONO_METHOD_ATTR_PUBLIC) return MonoMemberVisibility::Public; assert(false); return MonoMemberVisibility::Private; }
GDMonoClassMember::Visibility GDMonoProperty::get_visibility() { MonoMethod *prop_method = mono_property_get_get_method(mono_property); if (prop_method == NULL) prop_method = mono_property_get_set_method(mono_property); switch (mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_ACCESS_MASK) { case MONO_METHOD_ATTR_PRIVATE: return GDMonoClassMember::PRIVATE; case MONO_METHOD_ATTR_FAM_AND_ASSEM: return GDMonoClassMember::PROTECTED_AND_INTERNAL; case MONO_METHOD_ATTR_ASSEM: return GDMonoClassMember::INTERNAL; case MONO_METHOD_ATTR_FAMILY: return GDMonoClassMember::PROTECTED; case MONO_METHOD_ATTR_PUBLIC: return GDMonoClassMember::PUBLIC; default: ERR_FAIL_V(GDMonoClassMember::PRIVATE); } }
bool GDMonoMethod::is_static() { return mono_method_get_flags(mono_method, NULL) & MONO_METHOD_ATTR_STATIC; }
bool GDMonoProperty::is_static() { MonoMethod *prop_method = mono_property_get_get_method(mono_property); if (prop_method == NULL) prop_method = mono_property_get_set_method(mono_property); return mono_method_get_flags(prop_method, NULL) & MONO_METHOD_ATTR_STATIC; }
void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base) { CRASH_COND(!CACHED_CLASS(GodotObject)->is_assignable_from(this)); if (methods_fetched) return; void *iter = NULL; MonoMethod *raw_method = NULL; while ((raw_method = mono_class_get_methods(get_mono_ptr(), &iter)) != NULL) { StringName name = mono_method_get_name(raw_method); // get_method implicitly fetches methods and adds them to this->methods GDMonoMethod *method = get_method(raw_method, name); ERR_CONTINUE(!method); if (method->get_name() != name) { #ifdef DEBUG_ENABLED String fullname = method->get_ret_type_full_name() + " " + name + "(" + method->get_signature_desc(true) + ")"; WARN_PRINTS("Method `" + fullname + "` is hidden by Godot API method. Should be `" + method->get_full_name_no_class() + "`. In class `" + namespace_name + "." + class_name + "`."); #endif continue; } #ifdef DEBUG_ENABLED // For debug builds, we also fetched from native base classes as well before if this is not a native base class. // This allows us to warn the user here if he is using snake_case by mistake. if (p_native_base != this) { GDMonoClass *native_top = p_native_base; while (native_top) { GDMonoMethod *m = native_top->get_method(name, method->get_parameters_count()); if (m && m->get_name() != name) { // found String fullname = m->get_ret_type_full_name() + " " + name + "(" + m->get_signature_desc(true) + ")"; WARN_PRINTS("Method `" + fullname + "` should be `" + m->get_full_name_no_class() + "`. In class `" + namespace_name + "." + class_name + "`."); break; } if (native_top == CACHED_CLASS(GodotObject)) break; native_top = native_top->get_parent_class(); } } #endif uint32_t flags = mono_method_get_flags(method->mono_method, NULL); if (!(flags & MONO_METHOD_ATTR_VIRTUAL)) continue; // Virtual method of Godot Object derived type, let's try to find GodotMethod attribute GDMonoClass *top = p_native_base; while (top) { GDMonoMethod *base_method = top->get_method(name, method->get_parameters_count()); if (base_method && base_method->has_attribute(CACHED_CLASS(GodotMethodAttribute))) { // Found base method with GodotMethod attribute. // We get the original API method name from this attribute. // This name must point to the virtual method. MonoObject *attr = base_method->get_attribute(CACHED_CLASS(GodotMethodAttribute)); StringName godot_method_name = CACHED_FIELD(GodotMethodAttribute, methodName)->get_string_value(attr); #ifdef DEBUG_ENABLED CRASH_COND(godot_method_name == StringName()); #endif MethodKey key = MethodKey(godot_method_name, method->get_parameters_count()); GDMonoMethod **existing_method = methods.getptr(key); if (existing_method) memdelete(*existing_method); // Must delete old one methods.set(key, method); break; } if (top == CACHED_CLASS(GodotObject)) break; top = top->get_parent_class(); } } methods_fetched = true; }
void virt_mono_throw_unhandled_exception (MonoObject *exc) { caddr_t err; char *message = (char *) ""; const char *name = (const char *) ""; MonoString *str; MonoMethod *method; MonoClass *klass; gboolean free_message = FALSE; gint i; if (mono_object_isinst (exc, mono_get_exception_class ())) { klass = mono_object_get_class (exc); method = NULL; while (klass && method == NULL) { gpointer m_iter = NULL; for (method = mono_class_get_methods (klass, &m_iter); method != NULL; method = mono_class_get_methods (klass, &m_iter)) { MonoMethodSignature *sig = mono_method_signature (method); guint32 flags = 0; const char *name = mono_method_get_name (method); mono_method_get_flags (method, &flags); if (!strcmp ("ToString", name) && sig->param_count == 0 #ifdef OLD_KIT_1_1_5 && (flags & METHOD_ATTRIBUTE_VIRTUAL) && (flags & METHOD_ATTRIBUTE_PUBLIC) #endif ) { break; } method = NULL; } if (method == NULL) klass = mono_class_get_parent (klass); } g_assert (method); str = (MonoString *) mono_runtime_invoke (method, exc, NULL, NULL); if (str) { message = mono_string_to_utf8 (str); free_message = TRUE; name = mono_class_get_name (klass); } } /* * g_printerr ("\nUnhandled Exception: %s.%s: %s\n", exc->vtable->klass->name_space, * exc->vtable->klass->name, message); */ g_printerr ("\nUnhandled Exception: %s\n", message); err = srv_make_new_error ("42000", "MN001", "Unhandled Mono Exception [%.200s]: %.200s", name, message); if (free_message) g_free (message); sqlr_resignal (err); }
static gboolean collect_coverage_for (MonoProfiler *prof, MonoMethod *method) { int i; char *classname; char *fqn; MonoMethodHeader *header; gboolean has_positive, found; guint32 iflags, flags, code_size; MonoClass *klass; MonoImage *image; flags = mono_method_get_flags (method, &iflags); if ((iflags & 0x1000 /*METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL*/) || (flags & 0x2000 /*METHOD_ATTRIBUTE_PINVOKE_IMPL*/)) return FALSE; //if (method->wrapper_type != MONO_WRAPPER_NONE) // return FALSE; klass = mono_method_get_class (method); image = mono_class_get_image (klass); /* Hacky way of determining the executing assembly */ if (! prof->outfile_name && (strcmp (mono_method_get_name (method), "Main") == 0)) { prof->outfile_name = g_strdup_printf ("%s.cov", mono_image_get_filename (image)); } /* Check filters */ if (prof->filters) { /* Check already filtered classes first */ if (g_hash_table_lookup (prof->filtered_classes, klass)) return FALSE; classname = mono_type_get_name (mono_class_get_type (klass)); fqn = g_strdup_printf ("[%s]%s", mono_image_get_name (image), classname); // Check positive filters first has_positive = FALSE; found = FALSE; for (i = 0; i < prof->filters->len; ++i) { char *filter = g_ptr_array_index (prof->filters_as_str, i); if (filter [0] == '+') { filter = &filter [1]; if (strstr (fqn, filter) != NULL) found = TRUE; has_positive = TRUE; } } if (has_positive && !found) return FALSE; for (i = 0; i < prof->filters->len; ++i) { // Is substring search suffices ??? // GPatternSpec *spec = g_ptr_array_index (filters, i); // if (g_pattern_match_string (spec, classname)) { char *filter = g_ptr_array_index (prof->filters_as_str, i); if (filter [0] == '+') continue; // Skip '-' filter = &filter [1]; if (strstr (fqn, filter) != NULL) { g_hash_table_insert (prof->filtered_classes, klass, klass); return FALSE; } } g_free (fqn); g_free (classname); } header = mono_method_get_header (method); mono_method_header_get_code (header, &code_size, NULL); if (code_size > 20000) { exit (1); g_warning ("Unable to instrument method %s:%s since it is too complex.", mono_class_get_name (klass), mono_method_get_name (method)); return FALSE; } g_hash_table_insert (prof->methods, method, method); g_hash_table_insert (prof->classes, klass, klass); g_hash_table_insert (prof->assemblies, mono_image_get_assembly (image), mono_image_get_assembly (image)); return TRUE; }