SlideShare a Scribd company logo
1 of 30
Download to read offline
C/C++プログラマのための開発ツール
2016/9/8
光成滋生
• (主にLinux上における)プログラム開発、
デバッグ、不具合調査のためのツールの紹介
• 広く浅く
• 同じことを複数の手段で
• キーワードや「できること」を知っていれば後は自分で
概要と目的
2/30
• gcc, clang
• ソース読み
• ag, GNU GLOBAL, Doxygen, callgrind
• デバッグ
• gdb, objdump, c++filt, core dump, addr2line
• メモリチェック
• ASan, Valgrind, TCMalloc
• 静的解析
• cppcheck, scan-build
• 実行時解析
• SystemTap, perf
目次
3/30
• 警告系オプション
• -Wall -Wextra ; 必須
• コンパイラの警告を無視してはいけない
• https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-
Options.html
• -Wnon-virtual-dtor
• -Woverloaded-virtual ; virtualを同名の関数で隠してしまった
• 他に-Weffc++など変わりものも
• clangには全ての警告を表示する-Weverythingがある
• ときどき便利
• -pedantic ; C/C++標準でないGNU拡張を使っていないか
C/C++コンパイル時のオプション(1/2)
4/30
• 最適化オプション
• -Ofast ; 最適化最大
• -march=native ; コンパイル環境のCPUに合わせた最適化
• -DNDEBUG ; assert()マクロの無効化
• デバッグ用オプション
• -g ; シンボル情報がつく(速度に影響はない)
• -g3 ; マクロも見えるようになる
• -S ; アセンブリ出力(生成コードが意図通りか?)
• objdumpによる逆アセンブルも便利
• objdump -CSlw -M intel
• -C ; C++の関数名を見やすくする(demangle)
• -S ; ソースコードを併記
C/C++コンパイル時のオプション(2/2)
5/30
• <vector>のソースを見たいが場所はどこ?
• echo "#include <vector>"|gcc -x c++ -E -|lv
• プロファイルをとる
• -pgオプション
• ただし関数に介入するのでオーバーヘッドあり
• perfやVTune(後述)など別のものがおすすめ
その他
6/30
• GNU GLOBAL
• https://www.gnu.org/software/global/
• 関数が定義されている場所を行ったり来たりできる
• 使い方
• 見たいソースのトップディレクトリでgtags
• 各自のエディタに応じて設定すること
• あとはがんばって:-)
• ag
• 高速なgrep ; grep -Irw 単語 ./
• apt install silversearcher-ag
• highway(https://github.com/tkengo/highway)というのも
• Visual Studioなどの統合環境 一度は触ってみるとよい
ソースコード読み補助ツール
7/30
• ソースコードのドキュメント自動化ツール
• apt install doxygen
• ソースコードのコメントに書くとマニュアルを生成する
• doxygen -gで設定ファイルを作成
• doxygen Doxyfileでhtmlを作成
• コメントのつけ方
• http://www.doxygen.jp/docblocks.html
Doxygen
8/30
• Graphviz
• ものの依存関係を画像表示する
• apt install graphviz
• 関数の呼び出し関係の画像化
• DoxygenのDoxyfileでHAVE_DOT = yes
• callgrind
• 呼び出し回数などを可視化
• apt install kcachegrind
• Valgrind(後述)の一部
• valgrind –tool=callgrind <binary>
• kcachegrind callgrind.out.*
関数呼び出しの可視化
9/30
• 落ちるプログラムをデバッグする
• rで実行して落ちたところでbtでバックトレースをみる
gdb(デバッグツール)
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
int main(int argc, char *argv[]) {
char buf[32];
int n = argc == 1 ? 10 : atoi(argv[1]);
printf("n=%d¥n", n);
memset(buf, 'A', n);
printf("n=%d¥n", n);
printf("buf[%d-1]=%c¥n", n, buf[n - 1]);
}
gcc -g t.cpp
gdb --args ./aout 10000
r
...
Program received signal SIGSEGV, Segmentation fault.
10/30
• s ; ステップ実行(1行ずつ実行する)
• 最適化オプションありではコードの位置がずれることが多い
• 一度コマンドを実行したあと[return]で同じコマンドを実行
• fin ; 関数の中から外に出るまで一気に動く
• n ; 関数の中に入らないでステップ実行
• up/down スタックフレームを登ったり降りたり
• p 変数 ; 変数を表示する
gdbのコマンドいくつか
11/30
• gdbを使わず落ちる場所だけ調べる
• 落ちたときのレジスタ, ip, backtrace, メモリマップなど表示
libSegFault.so
env LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so ¥
SEGFAULT_SIGNALS=all ./a.out 10000
*** Segmentation fault
Register dump:
RAX: 00007ffeb2670fd0 RBX: 0000000000000000 RCX: 00007ffeb2671000
...
Backtrace:
/lib/x86_64-linux-gnu/libc.so.6(memset+0x5d)[0x7f4cf774050d]
./a.out[0x400699]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7f4cf76d5f45]
./a.out[0x400569]
12/30
• https://github.com/herumi/misc/blob/master/dev/dea
d-lock.cpp
• ./a.outで返って来ない
• psでa.outのプロセスidをさがす
• sudo gdb -p <プロセスid>
• cat /proc/<プロセスid>/mapsなどで情報いろいろみえる
• info threadでthreadが2個あることがわかる
• t 2で2番目に行きbtでバックトレース
dead lockした実行ファイル(1/2)
(gdb) info thread
Id Target Id Frame
2 Thread 0x7f5b3883b700 (LWP 964) "a.out" ...
1 Thread 0x7f5b3984a780 (LWP 963) "a.out" ...
(gdb) t 2
[Switching to thread 2 (Thread 0x7f5b3883b700 (LWP 964))]
#0 0x00007f5b38f15f1c in __lll_lock_wait () from ...
13/30
• dead-lock.cppの12行目で止まっていた
• straceで呼ばれたシステムコールの確認
• ltraceで呼ばれたライブラリ関数の確認
• アドレスは毎回変わる
• sudo sysctl -w kernel.randomize_va_space=0で固定化
• セキュリティ低下につながるので本番環境では禁止
dead lockした実行ファイル(1/2)
gdb) bt
#0 ...5f1c in __lll_lock_wait () from ...libpthread.so.0
#1 ...1649 in _L_lock_909 () from ... libpthread.so.0
#2 ...1470 in pthread_mutex_lock () from ... libpthread.so.0
#3 ...0ecc in __gthread_mutex_lock (__mutex=0x7ffcec6c3e90) at ...
#4 ...12fa in std::mutex::lock (this=0x7ffcec6c3e90) at ...
#5 ...137e in std::lock_guard<std::mutex>::lock_guard ...
#6 ...0fbf in f (m=...) at dead-lock.cpp:12
#7 ...274f in std::_Bind_simple<void (*(std:...
14/30
• プログラムが異常終了したときの情報を保存したもの
• /proc/sys/kernel/core_patternで保存ファイル名を指定
• 例 : カレントディレクトリにcore.<プロセス名>
• shellでcoreサイズを制限していないか確認
• bashならulimit -a ; tcshならlimit
• 0ならcoreファイルは作られないので設定する
• ulimit -c unlimited / limit coredumpsize unlimited
• SEGVする実行ファイルを実行する
• gdb –c <coreファイル> ./a.outでいつものように操作
core dump
sudo sh -c 'echo core.%p > /proc/sys/kernel/core_pattern'
15/30
• coreファイルが無かったとき最低限の情報
• ip ; SEGVしたときに実行していたコードのアドレス
• libc-2.19.soのip番目をさがす
• 00007f55ac1c650d - 7f55ac13a000 = 0x8c50d
• libcの0x8c50d番目は何の関数か
• addr2lineを使う
• memsetのようだ ; 引数がおかしい?(と推測)
• objdump -Sでもわかる
dmesg
[609339.455455] a.out[1088]: segfault at 7fffa910f130 ip 00007f55ac1c650d
sp 00007fffa910ca08 error 6 in libc-2.19.so[7f55ac13a000+1ba000]
% addr2line -e /lib/x86_64-linux-gnu/libc-2.19.so 0x8c50d
/build/eglibc-oGUzwX/eglibc-2.19/string/../sysdeps/x86_64/memset.S:80
16/30
• Address Sanitizer(ASan)
• メモリ関係のエラーのチェック
• バッファオーバーフロー
• ヒープオーバーフロー
• スタックバッファーオーバーフロー
• メモリリーク
• -fsanitize=addressをつけてコンパイル
メモリ関係
17/30
• ./a.out 32 ; 問題なしだが33を指定すると
実行してみる
% ./a.out 33
n=33
==11277==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffdad25b7a0 at pc
0x0000004a691f bp 0x7ffdad25b650 sp 0x7ffdad25ae08
WRITE of size 33 at 0x7ffdad25b7a0 thread T0
#0 0x4a691e in __asan_memset
...
This frame has 5 object(s):
[32, 36) ''
[48, 52) ''
[64, 72) ''
[96, 128) 'buf'
[160, 164) 'n' <== Memory access at offset 128 partially underflows this variable
SUMMARY: AddressSanitizer: stack-buffer-overflow ??:0 __asan_memset
Shadow bytes around the buggy address:
0x100035a436a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100035a436b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100035a436c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100035a436d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100035a436e0: 00 00 00 00 f1 f1 f1 f1 04 f2 04 f2 00 f2 f2 f2
=>0x100035a436f0: 00 00 00 00[f2]f2 f2 f2 04 f3 f3 f3 00 00 00 00
...
18/30
• deleteしていない
メモリ解放し忘れ
% cat t.cpp
int main()
{
char *p = new char[10];
}
% clang++-3.6 -fsanitize=address no_free.cpp -g && ./a.out
=================================================================
==11320==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 10 byte(s) in 1 object(s) allocated from:
#0 0x4dc4f2 in operator new[](unsigned long) (/a.out+0x4dc4f2)
#1 0x4dd24f in main /no_free.cpp:3:12
#2 0x7fc2af5a4f44 in __libc_start_main /build/eglibc-oGUzwX/eglibc-
2.19/csu/libc-start.c:287
SUMMARY: AddressSanitizer: 10 byte(s) leaked in 1 allocation(s).
19/30
• delete p;
• cf. std::stringかstd::unique_ptr<char> p(new char[10])を使え
delete追加したけど間違ってる
% cat t.cpp
int main() {
char *p = new char[10];
delete p;
}
% clang++-3.6 -fsanitize=address no_free.cpp -g && ./a.out
==11464==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new []
vs operator delete) on 0x60200000eff0
#0 0x4dc942 in operator delete(void*) (/a.out+0x4dc942)
#1 0x4dd30a in main /no_free.cpp:4:2
#2 0x7fc0cf759f44 in __libc_start_main /build/eglibc-oGUzwX/eglibc-
2.19/csu/libc-start.c:287
#3 0x435f46 in _start (/a.out+0x435f46)
20/30
• ASanとは別のメモリチェックツール
• ASanとは排他的(ASanありでビルドしたものは動かない)
• 特別なコンパイルオプションは不要
• 他の便利なオプション--leak-check=full, --tool=callgrind
Valgrind
% g++ no_free.cpp -g
% valgrind ./a.out
==11471== Memcheck, a memory error detector
==11471== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==11471== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright
info
==11471== Command: ./a.out
==11471==
==11471== Mismatched free() / delete / delete []
==11471== at 0x4C2C2BC: operator delete(void*) (in
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11471== by 0x40066E: main (no_free.cpp:4)
==11471== Address 0x5a22040 is 0 bytes inside a block of size 10 alloc'd
==11471== at 0x4C2B800: operator new[](unsigned long) (in...
21/30
• 高速なmalloc/free
• http://goog-perftools.sourceforge.net/doc/tcmalloc.html
• リンク時にライブラリを指定する
• LD_PRELOADで指定する
• デバッグ機能も持つ
• apt install libtcmalloc*
• 他にも同様のツールはいろいろある
TCMalloc
% g++ no_free.cpp -g
% env LD_PRELOAD=/usr/lib/libtcmalloc_debug.so ./a.out
memory allocation/deallocation mismatch at 0x13280e0: allocated with new
[] being deallocated with delete
Abort (core dumped)
22/30
• 実行しないでエラーを検出するツール
• ASan, Valgrindは実行時解析
• 商用のものが性能がよいことが多い(ex. Coverity)
• cppcheck ; apt install cppcheck
• Visual Studio の /analyzeとか
• cppcheckでも表示される
• clangに付属のscan-build(Xcodeの静的解析ツール)
静的解析ツール
% cppcheck -enable=all no_free.cpp
Checking no_free.cpp...
[no_free.cpp:4]: (error) Mismatching allocation and deallocation: p
int f(int a) {
if (a != 3 || a != 5) return 4;
return 2;
}
t.cpp(3) : warning C6289: 不適切な演算子です:|| を使用した相互排除は常に 0
でない定数となります。&& を使用しようとしましたか?
23/30
• Linux kernelの中身を調べるツール
• https://wiki.ubuntu.com/Kernel/Systemtap
• インストール
• sudo apt install systemtap
• シンボル付きのカーネルが必要
• バイナリのインストール方法
• https://wiki.ubuntu.com/Kernel/Systemtap#Where_to_get_de
bug_symbols_for_kernel_X.3F
• 自分でビルド
• https://wiki.ubuntu.com/Kernel/Systemtap#How_do_I_build_a
_debuginfo_kernel_if_one_isn.27t_available.3F
• sudo dpkg -i linux-*.ddeb linux-*.debのあとリブート
SystemTap
24/30
• kernel内の関数の場所を調べる
• write(2)を調べたい
• 内部的にはSyS_write
• fs/read_write.cの514行目にある
• sync(8)したときの流れをみたい
• ext4のwriteまわりの関数は何があるだろう
• なんとなくext4_writepagesを見てみる
使い方
% stap -l 'kernel.function("SyS_write")'
kernel.function("SyS_write@/*/fs/read_write.c:514")
% stap -l 'kernel.function("ext4_write*")'
kernel.function("ext4_write_begin@/*/fs/ext4/inode.c:958")
kernel.function("ext4_write_dquot@/*/fs/ext4/super.c:5032")
...
kernel.function("ext4_writepage_trans_blocks@/*/fs/ext4/inode.c:4888")
kernel.function("ext4_writepages@/*/fs/ext4/inode.c:2473")
25/30
• awkライクなスクリプト言語
• コンパイルして実行できる
• 裏でkernel moduleになってloadされる
• ext4_writepagesが呼ばれたときにpidが自分が指定したコマ
ンドなら関数名と引数とバックトレースを表示する
• .call ; 呼ばれたとき
• pid() ; 今のpid
• $$parms ; 引数
stapスクリプト
>cat a.stp
probe kernel.function("ext4_writepages").call
{
if (pid() != target()) exit()
printf("%s(%s)¥n", probefunc(), $$parms)
print_backtrace()
}
26/30
• -c <command>でコマンド実行
• いろいろ試してみる
syncコマンドを実行してみる
% sudo stap a.stp -c "sync"
ext4_writepages(mapping=0xffff880402e009b0 wbc=0xffff880405a9bc58)
0xffffffff81247f80 : ext4_writepages+0x0/0xdb0 [kernel]
0xffffffff8115e47e : do_writepages+0x1e/0x40 [kernel]
0xffffffff811eaae0 : __writeback_single_inode+0x40/0x2a0 [kernel]
0xffffffff811eb9ca : writeback_sb_inodes+0x26a/0x440 [kernel]
0xffffffff811ebc3f : __writeback_inodes_wb+0x9f/0xd0 [kernel]
0xffffffff811ebef3 : wb_writeback+0x283/0x320 [kernel]
0xffffffff811ed77c : bdi_writeback_workfn+0x11c/0x4a0 [kernel]
0xffffffff81086078 : process_one_work+0x178/0x470 [kernel]
0xffffffff81086e91 : worker_thread+0x121/0x410 [kernel]
0xffffffff8108dc79 : kthread+0xc9/0xe0 [kernel]
0xffffffff8173a3e8 : ret_from_fork+0x58/0x90 [kernel]
27/30
• g++ user-backtrace.cpp -g
• sudo stap user.stp –c “./a.out” | c++filt
• print_ubacktrace
でバックトレース
ユーザランドのバックトレース
probe process("./a.out").function("*").call {
printf("%s -> %s¥n", thread_indent(1), probefunc())
}
probe process("./a.out").function("*").return {
printf("%s <- %s¥n", thread_indent(-1), probefunc())
}
0 a.out(23897): -> main
10 a.out(23897): -> h(int)
26 a.out(23897): -> g(int)
30 a.out(23897): -> f(int)
40 a.out(23897): <- g(int)
42 a.out(23897): -> f(int)
45 a.out(23897): <- g(int)
47 a.out(23897): <- h(int)
48 a.out(23897): -> g(int)
51 a.out(23897): -> f(int)
54 a.out(23897): <- g(int)
56 a.out(23897): -> f(int)
59 a.out(23897): <- g(int)
60 a.out(23897): <- h(int)
62 a.out(23897): <- main
64 a.out(23897): <- 0x7fb394b0cf45
#include <stdio.h>
void f(int x) {
printf("f=%d¥n", x);
}
void g(int x) {
puts("g");
for (int i = 0; i < 2; i++) {
f(x + i);
}
}
void h(int x) {
puts("h");
for (int i = 0; i < 2; i++) {
g((x + i) * (x + i));
}
}
int main(int argc, char *[]) {
h(argc);
}
28/30
• SystemTap Beginners Guide
• https://sourceware.org/systemtap/SystemTap_Beginners_Guide/
• スクリプトの文法
• https://sourceware.org/systemtap/langref/Language_elements.html
• サンプルいろいろ
• https://sourceware.org/systemtap/examples/
• 関数いろいろ
• https://sourceware.org/systemtap/man/
• 例 addrからn個のデータを文字列化 kernel_string_n(addr, n)
• Ftraceというkernelのevent記録ツールもある
• cf. ファイルキャッシュクリアの謎
• http://www.slideshare.net/herumi/kernel-fcachebug
参考文献
29/30
• CPU内部のカウンタを使って詳細な情報を収集
• apt install linux-tools-common
• perf listで取得可能な一覧を表示
• VM上では取得可能なeventは極めて制限される
• perf stat -e <イベント> <実行ファイル>
• sudo perf top ; 現在のkernelの詳細なtopを表示
• IntelのVTuneはこれのGUI版(便利)
perf
List of pre-defined events (to be used in -e):
cpu-cycles OR cycles [Hardware event]
instructions [Hardware event]
cache-references [Hardware event]
cache-misses [Hardware event]
branch-instructions OR branches [Hardware event]
...
30/30

More Related Content

What's hot

Python 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうPython 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうRyuji Tsutsui
 
画像処理ライブラリ OpenCV で 出来ること・出来ないこと
画像処理ライブラリ OpenCV で 出来ること・出来ないこと画像処理ライブラリ OpenCV で 出来ること・出来ないこと
画像処理ライブラリ OpenCV で 出来ること・出来ないことNorishige Fukushima
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門Norishige Fukushima
 
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)Shota Imai
 
Constexpr 中3女子テクニック
Constexpr 中3女子テクニックConstexpr 中3女子テクニック
Constexpr 中3女子テクニックGenya Murakami
 
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編Fixstars Corporation
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなKentaro Matsui
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使うKazuhiro Suga
 
メタプログラミングって何だろう
メタプログラミングって何だろうメタプログラミングって何だろう
メタプログラミングって何だろうKota Mizushima
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 TipsTakaaki Suzuki
 
コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」Masahito Zembutsu
 
中3女子でもわかる constexpr
中3女子でもわかる constexpr中3女子でもわかる constexpr
中3女子でもわかる constexprGenya Murakami
 
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜Preferred Networks
 
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説Takateru Yamagishi
 
C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜
C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜
C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜UnityTechnologiesJapan002
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 
x86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTx86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTtakesako
 
プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜京大 マイコンクラブ
 

What's hot (20)

Python 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそうPython 3.9からの新定番zoneinfoを使いこなそう
Python 3.9からの新定番zoneinfoを使いこなそう
 
画像処理ライブラリ OpenCV で 出来ること・出来ないこと
画像処理ライブラリ OpenCV で 出来ること・出来ないこと画像処理ライブラリ OpenCV で 出来ること・出来ないこと
画像処理ライブラリ OpenCV で 出来ること・出来ないこと
 
組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門組み込み関数(intrinsic)によるSIMD入門
組み込み関数(intrinsic)によるSIMD入門
 
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
強化学習の基礎と深層強化学習(東京大学 松尾研究室 深層強化学習サマースクール講義資料)
 
C++ マルチスレッド 入門
C++ マルチスレッド 入門C++ マルチスレッド 入門
C++ マルチスレッド 入門
 
Constexpr 中3女子テクニック
Constexpr 中3女子テクニックConstexpr 中3女子テクニック
Constexpr 中3女子テクニック
 
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編
CPU / GPU高速化セミナー!性能モデルの理論と実践:理論編
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
DockerコンテナでGitを使う
DockerコンテナでGitを使うDockerコンテナでGitを使う
DockerコンテナでGitを使う
 
メタプログラミングって何だろう
メタプログラミングって何だろうメタプログラミングって何だろう
メタプログラミングって何だろう
 
今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips今日からできる!簡単 .NET 高速化 Tips
今日からできる!簡単 .NET 高速化 Tips
 
コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」コンテナの作り方「Dockerは裏方で何をしているのか?」
コンテナの作り方「Dockerは裏方で何をしているのか?」
 
中3女子でもわかる constexpr
中3女子でもわかる constexpr中3女子でもわかる constexpr
中3女子でもわかる constexpr
 
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
Pythonの理解を試みる 〜バイトコードインタプリタを作成する〜
 
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
CUDAのアセンブリ言語基礎のまとめ PTXとSASSの概説
 
C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜
C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜
C#×LLVM=アセンブラ!? 〜詳説・Burstコンパイラー〜
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
x86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNTx86x64 SSE4.2 POPCNT
x86x64 SSE4.2 POPCNT
 
プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜プログラムを高速化する話Ⅱ 〜GPGPU編〜
プログラムを高速化する話Ⅱ 〜GPGPU編〜
 
π計算
π計算π計算
π計算
 

Similar to C/C++プログラマのための開発ツール

StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件yaegashi
 
Ubuntuで始めるコンテナ技術入門
Ubuntuで始めるコンテナ技術入門Ubuntuで始めるコンテナ技術入門
Ubuntuで始めるコンテナ技術入門Takenori Matsumoto
 
高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたこと高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたことMITSUNARI Shigeo
 
Android デバッグ小ネタ
Android デバッグ小ネタAndroid デバッグ小ネタ
Android デバッグ小ネタl_b__
 
Exploring the x64
Exploring the x64Exploring the x64
Exploring the x64FFRI, Inc.
 
Linux Performance Analysis in 15 minutes
Linux Performance Analysis in 15 minutesLinux Performance Analysis in 15 minutes
Linux Performance Analysis in 15 minutesYohei Azekatsu
 
Xbyakの紹介とその周辺
Xbyakの紹介とその周辺Xbyakの紹介とその周辺
Xbyakの紹介とその周辺MITSUNARI Shigeo
 
ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!Yohei Fushii
 
Lxc cf201207-presen
Lxc cf201207-presenLxc cf201207-presen
Lxc cf201207-presenKouhei Maeda
 
第一回コンテナ情報交換会@関西
第一回コンテナ情報交換会@関西第一回コンテナ情報交換会@関西
第一回コンテナ情報交換会@関西Masahide Yamamoto
 
Docker調査20150704
Docker調査20150704Docker調査20150704
Docker調査20150704HommasSlide
 
MINCS – containers in the shell script
MINCS – containers in the shell scriptMINCS – containers in the shell script
MINCS – containers in the shell scriptMasami Hiramatsu
 
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)inaz2
 
