# 帳票設計書 3-メモリアロケーションプロファイルレポート

## 概要

本ドキュメントは、Julia の `Profile.Allocs.print()` によって出力されるメモリアロケーションプロファイルレポートの設計仕様を記述する。

### 本帳票の処理概要

本帳票は、Julia プログラムのメモリアロケーション（ヒープ割り当て）のプロファイリング結果をフラットまたはツリー形式で標準出力に出力するレポートである。`Profile.Allocs.@profile` マクロでアロケーションをサンプリングし、`Profile.Allocs.print()` を呼び出すことで、型別・関数別のアロケーション情報がレポートされる。

**業務上の目的・背景**：Julia プログラムのメモリ効率を改善するために、どの関数がどのような型のオブジェクトをどれだけ割り当てているかを把握する必要がある。過剰なメモリアロケーションは GC 負荷の増加やパフォーマンス低下の原因となるため、アロケーションホットスポットの特定は重要なチューニング作業である。

**帳票の利用シーン**：開発者がアプリケーションのメモリ使用量を最適化する際に使用する。特に、GC 時間が長い場合やメモリ消費が予想以上に大きい場合に、アロケーションの発生箇所を特定するために利用される。サンプルレートを調整することで、オーバーヘッドと精度のバランスを制御できる。

**主要な出力内容**：
1. Count/Bytes（サンプル数/バイト数）：各関数でのアロケーションサイズ合計
2. Overhead（オーバーヘッド）：各関数が直接行ったアロケーションサイズ
3. File（ファイル名）：関数が定義されているソースファイルのパス
4. Line（行番号）：行番号
5. Function（関数名）：関数の名前
6. Total snapshots（合計スナップショット数）と Total bytes（合計バイト数）

**帳票の出力タイミング**：`Profile.Allocs.@profile` マクロで式を実行した後、`Profile.Allocs.print()` を明示的に呼び出したとき。

**帳票の利用者**：Julia プログラムの開発者、メモリ最適化エンジニア。

## 帳票種別

集計表（メモリアロケーションサンプルの集計レポート）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | Julia REPL | julia> | `Profile.Allocs.print()` コマンド実行 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト（標準出力） |
| 用紙サイズ | N/A（テキスト出力） |
| 向き | N/A |
| ファイル名 | N/A（標準出力のみ） |
| 出力方法 | 標準出力への表示 |
| 文字コード | UTF-8 |

## 帳票レイアウト

### レイアウト概要

フラット形式とツリー形式の2種類をサポートする。デフォルトはツリー形式。レイアウトは CPU プロファイルレポートと同様だが、カウント値がサンプル数ではなくバイト数である点が異なる。

```
┌─────────────────────────────────────────────────────────────┐
│  (ツリー形式の場合)                                          │
│  Overhead ╎ [+additional indent] Count File:Line  Function   │
│  =========================================================   │
│     30000╎    50000 @MyPkg/src/main.jl:45  compute(...)      │
│          ╎     ╎   30000 @MyPkg/src/utils.jl:10  helper()    │
├─────────────────────────────────────────────────────────────┤
│  Total snapshots: 100                                        │
│  Total bytes: 50000                                          │
└─────────────────────────────────────────────────────────────┘
```

### ヘッダー部

