Zapccを評価するために、図らずも自作RISC-VシミュレータをLLVMに移植したのだった。
LLVMに移植したので、どうせならLLVMのメモリリークチェック機能などについて試行してみたい。 TuringCompleteFM にも出ていたのだが、LLVMのSanitizerはかなり優秀らしいということで動作させてみた。 これまではValgrindしか使ってこなかったのでLLVMのSanitizerのほうが優秀なのであれば移行したい。
LLVMのSanitizerを調査した結果、大きく分けて3種類が存在するらしい。
- AddressSanitizer (detects addressability issues) and LeakSanitizer (detects memory leaks)
- ThreadSanitizer (detects data races and deadlocks) for C++ and Go
- MemorySanitizer (detects use of uninitialized memory)
一つ一つ試していこう。
Memory Sanitizer (未初期化メモリをチェックする)
Memory Sanitizerを有効化するためには、LLVMのコンパイルオプションに -fsanitize=memory
を追加してビルドする。
ビルド後のRISC-Vシミュレータを動作させると、いきなりGFlagsの部分で落ちてしまった。あれえ?Googleのコードなのに。
==21479==WARNING: MemorySanitizer: use-of-uninitialized-value #0 0x70ebf6 in std::_Rb_tree<char const*, std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*>, std::_Select1st<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, google::(anonymous namespace)::StringCmp, std::allocator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> > >::_M_get_insert_unique_pos(char const* const&) /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/stl_tree.h:2035:7 #1 0x70e6f0 in std::pair<std::_Rb_tree_iterator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, bool> std::_Rb_tree<char const*, std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*>, std::_Select1st<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, google::(anonymous namespace)::StringCmp, std::allocator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> > >::_M_insert_unique<std::pair<char const*, google::(anonymous namespace)::CommandLineFlag*> >(std::pair<char const*, google::(anonymous namespace)::CommandLineFlag*>&&) /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/stl_tree.h:2091:4 #2 0x70d7ce in std::pair<std::_Rb_tree_iterator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, bool> std::map<char const*, google::(anonymous namespace)::CommandLineFlag*, google::(anonymous namespace)::StringCmp, std::allocator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> > >::insert<std::pair<char const*, google::(anonymous namespace)::CommandLineFlag*>, void>(std::pair<char const*, google::(anonymous namespace)::CommandLineFlag*>&&) /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/stl_map.h:810:16 #3 0x70d045 in google::(anonymous namespace)::FlagRegistry::RegisterFlag(google::(anonymous namespace)::CommandLineFlag*) /home/msyksphinz/work/forest/riscv_forest_zapcc/vendor/gflags/src/gflags.cc:750:12 #4 0x6fd174 in google::(anonymous namespace)::RegisterCommandLineFlag(char const*, char const*, char const*, google::(anonymous namespace)::FlagValue*, google::(anonymous namespace)::FlagValue*) /home/msyksphinz/work/forest/riscv_forest_zapcc/vendor/gflags/src/gflags.cc:1472:35 #5 0x730f93 in google::FlagRegisterer::FlagRegisterer<long>(char const*, char const*, char const*, long*, long*) /home/msyksphinz/work/forest/riscv_forest_zapcc/vendor/gflags/src/gflags.cc:1484:3 #6 0x47f5f0 in __cxx_global_var_init.4 /home/msyksphinz/work/forest/riscv_forest_zapcc/src/swimmer_main.cpp:62:1 #7 0x480e02 in _GLOBAL__sub_I_swimmer_main.cpp /home/msyksphinz/work/forest/riscv_forest_zapcc/src/swimmer_main.cpp #8 0x77695c in __libc_csu_init (/home/msyksphinz/work/forest/riscv_forest_zapcc/build_riscvforest/riscvforest+0x77695c) #9 0x7f085bce0b27 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:266 #10 0x490b39 in _start (/home/msyksphinz/work/forest/riscv_forest_zapcc/build_riscvforest/riscvforest+0x490b39) SUMMARY: MemorySanitizer: use-of-uninitialized-value /usr/lib/gcc/x86_64-linux-gnu/7.3.0/../../../../include/c++/7.3.0/bits/stl_tree.h:2035:7 in std::_Rb_tree<char const*, std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*>, std::_Select1st<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> >, google::(anonymous namespace)::StringCmp, std::allocator<std::pair<char const* const, google::(anonymous namespace)::CommandLineFlag*> > >::_M_get_insert_unique_pos(char const* const&) Exiting
というわけで Memory Sanitizerはいったんスキップする。
Address Sanitizer (メモリリークをチェックする)
Memory Sanitizerを有効化するためには、LLVMのコンパイルオプションに -fsanitize=address
を追加してビルドする。
ビルドしてバイナリを実行すると、エラーで落ちた。確かに、アドレスオーバフローを起こしている部分が存在した。 修正して再ビルドすると、エラー無く実行できるようになった。
==4691==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc68865b80 at pc 0x000000552502 bp 0x7ffc68865b30 sp 0x7ffc688652e0 READ of size 4 at 0x7ffc68865b80 thread T0 #0 0x552501 in __asan_memcpy /home/msyksphinz/software/llvm/llvm/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:23 #1 0x673654 in ModuleRom::ModuleRom(EnvBase*, _IO_FILE*) /home/msyksphinz/work/forest/riscv_forest_zapcc/src/module_rom.cpp:62:5 #2 0x5a4efd in RiscvPeThread::RiscvPeThread(_IO_FILE*, PrivMode, bool, bool, _IO_FILE*) /home/msyksphinz/work/forest/riscv_forest_zapcc/src/riscv_pe_thread.cpp:71:45 #3 0x59970f in main /home/msyksphinz/work/forest/riscv_forest_zapcc/src/swimmer_main.cpp:170:33 #4 0x7f0fc95ecb96 in __libc_start_main /build/glibc-OTsEL5/glibc-2.27/csu/../csu/libc-start.c:310 #5 0x489479 in _start (/home/msyksphinz/work/forest/riscv_forest_zapcc/build_riscvforest/riscvforest+0x489479) Address 0x7ffc68865b80 is located in stack of thread T0 at offset 64 in frame #0 0x6733cf in ModuleRom::ModuleRom(EnvBase*, _IO_FILE*) /home/msyksphinz/work/forest/riscv_forest_zapcc/src/module_rom.cpp:50 This frame has 3 object(s): [32, 64) 'init' (line 53) <== Memory access at offset 64 overflows this variable [96, 488) 's' (line 65) [560, 592) 'config_string' (line 90) HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork (longjmp and C++ exceptions *are* supported) SUMMARY: AddressSanitizer: stack-buffer-overflow /home/msyksphinz/software/llvm/llvm/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cc:23 in __asan_memcpy Shadow bytes around the buggy address: 0x10000d104b20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10000d104b30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10000d104b40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10000d104b50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x10000d104b60: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00 00 00 =>0x10000d104b70:[f2]f2 f2 f2 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 0x10000d104b80: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 0x10000d104b90: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 0x10000d104ba0: f8 f8 f8 f8 f8 f2 f2 f2 f2 f2 f2 f2 f2 f2 f8 f8 0x10000d104bb0: f8 f8 f3 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00 0x10000d104bc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb Shadow gap: cc ==4691==ABORTING
Thread Sanitizer (スレッドのレースコンディションをチェックする)
Thread Sanitizerを有効化するためには、 LLVMのコンパイルオプションに -fsanitize=thread
を追加してビルドする。
ビルドしてバイナリを実行しても、特にエラーは発生しなかった。まあ、確かにマルチスレッドは使用していないのでエラーは発生しないだろう。
実行速度
それぞれ実行速度を比較すると以下のようになった。まあValgrindは--leak-check=full
なのでもしかしたら正確な比較ではないかもしれないが、LLVMのSanitizer、結構速いね。
- Normal : 2m43.460s
- Thread Sanitizer : 16m37.715s
- Address Sanitizer : 7m53.666s
- Valgrind
--leak-check=full
: 3時間程度...