[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