# 帳票設計書 4-ProfileGCレポート

## 概要

本ドキュメントは、serve-dのProfileGCレポート機能の設計を記述する。profilegc.logファイルを解析し、GCメモリ割り当て情報をCodeLensおよびAPIレスポンスとして出力する帳票である。

### 本帳票の処理概要

ProfileGCレポートは、D言語ランタイムのGCプロファイリング機能が生成するprofilegc.logファイルを解析し、各ソースコード行におけるGCメモリ割り当て情報をエディタ上にCodeLensとして表示する帳票である。

**業務上の目的・背景**：D言語のガーベッジコレクタ（GC）によるメモリ割り当てはパフォーマンスに影響を与える重要な要素である。開発者がGC割り当てが発生している箇所を視覚的に把握し、パフォーマンスチューニングを行えるようにすることで、メモリ効率の高いコードの開発を支援する。

**帳票の利用シーン**：D言語プロジェクトをGCプロファイリングモードで実行した後、生成されたprofilegc.logファイルをエディタが検出し、各ソースコード行にメモリ割り当て情報がCodeLensとして表示される。また、served/getProfileGCEntries APIにより全エントリをプログラム的に取得可能。

**主要な出力内容**：
1. 割り当てバイト数（bytesAllocated）
2. 割り当て回数（allocationCount）
3. 割り当て型名（type）
4. ファイルURI（uri）と行番号（line）
5. 表示用ファイルパス（displayFile）

**帳票の出力タイミング**：プロジェクト起動時（profilegc.logが存在する場合）、profilegc.logファイルの作成・変更・削除時、およびtextDocument/codeLensリクエスト時に出力される。

**帳票の利用者**：D言語開発者（パフォーマンスチューニングを行うプログラマ）

## 帳票種別

プロファイルレポート（CodeLens表示 + APIレスポンス）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | エディタCodeLens | LSP `textDocument/codeLens` | ファイル表示時に自動出力 |
| - | APIレスポンス | `served/getProfileGCEntries` | クライアントからのリクエスト |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JSON-RPC（LSPプロトコル） |
| 用紙サイズ | N/A（デジタル出力） |
| 向き | N/A |
| ファイル名 | N/A（ストリーム出力） |
| 出力方法 | textDocument/codeLensレスポンス / served/getProfileGCEntriesレスポンス |
| 文字コード | UTF-8 |

### PDF固有設定

N/A

### Excel固有設定

N/A

## 帳票レイアウト

### レイアウト概要

2つの出力形式を持つ。CodeLensはエディタ上の行にインラインで表示され、APIレスポンスは全エントリの配列を返す。

