diff --git a/platforms/python/examples/coremark.py b/platforms/python/examples/coremark.py new file mode 100755 index 0000000..f9b2454 --- /dev/null +++ b/platforms/python/examples/coremark.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +import wasm3 +import os, time + +scriptpath = os.path.dirname(os.path.realpath(__file__)) +wasm_fn = os.path.join(scriptpath, "./wasm/coremark-minimal.wasm") + +print("Initializing Wasm3 engine...") + +def clock_ms(): + return int(round(time.time() * 1000)) + +env = wasm3.Environment() +rt = env.new_runtime(4096) + +with open(wasm_fn, "rb") as f: + mod = env.parse_module(f.read()) + rt.load(mod) + mod.link_function("env", "clock_ms", "i()", clock_ms) + +# Gas metering will only apply to metered (pre-instrumented) modules +mod.gasLimit = 200_000_000 + +wasm_run = rt.find_function("run") + +print("Running CoreMark 1.0...") +res = wasm_run() + +if res > 1: + print(f"Result: {res:.3f}") +else: + print("Error") + +if mod.gasUsed: + print(f"Gas used: {mod.gasUsed}") + diff --git a/platforms/python/m3module.c b/platforms/python/m3module.c index d283c9f..6ad08ca 100644 --- a/platforms/python/m3module.c +++ b/platforms/python/m3module.c @@ -20,6 +20,9 @@ typedef struct { PyObject_HEAD m3_environment *env; IM3Module m; + //bool is_gas_metered; + int64_t total_gas; + int64_t current_gas; } m3_module; typedef struct { @@ -117,6 +120,7 @@ M3_Environment_parse_module(m3_environment *env, PyObject *bytes) Py_INCREF(env); self->env = env; self->m = m; + self->total_gas = self->current_gas = 0; return self; } @@ -200,6 +204,40 @@ Module_name(m3_module *self, void * closure) return PyUnicode_FromString(m3_GetModuleName(self->m)); } +static int +Module_setGasLimit(m3_module *self, PyObject *value, void * closure) +{ + self->total_gas = PyFloat_AsDouble(value)*10000.0; + self->current_gas = self->total_gas; + return 0; +} + +static PyObject * +Module_getGasLimit(m3_module *self, void * closure) +{ + return PyFloat_FromDouble((double)(self->total_gas)/10000.0); +} + +static PyObject * +Module_getGasUsed(m3_module *self, void * closure) +{ + return PyFloat_FromDouble((double)(self->total_gas - self->current_gas)/10000.0); +} + +m3ApiRawFunction(metering_usegas) +{ + m3ApiGetArg (int32_t, gas) + + m3_module *mod = (m3_module *)(_ctx->userdata); + + mod->current_gas -= gas; + + if (UNLIKELY(mod->current_gas < 0)) { + m3ApiTrap("[trap] Out of gas"); + } + m3ApiSuccess(); +} + m3ApiRawFunction(CallImport) { PyObject *pFunc = (PyObject *)(_ctx->userdata); @@ -266,12 +304,23 @@ M3_Module_link_function(m3_module *self, PyObject *args) if (err && err != m3Err_functionLookupFailed) { return formatError(PyExc_RuntimeError, m3_GetModuleRuntime(self->m), err); } + + err = m3_LinkRawFunctionEx (self->m, "metering", "usegas", "v(i)", &metering_usegas, self); + /*if (!err) { + self->is_gas_metered = true; + }*/ + if (err && err != m3Err_functionLookupFailed) { + return formatError(PyExc_RuntimeError, m3_GetModuleRuntime(self->m), err); + } + Py_INCREF(pFunc); Py_RETURN_NONE; } static PyGetSetDef M3_Module_properties[] = { - {"name", (getter) Module_name, NULL, "module name", NULL}, + {"name", (getter) Module_name, NULL, "module name", NULL}, + {"gasLimit", (getter) Module_getGasLimit, (setter) Module_setGasLimit, "gas limit for metered modules", NULL}, + {"gasUsed", (getter) Module_getGasUsed, NULL, "gas used", NULL}, {0}, }; diff --git a/platforms/python/setup.py b/platforms/python/setup.py index effac01..afc1d90 100755 --- a/platforms/python/setup.py +++ b/platforms/python/setup.py @@ -21,7 +21,7 @@ setup( ext_modules=[ Extension('wasm3', sources=SOURCES, include_dirs=['m3'], - extra_compile_args=['-g', '-O3']) + extra_compile_args=['-g0', '-O3', '-DDEBUG', '-DNASSERTS']) ], classifiers = [