Test::Perl::Metrics::Liteでコードの”死活”監視

複数人で開発していて、コードベースが管理できないくらいに複雑化してしまうというケース、誰もが1回は経験したことがあるのではないかと思います。それを防ぐために、問題のあるコードに対してアラートをあげるのをテストで行うためのモジュールが、Test::Perl::Metrics::Liteです。拙作のPerl::Metrics::Liteを用いたテスト用のモジュールです。 

コードの問題の早期発見に関しては、メトリクスの可視化という手段が有効に機能します。色々な例外ケースを考えると、一律の閾値を設定することは難しいため、閾値によってエラーにするのではなく、可視化のみにするというのは割とよくある運用方法の一つです。これは、Gangliaなどでのシステム状態のモニタリングと似ています。このような用途では、先に紹介したPerl::Metrics::LiteによるJenkins+CheckStyleとの統合がおすすめです。

一方、どうしても認められない限界の許容値というのを設定することで、アラートをあげるという使い方もメトリクスにはあります。多様なレベルの開発者が参加する際には重要です。このような場合は、閾値は若干緩めに設定し、どうしても超えてはいけないラインを設定することで、コードが管理不能にならないようにコードベースを死守するために使います。概念としては、Nagiosなどを使った死活監視の概念と似ています。100行を超えるようなメソッドがコミットされる状況を監視するというのは、コードの死活監視といっても過言ではないからです。

Test::Perl::Metrics::Liteは後者のコードの死活監視を行うためのモジュールで、JenkinsなどのCIシステムとして統合して使うことを想定しています。

使い方ですが、以下のようなテストファイルを作り、これをCIで継続的に実行します。

ex) code_metrics.t

use Test::Perl::Metrics::Lite (-mccabe_complexity => 20, -loc => 100);
BEGIN {                                                                                                                                
    all_metrics_ok();                                                                                                                  
} 

上記の例は、コメント抜きの実行数が100行を超えていて、mccable complexityが20以上という条件で多数の分岐のある複雑なメソッドが引っかかることになります。この条件だと、そう簡単に手をつけられる状態ではなく、そのコードはほぼ死にかかっているといっても過言ではありません。このようなケースは、ビルドレベルでエラーにするというポリシーをとっておかないと、後々プロジェクトが崩壊してしまうため、テストケースという形で継続的にビルドするのに向いています。

まとめ

技術的負債を抱え込まないようにするために、メトリクスの可視化だけでなく、メトリクスによるコードの”死活”監視もすると捗るぞ、ということになります。

静的言語では、その言語の性質上このようなツール群はとても充実しているんですが、LL界隈ではあまり充実していないため、少し作ってみました。是非活用していただければ。

Perl::Metrics::LiteのCLIでPerlモジュールのメトリクス測定

Perl::Metrics::LiteにCLIを追加しました。FIleとSubroutineのメトリクスを測定できます。簡単にlocやmccableのcomplexityなどを測定できるので、casualに使ってみて頂ければ。

また、Jenkins用に組み込むためのmeasureperl-jenkinsというスクリプトも同梱したので使ってみてください。

CLIでのメトリクス測定

基本的な使い方としてはは、Perlのモジュールの入ったlibディレクトリを指定して使います。

measureperl lib

Metricsの閾値を超えた物だけを表示したい場合は、

measureperl -e lib

として実行します。
閾値のデフォルト値は、slocが60、mccable complexityが10になっています。あまり厳しすぎない程度の値なので、デフォルト値のままで使ってもよいんじゃないかなと思います。

以下は、Perl::Metrics::Liteモジュールの測定結果です。

次のように閾値も変更可能ですので、プロジェクトでの許容範囲にあわせて閾値をっ設定してみてください。

bin/measureperl -e -l 30 -c 6 lib

Jenkinsへの組み込み

CheckStyleに変換するためのReportクラスもあるので、以下のような形でJenkinsには、以下のようにcheckstyleの結果を出力することで統合が可能です。先日のJenkinsへのCheckStyleへの統合を、汎用なツールにしてみました。エラーで引っかかった行数のジャンプもできるようにしてあります ;)

measureperl-checkstyle --max_sub_lines 60 --max_sub_mccabe_complexity 10 --directory lib > checkstyle-result.xml

以下でインストールできるので、試してみてください。

cpanm Perl::Metrics::Lite

Enjoy measuring!

3分でPerlのプロジェクトをJenkins Readyにする

こんばんは、着る毛布が届く前に風邪を引いたdannです。

