Tarantool development patches archive
 help / color / mirror / Atom feed
* [Tarantool-patches] [PATCH v3 0/2] Implement LuaJIT platform metrics
@ 2020-09-20 17:12 Sergey Kaplun
  2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 1/2] core: introduce various " Sergey Kaplun
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Sergey Kaplun @ 2020-09-20 17:12 UTC (permalink / raw)
  To: Igor Munkin, Sergey Ostanevich; +Cc: tarantool-patches

The series consists of 2 patches. The first one adds corresponding
counters to LuaJIT internal structures. The second provides C and Lua
API using this counters to collect metrics.

Branch: https://github.com/tarantool/luajit/tree/skaplun/gh-5187-luajit-metrics
Issue: https://github.com/tarantool/tarantool/issues/5187

Changes in v2:
- Fixed naming and comments
- Fixed padding in struct GCState
- Dropped unnecessary initialisations inside lua_newstate()
- Avoided flushing any of metrics after each call of luaM_metrics()

Changes in v3:
- Cleaned up mess in Makefile.dep
- Fixed naming and comments
- Fixed padding in struct GCState for 64-bit architectures
- Fixed counting amount of JIT traces
- Fixed objects counting at trace recording
- Added counting of colors
- Added C and Lua tests

Sergey Kaplun (2):
  core: introduce various platform metrics
  misc: add C and Lua API for platform metrics

 Makefile                                      |   2 +-
 src/Makefile                                  |   5 +-
 src/Makefile.dep                              |  14 +-
 src/lib_init.c                                |   2 +
 src/lib_misc.c                                |  76 +++
 src/lj_asm.c                                  |   2 +
 src/lj_asm_arm.h                              |  19 +
 src/lj_asm_arm64.h                            |  19 +
 src/lj_asm_mips.h                             |  10 +
 src/lj_asm_ppc.h                              |   9 +
 src/lj_asm_x86.h                              |  10 +
 src/lj_cdata.c                                |   9 +
 src/lj_cdata.h                                |   2 +
 src/lj_gc.c                                   |  99 +++-
 src/lj_gc.h                                   |  33 +-
 src/lj_jit.h                                  |   3 +
 src/lj_mapi.c                                 |  65 +++
 src/lj_obj.h                                  |  31 +-
 src/lj_snap.c                                 |   1 +
 src/lj_state.c                                |   7 +-
 src/lj_str.c                                  |   5 +
 src/lj_tab.c                                  |   2 +
 src/lj_trace.c                                |   6 +-
 src/lj_udata.c                                |   2 +
 src/ljamalg.c                                 |   2 +
 src/lmisclib.h                                |  71 +++
 src/luaconf.h                                 |   1 +
 src/vm_arm.dasc                               |   6 +
 src/vm_arm64.dasc                             |   6 +
 src/vm_mips.dasc                              |   6 +
 src/vm_mips64.dasc                            |   6 +
 src/vm_ppc.dasc                               |   6 +
 src/vm_x64.dasc                               |   2 +
 src/vm_x86.dasc                               |   2 +
 test/clib-misclib-getmetrics.test.lua         | 188 +++++++
 test/clib-misclib-getmetrics/CMakeLists.txt   |   1 +
 test/clib-misclib-getmetrics/testgetmetrics.c | 287 +++++++++++
 test/lib-misc-getmetrics.test.lua             | 459 ++++++++++++++++++
 38 files changed, 1433 insertions(+), 43 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

-- 
2.28.0

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v3 1/2] core: introduce various platform metrics
  2020-09-20 17:12 [Tarantool-patches] [PATCH v3 0/2] Implement LuaJIT platform metrics Sergey Kaplun
@ 2020-09-20 17:12 ` Sergey Kaplun
  2020-10-05  6:36   ` Sergey Kaplun
  2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 2/2] misc: add C and Lua API for " Sergey Kaplun
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Sergey Kaplun @ 2020-09-20 17:12 UTC (permalink / raw)
  To: Igor Munkin, Sergey Ostanevich; +Cc: tarantool-patches

This patch introduces the following counters:
  - overall amount of allocated tables, cdata and udata objects
  - number of incremental GC steps grouped by GC state
  - number of white, gray, black objects
  - number of string hashes hits and misses
  - amount of allocated and freed memory
  - number of trace aborts and restored snapshots

Interfaces to obtain these metrics via both Lua and C API are
introduced in the next patch.

Part of tarantool/tarantool#5187
---
 src/lj_asm.c       |  2 +
 src/lj_asm_arm.h   | 19 +++++++++
 src/lj_asm_arm64.h | 19 +++++++++
 src/lj_asm_mips.h  | 10 +++++
 src/lj_asm_ppc.h   |  9 +++++
 src/lj_asm_x86.h   | 10 +++++
 src/lj_cdata.c     |  9 +++++
 src/lj_cdata.h     |  2 +
 src/lj_gc.c        | 99 ++++++++++++++++++++++++++++++++++++----------
 src/lj_gc.h        | 33 +++++++++++-----
 src/lj_jit.h       |  3 ++
 src/lj_obj.h       | 31 +++++++++++++--
 src/lj_snap.c      |  1 +
 src/lj_state.c     |  7 +++-
 src/lj_str.c       |  5 +++
 src/lj_tab.c       |  2 +
 src/lj_trace.c     |  6 ++-
 src/lj_udata.c     |  2 +
 src/vm_arm.dasc    |  6 +++
 src/vm_arm64.dasc  |  6 +++
 src/vm_mips.dasc   |  6 +++
 src/vm_mips64.dasc |  6 +++
 src/vm_ppc.dasc    |  6 +++
 src/vm_x64.dasc    |  2 +
 src/vm_x86.dasc    |  2 +
 25 files changed, 268 insertions(+), 35 deletions(-)

diff --git a/src/lj_asm.c b/src/lj_asm.c
index c2cf5a9..8fb3ccd 100644
--- a/src/lj_asm.c
+++ b/src/lj_asm.c
@@ -2273,6 +2273,7 @@ void lj_asm_trace(jit_State *J, GCtrace *T)
   as->J = J;
   as->T = T;
   J->curfinal = lj_trace_alloc(J->L, T);  /* This copies the IR, too. */
+  J->tracenum++;
   as->flags = J->flags;
   as->loopref = J->loopref;
   as->realign = NULL;
@@ -2379,6 +2380,7 @@ void lj_asm_trace(jit_State *J, GCtrace *T)
     lj_trace_free(J2G(J), J->curfinal);
     J->curfinal = NULL;  /* In case lj_trace_alloc() OOMs. */
     J->curfinal = lj_trace_alloc(J->L, T);
+    J->tracenum++;
     as->realign = NULL;
   }
 
diff --git a/src/lj_asm_arm.h b/src/lj_asm_arm.h
index 37bfa40..62f269e 100644
--- a/src/lj_asm_arm.h
+++ b/src/lj_asm_arm.h
@@ -1257,9 +1257,16 @@ static void asm_cnew(ASMState *as, IRIns *ir)
   {
     uint32_t k = emit_isk12(ARMI_MOV, id);
     Reg r = k ? RID_R1 : ra_allock(as, id, allow);
+    allow = rset_exclude(allow, r);
+    Reg gr = ra_allock(as, i32ptr(J2G(as->J)), allow);
     emit_lso(as, ARMI_STRB, RID_TMP, RID_RET, offsetof(GCcdata, gct));
     emit_lsox(as, ARMI_STRH, r, RID_RET, offsetof(GCcdata, ctypeid));
     emit_d(as, ARMI_MOV|ARMI_K12|~LJ_TCDATA, RID_TMP);
+    emit_lso(as, ARMI_STR, RID_TMP, gr,
+	     (int32_t)offsetof(global_State, gc.cdatanum));
+    emit_dn(as, ARMI_ADD|ARMI_K12|1, RID_TMP, RID_TMP);
+    emit_lso(as, ARMI_LDR, RID_TMP, gr,
+	     (int32_t)offsetof(global_State, gc.cdatanum));
     if (k) emit_d(as, ARMI_MOV^k, RID_R1);
   }
   args[0] = ASMREF_L;     /* lua_State *L */
@@ -1282,6 +1289,18 @@ static void asm_tbar(ASMState *as, IRIns *ir)
 		     rset_exclude(rset_exclude(RSET_GPR, tab), link));
   Reg mark = RID_TMP;
   MCLabel l_end = emit_label(as);
+  emit_lso(as, ARMI_STR, RID_TMP, gr,
+	   (int32_t)offsetof(global_State, gc.graynum));
+  emit_dn(as, ARMI_ADD|ARMI_K12|1, RID_TMP, RID_TMP);
+  emit_lso(as, ARMI_LDR, RID_TMP, gr,
+	   (int32_t)offsetof(global_State, gc.graynum));
+
+  emit_lso(as, ARMI_STR, RID_TMP, gr,
+	   (int32_t)offsetof(global_State, gc.blacknum));
+  emit_dn(as, ARMI_SUB|ARMI_K12|1, RID_TMP, RID_TMP);
+  emit_lso(as, ARMI_LDR, RID_TMP, gr,
+	   (int32_t)offsetof(global_State, gc.blacknum));
+
   emit_lso(as, ARMI_STR, link, tab, (int32_t)offsetof(GCtab, gclist));
   emit_lso(as, ARMI_STRB, mark, tab, (int32_t)offsetof(GCtab, marked));
   emit_lso(as, ARMI_STR, tab, gr,
diff --git a/src/lj_asm_arm64.h b/src/lj_asm_arm64.h
index 8fd92e7..6be912e 100644
--- a/src/lj_asm_arm64.h
+++ b/src/lj_asm_arm64.h
@@ -1220,9 +1220,16 @@ static void asm_cnew(ASMState *as, IRIns *ir)
   /* Initialize gct and ctypeid. lj_mem_newgco() already sets marked. */
   {
     Reg r = (id < 65536) ? RID_X1 : ra_allock(as, id, allow);
+    allow = rset_exclude(allow, r);
+    Reg gr = ra_allock(as, i64ptr(J2G(as->J)), allow);
     emit_lso(as, A64I_STRB, RID_TMP, RID_RET, offsetof(GCcdata, gct));
     emit_lso(as, A64I_STRH, r, RID_RET, offsetof(GCcdata, ctypeid));
     emit_d(as, A64I_MOVZw | A64F_U16(~LJ_TCDATA), RID_TMP);
+    emit_lso(as, A64I_STRx, RID_TMP, gr,
+	     (int32_t)offsetof(global_State, gc.cdatanum));
+    emit_dn(as, (A64I_ADDx^A64I_K12) | A64F_U12(1), RID_TMP, RID_TMP);
+    emit_lso(as, A64I_LDRx, RID_TMP, gr,
+	     (int32_t)offsetof(global_State, gc.cdatanum));
     if (id < 65536) emit_d(as, A64I_MOVZw | A64F_U16(id), RID_X1);
   }
   args[0] = ASMREF_L;     /* lua_State *L */
@@ -1245,6 +1252,18 @@ static void asm_tbar(ASMState *as, IRIns *ir)
 		     rset_exclude(rset_exclude(RSET_GPR, tab), link));
   Reg mark = RID_TMP;
   MCLabel l_end = emit_label(as);
+  emit_lso(as, A64I_STRx, RID_TMP, gr,
+	   (int32_t)offsetof(global_State, gc.graynum));
+  emit_dn(as, (A64I_ADDx^A64I_K12) | A64F_U12(1), RID_TMP, RID_TMP);
+  emit_lso(as, A64I_LDRx, RID_TMP, gr,
+	   (int32_t)offsetof(global_State, gc.graynum));
+
+  emit_lso(as, A64I_STRx, RID_TMP, gr,
+	   (int32_t)offsetof(global_State, gc.blacknum));
+  emit_dn(as, (A64I_SUBx^A64I_K12) | A64F_U12(1), RID_TMP, RID_TMP);
+  emit_lso(as, A64I_LDRx, RID_TMP, gr,
+	   (int32_t)offsetof(global_State, gc.blacknum));
+
   emit_lso(as, A64I_STRx, link, tab, (int32_t)offsetof(GCtab, gclist));
   emit_lso(as, A64I_STRB, mark, tab, (int32_t)offsetof(GCtab, marked));
   emit_lso(as, A64I_STRx, tab, gr,
diff --git a/src/lj_asm_mips.h b/src/lj_asm_mips.h
index affe7d8..0a1900f 100644
--- a/src/lj_asm_mips.h
+++ b/src/lj_asm_mips.h
@@ -1473,11 +1473,15 @@ static void asm_cnew(ASMState *as, IRIns *ir)
     return;
   }
 
+  /* Code incrementing cdatanum is sparse to avoid mips data hazards. */
+  emit_setgl(as, RID_RET+2, gc.cdatanum);
   /* Initialize gct and ctypeid. lj_mem_newgco() already sets marked. */
   emit_tsi(as, MIPSI_SB, RID_RET+1, RID_RET, offsetof(GCcdata, gct));
   emit_tsi(as, MIPSI_SH, RID_TMP, RID_RET, offsetof(GCcdata, ctypeid));
+  emit_tsi(as, MIPSI_AADDIU, RID_RET+2, RID_RET+2, 1);
   emit_ti(as, MIPSI_LI, RID_RET+1, ~LJ_TCDATA);
   emit_ti(as, MIPSI_LI, RID_TMP, id); /* Lower 16 bit used. Sign-ext ok. */
+  emit_getgl(as, RID_RET+2, gc.cdatanum);
   args[0] = ASMREF_L;     /* lua_State *L */
   args[1] = ASMREF_TMP1;  /* MSize size   */
   asm_gencall(as, ci, args);
@@ -1500,6 +1504,12 @@ static void asm_tbar(ASMState *as, IRIns *ir)
   emit_tsi(as, MIPSI_SB, mark, tab, (int32_t)offsetof(GCtab, marked));
   emit_setgl(as, tab, gc.grayagain);
   emit_getgl(as, link, gc.grayagain);
+  emit_setgl(as, RID_TMP, gc.graynum);
+  emit_tsi(as, MIPSI_AADDIU, RID_TMP, RID_TMP, 1);
+  emit_getgl(as, RID_TMP, gc.graynum);
+  emit_setgl(as, RID_TMP, gc.blacknum);
+  emit_tsi(as, MIPSI_AADDIU, RID_TMP, RID_TMP, -1);
+  emit_getgl(as, RID_TMP, gc.blacknum);
   emit_dst(as, MIPSI_XOR, mark, mark, RID_TMP);  /* Clear black bit. */
   emit_branch(as, MIPSI_BEQ, RID_TMP, RID_ZERO, l_end);
   emit_tsi(as, MIPSI_ANDI, RID_TMP, mark, LJ_GC_BLACK);
