From d62d408b4fcb2b8c5d0681c275e07b3e80047482 Mon Sep 17 00:00:00 2001 From: Tyler Zhang Date: Thu, 11 Mar 2021 05:57:41 +0000 Subject: [PATCH] Backtraces in Wasm3 (#195) * Add API to report backtraces when traps are encountered during execution * Add backtrace formatting * Fix memory issue with not resetting code mapping page when code page is released * Halve memory usage for code mapping pages * Move backtrace functionality to behind a compile-time flag * Reduce backtrace size in main * Fix segfault in constant initialization Co-authored-by: Volodymyr Shymanskyy --- platforms/app/main.c | 23 ++++++++- source/m3_code.c | 110 +++++++++++++++++++++++++++++++++++++++++ source/m3_code.h | 30 +++++++++++ source/m3_compile.c | 1 + source/m3_compile.h | 1 + source/m3_config.h | 3 ++ source/m3_core.c | 102 ++++++++++++++++++++++++++++++++++++++ source/m3_core.h | 21 ++++++-- source/m3_emit.c | 3 ++ source/m3_env.c | 95 +++++++++++++++++++++++++++++++++++ source/m3_env.h | 7 +++ source/m3_exec.h | 74 ++++++++++++++++++++------- source/m3_exec_defs.h | 2 - source/m3_math_utils.h | 28 +++++------ source/m3_module.c | 9 ++++ source/m3_parse.c | 3 ++ source/wasm3.h | 28 +++++++++++ 17 files changed, 498 insertions(+), 42 deletions(-) diff --git a/platforms/app/main.c b/platforms/app/main.c index 39ce342..ce01a58 100644 --- a/platforms/app/main.c +++ b/platforms/app/main.c @@ -21,10 +21,11 @@ #define LINK_WASI #endif +#define BACKTRACE_SIZE 1024 + IM3Environment env; IM3Runtime runtime; - M3Result link_all (IM3Module module) { M3Result res; @@ -389,6 +390,7 @@ int main (int i_argc, const char* i_argv[]) const char* argFile = NULL; const char* argFunc = "_start"; unsigned argStackSize = 64*1024; + char backtrace_buff[BACKTRACE_SIZE]; // m3_PrintM3Info (); @@ -451,7 +453,16 @@ int main (int i_argc, const char* i_argv[]) if (argDumpOnTrap) { repl_dump(); } - FATAL("repl_call: %s", result); + if (m3_BacktraceEnabled()) + { + backtrace_buff[0] = '\0'; + m3_GetBacktraceStr(runtime, backtrace_buff, BACKTRACE_SIZE); + FATAL("repl_call: %s\n%s", result, backtrace_buff); + } + else + { + FATAL("repl_call: %s", result); + } } } } @@ -498,6 +509,14 @@ int main (int i_argc, const char* i_argv[]) M3ErrorInfo info; m3_GetErrorInfo (runtime, &info); fprintf (stderr, " (%s)\n", info.message); + + if (m3_BacktraceEnabled()) + { + backtrace_buff[0] = '\0'; + m3_GetBacktraceStr(runtime, backtrace_buff, BACKTRACE_SIZE); + fprintf (stderr, "%s\n", backtrace_buff); + } + //TODO: if (result == m3Err_trapExit) { // warn that exit was called // fprintf(stderr, M3_ARCH "-wasi: exit(%d)\n", runtime->exit_code); diff --git a/source/m3_code.c b/source/m3_code.c index 4619948..f625caa 100644 --- a/source/m3_code.c +++ b/source/m3_code.c @@ -7,6 +7,16 @@ #include "m3_code.h" +#if d_m3RecordBacktraces +// Code mapping page ops + +M3CodeMappingPage * NewCodeMappingPage (u32 i_minCapacity); +void FreeCodeMappingPage (M3CodeMappingPage * i_page); +#endif // d_m3RecordBacktraces + +//--------------------------------------------------------------------------------------------------------------------------------- + + IM3CodePage NewCodePage (u32 i_minNumLines) { static u32 s_sequence = 0; @@ -23,6 +33,16 @@ IM3CodePage NewCodePage (u32 i_minNumLines) page->info.sequence = ++s_sequence; page->info.numLines = (pageSize - sizeof (M3CodePageHeader)) / sizeof (code_t); +#if d_m3RecordBacktraces + page->info.mapping = NewCodeMappingPage (page->info.numLines); + if (!page->info.mapping) + { + m3Free (page); + return NULL; + } + page->info.mapping->basePC = GetPageStartPC(page); +#endif // d_m3RecordBacktraces + m3log (runtime, "new page: %p; seq: %d; bytes: %d; lines: %d", GetPagePC (page), page->info.sequence, pageSize, page->info.numLines); } @@ -39,6 +59,9 @@ void FreeCodePages (IM3CodePage * io_list) m3log (code, "free page: %d; %p; util: %3.1f%%", page->info.sequence, page, 100. * page->info.lineIndex / page->info.numLines); IM3CodePage next = page->info.next; +#if d_m3RecordBacktraces + FreeCodeMappingPage (page->info.mapping); +#endif // d_m3RecordBacktraces m3Free (page); page = next; } @@ -79,6 +102,20 @@ void EmitWord64 (IM3CodePage i_page, const u64 i_word) } +#if d_m3RecordBacktraces +void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset) +{ + M3CodeMappingPage * page = i_page->info.mapping; + d_m3Assert (page->size < page->capacity); + + M3CodeMapEntry * entry = & page->entries[page->size++]; + pc_t pc = GetPagePC (i_page); + + entry->pcOffset = pc - page->basePC; + entry->moduleOffset = i_moduleOffset; +} +#endif // d_m3RecordBacktraces + pc_t GetPageStartPC (IM3CodePage i_page) { return & i_page->code [0]; @@ -143,3 +180,76 @@ IM3CodePage GetEndCodePage (IM3CodePage i_list) return end; } + +#if d_m3RecordBacktraces +bool ContainsPC (IM3CodePage i_page, pc_t i_pc) +{ + return GetPageStartPC (i_page) <= i_pc && i_pc < GetPagePC (i_page); +} + + +bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset) +{ + M3CodeMappingPage * mapping = i_page->info.mapping; + + u32 pcOffset = i_pc - mapping->basePC; + + u32 left = 0; + u32 right = mapping->size; + + while (left < right) + { + u32 mid = left + (right - left) / 2; + + if (mapping->entries[mid].pcOffset < pcOffset) + { + left = mid + 1; + } + else if (mapping->entries[mid].pcOffset > pcOffset) + { + right = mid; + } + else + { + *o_moduleOffset = mapping->entries[mid].moduleOffset; + return true; + } + } + + // Getting here means left is now one more than the element we want. + if (left > 0) + { + left--; + *o_moduleOffset = mapping->entries[left].moduleOffset; + return true; + } + else return false; +} +#endif // d_m3RecordBacktraces + +//--------------------------------------------------------------------------------------------------------------------------------- + + +#if d_m3RecordBacktraces +M3CodeMappingPage * NewCodeMappingPage (u32 i_minCapacity) +{ + M3CodeMappingPage * page; + u32 pageSize = sizeof (M3CodeMappingPage) + sizeof (M3CodeMapEntry) * i_minCapacity; + + m3Alloc ((void **) & page, u8, pageSize); + + if (page) + { + page->size = 0; + page->capacity = i_minCapacity; + } + + return page; +} + + +void FreeCodeMappingPage (M3CodeMappingPage * i_page) +{ + m3Free (i_page); +} +#endif // d_m3RecordBacktraces diff --git a/source/m3_code.h b/source/m3_code.h index 32116c1..44bd87e 100644 --- a/source/m3_code.h +++ b/source/m3_code.h @@ -32,6 +32,9 @@ pc_t GetPagePC (IM3CodePage i_page); void EmitWord_impl (IM3CodePage i_page, void* i_word); void EmitWord32 (IM3CodePage i_page, u32 i_word); void EmitWord64 (IM3CodePage i_page, u64 i_word); +# if d_m3RecordBacktraces +void EmitMappingEntry (IM3CodePage i_page, u32 i_moduleOffset); +# endif // d_m3RecordBacktraces void PushCodePage (IM3CodePage * io_list, IM3CodePage i_codePage); IM3CodePage PopCodePage (IM3CodePage * io_list); @@ -39,12 +42,39 @@ IM3CodePage PopCodePage (IM3CodePage * io_list); IM3CodePage GetEndCodePage (IM3CodePage i_list); // i_list = NULL is valid u32 CountCodePages (IM3CodePage i_list); // i_list = NULL is valid +# if d_m3RecordBacktraces +bool ContainsPC (IM3CodePage i_page, pc_t i_pc); +bool MapPCToOffset (IM3CodePage i_page, pc_t i_pc, u32 * o_moduleOffset); +# endif // d_m3RecordBacktraces + # ifdef DEBUG void dump_code_page (IM3CodePage i_codePage, pc_t i_startPC); # endif #define EmitWord(page, val) EmitWord_impl(page, (void*)(val)) +//--------------------------------------------------------------------------------------------------------------------------------- + +# if d_m3RecordBacktraces + +typedef struct M3CodeMapEntry +{ + u32 pcOffset; + u32 moduleOffset; +} +M3CodeMapEntry; + +typedef struct M3CodeMappingPage +{ + pc_t basePC; + u32 size; + u32 capacity; + M3CodeMapEntry entries []; +} +M3CodeMappingPage; + +# endif // d_m3RecordBacktraces + d_m3EndExternC #endif // m3_code_h diff --git a/source/m3_compile.c b/source/m3_compile.c index 33bd868..e31e78d 100644 --- a/source/m3_compile.c +++ b/source/m3_compile.c @@ -2168,6 +2168,7 @@ M3Result Compile_BlockStatements (IM3Compilation o) while (o->wasm < o->wasmEnd) { emit_stack_dump (o); + o->lastOpcodeStart = o->wasm; m3opcode_t opcode = * (o->wasm++); log_opcode (o, opcode); #ifndef d_m3CompileExtendedOpcode diff --git a/source/m3_compile.h b/source/m3_compile.h index fd217ad..b6fd4f5 100644 --- a/source/m3_compile.h +++ b/source/m3_compile.h @@ -86,6 +86,7 @@ typedef struct bytes_t wasm; bytes_t wasmEnd; + bytes_t lastOpcodeStart; M3CompilationScope block; diff --git a/source/m3_config.h b/source/m3_config.h index 79d5191..94c5c12 100644 --- a/source/m3_config.h +++ b/source/m3_config.h @@ -57,6 +57,9 @@ # define d_m3ProfilerSlotMask 0xFFFF # endif +# ifndef d_m3RecordBacktraces +# define d_m3RecordBacktraces 0 +# endif // profiling and tracing ------------------------------------------------------ diff --git a/source/m3_core.c b/source/m3_core.c index fcc8d47..0755832 100644 --- a/source/m3_core.c +++ b/source/m3_core.c @@ -9,6 +9,7 @@ #include "wasm3.h" #include "m3_core.h" +#include "m3_env.h" void m3_Abort(const char* message) { #ifdef DEBUG @@ -489,3 +490,104 @@ M3Result Read_utf8 (cstr_t * o_utf8, bytes_t * io_bytes, cbytes_t i_end) return result; } + +#if d_m3RecordBacktraces +u32 FindModuleOffset (IM3Runtime i_runtime, pc_t i_pc) +{ + // walk the code pages + IM3CodePage curr = i_runtime->pagesOpen; + bool pageFound = false; + + while (curr) + { + if (ContainsPC (curr, i_pc)) + { + pageFound = true; + break; + } + curr = curr->info.next; + } + + if (!pageFound) + { + curr = i_runtime->pagesFull; + while (curr) + { + if (ContainsPC (curr, i_pc)) + { + pageFound = true; + break; + } + curr = curr->info.next; + } + } + + if (pageFound) + { + u32 result = 0; + + bool pcFound = MapPCToOffset (curr, i_pc, & result); + d_m3Assert (pcFound); + + return result; + } + else return 0; +} + + +void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc) +{ + // don't try to push any more frames if we've already had an alloc failure + if (UNLIKELY (io_runtime->backtrace.backtraceTruncated)) + return; + + M3BacktraceFrame * newFrame; + m3Alloc ((void **) & newFrame, M3BacktraceFrame, 1); + + if (!newFrame) + { + io_runtime->backtrace.backtraceTruncated = true; + return; + } + + newFrame->moduleOffset = FindModuleOffset (io_runtime, i_pc); + + if (!io_runtime->backtrace.frames || !io_runtime->backtrace.lastFrame) + io_runtime->backtrace.frames = newFrame; + else + io_runtime->backtrace.lastFrame->next = newFrame; + io_runtime->backtrace.lastFrame = newFrame; +} + + +void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function) +{ + // If we've had an alloc failure then the last frame doesn't refer to the + // frame we want to fill in the function info for. + if (UNLIKELY (io_runtime->backtrace.backtraceTruncated)) + return; + + if (!io_runtime->backtrace.lastFrame) + return; + + io_runtime->backtrace.lastFrame->function = i_function; + io_runtime->backtrace.lastFrame->module = i_function->module; +} + + +void ClearBacktrace (IM3Runtime io_runtime) +{ + io_runtime->backtrace.backtraceTruncated = false; + + M3BacktraceFrame * currentFrame = io_runtime->backtrace.frames; + while (currentFrame) + { + M3BacktraceFrame * nextFrame = currentFrame->next; + m3Free (currentFrame); + currentFrame = nextFrame; + } + + io_runtime->backtrace.frames = NULL; + io_runtime->backtrace.lastFrame = NULL; +} +#endif // d_m3RecordBacktraces diff --git a/source/m3_core.h b/source/m3_core.h index 4b33667..cb1b8c6 100644 --- a/source/m3_core.h +++ b/source/m3_core.h @@ -145,15 +145,20 @@ typedef struct M3MemoryHeader } M3MemoryHeader; +struct M3CodeMappingPage; typedef struct M3CodePageHeader { - struct M3CodePage * next; + struct M3CodePage * next; - u32 lineIndex; - u32 numLines; - u32 sequence; // this is just used for debugging; could be removed - u32 usageCount; + u32 lineIndex; + u32 numLines; + u32 sequence; // this is just used for debugging; could be removed + u32 usageCount; + +# if d_m3RecordBacktraces + struct M3CodeMappingPage * mapping; +# endif // d_m3RecordBacktraces } M3CodePageHeader; @@ -243,6 +248,12 @@ size_t SPrintArg (char * o_string, size_t i_n, m3stack_t i_sp void ReportError (IM3Runtime io_runtime, IM3Module i_module, IM3Function i_function, ccstr_t i_errorMessage, ccstr_t i_file, u32 i_lineNum); +# if d_m3RecordBacktraces +void PushBacktraceFrame (IM3Runtime io_runtime, pc_t i_pc); +void FillBacktraceFunctionInfo (IM3Runtime io_runtime, IM3Function i_function); +void ClearBacktrace (IM3Runtime io_runtime); +# endif + d_m3EndExternC #endif // m3_core_h diff --git a/source/m3_emit.c b/source/m3_emit.c index 4b7a52c..46d7824 100644 --- a/source/m3_emit.c +++ b/source/m3_emit.c @@ -62,6 +62,9 @@ M3Result EmitOp (IM3Compilation o, IM3Operation i_operation) if (not result) { if (d_m3LogEmit) log_emit (o, i_operation); +# if d_m3RecordBacktraces + EmitMappingEntry (o->page, o->lastOpcodeStart - o->module->wasmStart); +# endif // d_m3RecordBacktraces EmitWord (o->page, i_operation); } } diff --git a/source/m3_env.c b/source/m3_env.c index 3dc0403..8fc88de 100644 --- a/source/m3_env.c +++ b/source/m3_env.c @@ -289,6 +289,9 @@ void Environment_ReleaseCodePages (IM3Environment i_environment, IM3CodePage i while (end) { end->info.lineIndex = 0; // reset page +#if d_m3RecordBacktraces + end->info.mapping->size = 0; +#endif // d_m3RecordBacktraces IM3CodePage next = end->info.next; if (not next) @@ -428,6 +431,7 @@ M3Result EvaluateExpression (IM3Module i_module, void * o_expressed, u8 i_type o->module = i_module; o->wasm = * io_bytes; o->wasmEnd = i_end; + o->lastOpcodeStart = o->wasm; o->block.depth = -1; // so that root compilation depth = 0 @@ -1111,3 +1115,94 @@ uint8_t * m3_GetMemory (IM3Runtime i_runtime, uint32_t * o_memorySizeInBytes, return memory; } + + +const char * m3_GetFunctionName (IM3Function i_function) +{ + if (!i_function || !i_function->name) + return ""; + + return i_function->name; +} + + +bool m3_BacktraceEnabled (void) +{ + return d_m3RecordBacktraces; +} + + +M3BacktraceInfo * m3_GetBacktrace (IM3Runtime i_runtime) +{ +# if d_m3RecordBacktraces + return & i_runtime->backtrace; +# else + return NULL; +# endif +} + + +uint32_t m3_GetBacktraceStr (IM3Runtime i_runtime, char* o_buffer, uint32_t i_bufferSize) +{ +# if d_m3RecordBacktraces + int remaining = i_bufferSize; + + int result; + + result = snprintf (o_buffer, remaining, "wasm backtrace:"); + if (result < 0) + return 0; + else + { + remaining -= result; + if (remaining < 0) + return i_bufferSize; + o_buffer += result; + } + + M3BacktraceFrame * curr = i_runtime->backtrace.frames; + int frameCount = 0; + while (curr) + { + const char* moduleName = m3_GetModuleName (curr->module); + const char* functionName = m3_GetFunctionName (curr->function); + + result = snprintf (o_buffer, remaining, "\n %d: 0x%" PRIx64 " - %s!%s", + frameCount, curr->moduleOffset, moduleName, functionName); + if (result < 0) + return 0; + else + { + remaining -= result; + if (remaining < 0) + return i_bufferSize; + o_buffer += result; + } + + curr = curr->next; + frameCount++; + } + + if (i_runtime->backtrace.backtraceTruncated) + { + result = snprintf (o_buffer, remaining, "\n (truncated)"); + if (result < 0) + return 0; + else + { + remaining -= result; + if (remaining < 0) + return i_bufferSize; + o_buffer += result; + } + } + + return i_bufferSize - remaining; +# else + (void)i_runtime; + (void)o_buffer; + (void)i_bufferSize; + + return 0; +# endif // d_m3RecordBacktraces +} diff --git a/source/m3_env.h b/source/m3_env.h index acc56f8..2fe48a4 100644 --- a/source/m3_env.h +++ b/source/m3_env.h @@ -141,6 +141,9 @@ typedef struct M3Module struct M3Runtime * runtime; struct M3Environment * environment; + bytes_t wasmStart; + bytes_t wasmEnd; + cstr_t name; u32 numFuncTypes; @@ -233,6 +236,10 @@ typedef struct M3Runtime #if d_m3VerboseLogs char error_message[256]; // the actual buffer. M3ErrorInfo can point to this #endif + +#if d_m3RecordBacktraces + M3BacktraceInfo backtrace; +#endif } M3Runtime; diff --git a/source/m3_exec.h b/source/m3_exec.h index 177477d..0319630 100644 --- a/source/m3_exec.h +++ b/source/m3_exec.h @@ -53,8 +53,27 @@ d_m3BeginExternC #define jumpOp(PC) jumpOpDirect(PC) +# if d_m3RecordBacktraces +#define pushBacktraceFrame() (PushBacktraceFrame (_mem->runtime, _pc - 1)) +#define fillBacktraceFrame(FUNCTION) (FillBacktraceFunctionInfo (_mem->runtime, function)) + +#define newTrap(err) return (pushBacktraceFrame (), err) +#define forwardTrap(err) return err +# else +#define pushBacktraceFrame() do {} while (0) +#define fillBacktraceFrame(FUNCTION) do {} while (0) + +#define newTrap(err) return err +#define forwardTrap(err) return err +# endif + d_m3RetSig Call (d_m3OpSig) { +# if d_m3RecordBacktraces + if (_mem) + ClearBacktrace (_mem->runtime); +# endif // d_m3RecordBacktraces + m3ret_t possible_trap = m3_Yield (); if (UNLIKELY(possible_trap)) return possible_trap; @@ -482,13 +501,15 @@ d_m3Op (Call) m3stack_t sp = _sp + stackOffset; m3ret_t r = Call (callPC, sp, _mem, d_m3OpDefaultArgs); + _mem = memory->mallocated; if (r == 0) - { - _mem = memory->mallocated; nextOp (); + else + { + pushBacktraceFrame (); + forwardTrap (r); } - else trapOp (r); } @@ -518,11 +539,14 @@ d_m3Op (CallIndirect) if (not r) { r = Call (function->compiled, sp, _mem, d_m3OpDefaultArgs); + _mem = memory->mallocated; if (not r) - { - _mem = memory->mallocated; nextOpDirect (); + else + { + pushBacktraceFrame (); + forwardTrap (r); } } } @@ -532,7 +556,9 @@ d_m3Op (CallIndirect) } else r = m3Err_trapTableIndexOutOfRange; - trapOp (r); + if (r) + newTrap (r); + else forwardTrap (r); } @@ -544,6 +570,7 @@ d_m3Op (CallRawFunction) ctx.function = immediate (IM3Function); ctx.userdata = immediate (void *); u64* const sp = ((u64*)_sp); + IM3Memory memory = m3MemInfo (_mem); IM3Runtime runtime = m3MemRuntime(_mem); @@ -594,7 +621,11 @@ d_m3Op (CallRawFunction) } #endif - trapOp (possible_trap); + if (possible_trap) { + _mem = memory->mallocated; + pushBacktraceFrame (); + } + forwardTrap (possible_trap); } @@ -659,7 +690,7 @@ d_m3Op (Compile) } else ReportError2 (function, result); - trapOp (result); + newTrap (result); } @@ -669,6 +700,7 @@ d_m3Op (Entry) d_m3ClearRegisters IM3Function function = immediate (IM3Function); + IM3Memory memory = m3MemInfo (_mem); #if d_m3SkipStackCheck if (true) @@ -692,6 +724,8 @@ d_m3Op (Entry) } m3ret_t r = nextOpImpl (); + // _mem needs to be valid for a potential fillBacktraceFrame call later + _mem = memory->mallocated; # if d_m3LogExec char str [100] = { '!', 0 }; @@ -705,9 +739,11 @@ d_m3Op (Entry) printf (" ** %s %p\n", GetFunctionName(function), _sp); # endif - return r; + if (r) + fillBacktraceFrame (); + forwardTrap (r); } - else trapOp (m3Err_trapStackOverflow); + else newTrap (m3Err_trapStackOverflow); } @@ -730,7 +766,7 @@ d_m3Op (Loop) } while (r == _pc); - trapOp (r); + forwardTrap (r); } @@ -993,7 +1029,7 @@ d_m3Select_f (f64, _fp0, s, slot (i32)) d_m3Op (Return) { m3StackCheck(); - trapOp (m3Err_none); + return m3Err_none; } @@ -1054,7 +1090,7 @@ d_m3Op (ContinueLoop) // has the potential to increase its native-stack usage. (don't forget ContinueLoopIf too.) void * loopId = immediate (void *); - trapOp (loopId); + return loopId; } @@ -1065,7 +1101,7 @@ d_m3Op (ContinueLoopIf) if (condition) { - trapOp (loopId); + return loopId; } else nextOp (); } @@ -1089,20 +1125,20 @@ d_m3Op (Const64) d_m3Op (Unsupported) { m3log (exec, "*** unsupported ***"); - trapOp ("unsupported instruction executed"); + newTrap ("unsupported instruction executed"); } d_m3Op (Unreachable) { m3log (exec, "*** trapping ***"); m3StackCheck(); - trapOp (m3Err_trapUnreachable); + newTrap (m3Err_trapUnreachable); } d_m3Op (End) { m3StackCheck(); - trapOp (m3Err_none); + return m3Err_none; } @@ -1150,11 +1186,11 @@ d_m3Op (SetGlobal_f64) #endif #ifdef DEBUG - #define d_outOfBounds trapOp (ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ + #define d_outOfBounds newTrap (ErrorRuntime (m3Err_trapOutOfBoundsMemoryAccess, \ _mem->runtime, "memory size: %zu; access offset: %zu", \ _mem->length, operand)) #else - #define d_outOfBounds trapOp (m3Err_trapOutOfBoundsMemoryAccess) + #define d_outOfBounds newTrap (m3Err_trapOutOfBoundsMemoryAccess) #endif // memcpy here is to support non-aligned access on some platforms. diff --git a/source/m3_exec_defs.h b/source/m3_exec_defs.h index f19aa81..7bb4e81 100644 --- a/source/m3_exec_defs.h +++ b/source/m3_exec_defs.h @@ -45,8 +45,6 @@ typedef m3ret_t (vectorcall * IM3Operation) (d_m3OpSig); #define nextOpDirect() return nextOpImpl() #define jumpOpDirect(PC) return jumpOpImpl((pc_t)(PC)) -#define trapOp(err) return (err) - d_m3EndExternC #endif // m3_exec_defs_h diff --git a/source/m3_math_utils.h b/source/m3_math_utils.h index 1a493c1..8c1b091 100644 --- a/source/m3_math_utils.h +++ b/source/m3_math_utils.h @@ -153,27 +153,27 @@ u64 rotr64(u64 n, unsigned c) { * Integer Div, Rem */ -#define OP_DIV_U(RES, A, B) \ - if (UNLIKELY(B == 0)) trapOp (m3Err_trapDivisionByZero); \ +#define OP_DIV_U(RES, A, B) \ + if (UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ RES = A / B; -#define OP_REM_U(RES, A, B) \ - if (UNLIKELY(B == 0)) trapOp (m3Err_trapDivisionByZero); \ +#define OP_REM_U(RES, A, B) \ + if (UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ RES = A % B; // 2's complement detection #if (INT_MIN != -INT_MAX) - #define OP_DIV_S(RES, A, B, TYPE_MIN) \ - if (UNLIKELY(B == 0)) trapOp (m3Err_trapDivisionByZero); \ - if (UNLIKELY(B == -1 and A == TYPE_MIN)) { \ - trapOp (m3Err_trapIntegerOverflow); \ - } \ + #define OP_DIV_S(RES, A, B, TYPE_MIN) \ + if (UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ + if (UNLIKELY(B == -1 and A == TYPE_MIN)) { \ + newTrap (m3Err_trapIntegerOverflow); \ + } \ RES = A / B; - #define OP_REM_S(RES, A, B, TYPE_MIN) \ - if (UNLIKELY(B == 0)) trapOp (m3Err_trapDivisionByZero); \ - if (UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ + #define OP_REM_S(RES, A, B, TYPE_MIN) \ + if (UNLIKELY(B == 0)) newTrap (m3Err_trapDivisionByZero); \ + if (UNLIKELY(B == -1 and A == TYPE_MIN)) RES = 0; \ else RES = A % B; #else @@ -189,10 +189,10 @@ u64 rotr64(u64 n, unsigned c) { #define OP_TRUNC(RES, A, TYPE, RMIN, RMAX) \ if (UNLIKELY(isnan(A))) { \ - trapOp (m3Err_trapIntegerConversion); \ + newTrap (m3Err_trapIntegerConversion); \ } \ if (UNLIKELY(A <= RMIN or A >= RMAX)) { \ - trapOp (m3Err_trapIntegerOverflow); \ + newTrap (m3Err_trapIntegerOverflow); \ } \ RES = (TYPE)A; diff --git a/source/m3_module.c b/source/m3_module.c index 8a6c662..c78a0d6 100644 --- a/source/m3_module.c +++ b/source/m3_module.c @@ -100,3 +100,12 @@ IM3Function Module_GetFunction (IM3Module i_module, u32 i_functionIndex) return func; } + + +const char* m3_GetModuleName (IM3Module i_module) +{ + if (!i_module || !i_module->name) + return ""; + + return i_module->name; +} diff --git a/source/m3_parse.c b/source/m3_parse.c index 7421101..369bc45 100644 --- a/source/m3_parse.c +++ b/source/m3_parse.c @@ -558,6 +558,9 @@ _ (m3Alloc (& module, M3Module, 1)); const u8 * pos = i_bytes; const u8 * end = pos + i_numBytes; + module->wasmStart = pos; + module->wasmEnd = end; + u32 magic, version; _ (Read_u32 (& magic, & pos, end)); _ (Read_u32 (& version, & pos, end)); diff --git a/source/wasm3.h b/source/wasm3.h index 57a81d4..42b7537 100644 --- a/source/wasm3.h +++ b/source/wasm3.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -44,6 +45,24 @@ typedef struct M3ErrorInfo const char * message; } M3ErrorInfo; +typedef struct M3BacktraceFrame +{ + uint64_t moduleOffset; + IM3Module module; + IM3Function function; + + struct M3BacktraceFrame * next; +} +M3BacktraceFrame; + +typedef struct M3BacktraceInfo +{ + bool backtraceTruncated; // true if an allocation failure occurred when writing backtrace + M3BacktraceFrame * frames; + M3BacktraceFrame * lastFrame; +} +M3BacktraceInfo; + typedef enum M3ValueType { @@ -208,6 +227,8 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") M3RawCall i_function, const void * i_userdata); + const char* m3_GetModuleName (IM3Module i_module); + //------------------------------------------------------------------------------------------------------------------------------- // functions //------------------------------------------------------------------------------------------------------------------------------- @@ -236,6 +257,8 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") void m3_GetErrorInfo (IM3Runtime i_runtime, M3ErrorInfo* o_info); void m3_ResetErrorInfo (IM3Runtime i_runtime); + const char* m3_GetFunctionName (IM3Function i_function); + //------------------------------------------------------------------------------------------------------------------------------- // debug info //------------------------------------------------------------------------------------------------------------------------------- @@ -244,6 +267,11 @@ d_m3ErrorConst (trapStackOverflow, "[trap] stack overflow") void m3_PrintM3Info (void); void m3_PrintProfilerInfo (void); + // The runtime owns the backtrace, do not free the backtrace you obtain + bool m3_BacktraceEnabled (void); + M3BacktraceInfo * m3_GetBacktrace (IM3Runtime i_runtime); // Returns NULL if backtrace is not enabled + uint32_t m3_GetBacktraceStr (IM3Runtime i_runtime, char* o_buffer, uint32_t i_bufferSize); + #if defined(__cplusplus) } #endif