From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 0BCEC23D1B for ; Fri, 4 May 2018 10:07:37 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id zx_ONWLHGIUt for ; Fri, 4 May 2018 10:07:36 -0400 (EDT) Received: from smtp55.i.mail.ru (smtp55.i.mail.ru [217.69.128.35]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 3FCFF23D06 for ; Fri, 4 May 2018 10:07:36 -0400 (EDT) From: Ilya Markov Subject: [tarantool-patches] [error 3/3] error: Add C frames in error.traceback Date: Fri, 4 May 2018 17:07:20 +0300 Message-Id: In-Reply-To: References: <14848871.eLyv8AkjAN@home.lan> In-Reply-To: References: Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: georgy@tarantool.org Cc: tarantool-patches@freelists.org * Add backtrace generation in exception contructor. * Add merge of lua and C traces. * Modify C backtrace with support of current fiber stack. Relates #677 --- src/backtrace.cc | 10 ++++- src/exception.cc | 49 +++++++++++++++++++++ src/lua/error.c | 92 +++++++++++++++++++++++++++++++++++++-- test/app/traceback.result | 45 ++++++++++++++++--- test/app/traceback.test.lua | 14 +++++- test/box/misc.result | 26 ++++++++--- test/replication/wal_off.result | 2 +- test/replication/wal_off.test.lua | 2 +- 8 files changed, 220 insertions(+), 20 deletions(-) diff --git a/src/backtrace.cc b/src/backtrace.cc index a86255e..c0c778e 100644 --- a/src/backtrace.cc +++ b/src/backtrace.cc @@ -368,7 +368,15 @@ backtrace_foreach(backtrace_cb cb, coro_context *coro_ctx, void *cb_ctx) { unw_cursor_t unw_cur; unw_context_t unw_ctx; - coro_unwcontext(&unw_ctx, coro_ctx); + if (coro_ctx == &fiber()->ctx) { + /* + * We don't have to move over stack as we are inspecting + * current fiber. + */ + unw_getcontext(&unw_ctx); + } else { + coro_unwcontext(&unw_ctx, coro_ctx); + } unw_init_local(&unw_cur, &unw_ctx); int frame_no = 0; unw_word_t sp = 0, old_sp = 0, ip, offset; diff --git a/src/exception.cc b/src/exception.cc index 56077f7..d6b36ab 100644 --- a/src/exception.cc +++ b/src/exception.cc @@ -36,6 +36,7 @@ #include "fiber.h" #include "reflection.h" +#include "backtrace.h" extern "C" { @@ -93,6 +94,7 @@ static const struct method_info exception_methods[] = { const struct type_info type_Exception = make_type("Exception", NULL, exception_methods); + void * Exception::operator new(size_t size) { @@ -114,6 +116,44 @@ Exception::~Exception() if (this != &out_of_memory) { assert(refs == 0); } + if (this->frames_count > 0) { + struct diag_frame *frame, *next; + /* Cleanup lua trace. */ + for (frame = rlist_first_entry(&this->frames, typeof(*frame), + link); + &frame->link != &this->frames;) { + next = rlist_next_entry(frame, link); + free(frame); + frame = next; + } + } +} + +static int +error_backtrace_cb(int frameno, void *frameret, const char *func, + size_t offset, void *cb_ctx) +{ + (void) frameno; + (void) frameret; + (void) offset; + Exception *e = (Exception *) cb_ctx; + + struct diag_frame * frame = + (struct diag_frame *) malloc(sizeof(*frame)); + if (frame == NULL) { + diag_set(OutOfMemory, sizeof(struct diag_frame), + "malloc", "struct diag_frame"); + return -1; + } + if (func) + snprintf(frame->func_name, sizeof(frame->func_name), "%s", func); + else + frame->func_name[0] = 0; + frame->filename[0] = 0; + frame->line = 0; + rlist_add_tail_entry(&e->frames, frame, link); + e->frames_count++; + return 0; } Exception::Exception(const struct type_info *type_arg, const char *file, @@ -121,6 +161,15 @@ Exception::Exception(const struct type_info *type_arg, const char *file, { error_create(this, exception_destroy, exception_raise, exception_log, type_arg, file, line); + int old_errno = errno; + if (cord()) { + backtrace_foreach(error_backtrace_cb, &fiber()->ctx, this); + if (this->frames_count > 0) { + rlist_shift_tail(&this->frames); + this->frames_count--; + } + } + errno = old_errno; } void diff --git a/src/lua/error.c b/src/lua/error.c index e6ccd32..3e667a5 100644 --- a/src/lua/error.c +++ b/src/lua/error.c @@ -31,6 +31,7 @@ #include #include +#include #include "utils.h" #include "error.h" @@ -104,15 +105,36 @@ luaT_pusherror(struct lua_State *L, struct error *e) luaL_setcdatagc(L, -2); } +static inline void +copy_frame(struct diag_frame *dest, struct diag_frame *src) +{ + dest->line = src->line; + strcpy(dest->func_name, src->func_name); + strcpy(dest->filename, src->filename); +} + static int traceback_error(struct lua_State *L, struct error *e) { lua_Debug ar; int level = 0; + struct rlist lua_frames; + struct rlist *lua_framesp; + struct diag_frame *frame, *next; + if (e->frames_count <= 0) { + lua_framesp = &e->frames; + } else { + lua_framesp = &lua_frames; + rlist_create(lua_framesp); + } + /* + * At this moment error object was created from exception so + * traceback list is already created and filled with C trace. + * Now we need to create lua trace and merge it with the existing one. + */ while (lua_getstack(L, level++, &ar) > 0) { lua_getinfo(L, "Sln", &ar); - struct diag_frame *frame = - (struct diag_frame *) malloc(sizeof(*frame)); + frame = (struct diag_frame *) malloc(sizeof(*frame)); if (frame == NULL) { luaT_pusherror(L, e); return 1; @@ -136,7 +158,67 @@ traceback_error(struct lua_State *L, struct error *e) frame->line = 0; e->frames_count++; } - rlist_add_entry(&e->frames, frame, link); + rlist_add_entry(lua_framesp, frame, link); + } + if (e->frames_count > 0) { + struct diag_frame *lua_frame = rlist_first_entry(lua_framesp, + typeof(*lua_frame), + link); + rlist_foreach_entry(frame, &e->frames, link) { + /* We insert trace of lua user code in c trace, + * where C calls lua.*/ + if (strncmp(frame->func_name, "lj_BC_FUNCC", + sizeof("lj_BC_FUNCC") - 1) == 0 && + frame->line != -1) { + /* We have to bypass internal error calls. */ + next = rlist_next_entry(frame, link); + if (strncmp(next->func_name, "lj_err_run", + sizeof("lj_err_run") - 1) == 0) + continue; + e->frames_count--; + for (;&lua_frame->link != lua_framesp; + lua_frame = rlist_next_entry(lua_frame, + link)) { + /* Skip empty frames. */ + if (strncmp(lua_frame->filename, + "[C]", + sizeof("[C]") - 1) == 0 && + (*lua_frame->func_name) == '?') + continue; + break; + } + + for (; &lua_frame->link != lua_framesp; + lua_frame = rlist_next_entry(lua_frame, + link)) { + if (lua_frame->filename[0]== 0 && + lua_frame->func_name[0] == 0) + break; + struct diag_frame *frame_copy = + (struct diag_frame *) + malloc(sizeof(*frame_copy)); + copy_frame(frame_copy, lua_frame); + rlist_add_entry(&frame->link, + frame_copy, link); + e->frames_count++; + /* + * Mark the C frame that it was replaced + * with lua. + */ + frame->line = -1; + } + } + } + } + if (&e->frames != lua_framesp) { + /* Cleanup lua trace. */ + for (frame = rlist_first_entry(lua_framesp, typeof(*frame), + link); + &frame->link != lua_framesp;) { + next = rlist_next_entry(frame, link); + free(frame); + frame = next; + } } luaT_pusherror(L, e); return 1; @@ -174,6 +256,10 @@ lua_error_gettraceback(struct lua_State *L) rlist_foreach_entry(frame, &e->frames, link) { if (frame->func_name[0] != 0 || frame->line > 0 || frame->filename[0] != 0) { + if (strncmp(frame->func_name, "lj_BC_FUNCC", + sizeof("lj_BC_FUNCC") - 1) == 0 && + frame->line == -1) + continue; /* push index */ lua_pushnumber(L, index++); /* push value - table of filename and line */ diff --git a/test/app/traceback.result b/test/app/traceback.result index bb54573..85576bf 100644 --- a/test/app/traceback.result +++ b/test/app/traceback.result @@ -5,17 +5,17 @@ err.type --- - ClientError ... -err.trace[1].file +err.trace[12].file --- - builtin/socket.lua ... -err.trace[1].line +err.trace[12].line --- - 997 ... #err.trace --- -- 8 +- 30 ... s, err = pcall(error, "oh no" ) --- @@ -33,7 +33,7 @@ err.message ... #err.trace --- -- 8 +- 33 ... nil_var=nil --- @@ -61,7 +61,7 @@ err.message ... #err.trace --- -- 10 +- 36 ... box.begin() --- @@ -82,7 +82,38 @@ err.message ... #err.trace --- -- 10 +- 39 +... +s = box.schema.space.create("space") +--- +... +_ = s:create_index("prim") +--- +... +_ =s:on_replace(fail) +--- +... +st,err = pcall(s.insert, s, {1}) +--- +... +err= err:unpack() +--- +... +err.type +--- +- LuajitError +... +err.message +--- +- '[string "function fail() return nil_var.b end "]:1: attempt to index global ''nil_var'' + (a nil value)' +... +#err.trace +--- +- 45 +... +s:drop() +--- ... errinj = box.error.injection --- @@ -117,7 +148,7 @@ err.message ... #err.trace --- -- 8 +- 32 ... space:drop() --- diff --git a/test/app/traceback.test.lua b/test/app/traceback.test.lua index 51af677..1034049 100644 --- a/test/app/traceback.test.lua +++ b/test/app/traceback.test.lua @@ -1,7 +1,7 @@ s, err = pcall(box.error, 1, "err") err.type -err.trace[1].file -err.trace[1].line +err.trace[12].file +err.trace[12].line #err.trace s, err = pcall(error, "oh no" ) @@ -27,6 +27,16 @@ err.type err.message #err.trace +s = box.schema.space.create("space") +_ = s:create_index("prim") +_ =s:on_replace(fail) +st,err = pcall(s.insert, s, {1}) +err= err:unpack() +err.type +err.message +#err.trace +s:drop() + errinj = box.error.injection space = box.schema.space.create('tweedledum') index = space:create_index('primary', { type = 'hash' }) diff --git a/test/box/misc.result b/test/box/misc.result index a5cad07..0a3c445 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -130,42 +130,58 @@ e:unpack() code: 1 message: Illegal parameters, bla bla trace: - - file: builtin/socket.lua - line: + - function: ClientError::ClientError(char const*, unsigned int, unsigned int, ...) + - function: BuildClientError + - function: box_error_set + - function: luaT_error_raise(lua_State*) - function: pcall - file: builtin/box/console.lua + function: eval line: - file: builtin/box/console.lua function: repl line: - file: builtin/box/console.lua - function: eval line: - function: pcall - file: builtin/socket.lua line: + - function: lua_pcall + - function: luaT_call + - function: luaB_pcall - function: pcall - file: builtin/box/console.lua + function: eval line: - file: builtin/box/console.lua function: repl line: - file: builtin/box/console.lua - function: eval line: - function: pcall - file: builtin/socket.lua line: + - function: lua_pcall + - function: luaT_call + - function: luaB_pcall - function: pcall - file: builtin/box/console.lua + function: eval line: - file: builtin/box/console.lua function: repl line: - file: builtin/box/console.lua - function: eval line: - function: pcall + - file: builtin/socket.lua + line: + - function: lua_pcall + - function: luaT_call + - function: lua_fiber_run_f + - function: fiber_cxx_invoke(int (*)(__va_list_tag*), __va_list_tag*) + - function: fiber_loop + - function: coro_init ... e.type --- diff --git a/test/replication/wal_off.result b/test/replication/wal_off.result index f50bfc2..451c8de 100644 --- a/test/replication/wal_off.result +++ b/test/replication/wal_off.result @@ -84,7 +84,7 @@ box.cfg { replication = wal_off_uri } check = "Read access to universe" --- ... -while string.find(box.info.replication[wal_off_id].upstream.message, check) == nil do fiber.sleep(0.01) end +while box.info.replication[wal_off_id].upstream.message == nil or string.find(box.info.replication[wal_off_id].upstream.message, check) == nil do fiber.sleep(0.01) end --- ... box.cfg { replication = "" } diff --git a/test/replication/wal_off.test.lua b/test/replication/wal_off.test.lua index 43cd0ae..9a5e02d 100644 --- a/test/replication/wal_off.test.lua +++ b/test/replication/wal_off.test.lua @@ -29,7 +29,7 @@ test_run:cmd('switch default') box.cfg { replication = wal_off_uri } check = "Read access to universe" -while string.find(box.info.replication[wal_off_id].upstream.message, check) == nil do fiber.sleep(0.01) end +while box.info.replication[wal_off_id].upstream.message == nil or string.find(box.info.replication[wal_off_id].upstream.message, check) == nil do fiber.sleep(0.01) end box.cfg { replication = "" } test_run:cmd("stop server wal_off") -- 2.7.4