# 帳票設計書 1-CPUプロファイルレポート（フラットフォーマット）

## 概要

本ドキュメントは、Julia の `Profile.print(format=:flat)` によって出力される CPU プロファイルレポート（フラットフォーマット）の設計仕様を記述する。

### 本帳票の処理概要

本帳票は、Julia プログラムの CPU プロファイリング結果をフラット形式（非階層形式）で標準出力またはファイルに出力するレポートである。`@profile` マクロでバックトレースを収集し、`Profile.print(format=:flat)` を呼び出すことで、関数ごとのサンプル数を集計したフラットテーブル形式のレポートが出力される。

**業務上の目的・背景**：Julia プログラムのパフォーマンスボトルネックを特定するために、各関数が CPU サンプルに何回出現したかを一覧で確認できることが必要である。フラット形式はツリー形式と異なり、コールスタックの階層を省略して各関数のサンプル数を直接比較できるため、ホットスポットの迅速な特定に向いている。

**帳票の利用シーン**：開発者がアプリケーションのパフォーマンスチューニングを行う際に使用する。特に、どの関数が最も多くの CPU 時間を消費しているかを素早く把握したい場合に利用される。REPL での対話的なプロファイリングや、スクリプトによるバッチ的なプロファイル分析に使用される。

**主要な出力内容**：
1. Count（サンプル数）：各関数がバックトレースに出現した回数
2. Overhead（オーバーヘッド）：各関数がバックトレースの最上位（実行中の関数）として出現した回数
3. File（ファイル名）：関数が定義されているソースファイルのパス（パッケージ名付き）
4. Line（行番号）：関数が定義されている行番号
5. Function（関数名）：関数の名前（型シグネチャ付き）
6. Total snapshots（合計スナップショット数）とUtilization（CPU利用率）

**帳票の出力タイミング**：`@profile` マクロで式を実行した後、`Profile.print(format=:flat)` を明示的に呼び出したとき、または SIGUSR1/SIGINFO シグナルを受信したとき。

**帳票の利用者**：Julia プログラムの開発者、パフォーマンスエンジニア。

## 帳票種別

集計表（CPU プロファイリングサンプルの集計レポート）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | Julia REPL | julia> | `Profile.print(format=:flat)` コマンド実行 |
| - | ターミナル | - | SIGUSR1/SIGINFO シグナル送信 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | テキスト（標準出力） / テキストファイル |
| 用紙サイズ | N/A（テキスト出力） |
| 向き | N/A |
| ファイル名 | 任意（`Profile.print(path)` で指定）|
| 出力方法 | 標準出力への表示 / ファイルへの書き出し |
| 文字コード | UTF-8 |

### テキスト出力固有設定

| 項目 | 内容 |
|-----|------|
| カラム幅 | ターミナル幅に自動調整（`displaysize(io)[2]`） |
| 色付き出力 | 対応（パッケージ名ごとに色分け） |
| クリッカブルリンク | 対応（エディタ設定に応じた URI リンク生成） |

## 帳票レイアウト

### レイアウト概要

フラットフォーマットは、ヘッダー行、区切り行、データ行、サマリー行で構成されるテーブル形式のレポートである。

