Skip to content

Commit 737baaf

Browse files
nshylocker
authored andcommitted
error: set trace of caller for API written in LuaC
In scope of the tarantool#9914 issue we are setting error trace for API to the caller frame. For the API written in LuaC without any Lua code around it is easy task. Just make `luaT_error` set the proper trace. By the way test we don't mess up trace for code evaluated thru netbox. Part of tarantool#9914 NO_CHANGELOG=incomplete NO_DOC=incomplete
1 parent 75e5cb6 commit 737baaf

File tree

7 files changed

+215
-34
lines changed

7 files changed

+215
-34
lines changed

src/box/lua/call.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ execute_lua_eval(lua_State *L)
447447
uint32_t expr_len = ctx->name_len;
448448
if (luaL_loadbuffer(L, expr, expr_len, "=eval")) {
449449
diag_set(LuajitError, lua_tostring(L, -1));
450-
luaT_error(L);
450+
luaT_error_at(L, 0);
451451
}
452452

453453
/* Unpack arguments */

src/box/lua/error.cc

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -118,33 +118,6 @@ error_create_table_case_get_reason(lua_State *L, int index,
118118
return reason != NULL ? reason : "";
119119
}
120120

121-
/**
122-
* Set the error location information (file, line) to Lua stack frame at
123-
* the given level. Level 1 is the Lua function that called this function.
124-
* If level is <= 0 or the function failed to get the location information,
125-
* the location is cleared.
126-
*/
127-
static void
128-
set_error_trace(lua_State *L, int level, struct error *error)
129-
{
130-
const char *file = "";
131-
unsigned line = 0;
132-
lua_Debug info;
133-
if (level > 0 &&
134-
lua_getstack(L, level, &info) &&
135-
lua_getinfo(L, "Sl", &info)) {
136-
if (*info.short_src) {
137-
file = info.short_src;
138-
} else if (*info.source) {
139-
file = info.source;
140-
} else {
141-
file = "eval";
142-
}
143-
line = info.currentline;
144-
}
145-
error_set_location(error, file, line);
146-
}
147-
148121
/**
149122
* Parse Lua arguments (they can come as single table or as
150123
* separate members) and construct struct error with given values.
@@ -256,7 +229,7 @@ luaT_error_create(lua_State *L, int top_base)
256229
raise:
257230
struct error *error;
258231
error = box_error_new(NULL, 0, code, custom_type, "%s", reason);
259-
set_error_trace(L, level, error);
232+
luaT_error_set_trace(L, level, error);
260233
/*
261234
* Set the previous error, if it was specified.
262235
*/
@@ -322,17 +295,18 @@ luaT_error_call(lua_State *L)
322295
if (lua_type(L, 3) != LUA_TNUMBER)
323296
goto bad_arg;
324297
int level = lua_tointeger(L, 3);
325-
set_error_trace(L, level, e);
298+
luaT_error_set_trace(L, level, e);
326299
}
327300
} else {
328301
e = luaT_error_create(L, 2);
329302
if (e == NULL)
330303
goto bad_arg;
331304
}
332305
diag_set_error(&fiber()->diag, e);
333-
return luaT_error(L);
306+
return luaT_error_at(L, 0);
334307
bad_arg:
335-
return luaL_error(L, "box.error(): bad arguments");
308+
diag_set(IllegalParams, "box.error(): bad arguments");
309+
return luaT_error(L);
336310
}
337311

338312
static int

src/lua/error.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,46 @@ luaT_pusherror(struct lua_State *L, struct error *e)
9393
luaL_setcdatagc(L, -2);
9494
}
9595

96+
void
97+
luaT_error_set_trace(lua_State *L, int level, struct error *error)
98+
{
99+
const char *file = "";
100+
unsigned line = 0;
101+
lua_Debug info;
102+
if (level > 0 &&
103+
lua_getstack(L, level, &info) &&
104+
lua_getinfo(L, "Sl", &info)) {
105+
if (*info.short_src) {
106+
file = info.short_src;
107+
} else if (*info.source) {
108+
file = info.source;
109+
} else {
110+
file = "eval";
111+
}
112+
line = info.currentline;
113+
}
114+
error_set_location(error, file, line);
115+
}
116+
96117
int
97-
luaT_error(lua_State *L)
118+
luaT_error_at(lua_State *L, int level)
98119
{
99120
struct error *e = diag_last_error(&fiber()->diag);
100121
assert(e != NULL);
122+
if (level > 0)
123+
luaT_error_set_trace(L, level, e);
101124
luaT_pusherror(L, e);
102125
lua_error(L);
103126
unreachable();
104127
return 0;
105128
}
106129

