[Tarantool-patches] [PATCH 1/2] Add a Lua backtrace to error

Leonid Vasiliev lvasiliev at tarantool.org
Thu Jan 30 19:42:36 MSK 2020


In accordance with https://github.com/tarantool/tarantool/issues/4398
we want to have a Lua backtrace for the box.error

Needed for #4398
---
 src/lib/core/diag.c       |  1 +
 src/lib/core/diag.h       |  1 +
 src/lib/core/exception.cc |  1 +
 src/lua/error.c           | 42 ++++++++++++++++++++++++++++++++++++++-
 src/lua/error.h           |  3 +++
 src/lua/error.lua         | 20 ++++++++++++++-----
 test/app/fiber.result     | 35 ++++++++++++++++++++++++--------
 test/app/fiber.test.lua   | 11 +++++++++-
 test/box/misc.result      | 34 +++++++++++++++++++++++--------
 test/box/misc.test.lua    | 10 +++++++++-
 10 files changed, 134 insertions(+), 24 deletions(-)

diff --git a/src/lib/core/diag.c b/src/lib/core/diag.c
index c350abb4a..d13f329ad 100644
--- a/src/lib/core/diag.c
+++ b/src/lib/core/diag.c
@@ -53,6 +53,7 @@ error_create(struct error *e,
 		e->line = 0;
 	}
 	e->errmsg[0] = '\0';
+	e->lua_bt = NULL;
 }
 
 struct diag *
diff --git a/src/lib/core/diag.h b/src/lib/core/diag.h
index f763957c2..4a39fe16b 100644
--- a/src/lib/core/diag.h
+++ b/src/lib/core/diag.h
@@ -84,6 +84,7 @@ struct error {
 	char file[DIAG_FILENAME_MAX];
 	/* Error description. */
 	char errmsg[DIAG_ERRMSG_MAX];
+	char *lua_bt;
 };
 
 static inline void
