2014この記事はQiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。私は元々JavaScriptを書くことが多かったのですが,最近はよくLuaのコードを書いています。JavaScriptと比べて,私はLuaの方が好きです。metatableやコルーチンは魅力的だし,後述するdebugライブラリも強力です。実際の開発を行うとなると,言語仕様や標準ライブラリだけではなくその周辺のツールも重要になってきます。Toolを使っているでしょう。Toolを使えば,強力なJavaScriptのデバッガやプロファイラを使うことができます。Extentionを使えばさらに,通信の内容をデバッグ用に変更したりと,さまざまなことができます。Luaではどうでしょう?一応,ちらほらデバッグツールを見かけますが,環境に依存していたり,機能が貧弱だったりします。Luaの場合,通常,単体で使うのではなくホスト環境があり,一部の機能をスクリプティングするために仕様することが多いでしょう。そのため,環境に依存したり,特定のライブラリに依存していると非常に使いづらいかと思います。そこで,夏休みにピュアLuaでLUPEという名前のLuaのデバッガを作りました。先日公開することができたため,この記事ではLUPEの機能と仕組みを説明したいと思います。LUPEの機能LUPEは以下のリポジトリで公開しています。https://github.com/tenntenn/lupeLUPEが提供する主な機能は以下のとおりです。簡易プロファイラコルーチン上でのデバッグLUPEはすべてLuaで書かれており,標準ライブラリのみを使用しています。そのため,ほとんどホスト環境に影響されずに,標準入出力が使える環境であれば使用できます。標準入出力が使えない場合も入出力方法を変えることで,デバッグすることは可能です。細かい使い方はREADMEを読んでもらうとして,ここでは基本的な使い方を説明します。lupe.luaをいずれかの方法で読み込みます。通常はrequireを使うと思いますが,ホスト環境が用意する関数にluaファイルを読み込む機能があればそれを使用することも可能です。lupe.luaを読み込むと,Lupeというオブジェクトがグローバル変数として定義されるので,そのオブジェクトstartというメソッドを呼びましょう。startメソッドは後述するdebug.sethookを使って,処理の途中で止まれるようにフックを仕込みます。require('lupe')Lupe:start()つぎに,処理を止めたい箇所でLupe()を呼び出します。ここで止まるendLupe()は,実行した行で処理を中断し,デバッグコマンドを受け付けるプロンプトを表示します。LUPE>使用できるデバッグコマンドは以下のとおりです。チャンクの評価それでは,以下のコードをデバッガで追ってみましょう。print(c)endhoge()luaコマンドで実行してみると,9行目で処理が止まります。fuga()13行目にブレークポイントを仕掛けてみます。200そして,中断した処理を継続させます。16:1ステップ進めて,変数の一覧を表示してみます。200変数に代入してみましょう。LUPE>c=300LUPE>run300cの中身がうまく変わっていますね。debugライブラリLuaにはdebugライブラリという強力なデバッグ用の強力な標準ライブラリが存在します。このライブラリを使えば,行や関数呼び出しごとにフック関数を仕掛けたり,現在の行で宣言されているローカル変数等を取得することができます。LUPEではその機能を使って,デバッガの機能を提供しています。LUPEで使用しているdebugライブラリの機能について簡単に説明します。debug.sethookdebug.sethookは,行,関数の呼び出し,関数からリターンごとに呼び出されるフックを仕掛けることができます。LUPEでは,この機能を使ってデバッグ用のコールスタックに,関数呼び出しごとのコンテキストを記録しています。変数の状態などは,行ごとに更新され,関数からリターンされるとその情報を破棄します。ブレークポイントは行ごとに,その行にブレークポイントがあるかどうか判断して,ブレークポイントがあればその行で処理を止めます。ステップ実行は,ステップ実行を行った時のコンテキストを記録しておき,行ごとにその行で止まるべきか判断させます。ステップアウトの場合は,ステップアウトを実行した場所より,上の階層になった場合に止まるようにコールスタックの状態を見て判断しています。debug.setlocaldebug.getlocalを使うと,その行から相対的に指定した階層のローカル変数を取得できます。debug.setlocalを使うとその逆ができます。この2つの関数を使って,ローカル変数の一覧を取得し,チャンクの評価で変数の内容が変わったときに変更させています。同様にクロージャで,上位値の取得・変更をする場合は,debug.getupvalueとdebug.setupvalueを使います。チャンクの評価Lua5.2ではloadに文字列を渡すとそれをLuaのチャンクとして評価します。環境を指定しない場合は,グローバル環境_ENVと同様の環境で評価されます。ローカル変数を反映させるには,自分で環境を作り,引数に渡します。Lua5.1ではloadはファイルを読み込むことしかできないので,代わりにloadstringを使います。loadstringは環境を指定することができないので,debug.setfenvを使って,事前に環境を設定する必要があります。collectgarbagecollectgarbageはdebugライブラリではありませんが,非常に強力です。引数に"stop"を指定するとGCの動作を止めることができ,ゲームのコアなパートや重要な演出を表示しているときなど,GCを走らせたくないときに使えます。引数に"count"を指定すると,現在Luaの処理系で使用しているメモリの使用量(キロバイト)が取得できます。LUPEでは,この機能を使って関数のメモリの使用量を計測する簡易プロファイラを実装しています。まとめこの記事では私が開発しているLUPEの紹介とそこで使われているLuaの強力なdebugライブラリの紹介を行いました。LUPEは現在開発中で,おそらくバグもいっぱいあると思います。ここまで機能を提供するピュアLuaで書かれたデバッガは他に無いかと思います。デバッガなので,開発の現場で使っても誰にも迷惑を書けることはありません。そのため,誰かの開発の役に立てればと思います。使ったよって報告がもらえるとテンションあがるので,お願いします。公開はされていませんが,LUPEのWebフロントエンドを作っています。完成すれば,マウス使ってデバッグができるようになると思います。CoderPortfolio.。