Browse Source

Mono: Some StackTrace to StackInfo[] fixes
- Sometimes `StackFrame.GetMethod()` returns null (e.g.: latest frame of a `MissingMethodException`). Still not sure what to do with that frame (maybe skip it), but at least it no longer fails.
- Skip `CSharpLanguage::debug_get_current_stack_info()` if an error is printed from `GDMonoUtils::update_corlib_cache()`.
- Fix crash when calling `GDMonoUtils::print_unhandled_exception(exc)` if there is no ScriptDebugger attached.

Ignacio Etcheverry 7 years ago
parent
commit
bff9627dc4

+ 5 - 1
modules/mono/csharp_script.cpp

@@ -449,7 +449,7 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::debug_get_current_stack_info()
 
 	// Printing an error here will result in endless recursion, so we must be careful
 
-	if (!gdmono->is_runtime_initialized() && GDMono::get_singleton()->get_api_assembly())
+	if (!gdmono->is_runtime_initialized() || !GDMono::get_singleton()->get_api_assembly() || !GDMonoUtils::mono_cache.corlib_cache_updated)
 		return Vector<StackInfo>();
 
 	MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
@@ -503,6 +503,10 @@ Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObjec
 			return Vector<StackInfo>();
 		}
 
+		// TODO
+		// what if the StackFrame method is null (method_decl is empty). should we skip this frame?
+		// can reproduce with a MissingMethodException on internal calls
+
 		sif.file = GDMonoMarshal::mono_string_to_godot(file_name);
 		sif.line = file_line_num;
 		sif.func = GDMonoMarshal::mono_string_to_godot(method_decl);

+ 6 - 0
modules/mono/glue/cs_files/DebuggingUtils.cs

@@ -26,6 +26,12 @@ namespace Godot
 
             MethodBase methodBase = frame.GetMethod();
 
+            if (methodBase == null)
+            {
+                methodDecl = string.Empty;
+                return;
+            }
+
             StringBuilder sb = new StringBuilder();
 
             if (methodBase is MethodInfo methodInfo)

+ 20 - 4
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -44,10 +44,13 @@ namespace GDMonoUtils {
 
 MonoCache mono_cache;
 
-#define CACHE_AND_CHECK(m_var, m_val)                                                        \
-	{                                                                                        \
-		m_var = m_val;                                                                       \
-		if (!m_var) ERR_PRINT("Mono Cache: Member " #m_var " is null. This is really bad!"); \
+#define CACHE_AND_CHECK(m_var, m_val)                             \
+	{                                                             \
+		m_var = m_val;                                            \
+		if (!m_var) {                                             \
+			ERR_EXPLAIN("Mono Cache: Member " #m_var " is null"); \
+			ERR_FAIL();                                           \
+		}                                                         \
 	}
 
 #define CACHE_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_class, m_val)
@@ -133,6 +136,12 @@ void MonoCache::clear_members() {
 	task_scheduler_handle = Ref<MonoGCHandle>();
 }
 
+void MonoCache::cleanup() {
+
+	corlib_cache_updated = false;
+	godot_api_cache_updated = false;
+}
+
 #define GODOT_API_CLASS(m_class) (GDMono::get_singleton()->get_api_assembly()->get_class(BINDINGS_NAMESPACE, #m_class))
 
 void update_corlib_cache() {
@@ -158,6 +167,8 @@ void update_corlib_cache() {
 	CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(bool)", true));
 	CACHE_METHOD_AND_CHECK(System_Diagnostics_StackTrace, ctor_Exception_bool, CACHED_CLASS(System_Diagnostics_StackTrace)->get_method_with_desc("System.Diagnostics.StackTrace:.ctor(System.Exception,bool)", true));
 #endif
+
+	mono_cache.corlib_cache_updated = true;
 }
 
 void update_godot_api_cache() {
@@ -231,6 +242,8 @@ void update_godot_api_cache() {
 	MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
 	mono_runtime_object_init(task_scheduler);
 	mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
+
+	mono_cache.corlib_cache_updated = true;
 }
 
 void clear_cache() {
@@ -402,6 +415,9 @@ void print_unhandled_exception(MonoObject *p_exc) {
 void print_unhandled_exception(MonoObject *p_exc, bool p_recursion_caution) {
 	mono_print_unhandled_exception(p_exc);
 #ifdef DEBUG_ENABLED
+	if (!ScriptDebugger::get_singleton())
+		return;
+
 	GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
 	MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
 

+ 7 - 1
modules/mono/mono_gd/gd_mono_utils.h

@@ -129,10 +129,16 @@ struct MonoCache {
 
 	Ref<MonoGCHandle> task_scheduler_handle;
 
+	bool corlib_cache_updated;
+	bool godot_api_cache_updated;
+
 	void clear_members();
-	void cleanup() {}
+	void cleanup();
 
 	MonoCache() {
+		corlib_cache_updated = false;
+		godot_api_cache_updated = false;
+
 		clear_members();
 	}
 };