# 機能設計書 70-ProfileGCエントリ取得

## 概要

本ドキュメントは、serve-dにおけるProfileGCエントリ取得機能（served/getProfileGCEntries）の設計を記述する。profilegc.logファイルを解析し、GCアロケーション情報をエントリとして返却する機能である。

### 本機能の処理概要

**業務上の目的・背景**：D言語のランタイムにはGC（ガベージコレクタ）のプロファイリング機能があり、profilegc.logファイルにアロケーション情報が記録される。本機能はこのログファイルを解析し、各ソースファイルの各行でのバイトアロケーション数やアロケーション回数を構造化データとして返却する。これにより、開発者はGCアロケーションのホットスポットを特定しパフォーマンスの最適化に活用できる。

**機能の利用シーン**：
- エディタからProfileGCエントリを取得してパフォーマンス分析に使用する場合
- コードレンズでGCアロケーション情報を表示する際のデータソースとして
- profilegc.logの解析結果を一括取得したい場合

**主要な処理内容**：
1. ProfileGCCacheの全キャッシュからエントリを収集する
2. 全エントリをフラットな配列として返却する

本機能はProfileGCCacheのデータ取得APIであり、キャッシュの更新は以下のイベントで行われる：
- プロジェクト利用可能時（onProfileGCProjectAvailable）
- ファイル監視による変更検出時（onChangeProfileGC）

**関連システム・外部連携**：
- profilegc.logファイル（D言語ランタイムのGCプロファイル出力）
- ファイルシステム監視（workspace/didChangeWatchedFiles）
- コードレンズ（機能No.71 ProfileGCコードレンズ）

**権限による制御**：特別な権限制御はない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 65 | ProfileGCエントリ取得（served/getProfileGCEntries） | 主画面 | profilegc.logファイルを解析しGCアロケーション情報をエントリとして返却する |
| 20 | コードレンズ（textDocument/codeLens） | 補助機能 | ProfileGCデータをパースしてコードレンズに表示するデータを取得する |

## 機能種別

データ取得・解析結果返却

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| (なし) | - | - | パラメータなしのリクエスト | - |

### 入力データソース

- LSPクライアントからの `served/getProfileGCEntries` メソッド呼び出し
- ProfileGCCacheの内部キャッシュ（profilegc.logの解析結果）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | ProfileGCEntry[] | GCアロケーション情報のエントリ配列 |
| result[].bytesAllocated | size_t | アロケーションバイト数 |
| result[].allocationCount | size_t | アロケーション回数 |
| result[].type | string | アロケーションの型情報 |
| result[].uri | DocumentUri | ファイルのURI |
| result[].displayFile | string | 表示用ファイル名 |
| result[].line | uint | 行番号 |

### 出力先

LSPクライアントへのレスポンス（JSON-RPC応答）。

## 処理フロー

### 処理シーケンス

```
1. served/getProfileGCEntriesリクエストを受信
   └─ getProfileGCEntries()関数が呼び出される
2. ProfileGCCacheの全キャッシュを走査
   └─ profileGCCache.cachesの全エントリを反復
3. 各キャッシュのエントリを結果リストに追加
   └─ cache.entriesをappenderに追加
4. 全エントリを配列として返却
```

### フローチャート

