[Tarantool-patches] [PATCH luajit v1 08/11] profile: introduce memory profiler

Sergey Kaplun skaplun at tarantool.org
Wed Dec 16 22:13:43 MSK 2020


This patch adds memory profile module. Lua and C API for it will
be added at the next patches.

When VM executes a trace, vmstate is set to the trace number.
Also this patch defines special macro LJ_VMST_TRACE equaled to
LJ_VMST__MAX to encapsulate all different traces into one vmstate when
profiled.
See <ljp_memprof.h> for details.

Part of tarantool/tarantool#5442
---
 src/Makefile              |   8 +-
 src/Makefile.dep          |   4 +
 src/lj_arch.h             |  22 ++
 src/lj_obj.h              |   8 +
 src/lj_state.c            |   8 +
 src/lmisclib.h            |  29 +++
 src/profile/ljp_memprof.c | 413 ++++++++++++++++++++++++++++++++++++++
 src/profile/ljp_memprof.h |  86 ++++++++
 8 files changed, 577 insertions(+), 1 deletion(-)
 create mode 100644 src/profile/ljp_memprof.c
 create mode 100644 src/profile/ljp_memprof.h

diff --git a/src/Makefile b/src/Makefile
index e00265c..1ade2ec 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -113,6 +113,12 @@ XCFLAGS=
 # Enable GC64 mode for x64.
 #XCFLAGS+= -DLUAJIT_ENABLE_GC64
 #
+# Disable the memory profiler.
+#XCFLAGS+= -DLUAJIT_DISABLE_MEMPROF
+#
+# Disable the thread safe profiler.
+#XCFLAGS+= -DLUAJIT_DISABLE_THREAD_SAFE
+#
 ##############################################################################
 
 ##############################################################################
@@ -469,7 +475,7 @@ DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS)
 DASM_DASC= vm_$(DASM_ARCH).dasc
 
 UTILS_O= utils/leb128.o
-PROFILE_O= profile/ljp_write.o profile/ljp_symtab.o
+PROFILE_O= profile/ljp_write.o profile/ljp_symtab.o profile/ljp_memprof.o
 BUILDVM_O= host/buildvm.o host/buildvm_asm.o host/buildvm_peobj.o \
 	   host/buildvm_lib.o host/buildvm_fold.o
 BUILDVM_T= host/buildvm
diff --git a/src/Makefile.dep b/src/Makefile.dep
index 831a5ce..c41fdcf 100644
--- a/src/Makefile.dep
+++ b/src/Makefile.dep
@@ -248,6 +248,10 @@ host/buildvm_lib.o: host/buildvm_lib.c host/buildvm.h lj_def.h lua.h luaconf.h \
 host/buildvm_peobj.o: host/buildvm_peobj.c host/buildvm.h lj_def.h lua.h \
  luaconf.h lj_arch.h lj_bc.h lj_def.h lj_arch.h
 host/minilua.o: host/minilua.c
+profile/ljp_memprof.o: profile/ljp_memprof.c profile/ljp_memprof.h lmisclib.h \
+ lua.h luaconf.h lj_def.h lj_obj.h lj_arch.h lj_frame.h lj_bc.h lj_jit.h \
+ lj_ir.h lj_gc.h profile/ljp_symtab.h lj_debug.h profile/ljp_symtab.h \
+ profile/ljp_write.h
 profile/ljp_symtab.o: profile/ljp_symtab.c lj_obj.h lua.h luaconf.h lj_def.h \
  lj_arch.h profile/ljp_write.h profile/ljp_symtab.h
 profile/ljp_write.o: profile/ljp_write.c profile/ljp_write.h utils/leb128.h \
diff --git a/src/lj_arch.h b/src/lj_arch.h
index c8d7138..5967849 100644
--- a/src/lj_arch.h
+++ b/src/lj_arch.h
@@ -213,6 +213,8 @@
 #define LJ_ARCH_VERSION		50
 #endif
 
