【valgrind】動的解析ツール

## はじめに
ずっと名前だけ知っていた「valgrind」を導入したら想像以上に便利だったので今後の自分や自分のようにvalgrind食わず嫌いしている方に読んでいただきたい記事になっております。

## valgrindとは
Linux環境のC/C++で使えるメモリチェックツール。確保・解放が適切に行われているかなどを確認できる。


## valgrindのインストール
筆者はMacを使っているのでこちらの記事を参考にインストールしました。記事に載っているコマンドをターミナルにコピペするだけでインストールできます。

https://qiita.com/keitean/items/8515e27b35661f577f75

【追記】
筆者はこの記事の投稿後、ubuntu環境に移行しvalgrindを使っていました。Macでは公式のvalgrindをインストールすることはできないため、非公式のものを使用することになるのですが、挙動がおかしい場合があるとのことなので、Macで使用される方はお気をつけください。



## 実際に使ってみる
まずは正常にメモリ管理が行われている関数で試してみる。
```main.c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr = (int *)malloc(4 * sizeof(int));

    if (arr == NULL) {
        printf("メモリの確保に失敗しました。\n");
        return (1);
    }

    for (int i = 0; i < 4; i++) {
        arr[i] = i + 1;
    }

    for (int i = 0; i < 4; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    free(arr);
    return (0);
}
```
以下のように実行コマンドと一緒に実行するだけでリークチェックを行ってくれる。

```
valgrind --leak-check=full ./a.out
```
`--leak-check`オプションでメモリリークを確認できる。
`full`以外にも`no`、`summary`、`yes`を選択でき、`summary`がデフォルトとなっている。


```valgrind --leak-check=full ./a.out
==420819== Memcheck, a memory error detector
==420819== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==420819== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==420819== Command: ./a.out
==420819== 
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
==420819== 
==420819== HEAP SUMMARY:
==420819==     in use at exit: 0 bytes in 0 blocks
==420819==   total heap usage: 2 allocs, 2 frees, 1,040 bytes allocated
==420819== 
==420819== All heap blocks were freed -- no leaks are possible
==420819== 
==420819== For lists of detected and suppressed errors, rerun with: -s
==420819== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
```
`All heap blocks were freed -- no leaks are possible`の部分からわかるように適切なメモリ解放が行われており、メモリリークが発生していない。



次にメモリ解放を適切に行わなかった場合を見ていく、以下のコードをコンパイルし先ほどと同じように実行する。
```not_freed.c
#include <stdio.h>
#include <stdlib.h>

int main() {
    int *arr = (int *)malloc(4 * sizeof(int));

    if (arr == NULL) {
        printf("メモリの確保に失敗しました。\n");
        return (1);
    }

    for (int i = 0; i < 4; i++) {
        arr[i] = i + 1;
    }

    for (int i = 0; i < 4; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    return (0);
}
```

```valgrind --leak-check=full ./a.out
==420373== Memcheck, a memory error detector
==420373== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==420373== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==420373== Command: ./a.out
==420373== 
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
==420373== 
==420373== HEAP SUMMARY:
==420373==     in use at exit: 16 bytes in 1 blocks
==420373==   total heap usage: 2 allocs, 1 frees, 1,040 bytes allocated
==420373== 
==420373== 16 bytes in 1 blocks are definitely lost in loss record 1 of 1
==420373==    at 0x4848899: malloc (in {PATH})
==420373==    by 0x401158: main (in {PATH})
==420373== 
==420373== LEAK SUMMARY:
==420373==    definitely lost: 16 bytes in 1 blocks
==420373==    indirectly lost: 0 bytes in 0 blocks
==420373==      possibly lost: 0 bytes in 0 blocks
==420373==    still reachable: 0 bytes in 0 blocks
==420373==         suppressed: 0 bytes in 0 blocks
==420373== 
==420373== For lists of detected and suppressed errors, rerun with: -s
==420373== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
```
すると、上記のようにメモリリークがどこの関数を通過する時点で起きたのかわかるようになっている。今回の場合、main関数の中で16バイト分のリークが発生しており、main関数の中でmallocが使われていることがわかるので、原因究明が速やかに済む。

（エラーの詳細に表示される`blocks`はvalgrindがメモリをブロック単位で管理しているためこのような値が出ているとのこと）


## まとめ
今までpurintfを使いながら手動でメモリリークやエラーの確認を行っていましたが、その回数が格段に減少し、かなり効率が上がりました。今後もお世話になります。

### 参考にしたサイト

https://qiita.com/QGv/items/d7857cb0fff80ecbe41d


https://qiita.com/SnykSec/items/263c10ca91d3bdb099aa

https://linux.die.net/man/1/valgrind

https://wagavulin.hatenablog.com/entry/2016/08/28/231547