VSCodeを使ったperlのデバッグ

(2年前に書いたのが下書きに残っていたので放流します。なお、記事は完結していません(なお残念ながら、詳細は失念。))

perl で書かれたOTRSというチケット管理システムをソースレベルで追跡する必要が生じたので、その作業を行った時のメモです。ただし記事の内容は、mod_perl2 で動く一般的な Web アプリのデバッグに役立つのではないかと思っています。OTRS は Vagrant 仮想環境上の CentOS7 において、RPM からインストールして正常に動いているものとします。

SSHの設定

デバッグ時は VSCode の Remote-SSH 経由でターゲットの Linux にログインしますが、この時デフォルトの vagrant ユーザとして接続すると、後述の plenv が有効にならないので、SSH 接続自体も otrs ユーザでできるように SSH 周りの設定を変更します。まずはターゲット(サーバ)側です。SSH のキーペアは vagrant ユーザのものを流用することにします。

$ whoami
vagrant
$ sudo install -d -o otrs -g apache -m 0700 /opt/otrs/.ssh
$ sudo install -o otrs -g apache -m 0600 ~/.ssh/authorized_keys /opt/otrs/.ssh

クライアント(Windows)側の ~/.ssh/config(抜粋):

Host  otrs6r
  Hostname 192.168.56.28
  User  otrs
  IdentityFile	"C:\Users\hotta\vm\otrs\.vagrant\machines\otrs6r\virtualbox\private_key"

また、otrs ユーザは sudo できるようにしておきます。

$ echo 'otrs ALL=(ALL) NOPASSWD: ALL' | sudo tee /etc/sudoers.d/otrs

perl 5.18.0 環境の作成

CentOS7 標準の perl は v5.16.3 ですが、後述する VSCode の Language Server and Debugger for Perl は perl の v5.18.0 以上を要求します。このため、まずは plenv を使って perl 5.18.0 環境を作成します。

plenv 環境の作成方法は本家サイトにある通りです。なお mod_perl2 のビルドに必要なので、perl のインストール時に -Duseithreads 等のコンパイルオプションを付けてインストールしています。

さらにモジュールを追加インストールするための cpanm と、VSCode で要求される Perl::LanguageServer も、合わせて導入しておきます。

$ whoami
otrs
$ git clone https://github.com/tokuhirom/plenv.git ~/.plenv
$ echo 'export PATH="$HOME/.plenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(plenv init -)"' >> ~/.bash_profile
$ git clone https://github.com/tokuhirom/Perl-Build.git ~/.plenv/plugins/perl-build/
$ exec $SHELL -l
$ plenv install 5.18.0 -Duseithreads -D ccflags=-fPIC 
$ plenv install-cpanm
$ plenv exec cpanm Perl::LanguageServer
$ plenv rehash
$ plenv local 5.18.0
$ perl --version | head -2


This is perl 5, version 18, subversion 0 (v5.18.0) built for x86_64-linux-thread-multi

OTRS 固有情報

OTRS が依存する perl モジュールも合わせて cpanm でインストールする必要があります。現時点で不足しているモジュールを表示してみます。

$ ./bin/otrs.CheckModules.pl | grep -E '(FAILED|required)'
  o Apache::DBI......................FAILED! Not all prerequisites for this module correctly installed.
  o Archive::Zip.....................Not installed! To install, you can use: 'yum install "perl(Archive::Zip)"'. (required - Required for compressed file generation.)
.....

これらをまとめてインストールします。他にも実行時に not installed が出たパッケージも合わせて入れています。Carton だと一発でいけるかもしれないんですが、いろいろと理解が追いつかなかったので断念。

$ plenv exec cpanm Apache::DBI
$ plenv exec cpanm Archive::Zip
$ plenv exec cpanm Date::Format
$ plenv exec cpanm DateTime
$ plenv exec cpanm DBI
$ plenv exec cpanm Moo
$ plenv exec cpanm Net::DNS
$ plenv exec cpanm Template
$ plenv exec cpanm XML::LibXML
$ plenv exec cpanm YAML::XS
$ plenv exec cpanm LWP::Protocol::https