+#define LJ_ARCH_NOMEMPROF	1
+
 #elif LUAJIT_TARGET == LUAJIT_ARCH_ARM64
 
 #define LJ_ARCH_BITS		64
@@ -234,6 +236,8 @@
 
 #define LJ_ARCH_VERSION		80
 
+#define LJ_ARCH_NOMEMPROF	1
+
 #elif LUAJIT_TARGET == LUAJIT_ARCH_PPC
 
 #ifndef LJ_ARCH_ENDIAN
@@ -299,6 +303,8 @@
 #define LJ_ARCH_XENON		1
 #endif
 
+#define LJ_ARCH_NOMEMPROF	1
+
 #elif LUAJIT_TARGET == LUAJIT_ARCH_MIPS32 || LUAJIT_TARGET == LUAJIT_ARCH_MIPS64
 
 #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL)
@@ -358,6 +364,8 @@
 #define LJ_ARCH_VERSION		10
 #endif
 
+#define LJ_ARCH_NOMEMPROF	1
+
 #else
 #error "No target architecture defined"
 #endif
@@ -564,4 +572,18 @@
 #define LJ_52			0
 #endif
 
+/* Disable or enable the memory profiler. */
+#if defined(LUAJIT_DISABLE_MEMPROF) || defined(LJ_ARCH_NOMEMPROF) || LJ_TARGET_WINDOWS || LJ_TARGET_CYGWIN || LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_XBOX360
+#define LJ_HASMEMPROF		0
+#else
+#define LJ_HASMEMPROF		1
+#endif
+
+/* Disable or enable the memory profiler's thread safety. */
+#if defined(LUAJIT_DISABLE_THREAD_SAFE) || LJ_TARGET_WINDOWS || LJ_TARGET_XBOX360
+#define LJ_THREAD_SAFE		0
+#else
+#define LJ_THREAD_SAFE		1
+#endif
+
 #endif
diff --git a/src/lj_obj.h b/src/lj_obj.h
index c94617d..c94b0bb 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -523,6 +523,14 @@ enum {
   LJ_VMST__MAX
 };
 
+/*
+** PROFILER HACK: VM is inside a trace. This is a pseudo-state used by profiler.
+** In fact, when VM executes a trace, vmstate is set to the trace number, but
+** we aggregate all such cases into one VM state during per-VM state profiling.
+*/
+
+#define LJ_VMST_TRACE		(LJ_VMST__MAX)
+
 #define setvmstate(g, st)	((g)->vmstate = ~LJ_VMST_##st)
 
 /* Metamethods. ORDER MM */
diff --git a/src/lj_state.c b/src/lj_state.c
index 1d9c628..09ac1b4 100644
--- a/src/lj_state.c
+++ b/src/lj_state.c
@@ -29,6 +29,10 @@
 #include "lj_alloc.h"
 #include "luajit.h"
 
+#if LJ_HASMEMPROF
+#include "profile/ljp_memprof.h"
+#endif
+
 /* -- Stack handling ------------------------------------------------------ */
 
 /* Stack sizes. */
@@ -243,6 +247,10 @@ LUA_API void lua_close(lua_State *L)
   global_State *g = G(L);
   int i;
   L = mainthread(g);  /* Only the main thread can be closed. */
+#if LJ_HASMEMPROF
+  if (ljp_memprof_is_running())
+    ljp_memprof_stop();
+#endif
 #if LJ_HASPROFILE
   luaJIT_profile_stop(L);
 #endif
