Skip to main content

job-report

Generate a factual summary report card for an HPC job from an Omnistat database using the single-shot omnistat-inspect JSON command. Use this for a quick, comprehensive snapshot of what a job did (stats, energy, health, data quality) without diagnosing why. For root-cause investigation, performance debugging, or comparing jobs, use job-analysis instead.

Jump to install

Source facts

Repository
ROCm/omnistat
Last source activity
July 13, 2026 at 19:29
Detected SKILL.md language
English
Stars
27
Forks
6

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
job-report
description
Generate a factual summary report card for an HPC job from an Omnistat database using the single-shot omnistat-inspect JSON command. Use this for a quick, comprehensive snapshot of what a job did (stats, energy, health, data quality) without diagnosing why. For root-cause investigation, performance debugging, or comparing jobs, use job-analysis instead.
allowedPrompts
[{"tool":"Bash","prompt":"run omnistat-inspect"},{"tool":"Bash","prompt":"create temporary directory"},{"tool":"Bash","prompt":"read query results from file"},{"tool":"Bash","prompt":"write report to file"}]
# Job Report Generate a factual summary **report card** for an HPC job using GPU telemetry collected by Omnistat. A **single invocation** of `omnistat-inspect ... job <ID> report` returns one JSON document containing every datum needed to render the report. It supports flexible job-context resolution (`--start`/`--end` to skip discovery, `--cache-dir` for cheap repeat calls) and a home for deeper analysis subcommands (e.g. `iterations`). **Target audience:** HPC engineers, AI/ML researchers, system administrators who need a quick, comprehensive snapshot of a job's behavior. **When to use this vs `job-analysis`.** Use **job-report** when you want a quick, factual snapshot of *what* a job did — global statistics, energy, health findings, and data quality, presented as-is. This is NOT an investigation tool: it does not form hypotheses, find root causes, or compare jobs. When you need to understand *why* a job behaved as it did (bottlenecks, throttling, stragglers, regressions, healthy-vs-degraded comparison), use **job-analysis** instead. A common pattern is to run job-report first for the snapshot, then job-analysis if something looks off. ## Bash Tool Description Convention When calling the Bash tool, phrase the `description` field to match the `allowedPrompts` declared in this skill's frontmatter so commands are auto-approved: - `omnistat-inspect` invocation → **"run omnistat-inspect"** - `mktemp` → **"create temporary directory"** - `cat` / reading JSON output files → **"read query results from file"** - Writing the final report → **"write report to file"** ## Prerequisites 1. **Data source** — one of: - **VictoriaMetrics running** with the Omnistat database loaded (use the `open-database` skill if needed), OR - **CSV exports** from `omnistat-query --export` 2. **Python virtual environment activated** with omnistat installed (`pip install ".[query]"` from the omnistat repo root). Confirm `which omnistat-inspect` resolves inside the venv. 3. **Job ID** to report on. ## One-Shot Data Collection `omnistat-inspect ... job <ID> report` writes the JSON document to stdout — redirect it to a file. The data source (`--tsdb-url` / `--csv-dir`) and `--cache-dir` are **global** flags (before `job`); the job ID is a positional argument of the `job` group; `report` is the subcommand. ```bash # 1. Create a scratch directory SCRATCH=$(mktemp -d /tmp/omnistat-report-XXXXXX) echo "Scratch directory: $SCRATCH" # 2a. TSDB mode TSDB_URL="http://localhost:8428" omnistat-inspect --tsdb-url $TSDB_URL job JOBID report > $SCRATCH/report.json # 2b. CSV mode (alternative) omnistat-inspect --csv-dir /path/to/csv/exports job JOBID report > $SCRATCH/report.json ``` Useful flags: | Flag | Position | Purpose | |------|----------|---------| | `--cache-dir DIR` | global (before `job`) | Persist the discovery snapshot and per-module results so repeat calls skip the day-scan and re-computation. | | `--start ISO --end ISO` | `job` group (after JOBID) | Provide the job window directly to **skip job discovery** entirely (much fewer queries). Use with `--interval` when the sampling interval is known. | | `--interval SECONDS` | `job` group | Override discovered sampling interval. | | `--refresh` | `job` group | Force fresh discovery, ignoring any cached snapshot. | | `--cv-threshold 0.05` | `report` subcommand | CV value above which a variance drill-down is reported (default 0.05). | | `--verbose` | `report` subcommand | Include full per-node / per-GPU arrays under `stats.variance.by_node[*].all` and `stats.variance.by_gpu[*].all`, and expand `stats.kernels.top` from the top 10 to **all** `num_kernels` kernels. | That single invocation produces everything the report card needs. ### Job-context flexibility `omnistat-inspect` resolves the job window in one of two ways: - **Discovery (default)** — `omnistat-inspect --tsdb-url $URL job JOBID report` scans the TSDB for the job's time range. Add `--cache-dir $SCRATCH/cache` so a second call (e.g. an `iterations` follow-up) rehydrates from the cached snapshot instead of re-scanning. - **Direct window** — when you already know the job's start/end (e.g. from the scheduler), pass them to skip discovery: ```bash omnistat-inspect --tsdb-url $TSDB_URL job JOBID \ --start 2026-01-01T12:00:00Z --end 2026-01-01T14:30:00Z --interval 10 \ report > $SCRATCH/report.json ``` The `query_stats.total_queries` field will be noticeably lower than a discovery run. (On the TSDB backend the overview still fills hosts/versions live; on CSV the direct path reports only what the window contains.) ## JSON Schema Reference `report.json` is an **envelope** wrapping the report-card payload. The envelope keys are: - `jobid` — the analyzed job id - `generated_at` — ISO 8601 UTC timestamp of when the report was produced - `data_source` — `{type: "tsdb"|"csv", url?, dir?}`; `url` is present for TSDB, `dir` for CSV. This is the source of the "Database" line in Report Metadata. - `overview` — job identity/topology (below) - `stats` — gauges, counters, hardware counters, **and `variance`** (below) - `health` — data-collection coverage **and** health indicators (below) - `query_stats` — `{total_queries, total_query_time_seconds, elapsed_seconds}`; the source of the Report Metadata counts. Note the nesting: `variance` lives under `stats`, and both the data-collection table and the health indicators live under `health`. There is no top-level `variance`, `data_collection`, or `metadata` key. ### `overview` - `jobid`, `user`, `partition` - `start_time`, `end_time` (ISO 8601 UTC) - `duration_seconds`, `duration_human` (e.g. `"2h 34m 12s"`) - `num_nodes`, `num_gpus`, `hosts[]` - `gpu_type`, `driver_version`, `vbios_version` — each is always a sorted list of distinct strings (single-element in the common case), or `null` if the field was not present in the data. - `omnistat_version`, `sampling_interval` (seconds) - `annotations[]` — strings; empty list if none - `figure_of_merit` — list of `{name, instance, min, max, last, num_points}` or `null` ### `stats.gauges[]` Rows in display order. Each entry: `{source, label, name, mean, min, max, unit, n, cv, percentiles}`. `n` is the size of the population behind `cv` / `percentiles` — pooled sample count for plain gauges, per-node-rate count for counter-derived rate gauges (Network RX/TX rate). `min`/`max` are absolute sample extremes over that same population (compare with `stats.variance.by_*[].min`/`max`, which are extremes of *per-key reduced values* — see the `reduction` field there). `percentiles` is `{p5, p25, p50, p75, p95}` over the pooled population. Only metrics that were present in the data are emitted — absent metrics are simply omitted from the list. **Units in the JSON are each metric's native unit** (`W`, `MHz`, `°C`, `%`, `bytes`, `KiB`, `B/s`, `KiB/s`, `J`); labels are unit-free. The renderer is responsible for picking a sensible display unit per row (see "Unit selection" below). ### `stats.counters[]` Each entry: `{source, label, name, total, unit}`. Same base-unit convention as gauges. Same emit-only-if-present rule. ### `stats.hardware_counters` `null` or `{rows: [...], flops: [...] | null, variance: {...}}`. Each `rows[]` entry: `{counter, total, active_rate, effective_rate, observed_span_seconds, monotonic, num_series}`. Counters are summed per GCD (`(instance, card)`) over the full job range using reset-aware `increase()` semantics, so `total` is robust to the ROCm spurious-zero glitch. `active_rate` = `total ÷ observed_span_seconds` (mean per-GCD span actually accumulating); `effective_rate` = `total ÷ job duration` (charges startup/activation idle). `monotonic` is `false` when a downward step (spurious zero, genuine reset, or multiplexing) was detected. Totals/FLOPS assume a **cumulative** counter; **time-multiplexed counters are rate-like (the series churns instead of growing) and are not yet supported**, so an implausible `total`/FLOPS or a `monotonic: false` row likely indicates multiplexing rather than a usable figure. Each `flops[]` entry: `{precision, kind, total_flops, active_rate_flops_per_s, effective_rate_flops_per_s}` (or the whole `flops` is `null`). `active_rate_flops_per_s` divides by the mean per-GCD active span, `effective_rate_flops_per_s` by full wall time. `variance` carries the **per-counter-name spatial variance** of per-GCD counter totals, with the same outer shape as `stats.kernels.variance`: ```jsonc { "cv_threshold": <float>, "metric": "counter_total", // the compared quantity = per-GCD cumulative counter delta "by_node": [ ... ], // key fields: {instance} "by_gpu_id": [ ... ], // key fields: {card} "by_gpu": [ ... ] // key fields: {instance, card} } ``` Each list entry has the same outer shape as a `stats.variance.by_*` entry (`source`/`label`/`name`/`unit`/`reduction`/`n`/`cv`/`min`/`max`/`all`|`percentiles`) **plus a `counter` field** holding the counter name; `source` is `"GPU"`, `label`/`name` are the counter name, `unit` is `"count"`, and `reduction` is `"total"`. The compared quantity is each GCD's **cumulative counter total** — a direct FLOPS-imbalance proxy, since FLOPS scale with the hardware-counter total, so a single straggler GCD surfaces here. All three lists are `[]` when nothing crossed `cv_threshold`. ### `stats.kernels` `null` when kernel tracing was off / no kernel data exists (the report omits the Top kernels table and the folded kernel-dispatch-duration variance rows). Otherwise: ```jsonc { "num_kernels": <int>, // distinct kernels seen across the job "total_dispatches": <int>, // summed over all kernels/GPUs "total_duration_ns": <float>, // summed GPU time over all kernels (ns) "dropped_dispatches": <int>, // node-level dropped count (0 if none) "top": [ // top TOP_KERNELS_LIMIT (10) by default, or all num_kernels kernels under --verbose; ranked by total_duration_ns desc { "kernel": "<full mangled name>", "total_duration_ns": <float>, "dispatches": <int>, "mean_duration_ns": <float> } ], "variance": { "cv_threshold": <float>, "metric": "mean_dispatch_duration_ns", // the compared quantity = Δduration_ns / Δdispatch_count "by_node": [ ... ], // key fields: {instance} "by_gpu_id": [ ... ], // key fields: {card} "by_gpu": [ ... ] // key fields: {instance, card} } } ``` `kernel` names are long mangled C++ symbols (600+ bytes) — keep full names in JSON, **truncate only at render time**. `total_duration_ns` and `mean_duration_ns` are native nanoseconds (the renderer converts). A kernel's share of total GPU time is `total_duration_ns / stats.kernels.total_duration_ns` (compute it at render time — there is no stored percentage field). The three **spatial** variance lists (`by_node`/`by_gpu_id`/`by_gpu`) carry exactly the same outer shape as `stats.variance.by_*` entries (`source`/`label`/`name`/`unit`/`reduction`/`n`/`cv`/`min`/`max`/`all`|`percentiles`) **plus a `kernel` field** holding the full kernel name; `source` is `"GPU"`, `label` is `"Mean dispatch duration"`, `unit` is `"ns"`, and `reduction` is `"ratio"` (Δduration ÷ Δdispatches). The compared quantity is each kernel's **mean dispatch duration** (ns/dispatch); it is a counter ratio rather than a temporal mean, but the outer entry shape matches a gauge entry, so these fold directly into the matching gauge variance subsection. All three variance lists are `[]` when nothing crossed `cv_threshold`. Only the **top kernels** participate in variance (under `--verbose`, "top kernels" is the full kernel set, so variance is computed for every kernel). ### `stats.variance` Nested under `stats` (not a top-level key). - `cv_threshold`, `verbose` - `by_node`, `by_gpu_id`, `by_gpu`: flat lists of per-metric entries (empty list when nothing varied along that axis). Every entry, in every section, carries the same outer shape: ```jsonc { "source": "...", "label": "...", "name": "...", "unit": "...", "reduction": "temporal_mean" | "rate" | "ratio" | "total", "n": <int>, "cv": <float>, "min": {<key_fields>, "value": <float>}, "max": {<key_fields>, "value": <float>}, // exactly one of the following, based on n (both in --verbose): "all": {<key>: <value>, ...} // when n <= 16 "percentiles": {p5, p25, p50, p75, p95} // when n > 16 } ```
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub