Skip to content

Commit b4e6bf0

Browse files
Buristanigormunkin
authored andcommitted
misc: add Lua API for memory profiler
This patch introduces Lua API for LuaJIT memory profiler implemented in the scope of the previous patch. Profiler returns true value if started/stopped successfully, returns nil on failure (plus an error message as a second result and a system-dependent error code as a third result). If LuaJIT is build without memory profiler both return false. <lj_errmsg.h> has adjusted with three new errors PROF_MISUSE/PROF_ISRUNNING/PROF_NOTRUNNING returned in case when profiler has used incorrectly/started/stopped already correspondingly. Part of tarantool/tarantool#5442 Reviewed-by: Sergey Ostanevich <sergos@tarantool.org> Reviewed-by: Igor Munkin <imun@tarantool.org> Signed-off-by: Igor Munkin <imun@tarantool.org>
1 parent 0dcae51 commit b4e6bf0

File tree

3 files changed

+178
-2
lines changed

3 files changed

+178
-2
lines changed

src/Makefile.dep

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ lib_jit.o: lib_jit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
2929
lj_vm.h lj_vmevent.h lj_lib.h luajit.h lj_libdef.h
3030
lib_math.o: lib_math.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
3131
lj_def.h lj_arch.h lj_lib.h lj_vm.h lj_libdef.h
32-
lib_misc.o: lib_misc.c lua.h luaconf.h lmisclib.h lj_obj.h lj_def.h lj_arch.h \
33-
lj_str.h lj_tab.h lj_lib.h lj_libdef.h
32+
lib_misc.o: lib_misc.c lua.h luaconf.h lmisclib.h lauxlib.h lj_obj.h \
33+
lj_def.h lj_arch.h lj_str.h lj_tab.h lj_lib.h lj_gc.h lj_err.h \
34+
lj_errmsg.h lj_memprof.h lj_wbuf.h lj_libdef.h
3435
lib_os.o: lib_os.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
3536
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_lib.h \
3637
lj_libdef.h

src/lib_misc.c

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,21 @@
88
#define lib_misc_c
99
#define LUA_LIB
1010

11+
#include <stdio.h>
12+
#include <errno.h>
13+
1114
#include "lua.h"
1215
#include "lmisclib.h"
16+
#include "lauxlib.h"
1317

1418
#include "lj_obj.h"
1519
#include "lj_str.h"
1620
#include "lj_tab.h"
1721
#include "lj_lib.h"
22+
#include "lj_gc.h"
23+
#include "lj_err.h"
24+
25+
#include "lj_memprof.h"
1826

1927
/* ------------------------------------------------------------------------ */
2028

@@ -67,8 +75,168 @@ LJLIB_CF(misc_getmetrics)
6775

6876
#include "lj_libdef.h"
6977