A story of porting OpenBSD/luna88k
A story of porting OpenBSD/luna88kA story of porting OpenBSD/luna88k
A story of porting OpenBSD/luna88kKenji Aoyama
 
Lvmを縮小してみた
Lvmを縮小してみたLvmを縮小してみた
Lvmを縮小してみたkoedoyoshida
 
スタート低レイヤー #0
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0Kiwamu Okabe
 
カーネルVm関西その参lt
カーネルVm関西その参ltカーネルVm関西その参lt
カーネルVm関西その参ltcosmo0920
 

Similar to C/C++プログラマのための開発ツール (20)

StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件StackExchangeで見たシステムプログラミング案件
StackExchangeで見たシステムプログラミング案件
 
Ubuntuで始めるコンテナ技術入門
Ubuntuで始めるコンテナ技術入門Ubuntuで始めるコンテナ技術入門
Ubuntuで始めるコンテナ技術入門
 
高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたこと高速な暗号実装のためにしてきたこと
高速な暗号実装のためにしてきたこと
 
Android デバッグ小ネタ
Android デバッグ小ネタAndroid デバッグ小ネタ
Android デバッグ小ネタ
 
Exploring the x64
Exploring the x64Exploring the x64
Exploring the x64
 
Linux Performance Analysis in 15 minutes
Linux Performance Analysis in 15 minutesLinux Performance Analysis in 15 minutes
Linux Performance Analysis in 15 minutes
 