diff --git a/src/lj_asm_ppc.h b/src/lj_asm_ppc.h
index 6daa861..be4b872 100644
--- a/src/lj_asm_ppc.h
+++ b/src/lj_asm_ppc.h
@@ -1057,6 +1057,9 @@ static void asm_cnew(ASMState *as, IRIns *ir)
     return;
   }
 
+  emit_setgl(as, RID_TMP, gc.cdatanum);
+  emit_tai(as, PPCI_ADDIC, RID_TMP, RID_TMP, 1);
+  emit_getgl(as, RID_TMP, gc.cdatanum);
   /* Initialize gct and ctypeid. lj_mem_newgco() already sets marked. */
   emit_tai(as, PPCI_STB, RID_RET+1, RID_RET, offsetof(GCcdata, gct));
   emit_tai(as, PPCI_STH, RID_TMP, RID_RET, offsetof(GCcdata, ctypeid));
@@ -1086,6 +1089,12 @@ static void asm_tbar(ASMState *as, IRIns *ir)
   lua_assert(LJ_GC_BLACK == 0x04);
   emit_rot(as, PPCI_RLWINM, mark, mark, 0, 30, 28);  /* Clear black bit. */
   emit_getgl(as, link, gc.grayagain);
+  emit_setgl(as, RID_TMP, gc.graynum);
+  emit_tai(as, PPCI_ADDIC, RID_TMP, RID_TMP, 1);
+  emit_getgl(as, RID_TMP, gc.graynum);
+  emit_setgl(as, RID_TMP, gc.blacknum);
+  emit_tai(as, PPCI_ADDIC, RID_TMP, RID_TMP, -1);
+  emit_getgl(as, RID_TMP, gc.blacknum);
   emit_condbranch(as, PPCI_BC|PPCF_Y, CC_EQ, l_end);
   emit_asi(as, PPCI_ANDIDOT, RID_TMP, mark, LJ_GC_BLACK);
   emit_tai(as, PPCI_LBZ, mark, tab, (int32_t)offsetof(GCtab, marked));
diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h
index 3e189b1..8c9cdaa 100644
--- a/src/lj_asm_x86.h
+++ b/src/lj_asm_x86.h
@@ -1835,6 +1835,10 @@ static void asm_cnew(ASMState *as, IRIns *ir)
     return;
   }
 
+  /* Increment cdatanum counter by address directly. */
+  emit_i8(as, 1);
+  emit_rmro(as, XO_ARITHi8, XOg_ADD, RID_NONE,
+	    ptr2addr(&J2G(as->J)->gc.cdatanum));
   /* Combine initialization of marked, gct and ctypeid. */
   emit_movtomro(as, RID_ECX, RID_RET, offsetof(GCcdata, marked));
   emit_gri(as, XG_ARITHi(XOg_OR), RID_ECX,
@@ -1861,6 +1865,12 @@ static void asm_tbar(ASMState *as, IRIns *ir)
   emit_movtomro(as, tmp|REX_GC64, tab, offsetof(GCtab, gclist));
   emit_setgl(as, tab, gc.grayagain);
   emit_getgl(as, tmp, gc.grayagain);
+  emit_i8(as, 1);
+  emit_rmro(as, XO_ARITHi8, XOg_ADD, RID_NONE,
+	    ptr2addr(&J2G(as->J)->gc.graynum));
+  emit_i8(as, 1);
+  emit_rmro(as, XO_ARITHi8, XOg_SUB, RID_NONE,
+	    ptr2addr(&J2G(as->J)->gc.blacknum));
   emit_i8(as, ~LJ_GC_BLACK);
   emit_rmro(as, XO_ARITHib, XOg_AND, tab, offsetof(GCtab, marked));
   emit_sjcc(as, CC_Z, l_end);
diff --git a/src/lj_cdata.c b/src/lj_cdata.c
index 68e16d7..5870ac4 100644
--- a/src/lj_cdata.c
+++ b/src/lj_cdata.c
@@ -46,6 +46,7 @@ GCcdata *lj_cdata_newv(lua_State *L, CTypeID id, CTSize sz, CTSize align)
   cd->marked |= 0x80;
   cd->gct = ~LJ_TCDATA;
   cd->ctypeid = id;
+  g->gc.cdatanum++;
   return cd;
 }
 
@@ -63,6 +64,13 @@ void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd)
 {
   if (LJ_UNLIKELY(cd->marked & LJ_GC_CDATA_FIN)) {
     GCobj *root;
+    /* Restore old value of color incremented before free. */
+      if (iswhite(obj2gco(cd)))
+	g->gc.whitenum++;
+      else if (isblack(obj2gco(cd)))
+	g->gc.blacknum++;
+      else
+	g->gc.graynum++;
     makewhite(g, obj2gco(cd));
     markfinalized(obj2gco(cd));
     if ((root = gcref(g->gc.mmudata)) != NULL) {
@@ -82,6 +90,7 @@ void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd)
   } else {
     lj_mem_free(g, memcdatav(cd), sizecdatav(cd));
   }
+  g->gc.cdatanum--;
 }
 
 void lj_cdata_setfin(lua_State *L, GCcdata *cd, GCobj *obj, uint32_t it)
diff --git a/src/lj_cdata.h b/src/lj_cdata.h
index 5bb0f5d..66b023b 100644
--- a/src/lj_cdata.h
+++ b/src/lj_cdata.h
@@ -45,6 +45,7 @@ static LJ_AINLINE GCcdata *lj_cdata_new(CTState *cts, CTypeID id, CTSize sz)
   cd = (GCcdata *)lj_mem_newgco(cts->L, sizeof(GCcdata) + sz);
   cd->gct = ~LJ_TCDATA;
   cd->ctypeid = ctype_check(cts, id);
+  G(cts->L)->gc.cdatanum++;
   return cd;
 }
 
@@ -54,6 +55,7 @@ static LJ_AINLINE GCcdata *lj_cdata_new_(lua_State *L, CTypeID id, CTSize sz)
   GCcdata *cd = (GCcdata *)lj_mem_newgco(L, sizeof(GCcdata) + sz);
   cd->gct = ~LJ_TCDATA;
   cd->ctypeid = id;
+  G(L)->gc.cdatanum++;
   return cd;
 }
 
diff --git a/src/lj_gc.c b/src/lj_gc.c
index 1c8e6ce..c7616f6 100644
--- a/src/lj_gc.c
+++ b/src/lj_gc.c
@@ -33,8 +33,18 @@
 #define GCFINALIZECOST	100
 
 /* Macros to set GCobj colors and flags. */
-#define white2gray(x)		((x)->gch.marked &= (uint8_t)~LJ_GC_WHITES)
-#define gray2black(x)		((x)->gch.marked |= LJ_GC_BLACK)
+#define white2gray(g, x) \
+  ( (g)->gc.whitenum--, \
+    (g)->gc.graynum++, \
+    lua_assert((ssize_t)(g)->gc.whitenum >=2), \
+    (x)->gch.marked &= (uint8_t)~LJ_GC_WHITES )
+
+#define gray2black(g, x) \
+  ( (g)->gc.graynum--, \
+    (g)->gc.blacknum++, \
+    lua_assert((ssize_t)(g)->gc.graynum >=0), \
+    (x)->gch.marked |= LJ_GC_BLACK )
+
 #define isfinalized(u)		((u)->marked & LJ_GC_FINALIZED)
 
 /* -- Mark phase ---------------------------------------------------------- */
@@ -49,24 +59,30 @@
   { if (iswhite(obj2gco(o))) gc_mark(g, obj2gco(o)); }
 
 /* Mark a string object. */
-#define gc_mark_str(s)		((s)->marked &= (uint8_t)~LJ_GC_WHITES)
+#define gc_mark_str(g, s) \
+  { if ((s)->marked & LJ_GC_WHITES) { \
+      (g)->gc.whitenum--; \
+      (g)->gc.graynum++; \
+      lua_assert((ssize_t)(g)->gc.whitenum >=2); \
+    }; \
+    (s)->marked &= (uint8_t)~LJ_GC_WHITES; }
 
 /* Mark a white GCobj. */
 static void gc_mark(global_State *g, GCobj *o)
 {
   int gct = o->gch.gct;
   lua_assert(iswhite(o) && !isdead(g, o));
-  white2gray(o);
+  white2gray(g, o);
   if (LJ_UNLIKELY(gct == ~LJ_TUDATA)) {
     GCtab *mt = tabref(gco2ud(o)->metatable);
-    gray2black(o);  /* Userdata are never gray. */
+    gray2black(g, o);  /* Userdata are never gray. */
     if (mt) gc_markobj(g, mt);
     gc_markobj(g, tabref(gco2ud(o)->env));
   } else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) {
     GCupval *uv = gco2uv(o);
     gc_marktv(g, uvval(uv));
     if (uv->closed)
-      gray2black(o);  /* Closed upvalues are never gray. */
+      gray2black(g, o);  /* Closed upvalues are never gray. */
   } else if (gct != ~LJ_TSTR && gct != ~LJ_TCDATA) {
     lua_assert(gct == ~LJ_TFUNC || gct == ~LJ_TTAB ||
 	       gct == ~LJ_TTHREAD || gct == ~LJ_TPROTO || gct == ~LJ_TTRACE);
@@ -230,7 +246,7 @@ static void gc_marktrace(global_State *g, TraceNo traceno)
   GCobj *o = obj2gco(traceref(G2J(g), traceno));
   lua_assert(traceno != G2J(g)->cur.traceno);
   if (iswhite(o)) {
-    white2gray(o);
+    white2gray(g, o);
     setgcrefr(o->gch.gclist, g->gc.gray);
     setgcref(g->gc.gray, o);
   }
@@ -264,7 +280,7 @@ static void gc_traverse_trace(global_State *g, GCtrace *T)
 static void gc_traverse_proto(global_State *g, GCproto *pt)
 {
   ptrdiff_t i;
-  gc_mark_str(proto_chunkname(pt));
+  gc_mark_str(g, proto_chunkname(pt));
   for (i = -(ptrdiff_t)pt->sizekgc; i < 0; i++)  /* Mark collectable consts. */
     gc_markobj(g, proto_kgc(pt, i));
 #if LJ_HASJIT
@@ -310,12 +326,12 @@ static size_t propagatemark(global_State *g)
   GCobj *o = gcref(g->gc.gray);
   int gct = o->gch.gct;
   lua_assert(isgray(o));
-  gray2black(o);
+  gray2black(g, o);
   setgcrefr(g->gc.gray, o->gch.gclist);  /* Remove from gray list. */
   if (LJ_LIKELY(gct == ~LJ_TTAB)) {
     GCtab *t = gco2tab(o);
     if (gc_traverse_tab(g, t) > 0)
-      black2gray(o);  /* Keep weak tables gray. */
+      black2gray(g, o);  /* Keep weak tables gray. */
     return sizeof(GCtab) + sizeof(TValue) * t->asize +
 			   (t->hmask ? sizeof(Node) * (t->hmask + 1) : 0);
   } else if (LJ_LIKELY(gct == ~LJ_TFUNC)) {
@@ -331,7 +347,7 @@ static size_t propagatemark(global_State *g)
     lua_State *th = gco2th(o);
     setgcrefr(th->gclist, g->gc.grayagain);
     setgcref(g->gc.grayagain, o);
-    black2gray(o);  /* Threads are never black. */
+    black2gray(g, o);  /* Threads are never black. */
     gc_traverse_thread(g, th);
     return sizeof(lua_State) + sizeof(TValue) * th->stacksize;
   } else {
@@ -403,6 +419,16 @@ static GCRef *gc_sweep(global_State *g, GCRef *p, uint32_t lim)
       setgcrefr(*p, o->gch.nextgc);
       if (o == gcref(g->gc.root))
 	setgcrefr(g->gc.root, o->gch.nextgc);  /* Adjust list anchor. */
+      if (iswhite(o)) {
+	g->gc.whitenum--;
+	lua_assert((ssize_t)g->gc.whitenum >= 2);
+      } else if (isblack(o)) {
+	g->gc.blacknum--;
+	lua_assert((ssize_t)g->gc.blacknum >= 0);
+      } else {
+	g->gc.graynum--;
+	lua_assert((ssize_t)g->gc.graynum >= 0);
+      }
       gc_freefunc[o->gch.gct - ~LJ_TSTR](g, o);
     }
   }
@@ -430,6 +456,16 @@ static GCRef *gc_sweep_str_chain(global_State *g, GCRef *p)
     } else {  /* Otherwise value is dead, free it. */
       lua_assert(isdead(g, o) || ow == LJ_GC_SFIXED);
       setgcrefr(*p, o->gch.nextgc);
+      if (iswhite(o)) {
+	g->gc.whitenum--;
+	lua_assert((ssize_t)g->gc.whitenum >= 2);
+      } else if (isblack(o)) {
+	g->gc.blacknum--;
+	lua_assert((ssize_t)g->gc.blacknum >= 0);
+      } else {
+	g->gc.graynum--;
+	lua_assert((ssize_t)g->gc.graynum >= 0);
+      }
       lj_str_free(g, &o->str);
     }
   }
@@ -437,11 +473,11 @@ static GCRef *gc_sweep_str_chain(global_State *g, GCRef *p)
 }
 
 /* Check whether we can clear a key or a value slot from a table. */
