2017年 Perl5 との戦いに生き残るための最高の開発環境を手に入れる - リモートデバッグ 編

##  はじめに

[前回](http://qiita.com/AnnPin/items/5f868f0c7cb5d1af306b) の記事の続きとなります。前回構築した開発環境を元にして、今度はリモートデバッグができる環境をつくります。


##  この記事を読むとできるようになること

この記事を最後まで読むと、こんな事ができるようになります。

![00_完成図.gif](https://qiita-image-store.s3.amazonaws.com/0/96400/3e5b32e2-6436-72f4-cd28-e61f987c198a.gif)


つまり、**リモートサーバ上で実行されている Perl5 スクリプトが実行された際に、IDE 上でデバッガを利用して変数の状態を可視化しながらデバッグが行えるようになります。** Perl5 で書かれた CGI のデバッグというと `print` デバッグ頼みになりがちかと思いますが、この記事で解説する環境構築を行うことで一気に Perl5 でもモダンな開発ができるようになります。


##  準備

[前回](http://qiita.com/AnnPin/items/5f868f0c7cb5d1af306b) の記事で、IntelliJ IDEA Community Edition を用いた Perl5 (以下、Perl) の開発環境の構築について説明しました。まだ IntelliJ IDEA Community Edition での Perl 開発環境を構築されていない場合は、先に [前回の記事](http://qiita.com/AnnPin/items/5f868f0c7cb5d1af306b) を御覧ください。
また、WebStorm などの IntelliJ IDEA Community Edition 以外の JetBrains 社製 IDE を使われる方で、まだ環境構築をされていない方は [こちら](http://qiita.com/AnnPin/items/01a500515ce50fd7bfe5) の記事の内容も併せてお読みいただくと良いかもしれません。

JetBrains 社で公開されている IDE は基本的に有償版でなければリモートデバッグ機能は使えない模様ですが、この記事で解説する Perl のリモートデバッグについては **無償版の Community Edition でも利用できます。** したがって、Perl のリモートデバッグは完全に無償で行うことができます。

この記事では前回同様、IntelliJ IDEA Community Edition 2016.3.3 (以下、IntelliJ IDEA) を用いて解説していきます。

また、この記事ではテスト用のリモート環境として Vagrant で VirtualBox 上に Ubuntu14.04 (Trusty) の VM を立て、その上でリモート環境用の Docker コンテナを利用するという方法を取っています。Vagrant だけを使う場合、Docker だけを使う場合、あるいは別の物理マシン上で Perl を動作させる場合のいずれであっても、読み替えは難しくないかと思います。

この記事で使用する VM 環境を作るためのスクリプトは GitHub のリポジトリ <https://github.com/AnnPin/idea-perl5-remote-debug-sample> として公開しておりますので、自分で試してみたいという方はご自由にダウンロードするなり clone を取るなりして活用してください。

なお、前回と同様に **Perl6 については恐らく対応していないかと思いますので、Perl5 限定の開発環境となるかと思います。** ご了承下さい。


##  リモート環境を構築するためのサンプルスクリプトを取得する

まずは、リモート環境用の VM を作成するためのスクリプトを取得します。GitHub から clone を取得し、リポジトリ内に存在している `dev` ディレクトリをデスクトップに配置します。
事前に [VirtualBox](https://www.virtualbox.org/) と [Vagrant](https://www.vagrantup.com/) をインストールしておいて下さい。テスト環境は macOS Yosemite 10.10.5 であり、`VirtualBox 5.1.14` と `Vagrant 1.9.1` で動作確認を行っています。

```bash
# GitHub からサンプルスクリプトを取得.
$ git clone git@github.com:AnnPin/idea-perl5-remote-debug-sample.git

# 取得したリポジトリ内へ移動.
$ cd idea-perl5-remote-debug-sample

# dev ディレクトリをデスクトップへ移動.
$ mv dev ~/Desktop

# デスクトップ上の dev ディレクトリへ移動.
$ cd ~/Desktop/dev
```


##  IntelliJ IDEA でプロジェクトを作成する

リモートデバッグを始める前に、まずはローカル環境で動作する Perl プロジェクトを IntelliJ IDEA で作成しましょう。IntelliJ IDEA を起動し、以下のウィンドウで `Create New Project` を作成します。

![01_セットアップ001.png](https://qiita-image-store.s3.amazonaws.com/0/96400/35740409-8cf4-724b-e50c-5f6022f7f0b3.png)


左側のメニューから `Perl5 module` を選択して `Next` をクリックします。

![01_セットアップ002.png](https://qiita-image-store.s3.amazonaws.com/0/96400/183760b1-1a1f-73f7-989e-e06f85a6a511.png)


IntelliJ IDEA で使用する Perl5 インタプリタを選択します。[前回](http://qiita.com/AnnPin/items/5f868f0c7cb5d1af306b) までの内容を行った後ならば既に Perl5 インタプリタが登録されているかと思いますが、まだ登録されていない場合あるいは新たにインタプリタを追加したい場合は `Configure...` をクリックして Perl5 インタプリタを追加しておいて下さい。

Perl5 インタプリタが選択できたら、`Next` をクリックします。

![01_セットアップ003.png](https://qiita-image-store.s3.amazonaws.com/0/96400/a0d5f82b-b20a-5b09-af3d-b6a725a1e591.png)


次に Perl プロジェクトの名前とそのプロジェクトが配置されるディレクトリを指定します。

ここで注意点があります。この記事で紹介しているリモート環境用のサンプルスクリプトを用いて VM を構築する場合、**ここで作成する Perl スクリプトのコードは、そのままホストマシンと VM との間の共有フォルダに配置してください。** このようにすることで、IntelliJ IDEA 上でプロジェクト中の Perl スクリプトに対して加えた変更が、リモート環境中の Perl スクリプトに即時反映されるため、開発が容易になります。先程 GitHub からサンプルスクリプトを取得してきた際に、リポジトリ内の `dev` ディレクトリをデスクトップに移動させました。後から `Vagrant` で VM を立ち上げますが、その際に `Vagrant` で作成される VM および VM 中で立ち上げる `Docker` コンテナは `~/Desktop/dev/files/docker-perl-debug/www/cgi-bin` 中に配置された Perl プロジェクトを CGI として認識するように設定されています。

したがってサンプルスクリプトを使用している場合、ここでは以下の画像を参考に `~/Desktop/dev/files/docker-perl-debug/www/cgi-bin` 中に Perl プロジェクトを作成するようにしてください。

なお、サンプルスクリプト中にはこれから作成しようとしている `remote-debug-test` は既に完成された状態で含まれているため、同じ名前のプロジェクト名にしようとしている場合には一度 `remote-debug-test` ディレクトリを削除するか、あるいは別のプロジェクト名を設定するようにしてください。

プロジェクト名とプロジェクトの保存ディレクトリが指定できたら、`Finish` をクリックして下さい。

![01_セットアップ004.png](https://qiita-image-store.s3.amazonaws.com/0/96400/bbf9ee48-6232-8554-6b85-97861dbe79ea.png)


まだ存在しないディレクトリ名を指定していた場合には以下のようなアラートが表示されるかと思いますが、そのまま `OK` ボタンをクリックして大丈夫です。

![01_セットアップ005.png](https://qiita-image-store.s3.amazonaws.com/0/96400/7107b27e-cac8-167d-fa13-dcc54a552464.png)


プロジェクトが作成されると、以下のような画面となります。ウィンドウ左側のプロジェクトツールウィンドウが表示されていない場合、Windows では `Alt` + `1` キー、macOS では `Command` + `1` キーを入力してプロジェクトツールウィンドウが以下のように表示された状態としておいてください。

![01_セットアップ006.png](https://qiita-image-store.s3.amazonaws.com/0/96400/774e7ea6-1df2-168f-188b-89247decff14.png)


それでは、簡単なサンプルスクリプトを作成しましょう。左側のプロジェクトツールウィンドウ上でプロジェクの名前 (ここでは `remote-debug-test`) を右クリックし、表示されるコンテキストメニューから `New` -> `Perl5 File` と選択します。

![01_セットアップ007.png](https://qiita-image-store.s3.amazonaws.com/0/96400/2034e79d-9cf0-0d43-163a-3d734f112447.png)


作成するサンプルスクリプトのファイル名を入力します。ここでは `test.pl` というサンプルスクリプトを作成しようとしているので、Name に `test` と入力します。Name には `.pl` などの拡張子は入力しないように気をつけて下さい。また、Kind には `Script` を選択します。

選択が完了したら、`OK` をクリックしましょう。

![01_セットアップ008.png](https://qiita-image-store.s3.amazonaws.com/0/96400/55c9e18a-4d09-834e-4109-84999c420bce.png)


これにより、ウィンドウ左側のプロジェクトツールウィンドウに `test.pl` が追加されます。

それでは、サンプルスクリプトを作成しましょう。`test.pl` の内容を以下のように書き換えてみて下さい。

```perl
#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';

print("Hello, remote debugging!\n");

my $res = multiply(8, 8);
print("$res\n");

sub multiply {
    my $n = shift;
    my $times = shift;
    my $acc = 0;

    for (my $i = 0; $i < $times; $i++) {
        $acc += $n;
    }

    return $acc;
}
```

書き換えが完了すると、以下の図のようになります。

![01_セットアップ009.png](https://qiita-image-store.s3.amazonaws.com/0/96400/361b8c29-71f3-366c-b537-6123c8d24730.png)


では、このサンプルスクリプトを実行してみましょう。ウィンドウ左側のプロジェクトツールウィンドウ上でプロジェクト名を右クリックし、表示されるコンテキストメニュー中から `Run 'test.pl'` をクリックします。

![01_セットアップ010.png](https://qiita-image-store.s3.amazonaws.com/0/96400/7295787c-f0a2-711b-3320-ac4c7ec137c9.png)


問題なくスクリプトが実行されれば、以下の図のようにプログラムの実行結果が画面下部のコンソールに出力されるはずです。

![01_セットアップ011.png](https://qiita-image-store.s3.amazonaws.com/0/96400/abccc83e-bb1c-1df6-1c4e-49279983d9d9.png)


以上で、ローカル環境で動作する Perl のプロジェクトが作成できました。ローカル環境側に `Devel::Camelcadedb` をインストールしているならば、IDE 上からデバッグを実行することもできます。詳細は、[前回の記事](http://qiita.com/AnnPin/items/5f868f0c7cb5d1af306b) を参照して下さい。


##  リモート環境用のサーバを準備する

さて、それではリモート環境用の VM を準備しましょう。この記事ではサンプルスクリプトを用いて、Vagrant と Docker を用いた VM および コンテナ をリモート環境サーバとして利用します。

ターミナルから、デスクトップ上にコピーした `dev` ディレクトリ内に移動し、Vagrant を用いて Ubuntu14.04(Trusty) の VM を作成します。 Vagrant で作成される VM はメモリを `1.5GB` に割り当て、ホストオンリーネットワークにおける IP アドレスを `192.168.64.64`　と割り当てています。また、`dev` ディレクトリ内の `files` ディレクトリを VM の `/vagrant` にマウントするように設定しています。これらの設定は同じく `dev` ディレクトリ中の `Vagrantfile` を編集することで変更できます。メモリは `1.5GB` 以外に設定されても大丈夫ですが、`512MB` 程度だと CPAN の実行がメモリ不足で落ちてしまい、`Devel::Camelcadedb` のインストールに失敗してしまう可能性があるので注意して下さい。IP アドレスは自由に変更していただいてもOKです。

(この記事中のスクリーンショットでは `dev.localhost` というホスト名を `192.168.64.64` の VM のアドレスに設定した状態となっています。)

```bash
$ cd ~/Desktop/dev
$ vagrant up
```

`vagrant up` コマンドにより、VirtualBox 上に自動的に Ubuntu14.04 (Trusty) の VM が作成されます。また、プロビジョニング設定を `provision.sh` に記述しているため、`vagrant up` コマンド中に Docker のインストールまでまとめて実行されます。

以下のスクリーンショットは VM の初回起動ではないためほとんどログが出力されていませんが、VM の初回起動時にはこれよりもたくさんのログ情報が表示されるかと思います。

![02_サーバ001.png](https://qiita-image-store.s3.amazonaws.com/0/96400/6d0fd353-ca09-1cd5-dfb3-86733d9c1801.png)


`vagrant up` コマンドが完了したら、VM が正しく起動できているか確認してみましょう。以下のコマンドを実行して下さい。

```bash
$ vagrant status
```

無事に VM が起動できているならば、status は以下のように `running` となるはずです。

![02_サーバ002.png](https://qiita-image-store.s3.amazonaws.com/0/96400/09908e8e-7be1-a15a-be01-ba8713341546.png)


無事に VM が起動できたら、VM 中に `ssh` でログインを行いましょう。以下のコマンドで VM 中にログインができます。

```bash
$ vagrant ssh
```

無事にログインができた場合、ターミナルのプロンプトが `vagrant@vagrant-ubuntu-trusty-64` のように VM のものに変化するのがわかるかと思います。

では、`cd` コマンドを使ってディレクトリを移動し、ホストマシンとの共有フォルダ内に移動しましょう。

```bash
$ cd /vagrant
$ cd docker-perl-debug/
```

以下にこの作業のスクリーンショットを示します。

![02_サーバ003.png](https://qiita-image-store.s3.amazonaws.com/0/96400/99df35c9-8058-2b9a-d9c7-f0195cea461f.png)


無事に `docker-perl-debug` ディレクトリ内に移動できたら、次に実際に Perl が動作する `Docker` コンテナのための Docker イメージの作成およびコンテナの立ち上げを行います。今回のサンプルスクリプトでは、`docker-perl-debug` 内の `Makefile` にイメージの作成からコンテナの立ち上げまでを自動的に行うように設定を記述しています。したがって、`docker-perl-debug` ディレクトリ内に移動できたら、次のコマンドを実行するだけで Docker コンテナが起動します。

```bash
$ make setup-nc
```

![02_サーバ004.png](https://qiita-image-store.s3.amazonaws.com/0/96400/5540e29e-4747-0037-bf5f-fda3f89a06db.png)


無事にコンテナが起動すると、以下のような画面となります。

![02_サーバ005.png](https://qiita-image-store.s3.amazonaws.com/0/96400/21cea7c3-7504-11dc-f7ed-556603746de3.png)


コンテナの作成はこのように自動化されていますが、中で行っていることはそんなに大した内容ではありません。大体、以下のようなことを行っています。
    -  Ubuntu14.04 の Docker イメージを元にして Apache をインストールするように Dockerfile に設定.
    -  コンテナ内のドキュメントルートを `/www` にするように `000-default.conf` に設定.
    -  し、`/www/cgi-bin` 中で CGI が動作するように `apache2.conf` に設定.
    -  Apache が CGI モジュールが有効になった状態で起動するように `setup.sh` に設定.
    -  `docker-perl-debug` ディレクトリ中の `www` ディレクトリがコンテナ中の `/www`、すなわちドキュメントルートにマウントされるように `Makefile` に設定.
    
コンテナセットアップ時のスクリプトの詳細に関しては、リポジトリ中の実際のスクリプトの記述を参照していただければと思います。

問題なくコンテナが起動したならば、次にコンテナ内へとログインを行いましょう。`Makefile` 中に `make bash` というコマンドでコンテナ内にログインができるように設定を記述してあります。

```bash
$ make bash
```

コンテナ内へのログインが成功すると、再びプロンプトが `root@(コンテナのハッシュ値)` のように変化します。コンテナ内では `/www` がドキュメントルートになっており、このディレクトリは VM 環境中の `/vagrant/docker-perl-debug/www` がマウントされるようになっています。したがって、この中の `cgi-bin` ディレクトリ中に先程 IntelliJ IDEA 上で作成した Perl プロジェクトが配置されるようになっています。以下のように `cd` コマンドを実行することでディレクトリ中に移動してみて下さい。

```bash
$ cd /www/cgi-bin/remote-debug-test/
```

以下にこの作業のスクリーンショットを示します。

![02_サーバ006.png](https://qiita-image-store.s3.amazonaws.com/0/96400/ca4671d5-5878-ba0f-7034-752356d94eda.png)


以上で、リモート環境として動作ささせる VM および コンテナ の準備が完了しました。


##  リモートデバッグを試してみる

さて、それでは IntelliJ IDEA 上で作成した Perl スクリプトである `test.pl` を **VM 中のコンテナ環境中で実行し、そのリモートデバッグを IntelliJ IDEA 上で行ってみましょう。**

まずは、前回までと同様に Perl が動作する環境には `Devel::Camelcadedb` モジュールのインストールが必要となります。コンテナ環境中で以下のコマンドを実行してみて下さい。

```bash
$ cpan install Devel::Camelcadedb
```

CPAN から `Devel::Camelcadedb` のインストールを行います。実は、先程立ち上げた Docker コンテナはセットアップ時に `Devel::Camelcadedb` をインストールするように設定されているため、このコマンドを実行すると *既にインストールされていて最新である (Devel::Camelcadedb is up to date (1.6.1.6).)* と表示されるかと思います。

ローカル環境でのデバッグとは異なり、リモートデバッグを行うにはさらにいくつか追加でコンテナ環境側に設定を行います。`Perl for IntelliJ IDEA` プラグインの公式リポジトリの [Wikiページ](https://github.com/Camelcade/Perl5-IDEA/wiki/Perl-Debugger) によると、リモート環境側には **Perl を実行するユーザ** に対して **3 つの環境変数の登録が必要** となっています。3 つの環境変数はそれぞれ以下のとおりです。

*  PERL5_DEBUG_ROLE
    -  リモート環境中の Perl プロセスの役割を設定する. "server" あるいは "client" を指定する.
    -  リモートデバッグを行う際、*ローカル環境中の IDE をリモート環境中の Perl プロセスに接続させる ("server")* のか、あるいは *リモート環境中の Perl プロセスをローカル環境中の IDE に接続させる ("client")* のかを指定する.
    
*  PERL5_DEBUG_HOST
    -  Perl プロセスが "server" として振る舞うように設定されている場合はリモート環境のアドレス ("localhost") を指定し、Perl プロセスが "client" として振る舞うように設定されている場合は IDE が動作しているローカル環境のアドレスを指定する.
    
*  PERL5_DEBUG_PORT
    -  リモートデバッグを行う際に使用するポート番号を設定する. Perl プロセスが "server" として振る舞うように設定されている場合はリモート環境のポート番号を指定し、Perl プロセスが "client" として振る舞うように設定されている場合は IDE が動作しているローカル環境のポート番号を指定する.
    
どのような設定でリモートデバッグを行うのかによって、設定内容は変わってくるかと思います。ここでは、**リモート環境中の Perl プロセスからローカル環境中の IDE へと接続をするように設定を行ってみます。**

この設定の場合、`PERL5_DEBUG_ROLE` は `"client"` となり、`PERL5_DEBUG_HOST` は IDE が動作しているローカル環境のアドレス、`PERL5_DEBUG_PORT` はリモートデバッグを行うための IDE が動作しているローカル環境のポート番号を指定することとなります。

今回の場合、`PERL5_DEBUG_HOST` にはVirtualBox の VM 上で動作している Docker コンテナ中からホストマシンのアドレスを入力することとなりますが、[こちら](https://gist.github.com/wakamsha/087d5f95c8dbe07b9591) によると、VirtualBox の VM 内からはホストマシンは `10.0.2.2` というアドレスで参照することができるようになっているようです。したがって、ここでは`PERL5_DEBUG_HOST` には `10.0.2.2` を設定します。

また、`PERL5_DEBUG_PORT` の値はここでは `18080` にしようかと思いますが、特にこのポート番号でなければならないというものではありません。別のポート番号を指定していただいても大丈夫です。ただし、後でからこのポート番号を IDE 側にも入力するため、`PERL5_DEBUG_PORT` と IDE 側のポート番号の設定は必ず同じポート番号を指定するようにしてください。

Bash では環境変数の設定は `export` コマンドで行うことができます。したがって、以下のように入力して下さい。

```bash
$ export PERL5_DEBUG_ROLE="client"
$ export PERL5_DEBUG_HOST="10.0.2.2"
$ export PERL5_DEBUG_PORT="18080"
```

この作業のスクリーンショットを以下に示します。

![03_接続テスト001.png](https://qiita-image-store.s3.amazonaws.com/0/96400/1ced0c04-8b64-f631-1339-95bacaab32ed.png)


ここまででリモート環境側への設定は完了しました。再び IntelliJ IDEA へと戻り、画面上部のメニューから `Run` -> `Edit Configurations...` を開いて下さい。

![03_接続テスト002.png](https://qiita-image-store.s3.amazonaws.com/0/96400/6e1a93d1-1243-31ad-aa4a-95562173a08b.png)


すると、Run/Debug Configurations ウィンドウが表示されます。ここにリモートデバッグ実行時の設定情報を記述します。ウィンドウ左上の `+` をクリックしましょう。

![03_接続テスト003.png](https://qiita-image-store.s3.amazonaws.com/0/96400/8d9e8d73-5d97-07d5-a121-ac34d2fdd463.png)


`Add New Configuration` メニューが表示されるので、この中から `Perl Remote Debugging` を選択してクリックします。

![03_接続テスト004.png](https://qiita-image-store.s3.amazonaws.com/0/96400/f063fdfb-77eb-46b3-6ebc-65d573fc1fd6.png)


すると、Perl のリモートデバッグ用の設定が新たに追加されます。このリモートデバッグ用の設定を以下のように指定します。

*  Name: リモートデバッグ設定の名前 (ここでは `remote debug` としている)
*  Remote project root: リモート環境中におけるプロジェクトディレクトリの位置となるので、`/www/cgi-bin/remote-debug-test` とする。
*  Connection mode: `Perl process connects to the IDE` とする。
*  Server host: `localhost` とする。
*  Server port: `18080` とする。

この中で、Perl プロジェクトの名前が `remote-debug-test` ではない場合には、Remote project root の値も併せて修正して下さい。また、今回はリモート環境中の Perl プロセスからローカル環境中の IDE にアクセスをするように設定したいので、Connection mode は `Perl process connects to the IDE`、Server host は `localhost` となります。

Server port は必ずしも `18080` である必要はありませんが、リモート環境中の環境変数 `PERL5_DEBUG_PORT` で設定した値と同じ値を入力するようにして下さい。

設定が完了したら、`OK` をクリックしましょう。

![03_接続テスト005.png](https://qiita-image-store.s3.amazonaws.com/0/96400/1b622e84-e919-8c91-0528-f9c5fb750835.png)


さて、いよいよリモートデバッグを試してみます。IntelliJ IDEA のエディター上でリモートデバッグ時に動作を停止させたい行にブレークポイントを設定しましょう。

![03_接続テスト006.png](https://qiita-image-store.s3.amazonaws.com/0/96400/c0a13637-0886-f057-142c-655b151af575.png)


ブレークポイントが設定できたら、画面上部のメニューから `Run` -> `Debug 'remote debug'` をクリックして先程設定したリモートデバッグ用の構成情報でデバッグを開始させます。

![03_接続テスト007.png](https://qiita-image-store.s3.amazonaws.com/0/96400/8aeafcd5-acc0-b7cc-e2bc-f155bde6827f.png)


デバッグが開始されると、IDE 上ではプログラムは実行されることなくただ画面下部のコンソール上に `Listening on localhost:18080...` という表示が出力されます。これは、IDE がリモート環境中の Perl スクリプトが実行された際に IDE に接続してくるのを待機している状態です。

したがって、リモート環境中から Perl スクリプトを実行してみましょう。注意点として、リモート環境中で Perl スクリプトを実行する場合には必ず、`/usr/bin/perl -d:Camelcadedb スクリプト名` というように、必ず `-d` オプションで Camelcadedb を読み込ませるようにしてください。なお、この記事で使用しているリモート環境でリモートデバッグを試している場合には、ここでは Perl を実行する際に `/usr/bin/perl` のようにフルパスで指定するようにしてください。理由は次のセクションで説明しますが、取り敢えずこの段階では `/usr/bin/perl -d:Camelcadedb スクリプト名` というフォーマットなのだと思っておいて下さい。

```bash
$ /usr/bin/perl -d:Camelcadedb test.pl
```

ブレークポイントの設定からリモートデバッグの実行までを行っている様子を以下に示しますね。

![03_接続テスト008.gif](https://qiita-image-store.s3.amazonaws.com/0/96400/b66e60bb-64b6-ad0c-8b00-3c9bee709595.gif)



どうでしょうか？ リモート環境中で Perl スクリプトが実行された直後に IDE 上でプログラムの動作がブレークポイントで停止しているのを確認できますね。このようにして、**リモート環境中で動作している Perl プログラムをローカル環境の IDE 上でデバッグを行うことができます。**


##  CGI のリモートデバッグ

###  事前準備（サンプルスクリプトでは既に設定済み）

さて、先程のセクションまででローカル環境中で動作している IntelliJ IDEA でリモート環境中で動作している Perl スクリプトをデバッグすることができるということまで紹介しました。

折角 Perl スクリプトのリモートデバッグができるようになったのだから、最後はやはり CGI として動作している Perl スクリプトのリモートデバッグにも挑戦しましょう。これができるようになることで、この記事の最初にお見せした **ブラウザ上での渡された入力をデバッガを用いて可視化しながら、ローカル環境の IDE でデバッグできる環境** を構築できるようになります。

サンプルスクリプトを使用して今までの記事通りにここまで作業を進めてこられている場合、**実は CGI のデバッグに必要な設定は既に Docker コンテナ側に設定されている状態となっています。**

具体的には、以下のように設定されている必要があります。
    1. リモート環境中に `perl` がインストールされており、`Devel::Camelcadedb` モジュールがインストールされている。
    2. Apache を実行しているユーザーに `PERL5_DEBUG_ROLE`、`PERL5_DEBUG_HOST`、`PERL5_DEBUG_PORT` の 3 つの環境変数が設定されている。
    3. Apache から CGI として Perl が実行される際に、Perl が `-d:Camelcadedb` のオプションが付けられた状態で起動するようになっている。
    
1. については特に説明しなくても大丈夫かと思います。Ubuntu14.04 (Trusty) は標準で `/usr/bin/perl` がインストールされていますし、`cpan install Devel::Camelcadedb` を実行することで `Devel::Camelcadedb` モジュールのインストールが行えるということは確認してきましたね。

今回の コンテナ 環境のセットアップスクリプト中では `Devel::Camelcadedb` がスクリプトから自動的に実行できるようにするため、`Dockerfile` 中に以下のように設定を記述しています。環境変数 `PERL_MM_USE_DEFAULT` を `1` に設定しておくことで、CPAN の初回起動時の設定を全て `yes` と入力するように自動化できます。

```Dockerfile
# cpan 自動コンフィグ
ENV PERL_MM_USE_DEFAULT 1
# Devel::Camelcadedb をインストール
RUN cpan install Devel::Camelcadedb
```

この時、`cpan install` の実行の際に `make`、`build-essential` パッケージが必要となる点に注意して下さい。

次に 2. についてですが、今回使用している Ubuntu14.04 (Trusty) 環境では Apache は `www-data` というユーザが実行するようになっています。したがって、環境変数はこの `www-data` に対して設定されている必要があります。今回の コンテナ 環境セットアップスクリプト中ではこの設定を `apache2.conf` の末尾に以下のように記述しています。

```apache2.conf
SetEnv PERL5_DEBUG_ROLE client
SetEnv PERL5_DEBUG_HOST 10.0.2.2
SetEnv PERL5_DEBUG_PORT 18080
```

これにより、Apache から CGI が起動される際にも環境変数が設定された状態となります。

最後に 3. についてはちょっと工夫が必要です。すなわち、リモートデバッグを行う際に Apache から CGI として Perl が起動される際には、前のセクションで見たように必ず `-d:Camelcadedb` をオプションとして設定する必要があります。Ubuntu 上で Apache が動いている場合、どのように Perl スクリプトが実行されるかは各 Perl スクリプト先頭の shebang (`#!/...`) に記述されている内容に依存します。したがって、そのまま shebang に `#!/usr/bin/perl -d:Camelcadedb` のように記述すれば良さそうですが、このように shebang を指定して CGI を実行しようとすると、ブラウザからアクセスした際に 500 Internal Server Error を吐いてしまいます。

これをどうにか回避できないかを考えました。まず初めに、Apache を実行しているユーザーにエイリアスとして `alias=perl"perl -d:Camelcadedb"` を設定し、CGI として Perl が実行される際にエイリアスに設定した Perl が実行されるようにできないかを考えました。現状の shebang は `/usr/bin/perl` のようにフルパス指定となっているため、このままではエイリアスを設定してもそのまま `/usr/bin/perl` を実行しようとしてしまいます。また、エイリアスにはフルパスを指定することができないことから、shebang 側をどうにか変更できないか考えました。 shebang を `/usr/bin/env perl` のように `/usr/bin/env` 経由で `perl` を解決するようにしておけば、問題なく実行できそうな気がしますが、Apache を実行しているユーザである `www-data` にたいするエイリアスを設定する方法がよく分からなかったため、この方法は断念しました。

次に、呼び出された際に `/usr/bin/perl` を `-d:Camelcadedb` オプションが付加された状態で実行するシェルスクリプトを作成する方法を考えてみました。**そして、結果からいえばこの方法で CGI のリモートデバッグができるようになりました。** 呼び出された際に与えられた引数 (Perl のスクリプト名) をそのまま `/usr/bin/perl` に渡し、その際に `-d:Camelcadedb` オプションを付加するようにすれば問題なくリモートデバッグができそうです。そこで、以下のようなシェルスクリプトを作成してみました。

```bash
#!/bin/bash

/usr/bin/perl -d:Camelcadedb ${1} 2>/dev/null
exit 0

```

このシェルスクリプトは実行すると Perl インタプリタのラッパーとして振る舞い、引数として入力された Perl スクリプトの名前を `/usr/bin/perl` に引数 ``${1}`` としてそのまま渡します。また、この際に `-d:Camelcadedb` オプションも同時に指定しています。

`-d:Camelcadedb` を付加した状態では、Perl スクリプトは `Connected.` のような IDE との接続ログ情報を標準エラー出力へと流します。CGI として動作させる上ではこの挙動は不要と考えたため、`2>/dev/null` と記述することで標準エラー出力を捨てています。

今回の Docker コンテナ環境では、このシェルスクリプトを `/usr/local/bin/perl` として配置しています。`/usr/local/bin` は `/usr/bin` よりも先にチェックされるパスであるため、ただ単に `perl` と入力するとこちらの `/usr/local/bin/perl` が優先的に実行されます。これが前のセクションの最後で `/usr/bin/perl -d:Camelcadedb Perlスクリプト名` のように `/usr/bin/perl` と Perl インタプリタをフルパスで入力しなければならないとしていた理由です。

このようにして `/usr/bin/perl` のラッパースクリプトを `/usr/local/bin/perl` に配置しておくことで、shebang を `#!/usr/local/bin/perl` と設定すればデバッガが有効になった状態で Perl スクリプトが実行されるため、CGI のリモートデバッグができるようになります。

> (もっと賢い方法があれば教えてください...)

-----

###  CGI を作成してリモートデバッグする

このようなリモートデバッグに必要な設定はリモート環境である Docker コンテナ側に既に設定されているため、CGI のデバッグに必要なことは CGI 用の Perl プロジェクトを IntelliJ IDEA 上で作成し、リモートデバッグ用の構成情報を IntelliJ IDEA に入力するだけです。

IntelliJ IDEA で新たに CGI 用の Perl プロジェクトを作成しましょう。ここでは、このプロジェクトを `~/dev/files/www/cgi-bin/IdeaPerl5` として作成しました。サンプルスクリプトを使用されている場合、既にこの Perl プロジェクトがリポジトリ中に存在しているかと思います。

次に、CGI 本体を作りましょう。ここではブラウザから自然数 $n$ を入力してもらい、$n$ 番目のフィボナッチ数を返すという単純な CGI を作ってみます。作成するファイルは、`index.pl`、`result.pl`、`Fibo.pm` の 3 つです。

このうち、`result.pl` だけはリモートデバッグ対象とするため、shebang を `#!/usr/local/bin/perl` に設定しておきます。

まず、`index.pl` は以下のようになります。

```perl
#!/usr/bin/perl
use strict;
use warnings FATAL => 'all';

print "Content-Type: text/html; charset=utf-8\n\n";
print "<html>\n";
print "<head><title>Remote debugging sample</title></head>\n";
print "<body>\n";
print "<p>n 番目のフィボナッチ数計算</p>\n";
print "<form action='result.pl' method='post'>\n";
print "<p> n : <input type='number' name='n'></p>\n";
print "<p><input type='submit' value='Run'></p>\n";
print "</form>\n";
print "</body>\n";
print "</html>\n";
exit;
```

続いて、`result.pl` です。

```perl
#!/usr/local/bin/perl
use strict;
use warnings FATAL => 'all';
use Fibo;

my $post_data;
read (STDIN, $post_data, $ENV{'CONTENT_LENGTH'});
my $fibo = Fibo->new();
my @t = split(/=/, $post_data);
my $result = $fibo->calc_nth_fibonacci($t[1]);

print "Content-Type: text/html; charset=utf-8\n\n";
print "<html>\n";
print "<head><title>Remote debugging sample</title></head>\n";
print "<body>\n";
print "<h1>答え : $result</h1>\n";
print "</body>\n";
print "</html>\n";
exit;
```

最後に、`Fibo.pm` は以下のとおりです。

```perl
package Fibo;
use strict;
use warnings FATAL => 'all';

sub new {
    my ($class, %args) = @_;
    bless \%args, $class;
}

sub calc_nth_fibonacci {
    my $self = shift;  # 第一引数は self
    my $n = shift;     # 第二引数.

    my @table = ();

    if ($n < 1) {
        return -1;
    }

    for (my $i = 1; $i <= $n; $i++) {
        if ($i == 1 or $i == 2) {
            @table = (@table, 1);
        } else {
            @table = (@table, $table[$i - 2] + $table[$i - 3]);
        }
    }

    return $table[scalar(@table) - 1];
}

1;
```

ファイルが作成できたら、IntelliJ IDEA 上でリモートデバッグのための構成情報を設定しましょう。画面上部のメニューから `Run` -> `Edit Configurations...` を選択し、Run/Debug Configurations ウィンドウを表示します。

新たに Perl Remote Debugging 構成情報を作成し、以下のように構成情報を設定します。

![04_リモートデバッグ001.png](https://qiita-image-store.s3.amazonaws.com/0/96400/cf16f3be-cd27-8f96-bd0d-3ab936926f01.png)


前のセクションとの違いは、Remote project root で指定しているディレクトリが `/www/cgi-bin/IdeaPerl5` に変更されただけです。残りの設定は前のセクションと同じ設定で大丈夫です。設定できたら `OK` ボタンをクリックしましょう。

後は `result.pl` をエディタで開き、CGI として動作している際に動作を一時的に停止させたい行にブレークポイントを設定してデバッグを開始し、ブラウザから CGI を実行すればリモートデバッグができるようになっています。

![00_完成図.gif](https://qiita-image-store.s3.amazonaws.com/0/96400/3e5b32e2-6436-72f4-cd28-e61f987c198a.gif)


##  終わりに

ここまで長い道のりでした。
わりと身内向けなネタだったのですが、誰かの開発の手助けとなれば幸いです。
最後までお読み頂きありがとうございました。