dreamin' blog

title: デバッグ手法まとめ
tags: debug
author: koheiSG
slide: false

この記事のライセンス

クリエイティブ・コモンズ・ライセンス
この記事はCC BY 4.0(クリエイティブ・コモンズ 表示 4.0 国際 ライセンス)の元で公開します。

デバッグは大事な能力

設計やテストも大事ですが、デバッグも大事です。

でも、実装を進めていくには、自分の書いたコードがどのように実行されるか?
テストが落ちた、意図通り実行されなかったとき、それを確かめて自分で書いたロジックを訂正する力が必要です。

良く訓練されたプログラマはデバッグの腕も良く訓練されています。
debugができないとプログラミングを覚えられないでしょう。

そこで、自分の普段行っている方法をまとめました。
プログラミング初心者の方には参考になるかもしれませんが、上級者の方々には他の技法も教えて欲しいです。

print(printf) debug

問題が起こりそうな場所で、print文を挿入し、変数の状態を確認します。
可能な限りロジックを含まないほうが良いが、人間に読めないもの(例えば暗号文など)の場合は最低限のロジックと一緒に実行します。

def price(product)
  p product.sale? # print debug用
  if product.sale?
    return product.price - 10
  end
  product.price
end

メリット

デメリット

logger debug

print debugの代わりにログを吐くようにします。

様々な理由で手元の環境では特定しにくいようなbugの場合に、別の環境にコードをデプロイして行うこともできます。

またdebugが必要なほど複雑なロジックを含むコードには、そもそもlogを仕込んでおく方が、後から問題が発生したときに運用がしやすいということもあるので、積極的に使っていくのが良さそうです。

logger("APIを呼び出します。")
res = call_api
logger("APIの返り値: #{res}")

メリット

step debug

ブレイクポイントを指定し、その場所でプログラムを自分の手で実行できるものです。
IDEに付属のものやJavaScriptだとブラウザ上、rubyやperlなどの言語ではコマンドライン上で、ステップデバッグを行うためのライブラリがあります。

printだけでなく、様々な関数の実行なども行なえます。

stepout/stepinなどの操作を覚えることで、ブレイクポイントした行から、
行を進めていくこともできます。実行リアルタイムでdebugをすることができます。

デバッガによっては、その行で定義されている変数を表示してくるものもあるため、
print文を実行する必要すらない場合もあります。

print debugのようにプログラムに変更を加える必要がないのも大きな魅力です。
誤ってdebug文がリリースされたということも少なくなるでしょう。
(スクリプト言語などの場合はstepデバッグのブレイクポイントをプログラム中に書き込む必要があるものも多い。できれば外から操作できるデバッグツールを使いたいところ)

ただ、一部副作用が生じるような関数呼び出しを行ってしまったりすると、
プログラムの動作自体が変わってしまうので、注意は必要。
(例えば、ステップデバッグ中にDBへの挿入を行う関数を実行したら、再度プログラムによってDBへの挿入を行ってしまう。同じ事がオブジェクトにも言えます)

メリット

デメリット

return debug (二分探索)

他のデバッグ手法と組み合わせて行います。
プログラムの実行を行い途中で終了(return)させてしまいます。
それを3段階で行う事でどこにbugが潜んでいるかを挟み撃ちにして追い込んで行く手法です。

1, プログラムの実行命令の最初でprint debug
2, 実行命令の最後でprint debug
3, 中間地点でprint debug

この3つを繰り返し行います。
1で意図した結果が出ない場合は1より前に問題がある。
3で問題が出ない場合はそれより後に問題がある。
1で発生しない2で発生しない3で問題が発生しない場合は、2と3の間でもう一度この作業を繰り返す。
1で発生しない2で発生3で発生の場合は、もちろん1と3の間でこの作業を繰り返す。

というわけです。

例えば下記のようなプログラムがあり、mainが実行される場合、
最初にbeforeの前とafterの後にdebug文を挿入します。

そもそも最初のbeforeの前のprintが呼ばれなかった場合、mainの呼び出しに問題があります。mainのdebugを終了します。
afterあとのprint文が実行できなければ、before process_1 process_2 afterのいずれかに問題がありますので、ちょうど実行の半分のprocess_1とprocess2の間でプログラムを終了するようにし、debugを行います。
この繰り返しを行うということですね。はい。

def main
  before
  process_1
  process_2
  after
end

private

def before
end

def after
end

def process_1
end

def process_2
end

これができれば、実力不足や能力不足でdebug(バグ取り)できなくとも、bugを発見できないことはないですね。

なども有効ですが、return debugは機械的に行なえるから良さそう。

疑問 そもそもreturn debugなんて言葉ないのでしょうか??

参考文献

昔の論争を蒸し返すようであれですが。。。
2009-03-22 - 未来のいつか/hyoshiokの日記
わたしがprintf()デバッグをする理由 - 本当は怖い情報科学
return debugという名前でいいのか自信がないのですが。。
デバッグ - Wikipedia