次に mod_perl2 をインストール。その後動作確認をしてみたら内部で Not Found エラーが出ていた DBD::mysql もインストール。


$ plenv exec cpanm HTML::HeadParser
$ plenv exec cpanm --sudo mod_perl2
$ plenv exec cpanm DBD::mysql --configure-args="--libs='-L/usr/lib64 -lssl -lcrypto -L/usr/local/lib -lmariadbclient'"

mod_perl2 のインストールスクリプトは、RPM 版の mod_perl の置き場所を上書きするようです。このためにインストール時に –sudo を付けています。

$ grep mod_perl.so .cpanm/build.log | grep ^cp
cp mod_perl.so /usr/lib64/httpd/modules
$ ls -l /usr/lib64/httpd/modules/mod_perl.so
-rwxr-xr-x. 1 root root 2015608  3月 30 17:19 /usr/lib64/httpd/modules/mod_perl.so

Apache が /usr/bin/perl を使う問題

OTRS では RPM パッケージで以下の設定が追加されます。

vagrant@otrs6r:$ grep -r Perlrequire /etc/httpd/
/etc/httpd/
conf.d/zzz_otrs.conf:    Perlrequire /opt/otrs/scripts/apache2-perl-startup.pl

Apache2 のドキュメントによると、Perlrequire は mod_perl2 のディレクティブで、perl のコードで言うところの

require "/opt/otrs/scripts/apache2-perl-startup.pl";

と同等の機能とのことです。apache2-perl-startup.pl のシバン(ファイル1行目の起動コマンドインタプリタ指定)には

!/usr/bin/env perl

と書かれているので、plenv で入れた perl を使ってくれるかと思いきや、そうもいかないようです(RPM から入れた v5.16.3 を指している)。

$ sudo /usr/bin/env perl -v | head -2

This is perl 5, version 16, subversion 3 (v5.16.3) built for x86_64-linux-thread-multi

root や sudo 経由の otrs ユーザでは、plenv local / global で指定した perl 5.18.0 になっていません。これは、plenv は plenv を入れたユーザの .bash_profile の PATH を書き換えることで plenv の方を呼び出しているため、他のユーザでは有効にならないためです。

ここはシンボリックリンクによる力技で解決しました。

$ sudo mv /usr/bin/perl /usr/bin/perl_
$ sudo ln -f /opt/otrs/.plenv/versions/5.18.0/bin/perl /usr/bin/perl
vagrant@otrs6r:~$ sudo perl -v | head -2

This is perl 5, version 18, subversion 0 (v5.18.0) built for x86_64-linux-thread-multi

これでようやく 5.18.0 の方を見てくれるようになりました。

OTRSがplenvを見てくれない問題

その後、ブラウザからアプリのログイン画面にアクセスすると Internal Server Error になりました。あるはずのモジュールが見つからないエラーです。これについては OTRS 側の index.pl の先頭に検索パスを追加することで回避しました。

$ vi bin/cgi-bin/index.pl
$ sed -n '22,27p' bin/cgi-bin/index.pl
# use ../../ as lib location
use FindBin qw($Bin);
use lib "$Bin/../../.plenv/versions/5.18.0/lib/perl5/site_perl/5.18.0/";  # <= 追加
use lib "$Bin/../..";
use lib "$Bin/../../Kernel/cpan-lib";
use lib "$Bin/../../Custom";

ここまでの時点でいったんブラウザからアプリのURLを開き、Internal Server Error が出ない程度には動くことを確認しておきます。

フォルダを開く

次に VSCode から「フォルダを開く」で /opt/otrs を開きますが、最初ファイルが多すぎて追跡できないという警告が出たので、Visual Studio Code is unable to watch for file changes in this large workspace” (error ENOSPC)に従って、以下の通りカーネルパラメータ fs.inotify.max_user_watches のチューニングを行いました。

