読者です 読者をやめる 読者になる 読者になる

muranoyaのブログ

https://github.com/muranoya

CとC++のデバッグ

会社でも家でも,C/C++を書いている.

Cについては,Cコンパイラを作ろうと思ったことがあるのでいくらか知識はあるし,付き合いもわりと長いので,実行時/コンパイル時エラーが起きてもだいたい何が悪いのか察しはつく.

C++はビャーネ・ストロヴストルップの入門書を流し読みして,わからないところはネットで検索したり,cpprefjpを読んだりしている程度で,テンプレートでFibonacci数は計算できないし,言語機能やライブラリにもそれほど詳しいわけでもない.

最近は,コードを新規で書くよりもデバッグをしているほうが長く感じる.少し前は研究室でSchemeインタプリタデバッグを少しだけ手伝ったりしていたし,会社でも新機能の追加よりもリファクタリングデバッグ的なことを最近やっている.私は,デバッグリファクタリングが嫌いというわけではなく,趣味でも気に食わないコードであれば延々あーでもないこーでもない,と書きなおしたりしているので割と楽しい.

まだインターンでチームに入ったばかりなので(その後そのまま入社するとはいえ),プロダクトコードの全体像もハッキリしていない.なので,リファクタリングのようなものはちょうどいいともいえるのではないかと思っている.

ところで,C/C++である関数を呼び出す部分でコードが失敗しているおり,その関数は至る所で呼びだされていて,どの呼び出しで失敗しているのかわからないとする.もしも,ある関数内でセグメンテーションフォルトなどでプログラムが落ちた場合は,コアダンプを取ってgdbのようなデバッガで調べれば,すぐに原因はわかる場合が多いように思う.

そうでない場合は,最近はマクロを使うようにしている.例えば,

/* この関数は非常に多く呼びだされているとする */
static int
sum(const int *d, int len)
{
...
}

のような場合に,

static int
sum(const int *d, int len)
{
...
}
static int
sum_(const int *d, int len, const char *file, const char *func, int line)
{
    fprintf(stderr, "%s : %s : %d\n", file, func, line);
    return sum(d, len);
}
#define sum(x, y) sum_((x), (y), __FILE__, __func__, __LINE__)

とするようにしている.
何か他に良いデバッグ方法などがあれば….printfやデバッガを使うものが最も基本的だけど,マルチスレッドやfork,複数プロセス間でTCP/IPなんかで通信を行うものになるとデバッグが難しい.LD_PRELOAD環境変数を使ってライブラリ関数を上書きする方法もあるけど,使ったことはないなあ.

最近,バックトラッキング演算子という言語機能を思いついたけど,これをうまく使えば実行時にバックトレースが簡単に表示できるようになるかも.バックトラッキング演算子は,スタックフレームに眠る変数にアクセスする演算子,と考えていて実装案自体はいくつかあるけど,果たしてそれは何の役に立つのか,と考えると難しかったが,あればやはり面白いかも.ただ,スタックフレームに眠る変数にどうアクセスするのか,関数呼び出し元の変数名を指定するとして,複数の関数から呼び出された時,変数名をどう処理するのかは謎だよなあ.コンパイル案,応用案で良いアイデアが思い浮かべば面白いんだけど.ちなみに,赤黒木のような少し複雑でノードの親子関係だけでなく叔父ノードまでも出てくるような場合だと実装がしやすいんじゃないか,と思っていたけど,微妙だ.

ところで,ブログを始めるにあたりはてなを選んだけど,プログラムコードを記事に入れるのが滅茶苦茶面倒だ.WYSIWYGで書いていたものをはてな記法にすると,文章が全部消える.コピペして貼り付けてもリンクなどは当然消える.しかしWYSIWYGでは入れる方法が無いようなので仕方ない.