利用するpcall()のerrfuncパラメータデバッグLUAプログラム
9640 ワード
cococos 2 d-xでは、プロファイル、インタフェース、ネットワークプロトコルなど、luaを使用して多くの上位機能を実現することがよくあります.LUAのコード量が増加すると、デバッグの必要性も増加します.decade,ldtなど、デバッグ可能なIDEツールがネットワークにいくつか登場しています.しかし、私は愚かなので、decadeは私の手で崩壊しやすく、LDTのネットワークデバッグはいつも理想的ではありません.そこで他のデバッグ方法を探し始めました.
cococos 2 d-xの理解が深まるにつれて、cococos 2 dxのサンプルコードに触れました.ここで少し変更するのを簡単に読むために、詳細はHelloLuaプロジェクトを参照してください.
このファイルの意図は非常に簡単で、システムのxpcall関数を通じてmain関数を実行し、同時にエラー処理関数__を転送していることがわかります.G__TRACKBACK__
C++でこのmainを実行するとluaというファイルの場合、次のような出力情報が得られます.
このように、現在のスタック情報を印刷しました.LUAに接触したばかりの人なら、普段の関数でエラーが発生していることを知っています.私たちはエラー情報が1つしかありません.
...2d-x-2.1.5\projects\Flyman\Resources\script/main.lua:12: attempt to index global 'p' (a nil value)
しかし、スタックの状況を知るのは難しい.呼び出し頻度の高い共通関数でエラーが発生した場合、どの呼び出しエラーが発生したのか分かりにくい.デバッグが非常に不便です.
上の発見があった後、好奇心から、私はこの不思議なxpcal関数を研究し始めました.LUAの公式マニュアルをめくってみると、
xpcall(f, err)This function is similar to pcall, except that you can set a new error handler.xpcall calls function f in protected mode, usingerr as the error handler. Any error insidef is not propagated; instead,xpcall catches the error, calls theerr function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In this case,xpcall also returns all results from the call, after this first result. In case of any error,xpcall returns false plus the result fromerr.
xpcallはネストされたluaです.pcallは、エラー処理関数を同時に渡します.関数が実行中にエラーがない場合はtrueを返し、そうでない場合はfalseを返します.
しかし、ニマ問題が来て、関数のパラメータと戻り値はどうしますか.xpcallを呼び出すと、関数は無パラメータと戻り値しかありませんか.xpcallを軽蔑する気持ちを持って、もっと強いxpcall関数を書くことができるかどうか考え始めました.
焦点がint lua_に移ったpcall(lua_State*L,int nargs,int nresults,int errfunc)は、xpcallがそれを用いて実現されるためである.元のコードを表示すると、次のことがわかります.
注意lua_pcallの行、luaを表示します.pcallの関数の原型は知っています.2番目のパラメータ0は、パラメータがないことを示し、3番目のパラメータLUA_MULTRETは複数の戻り値を表し、4番目のパラメータはスタックの1番上にエラー処理関数を表す.1番のビットには私たちが入力したエラー処理関数があります
このような分析を通じて、何が起こっているのか大体分かります.C++でLUA関数を呼び出すと、エラー処理関数が入力され、DEBUGのニーズを満たすことができます.
次にluaについて説明しますpcall()の公式解釈ドキュメント:
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc)
Calls a function in protected mode.
はい、保護モードでlua functionを呼び出します.lua_pcallとlua_callを比べてみると、もっと深く感じられます.私たちは下を見続けました.肝心なところが来ました.
If errfunc is 0, then the error message returned on the stack is exactly the original error message.Otherwise,errfunc is the stack index of anerror handler function. (In the current implementation, this index cannot be a pseudo-index.) In case of runtime errors, this function will be called with the error message and its return value will be the message returned on the stack bylua_pcall.
Typically, the error handler function is used to add more debug information to the error message, such as a stack traceback. Such information cannot be gathered after the return oflua_pcall, since by then the stack has unwound.
The lua_pcall function returns 0 in case of success or one of the following error codes (defined inlua.h):
LUA_ERRRUN: a runtime error. LUA_ERRMEM: memory allocation error. For such errors, Lua does not call the error handler function. LUA_ERRERR: error while running the error handler function.
上の赤い部分に注意して、エラー処理関数のパラメータはこの関数のスタックの中のシーケンス番号を指します!以前、私もずっとこのパラメータをどのように記入すべきか悩んでいました.ニマ、先生が教えてくれませんでした.私が他の人に教えたとき、私は彼に言いました.このパラメータは一般的に使わないので、自分でプライベートで勉強したいと思っています.ここであの学生に申し訳ありません.
では、この番号はいったいどういうことなのでしょうか.LUAスタックトップとスタックボトムのシーケンス番号はいずれも0であり、スタックボトムからスタックトップに向かうと正数であり、スタックトップからスタックボトムに向かうと負の数である.ニマ、これっぽっちの穴はどれだけLUAの学子を倒して、このようなハイエンドをして、うっかりして間違いを犯して、最も肝心なのはコードの中で、1会は正数で、1会は負数で、やった人は仙人が死にたいと思っています.はっきり言って、この番号には2つの意味があります.もし正であれば、スタックの底からスタックの頂上まで回数に従ってください.負であれば、スタックの頂上からスタックの底まで数えてください.
これで簡単です.私たちは自分のxpcall関数を実現します.コードは以下の通りです.
上のコードを実行しましょう.どうですか.xpcallと同じ効果を得たのではないでしょうか.理論的には印刷情報はそっくりです.最も重要なのは、関数パラメータを自由に入力し、戻り値を取得することです.(パラメータと戻り値が入力されない場合は、別のブログで説明します)
成功の興奮を持って冷静にならなければなりませんが、コードのDEBUGの過程で、スタック情報の中でデバッグに使用できる情報は非常に限られています.さらに重要なのは、関数内部の変数の場合です.例えばVS 2010でスタックを得た後、最も重要なのはいくつかの変数のビン値の状況であり、これらがあればBUGを迅速に調べることができます.
デバッグ関数をさらに改善する必要があるようですね、lua_pcallはすでに私たちに搾り取られており、基本的にはより多くの利用価値はありません.では、それを見てみましょう.G__TRACKBACK__()関数、彼はどのようにスタック情報を印刷したのですか?
debug.traceback()は、luaのdebugライブラリを使用しています.スタックを印刷できる以上、ファイル名、関数名、行番号、ローカル変数なども印刷できることを意味しますか?先人がすでにこの方面の仕事をしたことがあるかどうかを見て、度娘に聞いてみましょう.
上記のコードを入手すると、私たちは_G__TRACKBACK__新しいエラー処理関数を指し、main()関数を修正し、関数体の内部に局所変数aを加えて、私が望むように印刷できるかどうかを確認します.
これでmain関数をもう一度実行してみましょう.
aの値も印刷されることがわかります.これにより、デバッグ作業が大幅に便利になります.
実際の作業では、この方法は多くのコンソール情報を占有します.これは彼の大きな弊害です.エラー処理関数を自動的に選択して、普段スタック情報だけを表示して、デバッグ時になってから開くことができます.このスイッチはLUA層の中で作らなければならないことを覚えておいて、C++層でやらないでください.そうしないと、一度変更して、プログラムを再編成しなければなりません.それは苦痛です.
より詳細なDEBUG情報を取得するには、LUAのdebugライブラリを理解する必要があります.そこにはより包括的な情報が与えられると信じています.本文は私自身の個人的な方法で、比較的に簡単で分かりやすくて、基本的に大部分の問題を解決することができます.
cococos 2 d-xの理解が深まるにつれて、cococos 2 dxのサンプルコードに触れました.ここで少し変更するのを簡単に読むために、詳細はHelloLuaプロジェクトを参照してください.
--file: main.lua
function __G__TRACKBACK__(msg)
print("----------------------------------------")
print("LUA ERROR: " .. tostring(msg) .. "
")
print(debug.traceback())
print("----------------------------------------")
end
function main()
CCLuaLog("function main start")
p[2] = 2 --
CCLuaLog("function main finished")
end
xpcall(main, __G__TRACKBACK__)
このファイルの意図は非常に簡単で、システムのxpcall関数を通じてmain関数を実行し、同時にエラー処理関数__を転送していることがわかります.G__TRACKBACK__
C++でこのmainを実行するとluaというファイルの場合、次のような出力情報が得られます.
[LUA-print] ----------------------------------------
[LUA-print] LUA ERROR: ...2d-x-2.1.5\projects\Flyman\Resources\script/main.lua:12: attempt to index global 'p' (a nil value)
[LUA-print] stack traceback:
...2d-x-2.1.5\projects\Flyman\Resources\script/main.lua:6: in function <...2d-x-2.1.5>
...2d-x-2.1.5\projects\Flyman\Resources\script/main.lua:12: in function <...2d-x-2.1.5>
[C]: in function 'xpcall'
...2d-x-2.1.5\projects\Flyman\Resources\script/main.lua:16: in main chunk
[LUA-print] ----------------------------------------
このように、現在のスタック情報を印刷しました.LUAに接触したばかりの人なら、普段の関数でエラーが発生していることを知っています.私たちはエラー情報が1つしかありません.
...2d-x-2.1.5\projects\Flyman\Resources\script/main.lua:12: attempt to index global 'p' (a nil value)
しかし、スタックの状況を知るのは難しい.呼び出し頻度の高い共通関数でエラーが発生した場合、どの呼び出しエラーが発生したのか分かりにくい.デバッグが非常に不便です.
上の発見があった後、好奇心から、私はこの不思議なxpcal関数を研究し始めました.LUAの公式マニュアルをめくってみると、
xpcall(f, err)This function is similar to pcall, except that you can set a new error handler.xpcall calls function f in protected mode, usingerr as the error handler. Any error insidef is not propagated; instead,xpcall catches the error, calls theerr function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In this case,xpcall also returns all results from the call, after this first result. In case of any error,xpcall returns false plus the result fromerr.
xpcallはネストされたluaです.pcallは、エラー処理関数を同時に渡します.関数が実行中にエラーがない場合はtrueを返し、そうでない場合はfalseを返します.
しかし、ニマ問題が来て、関数のパラメータと戻り値はどうしますか.xpcallを呼び出すと、関数は無パラメータと戻り値しかありませんか.xpcallを軽蔑する気持ちを持って、もっと強いxpcall関数を書くことができるかどうか考え始めました.
焦点がint lua_に移ったpcall(lua_State*L,int nargs,int nresults,int errfunc)は、xpcallがそれを用いて実現されるためである.元のコードを表示すると、次のことがわかります.
static int luaB_xpcall (lua_State *L) {
int status;
luaL_checkany(L, 2);
lua_settop(L, 2);
lua_insert(L, 1); /* put error function under function to be called */
status = lua_pcall(L, 0, LUA_MULTRET, 1);
lua_pushboolean(L, (status == 0));
lua_replace(L, 1);
return lua_gettop(L); /* return status + all results */
}
注意lua_pcallの行、luaを表示します.pcallの関数の原型は知っています.2番目のパラメータ0は、パラメータがないことを示し、3番目のパラメータLUA_MULTRETは複数の戻り値を表し、4番目のパラメータはスタックの1番上にエラー処理関数を表す.1番のビットには私たちが入力したエラー処理関数があります
このような分析を通じて、何が起こっているのか大体分かります.C++でLUA関数を呼び出すと、エラー処理関数が入力され、DEBUGのニーズを満たすことができます.
次にluaについて説明しますpcall()の公式解釈ドキュメント:
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc)
Calls a function in protected mode.
はい、保護モードでlua functionを呼び出します.lua_pcallとlua_callを比べてみると、もっと深く感じられます.私たちは下を見続けました.肝心なところが来ました.
If errfunc is 0, then the error message returned on the stack is exactly the original error message.Otherwise,errfunc is the stack index of anerror handler function. (In the current implementation, this index cannot be a pseudo-index.) In case of runtime errors, this function will be called with the error message and its return value will be the message returned on the stack bylua_pcall.
Typically, the error handler function is used to add more debug information to the error message, such as a stack traceback. Such information cannot be gathered after the return oflua_pcall, since by then the stack has unwound.
The lua_pcall function returns 0 in case of success or one of the following error codes (defined inlua.h):
LUA_ERRRUN: a runtime error. LUA_ERRMEM: memory allocation error. For such errors, Lua does not call the error handler function. LUA_ERRERR: error while running the error handler function.
上の赤い部分に注意して、エラー処理関数のパラメータはこの関数のスタックの中のシーケンス番号を指します!以前、私もずっとこのパラメータをどのように記入すべきか悩んでいました.ニマ、先生が教えてくれませんでした.私が他の人に教えたとき、私は彼に言いました.このパラメータは一般的に使わないので、自分でプライベートで勉強したいと思っています.ここであの学生に申し訳ありません.
では、この番号はいったいどういうことなのでしょうか.LUAスタックトップとスタックボトムのシーケンス番号はいずれも0であり、スタックボトムからスタックトップに向かうと正数であり、スタックトップからスタックボトムに向かうと負の数である.ニマ、これっぽっちの穴はどれだけLUAの学子を倒して、このようなハイエンドをして、うっかりして間違いを犯して、最も肝心なのはコードの中で、1会は正数で、1会は負数で、やった人は仙人が死にたいと思っています.はっきり言って、この番号には2つの意味があります.もし正であれば、スタックの底からスタックの頂上まで回数に従ってください.負であれば、スタックの頂上からスタックの底まで数えてください.
これで簡単です.私たちは自分のxpcall関数を実現します.コードは以下の通りです.
int top = lua_gettop(m_state);
lua_getglobal(m_state, "__G__TRACKBACK__");// ,
if (lua_type(m_state, -1) != LUA_TFUNCTION) // , , !
{
CCLog("[LUA ERROR] can't find function <__g__trackback__>err");
lua_settop(m_state, top);
return;
}
int errfunc = lua_gettop(m_state); // , , gettop
lua_getglobal(m_state, "main"); //
if (lua_type(m_state, -1) != LUA_TFUNCTION)
{
CCLog("[LUA ERROR] can't find function err:%s", lua_tostring(m_state, -1));
lua_settop(m_state, top);
return;
}
if (lua_pcall(m_state, 0, 0, errfunc) != 0) // main, 0, 0,errfunc
{
lua_settop(m_state, top);
return;
}
lua_settop(m_state, top);
上のコードを実行しましょう.どうですか.xpcallと同じ効果を得たのではないでしょうか.理論的には印刷情報はそっくりです.最も重要なのは、関数パラメータを自由に入力し、戻り値を取得することです.(パラメータと戻り値が入力されない場合は、別のブログで説明します)
成功の興奮を持って冷静にならなければなりませんが、コードのDEBUGの過程で、スタック情報の中でデバッグに使用できる情報は非常に限られています.さらに重要なのは、関数内部の変数の場合です.例えばVS 2010でスタックを得た後、最も重要なのはいくつかの変数のビン値の状況であり、これらがあればBUGを迅速に調べることができます.
デバッグ関数をさらに改善する必要があるようですね、lua_pcallはすでに私たちに搾り取られており、基本的にはより多くの利用価値はありません.では、それを見てみましょう.G__TRACKBACK__()関数、彼はどのようにスタック情報を印刷したのですか?
debug.traceback()は、luaのdebugライブラリを使用しています.スタックを印刷できる以上、ファイル名、関数名、行番号、ローカル変数なども印刷できることを意味しますか?先人がすでにこの方面の仕事をしたことがあるかどうかを見て、度娘に聞いてみましょう.
local function tostringex(v, len)
if len == nil then len = 0 end
local pre = string.rep('\t', len)
local ret = ""
if type(v) == "table" then
if len > 5 then return "\t{ ... }" end
local t = ""
for k, v1 in pairs(v) do
t = t .. "
\t" .. pre .. tostring(k) .. ":"
t = t .. tostringex(v1, len + 1)
end
if t == "" then
ret = ret .. pre .. "{ }\t(" .. tostring(v) .. ")"
else
if len > 0 then
ret = ret .. "\t(" .. tostring(v) .. ")
"
end
ret = ret .. pre .. "{" .. t .. "
" .. pre .. "}"
end
else
ret = ret .. pre .. tostring(v) .. "\t(" .. type(v) .. ")"
end
return ret
end
local function tracebackex(msg)
local ret = ""
local level = 2
ret = ret .. "stack traceback:
"
while true do
--get stack info
local info = debug.getinfo(level, "Sln")
if not info then break end
if info.what == "C" then -- C function
ret = ret .. tostring(level) .. "\tC function
"
else -- Lua function
ret = ret .. string.format("\t[%s]:%d in function `%s`
", info.short_src, info.currentline, info.name or "")
end
--get local vars
local i = 1
while true do
local name, value = debug.getlocal(level, i)
if not name then break end
ret = ret .. "\t\t" .. name .. " =\t" .. tostringex(value, 3) .. "
"
i = i + 1
end
level = level + 1
end
return ret
end
local function tracebackAndVarieble(msg)
print(tracebackex())
end
__G__TRACKBACK__ = tracebackAndVarieble
function main()
CCLuaLog("function main start")
local a = 1
p[2] = 2 --
CCLuaLog("function main finished")
end
上記のコードを入手すると、私たちは_G__TRACKBACK__新しいエラー処理関数を指し、main()関数を修正し、関数体の内部に局所変数aを加えて、私が望むように印刷できるかどうかを確認します.
これでmain関数をもう一度実行してみましょう.
[LUA-print] stack traceback:
[[string "script/DebugTool.lua"]]:57 in function ``
msg = ...2d-x-2.1.5\projects\Flyman\Resources\script/main.lua:8: attempt to index global 'p' (a nil value) (string)
(*temporary) = function: 0512A6D8 (function)
[...2d-x-2.1.5\projects\Flyman\Resources\script/main.lua]:8 in function ``
a = 1 (number)
(*temporary) = nil (nil)
(*temporary) = nil (nil)
(*temporary) = attempt to index global 'p' (a nil value) (string)
aの値も印刷されることがわかります.これにより、デバッグ作業が大幅に便利になります.
実際の作業では、この方法は多くのコンソール情報を占有します.これは彼の大きな弊害です.エラー処理関数を自動的に選択して、普段スタック情報だけを表示して、デバッグ時になってから開くことができます.このスイッチはLUA層の中で作らなければならないことを覚えておいて、C++層でやらないでください.そうしないと、一度変更して、プログラムを再編成しなければなりません.それは苦痛です.
より詳細なDEBUG情報を取得するには、LUAのdebugライブラリを理解する必要があります.そこにはより包括的な情報が与えられると信じています.本文は私自身の個人的な方法で、比較的に簡単で分かりやすくて、基本的に大部分の問題を解決することができます.