Xbyakの紹介とその周辺
Xbyakの紹介とその周辺Xbyakの紹介とその周辺
Xbyakの紹介とその周辺
 
Slide
SlideSlide
Slide
 
ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!ホームディレクトリに埋もれた便利なコードをさがせ!
ホームディレクトリに埋もれた便利なコードをさがせ!
 
Lxc cf201207-presen
Lxc cf201207-presenLxc cf201207-presen
Lxc cf201207-presen
 
第一回コンテナ情報交換会@関西
第一回コンテナ情報交換会@関西第一回コンテナ情報交換会@関西
第一回コンテナ情報交換会@関西
 
Docker調査20150704
Docker調査20150704Docker調査20150704
Docker調査20150704
 
MINCS – containers in the shell script
MINCS – containers in the shell scriptMINCS – containers in the shell script
MINCS – containers in the shell script
 
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
ROP Illmatic: Exploring Universal ROP on glibc x86-64 (ja)
 
A story of porting OpenBSD/luna88k
A story of porting OpenBSD/luna88kA story of porting OpenBSD/luna88k
A story of porting OpenBSD/luna88k
 
Mincs 日本語版
Mincs 日本語版Mincs 日本語版
Mincs 日本語版
 
Lvmを縮小してみた
Lvmを縮小してみたLvmを縮小してみた
Lvmを縮小してみた
 
