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

Leonid Vasiliev lvasiliev at tarantool.org
Tue Mar 24 15:45:59 MSK 2020


Lua bactrace was added to a tarantool error.

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

@TarantoolBot document
Title: error.bt

Lua backtrace was added to a tarantool error.

Needed for #4398
---
 src/lib/core/diag.c       | 20 ++++++++++++++++++++
 src/lib/core/diag.h       |  5 +++++
 src/lib/core/exception.cc |  1 +
 src/lua/error.c           | 45 ++++++++++++++++++++++++++++++++++++++++++++-
 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, 160 insertions(+), 24 deletions(-)

diff --git a/src/lib/core/diag.c b/src/lib/core/diag.c
index c350abb..c392c1e 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 *
@@ -76,3 +77,22 @@ error_vformat_msg(struct error *e, const char *format, va_list ap)
 	vsnprintf(e->errmsg, sizeof(e->errmsg), format, ap);
 }
 
+void
+error_set_lua_bt(struct error *e, const char *lua_bt)
+{
+	if (e == NULL)
+		return;
+
+	if (lua_bt == NULL) {
+		free(e->lua_bt);
+		e->lua_bt = NULL;
+		return;
+	}
+
+	size_t bt_len = strlen(lua_bt);
+	e->lua_bt = realloc(e->lua_bt, bt_len + 1);
+	if (e->lua_bt == NULL)
+		return;
+	strcpy(e->lua_bt, lua_bt);
+	return;
+}
diff --git a/src/lib/core/diag.h b/src/lib/core/diag.h
index f763957..c917871 100644
--- a/src/lib/core/diag.h
+++ b/src/lib/core/diag.h
@@ -84,6 +84,8 @@ struct error {
 	char file[DIAG_FILENAME_MAX];
 	/* Error description. */
 	char errmsg[DIAG_ERRMSG_MAX];
+	/* Lua backtrace */
+	char *lua_bt;
 };
 
 static inline void
@@ -126,6 +128,9 @@ error_format_msg(struct error *e, const char *format, ...);
 void
 error_vformat_msg(struct error *e, const char *format, va_list ap);
 
+void
+error_set_lua_bt(struct error *e, const char *lua_bt);
+
 /**
  * Diagnostics Area - a container for errors
  */
diff --git a/src/lib/core/exception.cc b/src/lib/core/exception.cc
index 76dcea5..d316d9b 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 d82e78d..3a12e20 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,13 @@ luaT_pusherror(struct lua_State *L, struct error *e)
 	 * then set the finalizer.
 	 */
 	error_ref(e);
+
+	if (e->lua_bt == NULL) {
+		char *lua_bt = traceback(L);
+		error_set_lua_bt(e, lua_bt);
+		free(lua_bt);
+	}
+
 	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 64fa5eb..16cdaf7 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 7f24986..765ce73 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 7331f61..3908733 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 b8e9abc..f3616ec 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 047591b..f432d51 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 e1c2f99..e82ac2c 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.7.4



More information about the Tarantool-patches mailing list