如何使用标准挂钩在 Lua 中实现断点?
How to implement breakpoints in Lua using the standard hooks?
这个问题是由练习 25.7 on p. 提出的。 Lua(第 4 版)中的 264,更具体地说,提示中提出的优化(我在下面的引用中强调了它):
Exercise 25.7: Write a library for breakpoints. It should offer at least two functions
setbreakpoint(function, line) --> returns handle
removebreakpoint(handle)
We specify a breakpoint by a function and a line inside that function. When the program hits a breakpoint, the library should call debug.debug
. (Hint: for a basic implementation, use a line hook that checks whether it is in a breakpoint; to improve performance, use a call hook to trace program execution and only turn on the line hook when the program is running the target function.)
我不知道如何实现提示中描述的优化。
考虑以下代码(当然,这只是为了这个问题而编造的一个人为示例):
function tweedledum ()
while true do
local ticket = math.random(1000)
if ticket % 5 == 0 then tweedledee() end
if ticket % 17 == 0 then break end
end
end
function tweedledee ()
while true do
local ticket = math.random(1000)
if ticket % 5 == 0 then tweedledum() end
if ticket % 17 == 0 then break end
end
end
function main ()
tweedledum()
end
函数main
应该表示程序的入口点。函数 tweedledum
和 tweedledee
几乎完全相同,只是重复调用对方。
假设我在 tweedledum
的分配行上设置了一个断点。我可以实现一个调用钩子可以检查是否 tweedledum
已被调用,然后设置一个线钩子来检查何时调用所需的线 1.
更有可能的是,tweedledum
会在跳出循环之前调用 tweedledee
。假设发生这种情况。当前开启的line hook可以检测到已经不在tweedledum
,重新安装call hook
此时执行可以通过以下两种方式之一从tweedledee
切换到tweedledum
:
tweedledee
可以调用 tweedledum
(再一次);
tweedledee
可以 return 到它的调用者,恰好是 tweedledum
.
问题是: call hook 可以检测到 (1) 中的事件,但无法检测到 (2) 中的事件。
诚然,这个例子很不自然,但这是我能想到的最简单的说明问题的方法。
我能想到的最好的方法(而且它非常弱!)是在第一次调用[=16]时跟踪堆栈深度N
=],仅当堆栈深度低于 N
时,才让 line hook 重新安装 call hook。因此,只要tweedledee
在堆栈中,line hook就会生效,无论是否正在执行。
是否可以仅使用 Lua 中可用的标准挂钩来实现提示中描述的优化?2
1 我的理解是,通过安装 line hook,调用 hook 本质上是卸载自身。 AFAICT,每个协程只能激活一个挂钩。 如有错误请指正
2即:call,line,return,count hooks.
And here's the problem: the call hook can detect the event in (1) but it cannot detect the event in (2).
这就是你错的地方:有三种可能的挂钩事件:l
用于线路,c
用于呼叫,r
用于 return。
在你的钩子函数中,你可以将 return 和 call 事件视为几乎相同的事件,除了当 return
事件被触发,你仍然在被调用的函数中,所以目标函数在堆栈中高了一个位置。
debug.sethook(function(event, line)
if event == "call" or event == "return" then
if debug.getinfo(event=='call' and 2 or 3).func == target then
debug.sethook(debug.gethook(), 'crl')
else
debug.sethook(debug.gethook(), 'cr')
end
elseif event == 'line' then
-- Check if the line is right and possibly call debug.debug() here
end
end, 'cr')
都在manual;)
请注意,设置挂钩时,您可能需要检查您当前是否在目标函数内;否则你可以跳过一个断点,除非你在到达它之前调用(和 return 从)另一个函数。
这个问题是由练习 25.7 on p. 提出的。 Lua(第 4 版)中的 264,更具体地说,提示中提出的优化(我在下面的引用中强调了它):
Exercise 25.7: Write a library for breakpoints. It should offer at least two functions
setbreakpoint(function, line) --> returns handle
removebreakpoint(handle)
We specify a breakpoint by a function and a line inside that function. When the program hits a breakpoint, the library should call
debug.debug
. (Hint: for a basic implementation, use a line hook that checks whether it is in a breakpoint; to improve performance, use a call hook to trace program execution and only turn on the line hook when the program is running the target function.)
我不知道如何实现提示中描述的优化。
考虑以下代码(当然,这只是为了这个问题而编造的一个人为示例):
function tweedledum ()
while true do
local ticket = math.random(1000)
if ticket % 5 == 0 then tweedledee() end
if ticket % 17 == 0 then break end
end
end
function tweedledee ()
while true do
local ticket = math.random(1000)
if ticket % 5 == 0 then tweedledum() end
if ticket % 17 == 0 then break end
end
end
function main ()
tweedledum()
end
函数main
应该表示程序的入口点。函数 tweedledum
和 tweedledee
几乎完全相同,只是重复调用对方。
假设我在 tweedledum
的分配行上设置了一个断点。我可以实现一个调用钩子可以检查是否 tweedledum
已被调用,然后设置一个线钩子来检查何时调用所需的线 1.
更有可能的是,tweedledum
会在跳出循环之前调用 tweedledee
。假设发生这种情况。当前开启的line hook可以检测到已经不在tweedledum
,重新安装call hook
此时执行可以通过以下两种方式之一从tweedledee
切换到tweedledum
:
tweedledee
可以调用tweedledum
(再一次);tweedledee
可以 return 到它的调用者,恰好是tweedledum
.
问题是: call hook 可以检测到 (1) 中的事件,但无法检测到 (2) 中的事件。
诚然,这个例子很不自然,但这是我能想到的最简单的说明问题的方法。
我能想到的最好的方法(而且它非常弱!)是在第一次调用[=16]时跟踪堆栈深度N
=],仅当堆栈深度低于 N
时,才让 line hook 重新安装 call hook。因此,只要tweedledee
在堆栈中,line hook就会生效,无论是否正在执行。
是否可以仅使用 Lua 中可用的标准挂钩来实现提示中描述的优化?2
1 我的理解是,通过安装 line hook,调用 hook 本质上是卸载自身。 AFAICT,每个协程只能激活一个挂钩。 如有错误请指正
2即:call,line,return,count hooks.
And here's the problem: the call hook can detect the event in (1) but it cannot detect the event in (2).
这就是你错的地方:有三种可能的挂钩事件:l
用于线路,c
用于呼叫,r
用于 return。
在你的钩子函数中,你可以将 return 和 call 事件视为几乎相同的事件,除了当 return
事件被触发,你仍然在被调用的函数中,所以目标函数在堆栈中高了一个位置。
debug.sethook(function(event, line)
if event == "call" or event == "return" then
if debug.getinfo(event=='call' and 2 or 3).func == target then
debug.sethook(debug.gethook(), 'crl')
else
debug.sethook(debug.gethook(), 'cr')
end
elseif event == 'line' then
-- Check if the line is right and possibly call debug.debug() here
end
end, 'cr')
都在manual;)
请注意,设置挂钩时,您可能需要检查您当前是否在目标函数内;否则你可以跳过一个断点,除非你在到达它之前调用(和 return 从)另一个函数。