torutkのブログ

ソフトウェア・エンジニアのブログ

Bootchartコマンドの実行エラーとJavaのバージョンとOpenJDK

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の実行

Bootchartカーネルオプションで起動後、任意のユーザでログインし、bootchartコマンドを実行すると、Linuxブート時の詳細な情報がPNG形式の画像でユーザのホームページ下に生成されます。

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と少々古いものしかありません。

OpenJDK

OpenJDKが[epel]yumリポジトリからインストール可能なので、これをインストールすると、jpackageの仕組みに載る最新Javaバージョンが利用可能になると思われます。
本日時点で、java-1.6.0-openjdkというパッケージ名で、バージョンが1.6.0.0-1.0.b12.el5.2となっています。