基礎勉強会 #4
低レイヤの開発ツール
今回の話
- ソフトウェア開発につかう解析ツール
- デバッガ: gdb
- メモリプロファイラ: valgrind
- トレーサ: strace, ltrace
- パフォーマンスプロファイラ: time, gprof, perf
ツール
ツール
- 動的解析ツール
- 静的解析ツール
動的解析ツール
- デバッガ
- プロファイラ
- パフォーマンスプロファイラ
- メモリプロファイラ
- トレーサ
静的解析ツール
- フォーマッタ
- リンタ or リント
- メトリクス計測ツール
- 並列性バグ検出ツール
- OSS ライセンス検出ツール
コンパイラ・インタプリタ自体
- エラーだけでなく警告にも目を通そう
デバッグ
デバッグ
- print デバッグ
- デバッガを使ったデバッグ
print デバッグ
- 簡単なデバッグ方法
- 簡単にできるが非効率的になりがち
- そもそもログで出すべきかもしれない
デバッガを使ったデバッグ
- デバッグといえばこれ
- ツールを直に使う場合もあるが
- IDE に統合されている場合が多い
- 今回の説明ではツールをそのまま使う
- IDE は好きに使ってください
gdb
gdb
- GNUプロジェクトのデバッガ
- 現在バージョン 8
-
- 他のデバッガも gdb を参考に作られたものが多い
- コマンドは似ている
gdb の対応言語
- C/C++
- D
- Go
- Objective-C
- OpenCL C
- Fortran
- Pascal
- Rust
- Modula-2
- Ada
デモ
gdb の起動
- プログラムの実行
- コアファイルの読み込み
- プロセスへのアタッチ
TUI
- C-x C-a で TUI 切り替え
- アセンブリやレジスタの表示もできる
- layout asm
- layout regs
ステップ実行
- continue
- step
- next
リバースステップ実行
準備として record コマンドを実行する
- reverse-continue
- reverse-step
- reverse-next
print / p
式の値を表示する
(gdb) print 100
$7 = 100
(gdb) print/x 100
$8 = 0x64
(gdb) print/o 100
$9 = 0144
(gdb) print/t 100
$10 = 1100100
(gdb) p p
$11 = (person_t) 0x5555557572a0
(gdb) p (char*)p.name
$12 = 0x5555557572c0 "Alice"
break / b
ブレークポイントを設定する
- 関数
- ファイル中の行
- 条件も設定可能
作ったものは削除・無効化可能
- delete
- disable, enable
break
(gdb) b main.c:89 if *name == 'A'
Breakpoint 8 at 0x555555554aca: file main.c, line 89.
watch
式の値が変わった場合にブレイクする
backtrace / bt
バックトレースを表示する
(gdb) bt
#0 print_persons (p=0x555555757260) at main.c:78
#1 0x0000555555554b5a in main (argc=1, argv=0x7fffffffd698) at main.c:98
フレーム間の移動は up, down
その他
- info
- help
valgrind
valgrind とは
- 動的解析ツールをつくるためのフレームワーク
- 次のようなツールが提供されている
- memcheck
- cachegrind
- callgrind
- helgrind
- DRD
- massif
- DHAT
- SGcheck
- BBV
メモリーリーク
- やっかいなバグのひとつ
- 検出しづらい
- 単体テスト、結合テストでは見つからない
- 長時間実行してもメモリ使用量が増えてもバグかどうかは判断しづらい
- 時間経過につれてメモリ使用量は増える
- エラーが出ても気づきづらい
- OOM killer が出てているのでログを見れば分かる
- が、他のエラーを想定しがち
デモ
Valgrind
$ valgrind --leak-check=full <program>
==17417== Memcheck, a memory error detector
==17417== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==17417== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==17417== Command: ./main
==17417==
==17417==
==17417== HEAP SUMMARY:
==17417== in use at exit: 2,048 bytes in 2 blocks
==17417== total heap usage: 2 allocs, 0 frees, 2,048 bytes allocated
==17417==
==17417== 1,024 bytes in 1 blocks are definitely lost in loss record 1 of 2
==17417== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17417== by 0x10865B: f (main.c:6)
==17417== by 0x108689: main (main.c:20)
==17417==
==17417== 1,024 bytes in 1 blocks are definitely lost in loss record 2 of 2
==17417== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==17417== by 0x108674: g (main.c:13)
==17417== by 0x108693: main (main.c:21)
==17417==
==17417== LEAK SUMMARY:
==17417== definitely lost: 2,048 bytes in 2 blocks
==17417== indirectly lost: 0 bytes in 0 blocks
==17417== possibly lost: 0 bytes in 0 blocks
==17417== still reachable: 0 bytes in 0 blocks
==17417== suppressed: 0 bytes in 0 blocks
==17417==
==17417== For counts of detected and suppressed errors, rerun with: -v
==17417== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
Helgrind
$ valgrind --tool=helgrind ./main
略
==18542== This conflicts with a previous write of size 4 by thread #2
==18542== Locks held: none
==18542== at 0x10874B: f (main.c:8)
==18542== by 0x4C36C26: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==18542== by 0x4E496DA: start_thread (pthread_create.c:463)
==18542== by 0x518288E: clone (clone.S:95)
==18542== Address 0x309014 is 0 bytes inside data symbol "var"
略
トレーサ
トレーサ
- strace
- システムコールをトレース
- ltrace
- ライブラリ API をトレース
デモ
パフォーマンスプロファイラ
パフォーマンスの測定
- time
- gprof
- perf
time
% time sleep 3
sleep 3 0.00s user 0.00s system 0% cpu 3.003 total
コマンドの実行だけで終わる単純な計測
gprof
- コンパイル時にコード中の関数に仕組みし、計測する
- コンパイルの際にオプションが必要
$ gprof main gmon.out
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls us/call us/call name
51.65 0.56 0.56 1000 562.99 562.99 g
48.88 1.10 0.53 1000 532.83 532.83 f
(以下略)
perf
プロファイリングとトレーシングのツールコレクション
- サブコマンドの例
- top
- stat
- record, report
- timechart
perf record
コマンドを実行し、プロファイルをとる
% sudo perf record -a -g -F 997 sleep 10
[ perf record: Woken up 11 times to write data ]
[ perf record: Captured and wrote 6.181 MB perf.data (20127 samples) ]
% sudo perf report --stdio
# Total Lost Samples: 0
#
# Samples: 20K of event 'cycles:ppp'
# Event count (approx.): 14794093886
#
# Children Self Command Shared Object Symbol
# ........ ........ ............... ...............................................................
#
37.91% 0.00% swapper [kernel.kallsyms] [k] secondary_startup_64
|
---secondary_startup_64
|
|--33.30%--start_secondary
| cpu_startup_entry
perf top
システム全体での関数ごとの CPU 使用率
Samples: 55K of event 'cycles:ppp', Event count (approx.): 10091157696
Overhead Shared Object Symbol
3.20% [kernel] [k] dw_readl
1.61% chrome [.] 0x0000000004e67400
1.56% chrome [.] 0x00000000033b0b83
1.50% chrome [.] _fini
1.33% [kernel] [k] module_get_kallsym
1.29% [kernel] [k] syscall_return_via_sysret
0.97% libpthread-2.27.so [.] __pthread_mutex_lock
0.83% perf [.] 0x00000000001f78a0
perf stat
パフォーマンスカウンタの統計を取得する
$ sudo perf stat date
Thu Jan 10 01:19:27 JST 2019
Performance counter stats for 'date':
1.113312 task-clock (msec) # 0.607 CPUs utilized
0 context-switches # 0.000 K/sec
0 cpu-migrations # 0.000 K/sec
59 page-faults # 0.053 M/sec
993714 cycles # 0.893 GHz
785347 instructions # 0.79 insn per cycle
151250 branches # 135.856 M/sec
7899 branch-misses # 5.22% of all branches
0.001833304 seconds time elapsed
参考文献
- マニュアル
- gdb
- valgrind
- 各ツールの man ページ
- 詳解システム・パフォーマンス
次回の候補
-
アルゴリズム
-
データ構造→次回
-
ネットワーク
- 仮想化・コンテナ
- RDBMS, NoSQL
- オブジェクト指向プログラミング
- 関数型プログラミング
- 継続的インテグレーション
- 言語処理系
- 文章術
- 量子コンピューティング
低レイヤの開発ツール
By Shingo Suzuki
低レイヤの開発ツール
- 1,011