Xcode を使って Ruby C 拡張ライブラリをデバッグ

RMagick のテストが通らないものを調べる際にどのようにデバッグしようかと思ったのですが、Debugging Ruby C Extensions in XCode - Emil Soman's blog に有益なことが書かれておりました。元の記事では Xcode 5.1 が使われていたので最新の Xcode のスクリーンショットを交えつつ手順を書いてみます。

この記事では Ruby C 拡張ライブラリの C 言語で書かれたコードをデバッグする手法を記述します。

デバッグ対象のコードを入手

$ git clone https://github.com/rmagick/rmagick.git

Makefile を作成

Ruby C 拡張ライブラリの C 言語のコードは ext ディレクトリ配下にあります。RMagick では ext/RMagick となります。そこへ移動し Makefile を作成します。デバッグしやすいように CFLAGS を通じて -O0 と -ggdb3 を指定します。

$ cd ext/RMagick
$ CFLAGS="-O0 -ggdb3" LDFLAGS="-L`brew --prefix gmp`/lib" ruby extconf.rb

Xcode から make すると gmp のライブラリが見つからない旨のエラーが出てしまったので、ここでは LDFLAGS の設定も行っています。

Xcode のプロジェクトを作成

Xcode 起動時に表示される Welcome 画面からですと、赤枠で囲んだ部分をクリックして新規プロジェクトを作成します。 f:id:Watson:20190202070015p:plain

Cross-Platform タブに表示される External Build System というテンプレートを選択します。 f:id:Watson:20190202070232p:plain

プロジェクト名などは適当に埋めておきます。 f:id:Watson:20190202070415p:plain

プロジェクトの保存場所はわかりやすい場所にしておくと良いでしょう。

External Build Tool Configuration

make コマンドをどのディレクトリで実行するか指定します。ext/RMagick を指定します。 f:id:Watson:20190202070943p:plain

Ruby の実行ファイルを準備

Xcode で Ruby の実行ファイルを簡単に指定できるように準備します。ターミナルで以下のコマンドを実行します。

$ ln -sf `rbenv which ruby`

これで rbenv でインストールした Ruby の実行ファイルのシンボリックリンクができました。

Ruby の設定

Xcode で Edit Scheme... をクリックします。 f:id:Watson:20190202071712p:plain

Executable のところで Other... を選択し、先ほど準備した Ruby の実行ファイルのシンボリックリンクを選択します。 f:id:Watson:20190202071918p:plain

次に作業ディレクトリの設定を設定します。Options のタブを選択し Working Directory のところを設定します。 f:id:Watson:20190202072453p:plain

次に Arguments のタブに移動し、テスト用の Ruby ファイルを指定します。 f:id:Watson:20190202072709p:plain

デバッガが起動される際に実行される際に渡され、以下の様な感じで展開されます。

lldb -- ruby test.rb

テスト用のファイルには以下の様に、デバッグしたい内容に応じて書いて ext/RMagick に置いておきます。

require './RMagick2.so'

image = Magick::Image.new(20, 20) {
  self.background_color = 'gray50'
}

p image.gray?

ソースコードを追加

Xcode 左下にある + ボタンをクリックし Add Files to.... を選択します。 f:id:Watson:20190202073209p:plain

ext/RMagick 内の C 言語ファイルを追加します。 f:id:Watson:20190202073327p:plain

以上でデバッグの準備が完了です。お疲れ様です。

デバッグ

デバッグしたい箇所に Xcode でブレークポイントを設置し、▶ボタンをクリック (or command + R)で実行します。 f:id:Watson:20190202073523p:plain

あとは Xcode を操作したり、lldb のコマンドを実行したりしながらデバッグを進めて行きます。

まとめ

準備が若干面倒ですが、Xcode のインタフェースでデバッグできるようになって便利です。