色々な環境やプロジェクトに携わっていて、その度にJenkinsのセットアップやPerlプロジェクト用のモジュールのインストールなどをしていたりしませんか。また、プロジェクトを作るたびに、Job毎にプラグインの設定などを繰り返していたりしませんか?

これらの設定は、完全に自動化することが可能です。Jenkinsのように良く使うツールは、これらのセットアップも自動化していくと捗ります。以下では、その方法を説明します。

以下に、Jenkinsプロジェクトの開始に必要なテンプレートを用意したので、参考にしてみてください。以下では、以下のtemplateを使ったセットアップ方法について説明します。
https://github.com/dann/perl-jenkins-template

Jenkinsのプロジェクトに必要なPluginのセットアップの自動化

Jenkinsでは、GUIだけでなく、jenkins-cliを使うことでCUIでインストールすることができます。
これを使って以下のようなスクリプトで、JenkinsのPluginとJenkinsの統合に必要なPerlのモジュールをインストールします。
これによって、プロジェクトに必要なプラグインPerlのモジュールは自動でインストールできます。

https://github.com/dann/perl-jenkins-template/blob/master/setup_jenkins_modules.sh

#!/bin/bash

#==================================
# Configuration
#==================================
HOSTNAME=localhost
PORT=8080

#==================================
# Main
#==================================
JENKINS_PLUGINS=(
git
checkstyle
htmlpublisher
clover
plot
)

PERL_MODULES=(
Devel::Cover
Devel::Cover::Report::Clover
Perl::Metrics::Lite
https://github.com/dann/tap-to-junit-xml/tarball/master
)

setup() {
    fetch_jenkins_cli
    install_jenkins_plugins
    install_jenkins_related_perl_modules
    restart_jenkins
    clean_jenkins_cli
}

fetch_jenkins_cli() {
    curl -LO http://${HOSTNAME}:${PORT}/jnlpJars/jenkins-cli.jar
}

jenkins_cli() {
    java -jar jenkins-cli.jar -s http://${HOSTNAME}:${PORT} $@
}

install_jenkins_plugins() {
    for plugin in ${JENKINS_PLUGINS[@]}
    do
        jenkins_cli install-plugin ${plugin}
    done
}

install_jenkins_related_perl_modules() {
    for module in ${PERL_MODULES[@]}
    do
        cpanm $module
    done
}

restart_jenkins() {
   jenkins_cli safe-restart
}

clean_jenkins_cli() {
    rm jenkins-cli.jar
}

setup

Jenkinsのproject-templateを用意しておくと捗るぞ

概要

多くののLLのプロジェクトの場合は、シェルのステップを設定しているか、実行するためのbuild.xmlを用意しておいて、ビルドのステップのセットアップをして使っているかなんじゃないかと思います。こういったプロジェクトのステップの設定やどのプラグインを使うか、プラグインの設定など、そういったプロジェクトの設定を手動でするのって面倒ですよね。

要するに既存Job設定をコピーすることで、既存の設定を使い回せる機能があるので、それを使うということですね。

これによって、Clover用のプラグインディレクトリでcover_dbのディレクトリを指定したり、Devel CoverのHTMLの結果をHTML Publisherなどでpublishするディレクトリを指定したり、Publish Checkstyle analysis resultsなどの実行するプラグインをチェックしたりといったのを手動ですること無く、自動で設定された状態にすることが可能になります。

Job Templateのインストールの仕方

以下の定義が実際のjobのテンプレートになります。以下に、上記のビルドステップのスクリプトとプロジェクトで必要となるプラグインの設定が有効になるjobテンプレートを用意しておきました。Job定義はXMLでJenkinsのHomeディレクトリ(ex /var/lib/jenkins)以下のjobsディレクトリに保存されているので、既存Jobでよく使う部分はテンプレートのXMLとして定義して保存しておきます。
https://github.com/dann/perl-jenkins-template/blob/master/config.xml

上記のテンプレートを自動インストールするスクリプトは、以下になります。Configセクションの変数を各自の環境にあわせて使ってみてください。
https://github.com/dann/perl-jenkins-template/blob/master/setup_jenkins_job_template.sh

Job Templateの使い方

上記のインストールが終わった後に、新規プロジェクトを作ります。新規プロジェクトを作る場合には、Jenkinsで、New Job -> Copy existing job を選択し、そこで、perl-jenkins-templateを選択することで、jenkins readyなperlプロジェクト用のJobを作ることができます。

Jobテンプレートの適用になり有効になるもの

上記のJob templateをインストールすると、以下のものがデフォルトで有効になります。

  • JUnitとの統合によるテスト実行結果
  • Cloverの統合によるテストカバレッジの可視化
  • Devel::CoverのHTMLレポート
  • Perl::Metrics::LiteによるCheckStyleとの統合