```
┌─────────────────────────────────────────────┐
│  CodeLens出力（textDocument/codeLens）        │
├─────────────────────────────────────────────┤
│  CodeLens[]                                  │
│    ├─ range: TextRange (entry.line - 1)      │
│    └─ command: "X bytes allocated / Y alloc" │
│                                              │
│  APIレスポンス（served/getProfileGCEntries）   │
├─────────────────────────────────────────────┤
│  ProfileGCEntry[]                            │
│    ├─ bytesAllocated: size_t                 │
│    ├─ allocationCount: size_t                │
│    ├─ type: string                           │
│    ├─ uri: DocumentUri                       │
│    ├─ displayFile: string                    │
│    └─ line: uint                             │
└─────────────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | textDocument.uri | CodeLens対象ドキュメントのURI | CodeLensParamsのリクエストパラメータ | URI文字列 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | bytesAllocated | GC割り当てバイト数 | profilegc.logの第1カラム | 数値 | - |
| 2 | allocationCount | GC割り当て回数 | profilegc.logの第2カラム | 数値 | - |
| 3 | type | 割り当て型名 | profilegc.logの第3カラム以降（ファイル位置前まで） | 文字列 | - |
| 4 | uri | ソースファイルURI | profilegc.logの最終カラム（コロン前） + uriBuildNormalized | URI文字列 | - |
| 5 | displayFile | 表示用ファイルパス | profilegc.logの最終カラム（コロン前） | 文字列 | - |
| 6 | line | ソース行番号 | profilegc.logの最終カラム（コロン後） | 正整数 | - |

### フッター部

N/A

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| GCプロファイラデコレーション有効 | config.d.enableGCProfilerDecorations == true（CodeLens出力時） | Yes |
| profilegc.logファイル存在 | ワークスペース内にprofilegc.logが存在すること | Yes |
| URI一致 | CodeLens出力時、entry.uriがリクエストのtextDocument.uriと一致すること | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | profilegc.logファイル内の出現順 | 昇順 |

### 改ページ条件

N/A

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

### 参照テーブル一覧

本帳票はデータベースを使用しない。profilegc.logファイルを直接解析する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| N/A | - | - |

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

N/A

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| CodeLens表示テキスト | `format!"%s bytes allocated / %s allocations"(entry.bytesAllocated, entry.allocationCount)` | - | 行114 |
| CodeLens range | `TextRange(entry.line - 1, 0, entry.line - 1, 1)` | - | 行113（0-indexed変換） |
| uri | `uriBuildNormalized(relativeToUri, file)` | - | 行54（相対パスからURI構築） |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A1[プロジェクト起動] --> B1[onProfileGCProjectAvailable]
    A2[ファイル変更通知] --> B2[onChangeProfileGC]
    B1 --> C[profileGCCache.update]
    B2 --> D{変更タイプ?}
    D -->|created/changed| C
    D -->|deleted| E[profileGCCache.clear]
    C --> F[profilegc.logファイル読込]
    F --> G[行ごとにparse]
    G --> H[ProfileGCEntry生成]
    H --> I[キャッシュに格納]
    I --> J[CodeLens/APIリクエスト待ち]
    J --> K1[provideProfileGCCodeLens]
    J --> K2[getProfileGCEntries]
    K1 --> L{GCプロファイラ有効?}
    L -->|No| Z[空配列返却]
    L -->|Yes| M[URI一致エントリをCodeLensに変換]
    M --> N[CodeLens配列返却]
    K2 --> O[全キャッシュエントリ返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ファイル例外 | profilegc.logファイルの読み込み失敗 | traceログ: "File Exception processing profilegc: {msg}" | キャッシュからエントリ削除（行78-81） |
| 一般例外 | profilegc.logの解析中エラー | traceログ: "Exception processing profilegc: {e}" | キャッシュからエントリ削除（行83-86） |
| ファイルウォッチャー登録失敗 | dynamicRegistrationの呼び出し失敗 | errorログ: "Failed to dynamically register GC watchers" | エラーログ出力のみ（行148） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | profilegc.log内で数十〜数千行のエントリ |
| 目標出力時間 | キャッシュ済みデータのCodeLens応答は数ミリ秒以内 |
| 同時出力数上限 | ProfileGCCacheによるインメモリキャッシュで同時アクセス対応 |

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

- profilegc.logはローカルファイルシステム上のプロファイルデータであり、個人情報を含まない
- ファイルウォッチャーは`**/profilegc.log`パターンのみ監視

## 備考

- ProfileGCCacheは__gsharedでグローバルに共有される（行97）
- ファイルウォッチャーはdynamicRegistrationをサポートするクライアントでのみ登録（行134-149）
- profilegc.logの各行は「バイト数 回数 型名 ファイル:行番号」の形式（行35-57）
- カラム数が5未満の行はスキップされる（行37）
- 最終カラムの「ファイル:行番号」形式を解析してファイルパスと行番号を分離（行45-57）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | profilegc.d | `source/served/workers/profilegc.d` | ProfileGCEntry構造体（行15-22）: bytesAllocated, allocationCount, type, uri, displayFile, line |
| 1-2 | profilegc.d | `source/served/workers/profilegc.d` | ProfileGCCache構造体（行24-95）: PerDocumentCacheを内包するキャッシュ機構 |

**読解のコツ**: `__gshared`はD言語でスレッド間で共有されるグローバル変数を示す。ProfileGCCacheはサーバー全体で1つのインスタンスが共有される。

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

2つの出力パスがある。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | profilegc.d | `source/served/workers/profilegc.d` | provideProfileGCCodeLens関数（行99-120）: textDocument/codeLensハンドラ |
| 2-2 | profilegc.d | `source/served/workers/profilegc.d` | getProfileGCEntries関数（行122-129）: served/getProfileGCEntriesハンドラ |

**主要処理フロー（CodeLens）**:
1. **行102**: enableGCProfilerDecorations設定チェック
2. **行106-118**: 全キャッシュを走査し、URI一致エントリをCodeLensに変換
3. **行112-115**: TextRangeとCommandテキストを設定

#### Step 3: キャッシュ更新処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | profilegc.d | `source/served/workers/profilegc.d` | ProfileGCCache.update関数（行67-88）: profilegc.logの読み込みとキャッシュ更新 |
| 3-2 | profilegc.d | `source/served/workers/profilegc.d` | PerDocumentCache.process関数（行30-62）: ログファイル行の解析処理 |

**主要処理フロー（解析）**:
- **行33-60**: 行分割して各行を解析
- **行36-37**: カラム数5未満はスキップ
- **行41-57**: 最終カラムから「ファイル:行番号」を抽出してProfileGCEntryを生成

#### Step 4: イベントハンドラを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | profilegc.d | `source/served/workers/profilegc.d` | setupProfileGCWatchers関数（行131-150）: ファイルウォッチャーの動的登録 |
| 4-2 | profilegc.d | `source/served/workers/profilegc.d` | onProfileGCProjectAvailable関数（行152-156）: プロジェクト利用可能時の初期読み込み |
| 4-3 | profilegc.d | `source/served/workers/profilegc.d` | onChangeProfileGC関数（行158-172）: ファイル変更通知時のキャッシュ更新/クリア |

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

```
[初期化]
setupProfileGCWatchers()                          [profilegc.d:131]
    +-- rpc.registerCapabilitySync()               [profilegc.d:139]

