# 帳票設計書 2-EventReport

## 概要

本ドキュメントは、.NET RuntimeにおけるWindowsイベントログへのエラーレポート出力機能の仕様を定義する帳票設計書である。

### 本帳票の処理概要

本帳票は、.NETアプリケーションで未処理例外やFailFast、スタックオーバーフロー等の重大なエラーが発生した際に、Windowsイベントログへエラー情報を記録するレポート機能である。

**業務上の目的・背景**：.NETアプリケーションの運用において、重大なエラーによるプロセス終了が発生した場合、その原因を事後的に調査するためにはシステムに記録された情報が不可欠である。本帳票は、Windowsイベントログという標準的な仕組みを利用することで、アプリケーションのログファイルが消失した場合でもエラー情報を保持し、システム管理者や開発者が障害調査を行えるようにする。また、イベントログ監視ツールとの連携により、リアルタイムでのアラート発報も可能となる。

**帳票の利用シーン**：本帳票は、以下のような重大なエラー発生時に自動的にイベントログへ記録される。
- 未処理例外によるアプリケーション終了
- Environment.FailFast()呼び出しによる意図的な終了
- .NET Runtime内部エラーによる終了
- スタックオーバーフローによる終了
- コードコントラクト違反による終了

**主要な出力内容**：
1. アプリケーション名（プロセスパスから抽出）
2. CoreCLRバージョン情報
3. .NETバージョン情報
4. エラー種別に応じた説明文
5. 例外情報（未処理例外の場合）
6. スタックトレース情報（マネージドスタック）
7. 終了メッセージ（FailFastの場合）

**帳票の出力タイミング**：各種エラー発生時に自動的にイベントログへ記録される。ただし、デバッガがアタッチされている場合や、既に1回記録済みの場合は出力をスキップする。

**帳票の利用者**：システム管理者、運用監視チーム、開発者、DevOpsエンジニア

## 帳票種別

イベントログエントリ / エラーレポート

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | Windowsイベントビューア | eventvwr.msc | エラー発生時に自動記録 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | Windowsイベントログ |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | N/A（イベントログシステム管理） |
| 出力方法 | Windows Event Log API (ReportEvent) |
| 文字コード | Unicode (WCHAR) |

### イベントログ固有設定

| 項目 | 内容 |
|-----|------|
| イベントソース | ".NET Runtime" |
| イベントタイプ | EVENTLOG_ERROR_TYPE |
| カテゴリ | 0 |
| 最大エントリサイズ | 31842バイト (0x7C62) |

## 帳票レイアウト

### レイアウト概要

イベントログエントリとしてテキスト形式で出力される。

