# 機能設計書 58-パフォーマンス計測（Instrumentation）

## 概要

本ドキュメントは、ビルドプロセスの各ステップの実行時間・呼び出し回数を計測するインスツルメンテーション機能の設計を記述する。DEBUGモードでの診断情報出力に対応する。

### 本機能の処理概要

**業務上の目的・背景**：Tailwind CSSのビルドパフォーマンスをモニタリングし、ボトルネックを特定するために、各処理ステップの実行時間と呼び出し回数を計測する仕組みが必要である。`Instrumentation` クラスは、DEBUGモード時にビルドプロセスの詳細な診断情報を出力し、開発者がパフォーマンス問題を特定・解決できるようにする。

**機能の利用シーン**：`DEBUG=tailwindcss` 環境変数を設定してビルドを実行すると、各処理ステップの所要時間がstderrに出力される。PostCSSプラグイン、Viteプラグイン、Webpackローダー、CLIの全てで使用されている。ブラウザ版ではPerformance APIを使用してDevToolsで計測データを確認できる。

**主要な処理内容**：
1. start(label)：タイマーの開始（hrtime.bigint使用）
2. end(label)：タイマーの終了と経過時間の蓄積
3. hit(label)：呼び出し回数のカウント
4. report()：計測結果のフォーマット出力
5. reset()：計測データのクリア
6. Symbol.dispose：スコープ終了時の自動レポート出力（using構文対応）

**関連システム・外部連携**：process.hrtime（Node.js高精度タイマー）、Performance API（ブラウザ版）、process.stderr（出力先）

**権限による制御**：DEBUG環境変数によりレポート出力の有無が制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | インフラストラクチャ機能のため特定の画面に紐づかない |

## 機能種別

計測・診断 / ユーティリティ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| label | string | Yes | 計測対象のラベル名 | - |
| defaultFlush | (message: string) => void | No | レポート出力関数。デフォルトはprocess.stderr.write | - |

### 入力データソース

- process.hrtime.bigint()（Node.js版の高精度タイマー）
- performance.mark/measure（ブラウザ版）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| report | string | フォーマットされた計測レポート（ヒット数+タイマー情報） |

### 出力先

- process.stderr（Node.js版、DEBUGモード時）
- Performance API（ブラウザ版、DevToolsのPerformanceタブで確認）

## 処理フロー

### 処理シーケンス

```
1. Instrumentation インスタンス作成
   └─ using I = new Instrumentation()（自動dispose対応）
2. start(label) 呼び出し
   └─ timerStackにプッシュ、hitsカウント、hrtime取得
3. 処理実行
4. end(label) 呼び出し
   └─ 経過時間を計算しtimersに蓄積、timerStackからポップ
5. [スコープ終了] Symbol.dispose
   └─ DEBUG有効時にreport()を自動呼び出し
6. report() 実行
   └─ 未終了タイマーの自動終了 → ヒット情報+タイマー情報のフォーマット出力
```

### フローチャート

```mermaid
flowchart TD
    A[new Instrumentation] --> B[using宣言でスコープ管理]
    B --> C[start label]
    C --> D[timerStackにpush]
    D --> E[処理実行]
    E --> F[end label]
    F --> G{label一致?}
    G -->|Yes| H[経過時間計算・蓄積]
    G -->|No| I[Error: Mismatched timer label]
    H --> J{スコープ終了?}
    J -->|Yes| K[Symbol.dispose]
    K --> L{DEBUG有効?}
    L -->|Yes| M[report 出力]
    L -->|No| N[何もしない]
    J -->|No| C
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-58-01 | ネストラベル管理 | タイマーはスタック構造でネストされ、IDは親ラベルをスラッシュ区切りで連結（例: "Build//Scan for candidates"） | start/end時 |
| BR-58-02 | ラベル不一致検出 | end()時にtimerStack末尾のラベルと引数のラベルが一致しない場合はErrorをthrow | end時 |
| BR-58-03 | 自動レポート出力 | Symbol.dispose（using構文）によりスコープ終了時に自動的にreport()を呼び出す | DEBUG有効時 |
| BR-58-04 | 未終了タイマー自動終了 | report()時に未終了のタイマーがある場合、自動的にend()を呼び出す | report時 |
| BR-58-05 | ヒット＋タイマー混合出力 | ヒットのみ（タイマーなし）の情報を先に出力し、その後タイマー情報を出力 | report時 |
| BR-58-06 | 時間フォーマット | 経過時間はミリ秒（小数点2桁）でフォーマット。右揃えでパディング | report時 |

### 計算ロジック

- 経過時間：`(Number(value) / 1e6).toFixed(2)` ms（bigintナノ秒→ミリ秒変換）
- ネスト表現：`label.split('//').length` でインデント深度を計算

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

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ラベル不一致エラー | end()のラベルがtimerStack末尾と不一致 | Error("Mismatched timer label: ...")をthrow |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- process.hrtime.bigint()によるナノ秒精度の計測
- DefaultMapによる効率的なカウンタ・タイマー管理
- DEBUGが無効の場合、start/endの呼び出し自体が短絡評価でスキップされる（呼び出し側の `DEBUG && I.start(...)` パターン）

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

- 診断情報はstderrに出力され、本番環境ではDEBUGが無効のため出力されない
- 計測データにはファイルパスが含まれる可能性がある

## 備考

- Node.js版とブラウザ版で別の実装が存在する
  - Node.js版：`packages/@tailwindcss-node/src/instrumentation.ts` - process.hrtime.bigint使用
  - ブラウザ版：`packages/@tailwindcss-browser/src/instrumentation.ts` - Performance API使用
- TypeScript 5.2のExplicit Resource Management（using構文）に対応
- Symbol.dispose/Symbol.asyncDisposeのポリフィルが含まれている

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | instrumentation.ts | `packages/@tailwindcss-node/src/instrumentation.ts` | Instrumentationクラスのプライベートフィールド（#hits, #timers, #timerStack）を確認（11-13行目） |

**読解のコツ**: `#hits` はDefaultMap<string, {value: number}>でラベル→呼び出し回数のマッピング。`#timers` はDefaultMap<string, {value: bigint}>でラベル→累積時間のマッピング。`#timerStack` はスタック構造でネストしたタイマーを管理する。