130+
int
131+
luaT_error(lua_State *L)
132+
{
133+
return luaT_error_at(L, 1);
134+
}
135+
107136
int
108137
luaT_push_nil_and_error(lua_State *L)
109138
{

src/lua/error.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,23 @@ extern uint32_t CTID_CONST_STRUCT_ERROR_REF;
4343
struct error;
4444

4545
/**
46-
* Re-throws the last Tarantool error as a Lua object.
46+
* Re-throws the last Tarantool error as a Lua object. Set trace frame
47+
* of to the caller of Lua C API.
48+
*
4749
* \sa lua_error()
4850
* \sa box_error_last()
4951
*/
5052
LUA_API int
5153
luaT_error(lua_State *L);
5254

55+
/**
56+
* Same as luaT_error but set error trace frame according to given level.
57+
* luaT_error same as this function with level equal to 1. If level is 0
58+
* then error trace is unchanged.
59+
*/
60+
int
61+
luaT_error_at(lua_State *L, int level);
62+
5363
/**
5464
* Return nil as the first return value and an error as the
5565
* second. The error is received using box_error_last().
@@ -78,6 +88,15 @@ luaL_checkerror(struct lua_State *L, int narg);
7888
void
7989
tarantool_lua_error_init(struct lua_State *L);
8090

91+
/**
92+
* Set the error location information (file, line) to Lua stack frame at
93+
* the given level. Level 1 is the Lua function that called this function.
94+
* If level is <= 0 or the function failed to get the location information,
95+
* the location is cleared.
96+
*/
97+
void
98+
luaT_error_set_trace(lua_State *L, int level, struct error *error);
99+
81100
#if defined(__cplusplus)
82101
} /* extern "C" */
83102
#endif /* defined(__cplusplus) */

test/box-luatest/error_subsystem_improvements_test.lua

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,3 +1050,37 @@ g.test_illegal_params = function()
10501050
t.assert_equals(e.type, 'IllegalParams')
10511051
t.assert_equals(e.message, 'foo')
10521052
end
1053+
1054+
g.test_netbox_call_trace = function(cg)
1055+
local netbox = require('net.box')
1056+
local file = debug.getinfo(1, 'S').short_src
1057+
local line = debug.getinfo(1, 'l').currentline + 4
1058+
cg.server:exec(function()
1059+
box.schema.create_space('test')
1060+
rawset(_G, 'test_func', function()
1061+
box.space.test:insert({})
1062+
end)
1063+
end)
1064+
1065+
local function test_error(file, line, fn, ...)
1066+
local ok, err = pcall(fn, ...)
1067+
t.assert_not(ok)
1068+
t.assert(box.error.is(err))
1069+
t.assert_equals(err.message, "No index #0 is defined in space 'test'")
1070+
local trace = err.trace[1]
1071+
t.assert_equals(trace.line, line)
1072+
t.assert_equals(trace.file, file)
1073+
end
1074+
1075+
local c = netbox.connect(cg.server.net_box_uri)
1076+
test_error('eval', 3, c.eval, c, '\n\nbox.space.test:insert({})')
1077+
test_error(file, line, c.call, c, 'test_func')
1078+
end
1079+
1080+
g.after_test('test_netbox_call_trace', function(cg)
1081+
cg.server:exec(function()
1082+
if box.space.test ~= nil then
1083+
box.space.test:drop()
1084+
end
1085+
end)
1086+
end)

test/unit/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,15 @@ create_unit_test(PREFIX lua_utils
638638
${READLINE_LIBRARIES}
639639
)
640640

