この質問は、p。の演習25.7によって動機付けられています。Luaでのプログラミングの264 (第4版)、より具体的には、ヒントで提案された最適化(以下の引用で強調しました):
演習25.7:ブレークポイント用のライブラリを作成します。少なくとも2つの機能を提供する必要があります
setbreakpoint(function, line) --> returns handle
removebreakpoint(handle)
関数とその関数内の行によってブレークポイントを指定します。プログラムがブレークポイントに到達すると、ライブラリはを呼び出す必要があります
debug.debug
。(ヒント:基本的な実装では、ブレークポイントにあるかどうかをチェックするラインフックを使用します。パフォーマンスを向上させるには、呼び出しフックを使用してプログラムの実行をトレースし、プログラムがターゲット関数を実行しているときにのみラインフックをオンにします。)
ヒントで説明されている最適化を実装する方法がわかりません。
次のコードについて考えてみます(もちろん、これはこの質問のためだけに作成された人工的な例です)。
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
ます。これが発生するとします。現在有効になっているラインフックは、それがもう存在しないことを検出しtweedledum
、コールフックを再インストールできます。
この時点で、実行は次の2つの方法のいずれかでからtweedledee
に切り替えることができtweedledum
ます。
tweedledee
呼び出すことができますtweedledum
(まだ);tweedledee
呼び出し元に戻ることができますtweedledum
。これはたまたまです。そして、ここに問題があります。コールフックは(1)のイベントを検出できますが、(2)のイベントを検出できません。
確かに、この例は非常に人工的なものですが、問題を説明するために私が思いつくことができる最も簡単な方法です。
私が考えることができる最善のアプローチ(そしてそれは非常に弱いです!)はN
、の最初の呼び出しでスタックの深さを追跡し、スタックの深tweedledum
さが下に沈んだときにのみラインフックにコールフックを再インストールさせることN
です。したがって、ラインフックtweedledee
は、実行されているかどうかに関係なく、スタック内にある限り有効になります。
Luaで利用可能な標準フックのみを使用して、ヒントで説明されている最適化を実装することは可能ですか?2
1私の理解では、ラインフックをインストールすることにより、コールフックは本質的にそれ自体をアンインストールします。AFAICT、コルーチンごとにアクティブにできるフックは1つだけです。私が間違っている場合は私を訂正してください。
2つまり、フックの呼び出し、回線、戻り、およびカウント。
そして、ここに問題があります。コールフックは(1)のイベントを検出できますが、(2)のイベントを検出できません。
そして、それはあなたが間違っているところです:3つの可能なフックイベントがあります:l
ライン、c
コール、そしてr
リターンのためです。
フック関数内では、returnイベントとcallイベントをほぼ同じように扱うことができますが、return
イベントが発生した場合でも、呼び出された関数内にいるため、ターゲット関数はスタックの1つ上の位置にあります。
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')
フックを設定するときに、現在ターゲット関数内にいるかどうかを確認する必要がある場合があることに注意してください。そうしないと、ブレークポイントに到達する前に別の関数を呼び出したり、関数から戻ったりしない限り、ブレークポイントをスキップできます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加