CPU プロファイルレポートと同一フォーマット（`Profile.print_flat` / `Profile.print_tree` を再利用）。

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | Count/Bytes | 各関数のアロケーションバイト数合計 | `Alloc.size` の集計 | 右寄せ整数 | 動的 |
| 2 | Overhead | 各関数が直接行ったアロケーションバイト数 | リーフフレームのサイズ | 右寄せ整数 | 動的 |
| 3 | File | ファイルパス | `short_path()` | パッケージ色付き | 動的 |
| 4 | Line | 行番号 | `StackFrame.line` | 右寄せ | 動的 |
| 5 | Function | 関数名 | `StackFrame.func` | 左寄せ | 動的 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Total snapshots | 合計アロケーション数 | `length(data)` | 整数 |
| 2 | Total bytes | 合計アロケーションバイト数 | `root.count`（ツリー）/ `totalbytes`（フラット） | 整数 |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| sample_rate | サンプリングレート（0.0-1.0、デフォルト: 0.1） | No |
| format | 出力形式（`:tree` / `:flat`、デフォルト: `:tree`） | No |
| C | C フレームを含めるか（デフォルト: false） | No |
| maxdepth | 最大深度（ツリー形式） | No |
| mincount | 最小カウントフィルタ | No |
| sortedby | ソート順 | No |
| recur | 再帰処理モード | No |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | sortedby=:filefuncline（デフォルト） | ファイル名→関数名→行番号 |
| 2 | sortedby=:count | バイト数順 |
| 3 | sortedby=:overhead | オーバーヘッドバイト数順 |

### 改ページ条件

テキスト出力のため改ページは発生しない。

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| C内部バッファ（jl_fetch_alloc_profile） | アロケーションデータの取得 | - |
| BacktraceCache | バックトレースのメモ化キャッシュ | BTElement によるルックアップ |

### テーブル別参照項目詳細

#### RawAlloc（C構造体）

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| type | アロケーション型情報 | `load_type()` で変換 | Ptr{Type} からの安全な読み込み |
| backtrace | File, Line, Function | `stacktrace_memoized()` で変換 | RawBacktrace → StackTrace |
| size | Count/Bytes | そのまま使用 | アロケーションサイズ（バイト） |
| task | タスク情報 | 参照のみ | unrooted ポインタ、無効な場合あり |
| timestamp | タイムスタンプ | 参照のみ | UInt64 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| Count (n) | 各フレームのアロケーションバイト数合計 | なし（整数） | `r.size` を加算 |
| Overhead (m) | リーフフレームのアロケーションバイト数 | なし（整数） | 最初のフレームのみ加算 |
| Total bytes | 全アロケーションの合計バイト数 | なし（整数） | `root.count`（ツリー） |
| Total snapshots | アロケーション件数 | なし（整数） | `length(data)` |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A["Profile.Allocs.@profile 実行"] --> B["start(sample_rate) でサンプリング開始"]
    B --> C["式の実行（アロケーション記録）"]
    C --> D["stop() でサンプリング停止"]
    D --> E["Profile.Allocs.print() 呼び出し"]
    E --> F["fetch() で RawResults 取得"]
    F --> G["decode() で AllocResults 変換"]
    G --> H{format 判定}
    H -->|:tree| I["tree() でツリー出力"]
    H -->|:flat| J["flat() でフラット出力"]
    I --> K["サマリー行出力"]
    J --> K
    K --> L["終了"]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| サンプルなし | 収集されたアロケーションが0件 | "There were no samples collected." | sample_rate を上げるかプログラムをより長く実行 |
| combine=false | combine=false が指定された | ArgumentError | combine=true を使用（デフォルト） |
| 不正な format | `:tree` / `:flat` 以外 | "output format ... not recognized" | 正しい format を指定 |
| 不正な recur | サポート外の recur 値 | "recur value not recognized" | `:off`, `:flat`, `:flatc` を指定 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | sample_rate に依存（1.0 で全アロケーション記録） |
| 目標出力時間 | データ量に依存 |
| 同時出力数上限 | 1 |

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

アロケーションプロファイルにはソースコードのファイルパス、関数名、行番号、アロケーション型情報が含まれる。型情報からプログラムの内部構造が推測される可能性があるため、外部共有時は注意が必要。

## 備考