ビルドの結果としては、以下のような形で表示されます。

このJob Templateによって、プラグインだと次のようにCheckStyle, Clover, JUnitなどのプラグインの設定がされます。

また、ステップについては、以下のような設定が自動設定されます。

set -ex

#===============================================
# Configuration
#===============================================
export PATH="/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:${PATH}"                                                      
export LOG_DIR=$WORKSPACE/logs
export PERL5LIB="$WORKSPACE/lib:$WORKSPACE/t/lib:$WORKSPACE/t/*/lib:${PERL5LIB}"
export PERL_TEST_HARNET_DUMP_TAP=$LOG_DIR
export TEST_VERBOSE=1

#===============================================
# Main
#===============================================
clean() {
    [ -f Makefile ] && make -k realclean
    rm -rf Makefile Makfile.old MANIFEST blib *.tar.gz
    rm -rf $LOG_DIR
    cover -delete
}

setup() {
    perl Makefile.PL
    mkdir -p $LOG_DIR
}
do_test() {
    HARNESS_PERL_SWITCHES=-MDevel::Cover=+ignore,inc prove -lv t 2>&1 | tee -a $LOG_DIR/tests.tap
}

convert_to_junit_xml() {
    tap-to-junit-xml --input=$LOG_DIR/tests.tap --output=$LOG_DIR/tests.xml
}

make_coverage() {
    cover -report clover
    cover
}

measure_metrics() {
    measureperl-checkstyle --max_sub_lines 60 --max_sub_mccabe_complexity 10 --directory lib > checkstyle-result.xml
}

main() {
    clean
    setup
    do_test
    convert_to_junit_xml
    make_coverage
    measure_metrics
}

main

まとめ

まとめると、

  • JenkinsのPluginやプロジェクトに必要なモジュールのインストールを自動化しておくと捗るぞ
  • JenkinsのJobでプロジェクトに共通な設定は、あらかじめJob定義のXMLをテンプレートを用意しておき、Copy existing Jobでコピーすると設定を使い回せて捗るぞ

ということになります。

使うプラグインや設定など、プロジェクトによって異なる点もあると思いますが、このようなベースを用意しておくことで、いわゆる定型作業を減らすことができ、プロジェクト間でのばらつきを抑えることが可能になります。是非、このようなテンプレートをベースにして、各自のプロジェクトに合わせたJenkinsのJobテンプレートを作ってもらえたらなと思います。

これらは、Perlのプロジェクトだけで使える話ではなく、他の言語でも使える話ですので、是非自動化して、プロジェクトレベルでの標準化と自動化を進めてみてください。

Enjoy automating!

Perl::Metrics::Lite - プラガブルなメトリクス測定モジュール - Perl Advent Calendar 2011

Perl Advent Calendar Hacker Trackの21日目です。
http://perl-users.jp/articles/advent-calendar/2011/hacker/21

こんばんは。dann です。みなさん、意識は高まっていますか? 僕は上々です。
今回は拙作の Perl::Metrics::Lite というモジュールを紹介させて頂きます。

開発した動機

Perl::MetricsやPerl::Metrics::Simpleなど、PerlのコードのMetricsを測定するツールがあるのですが、どれも使い勝手の点でいまいちです。

Perl::Metricsは測定項目をプラガブルに追加可能なのですが、APIがプリミティブすぎてMetricsを測定するには使いずらいです。

一方、Perl::Metrics::Simpleは、Perl::MetricsをよりユーザーフレンドリなAPIにして、名前の通りシンプルに使えるように作られているのですが、今度は測定項目が拡張可能になっておらず、特定のメトリックしか測定できませんでした。

Perl::Metrics::Lite - プラガブルなメトリクス測定モジュール

従来のモジュールの問題点を解決するために、Perl::Metrics::SimpleとPerl::Metricsのいいとこどりをして、測定項目をプラガブルに追加できるPerl::Metrics::Simpleのような使い勝手のいいモジュールを作ってみました。
http://github.com/dann/p5-perl-metrics-lite

使い方はPerl::Metrics::Simpleと殆ど同じで、以下のように使います。

use Perl::Metrics::Lite;
my $analzyer   = Perl::Metrics::Lite->new;
my $analysis   = $analzyer->analyze_files(@ARGV);
my $file_stats = $analysis->file_stats;
my $sub_stats = $analysis->sub_stats;

