# 機能設計書 95-sanstats

## 概要

本ドキュメントは、LLVMプロジェクトにおけるsanstats（サニタイザ統計処理ツール）の機能設計書である。sanstatsは、Clangの-fsanitize-statsオプションで生成されたサニタイザ統計データを処理・表示するツールである。

### 本機能の処理概要

sanstatsは、サニタイザのランタイム統計情報を含むバイナリファイルを読み込み、各CFI（Control Flow Integrity）チェックポイントの実行回数をシンボル化して表示するツールである。

**業務上の目的・背景**：CFIなどのサニタイザ機能のパフォーマンスオーバーヘッドを分析するために、各チェックポイントがどの程度実行されているかを把握する必要がある。sanstatsは統計データをソースコード位置と紐付けて表示し、最適化の優先度判断を支援する。

**機能の利用シーン**：
- CFIのパフォーマンスオーバーヘッド分析
- ホットスポットの特定
- サニタイザ最適化の効果測定
- セキュリティチェックのカバレッジ確認

**主要な処理内容**：
1. 統計ファイルの読み込み
2. アドレスのシンボル化（ファイル名:行番号:関数名への変換）
3. CFI種別の判定と表示
4. 実行回数の表示

**関連システム・外部連携**：LLVMシンボライザ、-fsanitize-statsオプションで計装されたバイナリと連携する。

**権限による制御**：特になし（スタンドアロンツール）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| N/A | コマンドライン | 主画面 | コマンドライン引数による操作 |

## 機能種別

データ処理 / 統計分析

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| filename | string | Yes | 統計データファイル | 存在するファイルであること |
| --demangle | bool | No | 関数名をデマングル | - |

### 入力データソース

- サニタイザ統計ファイル（-fsanitize-statsで生成）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| アドレス | hex | 16進アドレス（18文字幅） |
| ソース位置 | text | ファイル名:行番号 |
| 関数名 | text | 関数名（デマングル可能） |
| CFI種別 | text | cfi-vcall/cfi-nvcall/cfi-derived-cast/cfi-unrelated-cast/cfi-icall |
| 実行回数 | integer | チェックポイントの実行回数 |

### 出力先

標準出力（stdout）

## 処理フロー

### 処理シーケンス

