デバッグの理論と実践

―なぜプログラムはうまく動かないのか

[cover photo]
TOPICS
Programming
発行年月日
PRINT LENGTH
444
ISBN
978-4-87311-593-1
原書
Why Programs Fail, 2nd Edition
FORMAT
Print
3,520円
この商品は品切れ再入荷未定です

『ビューティフルコード』『Making Software』の著者の一人であり、GNU Data Display Debugger(DDD)の開発者である著者が、なぜプログラムがうまく動かないかについて、効率的な原因究明とデバッグ方法を提案。なぜ「系統的」で「自動的」なデバッグが必要なのかの重要性を説き、そしてそれを実現するための手法として、差分デバッグ、科学的手法といった具体的なテクニックやさまざまなツールの詳細を紹介しています。デバッグ作業を効率化し、デバッグの苦痛を軽減するという著者の信念に基づいて書かれた本書は、多くのプログラマにとって福音となる一冊です。

目次

序文
まえがき
1 章 障害はどのように起こるのか
    1.1 プログラムがうまく動かない!
    1.2 欠陥から失敗へ
    1.3 時間と空間の迷路
    1.4 障害から修正まで
        1.4.1 問題の記録
        1.4.2 障害の再現
        1.4.3 テストケースの自動化と単純化
        1.4.4 感染の起源の検出
        1.4.5 最も感染起源の可能性が高い箇所に着目する
        1.4.6 感染の起源を特定する
        1.4.7 欠陥の修正
    1.5 自動デバッグテクニック
    1.6 バグか、故障か、それとも欠陥か?
    1.7 本章のまとめ
    1.8 ツール
    1.9 さらに詳しく学ぶ
    練習問題

2 章 問題の管理
    2.1 たくさんの問題たち
    2.2 問題の報告
        2.2.1 問題の事実
        2.2.2 製品の事実
        2.2.3 自動的に事実を取り出す
    2.3 問題の管理
    2.4 問題の分類
        2.4.1 重要度
        2.4.2 優先度
        2.4.3 識別子
        2.4.4 コメント
        2.4.5 通知
    2.5 問題の処理
    2.6 問題管理システムのメンテナンス
    2.7 問題としての要件
    2.8 重複を管理する
    2.9 関連問題と修正
    2.10 問題とテストの関係
    2.11 本章のまとめ
    2.12 ツール
    2.13 さらに詳しく学ぶ
    練習問題

3 章 プログラムを失敗させる
    3.1 デバッグ用テスト
    3.2 プログラムの制御
    3.3 プレゼンテーション層のテスト
        3.3.1 低レベルの対話操作
        3.3.2 システムレベルの対話操作
        3.3.3 高レベルの対話操作
        3.3.4 テスト結果の評価
    3.4 機能層のテスト
    3.5 ユニット層のテスト
    3.6 ユニットの分離
    3.7 デバッグに適したソフトウェアデザイン
    3.8 未知の問題の防止
    3.9 本章のまとめ
    3.10 ツール
    3.11 さらに詳しく学ぶ
    練習問題

4 章 問題の再現
    4.1 デバッグの最初の作業
    4.2 問題環境の再現
    4.3 プログラム実行の再現
        4.3.1 データの再現
        4.3.2 ユーザの対話操作の再現
        4.3.3 コミュニケーションの再現
        4.3.4 時刻の再現
        4.3.5 乱数の再現
        4.3.6 動作環境の再現
        4.3.7 スケジューリングの再現
        4.3.8 物理現象の影響
        4.3.9 デバッグツールの影響
    4.4 システムとの対話の再現
    4.5 ユニットに注目する
        4.5.1 コントロール層の構成
        4.5.2 制御の例
        4.5.3 モックオブジェクト
        4.5.4 さらなるユニット間の対話の制御
    4.6 クラッシュの再現
    4.7 本章のまとめ
    4.8 ツール
    4.9 さらに詳しく学ぶ
    練習問題

