[Tarantool-patches] [PATCH v4 2/2] misc: add C and Lua API for platform metrics
Sergey Ostanevich
sergos at tarantool.org
Fri Oct 9 17:45:07 MSK 2020
Hi!
Thanks for the patch!
I think it introduces a new user-visible interface, so we need a
@TarantoolBot document entry here?
Otherwise LGTM.
Sergos
On 05 окт 09:30, Sergey Kaplun wrote:
> This patch introduces both C and Lua API for LuaJIT platform
> metrics implemented in scope of the previous patch. New <lmisclib.h>
> header provides <luaM_metrics> C interface that fills the given
> <luam_metrics> structure with the platform metrics. Additionally
> <misc> module is loaded to Lua space and provides <getmetrics>
> method that yields the corresponding metrics via table.
>
> Part of tarantool/tarantool#5187
> ---
> Makefile | 2 +-
> src/Makefile | 5 +-
> src/Makefile.dep | 14 +-
> src/lib_init.c | 2 +
> src/lib_misc.c | 72 +++
> src/lj_mapi.c | 61 +++
> src/ljamalg.c | 2 +
> src/lmisclib.h | 64 +++
> src/luaconf.h | 1 +
> test/clib-misclib-getmetrics.test.lua | 174 ++++++++
> test/clib-misclib-getmetrics/CMakeLists.txt | 1 +
> test/clib-misclib-getmetrics/testgetmetrics.c | 242 ++++++++++
> test/lib-misc-getmetrics.test.lua | 418 ++++++++++++++++++
> 13 files changed, 1050 insertions(+), 8 deletions(-)
> create mode 100644 src/lib_misc.c
> create mode 100644 src/lj_mapi.c
> create mode 100644 src/lmisclib.h
> create mode 100755 test/clib-misclib-getmetrics.test.lua
> create mode 100644 test/clib-misclib-getmetrics/CMakeLists.txt
> create mode 100644 test/clib-misclib-getmetrics/testgetmetrics.c
> create mode 100755 test/lib-misc-getmetrics.test.lua
>
> diff --git a/Makefile b/Makefile
> index 0f93308..4a56917 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -84,7 +84,7 @@ FILE_A= libluajit.a
> FILE_SO= libluajit.so
> FILE_MAN= luajit.1
> FILE_PC= luajit.pc
> -FILES_INC= lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h
> +FILES_INC= lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h lmisclib.h
> FILES_JITLIB= bc.lua bcsave.lua dump.lua p.lua v.lua zone.lua \
> dis_x86.lua dis_x64.lua dis_arm.lua dis_arm64.lua \
> dis_arm64be.lua dis_ppc.lua dis_mips.lua dis_mipsel.lua \
> diff --git a/src/Makefile b/src/Makefile
> index 827d4a4..2786348 100644
> --- a/src/Makefile
> +++ b/src/Makefile
> @@ -480,13 +480,14 @@ LJVM_BOUT= $(LJVM_S)
> LJVM_MODE= elfasm
>
> LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \
> - lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o lib_ffi.o
> + lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o lib_ffi.o \
> + lib_misc.o
> LJLIB_C= $(LJLIB_O:.o=.c)
>
> LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \
> lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
> lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \
> - lj_strfmt.o lj_strfmt_num.o lj_api.o lj_profile.o \
> + lj_strfmt.o lj_strfmt_num.o lj_api.o lj_mapi.o lj_profile.o \
> lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \
> lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
> lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \
> diff --git a/src/Makefile.dep b/src/Makefile.dep
> index 2b1cb5e..556314e 100644
> --- a/src/Makefile.dep
> +++ b/src/Makefile.dep
> @@ -18,7 +18,7 @@ lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
> lj_ctype.h lj_cparse.h lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h \
> lj_ccallback.h lj_clib.h lj_strfmt.h lj_ff.h lj_ffdef.h lj_lib.h \
> lj_libdef.h
> -lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h
> +lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lmisclib.h lj_arch.h
> lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
> lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_state.h \
> lj_strfmt.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h
> @@ -29,6 +29,8 @@ lib_jit.o: lib_jit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
> lj_vm.h lj_vmevent.h lj_lib.h luajit.h lj_libdef.h
> lib_math.o: lib_math.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
> lj_def.h lj_arch.h lj_lib.h lj_vm.h lj_libdef.h
> +lib_misc.o: lib_misc.c lua.h luaconf.h lmisclib.h lj_obj.h lj_def.h lj_arch.h \
> + lj_str.h lj_tab.h lj_lib.h lj_libdef.h
> lib_os.o: lib_os.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
> lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_lib.h \
> lj_libdef.h
> @@ -137,6 +139,8 @@ lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \
> lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
> lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_func.h \
> lj_frame.h lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h
> +lj_mapi.o: lj_mapi.c lua.h luaconf.h lmisclib.h lj_obj.h lj_def.h lj_arch.h \
> + lj_dispatch.h lj_bc.h lj_jit.h lj_ir.h
> lj_mcode.o: lj_mcode.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
> lj_gc.h lj_err.h lj_errmsg.h lj_jit.h lj_ir.h lj_mcode.h lj_trace.h \
> lj_dispatch.h lj_bc.h lj_traceerr.h lj_vm.h
> @@ -215,9 +219,9 @@ ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \
> lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h lj_debug.c \
> lj_state.c lj_lex.h lj_alloc.h luajit.h lj_dispatch.c lj_ccallback.h \
> lj_profile.h lj_vmevent.c lj_vmevent.h lj_vmmath.c lj_strscan.c \
> - lj_strfmt.c lj_strfmt_num.c lj_api.c lj_profile.c lj_lex.c lualib.h \
> - lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h lj_bcwrite.c lj_load.c \
> - lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h \
> + lj_strfmt.c lj_strfmt_num.c lj_api.c lj_mapi.c lmisclib.h lj_profile.c \
> + lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h lj_bcwrite.c \
> + lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h \
> lj_ccallback.c lj_target.h lj_target_*.h lj_mcode.h lj_carith.c \
> lj_carith.h lj_clib.c lj_clib.h lj_cparse.c lj_cparse.h lj_lib.c lj_ir.c \
> lj_ircall.h lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h \
> @@ -227,7 +231,7 @@ ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \
> lj_emit_*.h lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c \
> lib_aux.c lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c \
> lib_io.c lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c \
> - lib_ffi.c lib_init.c
> + lib_ffi.c lib_misc.c lib_init.c
> luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h
> host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \
> lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \
> diff --git a/src/lib_init.c b/src/lib_init.c
> index 2ed370e..664aa7d 100644
> --- a/src/lib_init.c
> +++ b/src/lib_init.c
> @@ -12,6 +12,7 @@
> #include "lua.h"
> #include "lauxlib.h"
> #include "lualib.h"
> +#include "lmisclib.h"
>
> #include "lj_arch.h"
>
> @@ -26,6 +27,7 @@ static const luaL_Reg lj_lib_load[] = {
> { LUA_DBLIBNAME, luaopen_debug },
> { LUA_BITLIBNAME, luaopen_bit },
> { LUA_JITLIBNAME, luaopen_jit },
> + { LUAM_MISCLIBNAME, luaopen_misc },
> { NULL, NULL }
> };
>
> diff --git a/src/lib_misc.c b/src/lib_misc.c
> new file mode 100644
> index 0000000..ef11237
> --- /dev/null
> +++ b/src/lib_misc.c
> @@ -0,0 +1,72 @@
> +/*
> +** Miscellaneous Lua extensions library.
> +**
> +** Major portions taken verbatim or adapted from the LuaVela interpreter.
> +** Copyright (C) 2015-2019 IPONWEB Ltd.
> +*/
> +
> +#define lib_misc_c
> +#define LUA_LIB
> +
> +#include "lua.h"
> +#include "lmisclib.h"
> +
> +#include "lj_obj.h"
> +#include "lj_str.h"
> +#include "lj_tab.h"
> +#include "lj_lib.h"
> +
> +/* ------------------------------------------------------------------------ */
> +
> +static LJ_AINLINE void setnumfield(struct lua_State *L, GCtab *t,
> + const char *name, int64_t val)
> +{
> + setnumV(lj_tab_setstr(L, t, lj_str_newz(L, name)), (double)val);
> +}
> +
> +#define LJLIB_MODULE_misc
> +
> +LJLIB_CF(misc_getmetrics)
> +{
> + struct luam_Metrics metrics;
> + lua_createtable(L, 0, 22);
> + GCtab *m = tabV(L->top - 1);
> +
> + luaM_metrics(L, &metrics);
> +
> + setnumfield(L, m, "strhash_hit", metrics.strhash_hit);
> + setnumfield(L, m, "strhash_miss", metrics.strhash_miss);
> +
> + setnumfield(L, m, "gc_strnum", metrics.gc_strnum);
> + setnumfield(L, m, "gc_tabnum", metrics.gc_tabnum);
> + setnumfield(L, m, "gc_udatanum", metrics.gc_udatanum);
> + setnumfield(L, m, "gc_cdatanum", metrics.gc_cdatanum);
> +
> + setnumfield(L, m, "gc_total", metrics.gc_total);
> + setnumfield(L, m, "gc_freed", metrics.gc_freed);
> + setnumfield(L, m, "gc_allocated", metrics.gc_allocated);
> +
> + setnumfield(L, m, "gc_steps_pause", metrics.gc_steps_pause);
> + setnumfield(L, m, "gc_steps_propagate", metrics.gc_steps_propagate);
> + setnumfield(L, m, "gc_steps_atomic", metrics.gc_steps_atomic);
> + setnumfield(L, m, "gc_steps_sweepstring", metrics.gc_steps_sweepstring);
> + setnumfield(L, m, "gc_steps_sweep", metrics.gc_steps_sweep);
> + setnumfield(L, m, "gc_steps_finalize", metrics.gc_steps_finalize);
> +
> + setnumfield(L, m, "jit_snap_restore", metrics.jit_snap_restore);
> + setnumfield(L, m, "jit_trace_abort", metrics.jit_trace_abort);
> + setnumfield(L, m, "jit_mcode_size", metrics.jit_mcode_size);
> + setnumfield(L, m, "jit_trace_num", metrics.jit_trace_num);
> +
> + return 1;
> +}
> +
> +/* ------------------------------------------------------------------------ */
> +
> +#include "lj_libdef.h"
> +
> +LUALIB_API int luaopen_misc(struct lua_State *L)
> +{
> + LJ_LIB_REG(L, LUAM_MISCLIBNAME, misc);
> + return 1;
> +}
> diff --git a/src/lj_mapi.c b/src/lj_mapi.c
> new file mode 100644
> index 0000000..7645a44
> --- /dev/null
> +++ b/src/lj_mapi.c
> @@ -0,0 +1,61 @@
> +/*
> +** Miscellaneous public C API extensions.
> +**
> +** Major portions taken verbatim or adapted from the LuaVela.
> +** Copyright (C) 2015-2019 IPONWEB Ltd.
> +*/
> +
> +#include "lua.h"
> +#include "lmisclib.h"
> +
> +#include "lj_obj.h"
> +#include "lj_dispatch.h"
> +
> +#if LJ_HASJIT
> +#include "lj_jit.h"
> +#endif
> +
> +LUAMISC_API void luaM_metrics(lua_State *L, struct luam_Metrics *metrics)
> +{
> + lua_assert(metrics != NULL);
> + global_State *g = G(L);
> + GCState *gc = &g->gc;
> +#if LJ_HASJIT
> + jit_State *J = G2J(g);
> +#endif
> +
> + metrics->strhash_hit = g->strhash_hit;
> + metrics->strhash_miss = g->strhash_miss;
> +
> + metrics->gc_strnum = g->strnum;
> + metrics->gc_tabnum = gc->tabnum;
> + metrics->gc_udatanum = gc->udatanum;
> +#if LJ_HASFFI
> + metrics->gc_cdatanum = gc->cdatanum;
> +#else
> + metrics->gc_cdatanum = 0;
> +#endif
> +
> + metrics->gc_total = gc->total;
> + metrics->gc_freed = gc->freed;
> + metrics->gc_allocated = gc->allocated;
> +
> + metrics->gc_steps_pause = gc->state_count[GCSpause];
> + metrics->gc_steps_propagate = gc->state_count[GCSpropagate];
> + metrics->gc_steps_atomic = gc->state_count[GCSatomic];
> + metrics->gc_steps_sweepstring = gc->state_count[GCSsweepstring];
> + metrics->gc_steps_sweep = gc->state_count[GCSsweep];
> + metrics->gc_steps_finalize = gc->state_count[GCSfinalize];
> +
> +#if LJ_HASJIT
> + metrics->jit_snap_restore = J->nsnaprestore;
> + metrics->jit_trace_abort = J->ntraceabort;
> + metrics->jit_mcode_size = J->szallmcarea;
> + metrics->jit_trace_num = J->tracenum;
> +#else
> + metrics->jit_snap_restore = 0;
> + metrics->jit_trace_abort = 0;
> + metrics->jit_mcode_size = 0;
> + metrics->jit_trace_num = 0;
> +#endif
> +}
> diff --git a/src/ljamalg.c b/src/ljamalg.c
> index f1f2862..371bbb6 100644
> --- a/src/ljamalg.c
> +++ b/src/ljamalg.c
> @@ -48,6 +48,7 @@
> #include "lj_strfmt.c"
> #include "lj_strfmt_num.c"
> #include "lj_api.c"
> +#include "lj_mapi.c"
> #include "lj_profile.c"
> #include "lj_lex.c"
> #include "lj_parse.c"
> @@ -93,5 +94,6 @@
> #include "lib_bit.c"
> #include "lib_jit.c"
> #include "lib_ffi.c"
> +#include "lib_misc.c"
> #include "lib_init.c"
>
> diff --git a/src/lmisclib.h b/src/lmisclib.h
> new file mode 100644
> index 0000000..e2d1909
> --- /dev/null
> +++ b/src/lmisclib.h
> @@ -0,0 +1,64 @@
> +/*
> +** Miscellaneous public C API extensions.
> +**
> +** Major portions taken verbatim or adapted from the LuaVela.
> +** Copyright (C) 2015-2019 IPONWEB Ltd.
> +*/
> +
> +#ifndef _LMISCLIB_H
> +#define _LMISCLIB_H
> +
> +#include "lua.h"
> +
> +/* API for obtaining various platform metrics. */
> +
> +struct luam_Metrics {
> + /* Strings amount found in string hash instead of allocation of new one. */
> + size_t strhash_hit;
> + /* Strings amount allocated and put into string hash. */
> + size_t strhash_miss;
> +
> + /* Amount of allocated string objects. */
> + size_t gc_strnum;
> + /* Amount of allocated table objects. */
> + size_t gc_tabnum;
> + /* Amount of allocated udata objects. */
> + size_t gc_udatanum;
> + /* Amount of allocated cdata objects. */
> + size_t gc_cdatanum;
> +
> + /* Memory currently allocated. */
> + size_t gc_total;
> + /* Total amount of freed memory. */
> + size_t gc_freed;
> + /* Total amount of allocated memory. */
> + size_t gc_allocated;
> +
> + /* Count of incremental GC steps per state. */
> + size_t gc_steps_pause;
> + size_t gc_steps_propagate;
> + size_t gc_steps_atomic;
> + size_t gc_steps_sweepstring;
> + size_t gc_steps_sweep;
> + size_t gc_steps_finalize;
> +
> + /*
> + ** Overall number of snap restores (amount of guard assertions
> + ** leading to stopping trace executions and trace exits,
> + ** that are not stitching with other traces).
> + */
> + size_t jit_snap_restore;
> + /* Overall number of abort traces. */
> + size_t jit_trace_abort;
> + /* Total size of all allocated machine code areas. */
> + size_t jit_mcode_size;
> + /* Amount of JIT traces. */
> + unsigned int jit_trace_num;
> +};
> +
> +LUAMISC_API void luaM_metrics(lua_State *L, struct luam_Metrics *metrics);
> +
> +#define LUAM_MISCLIBNAME "misc"
> +LUALIB_API int luaopen_misc(lua_State *L);
> +
> +#endif /* _LMISCLIB_H */
> diff --git a/src/luaconf.h b/src/luaconf.h
> index 60cb928..8029040 100644
> --- a/src/luaconf.h
> +++ b/src/luaconf.h
> @@ -144,6 +144,7 @@
> #endif
>
> #define LUALIB_API LUA_API
> +#define LUAMISC_API LUA_API
>
> /* Support for internal assertions. */
> #if defined(LUA_USE_ASSERT) || defined(LUA_USE_APICHECK)
> diff --git a/test/clib-misclib-getmetrics.test.lua b/test/clib-misclib-getmetrics.test.lua
> new file mode 100755
> index 0000000..34adaba
> --- /dev/null
> +++ b/test/clib-misclib-getmetrics.test.lua
> @@ -0,0 +1,174 @@
> +#!/usr/bin/env tarantool
> +
> +local file = debug.getinfo(1, "S").source:sub(2)
> +local filepath = file:match("(.*/)")
> +local soext = jit.os == "OSX" and "dylib" or "so"
> +package.cpath = filepath..'clib-misclib-getmetrics/?.'..soext..";"
> + ..package.cpath
> +
> +local jit_opt_default_hotloop = 56
> +local jit_opt_default_hotexit = 10
> +local jit_opt_default_level = 3
> +local jit_opt_default_minstitch = 0
> +
> +local tap = require('tap')
> +
> +local test = tap.test("clib-misc-getmetrics")
> +test:plan(10)
> +
> +local testgetmetrics = require("testgetmetrics")
> +
> +test:ok(testgetmetrics.base())
> +test:ok(testgetmetrics.gc_allocated_freed())
> +test:ok(testgetmetrics.gc_steps())
> +
> +test:ok(testgetmetrics.objcount(function()
> + local table_new = require("table.new")
> + local ffi = require("ffi")
> +
> + jit.opt.start(0)
> +
> + local placeholder = {
> + str = {},
> + tab = {},
> + udata = {},
> + cdata = {},
> + }
> +
> + -- Separate objects creations to separate jit traces.
> + for i = 1, 1000 do
> + table.insert(placeholder.str, tostring(i))
> + end
> +
> + for i = 1, 1000 do
> + table.insert(placeholder.tab, table_new(i, 0))
> + end
> +
> + for i = 1, 1000 do
> + table.insert(placeholder.udata, newproxy())
> + end
> +
> + for i = 1, 1000 do
> + -- Check counting of VLA/VLS/aligned cdata.
> + table.insert(placeholder.cdata, ffi.new("char[?]", 4))
> + end
> +
> + for i = 1, 1000 do
> + -- Check counting of non-VLA/VLS/aligned cdata.
> + table.insert(placeholder.cdata, ffi.new("uint64_t", i))
> + end
> +
> + placeholder = nil
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level)
> +end))
> +
> +-- Compiled loop with a direct exit to the interpreter.
> +test:ok(testgetmetrics.snap_restores(function()
> + jit.opt.start(0, "hotloop=2")
> +
> + local old_metrics = misc.getmetrics()
> +
> + local sum = 0
> + for i = 1, 20 do
> + sum = sum + i
> + end
> +
> + local new_metrics = misc.getmetrics()
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level, "hotloop="..jit_opt_default_hotloop)
> +
> + -- A single snapshot restoration happened on loop finish.
> + return 1
> +end))
> +
> +-- Compiled loop with a side exit which does not get compiled.
> +test:ok(testgetmetrics.snap_restores(function()
> + jit.opt.start(0, "hotloop=2", "hotexit=2", "minstitch=15")
> +
> + local function foo(i)
> + -- math.fmod is not yet compiled!
> + return i <= 5 and i or math.fmod(i, 11)
> + end
> +
> + local sum = 0
> + for i = 1, 10 do
> + sum = sum + foo(i)
> + end
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level, "hotloop="..jit_opt_default_hotloop,
> + "hotexit="..jit_opt_default_hotexit,
> + "minstitch="..jit_opt_default_minstitch)
> +
> + -- Side exits from the root trace could not get compiled.
> + return 5
> +end))
> +
> +-- Compiled loop with a side exit which gets compiled.
> +test:ok(testgetmetrics.snap_restores(function()
> + -- Optimization level is important here as `loop` optimization
> + -- may unroll the loop body and insert +1 side exit.
> + jit.opt.start(0, "hotloop=5", "hotexit=5")
> +
> + local function foo(i)
> + return i <= 10 and i or tostring(i)
> + end
> +
> + local sum = 0
> + for i = 1, 20 do
> + sum = sum + foo(i)
> + end
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level, "hotloop="..jit_opt_default_hotloop,
> + "hotexit="..jit_opt_default_hotexit)
> +
> + -- 5 side exits to the interpreter before trace gets hot
> + -- and compiled
> + -- 1 side exit on loop end
> + return 6
> +end))
> +
> +-- Compiled scalar trace with a direct exit to the interpreter.
> +test:ok(testgetmetrics.snap_restores(function()
> + -- For calls it will be 2 * hotloop (see lj_dispatch.{c,h}
> + -- and hotcall at vm_*.dasc).
> + jit.opt.start(3, "hotloop=2", "hotexit=3")
> +
> + local function foo(i)
> + return i <= 15 and i or tostring(i)
> + end
> +
> + foo(1) -- interp only
> + foo(2) -- interp only
> + foo(3) -- interp only
> + foo(4) -- compile trace during this call
> + foo(5) -- follow the trace
> + foo(6) -- follow the trace
> + foo(7) -- follow the trace
> + foo(8) -- follow the trace
> + foo(9) -- follow the trace
> + foo(10) -- follow the trace
> +
> + -- Simply 2 side exits from the trace:
> + foo(20)
> + foo(21)
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level, "hotloop="..jit_opt_default_hotloop,
> + "hotexit="..jit_opt_default_hotexit)
> + return 2
> +end))
> +
> +test:ok(testgetmetrics.strhash())
> +
> +test:ok(testgetmetrics.tracenum_base(function()
> + local sum = 0
> + for i = 1, 200 do
> + sum = sum + i
> + end
> + -- Compiled only 1 loop as new trace.
> + return 1
> +end))
> diff --git a/test/clib-misclib-getmetrics/CMakeLists.txt b/test/clib-misclib-getmetrics/CMakeLists.txt
> new file mode 100644
> index 0000000..e7cc8f8
> --- /dev/null
> +++ b/test/clib-misclib-getmetrics/CMakeLists.txt
> @@ -0,0 +1 @@
> +build_lualib(testgetmetrics testgetmetrics.c)
> diff --git a/test/clib-misclib-getmetrics/testgetmetrics.c b/test/clib-misclib-getmetrics/testgetmetrics.c
> new file mode 100644
> index 0000000..32802d2
> --- /dev/null
> +++ b/test/clib-misclib-getmetrics/testgetmetrics.c
> @@ -0,0 +1,242 @@
> +#include <lua.h>
> +#include <luajit.h>
> +#include <lauxlib.h>
> +
> +#include <lmisclib.h>
> +
> +#include <assert.h>
> +
> +static int base(lua_State *L)
> +{
> + struct luam_Metrics metrics;
> + luaM_metrics(L, &metrics);
> +
> + /* Just check API. */
> + assert((ssize_t)metrics.strhash_hit >= 0);
> + assert((ssize_t)metrics.strhash_miss >= 0);
> +
> + assert((ssize_t)metrics.gc_strnum >= 0);
> + assert((ssize_t)metrics.gc_tabnum >= 0);
> + assert((ssize_t)metrics.gc_udatanum >= 0);
> + assert((ssize_t)metrics.gc_cdatanum >= 0);
> +
> + assert((ssize_t)metrics.gc_total >= 0);
> + assert((ssize_t)metrics.gc_freed >= 0);
> + assert((ssize_t)metrics.gc_allocated >= 0);
> +
> + assert((ssize_t)metrics.gc_steps_pause >= 0);
> + assert((ssize_t)metrics.gc_steps_propagate >= 0);
> + assert((ssize_t)metrics.gc_steps_atomic >= 0);
> + assert((ssize_t)metrics.gc_steps_sweepstring >= 0);
> + assert((ssize_t)metrics.gc_steps_sweep >= 0);
> + assert((ssize_t)metrics.gc_steps_finalize >= 0);
> +
> + assert((ssize_t)metrics.jit_snap_restore >= 0);
> + assert((ssize_t)metrics.jit_trace_abort >= 0);
> + assert((ssize_t)metrics.jit_mcode_size >= 0);
> + assert((ssize_t)metrics.jit_trace_num >= 0);
> +
> + lua_pushboolean(L, 1);
> + return 1;
> +}
> +
> +static int gc_allocated_freed(lua_State *L)
> +{
> + struct luam_Metrics oldm, newm;
> + /* Force up garbage collect all dead objects. */
> + lua_gc(L, LUA_GCCOLLECT, 0);
> +
> + luaM_metrics(L, &oldm);
> + /* Simple garbage generation. */
> + if (luaL_dostring(L, "local i = 0 for j = 1, 10 do i = i + j end"))
> + luaL_error(L, "failed to translate Lua code snippet");
> + lua_gc(L, LUA_GCCOLLECT, 0);
> + luaM_metrics(L, &newm);
> + assert(newm.gc_allocated - oldm.gc_allocated > 0);
> + assert(newm.gc_freed - oldm.gc_freed > 0);
> +
> + lua_pushboolean(L, 1);
> + return 1;
> +}
> +
> +static int gc_steps(lua_State *L)
> +{
> + struct luam_Metrics oldm, newm;
> + /*
> + * Some garbage has already happened before the next line,
> + * i.e. during fronted processing lua test chunk.
> + * Let's put a full garbage collection cycle on top
> + * of that, and confirm that non-null values are reported
> + * (we are not yet interested in actual numbers):
> + */
> + luaM_metrics(L, &oldm);
> + lua_gc(L, LUA_GCCOLLECT, 0);
> + assert(oldm.gc_steps_pause > 0);
> + assert(oldm.gc_steps_propagate > 0);
> + assert(oldm.gc_steps_atomic > 0);
> + assert(oldm.gc_steps_sweepstring > 0);
> + assert(oldm.gc_steps_sweep > 0);
> + /* Nothing to finalize, skipped. */
> + assert(oldm.gc_steps_finalize == 0);
> +
> + /*
> + * As long as we don't create new Lua objects
> + * consequent call should return the same values:
> + */
> + luaM_metrics(L, &newm);
> + assert(newm.gc_steps_pause - oldm.gc_steps_pause > 0);
> + assert(newm.gc_steps_propagate - oldm.gc_steps_propagate > 0);
> + assert(newm.gc_steps_atomic - oldm.gc_steps_atomic > 0);
> + assert(newm.gc_steps_sweepstring - oldm.gc_steps_sweepstring > 0);
> + assert(newm.gc_steps_sweep - oldm.gc_steps_sweep > 0);
> + /* Nothing to finalize, skipped. */
> + assert(newm.gc_steps_finalize == 0);
> + oldm = newm;
> +
> + /*
> + * Now the last phase: run full GC once and make sure that
> + * everything is being reported as expected:
> + */
> + lua_gc(L, LUA_GCCOLLECT, 0);
> + luaM_metrics(L, &newm);
> + assert(newm.gc_steps_pause - oldm.gc_steps_pause == 1);
> + assert(newm.gc_steps_propagate - oldm.gc_steps_propagate >= 1);
> + assert(newm.gc_steps_atomic - oldm.gc_steps_atomic == 1);
> + assert(newm.gc_steps_sweepstring - oldm.gc_steps_sweepstring >= 1);
> + assert(newm.gc_steps_sweep - oldm.gc_steps_sweep >= 1);
> + /* Nothing to finalize, skipped. */
> + assert(newm.gc_steps_finalize == 0);
> + oldm = newm;
> +
> + /*
> + * Now let's run three GC cycles to ensure that
> + * zero-to-one transition was not a lucky coincidence.
> + */
> + lua_gc(L, LUA_GCCOLLECT, 0);
> + lua_gc(L, LUA_GCCOLLECT, 0);
> + lua_gc(L, LUA_GCCOLLECT, 0);
> + luaM_metrics(L, &newm);
> + assert(newm.gc_steps_pause - oldm.gc_steps_pause == 3);
> + assert(newm.gc_steps_propagate - oldm.gc_steps_propagate >= 3);
> + assert(newm.gc_steps_atomic - oldm.gc_steps_atomic == 3);
> + assert(newm.gc_steps_sweepstring - oldm.gc_steps_sweepstring >= 3);
> + assert(newm.gc_steps_sweep - oldm.gc_steps_sweep >= 3);
> + /* Nothing to finalize, skipped. */
> + assert(newm.gc_steps_finalize == 0);
> +
> + lua_pushboolean(L, 1);
> + return 1;
> +}
> +
> +static int objcount(lua_State *L)
> +{
> + struct luam_Metrics oldm, newm;
> + int n = lua_gettop(L);
> + if (n != 1 || !lua_isfunction(L, 1))
> + luaL_error(L, "incorrect argument: 1 function is required");
> +
> + /* Force up garbage collect all dead objects. */
> + lua_gc(L, LUA_GCCOLLECT, 0);
> +
> + luaM_metrics(L, &oldm);
> + /* Generate garbage. */
> + lua_call(L, 0, 0);
> + lua_gc(L, LUA_GCCOLLECT, 0);
> + luaM_metrics(L, &newm);
> + assert(newm.gc_strnum - oldm.gc_strnum == 0);
> + assert(newm.gc_tabnum - oldm.gc_tabnum == 0);
> + assert(newm.gc_udatanum - oldm.gc_udatanum == 0);
> + assert(newm.gc_cdatanum - oldm.gc_cdatanum == 0);
> +
> + lua_pushboolean(L, 1);
> + return 1;
> +}
> +
> +static int snap_restores(lua_State *L)
> +{
> + struct luam_Metrics oldm, newm;
> + int n = lua_gettop(L);
> + if (n != 1 || !lua_isfunction(L, 1))
> + luaL_error(L, "incorrect arguments: 1 function is required");
> +
> + luaM_metrics(L, &oldm);
> + /* Generate snapshots. */
> + lua_call(L, 0, 1);
> + n = lua_gettop(L);
> + if (n != 1 || !lua_isnumber(L, 1))
> + luaL_error(L, "incorrect return value: 1 number is required");
> + size_t snap_restores = lua_tonumber(L, 1);
> + luaM_metrics(L, &newm);
> + assert(newm.jit_snap_restore - oldm.jit_snap_restore == snap_restores);
> +
> + lua_pushboolean(L, 1);
> + return 1;
> +}
> +
> +static int strhash(lua_State *L)
> +{
> + struct luam_Metrics oldm, newm;
> + lua_pushstring(L, "strhash_hit");
> + luaM_metrics(L, &oldm);
> + lua_pushstring(L, "strhash_hit");
> + lua_pushstring(L, "new_str");
> + luaM_metrics(L, &newm);
> + assert(newm.strhash_hit - oldm.strhash_hit == 1);
> + assert(newm.strhash_miss - oldm.strhash_miss == 1);
> + lua_pop(L, 3);
> + lua_pushboolean(L, 1);
> + return 1;
> +}
> +
> +static int tracenum_base(lua_State *L)
> +{
> + struct luam_Metrics metrics;
> + int n = lua_gettop(L);
> + if (n != 1 || !lua_isfunction(L, 1))
> + luaL_error(L, "incorrect arguments: 1 function is required");
> +
> + luaJIT_setmode(L, 0, LUAJIT_MODE_OFF);
> + luaJIT_setmode(L, 0, LUAJIT_MODE_FLUSH);
> + /* Force up garbage collect all dead objects. */
> + lua_gc(L, LUA_GCCOLLECT, 0);
> +
> + luaM_metrics(L, &metrics);
> + assert(metrics.jit_trace_num == 0);
> +
> + luaJIT_setmode(L, 0, LUAJIT_MODE_ON);
> + /* Generate traces. */
> + lua_call(L, 0, 1);
> + n = lua_gettop(L);
> + if (n != 1 || !lua_isnumber(L, 1))
> + luaL_error(L, "incorrect return value: 1 number is required");
> + size_t jit_trace_num = lua_tonumber(L, 1);
> + luaM_metrics(L, &metrics);
> + assert(metrics.jit_trace_num == jit_trace_num);
> +
> + luaJIT_setmode(L, 0, LUAJIT_MODE_FLUSH);
> + /* Force up garbage collect all dead objects. */
> + lua_gc(L, LUA_GCCOLLECT, 0);
> + luaM_metrics(L, &metrics);
> + assert(metrics.jit_trace_num == 0);
> +
> + luaJIT_setmode(L, 0, LUAJIT_MODE_ON);
> + lua_pushboolean(L, 1);
> + return 1;
> +}
> +
> +static const struct luaL_Reg testgetmetrics[] = {
> + {"base", base},
> + {"gc_allocated_freed", gc_allocated_freed},
> + {"gc_steps", gc_steps},
> + {"objcount", objcount},
> + {"snap_restores", snap_restores},
> + {"strhash", strhash},
> + {"tracenum_base", tracenum_base},
> + {NULL, NULL}
> +};
> +
> +LUA_API int luaopen_testgetmetrics(lua_State *L)
> +{
> + luaL_register(L, "testgetmetrics", testgetmetrics);
> + return 1;
> +}
> diff --git a/test/lib-misc-getmetrics.test.lua b/test/lib-misc-getmetrics.test.lua
> new file mode 100755
> index 0000000..2859e26
> --- /dev/null
> +++ b/test/lib-misc-getmetrics.test.lua
> @@ -0,0 +1,418 @@
> +#!/usr/bin/env tarantool
> +
> +-- This is a part of tarantool/luajit testing suite.
> +-- Major portions taken verbatim or adapted from the LuaVela testing suit.
> +-- Copyright (C) 2015-2019 IPONWEB Ltd.
> +
> +local tap = require('tap')
> +
> +local test = tap.test("lib-misc-getmetrics")
> +test:plan(10)
> +
> +local jit_opt_default_hotloop = 56
> +local jit_opt_default_hotexit = 10
> +local jit_opt_default_level = 3
> +local jit_opt_default_minstitch = 0
> +
> +-- Test Lua API.
> +test:test("base", function(subtest)
> + subtest:plan(19)
> + local metrics = misc.getmetrics()
> + subtest:ok(metrics.strhash_hit >= 0)
> + subtest:ok(metrics.strhash_miss >= 0)
> +
> + subtest:ok(metrics.gc_strnum >= 0)
> + subtest:ok(metrics.gc_tabnum >= 0)
> + subtest:ok(metrics.gc_udatanum >= 0)
> + subtest:ok(metrics.gc_cdatanum >= 0)
> +
> + subtest:ok(metrics.gc_total >= 0)
> + subtest:ok(metrics.gc_freed >= 0)
> + subtest:ok(metrics.gc_allocated >= 0)
> +
> + subtest:ok(metrics.gc_steps_pause >= 0)
> + subtest:ok(metrics.gc_steps_propagate >= 0)
> + subtest:ok(metrics.gc_steps_atomic >= 0)
> + subtest:ok(metrics.gc_steps_sweepstring >= 0)
> + subtest:ok(metrics.gc_steps_sweep >= 0)
> + subtest:ok(metrics.gc_steps_finalize >= 0)
> +
> + subtest:ok(metrics.jit_snap_restore >= 0)
> + subtest:ok(metrics.jit_trace_abort >= 0)
> + subtest:ok(metrics.jit_mcode_size >= 0)
> + subtest:ok(metrics.jit_trace_num >= 0)
> +end)
> +
> +test:test("gc-allocated-freed", function(subtest)
> + subtest:plan(1)
> +
> + -- Force up garbage collect all dead objects.
> + collectgarbage("collect")
> +
> + -- Bump getmetrincs table and string keys allocation.
> + local old_metrics = misc.getmetrics()
> +
> + -- Remember allocated size for getmetrics table.
> + old_metrics = misc.getmetrics()
> +
> + collectgarbage("collect")
> +
> + local new_metrics = misc.getmetrics()
> +
> + local diff_alloc = new_metrics.gc_allocated - old_metrics.gc_allocated
> + local getmetrics_alloc = diff_alloc
> +
> + -- Do not use test:ok to avoid extra allocated/freed objects.
> + assert(getmetrics_alloc > 0, "count allocated table for getmetrics")
> + old_metrics = new_metrics
> +
> + -- NB: Avoid operations that use internal global string buffer
> + -- (such as concatenation, string.format, table.concat)
> + -- while creating the string. Otherwise gc_freed/gc_allocated
> + -- relations will not be so straightforward.
> + local str = string.sub("Hello, world", 1, 5)
> + collectgarbage("collect")
> +
> + new_metrics = misc.getmetrics()
> +
> + diff_alloc = new_metrics.gc_allocated - old_metrics.gc_allocated
> + local diff_freed = new_metrics.gc_freed - old_metrics.gc_freed
> +
> + assert(diff_alloc > getmetrics_alloc,
> + "allocated str 'Hello' and table for getmetrics")
> + assert(diff_freed == getmetrics_alloc,
> + "freed old old_metrics")
> + old_metrics = new_metrics
> +
> + str = string.sub("Hello, world", 8, -1)
> +
> + new_metrics = misc.getmetrics()
> +
> + diff_alloc = new_metrics.gc_allocated - old_metrics.gc_allocated
> + diff_freed = new_metrics.gc_freed - old_metrics.gc_freed
> +
> + assert(diff_alloc > getmetrics_alloc,
> + "allocated str 'world' and table for getmetrics")
> + assert(diff_freed == 0, "nothing to free without collectgarbage")
> + old_metrics = new_metrics
> + collectgarbage("collect")
> +
> + new_metrics = misc.getmetrics()
> +
> + diff_alloc = new_metrics.gc_allocated - old_metrics.gc_allocated
> + diff_freed = new_metrics.gc_freed - old_metrics.gc_freed
> +
> + assert(diff_alloc == getmetrics_alloc,
> + "allocated last one table for getmetrics")
> + assert(diff_freed > 2 * getmetrics_alloc,
> + "freed str 'Hello' and 2 tables for getmetrics")
> + subtest:ok(true, "no assetion failed")
> +end)
> +
> +test:test("gc-steps", function(subtest)
> + subtest:plan(24)
> +
> + -- Some garbage has already created before the next line,
> + -- i.e. during fronted processing this chunk.
> + -- Let's put a full garbage collection cycle on top of that,
> + -- and confirm that non-null values are reported (we are not
> + -- yet interested in actual numbers):
> + collectgarbage("collect")
> + collectgarbage("stop")
> + local oldm = misc.getmetrics()
> + subtest:ok(oldm.gc_steps_pause > 0)
> + subtest:ok(oldm.gc_steps_propagate > 0)
> + subtest:ok(oldm.gc_steps_atomic > 0)
> + subtest:ok(oldm.gc_steps_sweepstring > 0)
> + subtest:ok(oldm.gc_steps_sweep > 0)
> + -- Nothing to finalize, skipped.
> + subtest:is(oldm.gc_steps_finalize, 0)
> +
> + -- As long as we stopped the GC, consequent call
> + -- should return the same values:
> + local newm = misc.getmetrics()
> + subtest:is(newm.gc_steps_pause - oldm.gc_steps_pause, 0)
> + subtest:is(newm.gc_steps_propagate - oldm.gc_steps_propagate, 0)
> + subtest:is(newm.gc_steps_atomic - oldm.gc_steps_atomic, 0)
> + subtest:is(newm.gc_steps_sweepstring - oldm.gc_steps_sweepstring, 0)
> + subtest:is(newm.gc_steps_sweep - oldm.gc_steps_sweep, 0)
> + -- Nothing to finalize, skipped.
> + subtest:is(newm.gc_steps_finalize, 0)
> + oldm = newm
> +
> + -- Now the last phase: run full GC once and make sure that
> + -- everything is being reported as expected:
> + collectgarbage("collect")
> + collectgarbage("stop")
> + newm = misc.getmetrics()
> + subtest:ok(newm.gc_steps_pause - oldm.gc_steps_pause == 1)
> + subtest:ok(newm.gc_steps_propagate - oldm.gc_steps_propagate >= 1)
> + subtest:ok(newm.gc_steps_atomic - oldm.gc_steps_atomic == 1)
> + subtest:ok(newm.gc_steps_sweepstring - oldm.gc_steps_sweepstring >= 1)
> + subtest:ok(newm.gc_steps_sweep - oldm.gc_steps_sweep >= 1)
> + -- Nothing to finalize, skipped.
> + subtest:is(newm.gc_steps_finalize, 0)
> + oldm = newm
> +
> + -- Now let's run three GC cycles to ensure that zero-to-one
> + -- transition was not a lucky coincidence.
> + collectgarbage("collect")
> + collectgarbage("collect")
> + collectgarbage("collect")
> + collectgarbage("stop")
> + newm = misc.getmetrics()
> + subtest:ok(newm.gc_steps_pause - oldm.gc_steps_pause == 3)
> + subtest:ok(newm.gc_steps_propagate - oldm.gc_steps_propagate >= 3)
> + subtest:ok(newm.gc_steps_atomic - oldm.gc_steps_atomic == 3)
> + subtest:ok(newm.gc_steps_sweepstring - oldm.gc_steps_sweepstring >= 3)
> + subtest:ok(newm.gc_steps_sweep - oldm.gc_steps_sweep >= 3)
> + -- Nothing to finalize, skipped.
> + subtest:is(newm.gc_steps_finalize, 0)
> +end)
> +
> +test:test("objcount", function(subtest)
> + subtest:plan(4)
> + local table_new = require("table.new")
> + local ffi = require("ffi")
> +
> + jit.opt.start(0)
> +
> + -- Remove all dead objects.
> + collectgarbage("collect")
> +
> + -- Bump strings and table creation.
> + local old_metrics = misc.getmetrics()
> + old_metrics = misc.getmetrics()
> +
> + local placeholder = {
> + str = {},
> + tab = {},
> + udata = {},
> + cdata = {},
> + }
> +
> + -- Separate objects creations to separate jit traces.
> + for i = 1, 1000 do
> + table.insert(placeholder.str, tostring(i))
> + end
> +
> + for i = 1, 1000 do
> + table.insert(placeholder.tab, table_new(i, 0))
> + end
> +
> + for i = 1, 1000 do
> + table.insert(placeholder.udata, newproxy())
> + end
> +
> + for i = 1, 1000 do
> + -- Check counting of VLA/VLS/aligned cdata.
> + table.insert(placeholder.cdata, ffi.new("char[?]", 4))
> + end
> +
> + for i = 1, 1000 do
> + -- Check counting of non-VLA/VLS/aligned cdata.
> + table.insert(placeholder.cdata, ffi.new("uint64_t", i))
> + end
> +
> + placeholder = nil
> + collectgarbage("collect")
> + local new_metrics = misc.getmetrics()
> +
> + -- Check that amount of objects not increased.
> + subtest:is(new_metrics.gc_strnum, old_metrics.gc_strnum,
> + "strnum don't change")
> + subtest:is(new_metrics.gc_tabnum, old_metrics.gc_tabnum,
> + "tabnum don't change")
> + subtest:is(new_metrics.gc_udatanum, old_metrics.gc_udatanum,
> + "udatanum don't change")
> + subtest:is(new_metrics.gc_cdatanum, old_metrics.gc_cdatanum,
> + "cdatanum don't change")
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level)
> +end)
> +
> +test:test("snap-restores-direct-loop", function(subtest)
> + -- Compiled loop with a direct exit to the interpreter.
> + subtest:plan(1)
> +
> + jit.opt.start(0, "hotloop=2")
> +
> + local old_metrics = misc.getmetrics()
> +
> + local sum = 0
> + for i = 1, 20 do
> + sum = sum + i
> + end
> +
> + local new_metrics = misc.getmetrics()
> +
> + -- A single snapshot restoration happened on loop finish:
> + subtest:is(new_metrics.jit_snap_restore - old_metrics.jit_snap_restore, 1)
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level, "hotloop="..jit_opt_default_hotloop)
> +end)
> +
> +test:test("snap-restores-loop-side-exit-non-compiled", function(subtest)
> + -- Compiled loop with a side exit which does not get compiled.
> + subtest:plan(1)
> +
> + jit.opt.start(0, "hotloop=2", "hotexit=2", "minstitch=15")
> +
> + local function foo(i)
> + -- math.fmod is not yet compiled!
> + return i <= 5 and i or math.fmod(i, 11)
> + end
> +
> + local old_metrics = misc.getmetrics()
> + local sum = 0
> + for i = 1, 10 do
> + sum = sum + foo(i)
> + end
> +
> + local new_metrics = misc.getmetrics()
> +
> + -- Side exits from the root trace could not get compiled.
> + subtest:is(new_metrics.jit_snap_restore - old_metrics.jit_snap_restore, 5)
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level, "hotloop="..jit_opt_default_hotloop,
> + "hotexit="..jit_opt_default_hotexit,
> + "minstitch="..jit_opt_default_minstitch)
> +end)
> +
> +test:test("snap-restores-loop-side-exit", function(subtest)
> + -- Compiled loop with a side exit which gets compiled.
> + subtest:plan(1)
> +
> + -- Optimization level is important here as `loop` optimization
> + -- may unroll the loop body and insert +1 side exit.
> + jit.opt.start(0, "hotloop=5", "hotexit=5")
> +
> + local function foo(i)
> + return i <= 10 and i or tostring(i)
> + end
> +
> + local old_metrics = misc.getmetrics()
> + local sum = 0
> + for i = 1, 20 do
> + sum = sum + foo(i)
> + end
> +
> + local new_metrics = misc.getmetrics()
> +
> + -- 5 side exits to the interpreter before trace gets hot
> + -- and compiled
> + -- 1 side exit on loop end
> + subtest:is(new_metrics.jit_snap_restore - old_metrics.jit_snap_restore, 6)
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level, "hotloop="..jit_opt_default_hotloop,
> + "hotexit="..jit_opt_default_hotexit)
> +end)
> +
> +test:test("snap-restores-scalar", function(subtest)
> + -- Compiled scalar trace with a direct exit to the interpreter.
> + subtest:plan(2)
> +
> + -- For calls it will be 2 * hotloop (see lj_dispatch.{c,h}
> + -- and hotcall at vm_*.dasc).
> + jit.opt.start(3, "hotloop=2", "hotexit=3")
> +
> + local function foo(i)
> + return i <= 15 and i or tostring(i)
> + end
> +
> + local old_metrics = misc.getmetrics()
> +
> + foo(1) -- interp only
> + foo(2) -- interp only
> + foo(3) -- interp only
> + foo(4) -- compile trace during this call
> + foo(5) -- follow the trace
> + foo(6) -- follow the trace
> + foo(7) -- follow the trace
> + foo(8) -- follow the trace
> + foo(9) -- follow the trace
> + foo(10) -- follow the trace
> +
> + local new_metrics = misc.getmetrics()
> +
> + -- No exits triggering snap restore so far: snapshot
> + -- restoration was inlined into the machine code.
> + subtest:is(new_metrics.jit_snap_restore - old_metrics.jit_snap_restore, 0)
> + old_metrics = new_metrics
> +
> + -- Simply 2 side exits from the trace:
> + foo(20)
> + foo(21)
> +
> + new_metrics = misc.getmetrics()
> + subtest:is(new_metrics.jit_snap_restore - old_metrics.jit_snap_restore, 2)
> +
> + -- Restore default jit settings.
> + jit.opt.start(jit_opt_default_level, "hotloop="..jit_opt_default_hotloop,
> + "hotexit="..jit_opt_default_hotexit)
> +end)
> +
> +test:test("strhash", function(subtest)
> + subtest:plan(1)
> +
> + local old_metrics = misc.getmetrics()
> +
> + local new_metrics = misc.getmetrics()
> + -- Do not use test:ok to avoid extra strhash hits/misses.
> + assert(new_metrics.strhash_hit - old_metrics.strhash_hit == 19)
> + assert(new_metrics.strhash_miss - old_metrics.strhash_miss == 0)
> + old_metrics = new_metrics
> +
> + local str1 = "strhash".."_hit"
> +
> + new_metrics = misc.getmetrics()
> + assert(new_metrics.strhash_hit - old_metrics.strhash_hit == 20)
> + assert(new_metrics.strhash_miss - old_metrics.strhash_miss == 0)
> + old_metrics = new_metrics
> +
> + new_metrics = misc.getmetrics()
> + assert(new_metrics.strhash_hit - old_metrics.strhash_hit == 19)
> + assert(new_metrics.strhash_miss - old_metrics.strhash_miss == 0)
> + old_metrics = new_metrics
> +
> + local str2 = "new".."string"
> +
> + new_metrics = misc.getmetrics()
> + assert(new_metrics.strhash_hit - old_metrics.strhash_hit == 19)
> + assert(new_metrics.strhash_miss - old_metrics.strhash_miss == 1)
> + subtest:ok(true, "no assertion failed")
> +end)
> +
> +test:test("tracenum-base", function(subtest)
> + subtest:plan(3)
> +
> + jit.off()
> + jit.flush()
> + collectgarbage("collect")
> + local metrics = misc.getmetrics()
> + subtest:is(metrics.jit_trace_num, 0)
> +
> + jit.on()
> + local sum = 0
> + for i = 1, 100 do
> + sum = sum + i
> + end
> +
> + metrics = misc.getmetrics()
> + subtest:is(metrics.jit_trace_num, 1)
> +
> + jit.off()
> + jit.flush()
> + collectgarbage("collect")
> +
> + metrics = misc.getmetrics()
> + subtest:is(metrics.jit_trace_num, 0)
> +
> + jit.on()
> +end)
> +
> +os.exit(test:check() and 0 or 1)
> --
> 2.28.0
>
More information about the Tarantool-patches
mailing list