# 機能設計書 113-Profile

## 概要

本ドキュメントは、Julia標準ライブラリ `Profile` モジュールの機能設計を記述する。CPUプロファイリング（@profile）、アロケーションプロファイリング（Allocs.@profile）、およびヒープスナップショット取得機能を提供するモジュールである。

### 本機能の処理概要

**業務上の目的・背景**：パフォーマンス最適化は高性能計算やデータ処理において重要な課題である。Profileモジュールは、Juliaプログラムの実行時パフォーマンスを分析するための統合的なプロファイリング基盤を提供する。CPUサンプリングプロファイラにより「どの関数にどれだけの時間が費やされているか」を、アロケーションプロファイラにより「どこでメモリ割り当てが発生しているか」を特定できる。これにより、ボトルネックの発見と最適化の指針を効率的に得ることができる。

**機能の利用シーン**：パフォーマンスボトルネックの特定（@profile）、メモリアロケーションのホットスポット検出（Allocs.@profile）、メモリリーク調査のためのヒープスナップショット取得、CI/CDパイプラインでの自動パフォーマンス回帰検出。

**主要な処理内容**：
1. CPUサンプリングプロファイリング（@profile マクロ）：一定間隔でバックトレースを取得し、実行時間の統計情報を収集
2. プロファイルデータの出力（Profile.print）：フラット表示またはツリー表示でプロファイル結果を表示
3. アロケーションプロファイリング（Profile.Allocs.@profile）：メモリ割り当てイベントのサンプリング記録
4. ヒープスナップショット（Profile.take_heap_snapshot）：GCヒープの完全なスナップショットを取得
5. プロファイルデータのクリア（Profile.clear）およびバッファ管理（Profile.init）

**関連システム・外部連携**：Julia Cランタイム（src/signal-handling.c）によるシグナルベースのサンプリング、libjulia内部のGCプロファイリング機構。

**権限による制御**：特になし。全てのユーザーが利用可能。ただし、CPUプロファイリングはOSのシグナル機構を使用するため、一部のサンドボックス環境では制限される可能性がある。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | REPL | 主画面 | @profileの実行、Profile.print()による結果表示 |

## 機能種別

計測・分析 / ユーティリティ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| expr | Expr | Yes | プロファイル対象のJulia式 | 有効なJulia式であること |
| n | Integer | No | Profile.init: バックトレースバッファサイズ | 正の整数 |
| delay | Float64 | No | Profile.init: サンプリング間隔（秒） | 正の浮動小数点数 |
| sample_rate | Float64 | No | Allocs.@profile: アロケーションサンプリング率 | 0.0〜1.0 |
| format | Symbol | No | Profile.print: 出力形式（:flat/:tree） | :flat または :tree |
| C | Bool | No | Profile.print: Cフレームを含むか | true/false |
| maxdepth | Int | No | Profile.print: ツリー表示の最大深度 | 正の整数 |
| mincount | Int | No | Profile.print: 表示する最小カウント | 非負整数 |

### 入力データソース

- 実行中のJuliaコードのバックトレース情報（Cランタイムが収集）
- GCによるアロケーション情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| プロファイルデータ | Vector{UInt64} | サンプリングされたバックトレースポインタの配列 |
| フラット表示 | テキスト | 関数ごとのサンプル数・割合の一覧 |
| ツリー表示 | テキスト | コールスタックのツリー構造でのサンプル数表示 |
| AllocResults | 構造体 | アロケーションプロファイルの結果（型、サイズ、バックトレース） |
| ヒープスナップショット | ファイル | .heapsnapshot形式のJSON（Chrome DevTools互換） |

### 出力先

- 標準出力（Profile.print）
- ファイル（take_heap_snapshot）
- 戻り値（Profile.fetch, Allocs.fetch）

## 処理フロー

### 処理シーケンス

```
1. Profile.init() でバッファを初期化
   └─ バッファサイズとサンプリング間隔を設定
2. @profile expr を実行
   └─ サンプリングタイマーを開始（Cランタイム）
   └─ 対象式を実行
   └─ サンプリングタイマーを停止
   └─ バッファにバックトレースデータを蓄積
3. Profile.print() で結果を表示
   └─ バックトレースポインタをスタックフレームに変換
   └─ フラット表示またはツリー表示でフォーマット
4. Profile.clear() でバッファをクリア
```

### フローチャート