78+
/* ----- misc.memprof module ---------------------------------------------- */
79+
80+
#define LJLIB_MODULE_misc_memprof
81+
82+
/*
83+
** Yep, 8Mb. Tuned in order not to bother the platform with too often flushes.
84+
*/
85+
#define STREAM_BUFFER_SIZE (8 * 1024 * 1024)
86+
87+
/* Structure given as ctx to memprof writer and on_stop callback. */
88+
struct memprof_ctx {
89+
/* Output file stream for data. */
90+
FILE *stream;
91+
/* Profiled global_State for lj_mem_free at on_stop callback. */
92+
global_State *g;
93+
/* Buffer for data. */
94+
uint8_t buf[STREAM_BUFFER_SIZE];
95+
};
96+
97+
/*
98+
** Default buffer writer function.
99+
** Just call fwrite to the corresponding FILE.
100+
*/
101+
static size_t buffer_writer_default(const void **buf_addr, size_t len,
102+
void *opt)
103+
{
104+
struct memprof_ctx *ctx = opt;
105+
FILE *stream = ctx->stream;
106+
const void * const buf_start = *buf_addr;
107+
const void *data = *buf_addr;
108+
size_t write_total = 0;
109+
110+
lua_assert(len <= STREAM_BUFFER_SIZE);
111+
112+
for (;;) {
113+
const size_t written = fwrite(data, 1, len - write_total, stream);
114+
115+
if (LJ_UNLIKELY(written == 0)) {
116+
/* Re-tries write in case of EINTR. */
117+
if (errno != EINTR) {
118+
/* Will be freed as whole chunk later. */
119+
*buf_addr = NULL;
120+
return write_total;
121+
}
122+
123+
errno = 0;
124+
continue;
125+
}
126+
127+
write_total += written;
128+
lua_assert(write_total <= len);
129+
130+
if (write_total == len)
131+
break;
132+
133+
data = (uint8_t *)data + (ptrdiff_t)written;
134+
}
135+
136+
*buf_addr = buf_start;
137+
return write_total;
138+
}
139+
140+
/* Default on stop callback. Just close the corresponding stream. */
141+
static int on_stop_cb_default(void *opt, uint8_t *buf)
142+
{
143+
struct memprof_ctx *ctx = opt;
144+
FILE *stream = ctx->stream;
145+
UNUSED(buf);
146+
lj_mem_free(ctx->g, ctx, sizeof(*ctx));
147+
return fclose(stream);
148+
}
149+
150+
/* local started, err, errno = misc.memprof.start(fname) */
151+
LJLIB_CF(misc_memprof_start)
152+
{
153+
struct lj_memprof_options opt = {0};
154+
const char *fname = strdata(lj_lib_checkstr(L, 1));
155+
struct memprof_ctx *ctx;
156+
int memprof_status;
157+
158+
/*
159+
** FIXME: more elegant solution with ctx.
160+
** Throws in case of OOM.
161+
*/
162+
ctx = lj_mem_new(L, sizeof(*ctx));
163+
opt.ctx = ctx;
164+
opt.buf = ctx->buf;
165+
opt.writer = buffer_writer_default;
166+
opt.on_stop = on_stop_cb_default;
167+
opt.len = STREAM_BUFFER_SIZE;
168+
169+
ctx->g = G(L);
170+
ctx->stream = fopen(fname, "wb");
171+
172+
if (ctx->stream == NULL) {
173+
lj_mem_free(ctx->g, ctx, sizeof(*ctx));
174+
return luaL_fileresult(L, 0, fname);
175+
}
176+
177+
memprof_status = lj_memprof_start(L, &opt);
178+
179+
if (LJ_UNLIKELY(memprof_status != PROFILE_SUCCESS)) {
180+
if (memprof_status == PROFILE_ERRIO) {
181+
fclose(ctx->stream);
182+
lj_mem_free(ctx->g, ctx, sizeof(*ctx));
183+
}
184+
switch (memprof_status) {
185+
case PROFILE_ERRUSE:
186+
lua_pushnil(L);
187+
lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
188+
lua_pushinteger(L, EINVAL);
189+
return 3;
190+
case PROFILE_ERRRUN:
191+
lua_pushnil(L);
192+
lua_pushstring(L, err2msg(LJ_ERR_PROF_ISRUNNING));
193+
lua_pushinteger(L, EINVAL);
194+
return 3;
195+
case PROFILE_ERRIO:
196+
return luaL_fileresult(L, 0, fname);
197+
default:
198+
lua_assert(0);
199+
return 0;
200+
}
201+
}
202+
lua_pushboolean(L, 1);
203+
return 1;
204+
}
205+
206+
/* local stopped, err, errno = misc.memprof.stop() */
207+
LJLIB_CF(misc_memprof_stop)
208+
{
209+
int status = lj_memprof_stop(L);
210+
if (status != PROFILE_SUCCESS) {
211+
switch (status) {
212+
case PROFILE_ERRUSE:
213+
lua_pushnil(L);
214+
lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
215+
lua_pushinteger(L, EINVAL);
216+
return 3;
217+
case PROFILE_ERRRUN:
218+
lua_pushnil(L);
219+
lua_pushstring(L, err2msg(LJ_ERR_PROF_NOTRUNNING));
220+
lua_pushinteger(L, EINVAL);
221+
return 3;
222+
case PROFILE_ERRIO:
223+
return luaL_fileresult(L, 0, NULL);
224+
default:
225+
lua_assert(0);
226+
return 0;
227+
}
228+
}
229+
lua_pushboolean(L, 1);
230+
return 1;
231+
}
232+
233+
#include "lj_libdef.h"
234+
235+
/* ------------------------------------------------------------------------ */
236+
70237
LUALIB_API int luaopen_misc(struct lua_State *L)
71238
{
72239
LJ_LIB_REG(L, LUAM_MISCLIBNAME, misc);
240+
LJ_LIB_REG(L, LUAM_MISCLIBNAME ".memprof", misc_memprof);
73241
return 1;
74242
}

src/lj_errmsg.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ ERRDEF(FFI_NYIPACKBIT, "NYI: packed bit fields")
185185
ERRDEF(FFI_NYICALL, "NYI: cannot call this C function (yet)")
186186
#endif
187187

188+
#if LJ_HASMEMPROF
189+
/* Profiler errors. */
190+
ERRDEF(PROF_MISUSE, "profiler misuse")
191+
ERRDEF(PROF_ISRUNNING, "profiler is running already")
192+
ERRDEF(PROF_NOTRUNNING, "profiler is not running")
193+
#endif
194+
188195
#undef ERRDEF
189196

190197
/* Detecting unused error messages:

0 commit comments

Comments
 (0)