« 2008年3月 | メイン | 2008年5月 »

2008年4月30日

Javaコードのバグを発見するFindBugs
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。中村です。

Javaコードをコンパイルしたクラスファイルからバグを見つけ出すFindBugsというツールを使ってみました。

FindBugsは単体で動作しますが、IDEのプラグインとしても提供されています。今回はEclipseのプラグインを試してみました。

プラグインの導入

SourceForgeのダウンロードページからedu.umd.cs.findbugs.plugin.eclipse_1.3.3.20080401.zipを取得して 解凍、Eclipseディレクトリのpluginsに置くだけといういつも通りな感じです。

使い方

プロジェクトのプロパティから通知する問題を選択したりと色々設定が出来ますが、単純に使うにはJavaプロジェクトのところで右クリック -> Find Bugs -> Find Bugsをクリックすると プロジェクトを調べて問題箇所にマーカーを付けてくれます。

使ってみる

早速無茶なコードを書いてみました。

FindBugs
FindBugs posted by (C)フォト蔵

すると、このようにバッチリと問題点が指摘されました。実行してNullPointerExceptionの発生箇所の行数を見るよりはこちらの方が一目瞭然で判りやすいと思います。

次に書いてはいけないコードの類をやってみました。

package net.unoh.findbugstest;

public class FindBugsTest {

    public static void main(String[] args) {
        Thread thread = new Thread();
        thread.run();
    }

}

すると次のように問題点が指摘されました。

[Ru] スレッドの中で run を実行しています。(本当は startを代わりに呼び出すべきではないのですか?)。 [RU_INVOKE_RUN]

このメソッドは、オブジェクトのrun()メソッドを明示的に呼び出しています。一般にRunnableを実装したクラスは、新しいスレッドがrun()メソッドを呼び出す事を期待しており、この場合、Thread.start()を呼び出すのが正しいやり方です。

こうした説明文がしっかりと表示される(しかも日本語で)点からすると、教育目的で使うのも良いかもしれません。

他に同じようなことをするものにLint4jなどがあるようなので、今度比較をしてみたいなと思います。

2008年4月28日

MacPorts の使い方と開発方法について
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは、naoya です。

先日、社内の勉強会で MacPorts の使い方から Ports の追加方法まで幅広く紹介しました。MacPorts は、FreeBSD の ports が元になっていると聞いていましたが、バイナリバッケージまで作れるとは知りませんでした。

詳しくは、資料を参照してください。

なお、資料の中で Ports で追加している bat というプログラムは、Global standard programming with GNU Autotoolsで紹介されているプログラムです。

Read this doc on Scribd: MacPorts

2008年4月25日

PHPでDocTest
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

yamaokaです。

何かソースコードを書いた場合、皆さんはどのようにテストをしていますか?  PHPの場合、PHPUnitSimpleTestを使ってユニットテストをすることが多いと思います。でも、ユニットテストのために新しいファイルを作ってメソッドを実装したりするの、面倒くさくないですか?

Pythonには標準でDocTestという仕組みが付いています。詳細はPythonのドキュメント日本語)を参照してもらうことにして、簡単に言うと、実際のソースコードの中にコメントとしてテストケースを書いてしまおうというアイデアです。

「実際のソースコード=テストケース」になるのですから、メソッドを修整したからテストケースも修整して…という手間が少なくて済みますよね。そうしたDocTestの仕組みを、PHPに移植しようとする試みを紹介します。

rhacoのDocTest

rhacoは「PHPのライブラリパッケージ/ セットアップアプリケーション」です(公式サイトより)。rhacoのソースコード自体にDocTestが記述されているので、サンプルとして見るにはちょうどいいでしょう。webアプリケーションフレームワークとして利用する以外に単にライブラリとして利用することもできるので、rhacoを利用していないソースコードのDocTestをrhacoを使って行うことも可能です。

さっそくDocTestを試してみましょう。次のようなFooクラスを用意します。

<?php
class Foo
{
    function bar()
    {
        /*** eq('bar', Foo::bar()); */
        return 'bar';
    }
    function baz()
    {
        /*** #pass */
    }
}
/*** … */」で囲まれた箇所がDocTestのテストケースとして処理されることになります。今はテストしないとか、テストコードをまだ書いていない場合は「#pass」と書いておけばスキップされます。

