Merge pull request #15463 from neikeq/the-stack-frame-madness

Mono: Implement stack info for errors and exceptions
This commit is contained in:
Rémi Verschelde 2018-01-09 19:44:10 +01:00 committed by GitHub
commit c037f6339f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 330 additions and 90 deletions

View file

@ -294,6 +294,11 @@ void ScriptDebuggerLocal::send_message(const String &p_message, const Array &p_a
print_line("MESSAGE: '" + p_message + "' - " + String(Variant(p_args)));
}
void ScriptDebuggerLocal::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
print_line("ERROR: '" + (p_descr.empty() ? p_err : p_descr) + "'");
}
ScriptDebuggerLocal::ScriptDebuggerLocal() {
profiling = false;

View file

@ -44,6 +44,7 @@ class ScriptDebuggerLocal : public ScriptDebugger {
public:
void debug(ScriptLanguage *p_script, bool p_can_continue);
virtual void send_message(const String &p_message, const Array &p_args);
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
virtual bool is_profiling() const { return profiling; }
virtual void add_profiling_frame_data(const StringName &p_name, const Array &p_data) {}

View file

@ -432,22 +432,6 @@ void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char
if (p_type == ERR_HANDLER_SCRIPT)
return; //ignore script errors, those go through debugger
ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
OutputError oe;
oe.error = p_err;
oe.error_descr = p_descr;
oe.source_file = p_file;
oe.source_line = p_line;
oe.source_func = p_func;
oe.warning = p_type == ERR_HANDLER_WARNING;
uint64_t time = OS::get_singleton()->get_ticks_msec();
oe.hr = time / 3600000;
oe.min = (time / 60000) % 60;
oe.sec = (time / 1000) % 60;
oe.msec = time % 1000;
Array cstack;
Vector<ScriptLanguage::StackInfo> si;
for (int i = 0; i < ScriptServer::get_language_count(); i++) {
@ -456,32 +440,8 @@ void ScriptDebuggerRemote::_err_handler(void *ud, const char *p_func, const char
break;
}
cstack.resize(si.size() * 2);
for (int i = 0; i < si.size(); i++) {
String path;
int line = 0;
if (si[i].script.is_valid()) {
path = si[i].script->get_path();
line = si[i].line;
}
cstack[i * 2 + 0] = path;
cstack[i * 2 + 1] = line;
}
oe.callstack = cstack;
sdr->mutex->lock();
if (!sdr->locking && sdr->tcp_client->is_connected_to_host()) {
if (sdr->errors.size() >= sdr->max_errors_per_frame) {
sdr->n_errors_dropped++;
} else {
sdr->errors.push_back(oe);
}
}
sdr->mutex->unlock();
ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)ud;
sdr->send_error(p_func, p_file, p_line, p_err, p_descr, p_type, si);
}
bool ScriptDebuggerRemote::_parse_live_edit(const Array &p_command) {
@ -928,6 +888,45 @@ void ScriptDebuggerRemote::send_message(const String &p_message, const Array &p_
mutex->unlock();
}
void ScriptDebuggerRemote::send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) {
OutputError oe;
oe.error = p_err;
oe.error_descr = p_descr;
oe.source_file = p_file;
oe.source_line = p_line;
oe.source_func = p_func;
oe.warning = p_type == ERR_HANDLER_WARNING;
uint64_t time = OS::get_singleton()->get_ticks_msec();
oe.hr = time / 3600000;
oe.min = (time / 60000) % 60;
oe.sec = (time / 1000) % 60;
oe.msec = time % 1000;
Array cstack;
cstack.resize(p_stack_info.size() * 3);
for (int i = 0; i < p_stack_info.size(); i++) {
cstack[i * 3 + 0] = p_stack_info[i].file;
cstack[i * 3 + 1] = p_stack_info[i].func;
cstack[i * 3 + 2] = p_stack_info[i].line;
}
oe.callstack = cstack;
mutex->lock();
if (!locking && tcp_client->is_connected_to_host()) {
if (errors.size() >= max_errors_per_frame) {
n_errors_dropped++;
} else {
errors.push_back(oe);
}
}
mutex->unlock();
}
void ScriptDebuggerRemote::_print_handler(void *p_this, const String &p_string, bool p_error) {
ScriptDebuggerRemote *sdr = (ScriptDebuggerRemote *)p_this;

View file

@ -157,6 +157,7 @@ public:
virtual void request_quit();
virtual void send_message(const String &p_message, const Array &p_args);
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info);
virtual void set_request_scene_tree_message_func(RequestSceneTreeMessageFunc p_func, void *p_udata);
virtual void set_live_edit_funcs(LiveEditFuncs *p_funcs);

View file

@ -254,7 +254,8 @@ public:
virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1) = 0;
struct StackInfo {
Ref<Script> script;
String file;
String func;
int line;
};
@ -391,6 +392,7 @@ public:
ScriptLanguage *get_break_language() const;
virtual void send_message(const String &p_message, const Array &p_args) = 0;
virtual void send_error(const String &p_func, const String &p_file, int p_line, const String &p_err, const String &p_descr, ErrorHandlerType p_type, const Vector<ScriptLanguage::StackInfo> &p_stack_info) = 0;
virtual bool is_remote() const { return false; }
virtual void request_quit() {}