```
Application: {アプリケーション名}
CoreCLR Version: {CoreCLRバージョン}
.NET Version: {.NETバージョン}
Description: {エラー説明}
[Message: {メッセージ}]
[Exception Info: {例外情報}]
[Stack:
   at {スタックフレーム1}
   at {スタックフレーム2}
   ...
]
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Application | アプリケーション名 | WszGetModuleFileName() | ファイル名のみ |
| 2 | CoreCLR Version | CoreCLRバージョン | VER_FILEVERSION_STR_L | バージョン文字列 |
| 3 | .NET Version | .NETバージョン | CLR_PRODUCT_VERSION_L | バージョン文字列 |
| 4 | Description | エラー説明 | イベントタイプに応じた固定文 | 説明文 |

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | Message | FailFastメッセージ | Environment.FailFast引数 | 文字列 | - |
| 2 | Exception Info | 例外タイプ | 例外オブジェクト | 型名 | - |
| 3 | Stack | スタックトレース | StackWalkFrames | メソッドシグネチャ | - |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | 切り詰めメッセージ | バッファ超過時 | 固定文 | "The remainder of the message was truncated." |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| エラー発生 | 対象エラーが発生していること | Yes |
| デバッガ非アタッチ | デバッガがアタッチされていないこと | Yes |
| 初回出力 | 同一プロセスで未出力であること | Yes |
| 設定有効 | EXTERNAL_logFatalError設定が0でないこと | Yes |

### イベントタイプ別設定

| イベントタイプ | イベントID | 説明 |
|--------------|-----------|------|
| ERT_UnhandledException | 1026 | 未処理例外 |
| ERT_ManagedFailFast | 1025 | マネージドFailFast |
| ERT_UnmanagedFailFast | 1023 | アンマネージドFailFast |
| ERT_StackOverflow | 1027 | スタックオーバーフロー |
| ERT_CodeContractFailed | 1028 | コードコントラクト違反 |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | スタックフレーム | スタック順（上から下） |

### 改ページ条件

N/A（イベントログエントリは単一エントリ）。ただし、最大サイズ（31842バイト）を超過した場合は切り詰め処理が行われる。

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

### 参照テーブル一覧

N/A（データベース参照なし、メモリ上の情報を直接取得）

### リソース文字列参照

| リソースID | 用途 | デフォルト値 |
|-----------|------|-------------|
| IDS_ER_APPLICATION | "Application: "ラベル | "Application: " |
| IDS_ER_UNKNOWN | 不明アプリ名 | "unknown" |
| IDS_ER_FRAMEWORK_VERSION | "CoreCLR Version: "ラベル | "CoreCLR Version: " |
| IDS_ER_UNHANDLEDEXCEPTION | 未処理例外説明 | "Description: The process was terminated due to an unhandled exception." |
| IDS_ER_MANAGEDFAILFAST | FailFast説明 | "Description: The application requested process termination through System.Environment.FailFast." |
| IDS_ER_UNMANAGEDFAILFAST | 内部エラー説明 | "Description: The process was terminated due to an internal error in the .NET Runtime " |
| IDS_ER_STACK_OVERFLOW | スタックオーバーフロー説明 | "Description: The process was terminated due to a stack overflow." |
| IDS_ER_CODECONTRACT_FAILED | コントラクト違反説明 | "Description: The application encountered a bug. A managed code contract (precondition, postcondition, object invariant, or assert) failed." |
| IDS_ER_STACK | "Stack:"ラベル | "Stack:" |
| IDS_ER_WORDAT | "   at"ラベル | "   at" |
| IDS_ER_MESSAGE_TRUNCATE | 切り詰めメッセージ | "The remainder of the message was truncated." |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| バッファ超過判定 | curSize >= MAX_SIZE_EVENTLOG_ENTRY_STRING | N/A | 31842バイト超過で切り詰め |
| 切り詰め位置 | dwMaxSizeLimit - truncCount - 1 | N/A | 改行位置で切り詰め |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[エラー発生] --> B{ShouldLogInEventLog?}
    B -->|No| Z[終了]
    B -->|Yes| C[EventReporter生成]
    C --> D[ヘッダー構築]
    D --> E{イベントタイプ}
    E -->|UnhandledException| F[例外情報追加]
    E -->|ManagedFailFast| G[メッセージ追加]
    E -->|StackOverflow| H[説明のみ]
    E -->|その他| I[説明のみ]
    F --> J[BeginStackTrace]
    G --> J
    J --> K[LogCallstackForEventReporter]
    K --> L{バッファ超過?}
    L -->|Yes| M[切り詰め処理]
    L -->|No| N[Report呼び出し]
    M --> N
    N --> O[ClrReportEvent]
    O --> Z
```

### 出力判定フロー (ShouldLogInEventLog)

