# 帳票設計書 7-gstat - GEOM統計レポート

## 概要

本ドキュメントは、FreeBSDシステムにおけるGEOMストレージ統計レポート(gstat)の帳票設計書である。gstatコマンドはGEOMフレームワークの統計情報を読み取り、ストレージデバイス（プロバイダ/コンシューマ）の読み書きスループット、レイテンシ、ビジー率をリアルタイムにcurses画面またはテキスト形式で出力する。

### 本帳票の処理概要

**業務上の目的・背景**：ストレージ管理者がGEOMレイヤのデバイスごとのI/Oパフォーマンスをリアルタイムに監視し、ディスクのボトルネック特定やストレージ設計の根拠データを得るために必要なレポートである。

**帳票の利用シーン**：ストレージI/Oの即時監視、ディスク障害の検知、RAIDアレイのパフォーマンス確認、ZFSプール構成の評価。

**主要な出力内容**：
1. デバイスごとのキュー長、転送/秒、読み取り/秒、読み取りKB/秒、読み取りレイテンシ
2. デバイスごとの書き込み/秒、書き込みKB/秒、書き込みレイテンシ
3. デバイスごとのビジー率（色付き表示：緑/マゼンタ/赤）
4. CSV出力モード（-C）でのタイムスタンプ付きデータ

**帳票の出力タイミング**：コマンド実行で即座にリアルタイム表示開始。-Iオプションで更新間隔を指定。-bオプションでバッチモード（1回出力）。-Bオプションで繰り返しバッチモード。

**帳票の利用者**：ストレージ管理者、システム管理者、パフォーマンスエンジニア。

## 帳票種別

集計表（curses画面/テキスト/CSV形式のリアルタイムGEOM I/O統計レポート）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| N/A | ターミナル/コンソール | N/A | `gstat [-abBcCdops] [-f filter] [-I interval]` コマンド実行 |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | curses画面（デフォルト）/テキスト（-b,-B）/CSV（-C） |
| 用紙サイズ | N/A（端末全画面） |
| 向き | N/A |
| ファイル名 | N/A |
| 出力方法 | curses画面更新 or printf標準出力 |
| 文字コード | ASCII |

## 帳票レイアウト

### レイアウト概要（テキスト/curses）

```
dT: x.xxxs  w: x.xxxs  filter: ...
 L(q)  ops/s    r/s   kBps   ms/r    w/s   kBps   ms/w   %busy Name
 xxxx  xxxxx  xxxxx  xxxxx  xxxxx  xxxxx  xxxxx  xxxxx  xxxxx.x devicename
```

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | L(q) | キュー長 | DSM_QUEUE_LENGTH | 整数 | 4桁 |
| 2 | ops/s | 合計転送/秒 | DSM_TRANSFERS_PER_SECOND | 小数点0桁 | 6桁 |
| 3 | r/s | 読み取り転送/秒 | DSM_TRANSFERS_PER_SECOND_READ | 小数点0桁 | 6桁 |
| 4 | kBps(r) | 読み取りKB/秒 | DSM_MB_PER_SECOND_READ * 1024 | 小数点0桁 | 6桁 |
| 5 | ms/r | 読み取りレイテンシ(ms) | DSM_MS_PER_TRANSACTION_READ | 小数点1-3桁 | 6桁 |
| 6 | w/s | 書き込み転送/秒 | DSM_TRANSFERS_PER_SECOND_WRITE | 小数点0桁 | 6桁 |
| 7 | kBps(w) | 書き込みKB/秒 | DSM_MB_PER_SECOND_WRITE * 1024 | 小数点0桁 | 6桁 |
| 8 | ms/w | 書き込みレイテンシ(ms) | DSM_MS_PER_TRANSACTION_WRITE | 小数点1-3桁 | 6桁 |
| 9 | %busy | ビジー率 | DSM_BUSY_PCT | 小数点1桁 | 6桁 |
| 10 | Name | デバイス名 | gprovider.lg_name | 文字列 | 可変 |

### CSV出力モード（-Cオプション）

先頭行にヘッダー、各行にタイムスタンプ+上記項目をカンマ区切りで出力。

### 追加列（オプション）

| オプション | 項目名 | 説明 |
|-----------|-------|------|
| -s | kB(r)/kB(w) | 転送あたりのKB数（読み/書きそれぞれ） |
| -d | d/s, kBps(d), ms/d | delete操作の統計 |
| -o | o/s, ms/o | その他操作の統計 |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| -fフィルタ | 正規表現でデバイス名をフィルタリング | No |
| -aオプション | ビジー率0.1%未満のデバイスをスキップ | No |
| -cオプション | コンシューマも表示 | No |
| -pオプション | rank=1のプロバイダのみ表示 | No |

### ソート順

GEOMスナップショット内の出現順

### 改ページ条件