次に、テストを実行するためのtest.phpを準備します。

<?php
require_once 'path/to/rhaco/Rhaco.php';
Rhaco::import('util.DocTest');
DocTest::execute('Foo.php');

そして、test.phpを実行してみます。

$ php -q test.php
success: 1 / fail: 0 / pass: 1 / none: 0 / all: 2

(Foo::baz:line. 9)PASS Foo.php


テストが実行されましたね! 試しにFoo::barのテストコードを「/*** eq('barrrr', Foo::bar()); */」に書き換えて実行しなおすと、次のようにきちんとエラーになります。
$ php -q test.php
success: 0 / fail: 1 / pass: 1 / none: 0 / all: 2

(Foo::bar:line. 4)FAIL expectation [string(6) "barrrr"
] : Result [string(3) "bar"
]
(Foo::baz:line. 9)PASS Foo.php


値の一致をテストする「eq」コマンド以外にもテスト用のコマンドが用意されているので、詳しくはrhacoのソースかドキュメントを参照してください。

その他のDocTest

その他のDocTestの試みとしては、現在次のバージョンが開発されているwebアプリケーションフレームワーク、MapleDocTest導入の試みがあげられます。また、同じくwebアプリケーションフレームワークのsymfonyでも、sfDocTestPluginが開発されています

PHPのライブラリ集、PEARでは、まだアルファ版ですがTesting_DocTestがリリースされています。

最後に

rhacoのDocTestを中心に紹介しましたが、いかがでしょうか。テストコードを記述する負担をできるだけ軽くする試みとして、DocTestは面白いと思います。反面、ソースコード中にテストコードが混在するので、ソースコードが見づらくなるといった欠点もあります。そうした場合、従来のユニットテストと併用するのも手ですね(rhacoの場合、DocTestからユニットテストを実行できます)。いろいろなテストコードの書き方がありますが、選択肢の一つとして考えてもよいのではないでしょうか。

2008年4月24日

rpmパッケージを作ろう
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

尾藤正人(a.k.a BTO)です

先日社内勉強会でrpmパッケージの作り方についてやってみました。
資料を公開しておくのでよろしければご参照ください。

Read this doc on Scribd: rpm

参考用に以前GNU Autotools用のサンプルプログラムで作ったbatのrpmパッケージを使いました。
次のようなコマンドでrpmパッケージを作成できます。
基本的な機能をある程度網羅したつもりなので、参考になれば幸いです。

rpmbuild -ta bat-0.0.3.tar.gz

bat-0.0.3.tar.gz

2008年4月22日

CSSによるデザインワークと相性のよいHTMLって?
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

yamazakiです。最近だいぶあたたかくなってきましたね。おかげで日中眠くて仕方ないわけですがいかがお過ごしでしょうか。

ウノウに入る以前も含めてそれなりに長いことHTMLとCSSを書いてきたわけですが、今回は試みに、「だいたいこういうところに気を使われたHTMLだと、CSSでのデザイン適用やレイアウトがやりやすいな」というこれまでの経験則を簡単ですがまとめてみたいと思います。