[プロジェクト起動時]
onProfileGCProjectAvailable()                     [profilegc.d:152]
    +-- profileGCCache.update(uri)                 [profilegc.d:155]
            +-- documents.getOrFromFilesystem()    [profilegc.d:73]
            +-- PerDocumentCache.process()          [profilegc.d:75]

[ファイル変更時]
onChangeProfileGC()                               [profilegc.d:158]
    +-- profileGCCache.update(uri)                 [profilegc.d:168]
    +-- profileGCCache.clear(uri)                  [profilegc.d:170]

[CodeLensリクエスト時]
provideProfileGCCodeLens()                        [profilegc.d:99]
    +-- config().d.enableGCProfilerDecorations     [profilegc.d:102]
    +-- profileGCCache.caches走査                   [profilegc.d:106-118]

[APIリクエスト時]
getProfileGCEntries()                             [profilegc.d:122]
    +-- profileGCCache.caches走査                   [profilegc.d:126-127]
```

### データフロー図

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

profilegc.log ──────────────> PerDocumentCache.process() ──> ProfileGCEntry[]
(GCプロファイルデータ)          (行解析・パース)

workspace/didChangeWatchedFiles ──> onChangeProfileGC() ──> キャッシュ更新/クリア

ProfileGCEntry[] ───────────> provideProfileGCCodeLens() ──> CodeLens[]
(URI一致フィルタ)              (表示テキスト生成)              (エディタ表示)

ProfileGCEntry[] ───────────> getProfileGCEntries() ───────> ProfileGCEntry[]
(全エントリ)                                                 (APIレスポンス)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| profilegc.d | `source/served/workers/profilegc.d` | ソース | ProfileGCレポートの全処理（キャッシュ管理、CodeLens提供、APIハンドラ） |
| types.d | `source/served/types.d` | ソース | 共通型定義、Configuration構造体（enableGCProfilerDecorations設定） |
| protoext.d | `source/served/lsp/protoext.d` | ソース | LSP拡張プロトコル型定義 |