```mermaid
flowchart TD
    A[served/getProfileGCEntries リクエスト受信] --> B[空のappenderを作成]
    B --> C{profileGCCache.caches を走査}
    C -->|次のキャッシュ| D[cache.entries を appender に追加]
    D --> C
    C -->|走査完了| E[lenses.data を返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-70-01 | キャッシュ依存 | getProfileGCEntriesはキャッシュ済みの解析結果のみを返却する（新たな解析は行わない） | 常時 |
| BR-70-02 | 全キャッシュ結合 | 全profilegc.logキャッシュからエントリをフラットに結合 | 常時 |

### 計算ロジック

ProfileGCCache.PerDocumentCache.process()によるログ解析ロジック：
```
各行のフォーマット: <bytesAllocated> <allocationCount> <type> <file>:<line>
- 5カラム未満の行はスキップ
- 最後のカラムから "file:line" を抽出
- typeは3列目から最後のカラムの前まで
```

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

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

データベース操作はない。ProfileGCCacheの内部キャッシュからの読み取りのみ。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | FileException | profilegc.logの読み取り失敗 | キャッシュをクリア（update内で処理） |
| - | Exception | ログ解析エラー | キャッシュをクリア（update内で処理） |

### リトライ仕様

ファイル監視によりprofilegc.logの変更が検出されると自動的にキャッシュが更新される。

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

トランザクション管理は不要。読み取り専用操作。

## パフォーマンス要件

- キャッシュベースの設計により高速にレスポンスを返却
- キャッシュの更新はプロジェクト利用可能時とファイル変更検出時のみ実行
- appenderを使用した効率的な配列構築

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

ファイルシステムの読み取りとキャッシュの参照のみ。

## 備考

- `@protocolMethod("served/getProfileGCEntries")` アノテーションにより登録
- profileGCCacheはモジュールレベルのグローバル変数（package __gshared）
- ProfileGCEntry構造体はprofilegc.dの15-22行目に定義
- キャッシュ更新のトリガー：
  - `@onProjectAvailable` でonProfileGCProjectAvailable（152-156行目）
  - `@protocolNotification("workspace/didChangeWatchedFiles")` でonChangeProfileGC（158-172行目）
- ファイル監視は `setupProfileGCWatchers` で `**/profilegc.log` パターンで登録される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | profilegc.d | `source/served/workers/profilegc.d` | ProfileGCEntry構造体（15-22行目）- エントリのデータ構造 |
| 1-2 | profilegc.d | `source/served/workers/profilegc.d` | ProfileGCCache構造体（24-95行目）- キャッシュ管理 |
| 1-3 | profilegc.d | `source/served/workers/profilegc.d` | PerDocumentCache構造体（26-63行目）- ドキュメント単位のキャッシュ |

**読解のコツ**: ProfileGCCacheはProfileGCEntry[]をDocumentUri単位で管理する入れ子構造。PerDocumentCacheのprocess()メソッドがprofilegc.logの行解析ロジック。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | profilegc.d | `source/served/workers/profilegc.d` | getProfileGCEntries関数（122-129行目） |

**主要処理フロー**:
1. **122行目**: `@protocolMethod("served/getProfileGCEntries")` でLSPメソッド登録
2. **125行目**: `appender!(ProfileGCEntry[])` で結果配列を初期化
3. **126-127行目**: `profileGCCache.caches` を走査し全エントリを追加
4. **128行目**: `lenses.data` を返却

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | profilegc.d | `source/served/workers/profilegc.d` | ProfileGCCache.update関数（67-88行目） |
| 3-2 | profilegc.d | `source/served/workers/profilegc.d` | PerDocumentCache.process関数（30-62行目） |

**process()の主要処理フロー**:
1. **33行目**: 既存エントリをクリア
2. **33-34行目**: profilegc.logの各行を走査
3. **35-37行目**: カラム数チェック（5カラム未満はスキップ）
4. **38-40行目**: type列の開始位置を計算
5. **41-49行目**: ファイル名と行番号を抽出
6. **50-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行目） - ファイル変更時 |

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

```
getProfileGCEntries() [profilegc.d:122-129]
    |
    +-- profileGCCache.caches (キャッシュ参照)
        +-- cache.entries (各ドキュメントのエントリ)

[キャッシュ更新系]
onProfileGCProjectAvailable() [profilegc.d:152-156]
    +-- profileGCCache.update(uri)
        +-- documents.getOrFromFilesystem(uri)
        +-- PerDocumentCache.process(uri, content) [profilegc.d:30-62]

onChangeProfileGC(params) [profilegc.d:158-172]
    +-- profileGCCache.update(change.uri) (created/changed)
    +-- profileGCCache.clear(change.uri) (deleted)

setupProfileGCWatchers() [profilegc.d:131-150]
    +-- rpc.registerCapabilitySync("**/profilegc.log")
```

### データフロー図

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

profilegc.log (ファイル)         ──▶  PerDocumentCache.process()         ──▶  ProfileGCCache
                                       (行解析・構造化)                          (キャッシュ)

workspace/didChangeWatchedFiles  ──▶  onChangeProfileGC()               ──▶  キャッシュ更新
@onProjectAvailable              ──▶  onProfileGCProjectAvailable()     ──▶  キャッシュ更新

served/getProfileGCEntries       ──▶  getProfileGCEntries()             ──▶  ProfileGCEntry[]
                                       (キャッシュ全エントリの結合)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| profilegc.d | `source/served/workers/profilegc.d` | ソース | ProfileGCEntry型、ProfileGCCache、getProfileGCEntries関数の定義（全173行） |
| code_lens.d | `source/served/commands/code_lens.d` | ソース | ProfileGCコードレンズとの連携 |
| extension.d | `source/served/extension.d` | ソース | バックエンドインスタンス管理 |
| types.d | `source/served/types.d` | ソース | DocumentUri型等の共通定義 |
