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 <vshymanskyi@gmail.com>
extensions
Tyler Zhang 3 years ago committed by GitHub
parent 9737dbf3cd
commit d62d408b4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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);

@ -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

@ -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

@ -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

@ -86,6 +86,7 @@ typedef struct
bytes_t wasm;
bytes_t wasmEnd;
bytes_t lastOpcodeStart;
M3CompilationScope block;

@ -57,6 +57,9 @@
# define d_m3ProfilerSlotMask 0xFFFF
# endif
# ifndef d_m3RecordBacktraces
# define d_m3RecordBacktraces 0
# endif
// profiling and tracing ------------------------------------------------------

@ -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

@ -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

@ -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);
}
}

@ -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 "<unknown>";
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
}

@ -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;

@ -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.

@ -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

@ -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;

@ -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 "<unknown>";
return i_module->name;
}

@ -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));

@ -15,6 +15,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <stdarg.h>
@ -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

Loading…
Cancel
Save