5 章 プログラムの単純化
    5.1 問題の単純化
    5.2 Gecko バグマラソン
    5.3 手動での単純化
    5.4 自動的な単純化
    5.5 単純化アルゴリズム
    5.6 ユーザの対話操作の単純化
    5.7 ランダム入力の単純化
    5.8 迅速に単純化する
        5.8.1 キャッシュ
        5.8.2 早期停止
        5.8.3 構文レベルでの単純化
        5.8.4 条件ではなく、差分を除去
    5.9 本章のまとめ
    5.10 ツール
    5.11 さらに詳しく学ぶ
    練習問題

6 章 科学的デバッグ
    6.1 デバッグの達人になる方法
    6.2 科学的手法
    6.3 科学的手法の適用
        6.3.1 sample のデバッグ:準備作業
        6.3.2 sample のデバッグ:仮説1
        6.3.3 sample のデバッグ:仮説2
        6.3.4 sample のデバッグ:仮説3
        6.3.5 sample のデバッグ:仮説4
    6.4 明確なデバッグ
    6.5 ログブックをつける
    6.6 略式デバッグ
    6.7 アルゴリズム的デバッグ
    6.8 仮説を立てる
        6.8.1 問題の記録
        6.8.2 プログラムコード
        6.8.3 失敗する実行
        6.8.4 代替の実行
        6.8.5 既出の仮説
    6.9 プログラムに関する推論
    6.10 本章のまとめ
    6.11 さらに詳しく学ぶ
    練習問題

7 章 エラーの推定
    7.1 値の起源の特定
    7.2 制御フローの理解
    7.3 依存関係の追跡
        7.3.1 ステートメントによる影響
        7.3.2 他のステートメントからの影響
        7.3.3 ステートメントの依存関係
        7.3.4 依存関係の追跡
        7.3.5 依存関係の活用
    7.4 プログラムスライス
        7.4.1 フォワードスライス
        7.4.2 バックワードスライス
        7.4.3 スライスの操作
        7.4.4 スライスの活用
        7.4.5 実行可能なスライス
    7.5 あやしいコードの追跡
        7.5.1 未初期化の変数の読み込み
        7.5.2 未使用の値
        7.5.3 到達不能なコード
    7.6 静的解析の限界
    7.7 本章のまとめ
    7.8 ツール
    7.9 さらに詳しく学ぶ
    練習問題

8 章 事実の観察
    8.1 状態を観察する
    8.2 実行のログ出力
        8.2.1 ログ出力関数
        8.2.2 ログ出力フレームワーク
        8.2.3 アスペクトを使ったログ出力
        8.2.4 バイナリレベルでのログ出力
    8.3 デバッガの使用
        8.3.1 デバッグセッション
        8.3.2 実行を制御する
        8.3.3 事後デバッグ
        8.3.4 データのログ出力
        8.3.5 関数の呼び出し
        8.3.6 修正と再開
        8.3.7 組み込みデバッガ
        8.3.8 デバッガについての注意事項
    8.4 イベントの問い合わせ
        8.4.1 ウォッチポイント
        8.4.2 統一的なイベントの問い合わせ
    8.5 インタプリタにフックを仕掛ける
    8.6 状態の可視化
    8.7 本章のまとめ
    8.8 ツール
    8.9 さらに詳しく学ぶ
    練習問題

9 章 感染源の追跡
    9.1 逆向きに推論する
    9.2 実行履歴の調査
    9.3 動的スライス
    9.4 起源の利用
    9.5 感染の追跡
    9.6 本章のまとめ
    9.7 ツール
    9.8 さらに詳しく学ぶ
    練習問題

10 章 期待される挙動のアサーション
    10.1 観察の自動化
    10.2 基本的なアサーション
    10.3 不変条件のアサーション
    10.4 正しいことを確認するアサーション
    10.5 仕様としてのアサーション
    10.6 アサーションから検証まで
    10.7 参照実行
    10.8 システムにおけるアサーション
        10.8.1 MALLOC_CHECK_ によるヒープの妥当性確認
        10.8.2 ElectricFence でバッファオーバーフローを回避する
        10.8.3 Valgrind を用いてメモリエラーを検出する
        10.8.4 言語拡張
    10.9 製品コードの確認
    10.10 本章のまとめ
    10.11 ツール
    10.12 さらに詳しく学ぶ
    練習問題

