*Luaライブラリ 要注意点 ~Debugライブラリ~ [#f8545427] **os.clock [#c80aef1d] >osライブラリのclockはベンチマークデバッグに利用できるので、osライブラリではなく、あえて、こちらに入れておく。 #sh(lua;){{ local x = os.clock() -- この行に来た瞬間のCPU時間 local s = 0 for i=1, 100000 do s = s + i end print(string.format("elapsed time: %.2f\n", os.clock()-x )) }} **debug.getinfo ([thread,] f [, what]) [#rc2199e6] >関数に関する情報を格納したテーブルを返します。~ f の値として、''関数を直接渡すか、 数値を指定''することができます。~ ''数値は指定したスレッドのコールスタック上で実行中のレベル f の関数を表します。~ レベル 0 は現在の関数 (getinfo 自身) で、レベル 1 は getinfo を呼んだ関数で、以下同様です。''~ (スタックにカウントされない末尾呼び出しを除きます)。そうでなければ、date は現在時刻を書式化する。~ format が `!´ で始まっていたら、日付は世界時 (Universal Time) で書式化される。~ このオプション文字の後、 format が *t であれば、 date は以下のフィールドを持つテーブルを返す。~ |プロパティ|意味| |source|関数の定義位置。| |short_source|sourceの短縮形。(今ひとつ存在意義は謎) | |linedefined|該当関数が定義されている行| |lastlinedefined|該当関数が定義されている最後の行| |what|関数の種類。"Lua"か"C"、メインチャンクの場合は"main"&br;debug.getinfo(print).what --> "C"&br;debug.getinfo(myfunc).what -->"Lua"&br;&br;i = loadstring("return 3")&br;debug.getinfo(i).what --> "main"| |name|関数の妥当と思われる名前。数値を指定してgetinfoを呼び出した場合のみ機能する。| |namewhat|nameフィールドの種類。対象の関数が何者か。"global", "local", "method", "field", ""(空文字)| |nups|関数が持つ上位値の数。レベルと関連。| |activelines|関数のアクティブ行の集合のテーブル。デバッガでも作らない限り、まぁあまり気にしなくても良い。| |func|関数自身。これだけだと、一見無意味に思えるが、該当レベルで関数がアクティブだったらここに入るというフィルターがかかっている。| --埋めるフィールドを指定する文字列 what を指定すると、戻り値のテーブルは lua_getinfo が返すフィールドをすべて持ちます。~ --what のデフォルトは、 有効な行番号のテーブルを除くすべての利用可能な情報を取得します。~ オプション 'f' を指定すると、 func という名前のフィールドにその関数自身を格納します。~ オプション 'L' を指定すると、activelines という名前のフィールドに有効な行番号のテーブルを格納します。 --nameフィールドの取り扱いは、わかりにくい。関数には表面上の表記とは異なり、実質的に名前が無かったり、あるいは、関数に複数の名前がつけられていることがある。~ --Luaはその関数を呼び出したコードから、その関数がどのよううにして呼び出されたかを見て、関数名を検索する。そして、その方法は、数値を指定してgetinfoを呼び出した場合のみうまく機能する。~ --例えば、 式 debug.getinfo(1,"n").name は、 現在の関数の名前 (適当な名前が見つかった場合) を格納したテーブルを返します。~ --また式 debug.getinfo(print) は、 print 関数に関するすべての利用可能な情報を格納したテーブルを返します。 **debug.getlocal ([thread,] f, local) [#bb9127c8] >スタック上のレベル f の関数のインデックス local のローカル変数の名前と値を返します。 明示的なローカル変数だけでなく、 引数や一時変数などにもアクセスします。~ ~ 最初の引数またはローカル変数はインデックス 1 で、 最後のアクティブな変数まで以下同様です。~ 負のインデックスを指定すると可変長引数を参照します。~ '-1' は最初の可変長引数です。指定したインデックスの変数が存在しなければ nil を返し、~ 範囲外のレベルを指定した場合はエラーを発生します。 (レベルが有効かどうかを調べるには debug.getinfo を使ってください。)~ ~ '(' (開き括弧) で始まる変数名は内部の変数 (ループ制御変数、 一時変数、 可変長引数、 および C の関数のローカル変数) を表します。~ ~ 引数 f に関数を指定することもできます。この場合は関数の引数の名前だけを返します。~ #sh(lua;){{ function foo (a, b) local x do local c = a-b end local a = 1 while true do local name, value = debug.getlocal(1,a) if not name then break end print(name, value) a = a + 1 end end foo(10, 20) --[[ a 10 b 20 x nil a 4 ]] }} --まず、関数自体の第1パラメータ、第2パラメータはローカルであるから表示される。~ --xもローカルであるから表示される。~ --cはローカルであるが、それを包むdo~endスコープを抜けると消えるので、debug.getlocalを呼んだ段階では、すでに存在せず、表示対象ではない。~ --name, value はdebug.getlocalを呼んだ段階では、まだ生成されていない。~ --初期化付き変数の場合は、初期化コードの終了後でないと可視とはならない。 -debug.getlocalの第3パラメータ >debug.getlocalの''第3パラメータには、該当の変数に設定する値を指定''できる。 #sh(lua;){{ local name, value = debug.getlocal(1, var_index, 5) }} とすると、name, varには元の変数の名前と値が返ってくるが、該当の変数の値自体は5になっている。 -debug.traceback ([thread,] [message [, level]]) --コルーチンのトレース >message が文字列でも nil でもなければ、それ以上の処理をせずに message を返します。~ そうでなければ、 スタックトレースの文字列を返します。~ 省略可能な文字列 message がトレースの先頭に追加されます。~ 省略可能な数値 level はトレースを開始するレベルを指定します。~ (デフォルトは 1 で、 traceback を呼んだ関数です) #sh(lua;){{ co = coroutine.create( function() local x = 10 coroutine.yield() error("some error") end) coroutine.resume(co) print(debug.traceback(co)) --> yieldでとまっている状態でコルーチンをトレース print(coroutine.resume(co)) print(debug.traceback(co)) -- > エラーが発生した後にコルーチンをトレース print(debug.getlocal(co,1,1)) -- > エラーが発生した後でもローカル変数は調査可能 --[[ 実行結果: stack traceback: [C]: in function 'yield' c:\test.lua:3: in function <c:\test.lua:1> false c:\a.lua:4: some error stack traceback: [C]: in function 'error' c:\test.lua:4: in function <c:\test.lua:1> x 10 みたいな形。 ]] }} **debug.sethook (hook, mask [, count]) [#y92cc70e] -指定された関数にフックを設定する。 >指定された関数にフックを設定する。 文字列 mask と数値 count は、いつフックが呼ばれるかを記述する。 文字列 mask には以下の文字を指定する |mask|意味| |c|フックはLuaが関数を呼ぶたびに呼ばれる。| |r|フックはLuaが関数から戻るたびに呼ばれる。| |l|フックはLuaがコードの新しい行に入るたびに呼ばれる。| >count がゼロでなければ、フックは count 命令が実行されるたびに、その直後に呼ばれる。引数なしで呼ぶとフックは無効になる。~ フックが呼ばれたとき、最初の引数はフックを起動したイベントを示す以下のいずれかの文字列である。 "call", "return" "tail return", "line", "count"。~ lineイベントの場合、フックは二番目の引数に新しい行番号が得られる。 ~ "tail return" を除いて、 フックの内部でレベル2の getinfo を呼べば、実行中の関数に関する情報をもっと得られる~ ( レベル0は getinfo 関数自身で、レベル1はフック関数である。)~ ( "tail return" はLuaが復帰をシミュレートしているだけであり、 getinfo は正しくないデータを返す。)~ -フックの使用例 > #sh(lua;){{ function hook_func(p) local fname = debug.getinfo(2).name print("[hook]"..p..":"..fname) end debug.sethook(hook_func, "c") -- call function function hoge1(a,b) return a,b end function hoge2(a,b) return a,b end hoge1(3,5) hoge2(1,2) --[[ 実行結果 [hook]call:hoge1 [hook]call:hoge2 --]] }} -呼び出し回数分析プロファイラの作成 --profiler.luaなど > #sh(lua;){{ local Counters = {} local Names = {} local function hook() local f = debug.getinfo(2, "f").func if Counters[f] == nil then Counters[f] = 1 Names[f] = debug.getinfo(2, "Sn") else Counters[f] = Counters[f] + 1 end end local f = assert(loadfile(arg[1])) debug.sethook(hook, "c") f() debug.sethook() function getname(func) local n = Names[func] if n.what == "C" then return n.name end local lc = string.format("[%s]:%s", n.short_src, n.linedefined) if n.namewhat ~= "" then return string.format("%s (%s)", lc, n.name) else return lc end end for func, count in pairs(Counters) do print(getname(func), count) end }} --別途test.luaなど > #sh(lua;){{ function abc() return 3 end abc() abc() abc() abc() }} #sh(lua;){{ 実行 lua profile.lua test.lua 実行結果 sethook 1 [test.lua]:1 (abc) 4 [test.lua]:0 (f) 1 }}