```mermaid
flowchart TD
    A["@profile expr"] --> B["サンプリングタイマー開始"]
    B --> C["対象式を実行"]
    C --> D["定期的にバックトレース取得"]
    D --> E["バッファにスタック情報蓄積"]
    C --> F["式の実行完了"]
    F --> G["サンプリングタイマー停止"]
    G --> H["Profile.print() 呼び出し"]
    H --> I{"表示形式?"}
    I -->|flat| J["関数ごとの集計表示"]
    I -->|tree| K["コールツリー表示"]
    J --> L["終了"]
    K --> L
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-113-01 | サンプリング方式 | 統計的サンプリングにより実行オーバーヘッドを最小化する | @profile使用時 |
| BR-113-02 | バッファ管理 | バッファが満杯になるとサンプリングを自動停止し警告を出す | バッファオーバーフロー時 |
| BR-113-03 | アロケーションサンプリング | sample_rate=1.0で全割り当てを記録、0.0で記録なし | Allocs.@profile使用時 |
| BR-113-04 | ヒープスナップショット形式 | Chrome DevTools互換のJSON形式で出力 | take_heap_snapshot使用時 |

### 計算ロジック

- サンプリング間隔のデフォルトは約1ミリ秒（10^6ナノ秒）
- フラット表示のカウントは、各関数がバックトレースに出現した回数
- ツリー表示は、コールスタックの包含関係をツリー構造で表現

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

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バッファオーバーフロー | プロファイルバッファが満杯 | Profile.init(n=larger_value)でバッファを拡大 |
| - | サンプリング失敗 | OSシグナル機構が利用不可 | サポートされたOSで実行する |

### リトライ仕様

リトライは不要。バッファオーバーフロー時はProfile.init()でバッファを拡大して再実行する。

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

該当なし。

## パフォーマンス要件

- サンプリングプロファイラのオーバーヘッドは通常5%以下
- Allocs.@profileのオーバーヘッドはsample_rateに比例
- ヒープスナップショットは全オブジェクトを走査するため、ヒープサイズに比例した時間がかかる

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

- プロファイルデータにはソースコードのファイルパスと関数名が含まれる
- ヒープスナップショットにはメモリ上のオブジェクト情報が含まれるため、取り扱いに注意

## 備考

- `@profile`マクロは`Profile.@profile`として名前空間が分離されている
- `Profile.Allocs.@profile`はJulia 1.8で追加されたアロケーションプロファイラ
- `Profile.Allocs`サブモジュールはJulia 1.11以降、全てのアロケーションの型を正しく報告する

---

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

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

### 推奨読解順序

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

プロファイルデータの内部表現を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Profile.jl | `stdlib/Profile/src/Profile.jl` | ProfileFormat型（出力フォーマット設定）、StackFrameTree型（ツリー構造） |
| 1-2 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | RawBacktrace, RawAlloc, RawResults構造体（C側データ構造とのマッピング） |

**読解のコツ**: Profile.jlのCランタイム連携部分はccallによるC関数呼び出しで実装されている。RawBacktrace等の構造体はC側の`jl_raw_backtrace_t`等と1対1で対応する。

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

@profileマクロとProfile.print()がメインのエントリーポイント。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Profile.jl | `stdlib/Profile/src/Profile.jl` | @profileマクロの展開ロジック、start_timer/stop_timerのccall |

**主要処理フロー**:
1. **マクロ展開**: `@profile expr` は `start_timer()` -> `expr` -> `stop_timer()` に展開される
2. **start_timer**: `ccall(:jl_profile_start_timer, ...)` でCランタイムのサンプリングを開始
3. **stop_timer**: `ccall(:jl_profile_stop_timer, ...)` でサンプリングを停止

#### Step 3: アロケーションプロファイラを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | **19-37行目**: RawBacktrace, RawAlloc, RawResults構造体の定義 |
| 3-2 | Allocs.jl | `stdlib/Profile/src/Allocs.jl` | **72-77行目**: @profileマクロの定義。sample_rateパラメータの処理 |

**主要処理フロー**:
- **72-77行目**: `@profile`マクロ。`_prof_expr`ヘルパーで式をラップ
- Cランタイムの`jl_start_alloc_profile`/`jl_stop_alloc_profile`を呼び出し

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

```
@profile expr
    |
    +-- Profile.start_timer()
    |   +-- ccall(:jl_profile_start_timer)       # Cランタイム
    |
    +-- expr の実行
    |   (定期的にシグナルハンドラがバックトレースを取得)
    |
    +-- Profile.stop_timer()
    |   +-- ccall(:jl_profile_stop_timer)
    |
    +-- Profile.print()
        +-- Profile.fetch()                       # バッファからデータ取得
        |   +-- ccall(:jl_profile_get_data)
        +-- StackTraces.lookup()                  # IPアドレス→StackFrame変換
        +-- print_flat() / print_tree()           # 表示

Profile.Allocs.@profile expr
    |
    +-- ccall(:jl_start_alloc_profile)
    +-- expr の実行
    +-- ccall(:jl_stop_alloc_profile)
    +-- Allocs.fetch()
        +-- ccall(:jl_fetch_alloc_profile)
        +-- decode() -- RawAlloc → Alloc変換
```

### データフロー図

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

Julia式        ───▶  @profile                  ───▶  バックトレースバッファ
                     サンプリングタイマー               (Vector{UInt64})

バッファ        ───▶  Profile.fetch()           ───▶  StackFrame情報
                     IPアドレス解決

StackFrame     ───▶  Profile.print()           ───▶  テキスト出力
                     フラット/ツリー集計               (stdout/IO)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Profile.jl | `stdlib/Profile/src/Profile.jl` | ソース | メインモジュール。CPUプロファイラ、print、init等 |
| Allocs.jl | `stdlib/Profile/src/Allocs.jl` | ソース | アロケーションプロファイラサブモジュール |
| heapsnapshot.jl | `stdlib/Profile/src/heapsnapshot.jl` | ソース | ヒープスナップショット機能 |
| Project.toml | `stdlib/Profile/Project.toml` | 設定 | パッケージ依存関係定義 |
| runtests.jl | `stdlib/Profile/test/runtests.jl` | テスト | ユニットテスト |
