CentOS 5.2にBootchartを入れて、Linuxの起動時間の詳細を調べようとしました。マシン起動時にbootchartログ版カーネルオプションを選択し、起動後、bootchartコマンドを実行するのですが、1台目のマシンは正常に見れましたが、2台目のマシンでは、bootchartコマンド実行時にエラーとなりました。エラーはJava関係のエラーでバージョンが合わない(ClassFormatError)というもの。問題の調査・解決を試みました。
Bootchartのインストールと起動
まずはBootchartのホームページよりソースRPM形式のパッケージを入手し、rpmbuildでRPMパッケージを作成、これをrpmでインストールしました。
ソースRPMからRPMパッケージの作成
rpmbuild時に必要とされるパッケージが、antとjakarta-commons-cliなので、インストールされていなければインストールします。(実はここに原因があったのですが・・・)
rpmbuildでソースRPMからRPMパッケージを作成します。
$ rpmbuild --rebuild bootchart-0.9-1.src.rpm : $ cd ~/rpm/RPMS/noarch $ ls bootchart-0.9-1.noarch.rpm bootchart-javadoc-0.9-1.noarch.rpm bootchart-logger-0.9-1.noarch.rpm $ sudo rpm -ivh bootchart-* : $
RPMパッケージでインストールすると、/etc/grub.confにBootchartオプション指定のあるカーネル起動設定が追記されます。マシン起動時にgrubのメニューで"Bootchart logging"という名称のカーネルを選択します。
bootchartコマンドのエラー
1台目(RPMビルドしたマシン)は無事生成されたのですが、2台目(RPMパッケージをインストールしたマシン)ではbootchartを実行したところ、以下のエラーが発生しました。
~$ bootchart Exception in thread "main" java.lang.ClassFormatError: org.bootchart.Main (unrec ognized class file version) at java.lang.VMClassLoader.defineClass(libgcj.so.7rh) at java.lang.ClassLoader.defineClass(libgcj.so.7rh) at java.security.SecureClassLoader.defineClass(libgcj.so.7rh) at java.net.URLClassLoader.findClass(libgcj.so.7rh) at gnu.gcj.runtime.SystemClassLoader.findClass(libgcj.so.7rh) at java.lang.ClassLoader.loadClass(libgcj.so.7rh) at java.lang.ClassLoader.loadClass(libgcj.so.7rh) at gnu.java.lang.MainThread.run(libgcj.so.7rh) ~$ java -version java version "1.6.0_10" Java(TM) SE Runtime Environment (build 1.6.0_10-b33) Java HotSpot(TM) Client VM (build 11.0-b15, mixed mode, sharing) ~$
エラーメッセージから読み取れることは、以下です。
- org.bootchart.Mainクラスのファイルバージョンが認識不可なため、クラスをロードできない
- JavaVMが、GCJのものになっている
Javaのバージョンが違うらしいのですが、環境変数PATHでは最新のjava 1.6.0が動くようになっています。なぜ、GCJが動くのか?
bootchartのJava起動の仕組み
まずは、bootchartを調べます。
$ which bootchart /usr/bin/bootchart $ file /usr/bin/bootchart /usr/bin/bootchart: Bourne shell script text executable $
幸いにも、シェルスクリプトです。ファイルの中を見ると、以下のことが分かります。
- /usr/share/java-utils/java-functions をsourceしている
- java起動のmainクラスにorg.bootchart.Mainを指定している
- クラスパスに、commons-cli.jarとbootchart.jarを指定している
- 起動オプションに-Djava.awt.headless=true -Dgnu.java.awt.peer.gtk.Graphics=Graphics2D"を指定している
/usr/share/java-utils/java-functionsとは何か
まず、/usr/share/java-util/java-functionsが属するパッケージを調べます。
$ rpm -qf /usr/share/java-util/java-functions jpackage-utils-1.7.3-1jpp.2.el5 $
jpackage-utilsは、RedHat系のLinuxで、複数のバージョンのJavaを使い分けるためのOS標準のユーティリティです。CentOS 5.2ではjpackage-utils-1.7.3-1jpp.2.el5が標準提供されています。
java-functionsの中身を調べると、パス、JavaVMオプション設定を記述する/etc/java/java.confの読み込み、Java実行コマンドなどの制御をしています。
環境変数_JAVA_HOMEを定義していると、このスクリプトの設定をオーバーライドできるようなので、SunのJDKを入れているなら、/usr/java/jdk1.6.0_10などを設定すればいいようです。
jpackageの開発元:: JPackage Project | Home ::には、SunのJDKをこのjpackageの仕組みでインストールする記述が掲載されています。
しかし、JDK1.6.0については、用意されているパッケージがUpdate6と少々古いものしかありません。