valgrind の使い方などについて記載します。
Valgrind(ヴァルグリンド)は、メモリデバッグや、メモリリークの検出、スレッドエラーの検出、プロファイリングなどを行うための仮想機械を利用したソフトウェア開発ツールです。
最もよく利用されている標準のツールは Memcheck です。 Memcheck はほぼすべての命令に特別な計測用のコードを挿入し、「正当性」があり、「アドレス可能」であるかという情報が、それぞれVビットおよびAビットに格納されているかを追跡する。データは移動したり加工されたりするが、計測用のコードは1ビットレベルで正確であるようにA、Vビットを追跡する。
さらに、Memcheck は標準のCメモリアロケータを独自の実装に置き換え、割り当て済みブロックの前後にメモリガードを挿入する。この機能により、Memcheckはプログラムが割り当てられたブロックよりわずかに外側の領域を読み書きする off-by-one エラーを検出できる。
Memcheck が検出や警告可能な問題には、下記のものがある。
こうした機能への代償として性能が低下する。Memcheckの元で動作するプログラムはValgrindなしで動作する場合と比べて5倍から20倍遅く、より多くのメモリを使用する(メモリ確保ごとにかなりのメモリを追加で消費する)。したがって、ほとんどの開発者は常にMemcheck(あるいは他のValgrindツール)の元でコードを走らせることはしない。特定のバグを解析したり、(Memcheckが検出可能な種類の)潜在的なバグがないことを検証するために使用するのが最も典型的な方法である。
ソースコードを取得してビルド、インストールを行う場合は以下の手順で行います。
(1) 下記URLからソースコード一式を取得します。
(2) 下記手順で解凍、ビルド、インストールを行います。
bzip2 -d valgrind-3.10.1.tar.bz2 tar -xf valgrind-3.10.1.tar ./configure make make install
バイナリファイルをインストールする手順は以下の通りです。Ubuntu の例です。
sudo apt-get install valgrind
オプションは以下の通りです。
valgrind --help usage: valgrind [options] prog-and-args tool-selection option, with default in [ ]: --tool=<name> use the Valgrind tool named <name> [memcheck] basic user options for all Valgrind tools, with defaults in [ ]: -h --help show this message --help-debug show this message, plus debugging options --help-dyn-options show the dynamically changeable options --version show version -q --quiet run silently; only print error msgs -v --verbose be more verbose -- show misc extra info --trace-children=no|yes Valgrind-ise child processes (follow execve)? [no] --trace-children-skip=patt1,patt2,... specifies a list of executables that --trace-children=yes should not trace into --trace-children-skip-by-arg=patt1,patt2,... same as --trace-children-skip= but check the argv[] entries for children, rather than the exe name, to make a follow/no-follow decision --child-silent-after-fork=no|yes omit child output between fork & exec? [no] --vgdb=no|yes|full activate gdbserver? [yes] full is slower but provides precise watchpoint/step --vgdb-error=<number> invoke gdbserver after <number> errors [999999999] to get started quickly, use --vgdb-error=0 and follow the on-screen directions --vgdb-stop-at=event1,event2,... invoke gdbserver for given events [none] where event is one of: startup exit valgrindabexit all none --track-fds=no|yes|all track open file descriptors? [no] all includes reporting stdin, stdout and stderr --time-stamp=no|yes add timestamps to log messages? [no] --log-fd=<number> log messages to file descriptor [2=stderr] --log-file=<file> log messages to <file> --log-socket=ipaddr:port log messages to socket ipaddr:port user options for Valgrind tools that report errors: --xml=yes emit error output in XML (some tools only) --xml-fd=<number> XML output to file descriptor --xml-file=<file> XML output to <file> --xml-socket=ipaddr:port XML output to socket ipaddr:port --xml-user-comment=STR copy STR verbatim into XML output --demangle=no|yes automatically demangle C++ names? [yes] --num-callers=<number> show <number> callers in stack traces [12] --error-limit=no|yes stop showing new errors if too many? [yes] --exit-on-first-error=no|yes exit code on the first error found? [no] --error-exitcode=<number> exit code to return if errors found [0=disable] --error-markers=<begin>,<end> add lines with begin/end markers before/after each error output in plain text mode [none] --show-error-list=no|yes show detected errors list and suppression counts at exit [no] -s same as --show-error-list=yes --keep-debuginfo=no|yes Keep symbols etc for unloaded code [no] This allows saved stack traces (e.g. memory leaks) to include file/line info for code that has been dlclose'd (or similar) --show-below-main=no|yes continue stack traces below main() [no] --default-suppressions=yes|no load default suppressions [yes] --suppressions=<filename> suppress errors described in <filename> --gen-suppressions=no|yes|all print suppressions for errors? [no] --input-fd=<number> file descriptor for input [0=stdin] --dsymutil=no|yes run dsymutil on Mac OS X when helpful? [yes] --max-stackframe=<number> assume stack switch for SP changes larger than <number> bytes [2000000] --main-stacksize=<number> set size of main thread's stack (in bytes) [min(max(current 'ulimit' value,1MB),16MB)] user options for Valgrind tools that replace malloc: --alignment=<number> set minimum alignment of heap allocations [16] --redzone-size=<number> set minimum size of redzones added before/after heap blocks (in bytes). [16] --xtree-memory=none|allocs|full profile heap memory in an xtree [none] and produces a report at the end of the execution none: no profiling, allocs: current allocated size/blocks, full: profile current and cumulative allocated size/blocks and freed size/blocks. --xtree-memory-file=<file> xtree memory report file [xtmemory.kcg.%p] uncommon user options for all Valgrind tools: --fullpath-after= (with nothing after the '=') show full source paths in call stacks --fullpath-after=string like --fullpath-after=, but only show the part of the path after 'string'. Allows removal of path prefixes. Use this flag multiple times to specify a set of prefixes to remove. --extra-debuginfo-path=path absolute path to search for additional debug symbols, in addition to existing default well known search paths. --debuginfo-server=ipaddr:port also query this server (valgrind-di-server) for debug symbols --allow-mismatched-debuginfo=no|yes [no] for the above two flags only, accept debuginfo objects that don't "match" the main object --smc-check=none|stack|all|all-non-file [all-non-file] checks for self-modifying code: none, only for code found in stacks, for all code, or for all code except that from file-backed mappings --read-inline-info=yes|no read debug info about inlined function calls and use it to do better stack traces. [yes] on Linux/Android/Solaris for the tools Memcheck/Massif/Helgrind/DRD only. [no] for all other tools and platforms. --read-var-info=yes|no read debug info on stack and global variables and use it to print better error messages in tools that make use of it (Memcheck, Helgrind, DRD) [no] --vgdb-poll=<number> gdbserver poll max every <number> basic blocks [5000] --vgdb-shadow-registers=no|yes let gdb see the shadow registers [no] --vgdb-prefix=<prefix> prefix for vgdb FIFOs [/tmp/vgdb-pipe] --run-libc-freeres=no|yes free up glibc memory at exit on Linux? [yes] --run-cxx-freeres=no|yes free up libstdc++ memory at exit on Linux and Solaris? [yes] --sim-hints=hint1,hint2,... activate unusual sim behaviours [none] where hint is one of: lax-ioctls lax-doors fuse-compatible enable-outer no-inner-prefix no-nptl-pthread-stackcache fallback-llsc none --fair-sched=no|yes|try schedule threads fairly on multicore systems [no] --kernel-variant=variant1,variant2,... handle non-standard kernel variants [none] where variant is one of: bproc android-no-hw-tls android-gpu-sgx5xx android-gpu-adreno3xx none --merge-recursive-frames=<number> merge frames between identical program counters in max <number> frames) [0] --num-transtab-sectors=<number> size of translated code cache [32] more sectors may increase performance, but use more memory. --avg-transtab-entry-size=<number> avg size in bytes of a translated basic block [0, meaning use tool provided default] --aspace-minaddr=0xPP avoid mapping memory below 0xPP [guessed] --valgrind-stacksize=<number> size of valgrind (host) thread's stack (in bytes) [1048576] --show-emwarns=no|yes show warnings about emulation limits? [no] --require-text-symbol=:sonamepattern:symbolpattern abort run if the stated shared object doesn't have the stated text symbol. Patterns can contain ? and *. --soname-synonyms=syn1=pattern1,syn2=pattern2,... synonym soname specify patterns for function wrapping or replacement. To use a non-libc malloc library that is in the main exe: --soname-synonyms=somalloc=NONE in libxyzzy.so: --soname-synonyms=somalloc=libxyzzy.so --sigill-diagnostics=yes|no warn about illegal instructions? [yes] --unw-stack-scan-thresh=<number> Enable stack-scan unwind if fewer than <number> good frames found [0, meaning "disabled"] NOTE: stack scanning is only available on arm-linux. --unw-stack-scan-frames=<number> Max number of frames that can be recovered by stack scanning [5] --resync-filter=no|yes|verbose [yes on MacOS, no on other OSes] attempt to avoid expensive address-space-resync operations --max-threads=<number> maximum number of threads that valgrind can handle [500] user options for Memcheck: --leak-check=no|summary|full search for memory leaks at exit? [summary] --leak-resolution=low|med|high differentiation of leak stack traces [high] --show-leak-kinds=kind1,kind2,.. which leak kinds to show? [definite,possible] --errors-for-leak-kinds=kind1,kind2,.. which leak kinds are errors? [definite,possible] where kind is one of: definite indirect possible reachable all none --leak-check-heuristics=heur1,heur2,... which heuristics to use for improving leak search false positive [all] where heur is one of: stdstring length64 newarray multipleinheritance all none --show-reachable=yes same as --show-leak-kinds=all --show-reachable=no --show-possibly-lost=yes same as --show-leak-kinds=definite,possible --show-reachable=no --show-possibly-lost=no same as --show-leak-kinds=definite --xtree-leak=no|yes output leak result in xtree format? [no] --xtree-leak-file=<file> xtree leak report file [xtleak.kcg.%p] --undef-value-errors=no|yes check for undefined value errors [yes] --track-origins=no|yes show origins of undefined values? [no] --partial-loads-ok=no|yes too hard to explain here; see manual [yes] --expensive-definedness-checks=no|auto|yes Use extra-precise definedness tracking [auto] --freelist-vol=<number> volume of freed blocks queue [20000000] --freelist-big-blocks=<number> releases first blocks with size>= [1000000] --workaround-gcc296-bugs=no|yes self explanatory [no]. Deprecated. Use --ignore-range-below-sp instead. --ignore-ranges=0xPP-0xQQ[,0xRR-0xSS] assume given addresses are OK --ignore-range-below-sp=<number>-<number> do not report errors for accesses at the given offsets below SP --malloc-fill=<hexnumber> fill malloc'd areas with given value --free-fill=<hexnumber> fill free'd areas with given value --keep-stacktraces=alloc|free|alloc-and-free|alloc-then-free|none stack trace(s) to keep for malloc'd/free'd areas [alloc-and-free] --show-mismatched-frees=no|yes show frees that don't match the allocator? [yes] Extra options read from ~/.valgrindrc, $VALGRIND_OPTS, ./.valgrindrc Memcheck is Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. Valgrind is Copyright (C) 2000-2017, and GNU GPL'd, by Julian Seward et al. LibVEX is Copyright (C) 2004-2017, and GNU GPL'd, by OpenWorks LLP et al. Bug reports, feedback, admiration, abuse, etc, to: www.valgrind.org.
重要そうなオプションを特記します。
オプション | 意味 |
---|---|
--show-reachable | yes を指定すると、無害かもしれないメモリリークも出力します。 |
--trace-children | yes を指定すると、解析中のプログラムがforkっで生成したプロセスも解析対象にします。 |
--track-fds | yes を指定すると、ファイルディスクリプタの閉じ忘れを報告します。 |
--error-limit | no を指定すると、エラー数が閾値を超えた場合でも、Valgrindが解析を継続します。 |
--num-callers | エラー箇所までのバックトレースを何段まで表示するかを指定します。 |
--exit-on-first-error | yes を指定すると、最初のエラーが発生した時点でプログラムの実行を直ちに停止します。--exit-on-first-error=yesというように使用します。 |
--error-exitcode | エラーで終了した場合に特定の終了コードを返すために使用します。--error-exitcode=1という感じで使用します。 |
gcc/g++ でビルドした c++ プログラムをサンプルに valgrind で「メモリリークを検出する方法」を示します。
コンパイラ : | gcc/g++, | 11.2.0 |
OS : | Ubuntu, | 22.04.1 (WSL) |
ツール: | Valgrind, | valgrind-3.18.1 |
[プログラムソース "memory_leak.cpp"]
#include <iostream> int main() { char *p; for ( size_t i=0; i<10; ++i ){ p = new char[10]; } return EXIT_SUCCESS; }
delete[] p の処理が無いので、 p = new char[10] で確保した動的メモリが未開放でリークとなります。
テストプログラム(memory_leak.cpp)をコンパイルします。
hidetoshi@Laptop4:~$ g++ -Wall -g memory_leak.cpp -o memory_leak memory_leak.cpp: In function ‘int main()’: memory_leak.cpp:5:11: warning: variable ‘p’ set but not used [-Wunused-but-set-variable] 5 | char *p;
コンパイルすると警告が出力されますが、宣言した変数 p を使用していないだけなので無視してください。
valgrind を使ってテストプログラム(memory_leak)を実行します。
hidetoshi@Laptop4:~$ valgrind --leak-check=full --leak-resolution=high --show-reachable=yes ./memory_leak ==1420== Memcheck, a memory error detector ==1420== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==1420== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==1420== Command: ./memory_leak ==1420== ==1420== ==1420== HEAP SUMMARY: ==1420== in use at exit: 100 bytes in 10 blocks ==1420== total heap usage: 11 allocs, 1 frees, 72,804 bytes allocated ==1420== ==1420== 100 bytes in 10 blocks are definitely lost in loss record 1 of 1 ==1420== at 0x484A2F3: operator new[](unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==1420== by 0x1091A8: main (memory_leak.cpp:8) ==1420== ==1420== LEAK SUMMARY: ==1420== definitely lost: 100 bytes in 10 blocks ==1420== indirectly lost: 0 bytes in 0 blocks ==1420== possibly lost: 0 bytes in 0 blocks ==1420== still reachable: 0 bytes in 0 blocks ==1420== suppressed: 0 bytes in 0 blocks ==1420== ==1420== For lists of detected and suppressed errors, rerun with: -s ==1420== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) hidetoshi@Laptop4:~$
100 bytes のメモリリークを検出できました。(definitely lost: 100 bytes in 10 blocks)
リークしているメモリを確保した場所は memory_leak.cpp:8 であることを教えてくれます。この情報を得るにはコンパイル時に -g を付けている必要があります。
gcc/g++ でビルドした c++ プログラムをサンプルに valgrind で「解放済みメモリー領域へのポインタを誤って使ってしまう問題の検出方法」を示します。
コンパイラ : | gcc/g++, | 11.2.0 |
OS : | Ubuntu, | 22.04.1 (WSL) |
ツール: | Valgrind, | valgrind-3.18.1 |
[プログラムソース "invalid_write.cpp"]
#include <iostream>
#include <thread>
#include <chrono>
int main()
{
char *p;
for ( size_t i=0; i<10; ++i ){
p = new char[10];
p[0] = 'a';
std::chrono::seconds dura(1); // 1秒
std::this_thread::sleep_for(dura);
delete[] p;
p[0] = 'a';
}
return EXIT_SUCCESS;
}
15行目の p[0] = 'a' が解放済みメモリー領域への書き込み部分です。
テストプログラム(invalid_write.cpp)をコンパイルします。
hidetoshi@Laptop4:~$ g++ -Wall -g invalid_write.cpp -o invalid_write
valgrind を使ってテストプログラム(invalid_write)を実行します。
hidetoshi@Laptop4:~$ valgrind --leak-check=full --leak-resolution=high --show-reachable=yes ./invalid_write ==2617== Memcheck, a memory error detector ==2617== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==2617== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==2617== Command: ./invalid_write ==2617== ==2617== Invalid write of size 1 ==2617== at 0x10926E: main (invalid_write.cpp:15) ==2617== Address 0x4dd2c80 is 0 bytes inside a block of size 10 free'd ==2617== at 0x484CA8F: operator delete[](void*) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2617== by 0x109269: main (invalid_write.cpp:14) ==2617== Block was alloc'd at ==2617== at 0x484A2F3: operator new[](unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2617== by 0x109237: main (invalid_write.cpp:10) ==2617== ==2617== ==2617== HEAP SUMMARY: ==2617== in use at exit: 0 bytes in 0 blocks ==2617== total heap usage: 11 allocs, 11 frees, 72,804 bytes allocated ==2617== ==2617== All heap blocks were freed -- no leaks are possible ==2617== ==2617== For lists of detected and suppressed errors, rerun with: -s ==2617== ERROR SUMMARY: 10 errors from 1 contexts (suppressed: 0 from 0) hidetoshi@Laptop4:~$
解放済みメモリへの書き込みを検出できました。(Invalid write of size 1)
書き込み場所は invalid_write.cpp:15 であることを教えてくれています。
そして、そのメモリは invalid_write.cpp:14 でメモリ解放されており、 invalid_write.cpp:10 でメモリ取得していることを示しています。
gcc/g++ でビルドした c++ プログラムをサンプルに valgrind で「メモリ領域外への不正アクセスの検出方法」を示します。
コンパイラ : | gcc/g++, | 11.2.0 |
OS : | Ubuntu, | 22.04.1 (WSL) |
ツール: | Valgrind, | valgrind-3.18.1 |
[プログラムソース "invalid_write_2.cpp"]
#include <iostream>
int main()
{
char *p;
for ( size_t i=0; i<10; ++i ){
p = new char[10];
p[10] = 'a';
delete[] p;
}
return EXIT_SUCCESS;
}
9行目の p[10] = 'a' がメモリ領域外への不正アクセス部分です。
テストプログラム(invalid_write_2.cpp)をコンパイルします。
hidetoshi@Laptop4:~$ g++ -Wall -g invalid_write_2.cpp -o invalid_write_2
valgrind を使ってテストプログラム(invalid_write)を実行します。
hidetoshi@Laptop4:~$ valgrind --leak-check=full --leak-resolution=high --show-reachable=yes ./invalid_write_2 ==2919== Memcheck, a memory error detector ==2919== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==2919== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info ==2919== Command: ./invalid_write_2 ==2919== ==2919== Invalid write of size 1 ==2919== at 0x1091D5: main (invalid_write_2.cpp:9) ==2919== Address 0x4dd2c8a is 0 bytes after a block of size 10 alloc'd ==2919== at 0x484A2F3: operator new[](unsigned long) (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==2919== by 0x1091C8: main (invalid_write_2.cpp:8) ==2919== ==2919== ==2919== HEAP SUMMARY: ==2919== in use at exit: 0 bytes in 0 blocks ==2919== total heap usage: 11 allocs, 11 frees, 72,804 bytes allocated ==2919== ==2919== All heap blocks were freed -- no leaks are possible ==2919== ==2919== For lists of detected and suppressed errors, rerun with: -s ==2919== ERROR SUMMARY: 10 errors from 1 contexts (suppressed: 0 from 0) hidetoshi@Laptop4:~$
メモリ領域外への不正アクセスを検出できました。(Invalid write of size 1)
書き込み場所は invalid_write_2.cpp:9 であることを教えてくれています。
そして、そのメモリは invalid_write.cpp_2:8 でメモリ取得していることを示しています。
本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。
2024-07-24 | - | 微修正 |
2022-08-13 | - | 1章、3章、4章、5章、6章 を追加 |
2022-01-02 | - | 新規作成 |