> [!abstract] 概要
> テストケースが与えられ、プログラムが失敗する。テストケースのどの状況が特定の障害に関与しているのか。デルタデバッギングアルゴリズムは、ある障害を引き起こすテストケースを、なお障害を再現する最小テストケースへ汎化・簡略化する。また、通過テストケースと失敗テストケースの差分を分離する。事例研究では、Mozilla ウェブブラウザが 95 のユーザー操作後にクラッシュした。プロトタイプ実装はこの入力を自動的に 3 つの関連ユーザー操作に簡略化した。同様に、896 行の HTML を障害を引き起こす 1 行に簡略化した。この事例研究では 139 回の自動テスト実行、すなわち 500 MHz PC 上で 35 分を要した。
## 論文情報
- **タイトル**: Simplifying and Isolating Failure-Inducing Input
- **著者**: [[Andreas Zeller]](Universität des Saarlandes)、Ralf Hildebrandt(DeTeLine)
- **媒体**: IEEE Transactions on Software Engineering, Vol. 28, No. 2
- **発表年**: 2002年2月
## 概要
デルタデバッギングは、プログラムの障害を引き起こす入力を自動的に簡略化・分離する手法である。2 つのアルゴリズムを提案する。ddmin は障害を引き起こすテストケースを 1-最小(任意の単一変更を除去すると障害が消える)になるまで簡略化する。dd は ddmin を拡張し、通過テストケースと失敗テストケースの間の 1-最小障害誘発差分を分離する。
## 問題設定
- **入力**: 障害を引き起こすテストケース c✘(変更の集合)と、正常に動作するテストケース c✔(通常は空入力)
- **出力**: ddmin では c✘ の 1-最小部分集合、dd では c✔ と c✘ の間の 1-最小差分
- **前提**: テスト関数 test が各テストケースに対して PASS(✔)、FAIL(✘)、UNRESOLVED(?)のいずれかを返す。入力は変更 δ₁, ..., δₙ に分解可能
## 提案手法
### ddmin: 最小化デルタデバッギングアルゴリズム
テストケースを n 個の部分集合 Δ₁, ..., Δₙ に分割し、以下の 4 規則を再帰的に適用する。
1. **部分集合への縮小**: いずれかの Δᵢ 単独でテストが失敗すれば、Δᵢ を新たなテストケースとして粒度 n=2 で再帰
2. **補集合への縮小**: いずれかの ∇ᵢ = c'✘ \ Δᵢ でテストが失敗すれば、∇ᵢ を新たなテストケースとして粒度 n−1 で再帰
3. **粒度の増加**: いずれのテストも失敗しなければ、部分集合数を 2n に倍増して再試行
4. **完了**: 粒度が |c'✘| に達したら(各部分集合が単一変更)、結果は 1-最小
**Figure 5: ddmin アルゴリズムの形式定義**
![[_attachments/delta-debugging/fig05-ddmin-algorithm.png]]
(Figure 5. 最小化デルタデバッギングアルゴリズム ddmin の形式定義。4 つの分岐規則と再帰不変条件を示す。)
### dd: 汎用デルタデバッギングアルゴリズム
ddmin を拡張し、失敗テストケースの縮小だけでなく通過テストケースの拡大も行う。テストが通過するたびに通過テストケース c'✔ を拡大し、失敗するたびに c'✘ を縮小する。ddmin の 4 規則に加え、2 つの規則を追加する。
5. **補集合への増加**: c'✘ \ Δᵢ が通過すれば、c'✘ \ Δᵢ を新たな通過テストケースとして採用
6. **部分集合への増加**: c'✔ ∪ Δᵢ が通過すれば、c'✔ ∪ Δᵢ を新たな通過テストケースとして採用
**Figure 15: dd アルゴリズムの形式定義**
![[_attachments/delta-debugging/fig15-dd-algorithm.png]]
(Figure 15. 汎用デルタデバッギングアルゴリズム dd の形式定義。ddmin を拡張し、通過テストケースの拡大規則を加えた 6 分岐。)
### 簡略化と分離の違い
- **簡略化**(ddmin): テストケースの各部分を関連性ある(除去すると障害が消える)ものにする
- **分離**(dd): テストケースの 1 つの関連部分を特定する(その特定部分を除去すると障害が消える)
分離は簡略化より効率的だが、プログラマが通過テストケースの共通文脈を念頭に置く必要がある。
## 新規性
- テストケース簡略化を自動化した**最初の汎用手法**。従来は手動の二分探索か、特定ドメイン向けの手法しかなかった
- ddmin と dd の 2 アルゴリズムにより、簡略化(全要素の関連性確認)と分離(単一の障害誘発差分の特定)の両方を形式化
- 先行研究 dd' (Zeller, 1999) との差異: dd' は単調性を仮定し 1-最小性を保証しない。dd は単調性を仮定せず 1-最小差分を保証
## 実験設定
- **実験環境**: Linux PC、500 MHz Pentium III。WYNOT プロトタイプ実装
- **対象プログラム**: GCC 2.95.2、Mozilla(Netscape 6 相当)、UNIX ユーティリティ 6 種(NROFF、TROFF、FLEX、CRTPLOT、UL、UNITS)
- **テストケース**: GCC バグ(C ソースコード)、Mozilla バグ #24735(HTML + ユーザー操作)、Miller のファズ入力(10³〜10⁶ 文字、16 種)
- **評価指標**: 最小化後の入力サイズ、テスト実行回数、テスト精度(低精度 vs 高精度=バックトレース比較)
**Figure 8: GCC 入力 bug.c の最小化過程**
![[_attachments/delta-debugging/fig08-gcc-minimization.png]]
(Figure 8. GCC 入力 bug.c の最小化過程。横軸はテスト実行回数、縦軸は入力サイズ。755 文字から 77 文字へ約 800 回のテストで簡略化。)
## 実験結果
### GCC
- 755 文字の C プログラム → **77 文字**の 1-最小テストケースに簡略化(731 テスト、34 秒)
- dd による分離では **59 テスト**で 2 文字の障害誘発差分を特定(ddmin の 731 テストに対し大幅削減)
- GCC 最適化オプション 31 個の中から、障害を防ぐオプション(`-ffast-math`、`-fforce-addr`)を 7 テストで特定
### Mozilla
- 711 の X イベント中 95 のユーザー操作 → **3 操作**に簡略化(82 テスト、21 分)
- 896 行の HTML → **1 行** `<SELECT NAME="priority" MULTIPLE SIZE=7>` に簡略化(57 テスト)
- 文字単位の追加簡略化で `<SELECT>` の 8 文字に到達
- dd による分離では **7 テスト**で 1 文字 `<` が障害誘発差分として特定
### UNIX ファズ入力
- 6 ユーティリティ × 16 ファズ入力 = 96 テスト中 42 回(43%)クラッシュ
- バッファオーバーラン型(FLEX: 2,121 文字、UL: 516 文字)と不正コマンド型(NROFF: 2 文字、CRTPLOT: 1 文字)に分類
- 高精度テスト(バックトレース比較)では最小化結果が大きくなる(NROFF: 低精度 2 文字 → 高精度 60 文字)
- dd は ddmin に比べ大幅にテスト回数を削減。ファズ入力でも dd は対数的テスト回数で 1 文字差分を分離
**Figure 6: 粒度増加を伴うテストケース最小化の詳細例**
![[_attachments/delta-debugging/fig06-minimization-example.png]]
(Figure 6. 変更 1, 7, 8 が最小テストケースとなる例。粒度を 2→4→8 と増加させながら、各部分集合とその補集合をテストする 19 ステップの過程。)
**Figure 16: GCC 入力の障害誘発差分の絞り込み**
![[_attachments/delta-debugging/fig16-narrowing-difference.png]]
(Figure 16. dd アルゴリズムによる障害誘発差分の絞り込み。59 テストで通過テストケースと失敗テストケースの差分が 2 文字に収束する過程。)
**Figure 17: 特定された障害誘発差分**
![[_attachments/delta-debugging/fig17-failure-inducing-difference.png]]
(Figure 17. 特定された障害誘発差分。`mult` 関数内の `i = i + j + 1;` の代入を除去すると障害が消えることを示す。)
## 考察
- テスト精度の選択がトレードオフを生む。高精度(バックトレース完全一致)では最小化結果が大きくなるが、元の障害と同一の障害を再現する。低精度では別の障害に収束する可能性がある
- 分離(dd)は簡略化(ddmin)より常に効率的だが、結果の解釈にプログラマの文脈理解が必要
- 入力構造の知識(文法など)を活用すれば、構文的に妥当な部分集合のみを生成でき、UNRESOLVED の削減と性能向上が期待できる(本論文では未実装)
- プログラム解析(スライシング)との相補性: 解析は因果関係を否定(依存関係のない文を除外)し、テストは因果関係を証明(変更による障害消失を確認)する
## 強み / 弱点・課題
### 強み
- 入力構造に関する事前知識を必要としない汎用手法
- 1-最小性の形式的保証と計算量の理論的解析
- GCC・Mozilla・UNIX ユーティリティへの実証で実用性を示した
- 自動回帰テストとの統合が容易(障害検出 → 自動簡略化のパイプライン)
### 弱点・課題
- 最悪計算量が O(n²) で、大規模入力(FLEX の 6,749 文字ファズ入力で 37,000 テスト以上)では実用的でない場合がある
- 入力を平坦な原子リストとして扱うため、木構造入力(XML、AST)では効率が悪い(→ HDD [[@2006__ICSE__HDD - Hierarchical Delta Debugging]] で解決)
- 単調性の欠如(変更の取り消しが可能な場合)への対処は未解決。`<A></A><B>` の例で、`</A>` が `<A>` の障害を取り消すケースが指摘されている
- テスト関数が決定論的再現を前提としており、非決定論的障害への適用は困難