From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp18.mail.ru (smtp18.mail.ru [94.100.176.155]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 51C7042F96C for ; Tue, 24 Mar 2020 15:46:12 +0300 (MSK) From: Leonid Vasiliev Date: Tue, 24 Mar 2020 15:45:59 +0300 Message-Id: <3a14bd84545d33eaded01ca0f23e12caba7eba9f.1585053742.git.lvasiliev@tarantool.org> In-Reply-To: References: In-Reply-To: References: Subject: [Tarantool-patches] [PATCH 1/6] error: Add a Lua backtrace to error List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: alexander.turenko@tarantool.org Cc: tarantool-patches@dev.tarantool.org 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 #include "utils.h" +#include + 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