From a095a0c533b6428dc513c27d8b1b3eb6c72a14af Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 16 Oct 2020 13:38:04 +0100 Subject: util.debug: Fix locals being reported under wrong stack frame in some cases (+tests!!) --- spec/util_debug_spec.lua | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ util/debug.lua | 2 +- 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 spec/util_debug_spec.lua diff --git a/spec/util_debug_spec.lua b/spec/util_debug_spec.lua new file mode 100644 index 00000000..510b7ac0 --- /dev/null +++ b/spec/util_debug_spec.lua @@ -0,0 +1,93 @@ +local dbg = require "util.debug"; + +describe("util.debug", function () + describe("traceback()", function () + it("works", function () + local tb = dbg.traceback(); + assert.is_string(tb); + end); + end); + describe("get_traceback_table()", function () + it("works", function () + local count = 0; + -- MUST stay in sync with the line numbers of these functions: + local f1_defined, f3_defined = 43, 15; + local function f3(f3_param) --luacheck: ignore 212/f3_param + count = count + 1; + + for i = 1, 2 do + local tb = dbg.get_traceback_table(i == 1 and coroutine.running() or nil, 0); + assert.is_table(tb); + --print(dbg.traceback(), "\n\n\n", require "util.serialization".serialize(tb, { fatal = false, unquoted = true})); + local found_f1, found_f3; + for _, frame in ipairs(tb) do + if frame.info.linedefined == f1_defined then + assert.equal(0, #frame.locals); + assert.equal("f2", frame.upvalues[1].name); + assert.equal("f1_upvalue", frame.upvalues[2].name); + found_f1 = true; + elseif frame.info.linedefined == f3_defined then + assert.equal("f3_param", frame.locals[1].name); + found_f3 = true; + end + end + assert.is_true(found_f1); + assert.is_true(found_f3); + end + end + local function f2() + local f2_local = "hello"; + return f3(f2_local); + end + local f1_upvalue = "upvalue1"; + local function f1() + f2(f1_upvalue); + end + + -- ok/err are caught and re-thrown so that + -- busted gets to handle them in its own way + local ok, err; + local function hook() + debug.sethook(); + ok, err = pcall(f1); + end + + -- Test the traceback is correct in various + -- types of caller environments + + -- From a Lua hook + debug.sethook(hook, "crl", 1); + local a = string.sub("abcdef", 3, 4); + assert.equal("cd", a); + debug.sethook(); + assert.equal(1, count); + + if not ok then + error(err); + end + ok, err = nil, nil; + + -- From a signal handler (C hook) + require "util.signal".signal("SIGUSR1", hook); + require "util.signal".raise("SIGUSR1"); + assert.equal(2, count); + + if not ok then + error(err); + end + ok, err = nil, nil; + + -- Inside a coroutine + local co = coroutine.create(function () + hook(); + end); + coroutine.resume(co); + + if not ok then + error(err); + end + + assert.equal(3, count); + end); + end); +end); diff --git a/util/debug.lua b/util/debug.lua index 9a28395a..4c924d40 100644 --- a/util/debug.lua +++ b/util/debug.lua @@ -104,7 +104,7 @@ local function get_traceback_table(thread, start_level) levels[(level-start_level)+1] = { level = level; info = info; - locals = get_locals_table(thread, level+(thread and 0 or 1)); + locals = get_locals_table(thread, level+1); upvalues = get_upvalues_table(info.func); }; end -- cgit v1.2.3