# 通知設計書 25-DebugCallbackRegistry

## 概要

本ドキュメントは、TensorFlowのC++コアランタイムにおける`DebugCallbackRegistry`の通知設計について記載する。本レジストリは、デバッグイベント（テンソル監視）をコールバック経由で通知するためのシングルトンクラスである。

### 本通知の処理概要

DebugCallbackRegistryは、TensorFlowのデバッグ機能において、監視対象ノードのテンソル値をコールバック関数経由で外部に通知するためのルーティングメカニズムを提供する。`memcbk:///`プロトコルで登録されたコールバックに対してDebugNodeKeyとTensorのペアが送信される。

**業務上の目的・背景**：TensorFlowのグラフ実行をデバッグする際、特定のノードのテンソル値をリアルタイムに観察する必要がある。DebugCallbackRegistryは、DebugTensorWatchで指定された`debug_url`（`memcbk:///`で始まるURL）に対応するコールバック関数を管理し、デバッグイベント発生時に該当のコールバックへテンソル情報をルーティングする。これにより、外部デバッグツールやテストフレームワークがTensorFlowの内部状態をインメモリで検査できる。

**通知の送信タイミング**：監視対象として設定されたノードが実行され、デバッグテンソルが生成されたタイミングで、該当するキーに登録されたコールバックが呼び出される。同一ノードの複数の実行結果は観察された順序で送信される。

**通知の受信者**：RegisterCallback()で登録されたEventCallback関数が受信者となる。コールバックのキーは`memcbk:///`URLの後続部分に対応する。

**通知内容の概要**：DebugNodeKey（デバイス名、ノード名、出力スロット、デバッグオペレーション名等）とTensor（テンソル値）のペアがコールバックに渡される。

**期待されるアクション**：コールバック関数の実装者はテンソル値の検査、ログ記録、可視化、テストアサーションなどの処理を行う。本レジストリは深いグラフ状態の検査に最適化されており、パフォーマンス重視の本番環境での使用は推奨されない。

## 通知種別

プロセス内コールバック（C++ std::function呼び出し）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（デバッグテンソル生成時に直接コールバック呼び出し） |
| 優先度 | 低（デバッグ・検査用途、本番利用非推奨） |
| リトライ | 無し |

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

debug_urlの`memcbk:///`以降の文字列をキーとしてGetCallback()で登録済みコールバックを検索し、見つかった場合にコールバックを呼び出す。キーに対応するコールバックが存在しない場合はnullptrが返され、通知はスキップされる。

## 通知テンプレート

### コールバック呼び出しの場合

| 項目 | 内容 |
|-----|------|
| 送信元 | デバッグランタイム（DebugTensorWatch経由） |
| コールバック型 | `std::function<void(const DebugNodeKey&, const Tensor&)>` |
| キー形式 | `memcbk:///` URLの後続文字列 |

### 本文テンプレート

```cpp
// コールバック関数のシグネチャ
void callback(const DebugNodeKey& key, const Tensor& tensor) {
  // key.device_name: デバイス名（例："/job:localhost/replica:0/task:0/cpu:0"）
  // key.node_name: ノード名
  // key.output_slot: 出力スロット番号
  // key.debug_op: デバッグオペレーション名
  // tensor: テンソル値
}
```

### 添付ファイル

なし（プロセス内通信）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| key (DebugNodeKey) | デバッグ対象ノードの識別情報 | デバッグランタイム | Yes |
| key.device_name | デバイス名 | グラフ実行コンテキスト | Yes |
| key.node_name | ノード名 | グラフ定義 | Yes |
| key.output_slot | 出力スロット番号 | グラフ定義 | Yes |
| key.debug_op | デバッグオペレーション名 | DebugTensorWatch設定 | Yes |
| key.debug_node_name | デバッグノード名 | DebugNodeKeyコンストラクタで生成 | Yes |
| key.device_path | デバイスパス文字列 | DeviceNameToDevicePath()で変換 | Yes |
| tensor | テンソル値 | ノード実行結果 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ノード実行 | デバッグ対象ノードの実行完了 | GetCallback(key)がnullptrでない | DebugTensorWatchで監視設定されたノード実行時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| コールバック未登録 | GetCallback()がnullptrを返した場合、コールバック呼び出しをスキップ |
| UnregisterCallback済み | UnregisterCallback()で削除されたキーに対する通知はスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[デバッグ対象ノード実行] --> B[デバッグテンソル生成]
    B --> C[DebugCallbackRegistry::singleton]
    C --> D[GetCallback key]
    D --> E{callback != nullptr?}
    E -->|Yes| F[callback DebugNodeKey, Tensor]
    E -->|No| G[通知スキップ]
    F --> H[終了]
    G --> H