スタート低レイヤー #0
スタート低レイヤー #0スタート低レイヤー #0
スタート低レイヤー #0
 
カーネルVm関西その参lt
カーネルVm関西その参ltカーネルVm関西その参lt
カーネルVm関西その参lt
 
about DakotagUI
about DakotagUIabout DakotagUI
about DakotagUI
 

More from MITSUNARI Shigeo

暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学MITSUNARI Shigeo
 
範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコルMITSUNARI Shigeo
 
暗認本読書会13 advanced
暗認本読書会13 advanced暗認本読書会13 advanced
暗認本読書会13 advancedMITSUNARI Shigeo
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenMITSUNARI Shigeo
 
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法MITSUNARI Shigeo
 
WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装MITSUNARI Shigeo
 
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化MITSUNARI Shigeo
 
BLS署名の実装とその応用
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用MITSUNARI Shigeo
 

More from MITSUNARI Shigeo (20)

暗号技術の実装と数学
暗号技術の実装と数学暗号技術の実装と数学
暗号技術の実装と数学
 
範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル範囲証明つき準同型暗号とその対話的プロトコル
範囲証明つき準同型暗号とその対話的プロトコル
 
暗認本読書会13 advanced
暗認本読書会13 advanced暗認本読書会13 advanced
暗認本読書会13 advanced
 
