g++ でカバレッジを取得する
最近 Erlang でコードを書くときにカバレッジをとっている。TDD で開発を進めていて make check するとテストが走る。テスト終了後にブラウザでカバレッジが表示されるようにしてある。カバレッジの重要性はCode Completeを読むとよく分かる。カバレッジをとると安心感が違う。書いたコードの不安な部分(不吉なにおいのする場所)を「テストが実際に実行しているか?」を確認できる。その確認により不安から解放され、目の前のコードに集中できるようになる。しかもカバレッジで判明する実行されていない部分にはたいていバグがある。
さて最近 Mona の安定性を上げるべく色々試しているのだが w3m で多くのサイトを訪れたり、Mosh の起動・終了を繰り返すとカーネル内の malloc が NULL を返す現象に悩まされている。最初はメモリリークしているだけだと思っていたがよく調べてみると、malloc 自体がバグっているように見えてきた。あえてコードを晒すことはしないが 2003 年(7年前!)に自分が書いたファーストフィットのアロケータがいくつかの条件で free list の管理に失敗している。もうこのコードがひどい。まずテストがない。そしてテストがない。さらにテストがない。メモリ管理は下支えの部分なのでここがバグっていたらとっても困るのに。
30分ほどデバッグを試みたが、大した規模でもないので書き直すことにした。ここでコストをかけて安定したものを作っておこう。Linux 上でコードとテストを書いた。カバレッジもとろう。ということで本題。
やることは
- g++ に -gcov オプションをつけてコンパイルする
- リンク時に -lgcov
- テストを実行。カバレッジがファイルに記録される
- gcov test.gcda でカバレッジが何 % か分かる
- ファイル名.gcov を見れば具体的なカバレッジ詳細が分かる
例:何%か分かる
96.51% 。
% gcov test.gcda | grep FirstFitAllocator -B2 /usr/local/include/gtest/gtest.h:creating 'gtest.h.gcov' File 'FirstFitAllocator.h' Lines executed:96.51% of 86 FirstFitAllocator.h:creating 'FirstFitAllocator.h.gcov'
例:ソースコードのどこが実行されていないか
ファイル名.gcov を見る。実行されていない部分は #### 。3ヶ所のうち前半2つは想定どおり。最後の1つは気づいていなかったのでテストを追加する。
% grep -A2 -B2 '###' FirstFitAllocator.h.gcov 100011: 124: Header* h = getHeaderFromPointer(p); 100011: 125: if (h->magic != MAGIC) { #####: 126: return false; --- : 127: } 100011: 128: if (freeList_ == NULL) { -- --- : 147: } --- : 148: } #####: 149: assert(false); --- : 150: return false; --- : 151: } -- 2: 172: freeList_ = p->next; --- : 173: } else { #####: 174: prev->next = p->next; --- : 175: } 2: 176: return getBody(p);