$ sudo vi /etc/sysctl.conf
fs.inotify.max_user_watches=524288      # この行を追加
$ sudo sysctl -p
$ cat /proc/sys/fs/inotify/max_user_watches
524288

この設定を行うと 540MiB ほど余計にメモリを喰うらしいので、VMのメモリがしょぼい場合は適宜減らした方がよいかもしれません。

さらに、追跡する必要のないディレクトリは VSCode の追跡対象から除外しておきます。Settings File Location を参照して settings.json を設置します。

$ mkdir -p .config/Code/User/
$ vi .config/Code/User/settings.json
$ cat .config/Code/User/settings.json
"files.watcherExclude": {
    "**/.config/**": true,
    "**/.cpan/**": true,
    "**/.cpanm/**": true,
    "**/.dircolors-solarized/**": true,
    "**/.git/objects/**": true,
    "**/.git/subtree-cache/**": true,
    "**/.pki/**": true,
    "**/.plenv/**": true,
    "**/.ssh/**": true,
    "**/.vscode-server/**": true,
    "**/node_modules/*/**": true
}

VSCodeの設定

VSCode では、拡張機能から Language Server and Debugger for Perl を導入します。

「実行」>「構成の追加」をすると、以下の launch.json が生成されました。

{
    // IntelliSense を使用して利用可能な属性を学べます。
    // 既存の属性の説明をホバーして表示します。
    // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "perl",
            "request": "launch",
            "name": "Perl-Debug",
            "program": "${workspaceFolder}/${relativeFile}",
            "stopOnEntry": true,
            "reloadModules": true
        }
    ]
}

その後もリソース不足で vscode-server がなかなか落ち着いてくれないので、以下のように VM のリソースを増やしました。

  config.vm.define "otrs6r" do |otrs6r|
    otrs6r.vm.box = "otrs20210330"
    otrs6r.vm.network "private_network", ip: "192.168.56.28"
    otrs6r.vm.network :forwarded_port, guest: 22, host: 12328
    otrs6r.vm.hostname = "otrs6r"
    otrs6r.vm.provider "virtualbox" do |vb|
      vb.memory = "6000"
      vb.cpus = "4"
    end
  end

ここまで調整して、ようやく vscode-server の初期化が正常終了しました。VSCode からの接続からファイルのパースが終わるまで数分かかりました。

(中略)

VSCodeにおけるコンパイルエラー

ブラウザからの実行時に最初に起動される bin/cgi-bin/index.pl を VSCode のエディタで開いてみると、自動的にコンパイルが行われますが、ここでエラーになります。

調べてみると、どうも VSCode の perl デバッグ機能は、コンパイル後のソースを .vscode 配下に置いているようで、そこにないファイルは「見つかりません」となるのかもしれません。

$ ls .vscode/perl-lang/opt/otrs/
Kernel  bin  scripts  var

もともと OTRS の RPM パッケージに入っている cpan モジュールは Kernel/cpan-lib あたりに入っていますが、これらは全部 plenv 配下の cpanm で入れ直さなければならないのかもしれません。ということで、エラーになるたびにモジュールを追加していきます。


$ plenv exec cpanm IO::Interactive
$ plenv exec cpanm Math::Random::Secure
$ plenv exec cpanm Crypt::PasswdMD5

「問題」ペインがおとなしくなったら、デバッグアイコンをクリックして「▶ Perl-Debug」をクリックしてデバッグを開始しますが、ここで次の難関が。

該当の箇所(30行目)は以下です。こんなのでエラーになる perl って深いなぁ。

cpan.orgのバグトラッカーの書き込みに従って、index.pl の先頭付近にダミーの関数を追加することで回避。

sub DB::DB {}

次は以下のようなエラーです。

タイトルとURLをコピーしました