From dbc0c0e124e22569d117fe8903929d3d10876a2f Mon Sep 17 00:00:00 2001 From: martinfouilleul Date: Thu, 12 Oct 2023 20:17:22 +0200 Subject: [PATCH] Split runtime abort/asserts and app abort/assert. Always log and abort normally from native code. Display a dialog and exit silently when aborting/asserting from wasm code or bindings bound checks --- scripts/bindgen.py | 8 +-- src/platform/native_debug.c | 21 ++---- src/platform/platform_debug.c | 1 + src/runtime.c | 118 +++++++++++++++++++++++++--------- src/runtime.h | 12 +++- src/runtime_memory.c | 4 +- src/wasmbind/core_api.json | 4 +- 7 files changed, 112 insertions(+), 56 deletions(-) diff --git a/scripts/bindgen.py b/scripts/bindgen.py index 748bec7..35b883d 100755 --- a/scripts/bindgen.py +++ b/scripts/bindgen.py @@ -135,8 +135,8 @@ def bindgen(apiName, spec, **kwargs): s += retTypeCName + '* __retPtr = (' + retTypeCName + '*)((char*)_mem + *(i32*)&_sp[0]);\n' s += '\t{\n' - s += '\t\tOC_ASSERT(((char*)__retPtr >= (char*)_mem) && (((char*)__retPtr - (char*)_mem) < m3_GetMemorySize(runtime)), "return pointer is out of bounds");\n' - s += '\t\tOC_ASSERT((char*)__retPtr + sizeof(' + retTypeCName + ') <= ((char*)_mem + m3_GetMemorySize(runtime)), "return pointer is out of bounds");\n' + s += '\t\tOC_ASSERT_DIALOG(((char*)__retPtr >= (char*)_mem) && (((char*)__retPtr - (char*)_mem) < m3_GetMemorySize(runtime)), "return pointer is out of bounds");\n' + s += '\t\tOC_ASSERT_DIALOG((char*)__retPtr + sizeof(' + retTypeCName + ') <= ((char*)_mem + m3_GetMemorySize(runtime)), "return pointer is out of bounds");\n' s += '\t}\n' for argIndex, arg in enumerate(decl['args']): @@ -178,8 +178,8 @@ def bindgen(apiName, spec, **kwargs): printError("binding '" + name + "' missing pointer length decoration for param '" + argName + "'") else: s += '\t{\n' - s += '\t\tOC_ASSERT(((char*)'+ argName + ' >= (char*)_mem) && (((char*)'+ argName +' - (char*)_mem) < m3_GetMemorySize(runtime)), "parameter \''+argName+'\' is out of bounds");\n' - s += '\t\tOC_ASSERT((char*)' + argName + ' + ' + s += '\t\tOC_ASSERT_DIALOG(((char*)'+ argName + ' >= (char*)_mem) && (((char*)'+ argName +' - (char*)_mem) < m3_GetMemorySize(runtime)), "parameter \''+argName+'\' is out of bounds");\n' + s += '\t\tOC_ASSERT_DIALOG((char*)' + argName + ' + ' proc = argLen.get('proc') if proc != None: diff --git a/src/platform/native_debug.c b/src/platform/native_debug.c index 0c804f0..85b73d7 100644 --- a/src/platform/native_debug.c +++ b/src/platform/native_debug.c @@ -7,7 +7,8 @@ **************************************************************************/ #include -#include "app/app.h" +#include "util/memory.h" +#include "util/strings.h" #include "platform_debug.c" //---------------------------------------------------------------- // Logging @@ -102,14 +103,7 @@ _Noreturn void oc_abort_ext(const char* file, const char* function, int line, co note.ptr); oc_log_error(msg.ptr); - - oc_str8_list options = { 0 }; - oc_str8_list_push(scratch.arena, &options, OC_STR8("OK")); - - oc_alert_popup(OC_STR8("Fatal Error"), msg, options); - - //TODO: could terminate more gracefully? - exit(-1); + abort(); } _Noreturn void oc_assert_fail(const char* file, const char* function, int line, const char* src, const char* fmt, ...) @@ -130,12 +124,5 @@ _Noreturn void oc_assert_fail(const char* file, const char* function, int line, oc_str8_ip(note)); oc_log_error(msg.ptr); - - oc_str8_list options = { 0 }; - oc_str8_list_push(scratch.arena, &options, OC_STR8("OK")); - - oc_alert_popup(OC_STR8("Assertion Failed"), msg, options); - - //TODO: could terminate more gracefully? - exit(-1); + abort(); } diff --git a/src/platform/platform_debug.c b/src/platform/platform_debug.c index dfc467f..1d0700a 100644 --- a/src/platform/platform_debug.c +++ b/src/platform/platform_debug.c @@ -5,6 +5,7 @@ * See LICENSE.txt for licensing information * **************************************************************************/ +#include #include "platform_debug.h" typedef struct oc_log_config diff --git a/src/runtime.c b/src/runtime.c index 48f686b..7a6a70c 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -95,20 +95,6 @@ u64 orca_check_cstring(IM3Runtime runtime, const char* ptr) return (len + 1); //include null-terminator } -void orca_wasm3_abort(IM3Runtime runtime, M3Result res, const char* file, const char* function, int line, const char* msg) -{ - M3ErrorInfo errInfo = { 0 }; - m3_GetErrorInfo(runtime, &errInfo); - if(errInfo.message && res == errInfo.result) - { - oc_abort_ext(file, function, line, "%s: %s (%s)", msg, res, errInfo.message); - } - else - { - oc_abort_ext(file, function, line, "%s: %s", msg, res); - } -} - void oc_bridge_window_set_title(oc_wasm_str8 title) { oc_str8 nativeTitle = oc_wasm_str8_to_native(title); @@ -133,6 +119,80 @@ void oc_bridge_clipboard_set_string(oc_wasm_str8 value) oc_runtime_clipboard_set_string(&__orcaApp.clipboard, value); } +void oc_assert_fail_dialog(const char* file, + const char* function, + int line, + const char* test, + const char* fmt, + ...) +{ + oc_arena_scope scratch = oc_scratch_begin(); + + va_list ap; + va_start(ap, fmt); + oc_str8 note = oc_str8_pushfv(scratch.arena, fmt, ap); + va_end(ap); + + oc_str8 msg = oc_str8_pushf(scratch.arena, + "Assertion failed in function %s() in file \"%s\", line %i:\n%s\nNote: %.*s\n", + function, + file, + line, + test, + oc_str8_ip(note)); + + oc_log_error(msg.ptr); + + oc_str8_list options = { 0 }; + oc_str8_list_push(scratch.arena, &options, OC_STR8("OK")); + + oc_alert_popup(OC_STR8("Assertion Failed"), msg, options); + exit(-1); + + oc_scratch_end(scratch); +} + +void oc_abort_ext_dialog(const char* file, const char* function, int line, const char* fmt, ...) +{ + oc_arena_scope scratch = oc_scratch_begin(); + + va_list ap; + va_start(ap, fmt); + oc_str8 note = oc_str8_pushfv(scratch.arena, fmt, ap); + va_end(ap); + + oc_str8 msg = oc_str8_pushf(scratch.arena, + "Fatal error in function %s() in file \"%s\", line %i:\n%.*s\n", + function, + file, + line, + oc_str8_ip(note)); + + oc_log_error(msg.ptr); + + oc_str8_list options = { 0 }; + oc_str8_list_push(scratch.arena, &options, OC_STR8("OK")); + + oc_alert_popup(OC_STR8("Fatal Error"), msg, options); + exit(-1); + + oc_scratch_end(scratch); +} + +void oc_wasm3_trap(IM3Runtime runtime, M3Result res, const char* file, const char* function, int line, const char* msg) +{ + M3ErrorInfo errInfo = { 0 }; + m3_GetErrorInfo(runtime, &errInfo); + if(errInfo.message && res == errInfo.result) + { + oc_abort_ext_dialog(file, function, line, "%s: %s (%s)", msg, res, errInfo.message); + } + else + { + oc_abort_ext_dialog(file, function, line, "%s: %s", msg, res); + } +} + void oc_bridge_log(oc_log_level level, int functionLen, char* function, @@ -441,13 +501,13 @@ i32 orca_runloop(void* user) M3Result res = m3_ParseModule(app->env.m3Env, &app->env.m3Module, (u8*)app->env.wasmBytecode.ptr, app->env.wasmBytecode.len); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "The application couldn't parse its web assembly module"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "The application couldn't parse its web assembly module"); } res = m3_LoadModule(app->env.m3Runtime, app->env.m3Module); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "The application couldn't load its web assembly module into the runtime"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "The application couldn't load its web assembly module into the runtime"); } m3_SetModuleName(app->env.m3Module, bundleNameCString); @@ -472,7 +532,7 @@ i32 orca_runloop(void* user) res = m3_CompileModule(app->env.m3Module); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "The application couldn't compile its web assembly module"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "The application couldn't compile its web assembly module"); } //NOTE: Find and type check event handlers. @@ -558,7 +618,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_ON_INIT], 0, 0); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } @@ -571,7 +631,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_FRAME_RESIZE], 2, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } @@ -616,7 +676,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_RAW_EVENT], 1, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } #else oc_log_error("oc_on_raw_event() is not supported on big endian platforms"); @@ -647,7 +707,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_FRAME_RESIZE], 2, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } } @@ -664,7 +724,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_DOWN], 1, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } } @@ -677,7 +737,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_UP], 1, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } } @@ -692,7 +752,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_WHEEL], 2, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } } @@ -706,7 +766,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_MOVE], 4, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } } @@ -729,7 +789,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_KEY_DOWN], 2, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } } @@ -741,7 +801,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_KEY_UP], 2, args); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } } @@ -760,7 +820,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_FRAME_REFRESH], 0, 0); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } @@ -923,7 +983,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_TERMINATE], 0, 0); if(res) { - ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(app->env.m3Runtime, res, "Runtime error"); } } diff --git a/src/runtime.h b/src/runtime.h index 46b20d5..bb7879a 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -130,7 +130,15 @@ oc_runtime* oc_runtime_get(void); oc_wasm_env* oc_runtime_get_env(void); oc_str8 oc_runtime_get_wasm_memory(void); -void orca_wasm3_abort(IM3Runtime runtime, M3Result res, const char* file, const char* function, int line, const char* msg); -#define ORCA_WASM3_ABORT(runtime, err, msg) orca_wasm3_abort(runtime, err, __FILE__, __FUNCTION__, __LINE__, msg) +void oc_abort_ext_dialog(const char* file, const char* function, int line, const char* fmt, ...); +void oc_assert_fail_dialog(const char* file, const char* function, int line, const char* test, const char* fmt, ...); + +#define _OC_ASSERT_DIALOG_(test, fmt, ...) \ + ((test) || (oc_assert_fail_dialog(__FILE__, __FUNCTION__, __LINE__, #test, fmt, ##__VA_ARGS__), 0)) +#define OC_ASSERT_DIALOG(test, ...) \ + _OC_ASSERT_DIALOG_(test, OC_VA_NOPT("", ##__VA_ARGS__) OC_ARG1(__VA_ARGS__) OC_VA_COMMA_TAIL(__VA_ARGS__)) + +void oc_wasm3_trap(IM3Runtime runtime, M3Result res, const char* file, const char* function, int line, const char* msg); +#define OC_WASM3_TRAP(runtime, err, msg) oc_wasm3_trap(runtime, err, __FILE__, __FUNCTION__, __LINE__, msg) #endif //__RUNTIME_H_ diff --git a/src/runtime_memory.c b/src/runtime_memory.c index 8687306..e73d674 100644 --- a/src/runtime_memory.c +++ b/src/runtime_memory.c @@ -151,13 +151,13 @@ oc_wasm_addr oc_wasm_arena_push(oc_wasm_addr arena, u64 size) M3Result res = m3_Call(env->exports[OC_EXPORT_ARENA_PUSH], 2, args); if(res) { - ORCA_WASM3_ABORT(env->m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(env->m3Runtime, res, "Runtime error"); } res = m3_GetResults(env->exports[OC_EXPORT_ARENA_PUSH], 1, retPointers); if(res) { - ORCA_WASM3_ABORT(env->m3Runtime, res, "Runtime error"); + OC_WASM3_TRAP(env->m3Runtime, res, "Runtime error"); } return (retValues[0]); diff --git a/src/wasmbind/core_api.json b/src/wasmbind/core_api.json index b3a7471..b3d4671 100644 --- a/src/wasmbind/core_api.json +++ b/src/wasmbind/core_api.json @@ -33,7 +33,7 @@ }, { "name": "oc_bridge_assert_fail", - "cname": "oc_assert_fail", + "cname": "oc_assert_fail_dialog", "ret": {"name": "void", "tag": "v"}, "args": [ {"name": "file", "type": {"name": "const char*", "tag": "p"}, @@ -53,7 +53,7 @@ }, { "name": "oc_bridge_abort_ext", - "cname": "oc_abort_ext", + "cname": "oc_abort_ext_dialog", "ret": {"name": "void", "tag": "v"}, "args": [ {"name": "file", "type": {"name": "const char*", "tag": "p"},