-static int gc_mayclear(cTValue *o, int val)
+static int gc_mayclear(global_State *g, cTValue *o, int val)
 {
   if (tvisgcv(o)) {  /* Only collectable objects can be weak references. */
     if (tvisstr(o)) {  /* But strings cannot be used as weak references. */
-      gc_mark_str(strV(o));  /* And need to be marked. */
+      gc_mark_str(g, strV(o));  /* And need to be marked. */
       return 0;
     }
     if (iswhite(gcV(o)))
@@ -453,7 +489,7 @@ static int gc_mayclear(cTValue *o, int val)
 }
 
 /* Clear collected entries from weak tables. */
-static void gc_clearweak(GCobj *o)
+static void gc_clearweak(global_State *g, GCobj *o)
 {
   while (o) {
     GCtab *t = gco2tab(o);
@@ -463,7 +499,7 @@ static void gc_clearweak(GCobj *o)
       for (i = 0; i < asize; i++) {
 	/* Clear array slot when value is about to be collected. */
 	TValue *tv = arrayslot(t, i);
-	if (gc_mayclear(tv, 1))
+	if (gc_mayclear(g, tv, 1))
 	  setnilV(tv);
       }
     }
@@ -473,8 +509,8 @@ static void gc_clearweak(GCobj *o)
       for (i = 0; i <= hmask; i++) {
 	Node *n = &node[i];
 	/* Clear hash slot when key or value is about to be collected. */
-	if (!tvisnil(&n->val) && (gc_mayclear(&n->key, 0) ||
-				  gc_mayclear(&n->val, 1)))
+	if (!tvisnil(&n->val) && (gc_mayclear(g, &n->key, 0) ||
+				  gc_mayclear(g, &n->val, 1)))
 	  setnilV(&n->val);
       }
     }
@@ -619,12 +655,21 @@ static void atomic(global_State *g, lua_State *L)
   udsize += gc_propagate_gray(g);  /* And propagate the marks. */
 
   /* All marking done, clear weak tables. */
-  gc_clearweak(gcref(g->gc.weak));
+  gc_clearweak(g, gcref(g->gc.weak));
 
   lj_buf_shrink(L, &g->tmpbuf);  /* Shrink temp buffer. */
 
   /* Prepare for sweep phase. */
   g->gc.currentwhite = (uint8_t)otherwhite(g);  /* Flip current white. */
+  if (g->strempty.marked & (uint8_t)LJ_GC_BLACK) {
+    g->gc.blacknum--;
+    g->gc.whitenum++;
+    lua_assert((ssize_t)g->gc.blacknum >=0);
+  } else if ((g->strempty.marked & (uint8_t)LJ_GC_WHITES) == 0) {
+    g->gc.graynum--;
+    g->gc.whitenum++;
+    lua_assert((ssize_t)g->gc.graynum >=0);
+  }
   g->strempty.marked = g->gc.currentwhite;
   setmref(g->gc.sweep, &g->gc.root);
   g->gc.estimate = g->gc.total - (GCSize)udsize;  /* Initial estimate. */
@@ -634,6 +679,7 @@ static void atomic(global_State *g, lua_State *L)
 static size_t gc_onestep(lua_State *L)
 {
   global_State *g = G(L);
+  g->gc.state_count[g->gc.state]++;
   switch (g->gc.state) {
   case GCSpause:
     gc_mark_start(g);  /* Start a new GC cycle by marking all GC roots. */
@@ -808,8 +854,18 @@ void LJ_FASTCALL lj_gc_barrieruv(global_State *g, TValue *tv)
   (*((uint8_t *)(x) - offsetof(GCupval, tv) + offsetof(GCupval, marked)))
   if (g->gc.state == GCSpropagate || g->gc.state == GCSatomic)
     gc_mark(g, gcV(tv));
-  else
+  else {
+    if (TV2MARKED(tv) & (uint8_t)LJ_GC_BLACK) {
+      g->gc.blacknum--;
+      g->gc.whitenum++;
+      lua_assert((ssize_t)g->gc.blacknum >=0);
+    } else if ((TV2MARKED(tv) & (uint8_t)LJ_GC_WHITES) == 0) {
+      g->gc.graynum--;
+      g->gc.whitenum++;
+      lua_assert((ssize_t)g->gc.graynum >=0);
+    }
     TV2MARKED(tv) = (TV2MARKED(tv) & (uint8_t)~LJ_GC_COLORS) | curwhite(g);
+  }
 #undef TV2MARKED
 }
 
@@ -825,7 +881,7 @@ void lj_gc_closeuv(global_State *g, GCupval *uv)
   setgcref(g->gc.root, o);
   if (isgray(o)) {  /* A closed upvalue is never gray, so fix this. */
     if (g->gc.state == GCSpropagate || g->gc.state == GCSatomic) {
-      gray2black(o);  /* Make it black and preserve invariant. */
+      gray2black(g, o);  /* Make it black and preserve invariant. */
       if (tviswhite(&uv->tv))
 	lj_gc_barrierf(g, o, gcV(&uv->tv));
     } else {
@@ -857,6 +913,8 @@ void *lj_mem_realloc(lua_State *L, void *p, GCSize osz, GCSize nsz)
   lua_assert((nsz == 0) == (p == NULL));
   lua_assert(checkptrGC(p));
   g->gc.total = (g->gc.total - osz) + nsz;
+  g->gc.allocated += nsz;
+  g->gc.freed += osz;
   return p;
 }
 
@@ -869,6 +927,7 @@ void * LJ_FASTCALL lj_mem_newgco(lua_State *L, GCSize size)
     lj_err_mem(L);
   lua_assert(checkptrGC(o));
   g->gc.total += size;
+  g->gc.allocated += size;
   setgcrefr(o->gch.nextgc, g->gc.root);
   setgcref(g->gc.root, o);
   newwhite(g, o);
diff --git a/src/lj_gc.h b/src/lj_gc.h
index 669bbe9..25e319a 100644
--- a/src/lj_gc.h
+++ b/src/lj_gc.h
@@ -8,11 +8,6 @@
 
 #include "lj_obj.h"
 
-/* Garbage collector states. Order matters. */
-enum {
-  GCSpause, GCSpropagate, GCSatomic, GCSsweepstring, GCSsweep, GCSfinalize
-};
-
 /* Bitmasks for marked field of GCobj. */
 #define LJ_GC_WHITE0	0x01
 #define LJ_GC_WHITE1	0x02
@@ -37,11 +32,28 @@ enum {
 #define isdead(g, v)	((v)->gch.marked & otherwhite(g) & LJ_GC_WHITES)
 
 #define curwhite(g)	((g)->gc.currentwhite & LJ_GC_WHITES)
-#define newwhite(g, x)	(obj2gco(x)->gch.marked = (uint8_t)curwhite(g))
+#define newwhite(g, x) \
+  ( (g)->gc.whitenum++, \
+    obj2gco(x)->gch.marked = (uint8_t)curwhite(g) )
+
 #define makewhite(g, x) \
-  ((x)->gch.marked = ((x)->gch.marked & (uint8_t)~LJ_GC_COLORS) | curwhite(g))
-#define flipwhite(x)	((x)->gch.marked ^= LJ_GC_WHITES)
-#define black2gray(x)	((x)->gch.marked &= (uint8_t)~LJ_GC_BLACK)
+  ( iswhite(x) ? /* Nothing to do */ : \
+    isblack(x) ? ((g)->gc.blacknum--, (g)->gc.whitenum++) : \
+    (lua_assert(isgray(x)), (g)->gc.graynum--, (g)->gc.whitenum++), \
+    lua_assert((ssize_t)(g)->gc.graynum >=0), \
+    lua_assert((ssize_t)(g)->gc.blacknum >=0), \
+    (x)->gch.marked = ((x)->gch.marked & (uint8_t)~LJ_GC_COLORS) | curwhite(g) )
+
+#define flipwhite(x) \
+  ( lua_assert(!isgray(x) && ! isblack(x)), \
+    (x)->gch.marked ^= LJ_GC_WHITES )
+
+#define black2gray(g, x) \
+  ( (g)->gc.blacknum--, \
+    (g)->gc.graynum++, \
+    lua_assert((ssize_t)(g)->gc.blacknum >=0), \
+    (x)->gch.marked &= (uint8_t)~LJ_GC_BLACK )
+
 #define fixstring(s)	((s)->marked |= LJ_GC_FIXED)
 #define markfinalized(x)	((x)->gch.marked |= LJ_GC_FINALIZED)
 
@@ -83,7 +95,7 @@ static LJ_AINLINE void lj_gc_barrierback(global_State *g, GCtab *t)
   GCobj *o = obj2gco(t);
   lua_assert(isblack(o) && !isdead(g, o));
   lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause);
-  black2gray(o);
+  black2gray(g, o);
   setgcrefr(t->gclist, g->gc.grayagain);
   setgcref(g->gc.grayagain, o);
 }
@@ -117,6 +129,7 @@ LJ_FUNC void *lj_mem_grow(lua_State *L, void *p,
 static LJ_AINLINE void lj_mem_free(global_State *g, void *p, size_t osize)
 {
   g->gc.total -= (GCSize)osize;
+  g->gc.freed += osize;
   g->allocf(g->allocd, p, osize, 0);
 }
 
diff --git a/src/lj_jit.h b/src/lj_jit.h
index 7eb3d2a..d82292f 100644
--- a/src/lj_jit.h
+++ b/src/lj_jit.h
@@ -474,6 +474,9 @@ typedef struct jit_State {
   MCode *mcbot;		/* Bottom of current mcode area. */
   size_t szmcarea;	/* Size of current mcode area. */
   size_t szallmcarea;	/* Total size of all allocated mcode areas. */
+  size_t tracenum;	/* Overall number of traces. */
+  size_t nsnaprestore;	/* Overall number of snap restores. */
+  size_t ntraceabort;	/* Overall number of abort traces. */
 
   TValue errinfo;	/* Additional info element for trace errors. */
 
diff --git a/src/lj_obj.h b/src/lj_obj.h
index f368578..c9a6904 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -571,6 +571,17 @@ typedef enum {
 #define basemt_obj(g, o)	((g)->gcroot[GCROOT_BASEMT+itypemap(o)])
 #define mmname_str(g, mm)	(strref((g)->gcroot[GCROOT_MMNAME+(mm)]))
 
+/* Garbage collector states. Order matters. */
+enum {
+  GCSpause, /* Start a GC cycle and mark the root set.*/
+  GCSpropagate, /* One gray object is processed. */
+  GCSatomic, /* Atomic transition from mark to sweep phase. */
+  GCSsweepstring, /* Sweep one chain of strings. */
+  GCSsweep, /* Sweep few objects from root. */
+  GCSfinalize, /* Finalize one userdata or cdata object. */
+  GCSmax
+};
+
 typedef struct GCState {
   GCSize total;		/* Memory currently allocated. */
   GCSize threshold;	/* Memory threshold. */
@@ -589,6 +600,18 @@ typedef struct GCState {
   GCSize estimate;	/* Estimate of memory actually in use. */
   MSize stepmul;	/* Incremental GC step granularity. */
   MSize pause;		/* Pause between successive GC cycles. */
+
+  size_t freed;		/* Total amount of freed memory. */
+  size_t allocated;	/* Total amount of allocated memory. */
+  size_t state_count[GCSmax]; /* Count of incremental GC steps per state. */
+  size_t tabnum;	/* Amount of allocated table objects. */
+  size_t udatanum;	/* Amount of allocated udata objects. */
+#ifdef LJ_HASFFI
+  size_t cdatanum;	/* Amount of allocated cdata objects. */
+#endif
+  size_t whitenum;	/* Amount of white objects. */
+  size_t graynum;	/* Amount of grey objects. */
+  size_t blacknum;	/* Amount of black objects. */
 } GCState;
 
 /* Global state, shared by all threads of a Lua universe. */
@@ -602,22 +625,24 @@ typedef struct global_State {
     BloomFilter next[2];
   } strbloom;
 #endif
+  size_t strhash_hit;	/* Strings amount found in string hash. */
+  size_t strhash_miss;	/* Strings amount allocated and put into string hash. */
   lua_Alloc allocf;	/* Memory allocator. */
   void *allocd;		/* Memory allocator data. */
   GCState gc;		/* Garbage collector. */
-  volatile int32_t vmstate;  /* VM state or current JIT code trace number. */
+  GCRef mainthref;	/* Link to main thread. */
   SBuf tmpbuf;		/* Temporary string buffer. */
   GCstr strempty;	/* Empty string. */
   uint8_t stremptyz;	/* Zero terminator of empty string. */
   uint8_t hookmask;	/* Hook mask. */
   uint8_t dispatchmode;	/* Dispatch mode. */
   uint8_t vmevmask;	/* VM event mask. */
-  GCRef mainthref;	/* Link to main thread. */
+  int32_t hookcount;	/* Instruction hook countdown. */
   TValue registrytv;	/* Anchor for registry. */
   TValue tmptv, tmptv2;	/* Temporary TValues. */
   Node nilnode;		/* Fallback 1-element hash part (nil key and value). */
   GCupval uvhead;	/* Head of double-linked list of all open upvalues. */
-  int32_t hookcount;	/* Instruction hook countdown. */
+  volatile int32_t vmstate;  /* VM state or current JIT code trace number. */
   int32_t hookcstart;	/* Start count for instruction hook counter. */
   lua_Hook hookf;	/* Hook function. */
   lua_CFunction wrapf;	/* Wrapper for C function calls. */
diff --git a/src/lj_snap.c b/src/lj_snap.c
index 7554caf..4cf27e7 100644
--- a/src/lj_snap.c
+++ b/src/lj_snap.c
@@ -904,6 +904,7 @@ const BCIns *lj_snap_restore(jit_State *J, void *exptr)
     L->top = frame + snap->nslots;
     break;
   }
+  J->nsnaprestore++;
   return pc;
 }
 
diff --git a/src/lj_state.c b/src/lj_state.c
index 632dd07..d85a02a 100644
--- a/src/lj_state.c
+++ b/src/lj_state.c
@@ -164,6 +164,10 @@ static void close_state(lua_State *L)
   lj_gc_freeall(g);
   lua_assert(gcref(g->gc.root) == obj2gco(L));
   lua_assert(g->strnum == 0);
+  lua_assert(g->gc.blacknum == 0);
+  lua_assert(g->gc.graynum == 0);
+  /* At least lua_State, strempty and state enviroment. */
+  lua_assert(g->gc.whitenum >= 2);
   lj_trace_freestate(g);
 #if LJ_HASFFI
   lj_ctype_freestate(g);
@@ -214,7 +218,8 @@ LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud)
   g->gc.state = GCSpause;
   setgcref(g->gc.root, obj2gco(L));
   setmref(g->gc.sweep, &g->gc.root);
-  g->gc.total = sizeof(GG_State);
+  g->gc.allocated = g->gc.total = sizeof(GG_State);
+  g->gc.whitenum = 2; /* State and strempty. */
   g->gc.pause = LUAI_GCPAUSE;
   g->gc.stepmul = LUAI_GCMUL;
   lj_dispatch_init((GG_State *)L);
diff --git a/src/lj_str.c b/src/lj_str.c
index 24e90ca..8ff955e 100644
--- a/src/lj_str.c
+++ b/src/lj_str.c
@@ -222,6 +222,7 @@ GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx)
                       str_fastcmp(str, strdata(sx), len) == 0) {
 	/* Resurrect if dead. Can only happen with fixstring() (keywords). */
 	if (isdead(g, o)) flipwhite(o);
+	g->strhash_hit++;
 	return sx;  /* Return existing string. */
       }
       o = gcnext(o);
@@ -234,6 +235,7 @@ GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx)
                       memcmp(str, strdata(sx), len) == 0) {
 	/* Resurrect if dead. Can only happen with fixstring() (keywords). */
 	if (isdead(g, o)) flipwhite(o);
+	g->strhash_hit++;
 	return sx;  /* Return existing string. */
       }
       o = gcnext(o);
@@ -266,6 +268,7 @@ GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx)
 	    if (sx->hash == fh && sx->len == len && str_fastcmp(str, strdata(sx), len) == 0) {
 	      /* Resurrect if dead. Can only happen with fixstring() (keywords). */
 	      if (isdead(g, o)) flipwhite(o);
+	      g->strhash_hit++;
 	      return sx;  /* Return existing string. */
 	    }
 	    o = gcnext(o);
