# 通知設計書 23-メモリビジュアライザ通知

## 概要

本ドキュメントは、Bun DevServerにおけるメモリ使用状況をビジュアライザに送信する通知機能の設計を定義する。この通知は、開発者がDevServerのメモリ消費を監視し、メモリリークやパフォーマンス問題をデバッグするために使用される。

### 本通知の処理概要

この通知は、DevServerの各コンポーネント（インクリメンタルグラフ、ソースマップ、アセット等）のメモリ使用量、およびシステム全体のメモリ状況をWebSocket経由でビジュアライザクライアントに定期的に送信するデバッグ機能である。

**業務上の目的・背景**：大規模プロジェクトの開発において、DevServerのメモリ消費は重要な関心事である。インクリメンタルバンドリングでは、ファイルグラフやソースマップなど多くのデータがメモリに保持されるため、メモリリークが発生しやすい。本通知により、開発者（特にBunの開発者）はメモリ使用量をリアルタイムで監視し、どのコンポーネントがメモリを消費しているかを特定できる。これにより、メモリ最適化やリーク検出が容易になる。

**通知の送信タイミング**：以下のタイミングで送信される：
1. WebSocketクライアントが`memory_visualizer`トピックにサブスクライブしたとき（初回）
2. 1秒間隔のタイマーイベント（memory_visualizer_timer）
3. ソースマップの参照解除時など、メモリ状態が変化したとき

**通知の受信者**：`memory_visualizer`トピックにサブスクライブしているWebSocketクライアント。通常は`/_bun/memory_visualizer`にアクセスしているブラウザウィンドウ。

**通知内容の概要**：以下のメモリ情報をバイナリ形式で送信：
- インクリメンタルグラフ（クライアント/サーバー）のメモリ使用量
- JSコードのメモリ使用量
- ソースマップのメモリ使用量
- アセットのメモリ使用量
- プロセス全体のメモリ使用量
- システムメモリ使用量
- ソースマップエントリの詳細情報

**期待されるアクション**：ビジュアライザクライアントはメモリデータをグラフやチャートとして表示し、開発者がメモリ消費の傾向やリークの兆候を視覚的に確認できるようにする。

## 通知種別

WebSocket通知（DevServer内部デバッグ機能）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（publish） |
| 優先度 | 低（デバッグ機能） |
| リトライ | 無し |

### 送信先決定ロジック

`HmrTopic.memory_visualizer`トピックにサブスクライブしているすべてのWebSocketクライアントに対してブロードキャストされる。サブスクライバ数は`dev.emit_memory_visualizer_events`で参照カウントされ、0より大きい場合にのみメッセージが生成・送信される。

## 通知テンプレート

### メッセージフォーマット

| 項目 | 内容 |
|-----|------|
| プロトコル | WebSocket |
| 形式 | バイナリ（ArrayBuffer） |
| エンコーディング | Little-endian |

### 本文テンプレート