641+
create_unit_test(PREFIX lua_error
642+
SOURCES lua_error.c
643+
LIBRARIES unit core server
644+
${LUAJIT_LIBRARIES}
645+
${CURL_LIBRARIES}
646+
${LIBYAML_LIBRARIES}
647+
${READLINE_LIBRARIES}
648+
)
649+
641650
create_unit_test(PREFIX lua_msgpack
642651
SOURCES lua_msgpack.c
643652
LIBRARIES unit core server

test/unit/lua_error.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#include <stdio.h>
2+
3+
#include "diag.h"
4+
#include "fiber.h"
5+
#include "lualib.h"
6+
#include "memory.h"
7+
8+
#include "lua/error.h"
9+
#include "lua/utils.h"
10+
11+
#define UNIT_TAP_COMPATIBLE 1
12+
#include "unit.h"
13+
14+
static int
15+
raise_error(lua_State *L)
16+
{
17+
diag_set(IllegalParams, "foo");
18+
return luaT_error(L);
19+
}
20+
21+
static int
22+
raise_error_at(lua_State *L)
23+
{
24+
int level = lua_tointeger(L, 1);
25+
diag_set(IllegalParams, "bar");
26+
return luaT_error_at(L, level);
27+
}
28+
29+
static int
30+
error_trace(lua_State *L)
31+
{
32+
struct error *e = luaL_checkerror(L, 1);
33+
34+
lua_createtable(L, 0, 2);
35+
lua_pushstring(L, "file");
36+
lua_pushstring(L, e->file);
37+
lua_settable(L, -3);
38+
lua_pushstring(L, "line");
39+
lua_pushinteger(L, e->line);
40+
lua_settable(L, -3);
41+
return 1;
42+
}
43+
44+
const char *test_error_lua =
45+
"local this_file = debug.getinfo(1, 'S').short_src\n"
46+
"local line1 = debug.getinfo(1, 'l').currentline + 1\n"
47+
"local f1 = function(fn, ...) fn(...) end\n"
48+
"local line2 = debug.getinfo(1, 'l').currentline + 1\n"
49+
"local f2 = function(fn, ...) f1(fn, ...) end\n"
50+
"local line3 = debug.getinfo(1, 'l').currentline + 1\n"
51+
"local f3 = function(fn, ...) f2(fn, ...) end\n"
52+
"\n"
53+
"local function check(line, fn, ...)\n"
54+
" local ok, err = pcall(f3, fn, ...)\n"
55+
"\n"
56+
" assert(not ok, string.format('got %s', err))\n"
57+
" local trace = test_error_trace(err)\n"
58+
" assert(trace.file == this_file, string.format('got \"%s\"', trace.file))\n"
59+
" assert(trace.line == line,\n"
60+
" string.format('expected %d, got %d', line, trace.line))\n"
61+
"end\n"
62+
"\n"
63+
"assert(test_raise_error ~= nil)"
64+
"check(line1, test_raise_error)\n"
65+
"check(line1, test_raise_error_at, 1)\n"
66+
"check(line2, test_raise_error_at, 2)\n"
67+
"check(line3, test_raise_error_at, 3)\n"
68+
"\n"
69+
"local ok, err = pcall(f3, test_raise_error_at, 0)\n"
70+
"assert(not ok, string.format('got %s', err))\n"
71+
"local trace = test_error_trace(err)\n"
72+
"assert(string.find(trace.file, 'lua_error%.c') ~= nil,\n"
73+
" string.format('got %s', trace.file))\n";
74+
75+
static void
76+
test_error(lua_State *L)
77+
{
78+
plan(1);
79+
header();
80+
81+
int rc;
82+
83+
lua_pushcfunction(L, raise_error);
84+
lua_setglobal(L, "test_raise_error");
85+
lua_pushcfunction(L, raise_error_at);
86+
lua_setglobal(L, "test_raise_error_at");
87+
lua_pushcfunction(L, error_trace);
88+
lua_setglobal(L, "test_error_trace");
89+
90+
rc = luaT_dostring(L, test_error_lua);
91+
ok(rc == 0, rc == 0 ? "OK" : "got %s",
92+
diag_last_error(diag_get())->errmsg);
93+
94+
footer();
95+
check_plan();
96+
}
97+
98+
int
99+
main(void)
100+
{
101+
plan(1);
102+
header();
103+
104+
struct lua_State *L = luaL_newstate();
105+
luaL_openlibs(L);
106+
memory_init();
107+
tarantool_lua_error_init(L);
108+
109+
test_error(L);
110+
111+
memory_free();
112+
lua_close(L);
113+
114+
footer();
115+
return check_plan();
116+
}

0 commit comments

Comments
 (0)