[tarantool-patches] [error 3/3] error: Add C frames in error.traceback
Ilya Markov
imarkov at tarantool.org
Fri May 4 17:07:20 MSK 2018
* 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 <diag.h>
#include <fiber.h>
+#include <backtrace.h>
#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: <number>
+ - 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: <number>
- file: builtin/box/console.lua
function: repl
line: <number>
- file: builtin/box/console.lua
- function: eval
line: <number>
- function: pcall
- file: builtin/socket.lua
line: <number>
+ - function: lua_pcall
+ - function: luaT_call
+ - function: luaB_pcall
- function: pcall
- file: builtin/box/console.lua
+ function: eval
line: <number>
- file: builtin/box/console.lua
function: repl
line: <number>
- file: builtin/box/console.lua
- function: eval
line: <number>
- function: pcall
- file: builtin/socket.lua
line: <number>
+ - function: lua_pcall
+ - function: luaT_call
+ - function: luaB_pcall
- function: pcall
- file: builtin/box/console.lua
+ function: eval
line: <number>
- file: builtin/box/console.lua
function: repl
line: <number>
- file: builtin/box/console.lua
- function: eval
line: <number>
- function: pcall
+ - file: builtin/socket.lua
+ line: <number>
+ - 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
More information about the Tarantool-patches
mailing list