```mermaid
flowchart TD
    A[開始] --> B{デバッガアタッチ?}
    B -->|Yes| C[FALSE返却]
    B -->|No| D{既に出力済み?}
    D -->|Yes| C
    D -->|No| E{logFatalError設定=0?}
    E -->|Yes| C
    E -->|No| F[TRUE返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| イベントログファイル破損 | ERROR_EVENTLOG_FILE_CORRUPT | ログ出力のみ | ログファイルの修復が必要 |
| イベントログ満杯 | ERROR_LOG_FILE_FULL | ログ出力のみ | 古いエントリの削除が必要 |
| メモリ不足 | ERROR_NOT_ENOUGH_MEMORY | ログ出力のみ | システムリソースの確認 |
| その他のエラー | 上記以外のエラーコード | アサーション（デバッグ時） | 原因調査が必要 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1エントリ/エラー発生 |
| 目標出力時間 | ミリ秒オーダー |
| 同時出力数上限 | 1（プロセスあたり1回のみ） |

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

- イベントログはシステムレベルのログであり、アクセス権限はWindowsのセキュリティポリシーに従う
- スタックトレースにはメソッド名やパラメータ情報が含まれるため、機密情報が漏洩しないよう注意が必要
- FailFastメッセージには任意の文字列が含まれる可能性があるため、アプリケーション側で機密情報を含めないよう注意が必要
- イベントログへのアクセスには通常、管理者権限が必要

## 備考

- CoreCLR（ネイティブコード実装）とNativeAOT（C#実装）の2つの実装が存在する
- NativeAOT版はRhFailFastReasonを使用し、若干異なる列挙値を持つ
- デバッガアタッチ時は出力がスキップされるため、デバッグ中は別の手段でエラー情報を確認する必要がある
- 同一プロセスで複数回のエラーが発生しても、イベントログへの出力は初回のみ

---

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

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

### 推奨読解順序

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

まず、EventReporterクラスの構造と列挙型を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | eventreporter.h | `src/coreclr/vm/eventreporter.h` | EventReporterクラスの定義、EventReporterType列挙型、主要メソッドのシグネチャを理解する |
| 1-2 | EventReporter.cs | `src/coreclr/nativeaot/System.Private.CoreLib/src/System/EventReporter.cs` | NativeAOT版の実装、RhFailFastReasonとの対応を理解する |

**読解のコツ**: C++版とC#版の両方の実装があるため、まずヘッダファイルでインターフェースを把握し、その後実装の詳細を確認する。

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

処理の起点となるコンストラクタと出力判定ロジックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | eventreporter.cpp | `src/coreclr/vm/eventreporter.cpp` | EventReporterコンストラクタ（行32-152）でヘッダー構築処理を理解する |
| 2-2 | eventreporter.cpp | `src/coreclr/vm/eventreporter.cpp` | ShouldLogInEventLog()（行456-487）で出力判定ロジックを理解する |

**主要処理フロー**:
1. **行32-41**: コンストラクタ開始、CONTRACTL宣言
2. **行42**: m_eventTypeの保存
3. **行44-78**: アプリケーション名の取得と追加
4. **行80-93**: CoreCLRバージョンと.NETバージョンの追加
5. **行97-151**: イベントタイプに応じた説明文の追加

#### Step 3: 説明追加処理を理解する

AddDescriptionメソッドによる詳細情報の追加処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | eventreporter.cpp | `src/coreclr/vm/eventreporter.cpp` | AddDescription()（行165-231）で追加情報の構築を理解する |

**主要処理フロー**:
- **行188-208**: イベントタイプに応じたラベルプレフィックスの追加
- **行209-210**: FailFastの場合は"Message: "
- **行211-220**: UnhandledExceptionの場合は"Exception Info: "
- **行221-228**: CodeContractFailedの場合は"Contract details: "
- **行229-231**: 実際の説明文字列の追加

#### Step 4: スタックトレース処理を理解する

スタックトレースの収集と出力処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | eventreporter.cpp | `src/coreclr/vm/eventreporter.cpp` | BeginStackTrace()（行243-261）とAddStackTrace()（行273-324）を理解する |
| 4-2 | eventreporter.cpp | `src/coreclr/vm/eventreporter.cpp` | LogCallstackForEventReporter()（行576-583）でスタックウォークの呼び出しを理解する |
| 4-3 | eventreporter.cpp | `src/coreclr/vm/eventreporter.cpp` | LogCallstackForEventReporterCallback()（行506-532）で各フレームの処理を理解する |

**主要処理フロー**:
- **行279-323**: AddStackTrace()でのバッファ管理と切り詰め処理
- **行287-288**: MAX_SIZE_EVENTLOG_ENTRY_STRING (31842バイト)超過チェック
- **行291-318**: 切り詰め位置の決定と切り詰めメッセージの追加

#### Step 5: イベントログ出力処理を理解する

最終的なイベントログへの書き込み処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | eventreporter.cpp | `src/coreclr/vm/eventreporter.cpp` | Report()（行370-442）でイベントログへの出力を理解する |

**主要処理フロー**:
- **行379-401**: イベントタイプからイベントIDへの変換
- **行410-416**: ClrReportEvent()の呼び出し
- **行418-439**: エラーハンドリング（破損、満杯、メモリ不足）

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

```
[エラー発生]
    │
    └─ DoReportForUnhandledNativeException() / その他呼び出し元
           │
           ├─ ShouldLogInEventLog()
           │      ├─ CORDebuggerAttached()
           │      ├─ minipal_is_native_debugger_present()
           │      ├─ InterlockedExchange() [一度だけチェック]
           │      └─ CLRConfig::GetConfigValue(EXTERNAL_logFatalError)
           │
           └─ EventReporter(type)
                  │
                  ├─ WszGetModuleFileName() [アプリケーション名取得]
                  ├─ LoadResource() [各種リソース文字列読み込み]
                  │
                  ├─ AddDescription() [詳細情報追加]
                  │
                  ├─ BeginStackTrace() [スタックセクション開始]
                  │
                  ├─ LogCallstackForEventReporter()
                  │      └─ LogCallstackForEventReporterWorker()
                  │             └─ StackWalkFrames()
                  │                    └─ LogCallstackForEventReporterCallback()
                  │                           ├─ TypeString::AppendMethodInternal()
                  │                           └─ AddStackTrace()
                  │
                  └─ Report()
                         └─ ClrReportEvent()
                                └─ ReportEvent() [Windows API]
