Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
---
name: memory-snapshot-report
description: Generate and view Unity memory snapshot reports. Use when the user wants to analyze a Unity memory snapshot, export it to a database, or generate/view an HTML report.
description: Generate and view Unity memory snapshot reports. Use when the user wants to analyze a Unity memory snapshot, export it to a database, validate an export against Unity golden values, or generate/view an HTML report.
---

# Memory Snapshot Report

This is the **analysis workflow** for the tool (export → validate → report, plus ad-hoc SQL).
To build, launch, and **screenshot** the tool end-to-end from a clean checkout, use the
`run-memory-snapshot-data-tool` skill and its driver.

## When to use

- User wants to analyze a Unity memory snapshot (`.snap` file).
- User wants to export a snapshot to a DuckDB or SQLite database.
- User wants to generate or view an HTML report from an exported snapshot database.
- User wants to validate an export against Unity golden values.

## Prerequisites

- .NET 10 SDK.
- Project path: **MemorySnapshotDataTools** is the project root; run commands from that directory.
- Run commands from the **repo root** (the directory containing `MemorySnapshotDataTools.sln`).

## Steps

### 1. Export snapshot to database

From the MemorySnapshotDataTools directory:
From the repo root:

```bash
dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- export <path/to/snapshot.snap> <path/to/output.duckdb> --validate minimal --verbose
Expand All @@ -45,9 +50,10 @@ dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- batch-export <dir

### 2. Validate export against Unity golden JSON

The golden extractor lives in the `com.unity.memory-snapshot-data-tools` package under `UnityPackage/`
in this repo, imported into U6TestBed via a local `file:` path in `Packages/manifest.json`. After extracting
`*_golden.json` in Unity (**Tools → Memory Snapshot Validation → Extract Golden Values**):
The golden extractor lives in the `com.unity.memory-snapshot-data-tools` package under
`UnityPackage/` in this repo, imported into a Unity project via a local `file:` path in that
project's `Packages/manifest.json`. After extracting `*_golden.json` in Unity
(**Tools → Memory Snapshot Validation → Extract Golden Values**):

```bash
dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- validate \
Expand All @@ -74,11 +80,14 @@ dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- report <path/to/o
- Omit `--out` to write to a temp file and open in the browser.
- Use `--title "My Report"` to set the report title.
- Report works with either DuckDB or SQLite databases produced by the export command.
Prefer **DuckDB** — the SQLite report query is dramatically slower (seconds → minutes).

### 4. Optional

- Open the generated HTML file or DB in the user’s preferred viewer.
- For ad-hoc SQL, use the same DB path; tables include `snapshot_info`, `native_objects`, `managed_objects`, `connections`, `native_roots`, `memory_regions`, `native_allocations`, `system_memory_regions`, and `summary_metrics` (MemoryProfiler Summary-page breakdown).
- For ad-hoc SQL, use the same DB path; tables are `snapshot_info`, `native_objects`,
`managed_objects`, `connections`, `native_roots`, `memory_regions`, `native_allocations`,
`system_memory_regions`, and `summary_metrics` (MemoryProfiler Summary-page breakdown).

## Domain

Expand Down
114 changes: 114 additions & 0 deletions .claude/skills/run-memory-snapshot-data-tool/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
name: run-memory-snapshot-data-tool
description: Build, run, screenshot, and test the Memory Snapshot Data Tool — the .NET 10 CLI that exports Unity .snap memory snapshots to DuckDB/SQLite and renders an HTML report. Use when asked to run, start, build, or test the tool, export a snapshot, generate or screenshot a report, or confirm a change works in the real app.
---

# Run: Memory Snapshot Data Tool

A .NET 10 CLI (`MemorySnapshotDataTools`) that exports a Unity memory snapshot (`.snap`)
to a DuckDB/SQLite database, then runs SQL to render a self-contained **HTML report**.
The report is the real product, so the agent path drives the whole pipeline and
**screenshots the rendered report with headless Chrome** — a CLI run you can look at.