```
┌─────────────────────────────────────────────────────────────┐
│  Count  Overhead  File                  Line  Function      │  ← ヘッダー行
│  =====  ========  ====                  ====  ========      │  ← 区切り行
├─────────────────────────────────────────────────────────────┤
│    150        30  @Base/array.jl         123  getindex(...)  │  ← データ行
│    120        15  @MyPkg/src/main.jl      45  compute(...)   │     （ソート順で出力）
│    ...       ...  ...                    ...  ...            │
├─────────────────────────────────────────────────────────────┤
│  Total snapshots: 500. Utilization: 85%                      │  ← サマリー行
└─────────────────────────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Count | バックトレースにおける出現回数（全深度） | `parse_flat` の `n` 配列 | 右寄せ整数（最小幅6桁） |
| 2 | Overhead | バックトレース最上位への出現回数 | `parse_flat` の `m` 配列 | 右寄せ整数（最小幅9桁） |
| 3 | File | ソースファイルパス（パッケージ名プレフィックス付き） | `short_path()` による短縮パス | 左寄せ文字列（クリッカブルリンク付き） |
| 4 | Line | ソースコードの行番号 | `StackFrame.line` | 右寄せ整数 |
| 5 | Function | 関数名（型シグネチャ付き） | `StackFrame.func` / `show_spec_linfo` | 左寄せ文字列 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | Count | 各関数のサンプル出現回数 | `n[i]` | 右寄せ整数 | 動的（最大値の桁数） |
| 2 | Overhead | 各関数のオーバーヘッド回数 | `m[i]` | 右寄せ整数 | 動的（最大値の桁数） |
| 3 | File | ファイルパス | `short_path(li.file, filenamemap)` | 左寄せ、パッケージ色付き | 2*ntext/5 |
| 4 | Line | 行番号 | `li.line` | 右寄せ | 動的 |
| 5 | Function | 関数名 | `funcnames[i]` | 左寄せ | 3*ntext/5 |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Total snapshots | 合計サンプル数 | `totalshots` | 整数 |
| 2 | Utilization | CPU利用率（非スリープ比率） | `(1 - nsleeping/totalshots) * 100` | パーセント（整数） |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| format | `:flat` を指定 | Yes |
| C | C/Fortran フレームを含めるか（デフォルト: false） | No |
| combine | 同一行のIPをマージするか（デフォルト: true） | No |
| mincount | 最小サンプル数フィルタ（デフォルト: 0） | No |
| threads | 対象スレッドID | No |
| tasks | 対象タスクID | No |

### ソート順

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

### 改ページ条件

テキスト出力のため改ページは発生しない。ターミナル幅に応じてファイル名・関数名が自動的に切り詰められる。

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

### 参照テーブル一覧

本帳票はデータベースを参照しない。プロファイルデータは C ランタイムの内部バッファから取得される。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| C内部バッファ（jl_profile_get_data） | バックトレースデータの取得 | - |
| LineInfoDict | 命令ポインタからスタックフレーム情報への変換 | IP アドレスによるルックアップ |

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

#### C 内部プロファイルバッファ

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| instruction pointer (IP) | File, Line, Function | `fetch()` で取得 | `lookup()` でスタックフレームに変換 |
| threadid（メタデータ） | スレッドフィルタ | メタデータオフセット5 | ブロック末尾から逆算 |
| taskid（メタデータ） | タスクフィルタ | メタデータオフセット4 | ブロック末尾から逆算 |
| sleepstate（メタデータ） | Utilization 計算 | メタデータオフセット2 | 1=非スリープ, 2=スリープ |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| Count (n) | バックトレース内での出現回数（再帰除外） | なし（整数） | `parse_flat` 内で集計 |
| Overhead (m) | バックトレース最上位での出現回数 | なし（整数） | リーフフレームのみカウント |
| Utilization | `(1 - nsleeping / totalshots) * 100` | `round(Int, ...)` | パーセント表示 |
| 列幅 (wcounts) | `max(6, ndigits(maximum(n)))` | なし | Count列の最小幅6 |
| 列幅 (wself) | `max(9, ndigits(maximum(m)))` | なし | Overhead列の最小幅9 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A["Profile.print(format=:flat) 呼び出し"] --> B["fetch() でバックトレースデータ取得"]
    B --> C["getdict() で LineInfoDict 構築"]
    C --> D["print_group() で format 判定"]
    D --> E["flat() 関数呼び出し"]
    E --> F["parse_flat() でサンプル集計"]
    F --> G["print_flat() でテーブル出力"]
    G --> H["サマリー行出力"]
    H --> I["終了"]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| バッファ未初期化 | プロファイルが一度も実行されていない | "The profiling data buffer is not initialized." | `@profile` マクロでプロファイルを実行する |
| サンプルなし | 収集されたサンプルが0件 | "There were no samples collected." | プログラムをより長く実行するか、`Profile.init()` でサンプル間隔を調整 |
| バッファフル | バッファが満杯 | "The profile data buffer is full" | `Profile.init()` でバッファサイズを拡大 |
| メタデータ欠落 | データにメタデータが含まれていない | "Profile data is missing required metadata" | 正常なプロファイルデータを使用する |
| 不正な format 指定 | `:tree` / `:flat` 以外の format | "output format ... not recognized" | `format=:flat` または `format=:tree` を指定 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 最大 10,000,000 サンプル（デフォルトバッファサイズ） |
| 目標出力時間 | データ量に依存（IP ルックアップが律速） |
| 同時出力数上限 | 1（シングルスレッドで出力） |

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

プロファイルレポートにはソースコードのファイルパス、関数名、行番号が含まれるため、外部に共有する際は機密情報の有無を確認すること。特に `C=true` オプションを使用した場合、C ランタイムの内部関数情報も出力される。

## 備考

- `Profile.print(path::String)` でファイルに直接出力する場合、カラム幅はデフォルト 1000 に設定される。
- パッケージ名は `@Base`、`@Core` などのプレフィックスで表示され、それぞれ固有の色が割り当てられる。
- エディタ連携（VS Code, Sublime Text, IntelliJ IDEA 等）に対応したクリッカブルリンクが生成される。

---

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

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

### 推奨読解順序

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

まず、プロファイルデータの内部構造とメタデータの配置を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `nmeta` 定数（行44）と `META_OFFSET_*` 定数（行215-218）でメタデータの構造を理解する |
| 1-2 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `ProfileFormat` 構造体（行194-212）でフォーマット設定のパラメータを理解する |
| 1-3 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `LineInfoDict` / `LineInfoFlatDict` 型エイリアス（行191-192）で辞書のデータ型を理解する |

**読解のコツ**: プロファイルデータは UInt64 の配列として格納され、バックトレースブロックの末尾に NULL マーカー2つとメタデータ4フィールドが付加される構造になっている。

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

処理の起点となる `Profile.print` 関数を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `print(io::IO, ...)` 関数（行271-361）がメインのエントリーポイント |

**主要処理フロー**:
1. **行271-285**: 引数の受け取りとデフォルト値の設定（`format=:tree` がデフォルト）
2. **行287**: `ProfileFormat` 構造体の生成
3. **行288-289**: `groupby=:none` の場合は `print_group` を直接呼び出し
4. **行291-359**: `groupby` 指定がある場合はスレッド/タスクごとにグループ化して出力

#### Step 3: フラット形式の集計処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `flat()` 関数（行819-853）でフラット形式の出力制御を理解する |
| 3-2 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `parse_flat()` 関数（行750-815）でサンプル集計ロジックを理解する |

**主要処理フロー**:
- **行821**: `parse_flat` を呼び出してサンプルを集計
- **行828**: Utilization（CPU利用率）を計算
- **行840**: `print_flat` を呼び出してテーブルを出力

#### Step 4: テーブル出力処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `print_flat()` 関数（行889-969）でテーブルのフォーマット処理を理解する |
| 4-2 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `short_path()` 関数（行546-595）でファイルパスの短縮処理を理解する |
| 4-3 | Profile.jl | `stdlib/Profile/src/Profile.jl` | `editor_link()` 関数（行857-887）でエディタ連携リンクの生成を理解する |

**主要処理フロー**:
- **行893-898**: ソート順の決定（`:count`, `:overhead`, `:filefuncline`）
- **行905-906**: 列幅の計算
- **行926-929**: ヘッダー行の出力
- **行930-967**: 各データ行の出力（色付き、リンク付き）

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

```
Profile.print(io, data, lidict; format=:flat)
    |
    +-- ProfileFormat(...)                    # フォーマット設定構築
    |
    +-- print_group(io, data, lidict, ...)    # グループ出力制御
            |
            +-- flat(io, data, lidict, ...)   # フラット形式出力
                    |
                    +-- parse_flat(...)        # サンプル集計
                    |       |
                    |       +-- is_block_end() # ブロック末尾判定
                    |       +-- has_meta()     # メタデータ有無確認
                    |
                    +-- print_flat(...)        # テーブル出力
                            |
                            +-- short_path()   # ファイルパス短縮
                            +-- editor_link()  # エディタリンク生成
                            +-- liperm()       # ソート順計算
```

### データフロー図

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

C内部バッファ ──fetch()──> Vector{UInt64} ──parse_flat()──> (lilist, n, m)
                               |                                    |
                        getdict() / lookup()                  print_flat()
                               |                                    |
                        LineInfoDict ───────────────> short_path() + editor_link()
                                                                    |
                                                              stdout / ファイル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Profile.jl | `stdlib/Profile/src/Profile.jl` | ソース | フラットプロファイルレポートの全ロジック（print, flat, parse_flat, print_flat） |
| timing.jl | `base/timing.jl` | ソース | `format_bytes` 等のユーティリティ関数 |
| stacktraces.jl | `base/stacktraces.jl` | ソース | `StackFrame` 型定義、`lookup` 関数 |