egディレクトリに実行可能な簡単なサンプルをいれてあるので、
是非実行して確認してみてください。
https://github.com/dann/p5-perl-metrics-lite/blob/master/eg/measureperl

測定項目の拡張方法

以下に幾つかFileとSubroutineのMetricsの測定のPluginのサンプルをいれています。
initとmeasureメソッドを実装する必要があります。measureメソッドのfileまたはsubroutineのPPI::Documentを操作して、必要なMetricsの計算を行います。

まとめ

Perl::Metrics::Liteにより、簡単にFileとSubroutineのMetricsを拡張して、測定項目を追加していくことが可能になりました。是非色々なMetricsの測定をしてみてください。

Enjoy measuring!

Jenkinsで継続的メトリクス測定のすすめ - Perl Advent Calendar 2011

Perl Advent Calendar Test Trackの11日目です

はじめに

こんばんは、家で凍死しそうなので、そろそろセラムヒートでも買おうと思っているdannです

Test Track 11日目です! ikasam_a さんから「Jenkinsの話を書いて!」と言われたので、ACDD(Advent Calendar Driven Development) という手法で作った、メトリクス測定ツールとJenkinsへのインテグレーションについての話をします。

メトリクスを測定すると捗るぞ

大きなチームで開発する時に、数百行の謎メソッドができたりとかあったりしますよね(涙)

僕は、Working Effectively With Legacy CodeやClean Codeを読んでいること前提にチームの人がコードを書いていると思っていたら、あれ?という場面があったりします。こういうときは、品質の問題を測定できるようにすると、数値レベルで具体的にした共通認識ができ、レビューが捗ります。

今回測定したメトリクス

メトリクスを測定するなんていうと、用語の定義が間違ってる喝!とSoftware Engineeringの専門家に怒られちゃうわけですが、そこは気にせずメトリクスを測定って言うことにします。

今回測定したのは、以下の二つのメトリクスです。

  • メソッドの行数
  • Cyclomatic Complexity

メソッドの行数は、そのまんまですね。Cyclomatic Complexityは10を超えていると複雑すぎなので、分割を検討した方が良いというのが一つ一般的な指標になってます。

これらのメトリクスは、Perl::Metrics::Simpleというモジュールで簡単に取得できます。ではこれをJenkinsに統合しましょう。

Jenkinsでのメトリクス監視の方法

これらのメトリクスの測定結果をどのようにJenkinsに統合するかです。今まで、自分の日記のJenkinsのエントリを読んでもらっている人は、勘のいい方は気づいてるんじゃないかと思うんですが、Javaのツールにマッピングすればいいんですね。

この分野は、静的言語でもあることから、Javaは周辺ツールがとても充実しており、今回はそのうちの一つであるCheckStyleXMLに測定結果をマッピングしてみました。これにより、チェック結果のViewをそのまま流用できるわけです。

作ってみたマッピングするツール pm-checkstyle は以下の通りです。
https://github.com/dann/p5-app-checkstyle

使い方

上記のスクリプトのconfigurationの設定を最初にします。
CCが10、sub lengthが50行という設定だと、以下のように書きます。

our $MAX_CYCLOMATIC_COMPLEXITY = 10;
our $MAX_SUB_LENGTH            = 50;

以下のようにスクリプトの引数にlibディレクトリを指定します。そうすると、workspaceにcheckstyle-result.xmlが生成されます。

pm-checkstyle lib

後は、Jenkinsにcheckstyle pluginをいれておけば、ビルド時に自動でcheckstyleのViewにメトリクスでの測定結果が表示されます。

check styleでのチェック結果

ビルド結果には次のようにcheck styleでチェックに引っかかった項目がひっかかります

エラー数はファイル毎に次のようにみることができます

ファイルをクリックするとエラー詳細は次のように表示されます。

おまけ

以下のようにPerl::Metrics::Simple::Analysis::Fileを変更することでメソッドの行数を取得することが可能になります。上記のpm-checkstyleでは、line numberは1という固定値になってますが、以下の変更を加えてcheckstyle.plのテンプレートを変更すれば、行数のマッピングも出来ます。そうすることで、check_styleのDetailのレポートから直接ソースコードの該当メソッドにジャンプすることができるようになります。

--- Perl/Metrics/Simple/Analysis/File.pm.orig	2011-12-10 13:49:45.000000000 +0900
+++ Perl/Metrics/Simple/Analysis/File.pm	2011-12-10 13:34:52.000000000 +0900
@@ -327,6 +327,7 @@
             name              => $sub->name,
             lines             => $sub_length,
             mccabe_complexity => $self->measure_complexity($sub),