curses画面の行数に基づき、画面外のデバイスはスキップ

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

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| libgeom統計 | GEOM I/O統計スナップショット | geom_stats_snapshot_get() |
| GEOMトポロジ | デバイス名、クラス名 | geom_gettree() |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| dt | tp.tv_sec - tq.tv_sec + (nsec差分) * 1e-9 | 浮動小数点 | スナップショット間隔 |
| KB/s | MB_PER_SECOND * 1024 | 小数点0桁 | MB→KB変換 |
| ms/r, ms/w | DSM_MS_PER_TRANSACTION_READ/WRITE | 動的桁数 | 1e3超:0桁, 1e0超:1桁, 他:3桁 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[コマンド実行] --> B[geom_gettree: トポロジ取得]
    B --> C[geom_stats_open: 統計チャネルオープン]
    C --> D[初回スナップショット取得]
    D --> E{curses初期化?}
    E -->|!flag_b| F[initscr, start_color等]
    E -->|flag_b| G[バッチモード]
    F --> H[メインループ]
    G --> H
    H --> I[geom_stats_snapshot_get: 新スナップショット]
    I --> J[dt計算（スナップショット間隔）]
    J --> K[デバイスループ]
    K --> L{フィルタ条件判定}
    L -->|Pass| M[devstat_compute_statistics: 統計計算]
    L -->|Skip| K
    M --> N[PRINTMSG: 統計出力]
    N --> K
    K --> O{全デバイス処理完了}
    O --> P{バッチモード?}
    P -->|flag_b & !flag_B| Q[終了]
    P -->|flag_B| R[usleep → メインループ]
    P -->|curses| S[refresh, getch, usleep]
    R --> H
    S --> T{キー入力}
    T -->|q| U[終了]
    T -->|f| V[libedit: フィルタ入力]
    T -->|c| W[flag_c切替]
    T -->|< >| X[間隔変更]
    T -->|other| H
    V --> H
    W --> H
    X --> H
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| geom_gettree失敗 | GEOMフレームワーク異常 | err(1, "geom_gettree = %d") | カーネルGEOM確認 |
| geom_stats_open失敗 | 統計チャネルオープン失敗 | err(1, "geom_stats_open()") | /dev/geom.ctlの権限確認 |
| 正規表現エラー | -fの正規表現が不正 | errx(EX_USAGE, "Invalid filter") | 正規表現構文確認 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | GEOMデバイス数（通常数～数百） |
| 目標出力時間 | リアルタイム（デフォルト1秒間隔） |
| 同時出力数上限 | N/A |

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

- geom統計の読み取りには通常root権限が不要
- ストレージトポロジ情報の露出リスクは低い

## 備考

- ビジー率による色分け：80%超=赤(3)、50%超=マゼンタ(2)、それ以下=緑(1)（58-59行目、509-514行目）
- isatty(stdout)が偽の場合は自動的にバッチモード（107-108行目）
- libedit/historyによるフィルタ入力のインタラクティブ編集をサポート（188-201行目）
- sequence0/sequence1の不一致時はスキップ（データ整合性チェック、341行目）
- コンシューマ表示時はclass/geom/provider形式でパス表示（389-396行目）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | gstat.c | `usr.sbin/gstat/gstat.c` | 54-56行目: グローバルフラグ（flag_a, flag_b, flag_B, flag_c, flag_C, flag_d, flag_o, flag_p, flag_s） |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | gstat.c | `usr.sbin/gstat/gstat.c` | 76-613行目: main関数。全処理が単一のmain関数内に集約（オプション解析→初期化→メインループ→終了） |

**主要処理フロー**:
1. **111行目**: getoptでオプション解析
2. **177行目**: geom_gettree()でGEOMトポロジ取得
3. **180行目**: geom_stats_open()で統計チャネルオープン
4. **187-216行目**: curses/libedit初期化
5. **218-606行目**: メインループ（スナップショット取得→統計計算→出力→待機）

#### Step 3: メインループの統計計算を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | gstat.c | `usr.sbin/gstat/gstat.c` | 350-375行目: devstat_compute_statistics呼び出し。16種類のDSM_*統計値を一括計算 |

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

```
main() [76行目]
    |
    +-- geom_gettree() [177行目] ... GEOMトポロジ取得
    +-- geom_stats_open() [180行目] ... 統計チャネル
    +-- history_init() / el_init() ... libedit初期化
    +-- initscr() / start_color() ... curses初期化
    +-- メインループ [218行目]
           |
           +-- geom_stats_snapshot_get() [219行目] ... スナップショット
           +-- geom_stats_snapshot_next() [306行目] ... デバイス走査
           +-- geom_lookupid() [312行目] ... デバイス名解決
           +-- regexec() [337行目] ... フィルタマッチ
           +-- devstat_compute_statistics() [350行目] ... 統計計算
           +-- PRINTMSG() ... 出力（printw or printf）
           +-- refresh() [548行目] ... curses画面更新
           +-- getch() [550行目] ... キー入力処理
```

### データフロー図

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

/dev/geom.ctl ───────▶ geom_stats_snapshot_get()  ──▶ curses画面
GEOM kernel stats               |                       or
                    devstat_compute_statistics()      ──▶ 標準出力
GEOMトポロジ ────────▶ geom_gettree()                     (printf/CSV)
                    geom_lookupid()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| gstat.c | `usr.sbin/gstat/gstat.c` | ソース | 全処理が単一ファイルに集約（622行） |
