Karatoga
Karatoga

Reputation: 61

Get complete stacktrace of a coroutine, including where a coroutine is resumed

A simple example:

coroutine.resume(coroutine.create(function()
    print(debug.traceback())
end))
print(debug.traceback())

the output:

stack traceback:
        ./v.lua:2: in function <./v.lua:1>
stack traceback:
        ./v.lua:4: in main chunk
        [C]: in ?

It shows that traceback inside a coroutine doesn't know about how it is resumed, so that xxx: in main chunk doesn't show.

How can I get the complete stacktrace inside a coroutine?

Upvotes: 4

Views: 750

Answers (1)

Karatoga
Karatoga

Reputation: 61

Well, I found a workaround here.

Since one Lua VM has only one execution point at one time (and that's why a full call stack is there), we can record resumption information manually.

In lua, manually build a resume-stack trace.

local xresume = coroutine.resume
local xtrace = debug.traceback

-- magic here! Take care of main thread.
local mainthr = coroutine.running()      -- captureing is a must.
debug.traceback = function(athr)
  if athr then return xtrace(athr) end  -- no interest in specified thread.
  return xtrace(mainthr)
end

coroutine.resume = function(thr, ...)
  -- another magic.
  local uptrace = debug.traceback
  debug.traceback = function(athr)
    if athr then return xtrace(athr) end  -- no interest in specified thread.
    return xtrace(thr)     -- trace the stack of thr.
      .. '\n' .. uptrace() -- trace from thr's resume point.
  end
  
  local result = { xresume(thr, ...) }
  debug.traceback = uptrace
  return table.unpack(result)
end

Other tips:

  • Using global table to store threads also works. But you still need to capture main thread so that it can be traced everywhere.

  • Writing codes in C function can prevent traceback into hooked coroutine.resume and debug.traceback itself, give you a clearer output.

  • You won't get much performance hit when debug.traceback is not called.

Upvotes: 2

Related Questions