```

### データフロー図

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

WszGetModuleFileName()     EventReporterコンストラクタ         m_Description
       │                          │                                │
       └─ appPath ───────────────▶ アプリ名抽出 ─────────────────▶ "Application: xxx"
                                   │
VER_FILEVERSION_STR_L ────────────▶ バージョン追加 ──────────────▶ "CoreCLR Version: xxx"
                                   │
CLR_PRODUCT_VERSION_L ────────────▶ バージョン追加 ──────────────▶ ".NET Version: xxx"
                                   │
m_eventType ──────────────────────▶ 説明文選択 ──────────────────▶ "Description: xxx"


例外/FailFast情報          AddDescription()                    m_Description
       │                          │                                │
       └─ 詳細情報 ──────────────▶ ラベル+内容追加 ───────────────▶ "Message: xxx" / "Exception Info: xxx"


StackWalkFrames()          AddStackTrace()                     m_Description
       │                          │                                │
       └─ 各フレーム ────────────▶ バッファ追加 ──────────────────▶ "   at Method(args)"
                                   │
                                   └─ 超過時 ────────────────────▶ "...truncated..."


m_Description              Report()                            Windows Event Log
       │                          │                                │
       └─ 完成した文字列 ────────▶ ClrReportEvent() ──────────────▶ イベントID 1023-1028
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| eventreporter.cpp | `src/coreclr/vm/eventreporter.cpp` | ソース | CoreCLR版イベントレポーターの実装 |
| eventreporter.h | `src/coreclr/vm/eventreporter.h` | ヘッダ | EventReporterクラスの定義 |
| EventReporter.cs | `src/coreclr/nativeaot/System.Private.CoreLib/src/System/EventReporter.cs` | ソース | NativeAOT版イベントレポーターの実装 |
| clrversion.h | `src/coreclr/inc/clrversion.h` | ヘッダ | バージョン定義（VER_FILEVERSION_STR_L等） |
| resource.h | `src/coreclr/dlls/mscorrc/resource.h` | ヘッダ | リソース文字列ID定義 |
| typestring.h | `src/coreclr/vm/typestring.h` | ヘッダ | メソッドシグネチャ文字列化 |
| debugdebugger.cpp | `src/coreclr/vm/debugdebugger.cpp` | ソース | DebugStackTrace::GetStackFramesFromException()の実装 |