```

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

### 参照テーブル一覧

本コンポーネントはデータベースを参照しない。

### 更新テーブル一覧

なし。コールバック関数の実装に依存する。

#### 内部管理データ構造

| データ構造 | 操作 | 概要 |
|-----------|------|------|
| keyed_callback_ (std::map) | READ/INSERT/DELETE | キーとコールバック関数のマッピング |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| コールバック未登録 | GetCallback()でキーが見つからない | nullptrを返却し、呼び出し元で処理をスキップ |
| コールバック内例外 | コールバック関数内で例外が発生 | コールバック実装者の責任で処理。レジストリ自体は例外を捕捉しない |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 制限 | なし（デバッグ対象ノードの実行ごとに通知） |

### 配信時間帯

制限なし。セッション実行中に動作する。

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

- テンソル値にはモデルのパラメータや入力データが含まれる可能性がある
- コールバック関数の登録はプロセス内のC++コードからのみ可能
- mutex（mu_）によりスレッドセーフな操作が保証されている
- 本番環境での使用は推奨されない（パフォーマンス最適化されていない旨がコメントに明記）

## 備考

- シングルトンパターンで実装されている（singleton()メソッド）
- 静的インスタンスポインタ（instance_）はnullptr初期化される（行22）
- キーとコールバックのマッピングはstd::mapで管理されている
- すべての操作がmutex_lock(mu_)で保護されている
- DebugNodeKeyはデバイス名をパス形式に変換する機能を持つ（DeviceNameToDevicePath）

---

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

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

### 推奨読解順序

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

デバッグ情報を表す基本データ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | debug_node_key.h | `tensorflow/core/debug/debug_node_key.h` | DebugNodeKey構造体の全フィールド（device_name, node_name, output_slot, debug_op, debug_node_name, device_path, io_of_node, is_input, io_index）（行26-53） |
| 1-2 | tensor.h | `tensorflow/core/framework/tensor.h` | Tensorクラスの基本API |

**読解のコツ**: DebugNodeKeyは不変オブジェクト（constメンバ）として設計されている。コンストラクタでdevice_pathやdebug_node_nameが自動生成される。

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

DebugCallbackRegistryのインターフェースを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | debug_callback_registry.h | `tensorflow/core/debug/debug_callback_registry.h` | EventCallback型定義（行42）、singleton(), GetCallback(), RegisterCallback(), UnregisterCallback()のAPI（行40-66） |

**主要処理フロー**:
1. **行42**: EventCallback型 = `std::function<void(const DebugNodeKey&, const Tensor&)>`
2. **行45**: singleton() - シングルトンアクセス
3. **行48**: GetCallback(key) - 登録済みコールバック取得
4. **行52**: RegisterCallback(key, callback) - コールバック登録
5. **行55**: UnregisterCallback(key) - コールバック解除

#### Step 3: 実装の詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | debug_callback_registry.cc | `tensorflow/core/debug/debug_callback_registry.cc` | シングルトン初期化（行20-29）、RegisterCallback/GetCallback/UnregisterCallbackの実装（行31-47） |

**主要処理フロー**:
- **行22**: 静的インスタンスポインタのnullptr初期化
- **行24-29**: singleton() - nullチェック付きの遅延初期化
- **行31-35**: RegisterCallback() - mutex_lockの下でkeyed_callback_にinsert
- **行37-42**: GetCallback() - mutex_lockの下でfindして結果を返却
- **行44-47**: UnregisterCallback() - mutex_lockの下でerase

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

```
[デバッグセッション設定時]
DebugCallbackRegistry::singleton()
    +-- RegisterCallback(key, callback)
            +-- mutex_lock(mu_)
            +-- keyed_callback_[key] = callback

[グラフ実行時（デバッグ対象ノード）]
デバッグランタイム
    +-- DebugCallbackRegistry::singleton()
            +-- GetCallback(key)
                    +-- mutex_lock(mu_)
                    +-- keyed_callback_.find(key)
                    +-- [found] return &callback
                    +-- [not found] return nullptr
    +-- [callback != nullptr]
            +-- callback(DebugNodeKey, Tensor)

[デバッグセッション終了時]
DebugCallbackRegistry::singleton()
    +-- UnregisterCallback(key)
            +-- mutex_lock(mu_)
            +-- keyed_callback_.erase(key)
```

### データフロー図

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

DebugTensorWatch設定 --------> RegisterCallback() ------------> keyed_callback_マップ
(debug_url, callback)           (キーとコールバックを登録)

DebugNodeKey + Tensor --------> GetCallback() -----------------> EventCallback*
(ノード実行結果)                 (キーでコールバック検索)           |
                                                                 +-> callback(key, tensor)
                                                                     (ユーザ定義処理)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| debug_callback_registry.h | `tensorflow/core/debug/debug_callback_registry.h` | ソース | DebugCallbackRegistryクラス定義 |
| debug_callback_registry.cc | `tensorflow/core/debug/debug_callback_registry.cc` | ソース | DebugCallbackRegistry実装 |
| debug_node_key.h | `tensorflow/core/debug/debug_node_key.h` | ソース | DebugNodeKey構造体定義 |
| tensor.h | `tensorflow/core/framework/tensor.h` | ソース | Tensorクラス定義 |
| mutex.h | `tensorflow/core/platform/mutex.h` | ソース | スレッド同期プリミティブ |