diff --git a/src/lmisclib.h b/src/lmisclib.h
index 0c07707..b4c41da 100644
--- a/src/lmisclib.h
+++ b/src/lmisclib.h
@@ -60,6 +60,35 @@ struct luam_Metrics {
 
 LUAMISC_API void luaM_metrics(lua_State *L, struct luam_Metrics *metrics);
 
+/* Profiler public API. */
+#define LUAM_PROFILE_SUCCESS 0
+#define LUAM_PROFILE_ERR     1
+#define LUAM_PROFILE_ERRRUN  2
+#define LUAM_PROFILE_ERRMEM  3
+#define LUAM_PROFILE_ERRIO   4
+
+/* Profiler options. */
+struct luam_Prof_options {
+  /* Context for the profile writer and final callback. */
+  void *ctx;
+  /* Custom buffer to write data. */
+  uint8_t *buf;
+  /* The buffer's size. */
+  size_t len;
+  /*
+  ** Writer function for profile events.
+  ** Should return amount of written bytes on success or zero in case of error.
+  ** Setting *data to NULL means end of profiling.
+  */
+  size_t (*writer)(const void **data, size_t len, void *ctx);
+  /*
+  ** Callback on profiler stopping. Required for correctly cleaning
+  ** at vm shoutdown when profiler still running.
+  ** Returns zero on success.
+  */
+  int (*on_stop)(void *ctx, uint8_t *buf);
+};
+
 #define LUAM_MISCLIBNAME "misc"
 LUALIB_API int luaopen_misc(lua_State *L);
 
diff --git a/src/profile/ljp_memprof.c b/src/profile/ljp_memprof.c
new file mode 100644
index 0000000..2137d5a
--- /dev/null
+++ b/src/profile/ljp_memprof.c
@@ -0,0 +1,413 @@
+/*
+** Implementation of memory profiler.
+**
+** Major portions taken verbatim or adapted from the LuaVela.
+** Copyright (C) 2015-2019 IPONWEB Ltd.
+*/
+
+#include <errno.h>
+
+#include "profile/ljp_memprof.h"
+#include "lmisclib.h"
+#include "lj_def.h"
+#include "lj_arch.h"
+
+#if LJ_HASMEMPROF
+
+#if LJ_IS_THREAD_SAFE
+#include <pthread.h>
+#endif
+
+#include "lua.h"
+
+#include "lj_obj.h"
+#include "lj_frame.h"
+#include "lj_debug.h"
+#include "lj_gc.h"
+#include "profile/ljp_symtab.h"
+#include "profile/ljp_write.h"
+
+/* Allocation events: */
+#define AEVENT_ALLOC   ((uint8_t)1)
+#define AEVENT_FREE    ((uint8_t)2)
+#define AEVENT_REALLOC ((uint8_t)(AEVENT_ALLOC | AEVENT_FREE))
+
+/* Allocation sources: */
+#define ASOURCE_INT   ((uint8_t)(1 << 2))
+#define ASOURCE_LFUNC ((uint8_t)(2 << 2))
+#define ASOURCE_CFUNC ((uint8_t)(3 << 2))
+
+/* Aux bits: */
+
+/*
+** Reserved. There is ~1 second between each two events marked with this flag.
+** This will possibly be used later to implement dumps of the evolving heap.
+*/
+#define LJM_TIMESTAMP ((uint8_t)(0x40))
+
+#define LJM_EPILOGUE_HEADER 0x80
+
+enum memprof_state {
+  /* memprof is not running. */
+  MPS_IDLE,
+  /* memprof is running. */
+  MPS_PROFILE,
+  /*
+  ** Stopped in case of stopped stream.
+  ** Saved errno is returned to user at memprof_stop.
+  */
+  MPS_HALT
+};
+
+struct alloc {
+  lua_Alloc allocf; /* Allocating function. */
+  void *state; /* Opaque allocator's state. */
+};
+
+struct memprof {
+  global_State *g; /* Profiled VM. */
+  enum memprof_state state; /* Internal state. */
+  struct ljp_buffer out; /* Output accumulator. */
+  struct alloc orig_alloc; /* Original allocator. */
+  struct luam_Prof_options opt; /* Profiling options. */
+  int saved_errno; /* Saved errno when profiler deinstrumented. */
+};
+
+#if LJ_IS_THREAD_SAFE
+
+pthread_mutex_t memprof_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static LJ_AINLINE int memprof_lock(void)
+{
+  return pthread_mutex_lock(&memprof_mutex);
+}
+
+static LJ_AINLINE int memprof_unlock(void)
+{
+  return pthread_mutex_unlock(&memprof_mutex);
+}
+
+#else /* LJ_IS_THREAD_SAFE */
+
+#define memprof_lock()
+#define memprof_unlock()
+
+#endif /* LJ_IS_THREAD_SAFE */
+
+static struct memprof memprof = {0};
+
+const unsigned char ljm_header[] = {'l', 'j', 'm', LJM_CURRENT_FORMAT_VERSION,
+				    0x0, 0x0, 0x0};
+
+static void memprof_write_lfunc(struct ljp_buffer *out, uint8_t header,
+				GCfunc *fn, struct lua_State *L,
+				cTValue *nextframe)
+{
+  const BCLine line = lj_debug_frameline(L, fn, nextframe);
+  ljp_write_byte(out, header | ASOURCE_LFUNC);
+  ljp_write_u64(out, (uintptr_t)funcproto(fn));
+  ljp_write_u64(out, line >= 0 ? (uintptr_t)line : 0);
+}
+
+static void memprof_write_cfunc(struct ljp_buffer *out, uint8_t header,
+				const GCfunc *fn)
+{
+  ljp_write_byte(out, header | ASOURCE_CFUNC);
+  ljp_write_u64(out, (uintptr_t)fn->c.f);
+}
+
+static void memprof_write_ffunc(struct ljp_buffer *out, uint8_t header,
+				GCfunc *fn, struct lua_State *L,
+				cTValue *frame)
+{
+  cTValue *pframe = frame_prev(frame);
+  GCfunc *pfn = frame_func(pframe);
+
+  /*
+  ** NB! If a fast function is called by a Lua function, report the
+  ** Lua function for more meaningful output. Otherwise report the fast
+  ** function as a C function.
+  */
+  if (pfn != NULL && isluafunc(pfn))
+    memprof_write_lfunc(out, header, pfn, L, frame);
+  else
+    memprof_write_cfunc(out, header, fn);
+}
+
+static void memprof_write_func(struct memprof *mp, uint8_t header)
+{
+  struct ljp_buffer *out = &mp->out;
+  lua_State *L = gco2th(gcref(mp->g->mem_L));
+  cTValue *frame = L->base - 1;
+  GCfunc *fn;
+
+  fn = frame_func(frame);
+
+  if (isluafunc(fn))
+    memprof_write_lfunc(out, header, fn, L, NULL);
+  else if (isffunc(fn))
+    memprof_write_ffunc(out, header, fn, L, frame);
+  else if (iscfunc(fn))
+    memprof_write_cfunc(out, header, fn);
+  else
+    lua_assert(0);
+}
+
+static void memprof_write_hvmstate(struct memprof *mp, uint8_t header)
+{
+  ljp_write_byte(&mp->out, header | ASOURCE_INT);
+}
+
+/*
+** NB! In ideal world, we should report allocations from traces as well.
+** But since traces must follow the semantics of the original code, behaviour of
+** Lua and JITted code must match 1:1 in terms of allocations, which makes
+** using memprof with enabled JIT virtually redundant. Hence the stub below.
+*/
+static void memprof_write_trace(struct memprof *mp, uint8_t header)
+{
+  ljp_write_byte(&mp->out, header | ASOURCE_INT);
+}
+
+typedef void (*memprof_writer)(struct memprof *mp, uint8_t header);
+
+static const memprof_writer memprof_writers[] = {
+  memprof_write_hvmstate, /* LJ_VMST_INTERP */
+  memprof_write_func, /* LJ_VMST_LFUNC */
+  memprof_write_func, /* LJ_VMST_FFUNC */
+  memprof_write_func, /* LJ_VMST_CFUNC */
+  memprof_write_hvmstate, /* LJ_VMST_GC */
+  memprof_write_hvmstate, /* LJ_VMST_EXIT */
+  memprof_write_hvmstate, /* LJ_VMST_RECORD */
+  memprof_write_hvmstate, /* LJ_VMST_OPT */
+  memprof_write_hvmstate, /* LJ_VMST_ASM */
+  memprof_write_trace /* LJ_VMST_TRACE */
+};
+
+static void memprof_write_caller(struct memprof *mp, uint8_t aevent)
+{
+  const global_State *g = mp->g;
+  const uint32_t _vmstate = (uint32_t)~g->vmstate;
+  const uint32_t vmstate = _vmstate < LJ_VMST_TRACE ? _vmstate : LJ_VMST_TRACE;
+  const uint8_t header = aevent;
+
+  memprof_writers[vmstate](mp, header);
+}
+
+static int memprof_stop(const struct lua_State *L);
+
+static void *memprof_allocf(void *ud, void *ptr, size_t osize, size_t nsize)
+{
+  struct memprof *mp = &memprof;
+  struct alloc *oalloc = &mp->orig_alloc;
+  struct ljp_buffer *out = &mp->out;
+  void *nptr;
+
+  lua_assert(MPS_PROFILE == mp->state);
+  lua_assert(oalloc->allocf != memprof_allocf);
+  lua_assert(oalloc->allocf != NULL);
+  lua_assert(ud == oalloc->state);
+
+  nptr = oalloc->allocf(ud, ptr, osize, nsize);
+
+  if (nsize == 0) {
+    memprof_write_caller(mp, AEVENT_FREE);
+    ljp_write_u64(out, (uintptr_t)ptr);
+    ljp_write_u64(out, (uint64_t)osize);
+  } else if (ptr == NULL) {
+    memprof_write_caller(mp, AEVENT_ALLOC);
+    ljp_write_u64(out, (uintptr_t)nptr);
+    ljp_write_u64(out, (uint64_t)nsize);
+  } else {
+    memprof_write_caller(mp, AEVENT_REALLOC);
+    ljp_write_u64(out, (uintptr_t)ptr);
+    ljp_write_u64(out, (uint64_t)osize);
+    ljp_write_u64(out, (uintptr_t)nptr);
+    ljp_write_u64(out, (uint64_t)nsize);
+  }
+
+  /* Deinstrument memprof if required. */
+  if (LJ_UNLIKELY(ljp_write_test_flag(out, STREAM_STOP)))
+    memprof_stop(NULL);
+
+  return nptr;
+}
+
+static void memprof_write_prologue(struct ljp_buffer *out)
+{
+  size_t i = 0;
+  const size_t len = sizeof(ljm_header) / sizeof(ljm_header[0]);
+
+  for (; i < len; i++)
+    ljp_write_byte(out, ljm_header[i]);
+}
+
+int ljp_memprof_start(struct lua_State *L, const struct luam_Prof_options *opt)
+{
+  struct memprof *mp = &memprof;
+  struct alloc *oalloc = &mp->orig_alloc;
+
+  lua_assert(opt->writer != NULL && opt->on_stop != NULL);
+  lua_assert(opt->buf != NULL && opt->len != 0);
+
+  memprof_lock();
+
+  if (mp->state != MPS_IDLE) {
+    memprof_unlock();
+    return LUAM_PROFILE_ERRRUN;
+  }
+
+  /* Discard possible old errno. */
+  mp->saved_errno = 0;
+
+  /* Init options: */
+  memcpy(&mp->opt, opt, sizeof(*opt));
+
+  /* Init general fields: */
+  mp->g = G(L);
+  mp->state = MPS_PROFILE;
+
+  /* Init output: */
+  ljp_write_init(&mp->out, mp->opt.writer, mp->opt.ctx, mp->opt.buf,
+		 mp->opt.len);
+  ljp_symtab_write(&mp->out, mp->g);
+  memprof_write_prologue(&mp->out);
+
+  if (LJ_UNLIKELY(ljp_write_test_flag(&mp->out, STREAM_ERR_IO) ||
+		  ljp_write_test_flag(&mp->out, STREAM_STOP))) {
+    /* on_stop call may change errno value. */
+    int saved_errno = ljp_write_errno(&mp->out);
+    mp->opt.on_stop(mp->opt.ctx, mp->opt.buf);
+    ljp_write_terminate(&mp->out);
+    mp->state = MPS_IDLE;
+    memprof_unlock();
+    errno = saved_errno;
+    return LUAM_PROFILE_ERRIO;
+  }
+
+  /* Override allocating function: */
+  oalloc->allocf = lua_getallocf(L, &oalloc->state);
+  lua_assert(oalloc->allocf != NULL);
+  lua_assert(oalloc->allocf != memprof_allocf);
+  lua_assert(oalloc->state != NULL);
+  lua_setallocf(L, memprof_allocf, oalloc->state);
+
+  memprof_unlock();
+  return LUAM_PROFILE_SUCCESS;
+}
+
+static int memprof_stop(const struct lua_State *L)
+{
+  struct memprof *mp = &memprof;
+  struct alloc *oalloc = &mp->orig_alloc;
+  struct ljp_buffer *out = &mp->out;
+  int return_status = LUAM_PROFILE_SUCCESS;
+  int saved_errno = 0;
+  struct lua_State *main_L;
+  int cb_status;
+
+  memprof_lock();
+
+  if (mp->state == MPS_HALT) {
+    errno = mp->saved_errno;
+    mp->state = MPS_IDLE
+    memprof_unlock();
+    return LUAM_PROFILE_ERRIO;
+  }
+
+  if (mp->state != MPS_PROFILE) {
+    memprof_unlock();
+    return LUAM_PROFILE_ERRRUN;
+  }
+
+  if (L != NULL && mp->g != G(L)) {
+    memprof_unlock();
+    return LUAM_PROFILE_ERR;
+  }
+
+  mp->state = MPS_IDLE;
+
+  lua_assert(mp->g != NULL);
+  main_L = mainthread(mp->g);
+
+  lua_assert(memprof_allocf == lua_getallocf(main_L, NULL));
+  lua_assert(oalloc->allocf != NULL);
+  lua_assert(oalloc->state != NULL);
+  lua_setallocf(main_L, oalloc->allocf, oalloc->state);
+
+  if (LJ_UNLIKELY(ljp_write_test_flag(out, STREAM_STOP))) {
+    lua_assert(ljp_write_test_flag(out, STREAM_ERR_IO));
+    mp->state = MPS_HALT;
+    /* on_stop call may change errno value. */
+    mp->saved_errno = ljp_write_errno(out);
+    /* Ignore possible errors. mp->opt.buf == NULL here. */
+    mp->opt.on_stop(mp->opt.ctx, mp->opt.buf);
+    ljp_write_terminate(out);
+    memprof_unlock();
+    return LUAM_PROFILE_ERRIO;
+  }
+  ljp_write_byte(out, LJM_EPILOGUE_HEADER);
+
+  ljp_write_flush_buffer(out);
+
+  cb_status = mp->opt.on_stop(mp->opt.ctx, mp->opt.buf);
+  if (LJ_UNLIKELY(ljp_write_test_flag(out, STREAM_ERR_IO) || cb_status != 0)) {
+    saved_errno = ljp_write_errno(out);
+    return_status = LUAM_PROFILE_ERRIO;
+  }
+
+  ljp_write_terminate(out);
+
+  memprof_unlock();
+  errno = saved_errno;
+  return return_status;
+}
+
+int ljp_memprof_stop(void)
+{
+  return memprof_stop(NULL);
+}
+
+int ljp_memprof_stop_vm(const struct lua_State *L)
+{
+  return memprof_stop(L);
+}
+
+int ljp_memprof_is_running(void)
+{
+  struct memprof *mp = &memprof;
+  int running;
+
+  memprof_lock();
+  running = mp->state == MPS_PROFILE;
+  memprof_unlock();
+
+  return running;
+}
+
+#else /* LJ_HASMEMPROF */
+
+int ljp_memprof_start(struct lua_State *L, const struct luam_Prof_options *opt)
+{
+  UNUSED(L);
+  UNUSED(opt);
+  return LUAM_PROFILE_ERR;
+}
+
+int ljp_memprof_stop(void)
+{
+  return LUAM_PROFILE_ERR;
+}
+
+int ljp_memprof_stop_vm(const struct lua_State *L)
+{
+  UNUSED(L);
+  return LUAM_PROFILE_ERR;
+}
+
+int ljp_memprof_is_running(void)
+{
+  return 0;
+}
+
+#endif /* LJ_HASMEMPROF */
diff --git a/src/profile/ljp_memprof.h b/src/profile/ljp_memprof.h
new file mode 100644
index 0000000..90c1990
--- /dev/null
+++ b/src/profile/ljp_memprof.h
@@ -0,0 +1,86 @@
+/*
+** Memory profiler.
+**
+** Major portions taken verbatim or adapted from the LuaVela.
+** Copyright (C) 2015-2019 IPONWEB Ltd.
+*/
+
+#ifndef _LJP_MEMPROF_H
+#define _LJP_MEMPROF_H
+
+/*
+** Event stream format:
+**
+** stream         := symtab memprof
+** symtab         := see <ljp_symtab.h>
+** memprof        := prologue event* epilogue
+** prologue       := 'l' 'j' 'm' version reserved
+** version        := <BYTE>
+** reserved       := <BYTE> <BYTE> <BYTE>
+** event          := event-alloc | event-realloc | event-free
+** event-alloc    := event-header loc? naddr nsize
+** event-realloc  := event-header loc? oaddr osize naddr nsize
+** event-free     := event-header loc? oaddr osize
+** event-header   := <BYTE>
+** loc            := loc-lua | loc-c
+** loc-lua        := sym-addr line-no
+** loc-c          := sym-addr
+** sym-addr       := <ULEB128>
+** line-no        := <ULEB128>
+** oaddr          := <ULEB128>
+** naddr          := <ULEB128>
+** osize          := <ULEB128>
+** nsize          := <ULEB128>
+** epilogue       := event-header
+**
+** <BYTE>   :  A single byte (no surprises here)
+** <ULEB128>:  Unsigned integer represented in ULEB128 encoding
+**
+** (Order of bits below is hi -> lo)
+**
+** version: [VVVVVVVV]
+**  * VVVVVVVV: Byte interpreted as a plain integer version number
+**
+** event-header: [FTUUSSEE]
+**  * EE   : 2 bits for representing allocation event type (AEVENT_*)
+**  * SS   : 2 bits for representing allocation source type (ASOURCE_*)
+**  * UU   : 2 unused bits
+**  * T    : Reserved. 0 for regular events, 1 for the events marked with
+**           the timestamp mark. It is assumed that the time distance between
+**           two marked events is approximately the same and is equal
+**           to 1 second. Always zero for now.
+**  * F    : 0 for regular events, 1 for epilogue's *F*inal header
+**           (if F is set to 1, all other bits are currently ignored)
+*/
+
+struct lua_State;
+
+#define LJM_CURRENT_FORMAT_VERSION 0x02
+
+struct luam_Prof_options;
+
+/*
+** Starts profiling. Returns LUAM_PROFILE_SUCCESS on success and one of
+** LUAM_PROFILE_ERR* codes otherwise. Destroyer is called in case of
+** LUAM_PROFILE_ERRIO.
+*/
+int ljp_memprof_start(struct lua_State *L, const struct luam_Prof_options *opt);
+
+/*
+** Stops profiling. Returns LUAM_PROFILE_SUCCESS on success and one of
+** LUAM_PROFILE_ERR* codes otherwise. If writer() function returns zero
+** on call at buffer flush, profiled stream stops, or on_stop() callback
+** returns non-zero value, returns LUAM_PROFILE_ERRIO.
+*/
+int ljp_memprof_stop(void);
+
+/*
+** VM g is currently being profiled, behaves exactly as ljp_memprof_stop().
+** Otherwise does nothing and returns LUAM_PROFILE_ERR.
+*/
+int ljp_memprof_stop_vm(const struct lua_State *L);
+
+/* Check that profiler is running. */
+int ljp_memprof_is_running(void);
+
+#endif
-- 
2.28.0



More information about the Tarantool-patches mailing list