View file

@ -313,24 +313,22 @@ void ScriptEditor::_goto_script_line2(int p_line) {
void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
editor->push_item(p_script.ptr());
Ref<Script> script = Object::cast_to<Script>(*p_script);
if (!script.is_null() && script->get_path().is_resource_file()) {
if (edit(p_script, p_line, 0)) {
editor->push_item(p_script.ptr());
if (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) {
int selected = tab_container->get_current_tab();
if (selected < 0 || selected >= tab_container->get_child_count())
return;
Ref<Script> script = Object::cast_to<Script>(*p_script);
if (!script.is_null() && script->get_path().is_resource_file())
edit(p_script, p_line, 0);
ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
if (!current)
return;
current->goto_line(p_line, true);
}
}
int selected = tab_container->get_current_tab();
if (selected < 0 || selected >= tab_container->get_child_count())
return;
ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
if (!current)
return;
current->goto_line(p_line, true);
}
void ScriptEditor::_update_history_arrows() {

View file

@ -1616,30 +1616,33 @@ void ScriptEditorDebugger::_error_selected(int p_idx) {
error_stack->clear();
Array st = error_list->get_item_metadata(p_idx);
for (int i = 0; i < st.size(); i += 2) {
for (int i = 0; i < st.size(); i += 3) {
String script = st[i];
int line = st[i + 1];
String func = st[i + 1];
int line = st[i + 2];
Array md;
md.push_back(st[i]);
md.push_back(st[i + 1]);
md.push_back(st[i + 2]);
String str = script.get_file() + ":" + itos(line);
String str = func + " in " + script.get_file() + ":line " + itos(line);
error_stack->add_item(str);
error_stack->set_item_metadata(error_stack->get_item_count() - 1, md);
error_stack->set_item_tooltip(error_stack->get_item_count() - 1, TTR("File:") + " " + String(st[i]) + "\n" + TTR("Line:") + " " + itos(line));
error_stack->set_item_tooltip(error_stack->get_item_count() - 1,
TTR("File:") + " " + script + "\n" + TTR("Function:") + " " + func + "\n" + TTR("Line:") + " " + itos(line));
}
}
void ScriptEditorDebugger::_error_stack_selected(int p_idx) {
Array arr = error_stack->get_item_metadata(p_idx);
if (arr.size() != 2)
if (arr.size() != 3)
return;
Ref<Script> s = ResourceLoader::load(arr[0]);
emit_signal("goto_script_line", s, int(arr[1]) - 1);
emit_signal("goto_script_line", s, int(arr[2]) - 1);
}
void ScriptEditorDebugger::set_hide_on_stop(bool p_hide) {

View file

@ -349,7 +349,9 @@ public:
csi.resize(_debug_call_stack_pos);
for (int i = 0; i < _debug_call_stack_pos; i++) {
csi[_debug_call_stack_pos - i - 1].line = _call_stack[i].line ? *_call_stack[i].line : 0;
csi[_debug_call_stack_pos - i - 1].script = Ref<GDScript>(_call_stack[i].function->get_script());
if (_call_stack[i].function)
csi[_debug_call_stack_pos - i - 1].func = _call_stack[i].function->get_name();
csi[_debug_call_stack_pos - i - 1].file = _call_stack[i].function->get_script()->get_path();
}
return csi;
}

View file

@ -445,6 +445,72 @@ String CSharpLanguage::_get_indentation() const {
return "\t";
}
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_editor_tools_assembly())
return Vector<StackInfo>();
MonoObject *stack_trace = mono_object_new(mono_domain_get(), CACHED_CLASS(System_Diagnostics_StackTrace)->get_mono_ptr());
MonoBoolean need_file_info = true;
void *ctor_args[1] = { &need_file_info };
CACHED_METHOD(System_Diagnostics_StackTrace, ctor_bool)->invoke_raw(stack_trace, ctor_args);
Vector<StackInfo> si;
si = stack_trace_get_info(stack_trace);
return si;
}
Vector<ScriptLanguage::StackInfo> CSharpLanguage::stack_trace_get_info(MonoObject *p_stack_trace) {
// Printing an error here could result in endless recursion, so we must be careful
MonoObject *exc = NULL;
GDMonoUtils::StackTrace_GetFrames st_get_frames = CACHED_METHOD_THUNK(System_Diagnostics_StackTrace, GetFrames);
MonoArray *frames = st_get_frames(p_stack_trace, &exc);
if (exc) {
GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
return Vector<StackInfo>();
}
int frame_count = mono_array_length(frames);
if (frame_count <= 0)
return Vector<StackInfo>();
GDMonoUtils::DebugUtils_StackFrameInfo get_sf_info = CACHED_METHOD_THUNK(DebuggingUtils, GetStackFrameInfo);
Vector<StackInfo> si;
si.resize(frame_count);
for (int i = 0; i < frame_count; i++) {
StackInfo &sif = si[i];
MonoObject *frame = mono_array_get(frames, MonoObject *, i);
MonoString *file_name;
int file_line_num;
MonoString *method_decl;
get_sf_info(frame, &file_name, &file_line_num, &method_decl, &exc);
if (exc) {
GDMonoUtils::print_unhandled_exception(exc, true /* fail silently to avoid endless recursion */);
return Vector<StackInfo>();
}
sif.file = GDMonoMarshal::mono_string_to_godot(file_name);
sif.line = file_line_num;
sif.func = GDMonoMarshal::mono_string_to_godot(method_decl);
}
return si;
}
void CSharpLanguage::frame() {
const Ref<MonoGCHandle> &task_scheduler_handle = GDMonoUtils::mono_cache.task_scheduler_handle;
@ -1049,7 +1115,7 @@ bool CSharpInstance::has_method(const StringName &p_method) const {
GDMonoClass *top = script->script_class;
while (top && top != script->native) {
if (top->has_method(p_method)) {
if (top->has_fetched_method_unknown_params(p_method)) {
return true;
}
@ -1227,7 +1293,7 @@ ScriptInstance::RPCMode CSharpInstance::get_rpc_mode(const StringName &p_method)
GDMonoClass *top = script->script_class;
while (top && top != script->native) {
GDMonoMethod *method = top->get_method(p_method);
GDMonoMethod *method = top->get_fetched_method_unknown_params(p_method);
if (method && !method->is_static())
return _member_get_rpc_mode(method);
@ -1848,7 +1914,7 @@ void CSharpScript::set_source_code(const String &p_code) {
bool CSharpScript::has_method(const StringName &p_method) const {
return script_class->has_method(p_method);
return script_class->has_fetched_method_unknown_params(p_method);
}
Error CSharpScript::reload(bool p_keep_state) {

View file

@ -303,7 +303,7 @@ public:
/* TODO */ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
/* TODO */ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) { return ""; }
/* TODO */ virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
virtual Vector<StackInfo> debug_get_current_stack_info();
/* PROFILING FUNCTIONS */
/* TODO */ virtual void profiling_start() {}
@ -335,6 +335,8 @@ public:
virtual void *alloc_instance_binding_data(Object *p_object);
virtual void free_instance_binding_data(void *p_data);
Vector<StackInfo> stack_trace_get_info(MonoObject *p_stack_trace);
CSharpLanguage();
~CSharpLanguage();
};

View file

@ -151,7 +151,7 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
if (p_line >= 0) {
args.push_back("-g");
args.push_back(script_path + ":" + itos(p_line) + ":" + itos(p_col));
args.push_back(script_path + ":" + itos(p_line + 1) + ":" + itos(p_col));
} else {
args.push_back(script_path);
}
@ -170,6 +170,11 @@ Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int
monodevel_instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path()));
String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
if (p_line >= 0) {
script_path += ";" + itos(p_line + 1) + ";" + itos(p_col);
}
monodevel_instance->execute(script_path);
} break;
default:

View file

@ -0,0 +1,77 @@
using System;
using System.Diagnostics;
using System.Reflection;
using System.Text;
namespace Godot
{
internal static class DebuggingUtils
{
internal static void AppendTypeName(this StringBuilder sb, Type type)
{
if (type.IsPrimitive)
sb.Append(type.Name);
else if (type == typeof(void))
sb.Append("void");
else
sb.Append(type.ToString());
sb.Append(" ");
}
public static void GetStackFrameInfo(StackFrame frame, out string fileName, out int fileLineNumber, out string methodDecl)
{
fileName = frame.GetFileName();
fileLineNumber = frame.GetFileLineNumber();
MethodBase methodBase = frame.GetMethod();
StringBuilder sb = new StringBuilder();
if (methodBase is MethodInfo methodInfo)
sb.AppendTypeName(methodInfo.ReturnType);
sb.Append(methodBase.DeclaringType.FullName);
sb.Append(".");
sb.Append(methodBase.Name);
if (methodBase.IsGenericMethod)
{
Type[] genericParams = methodBase.GetGenericArguments();
sb.Append("<");
for (int j = 0; j < genericParams.Length; j++)
{
if (j > 0)
sb.Append(", ");
sb.AppendTypeName(genericParams[j]);
}
sb.Append(">");
}
sb.Append("(");
bool varArgs = (methodBase.CallingConvention & CallingConventions.VarArgs) != 0;
ParameterInfo[] parameter = methodBase.GetParameters();
for (int i = 0; i < parameter.Length; i++)
{
if (i > 0)
sb.Append(", ");
if (i == parameter.Length - 1 && varArgs)
sb.Append("params ");
sb.AppendTypeName(parameter[i].ParameterType);
}
sb.Append(")");
methodDecl = sb.ToString();
}
}
}

View file

@ -226,7 +226,7 @@ void GDMono::initialize() {
mono_install_unhandled_exception_hook(gdmono_unhandled_exception_hook, NULL);
OS::get_singleton()->print("Mono: ALL IS GOOD\n");
OS::get_singleton()->print("Mono: INITIALIZED\n");
}
#ifndef MONO_GLUE_DISABLED

View file

@ -89,11 +89,6 @@ Vector<MonoClassField *> GDMonoClass::get_enum_fields() {
}
#endif
bool GDMonoClass::has_method(const StringName &p_name) {
return get_method(p_name) != NULL;
}
bool GDMonoClass::has_attribute(GDMonoClass *p_attr_class) {
#ifdef DEBUG_ENABLED
@ -225,7 +220,7 @@ void GDMonoClass::fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base
methods_fetched = true;
}
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name) {
GDMonoMethod *GDMonoClass::get_fetched_method_unknown_params(const StringName &p_name) {
ERR_FAIL_COND_V(!methods_fetched, NULL);
@ -239,6 +234,11 @@ GDMonoMethod *GDMonoClass::get_method(const StringName &p_name) {
return NULL;
}
bool GDMonoClass::has_fetched_method_unknown_params(const StringName &p_name) {
return get_fetched_method_unknown_params(p_name) != NULL;
}
GDMonoMethod *GDMonoClass::get_method(const StringName &p_name, int p_params_count) {
MethodKey key = MethodKey(p_name, p_params_count);
@ -303,6 +303,8 @@ GDMonoMethod *GDMonoClass::get_method_with_desc(const String &p_description, boo
MonoMethod *method = mono_method_desc_search_in_class(desc, mono_class);
mono_method_desc_free(desc);
ERR_FAIL_COND_V(mono_method_get_class(method) != mono_class, NULL);
return get_method(method);
}

View file

@ -112,7 +112,8 @@ public:
Vector<MonoClassField *> get_enum_fields();
#endif
bool has_method(const StringName &p_name);
GDMonoMethod *get_fetched_method_unknown_params(const StringName &p_name);
bool has_fetched_method_unknown_params(const StringName &p_name);
bool has_attribute(GDMonoClass *p_attr_class);
MonoObject *get_attribute(GDMonoClass *p_attr_class);
@ -120,8 +121,7 @@ public:
void fetch_attributes();
void fetch_methods_with_godot_api_checks(GDMonoClass *p_native_base);
GDMonoMethod *get_method(const StringName &p_name);
GDMonoMethod *get_method(const StringName &p_name, int p_params_count);
GDMonoMethod *get_method(const StringName &p_name, int p_params_count = 0);
GDMonoMethod *get_method(MonoMethod *p_raw_method);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name);
GDMonoMethod *get_method(MonoMethod *p_raw_method, const StringName &p_name, int p_params_count);

View file

@ -31,6 +31,7 @@
#include "gd_mono_utils.h"
#include "os/dir_access.h"
#include "os/os.h"
#include "project_settings.h"
#include "reference.h"
@ -53,6 +54,7 @@ MonoCache mono_cache;
#define CACHE_NS_CLASS_AND_CHECK(m_ns, m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.class_##m_ns##_##m_class, m_val)
#define CACHE_RAW_MONO_CLASS_AND_CHECK(m_class, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.rawclass_##m_class, m_val)
#define CACHE_FIELD_AND_CHECK(m_class, m_field, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.field_##m_class##_##m_field, m_val)
#define CACHE_METHOD_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.method_##m_class##_##m_method, m_val)
#define CACHE_METHOD_THUNK_AND_CHECK(m_class, m_method, m_val) CACHE_AND_CHECK(GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method, m_val)
void MonoCache::clear_members() {
@ -72,6 +74,13 @@ void MonoCache::clear_members() {
class_String = NULL;
class_IntPtr = NULL;
#ifdef DEBUG_ENABLED
class_System_Diagnostics_StackTrace = NULL;
methodthunk_System_Diagnostics_StackTrace_GetFrames = NULL;
method_System_Diagnostics_StackTrace_ctor_bool = NULL;
method_System_Diagnostics_StackTrace_ctor_Exception_bool = NULL;
#endif
rawclass_Dictionary = NULL;
class_Vector2 = NULL;
@ -94,6 +103,11 @@ void MonoCache::clear_members() {
class_WeakRef = NULL;
class_MarshalUtils = NULL;
#ifdef DEBUG_ENABLED
class_DebuggingUtils = NULL;
methodthunk_DebuggingUtils_GetStackFrameInfo = NULL;
#endif
class_ExportAttribute = NULL;
field_ExportAttribute_hint = NULL;
field_ExportAttribute_hintString = NULL;
@ -137,6 +151,13 @@ void update_corlib_cache() {
CACHE_CLASS_AND_CHECK(double, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_double_class()));
CACHE_CLASS_AND_CHECK(String, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_string_class()));
CACHE_CLASS_AND_CHECK(IntPtr, GDMono::get_singleton()->get_corlib_assembly()->get_class(mono_get_intptr_class()));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(System_Diagnostics_StackTrace, GDMono::get_singleton()->get_corlib_assembly()->get_class("System.Diagnostics", "StackTrace"));
CACHE_METHOD_THUNK_AND_CHECK(System_Diagnostics_StackTrace, GetFrames, (StackTrace_GetFrames)CACHED_CLASS(System_Diagnostics_StackTrace)->get_method("GetFrames")->get_thunk());
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
}
void update_godot_api_cache() {
@ -161,6 +182,10 @@ void update_godot_api_cache() {
CACHE_CLASS_AND_CHECK(WeakRef, GODOT_API_CLASS(WeakRef));
CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
#ifdef DEBUG_ENABLED
CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
#endif
// Attributes
CACHE_CLASS_AND_CHECK(ExportAttribute, GODOT_API_CLASS(ExportAttribute));
CACHE_FIELD_AND_CHECK(ExportAttribute, hint, CACHED_CLASS(ExportAttribute)->get_field("hint"));
@ -183,6 +208,10 @@ void update_godot_api_cache() {
CACHE_METHOD_THUNK_AND_CHECK(SignalAwaiter, FailureCallback, (SignalAwaiter_FailureCallback)GODOT_API_CLASS(SignalAwaiter)->get_method("FailureCallback", 0)->get_thunk());
CACHE_METHOD_THUNK_AND_CHECK(GodotTaskScheduler, Activate, (GodotTaskScheduler_Activate)GODOT_API_CLASS(GodotTaskScheduler)->get_method("Activate", 0)->get_thunk());
#ifdef DEBUG_ENABLED
CACHE_METHOD_THUNK_AND_CHECK(DebuggingUtils, GetStackFrameInfo, (DebugUtils_StackFrameInfo)GODOT_API_CLASS(DebuggingUtils)->get_method("GetStackFrameInfo", 4)->get_thunk());
#endif
{
/*
* TODO Right now we only support Dictionary<object, object>.
@ -366,9 +395,47 @@ String get_exception_name_and_message(MonoObject *p_ex) {
return res;
}
void print_unhandled_exception(MonoObject *p_ex) {
ERR_PRINT(GDMonoUtils::get_exception_name_and_message(p_ex).utf8());
mono_print_unhandled_exception(p_ex);
void print_unhandled_exception(MonoObject *p_exc) {
print_unhandled_exception(p_exc, false);
}
void print_unhandled_exception(MonoObject *p_exc, bool p_fail_silently) {
mono_print_unhandled_exception(p_exc);
#ifdef DEBUG_ENABLED
GDMonoClass *st_klass = CACHED_CLASS(System_Diagnostics_StackTrace);
MonoObject *stack_trace = mono_object_new(mono_domain_get(), st_klass->get_mono_ptr());
MonoBoolean need_file_info = true;
void *ctor_args[2] = { p_exc, &need_file_info };
MonoObject *unexpected_exc = NULL;
CACHED_METHOD(System_Diagnostics_StackTrace, ctor_Exception_bool)->invoke_raw(stack_trace, ctor_args, &unexpected_exc);
if (unexpected_exc != NULL) {
mono_print_unhandled_exception(unexpected_exc);
if (p_fail_silently) {
// Called from CSharpLanguage::get_current_stack_info,
// so printing an error here could result in endless recursion
OS::get_singleton()->printerr("Mono: Method GDMonoUtils::print_unhandled_exception failed");
return;
} else {
ERR_FAIL();
}
}
Vector<ScriptLanguage::StackInfo> si;
if (stack_trace != NULL)
si = CSharpLanguage::get_singleton()->stack_trace_get_info(stack_trace);
String file = si.size() ? si[0].file : __FILE__;
String func = si.size() ? si[0].func : FUNCTION_STR;
int line = si.size() ? si[0].line : __LINE__;
String error_msg = "Unhandled exception";
String exc_msg = GDMonoUtils::get_exception_name_and_message(p_exc);
ScriptDebugger::get_singleton()->send_error(func, file, line, error_msg, exc_msg, ERR_HANDLER_ERROR, si);
#endif
}
} // namespace GDMonoUtils

View file

@ -46,13 +46,10 @@ typedef MonoObject *(*MarshalUtils_ArraysToDict)(MonoArray *, MonoArray *, MonoO
typedef MonoObject *(*SignalAwaiter_SignalCallback)(MonoObject *, MonoArray **, MonoObject **);
typedef MonoObject *(*SignalAwaiter_FailureCallback)(MonoObject *, MonoObject **);
typedef MonoObject *(*GodotTaskScheduler_Activate)(MonoObject *, MonoObject **);
typedef MonoArray *(*StackTrace_GetFrames)(MonoObject *, MonoObject **);
typedef void (*DebugUtils_StackFrameInfo)(MonoObject *, MonoString **, int *, MonoString **, MonoObject **);
struct MonoCache {
// Format for cached classes in the Godot namespace: class_<Class>
// Macro: CACHED_CLASS(<Class>)
// Format for cached classes in a different namespace: class_<Namespace>_<Class>
// Macro: CACHED_NS_CLASS(<Namespace>, <Class>)
// -----------------------------------------------
// corlib classes
@ -73,6 +70,13 @@ struct MonoCache {
GDMonoClass *class_String;
GDMonoClass *class_IntPtr;
#ifdef DEBUG_ENABLED
GDMonoClass *class_System_Diagnostics_StackTrace;
StackTrace_GetFrames methodthunk_System_Diagnostics_StackTrace_GetFrames;
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_bool;
GDMonoMethod *method_System_Diagnostics_StackTrace_ctor_Exception_bool;
#endif
MonoClass *rawclass_Dictionary;
// -----------------------------------------------
@ -96,6 +100,11 @@ struct MonoCache {
GDMonoClass *class_WeakRef;
GDMonoClass *class_MarshalUtils;
#ifdef DEBUG_ENABLED
GDMonoClass *class_DebuggingUtils;
DebugUtils_StackFrameInfo methodthunk_DebuggingUtils_GetStackFrameInfo;
#endif
GDMonoClass *class_ExportAttribute;
GDMonoField *field_ExportAttribute_hint;
GDMonoField *field_ExportAttribute_hintString;
@ -167,7 +176,8 @@ MonoDomain *create_domain(const String &p_friendly_name);
String get_exception_name_and_message(MonoObject *p_ex);
void print_unhandled_exception(MonoObject *p_ex);
void print_unhandled_exception(MonoObject *p_exc);
void print_unhandled_exception(MonoObject *p_exc, bool p_fail_silently);
} // namespace GDMonoUtils
@ -175,9 +185,9 @@ void print_unhandled_exception(MonoObject *p_ex);
#define CACHED_CLASS(m_class) (GDMonoUtils::mono_cache.class_##m_class)
#define CACHED_CLASS_RAW(m_class) (GDMonoUtils::mono_cache.class_##m_class->get_mono_ptr())
#define CACHED_NS_CLASS(m_ns, m_class) (GDMonoUtils::mono_cache.class_##m_ns##_##m_class)
#define CACHED_RAW_MONO_CLASS(m_class) (GDMonoUtils::mono_cache.rawclass_##m_class)
#define CACHED_FIELD(m_class, m_field) (GDMonoUtils::mono_cache.field_##m_class##_##m_field)
#define CACHED_METHOD(m_class, m_method) (GDMonoUtils::mono_cache.method_##m_class##_##m_method)
#define CACHED_METHOD_THUNK(m_class, m_method) (GDMonoUtils::mono_cache.methodthunk_##m_class##_##m_method)
#ifdef REAL_T_IS_DOUBLE