gd_mono_utils.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*************************************************************************/
  2. /* gd_mono_utils.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "gd_mono_utils.h"
  31. #include <mono/metadata/debug-helpers.h>
  32. #include <mono/metadata/exception.h>
  33. #include "core/debugger/engine_debugger.h"
  34. #include "core/debugger/script_debugger.h"
  35. #include "core/io/dir_access.h"
  36. #include "core/object/ref_counted.h"
  37. #include "core/os/mutex.h"
  38. #include "core/os/os.h"
  39. #ifdef TOOLS_ENABLED
  40. #include "editor/debugger/editor_debugger_node.h"
  41. #endif
  42. #include "../csharp_script.h"
  43. #include "../utils/macros.h"
  44. #include "gd_mono.h"
  45. #include "gd_mono_cache.h"
  46. namespace GDMonoUtils {
  47. void set_main_thread(MonoThread *p_thread) {
  48. mono_thread_set_main(p_thread);
  49. }
  50. MonoThread *attach_current_thread() {
  51. ERR_FAIL_COND_V(!GDMono::get_singleton()->is_runtime_initialized(), nullptr);
  52. MonoDomain *scripts_domain = GDMono::get_singleton()->get_scripts_domain();
  53. #ifndef GD_MONO_SINGLE_APPDOMAIN
  54. MonoThread *mono_thread = mono_thread_attach(scripts_domain ? scripts_domain : mono_get_root_domain());
  55. #else
  56. // The scripts domain is the root domain
  57. MonoThread *mono_thread = mono_thread_attach(scripts_domain);
  58. #endif
  59. ERR_FAIL_NULL_V(mono_thread, nullptr);
  60. return mono_thread;
  61. }
  62. void detach_current_thread() {
  63. ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
  64. MonoThread *mono_thread = mono_thread_current();
  65. ERR_FAIL_NULL(mono_thread);
  66. mono_thread_detach(mono_thread);
  67. }
  68. void detach_current_thread(MonoThread *p_mono_thread) {
  69. ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
  70. ERR_FAIL_NULL(p_mono_thread);
  71. mono_thread_detach(p_mono_thread);
  72. }
  73. MonoThread *get_current_thread() {
  74. return mono_thread_current();
  75. }
  76. bool is_thread_attached() {
  77. return mono_domain_get() != nullptr;
  78. }
  79. MonoDomain *create_domain(const String &p_friendly_name) {
  80. print_verbose("Mono: Creating domain '" + p_friendly_name + "'...");
  81. MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), nullptr);
  82. if (domain) {
  83. // Workaround to avoid this exception:
  84. // System.Configuration.ConfigurationErrorsException: Error Initializing the configuration system.
  85. // ---> System.ArgumentException: The 'ExeConfigFilename' argument cannot be null.
  86. mono_domain_set_config(domain, ".", "");
  87. }
  88. return domain;
  89. }
  90. // TODO:
  91. // Implement all of the disabled exception logging below. Once we move to .NET 6.
  92. // It will have to be done from C# as UnmanagedCallersOnly doesn't allow throwing.
  93. #warning TODO
  94. #if 0
  95. String get_exception_name_and_message(MonoException *p_exc) {
  96. String res;
  97. MonoClass *klass = mono_object_get_class((MonoObject *)p_exc);
  98. MonoType *type = mono_class_get_type(klass);
  99. char *full_name = mono_type_full_name(type);
  100. res += full_name;
  101. mono_free(full_name);
  102. res += ": ";
  103. MonoProperty *prop = mono_class_get_property_from_name(klass, "Message");
  104. MonoString *msg = (MonoString *)property_get_value(prop, (MonoObject *)p_exc, nullptr, nullptr);
  105. res += GDMonoMarshal::mono_string_to_godot(msg);
  106. return res;
  107. }
  108. #endif
  109. void debug_print_unhandled_exception(MonoException *p_exc) {
  110. print_unhandled_exception(p_exc);
  111. debug_send_unhandled_exception_error(p_exc);
  112. }
  113. void debug_send_unhandled_exception_error(MonoException *p_exc) {
  114. #ifdef DEBUG_ENABLED
  115. if (!EngineDebugger::is_active()) {
  116. #ifdef TOOLS_ENABLED
  117. if (Engine::get_singleton()->is_editor_hint()) {
  118. #warning TODO
  119. #if 0
  120. ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_exc));
  121. #endif
  122. }
  123. #endif
  124. return;
  125. }
  126. static thread_local bool _recursion_flag_ = false;
  127. if (_recursion_flag_) {
  128. return;
  129. }
  130. _recursion_flag_ = true;
  131. SCOPE_EXIT { _recursion_flag_ = false; };
  132. ScriptLanguage::StackInfo separator;
  133. separator.file = String();
  134. separator.func = "--- " + RTR("End of inner exception stack trace") + " ---";
  135. separator.line = 0;
  136. Vector<ScriptLanguage::StackInfo> si;
  137. String exc_msg;
  138. #warning TODO
  139. #if 0
  140. while (p_exc != nullptr) {
  141. GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
  142. MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
  143. MonoBoolean need_file_info = true;
  144. void *ctor_args[2] = { p_exc, &need_file_info };
  145. MonoException *unexpected_exc = nullptr;
  146. CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
  147. if (unexpected_exc) {
  148. GDMonoInternals::unhandled_exception(unexpected_exc);
  149. return;
  150. }
  151. Vector<ScriptLanguage::StackInfo> _si;
  152. if (stack_trace != nullptr) {
  153. _si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
  154. for (int i = _si.size() - 1; i >= 0; i--) {
  155. si.insert(0, _si[i]);
  156. }
  157. }
  158. exc_msg += (exc_msg.length() > 0 ? " ---> " : "") + GDMonoUtils::get_exception_name_and_message(p_exc);
  159. GDMonoClass *exc_class = GDMono::get_singleton()->get_class(mono_get_exception_class());
  160. GDMonoProperty *inner_exc_prop = exc_class->get_property("InnerException");
  161. CRASH_COND(inner_exc_prop == nullptr);
  162. MonoObject *inner_exc = inner_exc_prop->get_value((MonoObject *)p_exc);
  163. if (inner_exc != nullptr) {
  164. si.insert(0, separator);
  165. }
  166. p_exc = (MonoException *)inner_exc;
  167. }
  168. #endif
  169. String file = si.size() ? si[0].file : __FILE__;
  170. String func = si.size() ? si[0].func : FUNCTION_STR;
  171. int line = si.size() ? si[0].line : __LINE__;
  172. String error_msg = "Unhandled exception";
  173. EngineDebugger::get_script_debugger()->send_error(func, file, line, error_msg, exc_msg, true, ERR_HANDLER_ERROR, si);
  174. #endif
  175. }
  176. void debug_unhandled_exception(MonoException *p_exc) {
  177. GDMonoInternals::unhandled_exception(p_exc); // prints the exception as well
  178. }
  179. void print_unhandled_exception(MonoException *p_exc) {
  180. mono_print_unhandled_exception((MonoObject *)p_exc);
  181. }
  182. void set_pending_exception(MonoException *p_exc) {
  183. #ifdef NO_PENDING_EXCEPTIONS
  184. debug_unhandled_exception(p_exc);
  185. #else
  186. if (get_runtime_invoke_count() == 0) {
  187. debug_unhandled_exception(p_exc);
  188. return;
  189. }
  190. if (!mono_runtime_set_pending_exception(p_exc, false)) {
  191. ERR_PRINT("Exception thrown from managed code, but it could not be set as pending:");
  192. GDMonoUtils::debug_print_unhandled_exception(p_exc);
  193. }
  194. #endif
  195. }
  196. thread_local int current_invoke_count = 0;
  197. ScopeThreadAttach::ScopeThreadAttach() {
  198. if (likely(GDMono::get_singleton()->is_runtime_initialized()) && unlikely(!mono_domain_get())) {
  199. mono_thread = GDMonoUtils::attach_current_thread();
  200. }
  201. }
  202. ScopeThreadAttach::~ScopeThreadAttach() {
  203. if (unlikely(mono_thread)) {
  204. GDMonoUtils::detach_current_thread(mono_thread);
  205. }
  206. }
  207. } // namespace GDMonoUtils