valgrind

valgrind の使い方などについて記載します。

 

 

 

1. 概要

Valgrind(ヴァルグリンド)は、メモリデバッグや、メモリリークの検出、スレッドエラーの検出、プロファイリングなどを行うための仮想機械を利用したソフトウェア開発ツールです。

最もよく利用されている標準のツールは Memcheck です。 Memcheck はほぼすべての命令に特別な計測用のコードを挿入し、「正当性」があり、「アドレス可能」であるかという情報が、それぞれVビットおよびAビットに格納されているかを追跡する。データは移動したり加工されたりするが、計測用のコードは1ビットレベルで正確であるようにA、Vビットを追跡する。

さらに、Memcheck は標準のCメモリアロケータを独自の実装に置き換え、割り当て済みブロックの前後にメモリガードを挿入する。この機能により、Memcheckはプログラムが割り当てられたブロックよりわずかに外側の領域を読み書きする off-by-one エラーを検出できる。

Memcheck が検出や警告可能な問題には、下記のものがある。

こうした機能への代償として性能が低下する。Memcheckの元で動作するプログラムはValgrindなしで動作する場合と比べて5倍から20倍遅く、より多くのメモリを使用する(メモリ確保ごとにかなりのメモリを追加で消費する)。したがって、ほとんどの開発者は常にMemcheck(あるいは他のValgrindツール)の元でコードを走らせることはしない。特定のバグを解析したり、(Memcheckが検出可能な種類の)潜在的なバグがないことを検証するために使用するのが最も典型的な方法である。

 

 

2. インストール

 

2-1. ビルド&インストール

ソースコードを取得してビルド、インストールを行う場合は以下の手順で行います。

 

(1) 下記URLからソースコード一式を取得します。

Valgrind Home

 

(2) 下記手順で解凍、ビルド、インストールを行います。

bzip2 -d valgrind-3.10.1.tar.bz2
tar -xf valgrind-3.10.1.tar
./configure
make
make install

 

 

2-2. バイナリインストール

バイナリファイルをインストールする手順は以下の通りです。Ubuntu の例です。

 

sudo apt-get install valgrind

 

 

3. オプション一覧

オプションは以下の通りです。

 

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という感じで使用します。

 

 

 

4. メモリリークを検出する方法

[概要]

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 を付けている必要があります。

 

 

 

5. 解放済みメモリー領域へのポインタを誤って使ってしまう問題の検出方法

[概要]

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 でメモリ取得していることを示しています。

 

 

 

6. メモリ領域外への不正アクセスの検出方法

[概要]

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 ライセンスで提供されます。

The MIT License (MIT)

Copyright © 2022 Kinoshita Hidetoshi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 

 


参考

 


 

記載

2024-07-24 - 微修正
2022-08-13 - 1章、3章、4章、5章、6章 を追加
2022-01-02 - 新規作成

 

Programming Items トップページ

プライバシーポリシー