The driver is [smoke.sh](.claude/skills/run-memory-snapshot-data-tool/smoke.sh):
`build → export → summary → report → screenshot`, with a pass/fail check at each step.

**Paths below are relative to the repo root** (the directory containing `MemorySnapshotDataTools.sln`).

## Prerequisites

- **.NET 10 SDK** — required. Verify: `dotnet --version` (10.0.103 here).
- **A `.snap` snapshot** — required, and **NOT in this repo**. Captures are large
(16 MB–600+ MB) and user-specific. Get one from the Unity **Memory Profiler**
("Capture") or point at one you already have. You pass its path to the driver.
- **Google Chrome** — optional, only for the screenshot step. Expected at
`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`. Without it the
driver still runs and writes the HTML; it just skips the PNG.

## Build

```bash
dotnet build MemorySnapshotDataTools.sln -c Release
```

## Run (agent path) — the driver

The snapshot is required; supply it as an argument (or via `MSDT_SNAP`):

```bash
.claude/skills/run-memory-snapshot-data-tool/smoke.sh /path/to/snapshot.snap
```

End-to-end on a 16 MB snapshot this takes **~8s** (most of it the build). Exit code `0`
means every step passed. Artifacts land in `/tmp/msdt-run/`:

- `out.duckdb` — the exported database
- `summary.txt` — the `summary` command's text output
- `report.html` — the rendered report
- `report.png` — **headless-Chrome screenshot of the report — open/Read this to see it**

Useful env overrides (see the header of [smoke.sh](.claude/skills/run-memory-snapshot-data-tool/smoke.sh)):