- `Profile.Allocs` は Julia 1.8 で追加された機能である。
- Julia 1.11 以降、全てのアロケーションに型情報が報告される（以前のバージョンでは `UnknownType` が発生する場合があった）。
- `sample_rate=1.0` で全アロケーションを記録できるが、オーバーヘッドが大きくなる。デフォルトは `0.1`。
- CPU プロファイルの `Profile.print` と `Profile.Allocs.print` は別の関数である。
- 特殊な型として `CorruptType`、`BufferType`、`UnknownType` がある。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `RawAlloc`（行25-31）、`RawBacktrace`（行19-22）、`RawResults`（行34-37）の C 構造体マッピングを理解する |
| 1-2 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `Alloc`（行132-138）、`AllocResults`（行140-142）のデコード済みデータ構造を理解する |
| 1-3 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `StackFrameTree{T}`（行974-990）のツリー構造を理解する（共有データ構造） |

**読解のコツ**: `RawAlloc` は C の `jl_raw_alloc_t` に対応し、`decode()` 関数で Julia オブジェクトの `Alloc` に変換される。型情報は `load_type()` で安全に読み込まれる。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `@profile` マクロ（行72-77）でサンプリングの開始・停止を理解する |
| 2-2 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `print()` 関数群（行243-268）で出力のエントリーポイントを理解する |

**主要処理フロー**:
1. **行72-77**: `@profile` マクロが `start()` と `stop()` を呼び出し
2. **行96-98**: `start()` が ccall で C ランタイムのサンプリングを開始
3. **行243-248**: `print()` が `fetch()` → `Profile.print()` を呼び出し
4. **行252-268**: `Profile.print(io, data::AllocResults, ...)` で format 分岐

#### Step 3: データ取得・デコード処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `fetch()`（行125-128）と `decode()`（行185-192）でデータ変換を理解する |
| 3-2 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `load_type()`（行164-173）で型情報のロードと特殊型の処理を理解する |
| 3-3 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `stacktrace_memoized()`（行203-223）でバックトレースのメモ化を理解する |

#### Step 4: 出力処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `flat()`（行321-333）でフラット形式の出力を理解する |
| 4-2 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `tree()`（行405-420）でツリー形式の出力を理解する |
| 4-3 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | `parse_flat()`（行286-319）と `tree!()`（行335-403）でデータ集計を理解する |

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

```
Profile.Allocs.@profile expr
    |
    +-- start(sample_rate)                   # ccall: jl_start_alloc_profile
    +-- expr の実行
    +-- stop()                               # ccall: jl_stop_alloc_profile

Profile.Allocs.print()
    |
    +-- fetch()                              # ccall: jl_fetch_alloc_profile
    |       +-- decode(raw_results)
    |               +-- decode_alloc(cache, raw_alloc)
    |                       +-- load_type(ptr)
    |                       +-- stacktrace_memoized(cache, trace)
    |
    +-- Profile.print(io, data::AllocResults)
            |
            +-- (format=:tree) tree(io, data, cols, fmt)
            |       +-- tree!(root, all, C, recur)
            |       +-- Profile.print_tree(io, root, cols, fmt, false)
            |
            +-- (format=:flat) flat(io, data, cols, fmt)
                    +-- parse_flat(T, data, C)
                    +-- Profile.print_flat(io, lilist, n, m, cols, ...)
```

### データフロー図

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

C内部バッファ ──fetch()──> RawResults
                               |
                         decode()
                               |
                        AllocResults (Vector{Alloc})
                               |
                   +-- tree!() / parse_flat()
                   |           |
                   |    StackFrameTree / (lilist, n, m)
                   |           |
                   +-- print_tree() / print_flat()
                               |
                        stdout (テキスト出力)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Allocs.jl | `stdlib/Profile/src/Allocs.jl` | ソース | アロケーションプロファイルの全ロジック |
| Profile.jl | `stdlib/Profile/src/Profile.jl` | ソース | 共有データ構造（StackFrameTree）と出力関数（print_flat, print_tree） |
| stacktraces.jl | `base/stacktraces.jl` | ソース | StackFrame/StackTrace 型定義、lookup 関数 |