+            line_number       => $sub->line_number,
             };
     }
     return \@subs;

メトリクス測定の効果

メトリクスの測定をCIに組み込むことの効果には以下のようなものがあります。

  • 継続的にメトリクスをチェックすることができ、問題を早期に発見できる
  • Jenkinsおじさんが自動でチェックしてくれるので、レビューの工数が削減できる
  • 指摘をJenkinsおじさんがしてくれるのでレビュー時に人間関係が悪化しない
  • Jenkinsおじさんに指摘されないように気をつけるため、全員のコードが綺麗になる

まとめ

メトリクスの測定で、レビュー対象を早期に摘出するとコード品質をあげることが可能になります。早期に問題を見つけるのは、テストやビルドだけでなく、こういった静的解析による機械的なレビューによってもあげることが可能なわけです。

これらのメトリクスによる定量評価もCIに組み込むことでその価値を発揮します。性能だけでなく、品質もある一定線は定量評価が可能であり、人感じるのではなく、測定によって定量評価をすすめることで、改善効果を実感することができます。

CheckStyleのチェック項目は数多く存在しますが、Perl::Metrics::SimpleをベースにしてJava版と同じようにモジュール化をすることで、より高度なチェックも可能になります。興味のある方は是非トライしてみてください。

2003年頃からCruise Control -> Continuum -> Hudson (Jenkins) とCIシステムを使ってきたわけですが、当時はJava界隈ではよく使われてましたが、あまりPerlのプロジェクトでは使われていませんでした。今では、Jenkinsの素晴らしいQualityから、Perlのプロジェクトでも大分使われるようになってきていますね。遅筆ではありますが、今後もJava界隈で今までに培ったCIの使い方のノウハウを、Perlのプロジェクトでも活かす方法を書いていきたいところです。

では、Enjoy Jenkins!

次回は、tsucchi さんです!お楽しみに!

# 追記
現在は、拙作のPerl::Metrics::Liteのツールを使うことで、以下のようにJenkinsに統合することが可能になっています。

measureperl-checkstyle --max_sub_lines 100 --max_sub_mccabe_complexity 10 --directory lib > checkstyle-result.xml

Javaの標準Collectionフレームワーク代替としてのfastutil/HPPCの使用のすすめ

Goな人が最適化しているエントリ(http://blog.golang.org/2011/06/profiling-go-programs.html
)を読んで、ちょっと面白いなと思ったので、元の論文を読んでみました。読んでみたところ、Java 64bit版は標準のC++の5.8倍遅いとなっていたので本当かな?と思い、元のプログラムを見てみました。
http://research.google.com/pubs/pub37122.html

プログラムをざっと見て、オリジナルのJavaの標準のコレクションフレームワークの使用時の罠にはまっていることに気づいたので、以下のチューニングを施してみました。

  • -XX:+UseCompressedOopsオプションを使っても、Integerなどのオブジェクトはプリミティブ型の数倍のメモリ消費量を使ってしまうことから、整数オブジェクトの配列ではなく、プリミティブ型専用のコレクションを使うこと
  • 内部のストレージにダイレクトにアクセスが可能なオーバーヘッドの少ないコレクションを使うこと
  • Iteratorでのアクセスが遅いことから、拡張for文を使うのではなくリストにダイレクトにアクセスすること

たった数十行の修正で、C++のバージョンよりも約2割ほど早くすることができました。また、このコードは、オリジナルのmulti-language-benchmarkプロジェクトのJavaでの最適化バージョン(java-pro)のものよりも1割程度高速です。測定環境(CPU, Memory, Javaのversionなどなど)により性能値は変わるので、測定可能なコードを以下においておきました。
https://github.com/dann/java-multi-language-benchmark

ここで使ったのはfastutilなんですが、fastutil/HPPCは、Javaのコレクションフレームワークに近い(一部互換の)APIを持つ、上記のことを実現するのに適したコレクションフレームワークで、比較的簡単に標準コレクションフレームワークを使ったコードの置き換えが可能です。上記のような理由で、Javaのコレクションフレームワーク使用時に性能が出せない場合にはとてもお勧めできます。

また、Javaで性能を出すためのプログラムをどのように書けばよいのかという点で、とても学ぶことの多いソースであるので、アプリケーションとしてのJavaのコードを読み飽きた方にも非常におすすめです。何故Javaのコレクションフレームワークが遅いのかをJDKのコード、メモリの使用量などを調べながら読み比べてみると面白いです。

# YourKitでプロファイルした感じだと、もう少しチューニングできそうなので、暇な時にもう少しやってみようかと思ってます。