```
MessageId.memory_visualizer (1バイト: 'M')
├─ 固定フィールド (各u32)
│   ├─ incremental_graph_client: クライアントグラフのメモリ使用量
│   ├─ incremental_graph_server: サーバーグラフのメモリ使用量
│   ├─ js_code: JSコードのメモリ使用量
│   ├─ source_maps: ソースマップのメモリ使用量
│   ├─ assets: アセットのメモリ使用量
│   ├─ other: その他のメモリ使用量
│   ├─ devserver_tracked: DevServerが追跡しているメモリ総量
│   ├─ process_used: プロセスのメモリ使用量
│   ├─ system_used: システム使用メモリ
│   └─ system_total: システム総メモリ
├─ ソースマップエントリ数 (u32)
└─ 各ソースマップエントリ:
    ├─ key (u64): ソースマップのキー
    ├─ ref_count (u32): 参照カウント
    ├─ weak_ref_count (u32): 弱参照カウント
    ├─ expire (f64): 有効期限タイムスタンプ
    ├─ files_len (u32): ファイル数
    └─ overlapping_memory_cost (u32): 重複メモリコスト
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| incremental_graph_client | クライアントグラフのメモリ | memoryCostDetailed() | Yes |
| incremental_graph_server | サーバーグラフのメモリ | memoryCostDetailed() | Yes |
| js_code | JSコードのメモリ | memoryCostDetailed() | Yes |
| source_maps | ソースマップのメモリ | memoryCostDetailed() | Yes |
| assets | アセットのメモリ | memoryCostDetailed() | Yes |
| other | その他のメモリ | memoryCostDetailed() | Yes |
| devserver_tracked | 追跡メモリ総量 | allocation_scope.stats() | Yes |
| process_used | プロセスメモリ | selfProcessMemoryUsage() | Yes |
| system_used | システム使用メモリ | totalmem() - freemem() | Yes |
| system_total | システム総メモリ | totalmem() | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| サブスクライブ | WebSocket subscribe メッセージ | bake_debugging_features有効 | クライアントがトピックにサブスクライブ時 |
| タイマー | memory_visualizer_timer | emit_memory_visualizer_events > 0 | 1秒間隔で定期送信 |
| 状態変更 | ソースマップunref等 | emit_memory_visualizer_events > 0 | メモリ状態変化時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| bake_debugging_features無効 | コンパイル時フラグが無効の場合、機能全体が無効 |
| emit_memory_visualizer_events == 0 | サブスクライバがいない場合は送信をスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[トリガー発生] --> B{トリガー種別}
    B -->|サブスクライブ| C[emitMemoryVisualizerMessage呼び出し]
    B -->|タイマー| D[emitMemoryVisualizerMessageTimer呼び出し]
    B -->|状態変更| E[emitMemoryVisualizerMessageIfNeeded呼び出し]
    D --> F{bake_debugging_features有効?}
    E --> G{emit_memory_visualizer_events > 0?}
    C --> H[emitMemoryVisualizerMessage実行]
    F -->|No| I[処理終了]
    F -->|Yes| H
    G -->|No| I
    G -->|Yes| H
    H --> J[ペイロードバッファ確保]
    J --> K[MessageId.memory_visualizer追加]
    K --> L[writeMemoryVisualizerMessage呼び出し]
    L --> M[固定フィールド書き込み]
    M --> N[ソースマップエントリ書き込み]
    N --> O[publish実行]
    O --> P{タイマー?}
    P -->|Yes| Q[タイマー再スケジュール]
    P -->|No| I
    Q --> I
```

## データベース参照・更新仕様

### 参照テーブル一覧

本通知はデータベースを使用しない。すべてのデータはメモリ上のDevServer構造から取得される。

| 構造体名 | 用途 | 備考 |
|---------|------|------|
| SourceMapStore | ソースマップ情報 | dev.source_maps |
| AllocationScope | メモリ追跡 | dev.allocation_scope |

### 更新テーブル一覧

なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| メモリ不足 | ペイロード生成中にOOM | 処理を中断し、メッセージは送信されない |
| タイマーエラー | タイマー状態が不正 | assertでデバッグビルドのみクラッシュ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 約60件（1秒間隔タイマー） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（開発環境デバッグ機能）

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

- この機能はデバッグ機能であり、`bake_debugging_features`フラグで制御される
- デフォルトではリリースビルドでは無効
- プロセスおよびシステムのメモリ情報が含まれる
- ローカル開発環境でのみ使用されることを想定

## 備考

- この機能は`/_bun/memory_visualizer`エンドポイントで提供されるHTMLページと組み合わせて使用される
- タイマーは最初のサブスクライバ接続時に開始され、全サブスクライバ切断時に停止される
- ソースマップのリーク検出に特に有用（ref_countとweak_ref_countの不整合を検出可能）

---

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

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

### 推奨読解順序

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

メモリコスト計算に使用されるデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DevServer.zig | `src/bake/DevServer.zig` | emit_memory_visualizer_events、memory_visualizer_timerフィールド（行222-224） |
| 1-2 | DevServer.zig | `src/bake/DevServer.zig` | HmrTopic.memory_visualizerの定義（行3930） |
| 1-3 | DevServer.zig | `src/bake/DevServer.zig` | MessageId.memory_visualizerのドキュメント（行3866-3879） |

**読解のコツ**: Fieldsというextern structがペイロードの構造を定義している点に注目。

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