```
1. コマンドライン解析
   └─ 入力ファイル名とオプション取得
2. ファイル読み込み
   └─ MemoryBuffer::getFile()
3. ポインタサイズ取得
   └─ ファイル先頭1バイト
4. モジュールごとの処理
   └─ ReadModule()で各モジュール解析
5. エントリごとの処理
   └─ アドレス/データ読込
   └─ シンボル化
   └─ CFI種別判定
   └─ 出力
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[コマンドライン解析]
    B --> C[MemoryBuffer::getFile]
    C --> D{読込成功?}
    D -->|No| E[エラー出力]
    D -->|Yes| F[ポインタサイズ取得]
    F --> G{ファイル終端?}
    G -->|Yes| H[終了]
    G -->|No| I[ReadModule]
    I --> J[モジュール名取得]
    J --> K{有効?}
    K -->|No| L[エラー終了]
    K -->|Yes| M[LLVMSymbolizer作成]
    M --> N{エントリ終端?}
    N -->|Yes| G
    N -->|No| O[アドレス/データ読込]
    O --> P[symbolizeCode]
    P --> Q[KindFromData]
    Q --> R[CountFromData]
    R --> S[結果出力]
    S --> N
    E --> H
    L --> H
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-95-01 | アドレス調整 | 戻りアドレスなので-1して正確な位置取得 | シンボル化時 |
| BR-95-02 | 終端判定 | アドレスとデータが両方0でモジュール終端 | モジュール読込時 |
| BR-95-03 | Kind抽出 | 上位kSanitizerStatKindBitsビットがCFI種別 | データ解析時 |
| BR-95-04 | Count抽出 | 残りビットが実行回数 | データ解析時 |

### 計算ロジック

- KindFromData: Data >> (SizeofPtr * 8 - kSanitizerStatKindBits)
- CountFromData: Data & ((1ull << (SizeofPtr * 8 - kSanitizerStatKindBits)) - 1)

## データベース操作仕様

### 操作別データベース影響一覧

本ツールはデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | ファイルエラー | ファイル読込失敗 | ファイルパス確認 |
| 1 | フォーマットエラー | short read（不完全データ） | 有効な統計ファイルを使用 |

### リトライ仕様

リトライは実装されていない。

## トランザクション仕様

該当なし

## パフォーマンス要件

- ファイルサイズに比例した処理時間
- シンボル化処理がボトルネックになる可能性あり

## セキュリティ考慮事項

- 入力ファイルの基本的な形式検証のみ実施
- バッファオーバーフロー対策としてBegin/End境界チェック

## 備考

統計ファイル形式：1バイトポインタサイズ + [モジュール名\0 + (アドレス, データ)* + (0, 0)]*

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SanitizerStats.h | `llvm/include/llvm/Transforms/Utils/SanitizerStats.h` | kSanitizerStatKindBits、SanStat_*列挙型 |
| 1-2 | sanstats.cpp | `llvm/tools/sanstats/sanstats.cpp` | KindFromData、CountFromData関数 |

**読解のコツ**: SanStat_*列挙型がCFI種別を定義し、上位ビットにKind、下位ビットにCountが格納される。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sanstats.cpp | `llvm/tools/sanstats/sanstats.cpp` | main関数 |

**主要処理フロー**:
1. **128-130行目**: コマンドライン解析
2. **132-140行目**: ファイル読込とバッファ取得
3. **146行目**: ポインタサイズ取得（先頭1バイト）
4. **147-154行目**: ReadModule()をループ呼び出し

#### Step 3: モジュール処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sanstats.cpp | `llvm/tools/sanstats/sanstats.cpp` | ReadModule関数 |

**主要処理フロー**:
- **54-69行目**: モジュール名（ファイル名）の取得
- **71-74行目**: LLVMSymbolizerの設定
- **76-125行目**: エントリごとのループ処理
- **77-80行目**: アドレスとデータの読込
- **94-101行目**: シンボル化と出力
- **103-122行目**: CFI種別の判定と表示

### プログラム呼び出し階層図

```
main() [sanstats.cpp:128]
    │
    ├─ cl::ParseCommandLineOptions()
    │
    ├─ MemoryBuffer::getFile() [132行目]
    │
    └─ [ループ] ReadModule() [54行目]
           │
           ├─ モジュール名抽出
           │
           ├─ LLVMSymbolizer作成
           │
           └─ [エントリループ]
                  │
                  ├─ ReadLE() x2 [アドレス、データ]
                  │
                  ├─ symbolizeCode()
                  │
                  ├─ KindFromData() [35-37行目]
                  │
                  ├─ CountFromData() [39-41行目]
                  │
                  └─ 出力（switch-case）
```

### データフロー図

```
[入力]                        [処理]                        [出力]

統計ファイル ──────────▶ MemoryBuffer ──────▶ バイト列
                              │
                              ▼
                        ポインタサイズ取得
                              │
                              ▼
モジュール名 ◀───────── 文字列抽出
                              │
                              ▼
                        エントリ読込 ──────▶ (Address, Data)
                              │
                              ▼
                        KindFromData ─────▶ CFI種別
                        CountFromData ────▶ 実行回数
                              │
                              ▼
                        LLVMSymbolizer ───▶ DILineInfo
                              │
                              ▼
                        フォーマット出力 ──▶ 標準出力
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sanstats.cpp | `llvm/tools/sanstats/sanstats.cpp` | ソース | 全機能の実装 |
| SanitizerStats.h | `llvm/include/llvm/Transforms/Utils/SanitizerStats.h` | ヘッダ | 統計種別定義 |