11 章 異常の検出
    11.1 正常な動作の取得
    11.2 カバレッジの比較
    11.3 統計的デバッグ
    11.4 現場のデータの収集
    11.5 動的な不変条件
    11.6 実行時の不変条件
    11.7 異常から欠陥へ
    11.8 本章のまとめ
    11.9 ツール
    11.10 さらに詳しく学ぶ
    練習問題

12 章 原因と結果
    12.1 原因と代替世界
    12.2 原因の検証
    12.3 実際の因果関係
    12.4 真の原因の発見
    12.5 原因の絞り込み
    12.6 原因の絞り込みの例
    12.7 共通のコンテキスト
    12.8 デバッグに関する原因
    12.9 本章のまとめ
    12.10 さらに詳しく学ぶ
    練習問題

13 章 障害原因の分離
    13.1 原因を自動的に分離する
    13.2 「分離」対「単純化」
    13.3 分離のアルゴリズム
    13.4 分離を実装する
    13.5 障害を誘発する入力の分離
    13.6 障害を誘発するスケジューリングの分離
    13.7 障害を誘発する変更の分離
    13.8 問題と限界
    13.9 本章のまとめ
    13.10 ツール
    13.11 さらに詳しく学ぶ
    練習問題

14 章 原因と結果の連鎖の特定
    14.1 原因が役に立たない
    14.2 プログラムの状態の取得
    14.3 プログラムの状態を比較する
    14.4 関連するプログラムの状態を特定
    14.5 原因と結果の連鎖を特定
    14.6 障害を誘発するコードの特定
    14.7 課題とリスク
    14.8 本章のまとめ
    14.9 ツール
    14.10 さらに詳しく学ぶ
    練習問題

15 章 欠陥の修正
    15.1 欠陥箇所の特定
    15.2 最もエラーの可能性が高い箇所に注目
    15.3 欠陥の検証
        15.3.1 そのエラーが障害を引き起こしたのか?
        15.3.2 原因は本当にエラーなのか?
        15.3.3 コーディングの前に考えよう
    15.4 欠陥の修正
        15.4.1 障害はもう起きないか?
        15.4.2 修正が新たな問題を誘発していないか?
        15.4.3 別の場所で同じミスをしていないか?
        15.4.4 やり残したことは?
    15.5 回避策
    15.6 本章のまとめ
    15.7 さらに詳しく学ぶ
    練習問題

16 章 失敗から学ぶ
    16.1 欠陥はどこにあるのか
    16.2 過去を検索する
    16.3 欠陥はどこから来たのか
    16.4 仕様策定時のエラー
        16.4.1 何をすべきか
        16.4.2 どこに焦点を当てるべきか
    16.5 プログラミング時のエラー
        16.5.1 何をすべきか
        16.5.2 どこに焦点を当てるべきか
    16.6 品質保証プロセスのエラー
        16.6.1 何をすべきか
        16.6.2 どこに焦点を当てるべきか
    16.7 問題の予測
        16.7.1 インポートからのエラー予測
        16.7.2 変更頻度からのエラー予測
        16.7.3 バグに対するキャッシュ
        16.7.4 リコンメンデーションシステム
        16.7.5 注意
    16.8 プロセスの改善
    16.9 本章のまとめ
    16.10 さらに詳しく学ぶ
    練習問題

付録 形式的な定義
    A.1 差分デバッグ
        A.1.1 設定
        A.1.2 成功する実行と失敗する実行
        A.1.3 テスト
        A.1.4 最小性
        A.1.5 単純化
        A.1.6 差分
        A.1.7 分離
    A.2 メモリグラフ
        A.2.1 形式的な構造
        A.2.2 データ構造の展開
        A.2.3 頂点と辺のマッチング
        A.2.4 共通部分グラフの導出
        A.2.5 グラフの差分の導出
        A.2.6 部分的な状態変更の適用
        A.2.7 C 言語での状態のキャプチャ
    A.3 原因と結果の連鎖
    
用語集
参考文献
索引