@@ -276,6 +279,7 @@ GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx)
 	    if (sx->hash == fh && sx->len == len && memcmp(str, strdata(sx), len) == 0) {
 	      /* Resurrect if dead. Can only happen with fixstring() (keywords). */
 	      if (isdead(g, o)) flipwhite(o);
+	      g->strhash_hit++;
 	      return sx;  /* Return existing string. */
 	    }
 	    o = gcnext(o);
@@ -293,6 +297,7 @@ GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx)
     }
   }
 #endif
+  g->strhash_miss++;
   /* Nope, create a new string. */
   s = lj_mem_newt(L, sizeof(GCstr)+len+1, GCstr);
   newwhite(g, s);
diff --git a/src/lj_tab.c b/src/lj_tab.c
index ff216f3..c5f358e 100644
--- a/src/lj_tab.c
+++ b/src/lj_tab.c
@@ -141,6 +141,7 @@ static GCtab *newtab(lua_State *L, uint32_t asize, uint32_t hbits)
   }
   if (hbits)
     newhpart(L, t, hbits);
+  G(L)->gc.tabnum++;
   return t;
 }
 
@@ -240,6 +241,7 @@ void LJ_FASTCALL lj_tab_free(global_State *g, GCtab *t)
     lj_mem_free(g, t, sizetabcolo((uint32_t)t->colo & 0x7f));
   else
     lj_mem_freet(g, t);
+  g->gc.tabnum--;
 }
 
 /* -- Table resizing ------------------------------------------------------ */
diff --git a/src/lj_trace.c b/src/lj_trace.c
index d85b47f..9bcbce6 100644
--- a/src/lj_trace.c
+++ b/src/lj_trace.c
@@ -176,6 +176,7 @@ void LJ_FASTCALL lj_trace_free(global_State *g, GCtrace *T)
   lj_mem_free(g, T,
     ((sizeof(GCtrace)+7)&~7) + (T->nins-T->nk)*sizeof(IRIns) +
     T->nsnap*sizeof(SnapShot) + T->nsnapmap*sizeof(SnapEntry));
+  J->tracenum--;
 }
 
 /* Re-enable compiling a prototype by unpatching any modified bytecode. */
@@ -538,8 +539,10 @@ static int trace_downrec(jit_State *J)
   /* Restart recording at the return instruction. */
   lua_assert(J->pt != NULL);
   lua_assert(bc_isret(bc_op(*J->pc)));
-  if (bc_op(*J->pc) == BC_RETM)
+  if (bc_op(*J->pc) == BC_RETM) {
+    J->ntraceabort++;
     return 0;  /* NYI: down-recursion with RETM. */
+  }
   J->parent = 0;
   J->exitno = 0;
   J->state = LJ_TRACE_RECORD;
@@ -616,6 +619,7 @@ static int trace_abort(jit_State *J)
     return trace_downrec(J);
   else if (e == LJ_TRERR_MCODEAL)
     lj_trace_flushall(L);
+  J->ntraceabort++;
   return 0;
 }
 
diff --git a/src/lj_udata.c b/src/lj_udata.c
index bd0321b..70c722a 100644
--- a/src/lj_udata.c
+++ b/src/lj_udata.c
@@ -24,11 +24,13 @@ GCudata *lj_udata_new(lua_State *L, MSize sz, GCtab *env)
   /* Chain to userdata list (after main thread). */
   setgcrefr(ud->nextgc, mainthread(g)->nextgc);
   setgcref(mainthread(g)->nextgc, obj2gco(ud));
+  g->gc.udatanum++;
   return ud;
 }
 
 void LJ_FASTCALL lj_udata_free(global_State *g, GCudata *ud)
 {
+  g->gc.udatanum--;
   lj_mem_free(g, ud, sizeudata(ud));
 }
 