- `MSDT_SNAP_DIR=/path/to/captures` — auto-pick the **smallest** `.snap` in a directory
(handy when you don't care which snapshot; smallest = fastest).
- `MSDT_OUT_DIR=/some/dir` — write artifacts elsewhere (default `/tmp/msdt-run`).
- `MSDT_NO_BUILD=1` — skip the build (you just built).
- `MSDT_SQLITE=1` — also export+report via the SQLite backend. **Slow** — see Gotchas.

## Direct CLI invocation

To run one command without the driver (`export`/`report`/`summary`/`batch-export`/
`multi-report`/`validate`):

```bash
dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -c Release -- --help
dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -c Release -- export /path/to/snapshot.snap /tmp/out.duckdb --validate minimal
dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -c Release -- summary /tmp/out.duckdb
dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -c Release -- report /tmp/out.duckdb --out /tmp/report.html --title "My Report"
```

- `.duckdb` extension → DuckDB (default); `.db` + `--destination sqlite` → SQLite.
- `summary` accepts **either** a `.snap` or an exported DB and prints a memory breakdown
without writing a database (decoding a raw `.snap` is slower than reading a DB).
- `report --out` is optional; omit it and the tool writes a temp file and tries to open it
in a browser (don't rely on the auto-open when running headless — always pass `--out`).

## Run (human path)

`dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- report foo.duckdb` with no
`--out` opens the HTML in your default browser. Fine interactively; useless headless — use
the driver or pass `--out` instead.

## Test

```bash
dotnet test MemorySnapshotDataTools.sln -c Release --no-build
```

19 tests, ~0.5s. (Build first, or drop `--no-build`.)

## Gotchas

- **SQLite report is ~1000× slower than DuckDB.** On the same 16 MB snapshot, the DuckDB
`report` query was **0.1s**; the SQLite `report` query was **146s**. Export is fast for
both (~1s). Default to DuckDB; only set `MSDT_SQLITE=1` when you specifically need to
exercise the SQLite path, and expect it to take minutes.
- **Snapshots are not in the repo and are huge.** The driver fails fast with a clear message
if you don't pass one.
- **`report` opens the DB read-only** (defense-in-depth, per `CLAUDE.md`), so reporting can't
mutate the database — but a *second* process holding a write lock on a DuckDB file can still
block it. If you hit a DuckDB lock error on a pre-existing `.duckdb`, a GUI client
(e.g. DataGrip) probably has it open — close it rather than copying the file. The driver
sidesteps this by exporting a fresh DB into `/tmp/msdt-run`.
- **CLI exit codes are meaningful:** `0` success, `1` bad args / file not found,
`2` cancelled (Ctrl-C), `3` validation/error (for `validate`). The driver asserts on them.
- **The built CLI lives in a RID-specific folder** (`Cli/bin/Release/net10.0/osx-arm64/`).
The driver globs for `MemorySnapshotDataTools.dll` so it doesn't hard-code the RID.

## Troubleshooting

- `SMOKE FAIL: No .snap provided` — pass a snapshot path, or set `MSDT_SNAP` / `MSDT_SNAP_DIR`.
- `SMOKE FAIL: CLI dll not found under Cli/bin/Release` — you set `MSDT_NO_BUILD=1` without
having built. Build first, or unset it.
- `WARN: screenshot not produced` / `Chrome not found` — Chrome isn't at the expected path;
the HTML is still in `MSDT_OUT_DIR`. Open it in any browser.
111 changes: 111 additions & 0 deletions .claude/skills/run-memory-snapshot-data-tool/smoke.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
#!/usr/bin/env bash
#
# Smoke-drives the Memory Snapshot Data Tool end-to-end and leaves artifacts on disk:
#
# build (Release) -> export a .snap to DuckDB -> summary -> HTML report -> screenshot
#
# This is the agent path for "run the tool" / "confirm a change works in the real app."
# The tool is a CLI whose real product is a rendered HTML report, so the script renders
# that report headless with Chrome and writes a PNG you can actually look at.
#
# A .snap snapshot is required and is NOT shipped in this repo (captures are large and
# user-specific). You must supply one — capture it from the Unity Memory Profiler, or
# point at one you already have.
#
# Usage:
# .claude/skills/run-memory-snapshot-data-tool/smoke.sh <snapshot.snap>
# MSDT_SNAP=/path/to/snapshot.snap .claude/skills/run-memory-snapshot-data-tool/smoke.sh
# MSDT_SNAP_DIR=/path/to/captures .claude/skills/run-memory-snapshot-data-tool/smoke.sh # picks the smallest .snap there
#
# Env overrides:
# MSDT_SNAP explicit path to a .snap (alternative to the positional argument)
# MSDT_SNAP_DIR dir to search for the smallest .snap when no path is given (no default)
# MSDT_OUT_DIR where artifacts land (default: /tmp/msdt-run)
# MSDT_CONFIG dotnet build configuration (default: Release)
# MSDT_SQLITE=1 also exercise the SQLite backend (export + report).
# NOTE: the SQLite *report* query is very slow (~150s); DuckDB is ~0.1s.
# MSDT_NO_BUILD=1 skip the build step (assume the solution is already built)
#
# Exit code 0 = every checked step passed.

set -euo pipefail

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
cd "$REPO_ROOT"

OUT_DIR="${MSDT_OUT_DIR:-/tmp/msdt-run}"
CONFIG="${MSDT_CONFIG:-Release}"
CHROME="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
mkdir -p "$OUT_DIR"

fail() { echo "SMOKE FAIL: $*" >&2; exit 1; }
step() { echo; echo "==> $*"; }

# ---- resolve snapshot (arg > $MSDT_SNAP > smallest under $MSDT_SNAP_DIR) ----
SNAP="${1:-${MSDT_SNAP:-}}"
if [[ -z "$SNAP" && -n "${MSDT_SNAP_DIR:-}" ]]; then
step "Discovering smallest .snap under $MSDT_SNAP_DIR"
SNAP="$(find "$MSDT_SNAP_DIR" -maxdepth 1 -name '*.snap' -type f 2>/dev/null | while read -r f; do
printf '%s %s\n' "$(wc -c < "$f")" "$f"
done | sort -n | head -1 | cut -d' ' -f2-)"
fi
[[ -n "$SNAP" && -f "$SNAP" ]] || fail "No .snap provided. Pass one as an argument, set MSDT_SNAP, or set MSDT_SNAP_DIR. Captures are large and live outside this repo (see SKILL.md)."
echo "Using snapshot: $SNAP"

# ---- build ----
if [[ "${MSDT_NO_BUILD:-}" != "1" ]]; then
step "Building solution ($CONFIG)"
dotnet build MemorySnapshotDataTools.sln -c "$CONFIG" >/dev/null || fail "build failed"
fi

# ---- locate the CLI (RID-specific output dir; glob avoids hard-coding osx-arm64) ----
CLI_DLL="$(find "Cli/bin/$CONFIG" -name MemorySnapshotDataTools.dll 2>/dev/null | head -1)"
[[ -n "$CLI_DLL" ]] || fail "CLI dll not found under Cli/bin/$CONFIG — run a build first (unset MSDT_NO_BUILD)."
run_cli() { dotnet "$CLI_DLL" "$@"; }

step "CLI help (sanity)"
run_cli --help >/dev/null || fail "--help failed"

# ---- export -> DuckDB ----
DB="$OUT_DIR/out.duckdb"
rm -f "$DB" "$DB.wal"
step "export -> $DB (DuckDB)"
run_cli export "$SNAP" "$DB" --validate minimal --verbose || fail "export failed"
[[ -s "$DB" ]] || fail "export produced no database file"

# ---- summary (no DB generated; reads the one we just made) ----
step "summary $DB"
run_cli summary "$DB" | tee "$OUT_DIR/summary.txt"
grep -q "Memory Usage Summary" "$OUT_DIR/summary.txt" || fail "summary output missing expected header"

# ---- report -> HTML ----
HTML="$OUT_DIR/report.html"
rm -f "$HTML"
step "report $DB -> $HTML"
run_cli report "$DB" --out "$HTML" --title "MSDT Smoke Report" --verbose || fail "report failed"
grep -q "<!DOCTYPE html>" "$HTML" || fail "report HTML does not look like HTML"

# ---- screenshot the rendered report ----
PNG="$OUT_DIR/report.png"
rm -f "$PNG"
if [[ -x "$CHROME" ]]; then
step "screenshot $HTML -> $PNG"
"$CHROME" --headless --disable-gpu --hide-scrollbars --window-size=1400,2400 \
--screenshot="$PNG" "file://$HTML" >/dev/null 2>&1 || true
if [[ -s "$PNG" ]]; then echo "screenshot OK: $PNG"; else echo "WARN: screenshot not produced (open $HTML manually)"; fi
else
echo "WARN: Chrome not found at '$CHROME' — skipping screenshot. Open $HTML in a browser instead."
fi

# ---- optional SQLite backend coverage (slow report; off by default) ----
if [[ "${MSDT_SQLITE:-}" == "1" ]]; then
SDB="$OUT_DIR/out.db"
rm -f "$SDB" "$SDB-wal" "$SDB-shm"
step "export -> $SDB (SQLite)"
run_cli export "$SNAP" "$SDB" --destination sqlite --validate minimal || fail "sqlite export failed"
step "report from SQLite -> $OUT_DIR/report-sqlite.html (SLOW: query ~150s)"
run_cli report "$SDB" --out "$OUT_DIR/report-sqlite.html" || fail "sqlite report failed"
fi

step "DONE — artifacts in $OUT_DIR"
ls -la "$OUT_DIR"
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Project guidance for Claude

MemorySnapshotDataTool parses Unity memory snapshot (`.snap`) files and exports them to
MemorySnapshotDataTools parses Unity memory snapshot (`.snap`) files and exports them to
DuckDB / SQLite databases, then runs SQL to build HTML reports. Because the whole tool is
built around composing and executing SQL, **SQL safety is a first-class rule in this repo.**

Expand Down
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
MemorySnapshotDataTool © 2026 Unity Technologies
MemorySnapshotDataTools © 2026 Unity Technologies

Licensed under the Unity Companion License for Unity-dependent projects (see https://unity3d.com/legal/licenses/unity_companion_license).

Expand Down
Loading
Loading