通知の起点となる関数群を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DevServer.zig | `src/bake/DevServer.zig` | emitMemoryVisualizerMessage関数（行3638-3648） |
| 2-2 | DevServer.zig | `src/bake/DevServer.zig` | emitMemoryVisualizerMessageIfNeeded関数（行3632-3636） |
| 2-3 | DevServer.zig | `src/bake/DevServer.zig` | emitMemoryVisualizerMessageTimer関数（行3623-3630） |

**主要処理フロー**:
1. **行3639**: bake_debugging_featuresのコンパイル時アサート
2. **行3642-3645**: スタックフォールバックバッファの確保
3. **行3646**: MessageId.memory_visualizerを先頭に追加
4. **行3647**: writeMemoryVisualizerMessageでペイロード生成
5. **行3648**: publishでトピックに配信

#### Step 3: メッセージ生成処理を理解する

ペイロードの生成ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | DevServer.zig | `src/bake/DevServer.zig` | writeMemoryVisualizerMessage関数（行3651-3703） |
| 3-2 | memory_cost.zig | `src/bake/DevServer/memory_cost.zig` | memoryCostDetailed関数の実装 |

**主要処理フロー**:
- **行3653-3664**: Fieldsというextern structでペイロード構造を定義
- **行3665-3681**: 各メモリコストを取得してstructに格納
- **行3684-3702**: ソースマップエントリの詳細情報を書き込み

#### Step 4: サブスクリプション管理を理解する

タイマーの開始/停止を含むサブスクリプション管理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | HmrSocket.zig | `src/bake/DevServer/HmrSocket.zig` | memory_visualizerのon-subscribeフック（行83-93） |
| 4-2 | HmrSocket.zig | `src/bake/DevServer/HmrSocket.zig` | onUnsubscribe関数（行228-234） |

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

```
[サブスクライブ時]
HmrSocket.onMessage() [HmrSocket.zig:43]
    │
    └─ subscribe処理 [HmrSocket.zig:61-106]
           │
           ├─ ws.subscribe() [WebSocket購読]
           │
           └─ [memory_visualizerの場合]
                  │
                  ├─ emit_memory_visualizer_events += 1
                  ├─ dev.emitMemoryVisualizerMessage() [初回送信]
                  │
                  └─ [初回サブスクライバの場合]
                         └─ vm.timer.update() [タイマー開始]

[タイマーイベント時]
emitMemoryVisualizerMessageTimer() [DevServer.zig:3623]
    │
    ├─ bake_debugging_features チェック
    │
    ├─ emitMemoryVisualizerMessage() [DevServer.zig:3638]
    │      │
    │      ├─ MessageId.memory_visualizer 追加
    │      │
    │      ├─ writeMemoryVisualizerMessage() [DevServer.zig:3651]
    │      │      │
    │      │      ├─ memoryCostDetailed() 呼び出し
    │      │      ├─ selfProcessMemoryUsage() 呼び出し
    │      │      ├─ totalmem() / freemem() 呼び出し
    │      │      └─ source_maps イテレート
    │      │
    │      └─ dev.publish(.memory_visualizer, payload)
    │
    └─ vm.timer.update() [タイマー再スケジュール: 1000ms]
```

### データフロー図

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

memoryCostDetailed()       writeMemoryVisualizerMessage   WebSocket
├─ client_graph     ───▶        │                   ───▶  クライアント
├─ server_graph                 │                         (ビジュアライザ)
├─ js_code                      ▼
├─ source_maps           バイナリ
├─ assets                ペイロード生成
└─ other                        │
                                │
selfProcessMemoryUsage() ───▶   │
totalmem() / freemem()   ───▶   │
                                │
source_maps.entries      ───▶   ▼
                           publish()
                           (.memory_visualizer)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DevServer.zig | `src/bake/DevServer.zig` | ソース | メイン実装、emitMemoryVisualizerMessage関連関数 |
| HmrSocket.zig | `src/bake/DevServer/HmrSocket.zig` | ソース | WebSocket接続管理、タイマー開始/停止 |
| memory_cost.zig | `src/bake/DevServer/memory_cost.zig` | ソース | メモリコスト計算ロジック |
| SourceMapStore.zig | `src/bake/DevServer/SourceMapStore.zig` | ソース | ソースマップ管理、参照カウント |
