[Tarantool-patches] [PATCH 4/7] fiber: add PoC for Lua parent backtrace
eelchinov at tarantool.org
eelchinov at tarantool.org
Thu Jul 1 18:54:47 MSK 2021
From: Egor Elchinov <eelchinov at tarantool.org>
For now Lua backtrace is fully collected
at fiber creation moment and stored in a
dynamically allocated buffer.
Needed for: #4002
---
src/lib/core/fiber.h | 7 ++
src/lua/fiber.c | 168 ++++++++++++++++++++++++++++++++++++++++---
src/lua/fiber.h | 19 +++++
3 files changed, 185 insertions(+), 9 deletions(-)
diff --git a/src/lib/core/fiber.h b/src/lib/core/fiber.h
index beed58866..ce1c83d11 100644
--- a/src/lib/core/fiber.h
+++ b/src/lib/core/fiber.h
@@ -528,6 +528,7 @@ struct txn;
struct credentials;
struct lua_State;
struct ipc_wait_pad;
+struct parent_bt_lua;
struct fiber {
coro_context ctx;
@@ -634,6 +635,12 @@ struct fiber {
* Optional fiber.storage Lua reference.
*/
int ref;
+#if ENABLE_BACKTRACE
+ /**
+ * Lua parent backtrace (may be NULL)
+ */
+ struct parent_bt_lua *parent_bt;
+#endif /* ENABLE_BACKTRACE */
} lua;
/**
* Iproto sync.
diff --git a/src/lua/fiber.c b/src/lua/fiber.c
index 7b21361d4..026e30bc6 100644
--- a/src/lua/fiber.c
+++ b/src/lua/fiber.c
@@ -193,6 +193,18 @@ struct lua_fiber_tb_ctx {
int tb_frame;
};
+/**
+ * Lua parent traceback context.
+ */
+struct lua_parent_tb_ctx {
+ /* Lua stack to push values. */
+ struct lua_State *L;
+ /* Lua parent backtrace. */
+ struct parent_bt_lua *bt;
+ /* Count of traced frames (both C and Lua). */
+ int tb_frame;
+};
+
#ifdef ENABLE_BACKTRACE
static void
dump_lua_frame(struct lua_State *L, lua_Debug *ar, int tb_frame)
@@ -209,6 +221,22 @@ dump_lua_frame(struct lua_State *L, lua_Debug *ar, int tb_frame)
lua_settable(L, -3);
}
+static void
+dump_parent_lua_frame(struct lua_State *L, const char *name, const char *src,
+ int currentline, int tb_frame)
+{
+ char buf[512];
+ snprintf(buf, sizeof(buf), "%s in %s at line %i",
+ name != NULL ? name : "(unnamed)",
+ src, currentline);
+ lua_pushnumber(L, tb_frame);
+ lua_newtable(L);
+ lua_pushstring(L, "L");
+ lua_pushstring(L, buf);
+ lua_settable(L, -3);
+ lua_settable(L, -3);
+}
+
static int
fiber_backtrace_cb(int frameno, void *frameret, const char *func, size_t offset, void *cb_ctx)
{
@@ -260,7 +288,115 @@ fiber_backtrace_cb(int frameno, void *frameret, const char *func, size_t offset,
lua_settable(L, -3);
return 0;
}
-#endif
+
+static int
+fiber_parent_backtrace_cb(int frameno, void *frameret, const char *func,
+ size_t offset, void *cb_ctx)
+{
+ int lua_frame = 0;
+ struct lua_parent_tb_ctx *tb_ctx = (struct lua_parent_tb_ctx *)cb_ctx;
+ struct parent_bt_lua *bt = tb_ctx->bt;
+ struct lua_State *L = tb_ctx->L;
+ /*
+ * There is impossible to get func == NULL until
+ * https://github.com/tarantool/tarantool/issues/5326
+ * will not resolved, but is possible afterwards.
+ */
+ if (bt != NULL && func != NULL && strstr(func, "lj_BC_FUNCC") == func) {
+ /* We are in the LUA vm. */
+ while (lua_frame < bt->cnt) {
+ tb_ctx->tb_frame++;
+ dump_parent_lua_frame(L, bt->names[lua_frame],
+ bt->sources[lua_frame],
+ bt->lines[lua_frame],
+ tb_ctx->tb_frame);
+ lua_frame++;
+ }
+ }
+ char buf[512];
+ int l = snprintf(buf, sizeof(buf), "#%-2d %p in ", frameno, frameret);
+ if (func)
+ snprintf(buf + l, sizeof(buf) - l, "%s+%zu", func, offset);
+ else
+ snprintf(buf + l, sizeof(buf) - l, "?");
+ tb_ctx->tb_frame++;
+ lua_pushnumber(L, tb_ctx->tb_frame);
+ lua_newtable(L);
+ lua_pushstring(L, "C");
+ lua_pushstring(L, buf);
+ lua_settable(L, -3);
+ lua_settable(L, -3);
+ return 0;
+}
+
+static int
+fiber_parent_bt_init(struct fiber *f, struct lua_State *L)
+{
+ int lua_frame = 0, tb_frame = 0;
+ lua_Debug ar;
+ struct parent_bt_lua *bt = NULL;
+
+ bt = (struct parent_bt_lua *)malloc(sizeof(*bt));
+ if (bt == NULL){
+ diag_set(OutOfMemory, sizeof(*bt), "malloc", "bt");
+ return 1;
+ }
+
+ while (tb_frame < PARENT_BT_LUA_LEN_MAX &&
+ lua_getstack(L, lua_frame, &ar) > 0) {
+ /* Skip all following C-frames. */
+ lua_getinfo(L, "Sln", &ar);
+ if (*ar.what != 'C')
+ break;
+ if (ar.name != NULL) {
+ /* Dump frame if it is a C built-in call. */
+ bt->lines[tb_frame] = ar.currentline;
+ memset(bt->names[tb_frame], 0,
+ PARENT_BT_LUA_NAME_MAX);
+ strncpy(bt->names[tb_frame], ar.name,
+ PARENT_BT_LUA_NAME_MAX - 1);
+ memset(bt->sources[tb_frame], 0,
+ PARENT_BT_LUA_NAME_MAX);
+ strncpy(bt->sources[tb_frame], ar.source,
+ PARENT_BT_LUA_NAME_MAX - 1);
+ tb_frame++;
+ }
+ lua_frame++;
+ }
+ while (tb_frame < PARENT_BT_LUA_LEN_MAX &&
+ lua_getstack(L, lua_frame, &ar) > 0) {
+ /* Trace Lua frame. */
+ lua_getinfo(L, "Sln", &ar);
+ if (*ar.what == 'C')
+ break;
+ bt->lines[tb_frame] = ar.currentline;
+ memset(bt->names[tb_frame], 0, PARENT_BT_LUA_NAME_MAX);
+ if (ar.name != NULL) {
+ strncpy(bt->names[tb_frame], ar.name,
+ PARENT_BT_LUA_NAME_MAX - 1);
+ } else {
+ strncpy(bt->names[tb_frame], "(unnamed)",
+ PARENT_BT_LUA_NAME_MAX - 1);
+ }
+ memset(bt->sources[tb_frame], 0, PARENT_BT_LUA_NAME_MAX);
+ strncpy(bt->sources[tb_frame], ar.source,
+ PARENT_BT_LUA_NAME_MAX - 1);
+ tb_frame++;
+ lua_frame++;
+ }
+
+ bt->cnt = tb_frame;
+ f->storage.lua.parent_bt = bt;
+
+ return 0;
+}
+
+static void
+fiber_parent_bt_free(struct fiber *f)
+{
+ free(f->storage.lua.parent_bt);
+}
+#endif /* ENABLE_BACKTRACE */
static int
lbox_fiber_statof_map(struct fiber *f, void *cb_ctx, bool backtrace)
@@ -299,6 +435,7 @@ lbox_fiber_statof_map(struct fiber *f, void *cb_ctx, bool backtrace)
if (backtrace) {
#ifdef ENABLE_BACKTRACE
struct lua_fiber_tb_ctx tb_ctx;
+ struct lua_parent_tb_ctx parent_tb_ctx;
tb_ctx.L = L;
tb_ctx.R = f->storage.lua.stack;
tb_ctx.lua_frame = 0;
@@ -309,14 +446,14 @@ lbox_fiber_statof_map(struct fiber *f, void *cb_ctx, bool backtrace)
f != fiber() ? &f->ctx : NULL, &tb_ctx);
lua_settable(L, -3);
- tb_ctx.lua_frame = 0;
- tb_ctx.tb_frame = 0;
- tb_ctx.R = NULL;
+ parent_tb_ctx.L = L;
+ parent_tb_ctx.bt = f->storage.lua.parent_bt;
+ parent_tb_ctx.tb_frame = 0;
lua_pushstring(L, "backtrace_parent");
lua_newtable(L);
- backtrace_foreach_ip(fiber_backtrace_cb,
+ backtrace_foreach_ip(fiber_parent_backtrace_cb,
f->parent_bt_ip_buf,
- FIBER_PARENT_BT_MAX, &tb_ctx);
+ FIBER_PARENT_BT_MAX, &parent_tb_ctx);
lua_settable(L, -3);
#endif /* ENABLE_BACKTRACE */
}
@@ -481,10 +618,14 @@ lua_fiber_run_f(MAYBE_UNUSED va_list ap)
* We can unref child stack here,
* otherwise we have to unref child stack in join
*/
- if (f->flags & FIBER_IS_JOINABLE)
+ if (f->flags & FIBER_IS_JOINABLE) {
lua_pushinteger(L, coro_ref);
- else
+ } else {
+#ifdef ENABLE_BACKTRACE
+ fiber_parent_bt_free(f);
+#endif
luaL_unref(L, LUA_REGISTRYINDEX, coro_ref);
+ }
return result;
}
@@ -506,6 +647,11 @@ fiber_create(struct lua_State *L)
luaT_error(L);
}
+#ifdef ENABLE_BACKTRACE
+ // TODO: error handling
+ fiber_parent_bt_init(f, L);
+#endif
+
/* Move the arguments to the new coro */
lua_xmove(L, child_L, lua_gettop(L));
/* XXX: 'fiber' is leaked if this throws a Lua error. */
@@ -886,8 +1032,12 @@ lbox_fiber_join(struct lua_State *L)
lua_xmove(child_L, L, num_ret);
}
}
- if (child_L != NULL)
+ if (child_L != NULL) {
+#ifdef ENABLE_BACKTRACE
+ fiber_parent_bt_free(fiber);
+#endif
luaL_unref(L, LUA_REGISTRYINDEX, coro_ref);
+ }
return num_ret + 1;
}
diff --git a/src/lua/fiber.h b/src/lua/fiber.h
index e29898706..450840fb0 100644
--- a/src/lua/fiber.h
+++ b/src/lua/fiber.h
@@ -36,6 +36,25 @@ extern "C" {
struct lua_State;
+/**
+ * Maximal name length (including '\0')
+ * and backtrace length.
+ */
+enum {
+ PARENT_BT_LUA_NAME_MAX = 64,
+ PARENT_BT_LUA_LEN_MAX = 8
+};
+
+/**
+ * Stores lua parent backtrace for fiber.
+ */
+struct parent_bt_lua {
+ int cnt;
+ char names[PARENT_BT_LUA_LEN_MAX][PARENT_BT_LUA_NAME_MAX];
+ char sources[PARENT_BT_LUA_LEN_MAX][PARENT_BT_LUA_NAME_MAX];
+ int lines[PARENT_BT_LUA_LEN_MAX];
+};
+
/**
* Initialize box.fiber system
*/
--
2.31.1
More information about the Tarantool-patches
mailing list