組込みOSとシステムテスト
この資料はテスト技法セミナーのスライドに関する補助資料です.目次は右側に表示されます.
1 🧩 GCC関連ツール別:ログ取得と解析の詳細
1.1 gprof(GNU Profiler)
📥 ログ取得
コンパイル時に
-pgオプションを付与:gcc -pg program.c -o program実行すると
gmon.outが生成される。
📊 ログ解析
gprofコマンドで解析:gprof ./program gmon.out > profile.txt出力内容:
- 各関数の呼び出し回数
- 関数ごとの実行時間
- 呼び出し関係(コールグラフ)
🔎 補助ツール
gprof2dot + Graphviz → 関数呼び出し関係をグラフ化
gprof ./program gmon.out | gprof2dot | dot -Tpng -o callgraph.png
1.2 gcov(GNU Coverage)
📥 ログ取得
コンパイル時に
-fprofile-arcs -ftest-coverageを付与:gcc -fprofile-arcs -ftest-coverage program.c -o program実行後に
.gcdaファイルが生成される。
📊 ログ解析
gcovコマンドで行ごとの実行回数を解析:gcov program.c出力内容:
- 各行の実行回数
- カバレッジ率(テストで通った行の割合)
🔎 補助ツール
lcov → HTMLレポート生成
lcov --capture --directory . --output-file coverage.info genhtml coverage.info --output-directory outgcovr → XML/HTML形式でレポート生成(CI/CD統合に便利)
1.3 perf(Linux perf events)
📥 ログ取得
実行時に
perf statを利用:perf stat ./program出力内容:
- 実行命令数(instructions)
- CPUサイクル数
- キャッシュミス、分岐予測失敗など
📊 ログ解析
詳細解析:
perf record ./program perf reportperf annotateでアセンブリレベルの性能解析も可能。
🔎 補助ツール
Flamegraph → スタックトレースを可視化
perf record -F 99 -a -g -- ./program perf script | stackcollapse-perf.pl | flamegraph.pl > flamegraph.svg
1.4 Valgrind(特に callgrind)
📥 ログ取得
実行時に
callgrindを利用:valgrind --tool=callgrind ./programcallgrind.out.*が生成される。
📊 ログ解析
callgrind_annotateで解析:callgrind_annotate callgrind.out.*出力内容:
- 関数ごとの命令数
- 呼び出し関係と命令分布
🔎 補助ツール
- KCachegrind / QCachegrind → GUIで命令数や関数呼び出しを可視化
1.5 strace / ltrace
📥 ログ取得
システムコールやライブラリ呼び出しをトレース:
strace ./program ltrace ./program出力内容:
- 実行時に呼び出されたシステムコール
- ライブラリ関数呼び出し
📊 ログ解析
ログをファイルに保存して統計化:
strace -o trace.log ./programstrace-analyzer や sysdig で統計・可視化可能。
1.6 ✅ まとめ:ツールと解析対応表
| GCC関連ツール | ログ内容 | 解析方法 | 補助ツール |
|---|---|---|---|
| gprof | 関数呼び出し回数・時間 | gprof |
gprof2dot |
| gcov | 行ごとの実行回数・カバレッジ | gcov, lcov, gcovr |
HTMLレポート |
| perf | CPU命令数・性能イベント | perf report, perf annotate |
Flamegraph |
| valgrind (callgrind) | 命令数・関数統計 | callgrind_annotate |
KCachegrind |
| strace/ltrace | システムコール・ライブラリ呼び出し | strace-analyzer, sysdig |
統計・可視化 |
gcov は実行毎にlogを累積するが,他は,上書きする.
2 GDB
GDB(GNU Debugger)は、C/C++などのプログラムを実行しながら内部状態を調べるための標準的なデバッガです。よく使うコマンドは「ブレークポイント」「実行制御」「変数確認」「スタック解析」などに分類できます。以下に主要コマンドと具体例をまとめます。
2.1 🔑 基本操作の流れ
デバッグ情報付きでコンパイル
gcc -g sample.c -o sampleGDB起動
gdb ./sample
2.2 📍 ブレークポイント関連
break (b): 指定位置で停止
(gdb) break main # main関数の先頭で停止 (gdb) break sample.c:42 # 42行目で停止delete: ブレークポイント削除
(gdb) delete 1 # 1番のブレークポイント削除disable / enable: 一時的に無効化・再有効化
(gdb) disable 1 (gdb) enable 1watch: 変数の値が変わったら停止
(gdb) watch x
2.3 ▶️ 実行制御
run (r): プログラム開始
(gdb) run arg1 arg2continue (c): 次のブレークポイントまで実行
(gdb) continuenext (n): 次の行へ進む(関数呼び出しには入らない)
(gdb) nextstep (s): 次の行へ進む(関数呼び出しに入る)
(gdb) stepfinish (fin): 現在の関数を最後まで実行
(gdb) finishuntil (u): 指定行まで一気に進む
(gdb) until 50
2.4 🔍 変数・コード確認
list (l): ソースコード表示
(gdb) list 30,40 # 30〜40行を表示print (p): 変数や式の値を表示
(gdb) print x (gdb) print x+1 (gdb) print *ptrset var: 実行中の変数値を変更
(gdb) set var x = 10
2.5 🧠 スタック・関数解析
backtrace (bt): 関数呼び出し履歴を表示
(gdb) btframe (f): スタックフレームを切り替え
(gdb) frame 2info locals: ローカル変数一覧表示
(gdb) info locals
2.6 🛑 終了
quit (q): GDB終了
(gdb) quit
2.7 ✅ まとめ
- ブレークポイント系:
break,watch,delete
- 実行制御系:
run,continue,next,step,finish,until
- 変数確認系:
print,set var,info locals
- スタック解析系:
backtrace,frame
- 終了:
quit
これらを組み合わせることで、プログラムの流れを追跡し、変数や関数呼び出しを詳細に解析できるのがGDBの強みです。
3 LLVM .ll
以下では、clang が生成する .ll ファイル(LLVM IR:LLVM Intermediate Representation)について、 仕組み・用途・読み方・生成方法・デバッグ方法まで、包括的に説明します。
3.1 .ll ファイルとは何か(概要)
.ll は LLVM IR の「テキスト形式」のファイルです。
LLVM には 2 種類の IR 表現があります:
| 形式 | 拡張子 | 特徴 |
|---|---|---|
| テキスト形式 LLVM IR | .ll |
人間が読める。アセンブリ風の構文。 |
| バイナリ形式 LLVM ビットコード | .bc |
最適化やコンパイルの中間形式。高速で扱える。 |
clang は C/C++/Objective-C などのフロントエンドで、 ソース → LLVM IR(.ll or .bc) を経由して バックエンド(x86, arm, riscv など)にコード生成します。
つまり .ll は:
👉 C/C++ とマシン語の中間にある “中間言語(IR)” のソースコード
です。
3.2 .ll ファイルの生成方法(clangコマンド)
基本
clang -S -emit-llvm test.c -o test.ll最適化レベルを指定
clang -S -emit-llvm -O2 test.c -o test.llバイナリ形式(ビットコード)を生成
clang -emit-llvm -c test.c -o test.bc.bc → .ll の変換(llvm-dis)
llvm-dis test.bc -o test.ll4 LLVM / clang
4.1 .ll ファイルの構造
典型的な .ll(LLVM IR)は以下のような構造を持ちます:
- モジュール情報 (target triple / data layout)
- グローバル定義
- 関数定義(基本ブロックと命令)
- メタデータ(デバッグ情報・属性)
4.2 .ll ファイルの例と説明
入力(Cコード):
int add(int a, int b) {
return a + b;
}LLVM IR(test.ll):
; ModuleID = 'test.c'
source_filename = "test.c"
target triple = "x86_64-unknown-linux-gnu"
define i32 @add(i32 %a, i32 %b) {
entry:
%0 = add i32 %a, %b
ret i32 %0
}ポイント解説
| LLVM IR | 意味 |
|---|---|
define i32 @add(i32 %a, i32 %b) |
add 関数の定義(引数は i32 型) |
entry: |
基本ブロック(BB) |
%0 = add i32 %a, %b |
LLVM IR 命令。レジスタは %番号 |
ret i32 %0 |
戻り値 |
LLVM IR は「仮想レジスタ」を使い、SSA(Single Static Assignment)形式。
4.3 .ll が利用される目的
① 最適化の可視化・研究
LLVM の最適化パス(-O1/-O2/-O3)が どのようにコードを変更しているかを確認できる。
opt -O2 test.ll -S -o opt.ll② バックエンド間の移植を簡単にする
LLVM IR を一度生成すれば、 x86, arm, riscv, wasm など多様なバックエンドにコード生成できる。
llc -march=riscv32 test.ll -o test.s③ 静的解析・形式手法・研究用途
多くの研究が LLVM IR を分析対象としている(KLEE、BOLT、Sanitizer など)。
④ コンパイラ教育・可視化
C → LLVM IR → 機械語までの流れが明確になるので教育用途でも優秀。
4.4 LLVM IR の特徴(他の中間言語と比較)
| 特徴 | 説明 |
|---|---|
| SSA形式 | 命令ごとに新しいレジスタを割り当てる。最適化に適する。 |
| 強い型付け | i8/i32/f32 など型情報が厳密。 |
| 構造化されているが低レベル | レジスタ、ポインタ、メモリアクセスなどが明確。 |
| 高レベルの構造(関数・型)も保持 | 最適化のための十分な情報を持つ。 |
4.5 よく使う LLVM IR デバッグコマンド
● .ll の最適化を実行する
opt -O2 input.ll -S -o output.ll● .ll → マシン語
llc input.ll -o output.s● .ll を実行(JIT)
lli test.ll● 中間パスの分析
opt -analyze -mem2reg test.ll4.6 .ll を読むための基本知識
🔹 “%番号” は SSA 形式の仮想レジスタ
例:%3 は一度だけ代入される変数。
🔹 “@名前” はグローバル変数や関数
例:@add は関数。
🔹 i32, i8*, float は厳密な型
型システムが厳密なので混乱しにくい。
🔹 ブロックラベル(entry, if.then, loop.header)
制御フローが明確に見える。
🔹 alloca はスタック確保
C のローカル変数は多くの場合 alloc される。
🔹 load / store がメモリアクセス
C のメモリアクセスそのまま。
4.7 まとめ
clang の .ll は:
- LLVM IR(テキスト形式)
- C/C++ の中間表現
- 最適化研究・コード解析・バックエンド生成に必須
- シンプルで SSA ベースのアセンブリ風言語
であり、コンパイラ学習や静的解析の基盤となっています。