#### Step 2: Node.js版の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | instrumentation.ts | `packages/@tailwindcss-node/src/instrumentation.ts` | start（23-32行目）: namespace構築、hrtime取得、スタックpush |
| 2-2 | instrumentation.ts | `packages/@tailwindcss-node/src/instrumentation.ts` | end（35-49行目）: ラベル検証、経過時間計算、スタックpop |
| 2-3 | instrumentation.ts | `packages/@tailwindcss-node/src/instrumentation.ts` | report（57-99行目）: フォーマット出力 |
| 2-4 | instrumentation.ts | `packages/@tailwindcss-node/src/instrumentation.ts` | [Symbol.dispose]（102-104行目）: 自動レポート |

**主要処理フロー**:
- **23-32行目**: start - ネスト名前空間をスラッシュ区切りで構築し、timerStackにpush
- **35-49行目**: end - スタック末尾のラベル一致を検証し、bigintの差分を#timersに加算
- **57-99行目**: report - ヒット情報とタイマー情報をフォーマットし、ANSIカラーコード付きで出力
- **102-104行目**: DEBUG有効時にのみreport()を呼び出す

#### Step 3: ブラウザ版の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | instrumentation.ts | `packages/@tailwindcss-browser/src/instrumentation.ts` | 全体（1-29行目）: Performance API版の簡潔な実装 |

**主要処理フロー**:
- **2-4行目**: start - performance.mark()
- **6-13行目**: end - performance.mark() + performance.measure()
- **16-19行目**: hit - performance.mark（detailあり）
- **22-28行目**: error - performance.markでエラー記録後throw

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

```
[Node.js版]
Instrumentation
    |
    +-- start(label)
    |       +-- DefaultMap.get() [#hits カウント]
    |       +-- DefaultMap.get() [#timers 初期化]
    |       +-- process.hrtime.bigint()
    |       +-- #timerStack.push()
    |
    +-- end(label)
    |       +-- process.hrtime.bigint()
    |       +-- #timerStack.pop()
    |       +-- #timers.get().value += elapsed
    |
    +-- report(flush)
    |       +-- [未終了タイマー自動end]
    |       +-- [ヒット情報フォーマット]
    |       +-- [タイマー情報フォーマット]
    |       +-- flush(output)
    |       +-- reset()
    |
    +-- [Symbol.dispose]()
            +-- report() [DEBUG有効時]

[ブラウザ版]
Instrumentation
    +-- start(label)  → performance.mark()
    +-- end(label)    → performance.measure()
    +-- hit(label)    → performance.mark(detail)
    +-- error(error)  → performance.mark(detail) + throw
```

### データフロー図

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

start(label)    ───▶  hrtime取得 → stack push   (内部状態更新)
                            |
end(label)      ───▶  hrtime取得 → 差分計算     (内部状態更新)
                            |
[スコープ終了]   ───▶  report()
                            |
                            v
                  ヒット数 + タイマー情報     ───▶  process.stderr
                  のフォーマット                     (ANSI color)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| instrumentation.ts | `packages/@tailwindcss-node/src/instrumentation.ts` | ソース | Node.js版のInstrumentationクラス |
| instrumentation.ts | `packages/@tailwindcss-browser/src/instrumentation.ts` | ソース | ブラウザ版のInstrumentationクラス |
| env.ts | `packages/@tailwindcss-node/src/env.ts` | ソース | DEBUG環境変数の解決 |
| default-map.ts | `packages/tailwindcss/src/utils/default-map.ts` | ソース | DefaultMap（カウンタ・タイマー管理） |