暗認本読書会12
暗認本読書会12暗認本読書会12
暗認本読書会12
 
暗認本読書会11
暗認本読書会11暗認本読書会11
暗認本読書会11
 
暗認本読書会10
暗認本読書会10暗認本読書会10
暗認本読書会10
 
暗認本読書会9
暗認本読書会9暗認本読書会9
暗認本読書会9
 
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgenIntel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
Intel AVX-512/富岳SVE用SIMDコード生成ライブラリsimdgen
 
暗認本読書会8
暗認本読書会8暗認本読書会8
暗認本読書会8
 
暗認本読書会7
暗認本読書会7暗認本読書会7
暗認本読書会7
 
暗認本読書会6
暗認本読書会6暗認本読書会6
暗認本読書会6
 
暗認本読書会5
暗認本読書会5暗認本読書会5
暗認本読書会5
 
暗認本読書会4
暗認本読書会4暗認本読書会4
暗認本読書会4
 
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
深層学習フレームワークにおけるIntel CPU/富岳向け最適化法
 
私とOSSの25年
私とOSSの25年私とOSSの25年
私とOSSの25年
 
WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装WebAssembly向け多倍長演算の実装
WebAssembly向け多倍長演算の実装
 
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
Lifted-ElGamal暗号を用いた任意関数演算の二者間秘密計算プロトコルのmaliciousモデルにおける効率化
 
楕円曲線と暗号
楕円曲線と暗号楕円曲線と暗号
楕円曲線と暗号
 
HPC Phys-20201203
HPC Phys-20201203HPC Phys-20201203
HPC Phys-20201203
 
BLS署名の実装とその応用
BLS署名の実装とその応用BLS署名の実装とその応用
BLS署名の実装とその応用
 

