4.7 デバッグインターフェース
Lua には組み込みのデバッグ機能がありません。その代わりに関数とフック (hook) を使った特別なインターフェースが用意されています。このインターフェースを使うと、インタープリタの内部情報を必要とするデバッガやプロファイラといったツールを好きなように作成できます。
lua_Debug
typedef struct lua_Debug {
int event;
const char *name; /* (n) */
const char *namewhat; /* (n) */
const char *what; /* (S) */
const char *source; /* (S) */
size_t srclen; /* (S) */
int currentline; /* (l) */
int linedefined; /* (S) */
int lastlinedefined; /* (S) */
unsigned char nups; /* (u) number of upvalues */
unsigned char nparams; /* (u) number of parameters */
char isvararg; /* (u) */
char istailcall; /* (t) */
unsigned short ftransfer; /* (r) index of first value transferred */
unsigned short ntransfer; /* (r) number of transferred values */
char short_src[LUA_IDSIZE]; /* (S) */
/* private part */
other fields
} lua_Debug;
関数およびアクティベーションレコード1に関する様々な情報を保存するのに使われるデータ構造です。lua_getstack はこのデータ構造のプライベートな部分だけを埋め、その部分が後で使われます。他のフィールドを埋めるには lua_getinfo を呼びます。
lua_Debug のフィールドは次の意味を持ちます:
source: 関数を作成したチャンクのソースです。sourceが"@"で始まる場合、 以降の文字列が関数を定義したファイルの名前を表します。sourceが"="で始まる場合、以降の文字列がユーザー依存のフォーマットでソースを説明します2。これ以外のときは関数を定義する文字列がsourceとなります。srclen: 文字列sourceの長さです。short_src: 「出力可能な」バージョンのsourceです。エラーメッセージでの利用が想定されています。linedefined: 関数の定義が始まる行の行番号です。lastlinedefined: 関数の定義が終わる行の行番号です。what: 関数が Lua の関数なら"Lua"であり、C の関数なら"C"であり、チャンクのメインなら"main"です。currentline: 関数が現在実行している行の行番号です。行に関する情報が利用できないときは -1 になります。name: 関数を識別する名前です。Lua における関数はファーストクラスの値なので、固定された名前を持ちません。関数が複数のグローバル変数の値になることもあれば、テーブルのフィールドとしてのみ保存されることもあります。lua_getinfoは関数がどのように呼ばれたかを確認し、適切な名前を見つけます。名前が見つからないときnameはNULLとなります。namewhat:nameフィールドを説明します。関数の呼ばれ方に応じて、namewhatの値は"global","local","method","field","upvalue",""(空文字列) のいずれかとなります。空文字列は他のどれにも当てはまらない場合に使われます。istailcall: 関数が末尾呼び出しのとき true になります。この値が true のとき、現在のレベルを呼び出した関数のフレームはスタックに存在しません。nups: 関数のアップバリューの数です。nparams: 関数のパラメータの数です (C 関数では常に 0 となります)。isvararg: 関数が可変長引数関数なら true となります (C 関数では常に true です)。-
ftransfer: 「移動」される値のスタックにおける最初のインデックスです。移動される値とは呼び出しにおけるパラメータあるいはreturnにおける返り値を意味します。移動される値が複数ある場合、他の値はftransferから連続するインデックスにあります。このインデックスとlua_getlocalやlua_setlocalを使えば、移動される値に対するアクセスや改変が可能になります。このフィールドが意味を持つのは呼び出しフック (ftransferが一つ目のパラメータとなる) とリターンフック (ftransferが一つ目の返り値となる) でだけです。 ntransfer: 移動されている値の数です。Lua 関数の呼び出しでは、この値は常にnparamsと等しくなります。
lua_gethook
lua_Hook lua_gethook (lua_State *L);
現在のフック関数を返します。
lua_gethookcount
int lua_gethookcount (lua_State *L);
現在のフックカウントを返します。
lua_gethookmask
int lua_gethookmask (lua_State *L);
現在のフックマスクを返します。
lua_getinfo
int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
特定の関数あるいは関数呼び出しに関する情報を取得します。
関数呼び出しに関する情報を入手するには、パラメータ ar を有効なアクティベーションレコードとして呼び出します。有効なアクティベーションレコードを手に入れるには lua_getstack を呼び出すか、フックの引数として与えられる値を使います (参照: lua_Hook)。
関数に関する情報を入手するには、関数をスタックにプッシュした上で what を ">" で始まる文字列とします。このとき lua_getinfo によってスタックから値が一つポップされます。例えば関数 f が定義された行を知りたい場合には次のように書けます:
lua_Debug ar;
lua_getglobal(L, "f"); /* グローバル変数 'f' をスタックに積む */
lua_getinfo(L, ">S", &ar);
printf("%d\n", ar.linedefined);
文字列 what に含まれる文字が ar のどのフィールドを埋めるか、そしてスタックにどの値をプッシュするかを選択します:
'n': フィールドname,namewhatを埋めます。'S': フィールドsource,short_src,linedefined,lastlinedefined,whatを埋めます。'l': フィールドcurrentlineを埋めます。't': フィールドistailcallを埋めます。'u': フィールドnups,nparams,isvarargを埋めます。'f': 指定されたレベルで実行されている関数をスタックにプッシュします3。-
'L': 関数に含まれる有効な行の行番号をインデックスとするテーブルをスタックにプッシュします。「有効な」行とは、対応するコードが存在する行、つまりブレークポイントを配置できる行を意味します。例えば空行やコメントは有効な行ではありません。このオプションが
'f'と共に与えられた場合には、テーブルは関数の後にプッシュされます。これはメモリエラーが起こる可能性のある唯一のオプションです。
what に無効なオプションが含まれると、この関数は 0 を返します。ただしその後に続く有効なオプションは通常通り処理されます。
lua_getlocal
const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
与えられたアクティベーションレコードあるいは関数の、ローカル変数および一時的な値に関する情報を取得します。
アクティベーションレコードを使う場合には、lua_getstack の返り値あるいはフックの引数として取得された有効なアクティベーションレコードを ar として呼び出します。調べるローカル変数はインデックス n で指定します。変数のインデックスと名前については debug.getlocal を参照してください。
lua_getlocal は変数の値をスタックにプッシュし、名前を返します。
関数を使う場合には、ar を NULL とした上で調べる関数をスタックトップに配置しておく必要があります。この場合に見えるのは Lua 関数のパラメータだけであり、値はスタックにプッシュされません (アクティブな変数についての情報が存在しないからです)。
インデックス n がアクティブなローカル変数の数より大きい場合には何もプッシュせずに NULL を返します。
lua_getstack
int lua_getstack (lua_State *L, int level, lua_Debug *ar);
インタープリタランタイムのスタックに関する情報を取得します。
この関数は指定されたレベルで実行される関数のアクティベーションレコードを特定するための情報に関する lua_Debug のフィールドを埋めます。現在実行している関数がレベル 0 で、レベル n を呼んだ関数がレベル n + 1 となります (ただし末尾呼び出しはスタックを使わないので、途中に末尾呼び出しがあるとこの規則は成り立ちません)。スタックの深さよりも大きいレベルを指定すると、lua_getstack は 0 を返します。それ以外のときは 1 を返します。
lua_getupvalue
const char *lua_getupvalue (lua_State *L, int funcindex, int n);
スタックのインデックス funcindex にあるクロージャが持つ n 個目のアップバリューに関する情報を取得します。アップバリューの値をスタックにプッシュし、その名前を返します。n がアップバリューの個数よりも大きい場合には NULL を返します (そして何もプッシュしません)。
アップバリューについて詳しくは debug.getupvalue を参照してください。
lua_Hook
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
デバッグ用フック関数の型です。
フックが呼ばれるとき、引数 ar の event フィールドはフックを引き起こしたイベントに設定されます。Lua はイベントを表すのに次の定数を使います:
LUA_HOOKCALLLUA_HOOKRETLUA_HOOKTAILCALLLUA_HOOKLINELUA_HOOKCOUNT
ラインイベントでは currentline フィールドも設定されます。これら以外の ar のフィールドを取得するには、フックから lua_getinfo を呼び出す必要があります。
コールイベントでは、通常の呼び出しを表す LUA_HOOKCALL もしくは末尾呼び出しを表す LUA_HOOKTAILCALL のいずれが event となります。末尾呼び出しに対応するリターンイベントは起こりません。
Lua がフックを実行している間、他のフックの呼び出しは無効化されます。そのためフックから Lua の関数やチャンクをさらに呼ぶと、その実行はフックを呼び出さずに行われます。
フック関数は継続を持つことができません。つまり lua_yieldk, lua_pcallk, lua_callk を NULL でない k を指定して呼ぶことはできません。
フック関数は次の条件が満たされるときに限って yield できます:
- カウントイベントとラインイベントだけが yield できる。
- フック関数は
lua_yieldの呼び出しで実行を終えなければならず、そのときnresultsは 0 でなければならない (つまり値を返してはならない)。
lua_setcstacklimit
int (lua_setcstacklimit) (lua_State *L, unsigned int limit);
C スタックの上限を設定します。この上限は Lua においてネストされた呼び出しがどれだけ深くなれるかを制御するので、値を大きくすればスタックオーバーフローを回避できます。成功すれば古い上限を返し、エラーがあれば 0 を返します。この関数について詳しくは標準ライブラリの同じ関数 debug.setcstacklimit を参照してください。
lua_sethook
void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);
デバッグ用フック関数を設定します。
引数 f がフック関数を、mask がフックを呼び出すイベントを指定します。mask は次の定数をビットごとの OR で組み合わせた値です:
LUA_MASKCALLLUA_MASKRETLUA_MASKLINELUA_MASKCOUNT
引数 count は mask が LUA_MASKCOUNT のときに限って意味を持ちます。どのフックがどのイベントで呼ばれるのかを示します:
- 呼び出しフック (call hook): インタープリタが関数を呼び出すときに呼ばれます。このフックが呼ばれるのは Lua が新しい関数に入った直後、関数が引数を受け取る前です。
- リターンフック (return hook): インタープリタが関数から返るときに呼ばれます。このフックが呼ばれるのは Lua が関数を離れる直前です。
- ラインフック (line hook): インタープリタが新しい行のコードを開始するとき、および後ろ向きにジャンプするときに呼ばれます (後者ではジャンプ先が同じ行でも構いません)。このフックが呼ばれるのは Lua の関数を実行しているときだけです。
- カウントフック (count hook): インタープリタが
count個の命令を実行するたびに呼ばれます。このフックは Lua の関数を実行しているときに限って呼ばれます。
mask を 0 にすればフックが無効化されます。
lua_setlocal
const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
指定されたアクティベーションレコードのローカル変数の値を設定します。スタックトップにある値をローカル変数に代入し、その名前を返します。スタックトップにある値はポップされます。
インデックスがアクティブなローカル変数の数よりも大きいときは NULL を返します (そして何もポップしません)。
パラメータ ar と n の意味は lua_getlocal と同様です。
lua_setupvalue
const char *lua_setupvalue (lua_State *L, int funcindex, int n);
クロージャのアップバリューの値を設定します。スタックトップにある値をアップバリューに代入し、その名前を返します。スタックトップにある値はポップされます。
インデックスがアップバリューの数よりも大きいときは NULL を返します (そして何もポップしません)。
パラメータ funcindex と n の意味は lua_getupvalue と同様です。
lua_upvalueid
void *lua_upvalueid (lua_State *L, int funcindex, int n);
クロージャが持つ n 番目のアップバリューのユニークな識別子を返します。
ユニークな識別子を使うと、異なるクロージャがアップバリューを共有しているかどうかを確認できます。複数の Lua のクロージャがアップバリューを共有する (同じ外部ローカル変数にアクセスできる) とき、そのアップバリューを指すインデックスは同じ識別子を返します。
パラメータ funcindex と n の意味は lua_getupvalue と同様です。ただし n はアップバリューの数より大きくてはいけません。
lua_upvaluejoin
void lua_upvaluejoin (lua_State *L,
int funcindex1, int n1,
int funcindex2, int n2);
インデックス funcindex1 のクロージャが持つ n1 番目のアップバリューの参照先を、インデックス funcindex2 のクロージャが持つ n2 番目のアップバリューとします。
-
訳注: アクティベーションレコードはスタックフレームの別名。C のスタックフレームと区別するためにこう呼んでいるのだと思われる。[return]
-
訳注: 例えば C で定義された関数では
sourceが"=[C]"となる。[return] -
訳注: レベルは
lua_getstackの引数で指定する。関数をlua_getinfoに直接渡した場合にはその関数がスタックにプッシュされる。[return]