diff --git a/src/lib/core/exception.cc b/src/lib/core/exception.cc
index 76dcea553..d316d9bb3 100644
--- a/src/lib/core/exception.cc
+++ b/src/lib/core/exception.cc
@@ -42,6 +42,7 @@ extern "C" {
 static void
 exception_destroy(struct error *e)
 {
+	free(e->lua_bt);
 	delete (Exception *) e;
 }
 
diff --git a/src/lua/error.c b/src/lua/error.c
index d82e78dc4..11afe97f8 100644
--- a/src/lua/error.c
+++ b/src/lua/error.c
@@ -34,8 +34,44 @@
 #include <fiber.h>
 #include "utils.h"
 
+#include <string.h>
+
 static int CTID_CONST_STRUCT_ERROR_REF = 0;
 
+/*
+ * Memory for the traceback string is obtained with malloc,
+ * and can be freed with free.
+*/
+static char*
+traceback (lua_State *L) {
+	int top = lua_gettop(L);
+
+	lua_getfield(L, LUA_GLOBALSINDEX, "debug");
+	if (!lua_istable(L, -1)) {
+		lua_settop(L, top);
+		return NULL;
+	}
+	lua_getfield(L, -1, "traceback");
+	if (!lua_isfunction(L, -1)) {
+		lua_settop(L, top);
+		return NULL;
+	}
+
+	// call debug.traceback
+	lua_call(L, 0, 1);
+
+	// get result of the debug.traceback call
+	if (!lua_isstring(L, -1)) {
+		lua_settop(L, top);
+		return NULL;
+	}
+
+	char *bt = strdup(lua_tostring(L, -1));
+	lua_settop(L, top);
+
+	return bt;
+}
+
 struct error *
 luaL_iserror(struct lua_State *L, int narg)
 {
@@ -53,7 +89,7 @@ luaL_iserror(struct lua_State *L, int narg)
 	return e;
 }
 
-static struct error *
+struct error *
 luaL_checkerror(struct lua_State *L, int narg)
 {
 	struct error *error = luaL_iserror(L, narg);
@@ -85,6 +121,10 @@ luaT_pusherror(struct lua_State *L, struct error *e)
 	 * then set the finalizer.
 	 */
 	error_ref(e);
+
+	if (e->lua_bt == NULL)
+		e->lua_bt = traceback(L);
+
 	assert(CTID_CONST_STRUCT_ERROR_REF != 0);
 	struct error **ptr = (struct error **)
 		luaL_pushcdata(L, CTID_CONST_STRUCT_ERROR_REF);
diff --git a/src/lua/error.h b/src/lua/error.h
index 64fa5eba3..16cdaf7fe 100644
--- a/src/lua/error.h
+++ b/src/lua/error.h
@@ -65,6 +65,9 @@ luaT_pusherror(struct lua_State *L, struct error *e);
 struct error *
 luaL_iserror(struct lua_State *L, int narg);
 
+struct error *
+luaL_checkerror(struct lua_State *L, int narg);
+
 void
 tarantool_lua_error_init(struct lua_State *L);
 
diff --git a/src/lua/error.lua b/src/lua/error.lua
index 7f249864a..f296ecf94 100644
--- a/src/lua/error.lua
+++ b/src/lua/error.lua
@@ -24,6 +24,7 @@ struct error {
     char _file[DIAG_FILENAME_MAX];
     /* Error description. */
     char _errmsg[DIAG_ERRMSG_MAX];
+    char *lua_bt;
 };
 
 char *
@@ -83,10 +84,18 @@ local function error_trace(err)
         return {}
     end
     return {
-        { file = ffi.string(err._file), line = tonumber(err._line) };
+        { file = ffi.string(err._file), line = tonumber(err._line) }
     }
 end
 
+local function error_backtrace(err)
+    local result = "Backtrace is absent"
+    if err.lua_bt ~= ffi.nullptr then
+        result = ffi.string(err.lua_bt)
+    end
+    return result
+end
+
 local function error_errno(err)
     local e = err._saved_errno
     if e == 0 then
@@ -96,10 +105,11 @@ local function error_errno(err)
 end
 
 local error_fields = {
-    ["type"]        = error_type;
-    ["message"]     = error_message;
-    ["trace"]       = error_trace;
-    ["errno"]       = error_errno;
+    ["type"] = error_type,
+    ["message"] = error_message,
+    ["trace"] = error_trace,
+    ["errno"] = error_errno,
+    ["bt"] = error_backtrace
 }
 
 local function error_unpack(err)
diff --git a/test/app/fiber.result b/test/app/fiber.result
index 6d9604ad8..d25a266d5 100644
--- a/test/app/fiber.result
+++ b/test/app/fiber.result
@@ -1036,14 +1036,33 @@ st;
 ---
 - false
 ...
-e:unpack();
----
-- type: ClientError
-  code: 1
-  message: Illegal parameters, oh my
-  trace:
-  - file: '[string "function err() box.error(box.error.ILLEGAL_PA..."]'
-    line: 1
+unpack_res = e:unpack();
+---
+...
+unpack_res['code'] == 1;
+---
+- true
+...
+unpack_res['trace'][1]['file'] == '[string "function err()' ..
+    ' box.error(box.error.ILLEGAL_PA..."]';
+---
+- true
+...
+unpack_res['trace'][1]['line'] == 1;
+---
+- true
+...
+unpack_res['type'] == 'ClientError';
+---
+- true
+...
+unpack_res['message'] == 'Illegal parameters, oh my';
+---
+- true
+...
+unpack_res['bt'] == e.bt;
+---
+- true
 ...
 flag = false;
 ---
diff --git a/test/app/fiber.test.lua b/test/app/fiber.test.lua
index 6df210d9c..c9cbd7bee 100644
--- a/test/app/fiber.test.lua
+++ b/test/app/fiber.test.lua
@@ -428,7 +428,16 @@ function test1()
 end;
 st, e = test1();
 st;
-e:unpack();
+
+unpack_res = e:unpack();
+
+unpack_res['code'] == 1;
+unpack_res['trace'][1]['file'] == '[string "function err()' ..
+    ' box.error(box.error.ILLEGAL_PA..."]';
+unpack_res['trace'][1]['line'] == 1;
+unpack_res['type'] == 'ClientError';
+unpack_res['message'] == 'Illegal parameters, oh my';
+unpack_res['bt'] == e.bt;
 
 flag = false;
 function test2()
diff --git a/test/box/misc.result b/test/box/misc.result
index 5ac5e0f26..b94ba5058 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -125,14 +125,32 @@ e
 ---
 - Illegal parameters, bla bla
 ...
-e:unpack()
----
-- type: ClientError
-  code: 1
-  message: Illegal parameters, bla bla
-  trace:
-  - file: '[C]'
-    line: 4294967295
+unpack_res = e:unpack()
+---
+...
+unpack_res['code'] == 1
+---
+- true
+...
+unpack_res['trace'][1]['file'] == '[C]'
+---
+- true
+...
+unpack_res['trace'][1]['line'] == 4294967295
+---
+- true
+...
+unpack_res['type'] == 'ClientError'
+---
+- true
+...
+unpack_res['message'] == 'Illegal parameters, bla bla'
+---
+- true
+...
+unpack_res['bt'] == e.bt
+---
+- true
 ...
 e.type
 ---
diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua
index e1c2f990f..e82ac2cf9 100644
--- a/test/box/misc.test.lua
+++ b/test/box/misc.test.lua
@@ -35,7 +35,15 @@ box.error(box.error.ILLEGAL_PARAMS, "bla bla")
 box.error()
 e = box.error.last()
 e
-e:unpack()
+
+unpack_res = e:unpack()
+unpack_res['code'] == 1
+unpack_res['trace'][1]['file'] == '[C]'
+unpack_res['trace'][1]['line'] == 4294967295
+unpack_res['type'] == 'ClientError'
+unpack_res['message'] == 'Illegal parameters, bla bla'
+unpack_res['bt'] == e.bt
+
 e.type
 e.code
 e.message
-- 
2.17.1



More information about the Tarantool-patches mailing list