C/C++プログラマのための開発ツール

  • 2. • (主にLinux上における)プログラム開発、 デバッグ、不具合調査のためのツールの紹介 • 広く浅く • 同じことを複数の手段で • キーワードや「できること」を知っていれば後は自分で 概要と目的 2/30
  • 3. • gcc, clang • ソース読み • ag, GNU GLOBAL, Doxygen, callgrind • デバッグ • gdb, objdump, c++filt, core dump, addr2line • メモリチェック • ASan, Valgrind, TCMalloc • 静的解析 • cppcheck, scan-build • 実行時解析 • SystemTap, perf 目次 3/30
  • 4. • 警告系オプション • -Wall -Wextra ; 必須 • コンパイラの警告を無視してはいけない • https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect- Options.html • -Wnon-virtual-dtor • -Woverloaded-virtual ; virtualを同名の関数で隠してしまった • 他に-Weffc++など変わりものも • clangには全ての警告を表示する-Weverythingがある • ときどき便利 • -pedantic ; C/C++標準でないGNU拡張を使っていないか C/C++コンパイル時のオプション(1/2) 4/30
  • 5. • 最適化オプション • -Ofast ; 最適化最大 • -march=native ; コンパイル環境のCPUに合わせた最適化 • -DNDEBUG ; assert()マクロの無効化 • デバッグ用オプション • -g ; シンボル情報がつく(速度に影響はない) • -g3 ; マクロも見えるようになる • -S ; アセンブリ出力(生成コードが意図通りか?) • objdumpによる逆アセンブルも便利 • objdump -CSlw -M intel • -C ; C++の関数名を見やすくする(demangle) • -S ; ソースコードを併記 C/C++コンパイル時のオプション(2/2) 5/30
  • 6. • <vector>のソースを見たいが場所はどこ? • echo "#include <vector>"|gcc -x c++ -E -|lv • プロファイルをとる • -pgオプション • ただし関数に介入するのでオーバーヘッドあり • perfやVTune(後述)など別のものがおすすめ その他 6/30
  • 7. • GNU GLOBAL • https://www.gnu.org/software/global/ • 関数が定義されている場所を行ったり来たりできる • 使い方 • 見たいソースのトップディレクトリでgtags • 各自のエディタに応じて設定すること • あとはがんばって:-) • ag • 高速なgrep ; grep -Irw 単語 ./ • apt install silversearcher-ag • highway(https://github.com/tkengo/highway)というのも • Visual Studioなどの統合環境 一度は触ってみるとよい ソースコード読み補助ツール 7/30
  • 8. • ソースコードのドキュメント自動化ツール • apt install doxygen • ソースコードのコメントに書くとマニュアルを生成する • doxygen -gで設定ファイルを作成 • doxygen Doxyfileでhtmlを作成 • コメントのつけ方 • http://www.doxygen.jp/docblocks.html Doxygen 8/30
  • 9. • Graphviz • ものの依存関係を画像表示する • apt install graphviz • 関数の呼び出し関係の画像化 • DoxygenのDoxyfileでHAVE_DOT = yes • callgrind • 呼び出し回数などを可視化 • apt install kcachegrind • Valgrind(後述)の一部 • valgrind –tool=callgrind <binary> • kcachegrind callgrind.out.* 関数呼び出しの可視化 9/30
  • 10. • 落ちるプログラムをデバッグする • rで実行して落ちたところでbtでバックトレースをみる gdb(デバッグツール) #include <stdio.h> #include <stdlib.h> #include <memory.h> int main(int argc, char *argv[]) { char buf[32]; int n = argc == 1 ? 10 : atoi(argv[1]); printf("n=%d¥n", n); memset(buf, 'A', n); printf("n=%d¥n", n); printf("buf[%d-1]=%c¥n", n, buf[n - 1]); } gcc -g t.cpp gdb --args ./aout 10000 r ... Program received signal SIGSEGV, Segmentation fault. 10/30
  • 11. • s ; ステップ実行(1行ずつ実行する) • 最適化オプションありではコードの位置がずれることが多い • 一度コマンドを実行したあと[return]で同じコマンドを実行 • fin ; 関数の中から外に出るまで一気に動く • n ; 関数の中に入らないでステップ実行 • up/down スタックフレームを登ったり降りたり • p 変数 ; 変数を表示する gdbのコマンドいくつか 11/30
  • 12. • gdbを使わず落ちる場所だけ調べる • 落ちたときのレジスタ, ip, backtrace, メモリマップなど表示 libSegFault.so env LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so ¥ SEGFAULT_SIGNALS=all ./a.out 10000 *** Segmentation fault Register dump: RAX: 00007ffeb2670fd0 RBX: 0000000000000000 RCX: 00007ffeb2671000 ... Backtrace: /lib/x86_64-linux-gnu/libc.so.6(memset+0x5d)[0x7f4cf774050d] ./a.out[0x400699] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf5)[0x7f4cf76d5f45] ./a.out[0x400569] 12/30
  • 13. • https://github.com/herumi/misc/blob/master/dev/dea d-lock.cpp • ./a.outで返って来ない • psでa.outのプロセスidをさがす • sudo gdb -p <プロセスid> • cat /proc/<プロセスid>/mapsなどで情報いろいろみえる • info threadでthreadが2個あることがわかる • t 2で2番目に行きbtでバックトレース dead lockした実行ファイル(1/2) (gdb) info thread Id Target Id Frame 2 Thread 0x7f5b3883b700 (LWP 964) "a.out" ... 1 Thread 0x7f5b3984a780 (LWP 963) "a.out" ... (gdb) t 2 [Switching to thread 2 (Thread 0x7f5b3883b700 (LWP 964))] #0 0x00007f5b38f15f1c in __lll_lock_wait () from ... 13/30
  • 14. • dead-lock.cppの12行目で止まっていた • straceで呼ばれたシステムコールの確認 • ltraceで呼ばれたライブラリ関数の確認 • アドレスは毎回変わる • sudo sysctl -w kernel.randomize_va_space=0で固定化 • セキュリティ低下につながるので本番環境では禁止 dead lockした実行ファイル(1/2) gdb) bt #0 ...5f1c in __lll_lock_wait () from ...libpthread.so.0 #1 ...1649 in _L_lock_909 () from ... libpthread.so.0 #2 ...1470 in pthread_mutex_lock () from ... libpthread.so.0 #3 ...0ecc in __gthread_mutex_lock (__mutex=0x7ffcec6c3e90) at ... #4 ...12fa in std::mutex::lock (this=0x7ffcec6c3e90) at ... #5 ...137e in std::lock_guard<std::mutex>::lock_guard ... #6 ...0fbf in f (m=...) at dead-lock.cpp:12 #7 ...274f in std::_Bind_simple<void (*(std:... 14/30
  • 15. • プログラムが異常終了したときの情報を保存したもの • /proc/sys/kernel/core_patternで保存ファイル名を指定 • 例 : カレントディレクトリにcore.<プロセス名> • shellでcoreサイズを制限していないか確認 • bashならulimit -a ; tcshならlimit • 0ならcoreファイルは作られないので設定する • ulimit -c unlimited / limit coredumpsize unlimited • SEGVする実行ファイルを実行する • gdb –c <coreファイル> ./a.outでいつものように操作 core dump sudo sh -c 'echo core.%p > /proc/sys/kernel/core_pattern' 15/30
  • 16. • coreファイルが無かったとき最低限の情報 • ip ; SEGVしたときに実行していたコードのアドレス • libc-2.19.soのip番目をさがす • 00007f55ac1c650d - 7f55ac13a000 = 0x8c50d • libcの0x8c50d番目は何の関数か • addr2lineを使う • memsetのようだ ; 引数がおかしい?(と推測) • objdump -Sでもわかる dmesg [609339.455455] a.out[1088]: segfault at 7fffa910f130 ip 00007f55ac1c650d sp 00007fffa910ca08 error 6 in libc-2.19.so[7f55ac13a000+1ba000] % addr2line -e /lib/x86_64-linux-gnu/libc-2.19.so 0x8c50d /build/eglibc-oGUzwX/eglibc-2.19/string/../sysdeps/x86_64/memset.S:80 16/30
  • 17. • Address Sanitizer(ASan) • メモリ関係のエラーのチェック • バッファオーバーフロー • ヒープオーバーフロー • スタックバッファーオーバーフロー • メモリリーク • -fsanitize=addressをつけてコンパイル メモリ関係 17/30
  • 18. • ./a.out 32 ; 問題なしだが33を指定すると 実行してみる % ./a.out 33 n=33 ==11277==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffdad25b7a0 at pc 0x0000004a691f bp 0x7ffdad25b650 sp 0x7ffdad25ae08 WRITE of size 33 at 0x7ffdad25b7a0 thread T0 #0 0x4a691e in __asan_memset ... This frame has 5 object(s): [32, 36) '' [48, 52) '' [64, 72) '' [96, 128) 'buf' [160, 164) 'n' <== Memory access at offset 128 partially underflows this variable SUMMARY: AddressSanitizer: stack-buffer-overflow ??:0 __asan_memset Shadow bytes around the buggy address: 0x100035a436a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x100035a436b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x100035a436c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x100035a436d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0x100035a436e0: 00 00 00 00 f1 f1 f1 f1 04 f2 04 f2 00 f2 f2 f2 =>0x100035a436f0: 00 00 00 00[f2]f2 f2 f2 04 f3 f3 f3 00 00 00 00 ... 18/30
  • 19. • deleteしていない メモリ解放し忘れ % cat t.cpp int main() { char *p = new char[10]; } % clang++-3.6 -fsanitize=address no_free.cpp -g && ./a.out ================================================================= ==11320==ERROR: LeakSanitizer: detected memory leaks Direct leak of 10 byte(s) in 1 object(s) allocated from: #0 0x4dc4f2 in operator new[](unsigned long) (/a.out+0x4dc4f2) #1 0x4dd24f in main /no_free.cpp:3:12 #2 0x7fc2af5a4f44 in __libc_start_main /build/eglibc-oGUzwX/eglibc- 2.19/csu/libc-start.c:287 SUMMARY: AddressSanitizer: 10 byte(s) leaked in 1 allocation(s). 19/30
  • 20. • delete p; • cf. std::stringかstd::unique_ptr<char> p(new char[10])を使え delete追加したけど間違ってる % cat t.cpp int main() { char *p = new char[10]; delete p; } % clang++-3.6 -fsanitize=address no_free.cpp -g && ./a.out ==11464==ERROR: AddressSanitizer: alloc-dealloc-mismatch (operator new [] vs operator delete) on 0x60200000eff0 #0 0x4dc942 in operator delete(void*) (/a.out+0x4dc942) #1 0x4dd30a in main /no_free.cpp:4:2 #2 0x7fc0cf759f44 in __libc_start_main /build/eglibc-oGUzwX/eglibc- 2.19/csu/libc-start.c:287 #3 0x435f46 in _start (/a.out+0x435f46) 20/30
  • 21. • ASanとは別のメモリチェックツール • ASanとは排他的(ASanありでビルドしたものは動かない) • 特別なコンパイルオプションは不要 • 他の便利なオプション--leak-check=full, --tool=callgrind Valgrind % g++ no_free.cpp -g % valgrind ./a.out ==11471== Memcheck, a memory error detector ==11471== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==11471== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info ==11471== Command: ./a.out ==11471== ==11471== Mismatched free() / delete / delete [] ==11471== at 0x4C2C2BC: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==11471== by 0x40066E: main (no_free.cpp:4) ==11471== Address 0x5a22040 is 0 bytes inside a block of size 10 alloc'd ==11471== at 0x4C2B800: operator new[](unsigned long) (in... 21/30
  • 22. • 高速なmalloc/free • http://goog-perftools.sourceforge.net/doc/tcmalloc.html • リンク時にライブラリを指定する • LD_PRELOADで指定する • デバッグ機能も持つ • apt install libtcmalloc* • 他にも同様のツールはいろいろある TCMalloc % g++ no_free.cpp -g % env LD_PRELOAD=/usr/lib/libtcmalloc_debug.so ./a.out memory allocation/deallocation mismatch at 0x13280e0: allocated with new [] being deallocated with delete Abort (core dumped) 22/30
  • 23. • 実行しないでエラーを検出するツール • ASan, Valgrindは実行時解析 • 商用のものが性能がよいことが多い(ex. Coverity) • cppcheck ; apt install cppcheck • Visual Studio の /analyzeとか • cppcheckでも表示される • clangに付属のscan-build(Xcodeの静的解析ツール) 静的解析ツール % cppcheck -enable=all no_free.cpp Checking no_free.cpp... [no_free.cpp:4]: (error) Mismatching allocation and deallocation: p int f(int a) { if (a != 3 || a != 5) return 4; return 2; } t.cpp(3) : warning C6289: 不適切な演算子です:|| を使用した相互排除は常に 0 でない定数となります。&& を使用しようとしましたか? 23/30
  • 24. • Linux kernelの中身を調べるツール • https://wiki.ubuntu.com/Kernel/Systemtap • インストール • sudo apt install systemtap • シンボル付きのカーネルが必要 • バイナリのインストール方法 • https://wiki.ubuntu.com/Kernel/Systemtap#Where_to_get_de bug_symbols_for_kernel_X.3F • 自分でビルド • https://wiki.ubuntu.com/Kernel/Systemtap#How_do_I_build_a _debuginfo_kernel_if_one_isn.27t_available.3F • sudo dpkg -i linux-*.ddeb linux-*.debのあとリブート SystemTap 24/30
  • 25. • kernel内の関数の場所を調べる • write(2)を調べたい • 内部的にはSyS_write • fs/read_write.cの514行目にある • sync(8)したときの流れをみたい • ext4のwriteまわりの関数は何があるだろう • なんとなくext4_writepagesを見てみる 使い方 % stap -l 'kernel.function("SyS_write")' kernel.function("SyS_write@/*/fs/read_write.c:514") % stap -l 'kernel.function("ext4_write*")' kernel.function("ext4_write_begin@/*/fs/ext4/inode.c:958") kernel.function("ext4_write_dquot@/*/fs/ext4/super.c:5032") ... kernel.function("ext4_writepage_trans_blocks@/*/fs/ext4/inode.c:4888") kernel.function("ext4_writepages@/*/fs/ext4/inode.c:2473") 25/30
  • 26. • awkライクなスクリプト言語 • コンパイルして実行できる • 裏でkernel moduleになってloadされる • ext4_writepagesが呼ばれたときにpidが自分が指定したコマ ンドなら関数名と引数とバックトレースを表示する • .call ; 呼ばれたとき • pid() ; 今のpid • $$parms ; 引数 stapスクリプト >cat a.stp probe kernel.function("ext4_writepages").call { if (pid() != target()) exit() printf("%s(%s)¥n", probefunc(), $$parms) print_backtrace() } 26/30
  • 27. • -c <command>でコマンド実行 • いろいろ試してみる syncコマンドを実行してみる % sudo stap a.stp -c "sync" ext4_writepages(mapping=0xffff880402e009b0 wbc=0xffff880405a9bc58) 0xffffffff81247f80 : ext4_writepages+0x0/0xdb0 [kernel] 0xffffffff8115e47e : do_writepages+0x1e/0x40 [kernel] 0xffffffff811eaae0 : __writeback_single_inode+0x40/0x2a0 [kernel] 0xffffffff811eb9ca : writeback_sb_inodes+0x26a/0x440 [kernel] 0xffffffff811ebc3f : __writeback_inodes_wb+0x9f/0xd0 [kernel] 0xffffffff811ebef3 : wb_writeback+0x283/0x320 [kernel] 0xffffffff811ed77c : bdi_writeback_workfn+0x11c/0x4a0 [kernel] 0xffffffff81086078 : process_one_work+0x178/0x470 [kernel] 0xffffffff81086e91 : worker_thread+0x121/0x410 [kernel] 0xffffffff8108dc79 : kthread+0xc9/0xe0 [kernel] 0xffffffff8173a3e8 : ret_from_fork+0x58/0x90 [kernel] 27/30
  • 28. • g++ user-backtrace.cpp -g • sudo stap user.stp –c “./a.out” | c++filt • print_ubacktrace でバックトレース ユーザランドのバックトレース probe process("./a.out").function("*").call { printf("%s -> %s¥n", thread_indent(1), probefunc()) } probe process("./a.out").function("*").return { printf("%s <- %s¥n", thread_indent(-1), probefunc()) } 0 a.out(23897): -> main 10 a.out(23897): -> h(int) 26 a.out(23897): -> g(int) 30 a.out(23897): -> f(int) 40 a.out(23897): <- g(int) 42 a.out(23897): -> f(int) 45 a.out(23897): <- g(int) 47 a.out(23897): <- h(int) 48 a.out(23897): -> g(int) 51 a.out(23897): -> f(int) 54 a.out(23897): <- g(int) 56 a.out(23897): -> f(int) 59 a.out(23897): <- g(int) 60 a.out(23897): <- h(int) 62 a.out(23897): <- main 64 a.out(23897): <- 0x7fb394b0cf45 #include <stdio.h> void f(int x) { printf("f=%d¥n", x); } void g(int x) { puts("g"); for (int i = 0; i < 2; i++) { f(x + i); } } void h(int x) { puts("h"); for (int i = 0; i < 2; i++) { g((x + i) * (x + i)); } } int main(int argc, char *[]) { h(argc); } 28/30
  • 29. • SystemTap Beginners Guide • https://sourceware.org/systemtap/SystemTap_Beginners_Guide/ • スクリプトの文法 • https://sourceware.org/systemtap/langref/Language_elements.html • サンプルいろいろ • https://sourceware.org/systemtap/examples/ • 関数いろいろ • https://sourceware.org/systemtap/man/ • 例 addrからn個のデータを文字列化 kernel_string_n(addr, n) • Ftraceというkernelのevent記録ツールもある • cf. ファイルキャッシュクリアの謎 • http://www.slideshare.net/herumi/kernel-fcachebug 参考文献 29/30
  • 30. • CPU内部のカウンタを使って詳細な情報を収集 • apt install linux-tools-common • perf listで取得可能な一覧を表示 • VM上では取得可能なeventは極めて制限される • perf stat -e <イベント> <実行ファイル> • sudo perf top ; 現在のkernelの詳細なtopを表示 • IntelのVTuneはこれのGUI版(便利) perf List of pre-defined events (to be used in -e): cpu-cycles OR cycles [Hardware event] instructions [Hardware event] cache-references [Hardware event] cache-misses [Hardware event] branch-instructions OR branches [Hardware event] ... 30/30