diff --git a/src/vm_arm.dasc b/src/vm_arm.dasc
index d4cdaf5..ea3bf31 100644
--- a/src/vm_arm.dasc
+++ b/src/vm_arm.dasc
@@ -227,6 +227,12 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp
+|  ldr tmp, [DISPATCH, #DISPATCH_GL(gc.graynum)]
+|  add tmp, tmp, #1
+|  str tmp, [DISPATCH, #DISPATCH_GL(gc.graynum)]
+|  ldr tmp, [DISPATCH, #DISPATCH_GL(gc.blacknum)]
+|  sub tmp, tmp, #1
+|  str tmp, [DISPATCH, #DISPATCH_GL(gc.blacknum)]
 |  ldr tmp, [DISPATCH, #DISPATCH_GL(gc.grayagain)]
 |   bic mark, mark, #LJ_GC_BLACK		// black2gray(tab)
 |  str tab, [DISPATCH, #DISPATCH_GL(gc.grayagain)]
diff --git a/src/vm_arm64.dasc b/src/vm_arm64.dasc
index 3eaf376..a182f35 100644
--- a/src/vm_arm64.dasc
+++ b/src/vm_arm64.dasc
@@ -280,6 +280,12 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp
+|  ldr tmp, GL->gc.blacknum
+|  sub tmp, tmp, #1
+|  str tmp, GL->gc.blacknum
+|  ldr tmp, GL->gc.graynum
+|  add tmp, tmp, #1
+|  str tmp, GL->gc.graynum
 |  ldr tmp, GL->gc.grayagain
 |   and mark, mark, #~LJ_GC_BLACK	// black2gray(tab)
 |  str tab, GL->gc.grayagain
diff --git a/src/vm_mips.dasc b/src/vm_mips.dasc
index 1afd611..7de8beb 100644
--- a/src/vm_mips.dasc
+++ b/src/vm_mips.dasc
@@ -351,6 +351,12 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp, target
+|  lw tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
+|  addiu tmp, tmp, -1
+|  sw tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
+|  lw tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
+|  addiu tmp, tmp, 1
+|  sw tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
 |  lw tmp, DISPATCH_GL(gc.grayagain)(DISPATCH)
 |   andi mark, mark, ~LJ_GC_BLACK & 255		// black2gray(tab)
 |  sw tab, DISPATCH_GL(gc.grayagain)(DISPATCH)
diff --git a/src/vm_mips64.dasc b/src/vm_mips64.dasc
index c06270a..30cab6c 100644
--- a/src/vm_mips64.dasc
+++ b/src/vm_mips64.dasc
@@ -350,6 +350,12 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp, target
+|  ld tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
+|  addiu tmp, tmp, -1
+|  sd tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
+|  ld tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
+|  addiu tmp, tmp, 1
+|  sd tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
 |  ld tmp, DISPATCH_GL(gc.grayagain)(DISPATCH)
 |   andi mark, mark, ~LJ_GC_BLACK & 255		// black2gray(tab)
 |  sd tab, DISPATCH_GL(gc.grayagain)(DISPATCH)
diff --git a/src/vm_ppc.dasc b/src/vm_ppc.dasc
index b4260eb..3015869 100644
--- a/src/vm_ppc.dasc
+++ b/src/vm_ppc.dasc
@@ -474,6 +474,12 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp
+|  lwz tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
+|    addic tmp, tmp, 1
+|  stw tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
+|  lwz tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
+|    subic tmp, tmp, 1
+|  stw tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
 |  lwz tmp, DISPATCH_GL(gc.grayagain)(DISPATCH)
 |  // Assumes LJ_GC_BLACK is 0x04.
 |   rlwinm mark, mark, 0, 30, 28		// black2gray(tab)
diff --git a/src/vm_x64.dasc b/src/vm_x64.dasc
index 80753e0..d37b310 100644
--- a/src/vm_x64.dasc
+++ b/src/vm_x64.dasc
@@ -372,6 +372,8 @@
 |// Move table write barrier back. Overwrites reg.
 |.macro barrierback, tab, reg
 |  and byte tab->marked, (uint8_t)~LJ_GC_BLACK	// black2gray(tab)
+|  sub qword [DISPATCH+DISPATCH_GL(gc.blacknum)], 1
+|  add qword [DISPATCH+DISPATCH_GL(gc.graynum)], 1
 |  mov reg, [DISPATCH+DISPATCH_GL(gc.grayagain)]
 |  mov [DISPATCH+DISPATCH_GL(gc.grayagain)], tab
 |  mov tab->gclist, reg
diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc
index 56bee14..3e35cc6 100644
--- a/src/vm_x86.dasc
+++ b/src/vm_x86.dasc
@@ -477,6 +477,8 @@
 |// Move table write barrier back. Overwrites reg.
 |.macro barrierback, tab, reg
 |  and byte tab->marked, (uint8_t)~LJ_GC_BLACK	// black2gray(tab)
+|  sub dword [DISPATCH+DISPATCH_GL(gc.blacknum)], 1
+|  add dword [DISPATCH+DISPATCH_GL(gc.graynum)], 1
 |  mov reg, [DISPATCH+DISPATCH_GL(gc.grayagain)]
 |  mov [DISPATCH+DISPATCH_GL(gc.grayagain)], tab
 |  mov tab->gclist, reg
-- 
2.28.0

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [PATCH v3 2/2] misc: add C and Lua API for platform metrics
  2020-09-20 17:12 [Tarantool-patches] [PATCH v3 0/2] Implement LuaJIT platform metrics Sergey Kaplun
  2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 1/2] core: introduce various " Sergey Kaplun
@ 2020-09-20 17:12 ` Sergey Kaplun
  2020-10-05  6:39   ` Sergey Kaplun
  2020-09-20 17:13 ` [Tarantool-patches] [RFC v3] rfc: luajit metrics Sergey Kaplun
  2020-09-21 15:15 ` Sergey Kaplun
  3 siblings, 1 reply; 8+ messages in thread
From: Sergey Kaplun @ 2020-09-20 17:12 UTC (permalink / raw)
  To: Igor Munkin, Sergey Ostanevich; +Cc: tarantool-patches

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                                |  76 +++
 src/lj_mapi.c                                 |  65 +++
 src/ljamalg.c                                 |   2 +
 src/lmisclib.h                                |  71 +++
 src/luaconf.h                                 |   1 +
 test/clib-misclib-getmetrics.test.lua         | 188 +++++++
 test/clib-misclib-getmetrics/CMakeLists.txt   |   1 +
 test/clib-misclib-getmetrics/testgetmetrics.c | 287 +++++++++++
 test/lib-misc-getmetrics.test.lua             | 459 ++++++++++++++++++
 13 files changed, 1165 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..287b059
--- /dev/null
+++ b/src/lib_misc.c
@@ -0,0 +1,76 @@
+/*
+** 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_whitenum", metrics.gc_whitenum);
+  setnumfield(L, m, "gc_graynum", metrics.gc_graynum);
+  setnumfield(L, m, "gc_blacknum", metrics.gc_blacknum);
+
+  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..a3fb645
--- /dev/null
+++ b/src/lj_mapi.c
@@ -0,0 +1,65 @@
+/*
+** 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_whitenum = gc->whitenum;
+  metrics->gc_graynum = gc->graynum;
+  metrics->gc_blacknum = gc->blacknum;
+
+  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..e4970b5
--- /dev/null
+++ b/src/lmisclib.h
@@ -0,0 +1,71 @@
+/*
+** 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;
+
+  /* Amount of white objects. */
+  size_t gc_whitenum;
+  /* Amount of gray objects. */
+  size_t gc_graynum;
+  /* Amount of black objects. */
+  size_t gc_blacknum;
+
+  /* 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..840840c
--- /dev/null
+++ b/test/clib-misclib-getmetrics.test.lua
@@ -0,0 +1,188 @@
+#!/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(13)
+
+local testgetmetrics = require("testgetmetrics")
+
+test:ok(testgetmetrics.base())
+test:ok(testgetmetrics.gc_allocated_freed())
+test:ok(testgetmetrics.gc_colors_base())
+test:ok(testgetmetrics.gc_colors_strempty())
+
+-- Upvalues are never gray.
+tbar_t = {}
+test:ok(testgetmetrics.gc_colors_tbar(function()
+    local tabnew = require"table.new"
+    for i =1, 100 do
+        tbar_t.x = tabnew(i,0)
+    end
+    collectgarbage("collect")
+    tbar_t = nil
+end))
+
+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@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..85f0505
--- /dev/null
+++ b/test/clib-misclib-getmetrics/testgetmetrics.c
@@ -0,0 +1,287 @@
+#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_whitenum >= 0);
+	assert((ssize_t)metrics.gc_graynum >= 0);
+	assert((ssize_t)metrics.gc_blacknum >= 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_colors_base(lua_State *L)
+{
+	struct luam_Metrics metrics;
+	lua_gc(L, LUA_GCCOLLECT, 0);
+	luaM_metrics(L, &metrics);
+	/* There are no non-white objects after full gc cycle. */
+	assert(metrics.gc_blacknum == 0);
+	assert(metrics.gc_graynum == 0);
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
+static int gc_colors_strempty(lua_State *L)
+{
+	/*
+	 * Empty string is specially recolored at the end
+	 * of atomic phase.
+	 */
+	lua_pushstring(L, "");
+	/* Check that none of the internal asserts will fail. */
+	for (int i = 0; i < 100; i++)
+		lua_gc(L, LUA_GCCOLLECT, 0);
+	lua_pop(L, 1);
+	lua_pushboolean(L, 1);
+	return 1;
+}
+
+static int gc_colors_tbar(lua_State *L)
+{
+	int n = lua_gettop(L);
+	if (n != 1 || !lua_isfunction(L, 1))
+		luaL_error(L, "incorrect arguments: 1 function is required");
+	/* Check that none of the internal asserts will fail. */
+	lua_call(L, 0, 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_colors_base", gc_colors_base},
+	{"gc_colors_strempty", gc_colors_strempty},
+	{"gc_colors_tbar", gc_colors_tbar},
+	{"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..66423ab
--- /dev/null
+++ b/test/lib-misc-getmetrics.test.lua
@@ -0,0 +1,459 @@
+#!/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(13)
+
+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(22)
+    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_whitenum >= 2)
+    subtest:ok(metrics.gc_graynum >= 0)
+    subtest:ok(metrics.gc_blacknum >= 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-colors-base", function(subtest)
+    subtest:plan(2)
+
+    -- Force up garbage collect all dead objects.
+    collectgarbage("collect")
+
+    -- There are no non-white objects after full gc cycle.
+    local metrics = misc.getmetrics()
+    subtest:is(metrics.gc_graynum, 0)
+    subtest:is(metrics.gc_blacknum, 0)
+end)
+
+test:test("gc-colors-stremty", function(subtest)
+    subtest:plan(1)
+    -- Empty string is specially recolored at the end
+    -- of atomic phase.
+    local strempty = ""
+    -- Check that none of the internal asserts will fail.
+    for i=1, 100 do
+        collectgarbage("collect")
+    end
+    subtest:ok(true, "no assertion failed")
+end)
+
+-- Upvalues are never gray.
+tbar_t = {}
+test:test("gc-colors-tbar", function(subtest)
+    subtest:plan(1)
+    local tabnew = require"table.new"
+    for i =1, 100 do
+        tbar_t.x = tabnew(i,0)
+    end
+    collectgarbage("collect")
+    tbar_t = nil
+    subtest:ok(true, "no assertion failed")
+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@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 == 22)
+    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 == 23)
+    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 == 22)
+    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 == 22)
+    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

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [RFC v3] rfc: luajit metrics
  2020-09-20 17:12 [Tarantool-patches] [PATCH v3 0/2] Implement LuaJIT platform metrics Sergey Kaplun
  2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 1/2] core: introduce various " Sergey Kaplun
  2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 2/2] misc: add C and Lua API for " Sergey Kaplun
@ 2020-09-20 17:13 ` Sergey Kaplun
  2020-09-21 15:18   ` Sergey Kaplun
  2020-09-21 15:15 ` Sergey Kaplun
  3 siblings, 1 reply; 8+ messages in thread
From: Sergey Kaplun @ 2020-09-20 17:13 UTC (permalink / raw)
  To: Igor Munkin, Sergey Ostanevich; +Cc: tarantool-patches

Part of #5187
---

This patch adds RFC to LuaJIT metrics interfaces. Nevertheless name
`misc` for builtin library is not good and should be discussed, because
tons of user modules can use that name for their own libraries.

Branch: https://github.com/tarantool/tarantool/tree/skaplun/5187-luajit-metrics
Issue: https://github.com/tarantool/tarantool/issues/5187

Changes in v2:
- Fixed typos
- Made comments more verbose
- Avoided flushing any of metrics after each call of luaM_metrics()
Changes in v3:
- Added colors count metrics description
- Added description about how metrics are collected
- Added benchmarks

diff --git a/doc/rfc/5187-luajit-metrics.md b/doc/rfc/5187-luajit-metrics.md
index 4ed066cab..b3839c590 100644
--- a/doc/rfc/5187-luajit-metrics.md
+++ b/doc/rfc/5187-luajit-metrics.md
@@ -48,13 +48,20 @@ struct luam_Metrics {
   size_t strhash_miss;
 
   /* Amount of allocated string objects. */
-  size_t strnum;
+  size_t gc_strnum;
   /* Amount of allocated table objects. */
-  size_t tabnum;
+  size_t gc_tabnum;
   /* Amount of allocated udata objects. */
-  size_t udatanum;
+  size_t gc_udatanum;
   /* Amount of allocated cdata objects. */
-  size_t cdatanum;
+  size_t gc_cdatanum;
+
+  /* Amount of white objects. */
+  size_t gc_whitenum;
+  /* Amount of gray objects. */
+  size_t gc_graynum;
+  /* Amount of black objects. */
+  size_t gc_blacknum;
 
   /* Memory currently allocated. */
   size_t gc_total;
@@ -71,8 +78,10 @@ struct luam_Metrics {
   size_t gc_steps_sweep;
   size_t gc_steps_finalize;
 
-  /* Overall number of snap restores (amount of guard assertions
-  ** leading to stopping trace executions)
+  /*
+  ** 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. */
@@ -84,6 +93,32 @@ struct luam_Metrics {
 };
 ```
 
+Couple of words about how metrics are collected:
+- `strhash_*` -- whenever existing string is returned after attemption to
+  create new string there is incremented `strhash_hit` counter, if new string
+  created then `strhash_miss` is incremented instead.
+- `gc_*num`, `jit_trace_num` -- corresponding counter incremented whenever new
+  object is allocated. When object become garbage collected its counter is
+  decremented.
+- `gc_whitenum`, `gc_graynum`, `gc_blacknum` -- in so far as all objects are
+  created  as the current white, `gc_whitenum` is incremented at any object
+  creation. Whenever  color of object changes counter for old color is
+  decremented and counter for  new color is incremented instead.
+  *NB*: after full cycle of Garbage Collector there are only white objects.
+- `gc_total`, `gc_allocated`, `gc_freed` -- any time when allocation function
+  is called `gc_allocated` and/or `gc_freed` is increased and `gc_total`
+  increase when memory is allocated or reallocated, decrease when memory is
+  freed.
+- `gc_steps_*` -- corresponding counter increments whenever Garbage Collector
+  starts to execute 1 step of garbage collection.
+- `jit_snap_restore` -- whenever JIT machine exits from the trace and restores
+  interpreter state `jit_snap_restore` counter is incremented.
+- `jit_trace_abort` -- whenever JIT compiler can't record the trace in case NYI
+  BC this counter is incremented.
+- `jit_mcode_size` -- whenever new MCode area is allocated `jit_mcode_size` is
+  increased at corresponding size in bytes. Sets to 0 when all mcode area is
+  freed.
+
 All metrics are collected throughout the platform uptime. These metrics
 increase monotonically and can overflow:
   - `strhash_hit`
@@ -113,24 +148,71 @@ Tarantool 2.5.0-267-gbf047ad44
 type 'help' for interactive help
 tarantool> misc.getmetrics()
 ---
-- tabnum: 1812
-  gc_total: 1369927
-  strnum: 5767
-  jit_trace_num: 0
-  cdatanum: 89
-  jit_mcode_size: 0
-  udatanum: 17
-  jit_snap_restore: 0
-  gc_freed: 2239391
-  strhash_hit: 53759
-  gc_steps_finalize: 0
-  gc_allocated: 3609318
+- gc_graynum: 4443
+  strhash_hit: 53965
   gc_steps_atomic: 6
-  gc_steps_sweep: 296
+  strhash_miss: 6879
   gc_steps_sweepstring: 17920
+  gc_strnum: 5759
+  gc_tabnum: 1813
+  gc_cdatanum: 89
+  jit_snap_restore: 0
+  gc_total: 1370836
+  gc_udatanum: 17
+  gc_steps_finalize: 0
+  gc_allocated: 3616689
+  jit_trace_num: 0
+  gc_whitenum: 3460
+  jit_mcode_size: 0
+  gc_steps_sweep: 297
   jit_trace_abort: 0
-  strhash_miss: 6874
-  gc_steps_propagate: 10106
+  gc_freed: 2245853
   gc_steps_pause: 7
+  gc_steps_propagate: 10171
+  gc_blacknum: 3979
 ...
 ```
+
+## Benchmarks
+
+Benchmarks was taken from repo:
+[LuaJIT-test-cleanup](https://github.com/LuaJIT/LuaJIT-test-cleanup).
+
+Example of usage:
+```
+/usr/bin/time -f"array3d %U" ./luajit $BENCH_DIR/array3d.lua  300 >/dev/null
+```
+Taking into account the measurement error ~ 2%, it can be said that there is no
+difference in the performance.
+
+Benchmark results after and before patch (less is better):
+```
+   Benchmark   | AFTER (s) | BEFORE (s)
+---------------+-----------+-----------
+array3d        |   0.21    |   0.20
+binary-trees   |   3.34    |   3.24
+chameneos      |   2.95    |   2.99
+coroutine-ring |   1.02    |   1.02
+euler14-bit    |   1.04    |   1.05
+fannkuch       |   6.99    |   6.81
+fasta          |   8.28    |   8.28
+life           |   0.48    |   0.46
+mandelbrot     |   2.66    |   2.68
+mandelbrot-bit |   2.01    |   1.97
+md5            |   1.59    |   1.54
+nbody          |   1.36    |   1.56
+nsieve         |   2.11    |   2.06
+nsieve-bit     |   1.54    |   1.50
+nsieve-bit-fp  |   4.51    |   4.60
+partialsums    |   0.58    |   0.55
+pidigits-nogmp |   3.48    |   3.46
+ray            |   1.62    |   1.63
+recursive-ack  |   0.19    |   0.20
+recursive-fib  |   1.64    |   1.67
+scimark-fft    |   5.84    |   5.86
+scimark-lu     |   3.33    |   3.64
+scimark-sor    |   2.34    |   2.34
+scimark-sparse |   4.99    |   4.93
+series         |   0.95    |   0.94
+spectral-norm  |   0.95    |   0.97
+```

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Tarantool-patches] [RFC v3] rfc: luajit metrics
  2020-09-20 17:12 [Tarantool-patches] [PATCH v3 0/2] Implement LuaJIT platform metrics Sergey Kaplun
                   ` (2 preceding siblings ...)
  2020-09-20 17:13 ` [Tarantool-patches] [RFC v3] rfc: luajit metrics Sergey Kaplun
@ 2020-09-21 15:15 ` Sergey Kaplun
  3 siblings, 0 replies; 8+ messages in thread
From: Sergey Kaplun @ 2020-09-21 15:15 UTC (permalink / raw)
  To: Igor Munkin, Sergey Ostanevich; +Cc: tarantool-patches

Part of #5187
---

This patch adds RFC to LuaJIT metrics interfaces. Nevertheless name
`misc` for builtin library is not good and should be discussed, because
tons of user modules can use that name for their own libraries.

Branch: https://github.com/tarantool/tarantool/tree/skaplun/5187-luajit-metrics
Issue: https://github.com/tarantool/tarantool/issues/5187

Changes in v2:
- Fixed typos
- Made comments more verbose
- Avoided flushing any of metrics after each call of luaM_metrics()
Changes in v3:
- Added colors count metrics description
- Added description about how metrics are collected
- Added benchmarks

 doc/rfc/5187-luajit-metrics.md | 218 +++++++++++++++++++++++++++++++++
 1 file changed, 218 insertions(+)
 create mode 100644 doc/rfc/5187-luajit-metrics.md

diff --git a/doc/rfc/5187-luajit-metrics.md b/doc/rfc/5187-luajit-metrics.md
new file mode 100644
index 000000000..b3839c590
--- /dev/null
+++ b/doc/rfc/5187-luajit-metrics.md
@@ -0,0 +1,218 @@
+# LuaJIT metrics
+
+* **Status**: In progress
+* **Start date**: 17-07-2020
+* **Authors**: Sergey Kaplun @Buristan skaplun@tarantool.org,
+               Igor Munkin @igormunkin imun@tarantool.org,
+               Sergey Ostanevich @sergos sergos@tarantool.org
+* **Issues**: [#5187](https://github.com/tarantool/tarantool/issues/5187)
+
+## Summary
+
+LuaJIT metrics provide extra information about the Lua state. They consist of
+GC metrics (overall amount of objects and memory usage), JIT stats (both
+related to the compiled traces and the engine itself), string hash hits/misses.
+
+## Background and motivation
+
+One can be curious about their application performance. We are going to provide
+various metrics about the several platform subsystems behaviour. GC pressure
+produced by user code can weight down all application performance. Irrelevant
+traces compiled by the JIT engine can just burn CPU time with no benefits as a
+result. String hash collisions can lead to DoS caused by a single request. All
+these metrics should be well monitored by users wanting to improve the
+performance of their application.
+
+## Detailed design
+
+The additional header <lmisclib.h> is introduced to extend the existing LuaJIT
+C API with new interfaces. The first function provided via this header is the
+following:
+
+```
+/* API for obtaining various platform metrics. */
+
+LUAMISC_API void luaM_metrics(lua_State *L, struct luam_Metrics *metrics);
+```
+
+This function fills the structure pointed to by `metrics` with the corresponding
+metrics related to Lua state anchored to the given coroutine `L`.
+
+The `struct luam_Metrics` has the following definition:
+
+```
+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;
+
+  /* Amount of white objects. */
+  size_t gc_whitenum;
+  /* Amount of gray objects. */
+  size_t gc_graynum;
+  /* Amount of black objects. */
+  size_t gc_blacknum;
+
+  /* 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;
+};
+```
+
+Couple of words about how metrics are collected:
+- `strhash_*` -- whenever existing string is returned after attemption to
+  create new string there is incremented `strhash_hit` counter, if new string
+  created then `strhash_miss` is incremented instead.
+- `gc_*num`, `jit_trace_num` -- corresponding counter incremented whenever new
+  object is allocated. When object become garbage collected its counter is
+  decremented.
+- `gc_whitenum`, `gc_graynum`, `gc_blacknum` -- in so far as all objects are
+  created  as the current white, `gc_whitenum` is incremented at any object
+  creation. Whenever  color of object changes counter for old color is
+  decremented and counter for  new color is incremented instead.
+  *NB*: after full cycle of Garbage Collector there are only white objects.
+- `gc_total`, `gc_allocated`, `gc_freed` -- any time when allocation function
+  is called `gc_allocated` and/or `gc_freed` is increased and `gc_total`
+  increase when memory is allocated or reallocated, decrease when memory is
+  freed.
+- `gc_steps_*` -- corresponding counter increments whenever Garbage Collector
+  starts to execute 1 step of garbage collection.
+- `jit_snap_restore` -- whenever JIT machine exits from the trace and restores
+  interpreter state `jit_snap_restore` counter is incremented.
+- `jit_trace_abort` -- whenever JIT compiler can't record the trace in case NYI
+  BC this counter is incremented.
+- `jit_mcode_size` -- whenever new MCode area is allocated `jit_mcode_size` is
+  increased at corresponding size in bytes. Sets to 0 when all mcode area is
+  freed.
+
+All metrics are collected throughout the platform uptime. These metrics
+increase monotonically and can overflow:
+  - `strhash_hit`
+  - `strhash_miss`
+  - `gc_freed`
+  - `gc_allocated`,
+  - `gc_steps_pause`
+  - `gc_steps_propagate`
+  - `gc_steps_atomic`
+  - `gc_steps_sweepstring`
+  - `gc_steps_sweep`
+  - `gc_steps_finalize`
+  - `jit_snap_restore`
+  - `jit_trace_abort`
+
+They make sense only with comparing with their value from a previous
+`luaM_metrics()` call.
+
+There is also a complement introduced for Lua space -- `misc.getmetrics()`.
+This function is just a wrapper for `luaM_metrics()` returning a Lua table with
+the similar metrics. All returned values are presented as numbers with cast to
+double, so there is a corresponding precision loss. Function usage is quite
+simple:
+```
+$ ./src/tarantool
+Tarantool 2.5.0-267-gbf047ad44
+type 'help' for interactive help
+tarantool> misc.getmetrics()
+---
+- gc_graynum: 4443
+  strhash_hit: 53965
+  gc_steps_atomic: 6
+  strhash_miss: 6879
+  gc_steps_sweepstring: 17920
+  gc_strnum: 5759
+  gc_tabnum: 1813
+  gc_cdatanum: 89
+  jit_snap_restore: 0
+  gc_total: 1370836
+  gc_udatanum: 17
+  gc_steps_finalize: 0
+  gc_allocated: 3616689
+  jit_trace_num: 0
+  gc_whitenum: 3460
+  jit_mcode_size: 0
+  gc_steps_sweep: 297
+  jit_trace_abort: 0
+  gc_freed: 2245853
+  gc_steps_pause: 7
+  gc_steps_propagate: 10171
+  gc_blacknum: 3979
+...
+```
+
+## Benchmarks
+
+Benchmarks was taken from repo:
+[LuaJIT-test-cleanup](https://github.com/LuaJIT/LuaJIT-test-cleanup).
+
+Example of usage:
+```
+/usr/bin/time -f"array3d %U" ./luajit $BENCH_DIR/array3d.lua  300 >/dev/null
+```
+Taking into account the measurement error ~ 2%, it can be said that there is no
+difference in the performance.
+
+Benchmark results after and before patch (less is better):
+```
+   Benchmark   | AFTER (s) | BEFORE (s)
+---------------+-----------+-----------
+array3d        |   0.21    |   0.20
+binary-trees   |   3.34    |   3.24
+chameneos      |   2.95    |   2.99
+coroutine-ring |   1.02    |   1.02
+euler14-bit    |   1.04    |   1.05
+fannkuch       |   6.99    |   6.81
+fasta          |   8.28    |   8.28
+life           |   0.48    |   0.46
+mandelbrot     |   2.66    |   2.68
+mandelbrot-bit |   2.01    |   1.97
+md5            |   1.59    |   1.54
+nbody          |   1.36    |   1.56
+nsieve         |   2.11    |   2.06
+nsieve-bit     |   1.54    |   1.50
+nsieve-bit-fp  |   4.51    |   4.60
+partialsums    |   0.58    |   0.55
+pidigits-nogmp |   3.48    |   3.46
+ray            |   1.62    |   1.63
+recursive-ack  |   0.19    |   0.20
+recursive-fib  |   1.64    |   1.67
+scimark-fft    |   5.84    |   5.86
+scimark-lu     |   3.33    |   3.64
+scimark-sor    |   2.34    |   2.34
+scimark-sparse |   4.99    |   4.93
+series         |   0.95    |   0.94
+spectral-norm  |   0.95    |   0.97
+```
-- 
2.28.0

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Tarantool-patches] [RFC v3] rfc: luajit metrics
  2020-09-20 17:13 ` [Tarantool-patches] [RFC v3] rfc: luajit metrics Sergey Kaplun
@ 2020-09-21 15:18   ` Sergey Kaplun
  0 siblings, 0 replies; 8+ messages in thread
From: Sergey Kaplun @ 2020-09-21 15:18 UTC (permalink / raw)
  To: Igor Munkin, Sergey Ostanevich; +Cc: tarantool-patches

Sorri, I send this letter by a mistake. Feel free to ignore it.

Correct version is [1].

[1]: https://lists.tarantool.org/pipermail/tarantool-patches/2020-September/019546.html

-- 
Best regards,
Sergey Kaplun

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Tarantool-patches] [PATCH v3 1/2] core: introduce various platform metrics
  2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 1/2] core: introduce various " Sergey Kaplun
@ 2020-10-05  6:36   ` Sergey Kaplun
  0 siblings, 0 replies; 8+ messages in thread
From: Sergey Kaplun @ 2020-10-05  6:36 UTC (permalink / raw)
  To: Igor Munkin, Sergey Ostanevich; +Cc: tarantool-patches

On 20.09.20, Sergey Kaplun wrote:
> This patch introduces the following counters:
<snipped>
> 

This patch isn't relevant. You can see actual version here [1].

You can apply -R corresponding diff to return to this version.
==================================================================
diff --git a/src/lj_asm_arm.h b/src/lj_asm_arm.h
index 62f269e..4fd08b9 100644
--- a/src/lj_asm_arm.h
+++ b/src/lj_asm_arm.h
@@ -1289,18 +1289,6 @@ static void asm_tbar(ASMState *as, IRIns *ir)
 		     rset_exclude(rset_exclude(RSET_GPR, tab), link));
   Reg mark = RID_TMP;
   MCLabel l_end = emit_label(as);
-  emit_lso(as, ARMI_STR, RID_TMP, gr,
-	   (int32_t)offsetof(global_State, gc.graynum));
-  emit_dn(as, ARMI_ADD|ARMI_K12|1, RID_TMP, RID_TMP);
-  emit_lso(as, ARMI_LDR, RID_TMP, gr,
-	   (int32_t)offsetof(global_State, gc.graynum));
-
-  emit_lso(as, ARMI_STR, RID_TMP, gr,
-	   (int32_t)offsetof(global_State, gc.blacknum));
-  emit_dn(as, ARMI_SUB|ARMI_K12|1, RID_TMP, RID_TMP);
-  emit_lso(as, ARMI_LDR, RID_TMP, gr,
-	   (int32_t)offsetof(global_State, gc.blacknum));
-
   emit_lso(as, ARMI_STR, link, tab, (int32_t)offsetof(GCtab, gclist));
   emit_lso(as, ARMI_STRB, mark, tab, (int32_t)offsetof(GCtab, marked));
   emit_lso(as, ARMI_STR, tab, gr,
diff --git a/src/lj_asm_arm64.h b/src/lj_asm_arm64.h
index 6be912e..a32ba2d 100644
--- a/src/lj_asm_arm64.h
+++ b/src/lj_asm_arm64.h
@@ -1252,18 +1252,6 @@ static void asm_tbar(ASMState *as, IRIns *ir)
 		     rset_exclude(rset_exclude(RSET_GPR, tab), link));
   Reg mark = RID_TMP;
   MCLabel l_end = emit_label(as);
-  emit_lso(as, A64I_STRx, RID_TMP, gr,
-	   (int32_t)offsetof(global_State, gc.graynum));
-  emit_dn(as, (A64I_ADDx^A64I_K12) | A64F_U12(1), RID_TMP, RID_TMP);
-  emit_lso(as, A64I_LDRx, RID_TMP, gr,
-	   (int32_t)offsetof(global_State, gc.graynum));
-
-  emit_lso(as, A64I_STRx, RID_TMP, gr,
-	   (int32_t)offsetof(global_State, gc.blacknum));
-  emit_dn(as, (A64I_SUBx^A64I_K12) | A64F_U12(1), RID_TMP, RID_TMP);
-  emit_lso(as, A64I_LDRx, RID_TMP, gr,
-	   (int32_t)offsetof(global_State, gc.blacknum));
-
   emit_lso(as, A64I_STRx, link, tab, (int32_t)offsetof(GCtab, gclist));
   emit_lso(as, A64I_STRB, mark, tab, (int32_t)offsetof(GCtab, marked));
   emit_lso(as, A64I_STRx, tab, gr,
diff --git a/src/lj_asm_mips.h b/src/lj_asm_mips.h
index 0a1900f..f4b4b5d 100644
--- a/src/lj_asm_mips.h
+++ b/src/lj_asm_mips.h
@@ -1504,12 +1504,6 @@ static void asm_tbar(ASMState *as, IRIns *ir)
   emit_tsi(as, MIPSI_SB, mark, tab, (int32_t)offsetof(GCtab, marked));
   emit_setgl(as, tab, gc.grayagain);
   emit_getgl(as, link, gc.grayagain);
-  emit_setgl(as, RID_TMP, gc.graynum);
-  emit_tsi(as, MIPSI_AADDIU, RID_TMP, RID_TMP, 1);
-  emit_getgl(as, RID_TMP, gc.graynum);
-  emit_setgl(as, RID_TMP, gc.blacknum);
-  emit_tsi(as, MIPSI_AADDIU, RID_TMP, RID_TMP, -1);
-  emit_getgl(as, RID_TMP, gc.blacknum);
   emit_dst(as, MIPSI_XOR, mark, mark, RID_TMP);  /* Clear black bit. */
   emit_branch(as, MIPSI_BEQ, RID_TMP, RID_ZERO, l_end);
   emit_tsi(as, MIPSI_ANDI, RID_TMP, mark, LJ_GC_BLACK);
diff --git a/src/lj_asm_ppc.h b/src/lj_asm_ppc.h
index be4b872..aa2d45c 100644
--- a/src/lj_asm_ppc.h
+++ b/src/lj_asm_ppc.h
@@ -1089,12 +1089,6 @@ static void asm_tbar(ASMState *as, IRIns *ir)
   lua_assert(LJ_GC_BLACK == 0x04);
   emit_rot(as, PPCI_RLWINM, mark, mark, 0, 30, 28);  /* Clear black bit. */
   emit_getgl(as, link, gc.grayagain);
-  emit_setgl(as, RID_TMP, gc.graynum);
-  emit_tai(as, PPCI_ADDIC, RID_TMP, RID_TMP, 1);
-  emit_getgl(as, RID_TMP, gc.graynum);
-  emit_setgl(as, RID_TMP, gc.blacknum);
-  emit_tai(as, PPCI_ADDIC, RID_TMP, RID_TMP, -1);
-  emit_getgl(as, RID_TMP, gc.blacknum);
   emit_condbranch(as, PPCI_BC|PPCF_Y, CC_EQ, l_end);
   emit_asi(as, PPCI_ANDIDOT, RID_TMP, mark, LJ_GC_BLACK);
   emit_tai(as, PPCI_LBZ, mark, tab, (int32_t)offsetof(GCtab, marked));
diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h
index 8c9cdaa..959fc2d 100644
--- a/src/lj_asm_x86.h
+++ b/src/lj_asm_x86.h
@@ -1865,12 +1865,6 @@ static void asm_tbar(ASMState *as, IRIns *ir)
   emit_movtomro(as, tmp|REX_GC64, tab, offsetof(GCtab, gclist));
   emit_setgl(as, tab, gc.grayagain);
   emit_getgl(as, tmp, gc.grayagain);
-  emit_i8(as, 1);
-  emit_rmro(as, XO_ARITHi8, XOg_ADD, RID_NONE,
-	    ptr2addr(&J2G(as->J)->gc.graynum));
-  emit_i8(as, 1);
-  emit_rmro(as, XO_ARITHi8, XOg_SUB, RID_NONE,
-	    ptr2addr(&J2G(as->J)->gc.blacknum));
   emit_i8(as, ~LJ_GC_BLACK);
   emit_rmro(as, XO_ARITHib, XOg_AND, tab, offsetof(GCtab, marked));
   emit_sjcc(as, CC_Z, l_end);
diff --git a/src/lj_cdata.c b/src/lj_cdata.c
index 5870ac4..0dd8553 100644
--- a/src/lj_cdata.c
+++ b/src/lj_cdata.c
@@ -64,13 +64,6 @@ void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd)
 {
   if (LJ_UNLIKELY(cd->marked & LJ_GC_CDATA_FIN)) {
     GCobj *root;
-    /* Restore old value of color incremented before free. */
-      if (iswhite(obj2gco(cd)))
-	g->gc.whitenum++;
-      else if (isblack(obj2gco(cd)))
-	g->gc.blacknum++;
-      else
-	g->gc.graynum++;
     makewhite(g, obj2gco(cd));
     markfinalized(obj2gco(cd));
     if ((root = gcref(g->gc.mmudata)) != NULL) {
diff --git a/src/lj_gc.c b/src/lj_gc.c
index c7616f6..44c8aa1 100644
--- a/src/lj_gc.c
+++ b/src/lj_gc.c
@@ -33,18 +33,8 @@
 #define GCFINALIZECOST	100
 
 /* Macros to set GCobj colors and flags. */
-#define white2gray(g, x) \
-  ( (g)->gc.whitenum--, \
-    (g)->gc.graynum++, \
-    lua_assert((ssize_t)(g)->gc.whitenum >=2), \
-    (x)->gch.marked &= (uint8_t)~LJ_GC_WHITES )
-
-#define gray2black(g, x) \
-  ( (g)->gc.graynum--, \
-    (g)->gc.blacknum++, \
-    lua_assert((ssize_t)(g)->gc.graynum >=0), \
-    (x)->gch.marked |= LJ_GC_BLACK )
-
+#define white2gray(x)		((x)->gch.marked &= (uint8_t)~LJ_GC_WHITES)
+#define gray2black(x)		((x)->gch.marked |= LJ_GC_BLACK)
 #define isfinalized(u)		((u)->marked & LJ_GC_FINALIZED)
 
 /* -- Mark phase ---------------------------------------------------------- */
@@ -59,30 +49,24 @@
   { if (iswhite(obj2gco(o))) gc_mark(g, obj2gco(o)); }
 
 /* Mark a string object. */
-#define gc_mark_str(g, s) \
-  { if ((s)->marked & LJ_GC_WHITES) { \
-      (g)->gc.whitenum--; \
-      (g)->gc.graynum++; \
-      lua_assert((ssize_t)(g)->gc.whitenum >=2); \
-    }; \
-    (s)->marked &= (uint8_t)~LJ_GC_WHITES; }
+#define gc_mark_str(s)		((s)->marked &= (uint8_t)~LJ_GC_WHITES)
 
 /* Mark a white GCobj. */
 static void gc_mark(global_State *g, GCobj *o)
 {
   int gct = o->gch.gct;
   lua_assert(iswhite(o) && !isdead(g, o));
-  white2gray(g, o);
+  white2gray(o);
   if (LJ_UNLIKELY(gct == ~LJ_TUDATA)) {
     GCtab *mt = tabref(gco2ud(o)->metatable);
-    gray2black(g, o);  /* Userdata are never gray. */
+    gray2black(o);  /* Userdata are never gray. */
     if (mt) gc_markobj(g, mt);
     gc_markobj(g, tabref(gco2ud(o)->env));
   } else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) {
     GCupval *uv = gco2uv(o);
     gc_marktv(g, uvval(uv));
     if (uv->closed)
-      gray2black(g, o);  /* Closed upvalues are never gray. */
+      gray2black(o);  /* Closed upvalues are never gray. */
   } else if (gct != ~LJ_TSTR && gct != ~LJ_TCDATA) {
     lua_assert(gct == ~LJ_TFUNC || gct == ~LJ_TTAB ||
 	       gct == ~LJ_TTHREAD || gct == ~LJ_TPROTO || gct == ~LJ_TTRACE);
@@ -246,7 +230,7 @@ static void gc_marktrace(global_State *g, TraceNo traceno)
   GCobj *o = obj2gco(traceref(G2J(g), traceno));
   lua_assert(traceno != G2J(g)->cur.traceno);
   if (iswhite(o)) {
-    white2gray(g, o);
+    white2gray(o);
     setgcrefr(o->gch.gclist, g->gc.gray);
     setgcref(g->gc.gray, o);
   }
@@ -280,7 +264,7 @@ static void gc_traverse_trace(global_State *g, GCtrace *T)
 static void gc_traverse_proto(global_State *g, GCproto *pt)
 {
   ptrdiff_t i;
-  gc_mark_str(g, proto_chunkname(pt));
+  gc_mark_str(proto_chunkname(pt));
   for (i = -(ptrdiff_t)pt->sizekgc; i < 0; i++)  /* Mark collectable consts. */
     gc_markobj(g, proto_kgc(pt, i));
 #if LJ_HASJIT
@@ -326,12 +310,12 @@ static size_t propagatemark(global_State *g)
   GCobj *o = gcref(g->gc.gray);
   int gct = o->gch.gct;
   lua_assert(isgray(o));
-  gray2black(g, o);
+  gray2black(o);
   setgcrefr(g->gc.gray, o->gch.gclist);  /* Remove from gray list. */
   if (LJ_LIKELY(gct == ~LJ_TTAB)) {
     GCtab *t = gco2tab(o);
     if (gc_traverse_tab(g, t) > 0)
-      black2gray(g, o);  /* Keep weak tables gray. */
+      black2gray(o);  /* Keep weak tables gray. */
     return sizeof(GCtab) + sizeof(TValue) * t->asize +
 			   (t->hmask ? sizeof(Node) * (t->hmask + 1) : 0);
   } else if (LJ_LIKELY(gct == ~LJ_TFUNC)) {
@@ -347,7 +331,7 @@ static size_t propagatemark(global_State *g)
     lua_State *th = gco2th(o);
     setgcrefr(th->gclist, g->gc.grayagain);
     setgcref(g->gc.grayagain, o);
-    black2gray(g, o);  /* Threads are never black. */
+    black2gray(o);  /* Threads are never black. */
     gc_traverse_thread(g, th);
     return sizeof(lua_State) + sizeof(TValue) * th->stacksize;
   } else {
@@ -419,16 +403,6 @@ static GCRef *gc_sweep(global_State *g, GCRef *p, uint32_t lim)
       setgcrefr(*p, o->gch.nextgc);
       if (o == gcref(g->gc.root))
 	setgcrefr(g->gc.root, o->gch.nextgc);  /* Adjust list anchor. */
-      if (iswhite(o)) {
-	g->gc.whitenum--;
-	lua_assert((ssize_t)g->gc.whitenum >= 2);
-      } else if (isblack(o)) {
-	g->gc.blacknum--;
-	lua_assert((ssize_t)g->gc.blacknum >= 0);
-      } else {
-	g->gc.graynum--;
-	lua_assert((ssize_t)g->gc.graynum >= 0);
-      }
       gc_freefunc[o->gch.gct - ~LJ_TSTR](g, o);
     }
   }
@@ -456,16 +430,6 @@ static GCRef *gc_sweep_str_chain(global_State *g, GCRef *p)
     } else {  /* Otherwise value is dead, free it. */
       lua_assert(isdead(g, o) || ow == LJ_GC_SFIXED);
       setgcrefr(*p, o->gch.nextgc);
-      if (iswhite(o)) {
-	g->gc.whitenum--;
-	lua_assert((ssize_t)g->gc.whitenum >= 2);
-      } else if (isblack(o)) {
-	g->gc.blacknum--;
-	lua_assert((ssize_t)g->gc.blacknum >= 0);
-      } else {
-	g->gc.graynum--;
-	lua_assert((ssize_t)g->gc.graynum >= 0);
-      }
       lj_str_free(g, &o->str);
     }
   }
@@ -473,11 +437,11 @@ static GCRef *gc_sweep_str_chain(global_State *g, GCRef *p)
 }
 
 /* Check whether we can clear a key or a value slot from a table. */
-static int gc_mayclear(global_State *g, cTValue *o, int val)
+static int gc_mayclear(cTValue *o, int val)
 {
   if (tvisgcv(o)) {  /* Only collectable objects can be weak references. */
     if (tvisstr(o)) {  /* But strings cannot be used as weak references. */
-      gc_mark_str(g, strV(o));  /* And need to be marked. */
+      gc_mark_str(strV(o));  /* And need to be marked. */
       return 0;
     }
     if (iswhite(gcV(o)))
@@ -489,7 +453,7 @@ static int gc_mayclear(global_State *g, cTValue *o, int val)
 }
 
 /* Clear collected entries from weak tables. */
-static void gc_clearweak(global_State *g, GCobj *o)
+static void gc_clearweak(GCobj *o)
 {
   while (o) {
     GCtab *t = gco2tab(o);
@@ -499,7 +463,7 @@ static void gc_clearweak(global_State *g, GCobj *o)
       for (i = 0; i < asize; i++) {
 	/* Clear array slot when value is about to be collected. */
 	TValue *tv = arrayslot(t, i);
-	if (gc_mayclear(g, tv, 1))
+	if (gc_mayclear(tv, 1))
 	  setnilV(tv);
       }
     }
@@ -509,8 +473,8 @@ static void gc_clearweak(global_State *g, GCobj *o)
       for (i = 0; i <= hmask; i++) {
 	Node *n = &node[i];
 	/* Clear hash slot when key or value is about to be collected. */
-	if (!tvisnil(&n->val) && (gc_mayclear(g, &n->key, 0) ||
-				  gc_mayclear(g, &n->val, 1)))
+	if (!tvisnil(&n->val) && (gc_mayclear(&n->key, 0) ||
+				  gc_mayclear(&n->val, 1)))
 	  setnilV(&n->val);
       }
     }
@@ -655,21 +619,12 @@ static void atomic(global_State *g, lua_State *L)
   udsize += gc_propagate_gray(g);  /* And propagate the marks. */
 
   /* All marking done, clear weak tables. */
-  gc_clearweak(g, gcref(g->gc.weak));
+  gc_clearweak(gcref(g->gc.weak));
 
   lj_buf_shrink(L, &g->tmpbuf);  /* Shrink temp buffer. */
 
   /* Prepare for sweep phase. */
   g->gc.currentwhite = (uint8_t)otherwhite(g);  /* Flip current white. */
-  if (g->strempty.marked & (uint8_t)LJ_GC_BLACK) {
-    g->gc.blacknum--;
-    g->gc.whitenum++;
-    lua_assert((ssize_t)g->gc.blacknum >=0);
-  } else if ((g->strempty.marked & (uint8_t)LJ_GC_WHITES) == 0) {
-    g->gc.graynum--;
-    g->gc.whitenum++;
-    lua_assert((ssize_t)g->gc.graynum >=0);
-  }
   g->strempty.marked = g->gc.currentwhite;
   setmref(g->gc.sweep, &g->gc.root);
   g->gc.estimate = g->gc.total - (GCSize)udsize;  /* Initial estimate. */
@@ -854,18 +809,8 @@ void LJ_FASTCALL lj_gc_barrieruv(global_State *g, TValue *tv)
   (*((uint8_t *)(x) - offsetof(GCupval, tv) + offsetof(GCupval, marked)))
   if (g->gc.state == GCSpropagate || g->gc.state == GCSatomic)
     gc_mark(g, gcV(tv));
-  else {
-    if (TV2MARKED(tv) & (uint8_t)LJ_GC_BLACK) {
-      g->gc.blacknum--;
-      g->gc.whitenum++;
-      lua_assert((ssize_t)g->gc.blacknum >=0);
-    } else if ((TV2MARKED(tv) & (uint8_t)LJ_GC_WHITES) == 0) {
-      g->gc.graynum--;
-      g->gc.whitenum++;
-      lua_assert((ssize_t)g->gc.graynum >=0);
-    }
+  else
     TV2MARKED(tv) = (TV2MARKED(tv) & (uint8_t)~LJ_GC_COLORS) | curwhite(g);
-  }
 #undef TV2MARKED
 }
 
@@ -881,7 +826,7 @@ void lj_gc_closeuv(global_State *g, GCupval *uv)
   setgcref(g->gc.root, o);
   if (isgray(o)) {  /* A closed upvalue is never gray, so fix this. */
     if (g->gc.state == GCSpropagate || g->gc.state == GCSatomic) {
-      gray2black(g, o);  /* Make it black and preserve invariant. */
+      gray2black(o);  /* Make it black and preserve invariant. */
       if (tviswhite(&uv->tv))
 	lj_gc_barrierf(g, o, gcV(&uv->tv));
     } else {
diff --git a/src/lj_gc.h b/src/lj_gc.h
index 25e319a..2051220 100644
--- a/src/lj_gc.h
+++ b/src/lj_gc.h
@@ -32,28 +32,11 @@
 #define isdead(g, v)	((v)->gch.marked & otherwhite(g) & LJ_GC_WHITES)
 
 #define curwhite(g)	((g)->gc.currentwhite & LJ_GC_WHITES)
-#define newwhite(g, x) \
-  ( (g)->gc.whitenum++, \
-    obj2gco(x)->gch.marked = (uint8_t)curwhite(g) )
-
+#define newwhite(g, x)	(obj2gco(x)->gch.marked = (uint8_t)curwhite(g))
 #define makewhite(g, x) \
-  ( iswhite(x) ? /* Nothing to do */ : \
-    isblack(x) ? ((g)->gc.blacknum--, (g)->gc.whitenum++) : \
-    (lua_assert(isgray(x)), (g)->gc.graynum--, (g)->gc.whitenum++), \
-    lua_assert((ssize_t)(g)->gc.graynum >=0), \
-    lua_assert((ssize_t)(g)->gc.blacknum >=0), \
-    (x)->gch.marked = ((x)->gch.marked & (uint8_t)~LJ_GC_COLORS) | curwhite(g) )
-
-#define flipwhite(x) \
-  ( lua_assert(!isgray(x) && ! isblack(x)), \
-    (x)->gch.marked ^= LJ_GC_WHITES )
-
-#define black2gray(g, x) \
-  ( (g)->gc.blacknum--, \
-    (g)->gc.graynum++, \
-    lua_assert((ssize_t)(g)->gc.blacknum >=0), \
-    (x)->gch.marked &= (uint8_t)~LJ_GC_BLACK )
-
+  ((x)->gch.marked = ((x)->gch.marked & (uint8_t)~LJ_GC_COLORS) | curwhite(g))
+#define flipwhite(x)	((x)->gch.marked ^= LJ_GC_WHITES)
+#define black2gray(x)	((x)->gch.marked &= (uint8_t)~LJ_GC_BLACK)
 #define fixstring(s)	((s)->marked |= LJ_GC_FIXED)
 #define markfinalized(x)	((x)->gch.marked |= LJ_GC_FINALIZED)
 
@@ -95,7 +78,7 @@ static LJ_AINLINE void lj_gc_barrierback(global_State *g, GCtab *t)
   GCobj *o = obj2gco(t);
   lua_assert(isblack(o) && !isdead(g, o));
   lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause);
-  black2gray(g, o);
+  black2gray(o);
   setgcrefr(t->gclist, g->gc.grayagain);
   setgcref(g->gc.grayagain, o);
 }
diff --git a/src/lj_obj.h b/src/lj_obj.h
index c9a6904..600b68d 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -609,9 +609,6 @@ typedef struct GCState {
 #ifdef LJ_HASFFI
   size_t cdatanum;	/* Amount of allocated cdata objects. */
 #endif
-  size_t whitenum;	/* Amount of white objects. */
-  size_t graynum;	/* Amount of grey objects. */
-  size_t blacknum;	/* Amount of black objects. */
 } GCState;
 
 /* Global state, shared by all threads of a Lua universe. */
@@ -630,7 +627,6 @@ typedef struct global_State {
   lua_Alloc allocf;	/* Memory allocator. */
   void *allocd;		/* Memory allocator data. */
   GCState gc;		/* Garbage collector. */
-  GCRef mainthref;	/* Link to main thread. */
   SBuf tmpbuf;		/* Temporary string buffer. */
   GCstr strempty;	/* Empty string. */
   uint8_t stremptyz;	/* Zero terminator of empty string. */
@@ -638,8 +634,9 @@ typedef struct global_State {
   uint8_t dispatchmode;	/* Dispatch mode. */
   uint8_t vmevmask;	/* VM event mask. */
   int32_t hookcount;	/* Instruction hook countdown. */
+  GCRef mainthref;	/* Link to main thread. */
   TValue registrytv;	/* Anchor for registry. */
-  TValue tmptv, tmptv2;	/* Temporary TValues. */
+  TValue tmptv2, tmptv;	/* Temporary TValues. */
   Node nilnode;		/* Fallback 1-element hash part (nil key and value). */
   GCupval uvhead;	/* Head of double-linked list of all open upvalues. */
   volatile int32_t vmstate;  /* VM state or current JIT code trace number. */
diff --git a/src/lj_state.c b/src/lj_state.c
index d85a02a..1d9c628 100644
--- a/src/lj_state.c
+++ b/src/lj_state.c
@@ -164,10 +164,6 @@ static void close_state(lua_State *L)
   lj_gc_freeall(g);
   lua_assert(gcref(g->gc.root) == obj2gco(L));
   lua_assert(g->strnum == 0);
-  lua_assert(g->gc.blacknum == 0);
-  lua_assert(g->gc.graynum == 0);
-  /* At least lua_State, strempty and state enviroment. */
-  lua_assert(g->gc.whitenum >= 2);
   lj_trace_freestate(g);
 #if LJ_HASFFI
   lj_ctype_freestate(g);
@@ -219,7 +215,6 @@ LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud)
   setgcref(g->gc.root, obj2gco(L));
   setmref(g->gc.sweep, &g->gc.root);
   g->gc.allocated = g->gc.total = sizeof(GG_State);
-  g->gc.whitenum = 2; /* State and strempty. */
   g->gc.pause = LUAI_GCPAUSE;
   g->gc.stepmul = LUAI_GCMUL;
   lj_dispatch_init((GG_State *)L);
diff --git a/src/vm_arm.dasc b/src/vm_arm.dasc
index ea3bf31..d4cdaf5 100644
--- a/src/vm_arm.dasc
+++ b/src/vm_arm.dasc
@@ -227,12 +227,6 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp
-|  ldr tmp, [DISPATCH, #DISPATCH_GL(gc.graynum)]
-|  add tmp, tmp, #1
-|  str tmp, [DISPATCH, #DISPATCH_GL(gc.graynum)]
-|  ldr tmp, [DISPATCH, #DISPATCH_GL(gc.blacknum)]
-|  sub tmp, tmp, #1
-|  str tmp, [DISPATCH, #DISPATCH_GL(gc.blacknum)]
 |  ldr tmp, [DISPATCH, #DISPATCH_GL(gc.grayagain)]
 |   bic mark, mark, #LJ_GC_BLACK		// black2gray(tab)
 |  str tab, [DISPATCH, #DISPATCH_GL(gc.grayagain)]
diff --git a/src/vm_arm64.dasc b/src/vm_arm64.dasc
index a182f35..3eaf376 100644
--- a/src/vm_arm64.dasc
+++ b/src/vm_arm64.dasc
@@ -280,12 +280,6 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp
-|  ldr tmp, GL->gc.blacknum
-|  sub tmp, tmp, #1
-|  str tmp, GL->gc.blacknum
-|  ldr tmp, GL->gc.graynum
-|  add tmp, tmp, #1
-|  str tmp, GL->gc.graynum
 |  ldr tmp, GL->gc.grayagain
 |   and mark, mark, #~LJ_GC_BLACK	// black2gray(tab)
 |  str tab, GL->gc.grayagain
diff --git a/src/vm_mips.dasc b/src/vm_mips.dasc
index 7de8beb..1afd611 100644
--- a/src/vm_mips.dasc
+++ b/src/vm_mips.dasc
@@ -351,12 +351,6 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp, target
-|  lw tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
-|  addiu tmp, tmp, -1
-|  sw tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
-|  lw tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
-|  addiu tmp, tmp, 1
-|  sw tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
 |  lw tmp, DISPATCH_GL(gc.grayagain)(DISPATCH)
 |   andi mark, mark, ~LJ_GC_BLACK & 255		// black2gray(tab)
 |  sw tab, DISPATCH_GL(gc.grayagain)(DISPATCH)
diff --git a/src/vm_mips64.dasc b/src/vm_mips64.dasc
index 30cab6c..c06270a 100644
--- a/src/vm_mips64.dasc
+++ b/src/vm_mips64.dasc
@@ -350,12 +350,6 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp, target
-|  ld tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
-|  addiu tmp, tmp, -1
-|  sd tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
-|  ld tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
-|  addiu tmp, tmp, 1
-|  sd tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
 |  ld tmp, DISPATCH_GL(gc.grayagain)(DISPATCH)
 |   andi mark, mark, ~LJ_GC_BLACK & 255		// black2gray(tab)
 |  sd tab, DISPATCH_GL(gc.grayagain)(DISPATCH)
diff --git a/src/vm_ppc.dasc b/src/vm_ppc.dasc
index 3015869..b4260eb 100644
--- a/src/vm_ppc.dasc
+++ b/src/vm_ppc.dasc
@@ -474,12 +474,6 @@
 |
 |// Move table write barrier back. Overwrites mark and tmp.
 |.macro barrierback, tab, mark, tmp
-|  lwz tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
-|    addic tmp, tmp, 1
-|  stw tmp, DISPATCH_GL(gc.graynum)(DISPATCH)
-|  lwz tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
-|    subic tmp, tmp, 1
-|  stw tmp, DISPATCH_GL(gc.blacknum)(DISPATCH)
 |  lwz tmp, DISPATCH_GL(gc.grayagain)(DISPATCH)
 |  // Assumes LJ_GC_BLACK is 0x04.
 |   rlwinm mark, mark, 0, 30, 28		// black2gray(tab)
diff --git a/src/vm_x64.dasc b/src/vm_x64.dasc
index d37b310..80753e0 100644
--- a/src/vm_x64.dasc
+++ b/src/vm_x64.dasc
@@ -372,8 +372,6 @@
 |// Move table write barrier back. Overwrites reg.
 |.macro barrierback, tab, reg
 |  and byte tab->marked, (uint8_t)~LJ_GC_BLACK	// black2gray(tab)
-|  sub qword [DISPATCH+DISPATCH_GL(gc.blacknum)], 1
-|  add qword [DISPATCH+DISPATCH_GL(gc.graynum)], 1
 |  mov reg, [DISPATCH+DISPATCH_GL(gc.grayagain)]
 |  mov [DISPATCH+DISPATCH_GL(gc.grayagain)], tab
 |  mov tab->gclist, reg
diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc
index 3e35cc6..56bee14 100644
--- a/src/vm_x86.dasc
+++ b/src/vm_x86.dasc
@@ -477,8 +477,6 @@
 |// Move table write barrier back. Overwrites reg.
 |.macro barrierback, tab, reg
 |  and byte tab->marked, (uint8_t)~LJ_GC_BLACK	// black2gray(tab)
-|  sub dword [DISPATCH+DISPATCH_GL(gc.blacknum)], 1
-|  add dword [DISPATCH+DISPATCH_GL(gc.graynum)], 1
 |  mov reg, [DISPATCH+DISPATCH_GL(gc.grayagain)]
 |  mov [DISPATCH+DISPATCH_GL(gc.grayagain)], tab
 |  mov tab->gclist, reg
==================================================================

[1]: https://lists.tarantool.org/pipermail/tarantool-patches/2020-October/019881.html

-- 
Best regards,
Sergey Kaplun

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Tarantool-patches] [PATCH v3 2/2] misc: add C and Lua API for platform metrics
  2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 2/2] misc: add C and Lua API for " Sergey Kaplun
@ 2020-10-05  6:39   ` Sergey Kaplun
  0 siblings, 0 replies; 8+ messages in thread
From: Sergey Kaplun @ 2020-10-05  6:39 UTC (permalink / raw)
  To: Igor Munkin, Sergey Ostanevich; +Cc: tarantool-patches

On 20.09.20, Sergey Kaplun wrote:
> This patch introduces both C and Lua API for LuaJIT platform
<snipped>
> 
This patch isn't relevant. You can see actual version here [1].

You can apply -R corresponding diff to return to this version.
==================================================================
diff --git a/src/lib_misc.c b/src/lib_misc.c
index 287b059..ef11237 100644
--- a/src/lib_misc.c
+++ b/src/lib_misc.c
@@ -42,10 +42,6 @@ LJLIB_CF(misc_getmetrics)
   setnumfield(L, m, "gc_udatanum", metrics.gc_udatanum);
   setnumfield(L, m, "gc_cdatanum", metrics.gc_cdatanum);
 
-  setnumfield(L, m, "gc_whitenum", metrics.gc_whitenum);
-  setnumfield(L, m, "gc_graynum", metrics.gc_graynum);
-  setnumfield(L, m, "gc_blacknum", metrics.gc_blacknum);
-
   setnumfield(L, m, "gc_total", metrics.gc_total);
   setnumfield(L, m, "gc_freed", metrics.gc_freed);
   setnumfield(L, m, "gc_allocated", metrics.gc_allocated);
diff --git a/src/lj_mapi.c b/src/lj_mapi.c
index a3fb645..7645a44 100644
--- a/src/lj_mapi.c
+++ b/src/lj_mapi.c
@@ -36,10 +36,6 @@ LUAMISC_API void luaM_metrics(lua_State *L, struct luam_Metrics *metrics)
   metrics->gc_cdatanum = 0;
 #endif
 
-  metrics->gc_whitenum = gc->whitenum;
-  metrics->gc_graynum = gc->graynum;
-  metrics->gc_blacknum = gc->blacknum;
-
   metrics->gc_total = gc->total;
   metrics->gc_freed = gc->freed;
   metrics->gc_allocated = gc->allocated;
diff --git a/src/lmisclib.h b/src/lmisclib.h
index e4970b5..e2d1909 100644
--- a/src/lmisclib.h
+++ b/src/lmisclib.h
@@ -27,13 +27,6 @@ struct luam_Metrics {
   /* Amount of allocated cdata objects. */
   size_t gc_cdatanum;
 
-  /* Amount of white objects. */
-  size_t gc_whitenum;
-  /* Amount of gray objects. */
-  size_t gc_graynum;
-  /* Amount of black objects. */
-  size_t gc_blacknum;
-
   /* Memory currently allocated. */
   size_t gc_total;
   /* Total amount of freed memory. */
diff --git a/test/clib-misclib-getmetrics.test.lua b/test/clib-misclib-getmetrics.test.lua
index 840840c..34adaba 100755
--- a/test/clib-misclib-getmetrics.test.lua
+++ b/test/clib-misclib-getmetrics.test.lua
@@ -14,26 +14,12 @@ local jit_opt_default_minstitch = 0
 local tap = require('tap')
 
 local test = tap.test("clib-misc-getmetrics")
-test:plan(13)
+test:plan(10)
 
 local testgetmetrics = require("testgetmetrics")
 
 test:ok(testgetmetrics.base())
 test:ok(testgetmetrics.gc_allocated_freed())
-test:ok(testgetmetrics.gc_colors_base())
-test:ok(testgetmetrics.gc_colors_strempty())
-
--- Upvalues are never gray.
-tbar_t = {}
-test:ok(testgetmetrics.gc_colors_tbar(function()
-    local tabnew = require"table.new"
-    for i =1, 100 do
-        tbar_t.x = tabnew(i,0)
-    end
-    collectgarbage("collect")
-    tbar_t = nil
-end))
-
 test:ok(testgetmetrics.gc_steps())
 
 test:ok(testgetmetrics.objcount(function()
diff --git a/test/clib-misclib-getmetrics/testgetmetrics.c b/test/clib-misclib-getmetrics/testgetmetrics.c
index 85f0505..32802d2 100644
--- a/test/clib-misclib-getmetrics/testgetmetrics.c
+++ b/test/clib-misclib-getmetrics/testgetmetrics.c
@@ -20,10 +20,6 @@ static int base(lua_State *L)
 	assert((ssize_t)metrics.gc_udatanum >= 0);
 	assert((ssize_t)metrics.gc_cdatanum >= 0);
 
-	assert((ssize_t)metrics.gc_whitenum >= 0);
-	assert((ssize_t)metrics.gc_graynum >= 0);
-	assert((ssize_t)metrics.gc_blacknum >= 0);
-
 	assert((ssize_t)metrics.gc_total >= 0);
 	assert((ssize_t)metrics.gc_freed >= 0);
 	assert((ssize_t)metrics.gc_allocated >= 0);
@@ -63,44 +59,6 @@ static int gc_allocated_freed(lua_State *L)
 	return 1;
 }
 
-static int gc_colors_base(lua_State *L)
-{
-	struct luam_Metrics metrics;
-	lua_gc(L, LUA_GCCOLLECT, 0);
-	luaM_metrics(L, &metrics);
-	/* There are no non-white objects after full gc cycle. */
-	assert(metrics.gc_blacknum == 0);
-	assert(metrics.gc_graynum == 0);
-	lua_pushboolean(L, 1);
-	return 1;
-}
-
-static int gc_colors_strempty(lua_State *L)
-{
-	/*
-	 * Empty string is specially recolored at the end
-	 * of atomic phase.
-	 */
-	lua_pushstring(L, "");
-	/* Check that none of the internal asserts will fail. */
-	for (int i = 0; i < 100; i++)
-		lua_gc(L, LUA_GCCOLLECT, 0);
-	lua_pop(L, 1);
-	lua_pushboolean(L, 1);
-	return 1;
-}
-
-static int gc_colors_tbar(lua_State *L)
-{
-	int n = lua_gettop(L);
-	if (n != 1 || !lua_isfunction(L, 1))
-		luaL_error(L, "incorrect arguments: 1 function is required");
-	/* Check that none of the internal asserts will fail. */
-	lua_call(L, 0, 0);
-	lua_pushboolean(L, 1);
-	return 1;
-}
-
 static int gc_steps(lua_State *L)
 {
 	struct luam_Metrics oldm, newm;
@@ -269,9 +227,6 @@ static int tracenum_base(lua_State *L)
 static const struct luaL_Reg testgetmetrics[] = {
 	{"base", base},
 	{"gc_allocated_freed", gc_allocated_freed},
-	{"gc_colors_base", gc_colors_base},
-	{"gc_colors_strempty", gc_colors_strempty},
-	{"gc_colors_tbar", gc_colors_tbar},
 	{"gc_steps", gc_steps},
 	{"objcount", objcount},
 	{"snap_restores", snap_restores},
diff --git a/test/lib-misc-getmetrics.test.lua b/test/lib-misc-getmetrics.test.lua
index 66423ab..2859e26 100755
--- a/test/lib-misc-getmetrics.test.lua
+++ b/test/lib-misc-getmetrics.test.lua
@@ -7,7 +7,7 @@
 local tap = require('tap')
 
 local test = tap.test("lib-misc-getmetrics")
-test:plan(13)
+test:plan(10)
 
 local jit_opt_default_hotloop = 56
 local jit_opt_default_hotexit = 10
@@ -16,7 +16,7 @@ local jit_opt_default_minstitch = 0
 
 -- Test Lua API.
 test:test("base", function(subtest)
-    subtest:plan(22)
+    subtest:plan(19)
     local metrics = misc.getmetrics()
     subtest:ok(metrics.strhash_hit >= 0)
     subtest:ok(metrics.strhash_miss >= 0)
@@ -26,10 +26,6 @@ test:test("base", function(subtest)
     subtest:ok(metrics.gc_udatanum >= 0)
     subtest:ok(metrics.gc_cdatanum >= 0)
 
-    subtest:ok(metrics.gc_whitenum >= 2)
-    subtest:ok(metrics.gc_graynum >= 0)
-    subtest:ok(metrics.gc_blacknum >= 0)
-
     subtest:ok(metrics.gc_total >= 0)
     subtest:ok(metrics.gc_freed >= 0)
     subtest:ok(metrics.gc_allocated >= 0)
@@ -47,43 +43,6 @@ test:test("base", function(subtest)
     subtest:ok(metrics.jit_trace_num >= 0)
 end)
 
-test:test("gc-colors-base", function(subtest)
-    subtest:plan(2)
-
-    -- Force up garbage collect all dead objects.
-    collectgarbage("collect")
-
-    -- There are no non-white objects after full gc cycle.
-    local metrics = misc.getmetrics()
-    subtest:is(metrics.gc_graynum, 0)
-    subtest:is(metrics.gc_blacknum, 0)
-end)
-
-test:test("gc-colors-stremty", function(subtest)
-    subtest:plan(1)
-    -- Empty string is specially recolored at the end
-    -- of atomic phase.
-    local strempty = ""
-    -- Check that none of the internal asserts will fail.
-    for i=1, 100 do
-        collectgarbage("collect")
-    end
-    subtest:ok(true, "no assertion failed")
-end)
-
--- Upvalues are never gray.
-tbar_t = {}
-test:test("gc-colors-tbar", function(subtest)
-    subtest:plan(1)
-    local tabnew = require"table.new"
-    for i =1, 100 do
-        tbar_t.x = tabnew(i,0)
-    end
-    collectgarbage("collect")
-    tbar_t = nil
-    subtest:ok(true, "no assertion failed")
-end)
-
 test:test("gc-allocated-freed", function(subtest)
     subtest:plan(1)
 
@@ -404,26 +363,26 @@ test:test("strhash", function(subtest)
 
     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 == 22)
+    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 == 23)
+    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 == 22)
+    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 == 22)
+    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)
==================================================================

[1]: https://lists.tarantool.org/pipermail/tarantool-patches/2020-October/019882.html

-- 
Best regards,
Sergey Kaplun

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2020-10-05  6:39 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-20 17:12 [Tarantool-patches] [PATCH v3 0/2] Implement LuaJIT platform metrics Sergey Kaplun
2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 1/2] core: introduce various " Sergey Kaplun
2020-10-05  6:36   ` Sergey Kaplun
2020-09-20 17:12 ` [Tarantool-patches] [PATCH v3 2/2] misc: add C and Lua API for " Sergey Kaplun
2020-10-05  6:39   ` Sergey Kaplun
2020-09-20 17:13 ` [Tarantool-patches] [RFC v3] rfc: luajit metrics Sergey Kaplun
2020-09-21 15:18   ` Sergey Kaplun
2020-09-21 15:15 ` Sergey Kaplun

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox