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

Valgrind で Pthread の Debug

  • OS X Yosemite (10.10.5) でC(GNU99)でpthreadを用いたマルチスレッドプログラムを書いていて、なかなかerrorが取れなかったときに以下ツイートを起点にした議論を見て、ValgrindのHelgrind toolが使えそうということで使ってみた。

  • ValgrindはDarwinにも対応しており、ソースからコンパイルすればインストールできる。使い方は、コンパイル時に-gでソースレベルのデバッグ情報を付与し、valgrind --tool=helgrind ./a.outと実行するだけで良い。
==2575== Possible data race during write of size 1 at 0x5E205C0 by thread #1
==2575== Locks held: none
==2575==    at 0x4C30491: strcat (vg_replace_strmem.c:274)
...
==2575== 
==2575== This conflicts with a previous read of size 1 by thread #2
==2575== Locks held: 2, at addresses 0x605240 0x6051C0
==2575==    at 0x4C30590: strlen (vg_replace_strmem.c:412)
...
==2575==  Address 0x5e205c0 is 0 bytes inside a block of size 131,072 alloc'd
==2575==    at 0x4C2AF2A: realloc (vg_replace_malloc.c:692)
...
==2575==  Block was alloc'd by thread #1
  • 上記の省略した箇所には、具体的なプログラムの行数が出力される。ここで、以下のように別のtoolを試してみた方が良い、というメッセージが出力される。
valgrind: m_mallocfree.c:304 (get_bszB_as_is): Assertion 'bszB_lo == bszB_hi' failed.
valgrind: Heap block lo/hi size mismatch: lo = 131136, hi = 7875142615529697846.
This is probably caused by your program erroneously writing past the
end of a heap block and corrupting heap metadata.  If you fix any
invalid writes reported by Memcheck, this assertion failure will
probably go away.  Please try that before reporting this as a bug.
  • そこで--tool=memcheckとしてvalgrindを実行し、以下の出力を得る。
==2659== Thread 3:
==2659== Invalid write of size 1                                                                     
==2659==    at 0x4C2AC73: strcpy (vg_replace_strmem.c:458)
...
==2659==  Address 0x600d015 is 0 bytes after a block of size 9,045 alloc'd
==2659==    at 0x4C27BBD: malloc (vg_replace_malloc.c:296)
...
==2659== 
  • なんとなくmallocで割り当てたメモリよりも多く書き込んでいるのではないか、と当たりを付けてソースコードチェックすると、見事的中。reallocするかどうかの判定文の不等号から=が漏れていた点と、strcpyが\0を含めてコピーするのを見越して+1したサイズのメモリを確保しなければいけなかった点を発見して修正(その他にもhelgrindで多数検出し修正した)。

  • 良く分からないのは、OS X での実行結果。実は上記はLinuxで実行して確認した結果であり、LinuxではValgrindもプログラム実行でもエラーが検出・発生していないにも関わらず、OS XではValgrindでなぜかloop中で実施しているpthread_createがPossible data raceとして検出されてしまう。

# OS X
$ uname -a
Darwin nishidy-MBA.local 14.5.0 Darwin Kernel Version 14.5.0: Wed Jul 29 02:26:53 PDT 2015; root:xnu-2782.40.9~1/RELEASE_X86_64 x86_64

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin14.5.0
Thread model: posix

$ valgrind --version
valgrind-3.11.0

$ gcc -g -std=gnu99 -pthread -lpthread -O2 -Wall xxx.c
# Linux 
$ uname -a
Linux localhost.localdomain 3.19.8-100.fc20.x86_64 #1 SMP Tue May 12 17:08:50 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

$ gcc --version
gcc (GCC) 4.9.3
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ valgrind --version
valgrind-3.10.0

$ gcc -g -std=gnu99 -pthread -lpthread -O2 -Wall xxx.c

OS X でのデバッグ

  • gdbは標準でインストールされていないので、lldbを使う。