まあ、このあたりはCSS書く人とHTML書く人の間でちゃんとルールを決めておけばいいだけの話なので、そもそも何の役に立つのか疑問といえば疑問ですが(笑
たとえばユーザがCSSを書いてスキンを作れるようなサービスを作る際、どういったHTMLにするかを決める、みたいな時には少し参考になる、かもしれません。

ID、クラスを適切に割り振って、要素がCSS側から一意に特定できるように

たとえばグローバルナビゲーションとカテゴリー別のナビゲーションが同じulで書かれているとして、どちらがどちらかCSSで特定できないと、別の見た目にしようとしても難しいです。

<ul>
<li>グローバル</li>
<li>グローバル</li>
</ul>

<ul>
<li>カテゴリ内</li>
<li>カテゴリ内</li>
</ul>

なので可能な限り「表示される内容に共通項のない部分」にはそれぞれID割り振るなどして特定できるようにします。

<ul id="gNavi">
<li>グローバル</li>
<li>グローバル</li>
</ul>

<ul id="catNavi">
<li>カテゴリ内</li>
<li>カテゴリ内</li>
</ul>

さらに、「同じ『ナビゲーション』なので」ということで、同じクラスを振っておくと、似たような見た目にしたい場合に便利。

<div id="gNavi" class="navigation">
<ul>
<li>グローバル</li>
<li>グローバル</li>
</ul>
</div>
<div id="catNavi" class="navigation">
<ul>
<li>カテゴリ内</li>
<li>カテゴリ内</li>
</ul>
</div>

もちろん、たとえば「ページ内にH1タグは一つしかない」というポリシーが全ページできちんと貫かれていれば、H1タグに余計なIDをつけたりしなくても問題ないと思います。
大事なのは「ちゃんと別々のものだと区別できること」です。

同じようなものには同じクラスを割り振る

IDはその名の通り、ページ内に唯一のものに割り振っておくべきものです。一方で「クラス」は、同じようなものを「これとこれは同じようなものですよ」ということを表す場合に使います。
似たものはやはり似たような見た目にしたい場合が多いので、似たような要素にはできるだけ同じクラスが割り振られているほうが好ましいと思います。
逆に、たとえば同じような内容なんだけどちょっとだけ違うものに、全く別のクラスが指定されていると色々と面倒です。
たとえば、ブログの新着記事のリストと人気記事のリストがあったとして、

<ul class="recentPostList">
<li>記事1</li>
<li>記事2</li>
</ul>

<ul class="popularPostList">
<li>記事i</li>
<li>記事ii</li>
</ul>

としてしまうと、二つのリストに同じ見た目を指定したいときに、

ul.recentPostList,
ul.popularPostList{}

といちいち書かなくてはいけなくなります。※まあ2つくらいだったらいいのですが、これが多くなってくるとだいぶ残念な感じになっていきます。
こういう場合は、クラスはスペースで区切って複数指定できますので、たとえば

<ul class="postList recent">
<li>記事1</li>
<li>記事2</li>
</ul>

<ul class="postList popular">
<li>記事i</li>
<li>記事ii</li>
</ul>

とすると、CSSでは、共通のスタイルを「ul.postList」で指定し、それぞれちょっと別の見た目にしたければ「ul.recent/ul.popular」に指定、という形でスマートに書きやすくなりますし、メンテナンス性もよくなると思います。

tableのtrごとに「odd」「even」

テーブルは特に大きくなると見にくくなりがちです。
1行おきに色などを変えられるようにする意味で、テーブルの行ごとにクラスをなにかしら交互に指定してあると、スタイル割り当てがしやすくなります。
同じようにリスト系要素(li)など繰り返される要素にも割り当ててあると便利かも。

リスト系要素(ul,ol,dl)の最初と最後が特定できるようにしておく

:first-childなどの擬似要素がすべてのブラウザでちゃんと使えればこんなものはあまり必要ないのですが、IE6などの対応状況を考えると現実問題としてまだまだ使えません。
ので、リストなどの同じ要素の連続するものの場合は、できるだけ最初と最後に「これが最初の要素」「これが最後の要素」だということがわかるようにしておきます。

<ul>
<li class="first">xxxx</li>
<li>xxxx</li>
<li>xxxxx</li>
<li class="last">xxxx</li>
</ul>

なんでこういうことが必要になるかというと、

  • 最初の要素だけマージンを狭く/広くしたい
  • 最後の要素だけマージンを狭く/広くしたい
  • 最初の要素のところにだけ背景画像指定したい
  • インライン化して横並びのメニューにして、間に線を入れたいんだけど全部のli要素に線を割り当てると一本余計になるので消したい

などなど、最初/最後の要素はちょっとだけ違うスタイルにしたいことが結構よくあるからです。

h1~h6タグ直下には<span>か<a>タグが必ず入るようにしておく

「見出し」はやはり「画像に差し替えたい」とか、「特別な装飾を施したい」とかいったことがよくあります。
また、文字の書かれてる部分だけに装飾したい場合や、margin/paddingのコントロールの都合で子インライン要素があるほうが調整しやすくなります。
といったわけで、直下に<span>もしくは<a>タグなどのインライン要素が入るようにしてあると、CSSでのデザインの幅がだいぶ広がると思います。

<h1>hogehoge</h1>

ではなく

<h1><span>hogehoge</span></h1>

その他にもCSSの猛者の皆様的に「こうなっていると嬉しい」という例あると思いますので、もし何かあればぜひ教えてください!

2008年4月21日

PythonのORMを研究してみる(1)
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

Pythonで利用できるORMをざっと列挙してみると、次のようなものがあげられます。

実際に利用するとなると、ドキュメントの充実度とユーザ数でSQLAlchemyに一日の長があり、すでにPythonのORMの標準になったという感があります。

また、Djangoアプリケーションという点に限れば、Djangoに含まれるORM(django.db)を使うということになるでしょう。

しかしながら、必要とする機能や実行速度、生成するSQL、APIの好みといった点で、SQLAlchemyとDjango以外のライブラリを選択することもありうるでしょう。

実行速度という観点からみると、少し古い記事になりますが、下記のURLの記事が参考になります。

各ORMの特徴や実装、またそのORMを採用する理由というのは非常に興味深いものがあるので、今後何回かに分けて、上記のORMを紹介していきたいと思います。

というわけで、次回はMailmanの次期バージョンで採用されるらしい、Stormを紹介する予定です。

2008年4月 9日

テスト担当者のモチベーション
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは!やまもと@テスト番長です。

一般人に向かって自己紹介するとき、
「一応サラリーマンです。WEBサイト作ったりとかしてます。」「専門はテストです。」というと
「出来栄えをチェックする人だから、エライ人なんですねー」
と若干良い方に誤解されがちですが、
同業者に「専門はテストです。」というと「あー、大変っすよねー」と必ず同情されます。

テストというのはどうもモチベーションが上がりにくいお仕事のようです。
今回は来るべき五月病シーズンに向けて、特に新人に近い立場の方がモチベーションを失わずに居られる方法を幾つか考えてみましょう。

テスト担当を押し付けられたとき

新人を安易にテスト業務に割り当てるケースがあります。 新人はまだ経験と信頼性が足りない故に他の作業で使いづらく、そうなりがちです。

もしプログラミングの方に興味があるなら、そういう意向をアピールしておくべきです。
いつ頃プログラミングをさせてもらえるか、見通しを上司と話し合っておきましょう。

その上で今は経験を積むためだと割り切ってしまうのがいいでしょう。
テストのやり方を覚えるのも相当大事なことですし、テストしていても仕様書もコードも見られるし、決して退屈はしないハズ。
#いきなりプロジェクトが炎上してて退屈どころじゃないかもしれませんが。

要領良くこなすとスッキリ。

テストケースの出来具合によっては、まとめてチェック出来る項目が大量にあったりします。 要領良くテストを実施する方法を常に考えましょう。単純作業はなるべく排除しましょう。 やりがいのあるテスト項目に己の時間と精神力を集中させましょう。

攻めの姿勢に出る

用意されたテストケースをただ大量にこなしているだけだと飽きることもあるでしょうが、ここは一つ攻めの姿勢でいきましょう。 テストケースは抜けがあったりするものです。普通あります。 実施しながら穴を探しましょう。穴を見つけたらフィードバックしましょう。 レアなバグを見つけたら、周りの人に見せて褒めてもらうのがいいですね。 チームから頼りにされるようになりましょう。

スキルアップを考える

同じようなテスト作業ばっかり日々繰り返していると、成長できていないことに気づき、人生このままでいいのか悩みだします。スキルアップを心掛けましょう。 テスト関連の良い書籍や資格制度も出てきていますので、手を伸ばしてみましょう。 仲間がいれば一緒にやるといいですね。

休める時に積極的に休暇を取る

テストに関わる(廻される)人は真面目な人が多いので、休むことを積極的に考えましょう。 神経を使う仕事なのでリフレッシュは重要です。 体を動かしたり、趣味の時間を確保するのもいいですね。

たまにはコーディングする

たまにコーディングすると気分転換になります。 プログラムを(ある程度は)組めないとテストも出来ないので、離れてしまわないようにしましょう。 テスト用のツールなどを作れば仕事も楽になりますよ。

会社でのテスト業務の地位が低くて萎えるなら

テストの重要性を理解してもらえない環境だと、テンションのあがりようがありません。 テストを軽視するような人は所詮素人なので、そんな人達が何を言おうと気にすることはありませんが、心が乾いてしまう前に次の展開を考えましょう。


とにかく、状況に流されて何もしないのは一番よくありませんね。
なんだかテストに限らない気もしてきましたが、よかったら試してみてください。

他にも良い方法があればぜひ教えてください!

2008年4月 7日

LD_PRELOADを使って任意の関数呼び出しにフックしてみる
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

尾藤正人(a.k.a BTO)です

先日の社内勉強会のLTでLD_PRELOADについて簡単にやってみました。

LD_PRELOADって?

環境変数$LD_PRELOADを使うと他のライブラリの読み込みの前に任意のライブラリを先に読み込ませることができます。
実行プログラムの形式にELF形式を採用しているOSで使うことができます。
Linuxであれば問題なく使用できるはずです。

何ができるのか

プログラムを変更することなく、任意の関数を上書きしたり、任意の関数にフックすることができます。

libhookwriteを作ってみた

簡単なサンプルプログラムとしてlibhookwriteというのを作ってみました。
libhookwriteはその名の通りwrite(2)にフックをかけることができます。
といってもできることは限られていてファイルのタイムスタンプの更新か、任意のプログラムをsh経由で実行することしかできません。

ダウンロード

libhookwrite-0.0.1.tar.gz

インストール

お約束通り

tar zxvf libhookwrite-0.0.1.tar.gz
cd libhookwrite-0.0.1
./configure
make
sudo make install

rpmでもいけます。

rpmbuild -ta libhookwrite-0.0.1.tar.gz
sudo rpm -ivh libhookwrite-0.0.1-1.i386.rpm

使い方

環境変数$LD_PRELOADにlibhookwrite.soを指定します。
さらに環境変数$HOOKWRITE_UPDATE_FILEか$HOOKWRITE_COMMANDを指定します。

HOOKWRITE_UPDATE_FILEにファイル名を指定すると、write(2)が呼ばれたときに指定されたファイルのタイムスタンプを現在時刻に更新します。

HOOKWRITE_COMMANDに任意のコマンドを指定すると、write(2)が呼ばれたときに指定されたコマンドを/bin/sh経由で実行します。

例えば、次のようなシェルスクリプトcat.shを実行すると、write(2)が呼ばれた時点で$HOME/tmp/catというファイルのタイムスタンプを現在の時刻に更新して、"hoge"と出力します。

#!/bin/sh
HOOKWRITE_UPDATE_FILE=$HOME/tmp/cat ¥
  HOOKWRITE_COMMAND='echo hoge' ¥
  LD_PRELOAD=/usr/lib/libhookwrite.so ¥
  /bin/cat $@

使い道

例えばエディタがファイルを保存したときに、何らかのアクションを起こしたい場合に使えるかもしれません。
プログラムに依存しないので、vimだろうがemacsだろうが何でもいけるはず。
ただ、スワップファイルの書き出しにも反応してしまいますが。(^^;

vimを使ってsymfonyプロジェクトの開発をやってて、ファイルを保存したい瞬間にsymfony ccを走らせてキャッシュクリアしたい場合はこんな感じのaliasを作ればいいでしょうか。

alias vim="LD_PRELOAD=/usr/lib/libhookwrite.so HOOKWRITE_COMMAND='/symfony/project/symfony cc' /usr/bin/vim"

プログラム

プログラム自体は非常に簡単です。

static ssize_t (*write_org) (int fd, const void *buf, size_t count) = NULL;

write(2)と同じプロトタイプ宣言を持つ関数ポインタwrite_orgを定義しています。
オリジナルのwriteを保存するのに使います。

__attribute__((constructor))
void
_save_original_functions()
{
    write_org = (ssize_t(*)(int, const void *, size_t)) dlsym(RTLD_NEXT, "write");
}

dlsym(3)を使ってwriteのアドレスを取り出して、write_orgに保存しています。
__attribute__((constructor))というのはgccの拡張になってて、これを指定しておくとmain関数が呼ばれる前に実行してくれます。

後は単純にwrite関数を改めて定義して、その中に任意の処理を書いておきます。
最後に return write_org(fd, buf, count);でオリジナルの処理を呼び出して終わりです。

参考

man ld.soでマニュアルが読めます。
LD_PRELOADの他にもいろいろな機能がありますので、一度読んでおくといいかもしれません。

fakechroot

LD_PRELOADを使った面白いソフトウェアでfakechrootというのがあります。
chroot(2)の実行にはroot権限が必要ですが、fakechrootでは擬似的に一般ユーザ権限でchrootを実現しています。
LD_PRELOADを使って、ファイル操作に関わる部分にすべてフックをかけて擬似的にchroot環境を提供しているんでしょうね。

Mac OS Xだと

実行プログラムの形式がELFではなくMach-Oが採用されています。
なので、LD_PRELOADは使えないはず。
代わりにDYLD_INSERT_LIBRARIESというのが使えます。

man dyld

詳しく検証してないので、詳細はよくわかってません。

まとめ

LD_PRELOADを使うとプログラムに直接手を加えることなく、外から挙動をコントロールすることができます。
応用範囲は無限大です。

2008年4月 4日

gdbの使い方
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

今年の2月にマカーになったbokkoです。どうも僕の使っているフォントがほかの人には見づらいらしく、「そのフォントはねぇよw」と言われたり、外付けのキーボードを使っているせいか、「MacBookの意味なし!」と社内で言われてたりしています。

今日はgdbのお話です。gdbは非常に広く使われているデバッガで、特にC、C++のプログラムをデバッグするのによく使われています。

デバッガの使い方

プログラムをデバッグする際、例えば以下の方法が挙げられます。

1. ソースコードを読む
2. ソースコードに出力関数を仕込む(例えばprintf)
3. ソースコードを書き換えて実行してみる

これで十分な場合もありますが、そうでない場合もあります。これらの方法ではプログラムを実行している最中にこちらからソースコードレベルでのアクションを起こすことが難しいので、例えば、プログラムをある時点で止めて変数の内容を確認するといったことが困難です。2と3でもできないことはないですが、CやC++だと何か別のことをやろうとする度にコンパイルし直す必要があります。そこでデバッガの出番です。デバッガを使えばプログラムを1行ずつ実行しながら変数の内容を確認したり、あるループ内の処理がどう実行されてどのようにそのループを抜けたのか確認するのがやりやすくなります。

プログラムにデバッグ情報を持たせる

例えば、以下のように(ものすごく単純に)配列をランダムにソートするCのプログラムがあるとします。

/* random_sort.c */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define COUNT(arr) (sizeof(arr)/sizeof(arr[0]))
void random_sort(int arr[], int n);
void random_sort(int arr[], int n){
  int i, rnd, temp;
  srand(time(NULL));
  for(i=0;i<n;i++){
    rnd = rand() % n;
    temp = arr[i];
    arr[i] = arr[rnd];
    arr[rnd] = temp;
  }
}
int main(int argc, char *argv[]){
  int i, n;
  int nums[] = {1, 2, 3, 4, 5};
  n = COUNT(nums);
  random_sort(nums, n);
  for(i=0;i<n;i++){
    printf("%d ", nums[i]);
  }
  printf("\n");
  return 0;
}

このプログラムをgccでコンパイルしてgdbで実行するためには例えば、

$ gcc -g random_sort.c -o random_sort

とします。-gはプログラムにデバッグ情報を含ませるためのオプションです。(実際にはCでプログラムを書く際は警告を見逃さないように、もっとオプションを付け足したりするのですが、ここでは割愛します)。作成した実行プログラムをgdbに引数で渡してやります。

$ gdb ./random_sort                         7165 (c/tips/random_sort)
GNU gdb 6.3.50-20050815 (Apple version gdb-768) (Tue Oct  2 04:07:49 UTC 2007)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin"...Reading symbols for shared libraries ... done
(gdb)

これでgdbを使う準備ができました。次はよく使うコマンドについて解説します。

プログラムを実行する

プログラムを実行するにはrunコマンドを使います。

(gdb) run
Starting program: /Users/bokkko/programming/c/tips/random_sort/random_sort
Reading symbols for shared libraries ++. done
4 3 2 5 1
Program exited normally.
(gdb)

ただ単にrunコマンドを実行してもプログラムが正常、もしくは異常終了したり、ユーザからの入力待ちになったりするだけなので、この後は自分が挙動を調べたい関数にブレークポイントを設定したりします。

ブレークポイントを設定する

ブレークポイントを設定するにはbreakコマンドを使います。

(gdb) break random_sort
Breakpoint 1 at 0x1ef0: file random_sort.c, line 13.
(gdb)

関数だけでなく、行単位で設定することもできます。

(gdb) b 15
Breakpoint 1 at 0x1f04: file random_sort.c, line 15.
(gdb) r
Starting program: /Users/bokkko/programming/c/tips/random_sort/random_sort
Reading symbols for shared libraries ++. done
Breakpoint 1, random_sort (arr=0xbfffed24, n=5) at random_sort.c:15
15        for(i=0;i<n;i++){
(gdb)

プログラムを1行ずつ実行する

プログラムを1行ずつ実行するにはnextコマンドを使います。

(gdb) b main
Breakpoint 1 at 0x1f77: file random_sort.c, line 26.
(gdb) r
Starting program: /Users/bokkko/programming/c/tips/random_sort/random_sort
Reading symbols for shared libraries ++. done
Breakpoint 1, main (argc=1, argv=0xbfffed74) at random_sort.c:26
26        int nums[] = {1, 2, 3, 4, 5};
(gdb) next
27        n = COUNT(nums);
(gdb) next
28        random_sort(nums, n);
(gdb) next
30        for(i=0;<n;i++){
(gdb)

ソースコードを表示する

listコマンドを使うと一部のソースコードを表示することができます。
引数なしで実行した場合、現在の行の前後合わせて10行を表示します。

(gdb) b main
Breakpoint 1 at 0x1f77: file random_sort.c, line 26.
(gdb) r
Starting program: /Users/bokkko/programming/c/tips/random_sort/random_sort
Reading symbols for shared libraries ++. done
Breakpoint 1, main (argc=1, argv=0xbfffed74) at random_sort.c:26
26        int nums[] = {1, 2, 3, 4, 5};
(gdb) l
21      }
22
23      int main(int argc, char *argv[]){
24       
25        int i, n;
26        int nums[] = {1, 2, 3, 4, 5};
27        n = COUNT(nums);
28        random_sort(nums, n);
29
30        for(i=0;i<n;i++){
(gdb)

引数には関数や行番号を指定することもできます。

変数の内容を表示する

変数の内容を表示するにはprintコマンドを使います。

(gdb) print i
$5 = 0
(gdb)

また、単に変数を表示するだけでなく、計算もできます。

(gdb) pirnt i + 10
$5 = 10
(gdb)

配列の中身も以下のように表示できます。

(gdb) print nums
$1 = {1, 2, 3, 4, 5}
(gdb)

上記のソースコードにはありませんが、構造体の内容を表示することもできます。例えば、以下のような構造体とソースコードがあった場合、

typedef struct list {
  struct list *next;
  int data;
} List;
       ・
       ・
       ・
  List *l;
  l = (List *)malloc(sizeof(List));
  l->next = NULL;
  l->data = (int)NULL;
> return l;
       ・
       ・
       ・
//>で↓を実行
(gdb) p *l
  $1 = {
  next = 0x0,
  data = 0
}
(gdb)

pはprintのエイリアスです。gdbでは長かったり、よく使うコマンドにはエイリアスが用意されています。

また、環境によっては構造体の内容が上記のように改行されず、1行で表示されてしまっているかもしれません。この場合、ホームディレクトリに.gdbinitというファイルを作成して、

set print pretty on

と記述すれば幸せになれるかもしれません。ただ、構造体のメンバの数が多い場合、画面からはみ出ししまうことがあります。上記の設定はgdbを実行している途中で切り替えることもできるので、状況にあった表示を選択するといいでしょう。

(gdb) set print pretty off
(gdb) p *l
$1 = {next = 0x0, data = 0}
(gdb) set print pretty on
(gdb) p *l
$2 = {
  next = 0x0,
  data = 0
}
(gdb) 

バックトレースを表示する

自分が今、プログラムのどの関数を実行しているのか知りたい場合、backtraceコマンドを使います。以下はステップ実行してrandom_sort関数に入ってbacktraceを実行した様子です。

(gdb) backtrace
#0  random_sort (arr=0xbfffed24, n=5) at random_sort.c:15
#1  0x00001fb3 in main (argc=1, argv=0xbfffed74) at random_sort.c:28
(gdb)

このように自分が実行している関数がどこから呼び出されているのかもわかります。

変数の内容を監視する

変数の内容を表示するには先述したprintコマンドが使えますが、nextコマンドなどでプログラムをステップ実行しながら毎回指定した変数の内容を表示するにはdisplayコマンドを使います。

以下はforループ内でrand関数で生成した数値を監視している様子です。

(gdb) b random_sort
Breakpoint 1 at 0x1ef0: file random_sort.c, line 13.
(gdb) r
Starting program: /Users/bokkko/programming/c/tips/random_sort/random_sort
Reading symbols for shared libraries ++. done
Breakpoint 1, random_sort (arr=0xbfffed24, n=5) at random_sort.c:13
13        srand(time(NULL));
(gdb) display rnd
1: rnd = 0
(gdb) n
15        for(i=0;i<n;i++){
1: rnd = 0
(gdb)
16          rnd = rand() % n;
1: rnd = 0
(gdb)
17          temp = arr[i];
1: rnd = 4
(gdb)
18          arr[i] = arr[rnd];
1: rnd = 4
(gdb)
19          arr[rnd] = temp;
1: rnd = 4
(gdb)
15        for(i=0;i<n;i++){
1: rnd = 4
(gdb)
16          rnd = rand() % n;
1: rnd = 4
(gdb)
17          temp = arr[i];
1: rnd = 2
(gdb)

このように変数の内容がどこでどう変更されたかがわかります。nはnextコマンドのエイリアスで、プログラムの次の行を実行します。

Emacs上でgdbを使う


gdbはコマンドラインで使っていると不便に思うことがあります。例えば、実際のコードのどこが実行されているかわかりにくいですし、listコマンドは使う度に頭の中でコンテキストスイッチをしているようなもので、あまりいいものではありません。なので僕は普段、gdbはEmacs上で使っています。Emacs上でgdbを起動するにはM-x gdbを実行します。実行している様子は以下のような感じです。

gdb.png


このようにlistコマンドを使うことなく、ソースコードを見ながらデバッグできます。Visual StudioやEclipseのデバッグ機能を利用したことがある人はこっちの方がなじみやすいかもしれません。プログラム書く時はvi(m)だけど、「gdb使う時はEmacs上で」という人もいるそうです。

参考文献

GDBデバッギング入門
GDBデバッギング入門
posted with amazlet at 08.04.04
リチャード ストールマン ローランド ペシュ
アスキー
売り上げランキング: 42902
おすすめ度の平均: 4.0
5 Debugging with GDB
3 入門書ではありません


GDBハンドブック
GDBハンドブック
posted with amazlet at 08.04.04
アーノルド ロビンス
オライリージャパン
売り上げランキング: 55079
おすすめ度の平均: 4.0
5 単なるリファレンスとして使うもの
3 開発慣れしてる人でGDB忘れた人.お勧めです

追記:(2008/04/07)


上記の配列をランダムに並び替えるプログラムですが、ある方から上記のコードでは均等にランダムにならない、とのご指摘を頂きました。均等にランダムになるには関数random_sortの↓の部分を、

  for(i=0;i<n;i++){                                                                            
    rnd = rand() % n;                                                                          
    temp = arr[i];                                                                             
    arr[i] = arr[rnd];                                                                         
    arr[rnd] = temp;                                                                           
  }                                                                                            

↓のようにするのが正しいです。すみませんでした。

  for(i=0;i<n-1;i++){
    rnd = rand() % (n - i) + i;
    temp = arr[i];
    arr[i] = arr[rnd];
    arr[rnd] = temp;
  }

また、修正にあたり、こちらを参考にさせて頂きました。


この場を借りて御二方に感謝します。ありがとうございました。

2008年4月 1日

プチプチAPI に機能追加を行いました
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

Keitaです。
去年、公開いたしましたプチプチAPIですが、長い間動作していなかったので修正を行い、さらに機能追加を行いました。

去年は、世の中でもプチプチのおもちゃが発売などが発売され空前のプチプチブームになり、さらにツンデレボイスもついたりして、われわれも負けてられないと、ツンデレヴォイスをつけてみました。

残念ながら、プロの声優さんをゲットするには時間がなかったので、僕が一人で会議室で録音してみました。

サービスに人気が出たら、音声を追加しようと思います。

2008/04/01
API用のバインディングを作成された方があらわれました。わずかAPI公開10分後のことでした。

というわけでうれしくなったのでいくつか僕のものではない音声も追加せていただきました。

  [PR] 転職


About 2008年4月

2008年4月にブログ「ウノウラボ Unoh Labs」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2008年3月です。

次のアーカイブは2008年5月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

ウノウサービス